vncDesktopDX.cpp
Upload User: sbftbdw
Upload Date: 2007-01-03
Package Size: 379k
Code Size: 30k
Category:

Remote Control

Development Platform:

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