vncDesktop.cpp
上传用户:sbftbdw
上传日期:2007-01-03
资源大小:379k
文件大小:30k
源码类别:

远程控制编程

开发平台:

Visual C++

  1. //  Copyright (C) 1997, 1998 Olivetti & Oracle Research Laboratory
  2. //
  3. //  This file is part of the VNC system.
  4. //
  5. //  The VNC system is free software; you can redistribute it and/or modify
  6. //  it under the terms of the GNU General Public License as published by
  7. //  the Free Software Foundation; either version 2 of the License, or
  8. //  (at your option) any later version.
  9. //
  10. //  This program is distributed in the hope that it will be useful,
  11. //  but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. //  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  13. //  GNU General Public License for more details.
  14. //
  15. //  You should have received a copy of the GNU General Public License
  16. //  along with this program; if not, write to the Free Software
  17. //  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307,
  18. //  USA.
  19. //
  20. // If the source code for the VNC system is not available from the place 
  21. // whence you received this file, check http://www.orl.co.uk/vnc or contact
  22. // the authors on vnc@orl.co.uk for information on obtaining it.
  23. // vncDesktop implementation
  24. // System headers
  25. #include "stdhdrs.h"
  26. #include <omnithread.h>
  27. // Custom headers
  28. #include "WinVNC.h"
  29. #include "VNCHooksVNCHooks.h"
  30. #include "vncServer.h"
  31. #include "vncRegion.h"
  32. #include "rectlist.h"
  33. #include "vncDesktop.h"
  34. #include "vncService.h"
  35. // Constants
  36. const UINT RFB_SCREEN_UPDATE = RegisterWindowMessage("WinVNC.Update.DrawRect");
  37. const UINT RFB_COPYRECT_UPDATE = RegisterWindowMessage("WinVNC.Update.CopyRect");
  38. const UINT RFB_MOUSE_UPDATE = RegisterWindowMessage("WinVNC.Update.Mouse");
  39. const char szDesktopSink[] = "WinVNC desktop sink";
  40. // Atoms
  41. const char *VNC_WINDOWPOS_ATOMNAME = "VNCHooks.CopyRect.WindowPos";
  42. ATOM VNC_WINDOWPOS_ATOM = NULL;
  43. // The desktop handler thread
  44. // This handles the messages posted by RFBLib to the vncDesktop window
  45. class vncDesktopThread : public omni_thread
  46. {
  47. public:
  48. vncDesktopThread() {m_returnsig = NULL;};
  49. protected:
  50. ~vncDesktopThread() {if (m_returnsig != NULL) delete m_returnsig;};
  51. public:
  52. virtual BOOL Init(vncDesktop *desktop, vncServer *server);
  53. virtual void *run_undetached(void *arg);
  54. virtual void ReturnVal(BOOL result);
  55. protected:
  56. vncServer *m_server;
  57. vncDesktop *m_desktop;
  58. omni_mutex m_returnLock;
  59. omni_condition *m_returnsig;
  60. BOOL m_return;
  61. BOOL m_returnset;
  62. };
  63. BOOL
  64. vncDesktopThread::Init(vncDesktop *desktop, vncServer *server)
  65. {
  66. // Save the server pointer
  67. m_server = server;
  68. m_desktop = desktop;
  69. m_returnset = FALSE;
  70. m_returnsig = new omni_condition(&m_returnLock);
  71. // Start the thread
  72. start_undetached();
  73. // Wait for the thread to let us know if it failed to init
  74. { omni_mutex_lock l(m_returnLock);
  75. while (!m_returnset)
  76. {
  77. m_returnsig->wait();
  78. }
  79. }
  80. return m_return;
  81. }
  82. void
  83. vncDesktopThread::ReturnVal(BOOL result)
  84. {
  85. omni_mutex_lock l(m_returnLock);
  86. m_returnset = TRUE;
  87. m_return = result;
  88. m_returnsig->signal();
  89. }
  90. void *
  91. vncDesktopThread::run_undetached(void *arg)
  92. {
  93. // Attempt to initialise and return success or failure
  94. if (!m_desktop->Startup())
  95. {
  96. ReturnVal(FALSE);
  97. return NULL;
  98. }
  99. // Succeeded to initialise ok
  100. ReturnVal(TRUE);
  101. // START PROCESSING DESKTOP MESSAGES
  102. // Add the window into the clipboard system, so we get notified if it changes
  103. // *** This MUST be done AFTER ReturnVal, otherwise a deadlock can occur,
  104. // This is because when the SetClipboardViewer function is called, it causes
  105. // this thread to attempt to update any clients' clipboards, which involves
  106. // locking the vncServer object.  But the vncServer object is already locked
  107. // by the thread which is waiting for ReturnVal to be called...
  108. m_desktop->m_hnextviewer = SetClipboardViewer(m_desktop->Window());
  109. // All UpdateRect messages are cached into a region cache object and are
  110. // only passed to clients immediately before TriggerUpdate is called!
  111. vncRegion rgncache;
  112. rgncache.Clear();
  113. BOOL unhandled;
  114. MSG msg;
  115. while (TRUE)
  116. {
  117. unhandled = TRUE;
  118. if (!PeekMessage(&msg, m_desktop->Window(), NULL, NULL, PM_NOREMOVE))
  119. {
  120. // Thread has gone idle.  Now would be a good time to send an update.
  121. // First, we must check that the screen hasnt changed too much.
  122. // Has the display resolution or desktop changed?
  123. if (m_desktop->m_displaychanged || !vncService::InputDesktopSelected())
  124. {
  125. rfbServerInitMsg oldscrinfo = m_desktop->m_scrinfo;
  126. m_desktop->m_displaychanged = FALSE;
  127. // Attempt to close the old hooks
  128. if (!m_desktop->Shutdown())
  129. {
  130. m_server->KillAll();
  131. break;
  132. }
  133. // Now attempt to re-install them!
  134. if (!m_desktop->Startup())
  135. {
  136. m_server->KillAll();
  137. break;
  138. }
  139. // Check that the screen info hasn't changed
  140. log.Print(LL_INTINFO, VNCLOG("SCR: old screen format %dx%dx%dn"),
  141. oldscrinfo.framebufferWidth,
  142. oldscrinfo.framebufferHeight,
  143. oldscrinfo.format.bitsPerPixel);
  144. log.Print(LL_INTINFO, VNCLOG("SCR: new screen format %dx%dx%dn"),
  145. m_desktop->m_scrinfo.framebufferWidth,
  146. m_desktop->m_scrinfo.framebufferHeight,
  147. m_desktop->m_scrinfo.format.bitsPerPixel);
  148. if (memcmp(&m_desktop->m_scrinfo, &oldscrinfo, sizeof(oldscrinfo)) != 0)
  149. {
  150. m_server->KillAll();
  151. break;
  152. }
  153. // Add a full screen update to all the clients
  154. RECT rect = m_desktop->m_bmrect;
  155. m_server->UpdateRect(rect);
  156. m_server->UpdatePalette();
  157. }
  158. // TRIGGER THE UPDATE
  159. // Update the mouse if required
  160. if (m_desktop->m_cursormoved)
  161. {
  162. m_desktop->m_cursormoved = FALSE;
  163. // Tell the server that the cursor has moved!
  164. m_server->UpdateMouse();
  165. }
  166. // Check for moved windows
  167. m_desktop->CalcCopyRects();
  168. // Flush the cached region data to all clients
  169. m_server->UpdateRegion(rgncache);
  170. rgncache.Clear();
  171. // Trigger an update to be sent
  172. m_server->TriggerUpdate();
  173. }
  174. // Now wait on further messages, or quit if told to
  175. if (!GetMessage(&msg, m_desktop->Window(), 0,0))
  176. break;
  177. // Now switch, dependent upon the message type recieved
  178. if (msg.message == RFB_SCREEN_UPDATE)
  179. {
  180. // An area of the screen has changed
  181. RECT rect;
  182. rect.left = (SHORT) LOWORD(msg.wParam);
  183. rect.top = (SHORT) HIWORD(msg.wParam);
  184. rect.right = (SHORT) LOWORD(msg.lParam);
  185. rect.bottom = (SHORT) HIWORD(msg.lParam);
  186. rgncache.AddRect(rect);
  187. // m_server->UpdateRect(rect);
  188. unhandled = FALSE;
  189. }
  190. if (msg.message == RFB_MOUSE_UPDATE)
  191. {
  192. // Save the cursor ID
  193. m_desktop->SetCursor((HCURSOR) msg.wParam);
  194. m_desktop->m_cursormoved = TRUE;
  195. unhandled = FALSE;
  196. }
  197. if (unhandled)
  198. DispatchMessage(&msg);
  199. }
  200. log.Print(LL_INTINFO, VNCLOG("quitting desktop server threadn"));
  201. // Clear all the hooks and close windows, etc.
  202. m_desktop->Shutdown();
  203. // Clear the shift modifier keys, now that there are no remote clients
  204. vncKeymap::ClearShiftKeys();
  205. return NULL;
  206. }
  207. // Implementation
  208. vncDesktop::vncDesktop()
  209. {
  210. m_thread = NULL;
  211. m_hwnd = NULL;
  212. m_timerid = 0;
  213. m_hnextviewer = NULL;
  214. m_hcursor = NULL;
  215. m_cursormoved = TRUE;
  216. m_displaychanged = FALSE;
  217. m_hrootdc = NULL;
  218. m_hmemdc = NULL;
  219. m_membitmap = NULL;
  220. m_initialClipBoardSeen = FALSE;
  221. }
  222. vncDesktop::~vncDesktop()
  223. {
  224. log.Print(LL_INTINFO, VNCLOG("killing screen servern"));
  225. // If we created a thread then here we delete it
  226. // The thread itself does most of the cleanup
  227. if(m_thread != NULL)
  228. {
  229. // Post a close message to quit our message handler thread
  230. PostMessage(Window(), WM_QUIT, 0, 0);
  231. // Join with the desktop handler thread
  232. void *returnval;
  233. m_thread->join(&returnval);
  234. m_thread = NULL;
  235. }
  236. // Let's call Shutdown just in case something went wrong...
  237. Shutdown();
  238. }
  239. // Routine to startup and install all the hooks and stuff
  240. BOOL
  241. vncDesktop::Startup()
  242. {
  243. // Initialise the Desktop object
  244. KillScreenSaver();
  245. if (!InitDesktop())
  246. return FALSE;
  247. if (!InitBitmap())
  248. return FALSE;
  249. if (!ThunkBitmapInfo())
  250. return FALSE;
  251. if (!SetPixFormat())
  252. return FALSE;
  253. if (!SetPixShifts())
  254. return FALSE;
  255. if (!SetPalette())
  256. return FALSE;
  257. if (!InitWindow())
  258. return FALSE;
  259. // Add the system hook
  260. if (!SetHook(
  261. m_hwnd,
  262. RFB_SCREEN_UPDATE,
  263. RFB_COPYRECT_UPDATE,
  264. RFB_MOUSE_UPDATE
  265. ))
  266. {
  267. log.Print(LL_INTERR, VNCLOG("failed to set system hooksn"));
  268. // return FALSE;
  269. }
  270. // Start a timer to handle Polling Mode.  The timer will cause
  271. // an "idle" event once every second, which is necessary if Polling
  272. // Mode is being used, to cause TriggerUpdate to be called.
  273. m_timerid = SetTimer(m_hwnd, 1, 1000, NULL);
  274. // Get hold of the WindowPos atom!
  275. if ((VNC_WINDOWPOS_ATOM = GlobalAddAtom(VNC_WINDOWPOS_ATOMNAME)) == 0)
  276. return FALSE;
  277. // Everything is ok, so return TRUE
  278. return TRUE;
  279. }
  280. // Routine to shutdown all the hooks and stuff
  281. BOOL
  282. vncDesktop::Shutdown()
  283. {
  284. // If we created a timer then kill it
  285. if (m_timerid != NULL)
  286. KillTimer(NULL, m_timerid);
  287. // If we created a window then kill it and the hooks
  288. if(m_hwnd != NULL)
  289. {
  290. // Remove the system hooks
  291. UnSetHook(m_hwnd);
  292. // The window is being closed - remove it from the viewer list
  293. ChangeClipboardChain(m_hwnd, m_hnextviewer);
  294. // Close the hook window
  295. DestroyWindow(m_hwnd);
  296. m_hwnd = NULL;
  297. m_hnextviewer = NULL;
  298. }
  299. // Now free all the bitmap stuff
  300. if (m_hrootdc != NULL)
  301. {
  302. // Release our device context
  303. if(ReleaseDC(NULL, m_hrootdc) == 0)
  304. {
  305. //MessageBox(NULL, "Failed to ReleaseDC", "Error", MB_OK);
  306. }
  307. m_hrootdc = NULL;
  308. }
  309. if (m_hmemdc != NULL)
  310. {
  311. // Release our device context
  312. if (!DeleteDC(m_hmemdc))
  313. {
  314. //MessageBox(NULL, "Failed to DeleteDC", "Error", MB_OK);
  315. }
  316. m_hmemdc = NULL;
  317. }
  318. if (m_membitmap != NULL)
  319. {
  320. // Release the custom bitmap, if any
  321. if (!DeleteObject(m_membitmap))
  322. {
  323. //MessageBox(NULL, "Failed to DeleteObject", "Error", MB_OK);
  324. }
  325. m_membitmap = NULL;
  326. }
  327. m_oldbitmap = NULL;
  328. // Free the WindowPos atom!
  329. if (VNC_WINDOWPOS_ATOM != NULL)
  330. GlobalDeleteAtom(VNC_WINDOWPOS_ATOM);
  331. return TRUE;
  332. }
  333. // Routine to ensure we're on the correct NT desktop
  334. // This routine calls the vncNTOnly class, which implements NT specific WinVNC functions
  335. BOOL
  336. vncDesktop::InitDesktop()
  337. {
  338. // Ask for the current input desktop
  339. return vncService::SelectDesktop(NULL);
  340. }
  341. // Routine used to close the screen saver, if it's active...
  342. BOOL CALLBACK
  343. KillScreenSaverFunc(HWND hwnd, LPARAM lParam)
  344. {
  345. PostMessage(hwnd, WM_CLOSE, 0, 0);
  346. return TRUE;
  347. }
  348. void
  349. vncDesktop::KillScreenSaver()
  350. {
  351. OSVERSIONINFO osversioninfo;
  352. osversioninfo.dwOSVersionInfoSize = sizeof(osversioninfo);
  353. // *** If we're running as a service then we don't kill the screensaver
  354. if (vncService::RunningAsService())
  355. return;
  356. // Get the current OS version
  357. if (!GetVersionEx(&osversioninfo))
  358. return;
  359. // How to kill the screen saver depends on the OS
  360. switch (osversioninfo.dwPlatformId)
  361. {
  362. case VER_PLATFORM_WIN32_WINDOWS:
  363. {
  364. // Windows 95
  365. // Fidn the ScreenSaverClass window
  366. HWND hsswnd = FindWindow ("WindowsScreenSaverClass", NULL);
  367. if (hsswnd != NULL)
  368. PostMessage(hsswnd, WM_CLOSE, 0, 0); 
  369. break;
  370. case VER_PLATFORM_WIN32_NT:
  371. {
  372. // Windows NT
  373. // Find the screensaver desktop
  374. HDESK hDesk = OpenDesktop(
  375. "Screen-saver",
  376. 0,
  377. FALSE,
  378. DESKTOP_READOBJECTS | DESKTOP_WRITEOBJECTS
  379. );
  380. if (hDesk != NULL)
  381. {
  382. // Close all windows on the screen saver desktop
  383. EnumDesktopWindows(hDesk, (WNDENUMPROC) &KillScreenSaverFunc, 0);
  384. CloseDesktop(hDesk);
  385. // Pause long enough for the screen-saver to close
  386. Sleep(2000);
  387. // Reset the screen saver so it can run again
  388. SystemParametersInfo(SPI_SETSCREENSAVEACTIVE, TRUE, 0, SPIF_SENDWININICHANGE); 
  389. }
  390. break;
  391. }
  392. }
  393. }
  394. BOOL
  395. vncDesktop::InitBitmap()
  396. {
  397. // Get the device context for the whole screen and find it's size
  398. m_hrootdc = ::GetDC(NULL);
  399. if (m_hrootdc == NULL)
  400. return FALSE;
  401. m_bmrect.left = m_bmrect.top = 0;
  402. m_bmrect.right = GetDeviceCaps(m_hrootdc, HORZRES);
  403. m_bmrect.bottom = GetDeviceCaps(m_hrootdc, VERTRES);
  404. log.Print(LL_INTINFO, VNCLOG("bitmap dimensions are %d x %dn"), m_bmrect.right, m_bmrect.bottom);
  405. // Create a compatible memory DC and a bitmap for it
  406. m_hmemdc = CreateCompatibleDC(m_hrootdc);
  407. if (m_hmemdc == NULL)
  408. return FALSE;
  409. m_membitmap = CreateCompatibleBitmap(m_hrootdc, m_bmrect.right, m_bmrect.bottom);
  410. if (m_membitmap == NULL)
  411. return FALSE;
  412. log.Print(LL_INTINFO, VNCLOG("created memory bitmapn"));
  413. // Check that the device capabilities are ok
  414. if ((GetDeviceCaps(m_hrootdc, RASTERCAPS) & RC_BITBLT) == 0)
  415. {
  416. MessageBox(
  417. NULL,
  418. "vncDesktop : root device doesn't support BitBltn"
  419. "WinVNC cannot be used with this graphic device driver",
  420. szAppName,
  421. MB_ICONSTOP | MB_OK
  422. );
  423. return FALSE;
  424. }
  425. if ((GetDeviceCaps(m_hmemdc, RASTERCAPS) & RC_DI_BITMAP) == 0)
  426. {
  427. MessageBox(
  428. NULL,
  429. "vncDesktop : memory device doesn't support GetDIBitsn"
  430. "WinVNC cannot be used with this graphics device driver",
  431. szAppName,
  432. MB_ICONSTOP | MB_OK
  433. );
  434. return FALSE;
  435. }
  436. // Get the bitmap's format and colour details
  437. int result;
  438. m_bminfo.bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
  439. m_bminfo.bmi.bmiHeader.biBitCount = 0;
  440. result = ::GetDIBits(m_hmemdc, m_membitmap, 0, 1, NULL, &m_bminfo.bmi, DIB_RGB_COLORS);
  441. if (result == 0)
  442. return FALSE;
  443. result = ::GetDIBits(m_hmemdc, m_membitmap,  0, 1, NULL, &m_bminfo.bmi, DIB_RGB_COLORS);
  444. if (result == 0)
  445. return FALSE;
  446. log.Print(LL_INTINFO, VNCLOG("got bitmap formatn"));
  447. // Henceforth we want to use a top-down scanning representation
  448. m_bminfo.bmi.bmiHeader.biHeight = - abs(m_bminfo.bmi.bmiHeader.biHeight);
  449. // Is the bitmap palette-based or truecolour?
  450. m_bminfo.truecolour = (GetDeviceCaps(m_hrootdc, RASTERCAPS) & RC_PALETTE) == 0;
  451. return TRUE;
  452. }
  453. BOOL
  454. vncDesktop::ThunkBitmapInfo()
  455. {
  456. // Attempt to force the actual format into one we can handle
  457. // We can handle 8-bit-palette and 16/32-bit-truecolour modes
  458. switch (m_bminfo.bmi.bmiHeader.biBitCount)
  459. {
  460. case 1:
  461. case 4:
  462. // Correct the BITMAPINFO header to the format we actually want
  463. m_bminfo.bmi.bmiHeader.biClrUsed = 1 << m_bminfo.bmi.bmiHeader.biBitCount;
  464. m_bminfo.bmi.bmiHeader.biBitCount = 8;
  465. m_bminfo.bmi.bmiHeader.biSizeImage =
  466. abs((m_bminfo.bmi.bmiHeader.biWidth *
  467. m_bminfo.bmi.bmiHeader.biHeight *
  468. m_bminfo.bmi.bmiHeader.biBitCount)/ 8);
  469. m_bminfo.bmi.bmiHeader.biClrImportant = 0;
  470. m_bminfo.truecolour = FALSE;
  471. break;
  472. case 24:
  473. // Update the bitmapinfo header
  474. m_bminfo.bmi.bmiHeader.biBitCount = 32;
  475. m_bminfo.bmi.bmiHeader.biSizeImage =
  476. abs((m_bminfo.bmi.bmiHeader.biWidth *
  477. m_bminfo.bmi.bmiHeader.biHeight *
  478. m_bminfo.bmi.bmiHeader.biBitCount)/ 8);
  479. break;
  480. }
  481. return TRUE;
  482. }
  483. BOOL
  484. vncDesktop::SetPixFormat()
  485. {
  486. // Examine the bitmapinfo structure to obtain the current pixel format
  487. m_scrinfo.format.trueColour = m_bminfo.truecolour;
  488. m_scrinfo.format.bigEndian = 0;
  489. // Set up the native buffer width, height and format
  490. m_scrinfo.framebufferWidth = (CARD16) (m_bmrect.right - m_bmrect.left); // Swap endian before actually sending
  491. m_scrinfo.framebufferHeight = (CARD16) (m_bmrect.bottom - m_bmrect.top); // Swap endian before actually sending
  492. m_scrinfo.format.bitsPerPixel = (CARD8) m_bminfo.bmi.bmiHeader.biBitCount;
  493. m_scrinfo.format.depth        = (CARD8) m_bminfo.bmi.bmiHeader.biBitCount;
  494. // Calculate the number of bytes per row
  495. m_bytesPerRow = m_scrinfo.framebufferWidth * m_scrinfo.format.bitsPerPixel / 8;
  496. return TRUE;
  497. }
  498. BOOL
  499. vncDesktop::SetPixShifts()
  500. {
  501. // Sort out the colour shifts, etc.
  502. DWORD redMask=0, blueMask=0, greenMask = 0;
  503. switch (m_bminfo.bmi.bmiHeader.biBitCount)
  504. {
  505. case 16:
  506. // Standard 16-bit display
  507. if (m_bminfo.bmi.bmiHeader.biCompression == BI_RGB)
  508. {
  509. // each word single pixel 5-5-5
  510. redMask = 0x7c00; greenMask = 0x03e0; blueMask = 0x001f;
  511. }
  512. else
  513. {
  514. if (m_bminfo.bmi.bmiHeader.biCompression == BI_BITFIELDS)
  515. {
  516. redMask =   *(DWORD *) &m_bminfo.bmi.bmiColors[0];
  517. greenMask = *(DWORD *) &m_bminfo.bmi.bmiColors[1];
  518. blueMask =  *(DWORD *) &m_bminfo.bmi.bmiColors[2];
  519. }
  520. }
  521. break;
  522. case 32:
  523. // Standard 24/32 bit displays
  524. if (m_bminfo.bmi.bmiHeader.biCompression == BI_RGB)
  525. {
  526. redMask = 0xff0000; greenMask = 0xff00; blueMask = 0x00ff;
  527. }
  528. else
  529. {
  530. if (m_bminfo.bmi.bmiHeader.biCompression == BI_BITFIELDS)
  531. {
  532. redMask =   *(DWORD *) &m_bminfo.bmi.bmiColors[0];
  533. greenMask = *(DWORD *) &m_bminfo.bmi.bmiColors[1];
  534. blueMask =  *(DWORD *) &m_bminfo.bmi.bmiColors[2];
  535. }
  536. }
  537. break;
  538. default:
  539. // Other pixel formats are only valid if they're palette-based
  540. if (m_bminfo.truecolour)
  541. {
  542. log.Print(LL_INTERR, "unsupported truecolour pixel format for setpixshiftsn");
  543. return FALSE;
  544. }
  545. return TRUE;
  546. }
  547. // Convert the data we just retrieved
  548. MaskToMaxAndShift(redMask, m_scrinfo.format.redMax, m_scrinfo.format.redShift);
  549. MaskToMaxAndShift(greenMask, m_scrinfo.format.greenMax, m_scrinfo.format.greenShift);
  550. MaskToMaxAndShift(blueMask, m_scrinfo.format.blueMax, m_scrinfo.format.blueShift);
  551. return TRUE;
  552. }
  553. BOOL
  554. vncDesktop::SetPalette()
  555. {
  556. // Lock the current display palette into the memory DC we're holding
  557. // *** CHECK THIS FOR LEAKS!
  558. if (!m_bminfo.truecolour)
  559. {
  560. LOGPALETTE *palette;
  561. UINT size = sizeof(LOGPALETTE) + (sizeof(PALETTEENTRY) * 256);
  562. palette = (LOGPALETTE *) new char[size];
  563. if (palette == NULL)
  564. return FALSE;
  565. // Initialise the structure
  566. palette->palVersion = 0x300;
  567. palette->palNumEntries = 256;
  568. // Get the system colours
  569. if (GetSystemPaletteEntries(m_hrootdc,
  570. 0, 256, palette->palPalEntry) == 0)
  571. {
  572. delete [] palette;
  573. return FALSE;
  574. }
  575. // Create a palette from those
  576. HPALETTE pal = CreatePalette(palette);
  577. if (pal == NULL)
  578. {
  579. delete [] palette;
  580. return FALSE;
  581. }
  582. // Select the palette into our memory DC
  583. HPALETTE oldpalette = SelectPalette(m_hmemdc, pal, FALSE);
  584. if (oldpalette == NULL)
  585. {
  586. delete [] palette;
  587. DeleteObject(pal);
  588. return FALSE;
  589. }
  590. // Worked, so realise the palette
  591. RealizePalette(m_hmemdc);
  592. // It worked!
  593. delete [] palette;
  594. DeleteObject(oldpalette);
  595. return TRUE;
  596. }
  597. // Not a palette based local screen - forget it!
  598. return TRUE;
  599. }
  600. LRESULT CALLBACK DesktopWndProc(HWND hwnd, UINT iMsg, WPARAM wParam, LPARAM lParam);
  601. BOOL
  602. vncDesktop::InitWindow()
  603. {
  604. // Create the window class
  605. WNDCLASSEX wndclass;
  606. wndclass.cbSize = sizeof(wndclass);
  607. wndclass.style = 0;
  608. wndclass.lpfnWndProc = &DesktopWndProc;
  609. wndclass.cbClsExtra = 0;
  610. wndclass.cbWndExtra = 0;
  611. wndclass.hInstance = hAppInstance;
  612. wndclass.hIcon = NULL;
  613. wndclass.hCursor = NULL;
  614. wndclass.hbrBackground = (HBRUSH) GetStockObject(WHITE_BRUSH);
  615. wndclass.lpszMenuName = (const char *) NULL;
  616. wndclass.lpszClassName = szDesktopSink;
  617. wndclass.hIconSm = NULL;
  618. // Register it
  619. RegisterClassEx(&wndclass);
  620. // And create a window
  621. m_hwnd = CreateWindow(szDesktopSink,
  622. "WinVNC",
  623. WS_OVERLAPPEDWINDOW,
  624. CW_USEDEFAULT,
  625. CW_USEDEFAULT,
  626. 400, 200,
  627. NULL,
  628. NULL,
  629. hAppInstance,
  630. NULL);
  631. if (m_hwnd == NULL)
  632. return FALSE;
  633. // Set the "this" pointer for the window
  634. SetWindowLong(m_hwnd, GWL_USERDATA, (long)this);
  635. return TRUE;
  636. }
  637. BOOL
  638. vncDesktop::Init(vncServer *server)
  639. {
  640. log.Print(LL_INTINFO, VNCLOG("initialising desktop handlern"));
  641. // Save the server pointer
  642. m_server = server;
  643. // Load in the arrow cursor
  644. m_hdefcursor = LoadCursor(NULL, IDC_ARROW);
  645. m_hcursor = m_hdefcursor;
  646. // Spawn a thread to handle that window's message queue
  647. vncDesktopThread *thread = new vncDesktopThread;
  648. if (thread == NULL)
  649. return FALSE;
  650. m_thread = thread;
  651. return thread->Init(this, m_server);
  652. }
  653. void
  654. vncDesktop::RequestUpdate()
  655. {
  656. PostMessage(m_hwnd, WM_TIMER, 0, 0);
  657. }
  658. int
  659. vncDesktop::ScreenBuffSize()
  660. {
  661. return m_scrinfo.format.bitsPerPixel/8 *
  662. m_scrinfo.framebufferWidth *
  663. m_scrinfo.framebufferHeight;
  664. }
  665. void
  666. vncDesktop::FillDisplayInfo(rfbServerInitMsg *scrinfo)
  667. {
  668. memcpy(scrinfo, &m_scrinfo, sz_rfbServerInitMsg);
  669. }
  670. // Function to capture an area of the screen immediately prior to sending
  671. // an update.
  672. void
  673. vncDesktop::CaptureScreen(RECT &rect, BYTE *scrBuff, UINT scrBuffSize)
  674. {
  675. // Protect the memory bitmap
  676. omni_mutex_lock l(m_bitbltlock);
  677. // Finish drawing anything in this thread 
  678. // Wish we could do this for the whole system - maybe we should
  679. // do something with LockWindowUpdate here.
  680. GdiFlush();
  681. // Select the memory bitmap into the memory DC
  682. if ((m_oldbitmap = (HBITMAP) SelectObject(m_hmemdc, m_membitmap)) == NULL)
  683. return;
  684. // Capture screen into bitmap
  685. BOOL blitok = BitBlt(m_hmemdc, rect.left, rect.top,
  686. (rect.right-rect.left),
  687. (rect.bottom-rect.top),
  688. m_hrootdc, rect.left, rect.top, SRCCOPY);
  689. // Select the old bitmap back into the memory DC
  690. SelectObject(m_hmemdc, m_oldbitmap);
  691. // If the blit went ok then copy the data to our buffer...
  692. if (blitok)
  693. {
  694. // And copy this into the buffer
  695. CopyToBuffer(rect, scrBuff, scrBuffSize);
  696. }
  697. }
  698. // Add the mouse pointer to the buffer
  699. void
  700. vncDesktop::CaptureMouse(BYTE *scrBuff, UINT scrBuffSize)
  701. {
  702. // Protect the memory bitmap
  703. omni_mutex_lock l(m_bitbltlock);
  704. POINT CursorPos;
  705. ICONINFO IconInfo;
  706. // If the mouse cursor handle is invalid then forget it
  707. if (m_hcursor == NULL)
  708. return;
  709. // Get the cursor position
  710. if (!GetCursorPos(&CursorPos))
  711. return;
  712. // Translate position for hotspot
  713. if (GetIconInfo(m_hcursor, &IconInfo))
  714. {
  715. CursorPos.x -= ((int) IconInfo.xHotspot);
  716. CursorPos.y -= ((int) IconInfo.yHotspot);
  717. if (IconInfo.hbmMask != NULL)
  718. DeleteObject(IconInfo.hbmMask);
  719. if (IconInfo.hbmColor != NULL)
  720. DeleteObject(IconInfo.hbmColor);
  721. }
  722. // Select the memory bitmap into the memory DC
  723. if ((m_oldbitmap = (HBITMAP) SelectObject(m_hmemdc, m_membitmap)) == NULL)
  724. return;
  725. // Draw the cursor
  726. DrawIconEx(
  727. m_hmemdc, // handle to device context 
  728. CursorPos.x, CursorPos.y,
  729. m_hcursor, // handle to icon to draw 
  730. 0,0, // width of the icon 
  731. 0, // index of frame in animated cursor 
  732. NULL, // handle to background brush 
  733. DI_NORMAL | DI_COMPAT // icon-drawing flags 
  734. );
  735. // Select the old bitmap back into the memory DC
  736. SelectObject(m_hmemdc, m_oldbitmap);
  737. // Save the bounding rectangle
  738. m_cursorpos.left = CursorPos.x;
  739. m_cursorpos.top = CursorPos.y;
  740. m_cursorpos.right = CursorPos.x + GetSystemMetrics(SM_CXCURSOR);
  741. m_cursorpos.bottom = CursorPos.y + GetSystemMetrics(SM_CYCURSOR);
  742. // Clip the bounding rect to the screen
  743. RECT screen;
  744. screen.left=0;
  745. screen.top=0;
  746. screen.right=m_scrinfo.framebufferWidth;
  747. screen.bottom=m_scrinfo.framebufferHeight;
  748. // Copy the mouse cursor into the screen buffer, if any of it is visible
  749. if (IntersectRect(&m_cursorpos, &m_cursorpos, &screen))
  750. CopyToBuffer(m_cursorpos, scrBuff, scrBuffSize);
  751. }
  752. // Return the current mouse pointer position
  753. RECT
  754. vncDesktop::MouseRect()
  755. {
  756. return m_cursorpos;
  757. }
  758. void
  759. vncDesktop::SetCursor(HCURSOR cursor)
  760. {
  761. if (cursor == NULL)
  762. m_hcursor = m_hdefcursor;
  763. else
  764. m_hcursor = cursor;
  765. }
  766. // Manipulation of the clipboard
  767. void
  768. vncDesktop::SetClipText(LPSTR text)
  769. {
  770. // Open the system clipboard
  771. if (OpenClipboard(m_hwnd))
  772. {
  773. // Empty it
  774. if (EmptyClipboard())
  775. {
  776. HANDLE hMem = GlobalAlloc(GMEM_MOVEABLE | GMEM_DDESHARE, strlen(text)+1);
  777. if (hMem != NULL)
  778. {
  779. LPSTR pMem = (char*)GlobalLock(hMem);
  780. // Get the data
  781. strcpy(pMem, text);
  782. // Tell the clipboard
  783. GlobalUnlock(hMem);
  784. SetClipboardData(CF_TEXT, hMem);
  785. }
  786. }
  787. }
  788. // Now close it
  789. CloseClipboard();
  790. }
  791. // INTERNAL METHODS
  792. inline void
  793. vncDesktop::MaskToMaxAndShift(DWORD mask, CARD16 &max, CARD8 &shift)
  794. {
  795. for (shift = 0; (mask & 1) == 0; shift++)
  796. mask >>= 1;
  797. max = (CARD16) mask;
  798. }
  799. // Copy data from the memory bitmap into a buffer
  800. void
  801. vncDesktop::CopyToBuffer(RECT &rect, BYTE *destbuff, UINT destbuffsize)
  802. {
  803. // Protect the memory bitmap
  804. omni_mutex_lock l(m_bitbltlock);
  805. int y_inv;
  806. BYTE * destbuffpos;
  807. // Calculate the scanline-ordered y position to copy from
  808. y_inv = m_scrinfo.framebufferHeight-rect.top-(rect.bottom-rect.top);
  809. // Calculate where in the output buffer to put the data
  810. destbuffpos = destbuff + (m_bytesPerRow * rect.top);
  811. // Set the number of bytes for GetDIBits to actually write
  812. // NOTE : GetDIBits pads the destination buffer if biSizeImage < no. of bytes required
  813. m_bminfo.bmi.bmiHeader.biSizeImage = (rect.bottom-rect.top) * m_bytesPerRow;
  814. // Get the actual bits from the bitmap into the bit buffer
  815. if (GetDIBits(m_hmemdc, m_membitmap, y_inv,
  816. (rect.bottom-rect.top), destbuffpos,
  817. &m_bminfo.bmi, DIB_RGB_COLORS) == 0)
  818. {
  819. _RPT1(_CRT_WARN, "vncDesktop : [1] GetDIBits failed! %dn", GetLastError());
  820. _RPT3(_CRT_WARN, "vncDesktop : thread = %d, DC = %d, bitmap = %dn", omni_thread::self(), m_hmemdc, m_membitmap);
  821. _RPT2(_CRT_WARN, "vncDesktop : y = %d, height = %dn", y_inv, (rect.bottom-rect.top));
  822. }
  823. }
  824. // Callback routine used internally to catch window movement...
  825. BOOL CALLBACK
  826. EnumWindowsFn(HWND hwnd, LPARAM arg)
  827. {
  828. HANDLE prop = GetProp(hwnd, (LPCTSTR) MAKELONG(VNC_WINDOWPOS_ATOM, 0));
  829. if (prop != NULL)
  830. {
  831. if (IsWindowVisible(hwnd))
  832. {
  833. RECT dest;
  834. POINT source;
  835. // Get the window rectangle
  836. if (GetWindowRect(hwnd, &dest))
  837. {
  838. // Old position
  839. source.x = (SHORT) LOWORD(prop);
  840. source.y = (SHORT) HIWORD(prop);
  841. // Got the destination position.  Now send to clients!
  842. if ((source.x != dest.left) || (source.y != dest.top))
  843. {
  844. // Update the property entry
  845. SHORT x = dest.left;
  846. SHORT y = dest.top;
  847. SetProp(hwnd,
  848. (LPCTSTR) MAKELONG(VNC_WINDOWPOS_ATOM, 0),
  849. (HANDLE) MAKELONG(x, y));
  850. // Notify all clients of the copyrect
  851. ((vncServer*)arg)->CopyRect(dest, source);
  852. // Get the client to check for damage
  853. ((vncServer*)arg)->UpdateRect(dest);
  854. }
  855. }
  856. else
  857. RemoveProp(hwnd, (LPCTSTR) MAKELONG(VNC_WINDOWPOS_ATOM, 0));
  858. }
  859. else
  860. {
  861. RemoveProp(hwnd, (LPCTSTR) MAKELONG(VNC_WINDOWPOS_ATOM, 0));
  862. }
  863. }
  864. else
  865. {
  866. // If the window has become visible then save its position!
  867. if (IsWindowVisible(hwnd))
  868. {
  869. RECT dest;
  870. if (GetWindowRect(hwnd, &dest))
  871. {
  872. SHORT x = dest.left;
  873. SHORT y = dest.top;
  874. SetProp(hwnd,
  875. (LPCTSTR) MAKELONG(VNC_WINDOWPOS_ATOM, 0),
  876. (HANDLE) MAKELONG(x, y));
  877. }
  878. }
  879. }
  880. return TRUE;
  881. }
  882. // Routine to find out which windows have moved
  883. void
  884. vncDesktop::CalcCopyRects()
  885. {
  886. // Enumerate all the desktop windows for movement
  887. EnumWindows((WNDENUMPROC)EnumWindowsFn, (LPARAM) m_server);
  888. }
  889. // Window procedure for the Desktop window
  890. LRESULT CALLBACK
  891. DesktopWndProc(HWND hwnd, UINT iMsg, WPARAM wParam, LPARAM lParam)
  892. {
  893. vncDesktop *_this = (vncDesktop*)GetWindowLong(hwnd, GWL_USERDATA);
  894. switch (iMsg)
  895. {
  896. // GENERAL
  897. case WM_DISPLAYCHANGE:
  898. // The display resolution is changing
  899. // We must kick off any clients since their screen size will be wrong
  900. _this->m_displaychanged = TRUE;
  901. return 0;
  902. case WM_SYSCOLORCHANGE:
  903. case WM_PALETTECHANGED:
  904. // The palette colours have changed, so tell the server
  905. // Get the system palette
  906. if (!_this->SetPalette())
  907. PostQuitMessage(0);
  908. // Update any palette-based clients, too
  909. _this->m_server->UpdatePalette();
  910. return 0;
  911. // CLIPBOARD MESSAGES
  912. case WM_CHANGECBCHAIN:
  913. // The clipboard chain has changed - check our nextviewer handle
  914. if ((HWND)wParam == _this->m_hnextviewer)
  915. _this->m_hnextviewer = (HWND)lParam;
  916. else
  917. if (_this->m_hnextviewer != NULL)
  918. SendMessage(_this->m_hnextviewer,
  919. WM_CHANGECBCHAIN,
  920. wParam, lParam);
  921. return 0;
  922. case WM_DRAWCLIPBOARD:
  923. // The clipboard contents have changed
  924. if((GetClipboardOwner() != _this->Window()) &&
  925.     _this->m_initialClipBoardSeen)
  926. {
  927. LPSTR cliptext = NULL;
  928. // Open the clipboard
  929. if (OpenClipboard(_this->Window()))
  930. {
  931. // Get the clipboard data
  932. HGLOBAL cliphandle = GetClipboardData(CF_TEXT);
  933. if (cliphandle != NULL)
  934. {
  935. LPSTR clipdata = (LPSTR) GlobalLock(cliphandle);
  936. // Copy it into a new buffer
  937. if (clipdata == NULL)
  938. cliptext = NULL;
  939. else
  940. cliptext = strdup(clipdata);
  941. // Release the buffer and close the clipboard
  942. GlobalUnlock(cliphandle);
  943. }
  944. CloseClipboard();
  945. }
  946. if (cliptext != NULL)
  947. {
  948. int cliplen = strlen(cliptext);
  949. LPSTR unixtext = (char *)malloc(cliplen+1);
  950. // Replace CR-LF with LF - never send CR-LF on the wire,
  951. // since Unix won't like it
  952. int unixpos=0;
  953. for (int x=0; x<cliplen; x++)
  954. {
  955. if (cliptext[x] != 'x0d')
  956. {
  957. unixtext[unixpos] = cliptext[x];
  958. unixpos++;
  959. }
  960. }
  961. unixtext[unixpos] = 0;
  962. // Free the clip text
  963. free(cliptext);
  964. cliptext = NULL;
  965. // Now send the unix text to the server
  966. _this->m_server->UpdateClipText(unixtext);
  967. free(unixtext);
  968. }
  969. }
  970. _this->m_initialClipBoardSeen = TRUE;
  971. if (_this->m_hnextviewer != NULL)
  972. {
  973. // Pass the message to the next window in clipboard viewer chain.  
  974. return SendMessage(_this->m_hnextviewer, WM_DRAWCLIPBOARD, 0,0); 
  975. }
  976. return 0;
  977. default:
  978. return DefWindowProc(hwnd, iMsg, wParam, lParam);
  979. }
  980. }