1
// Copyright (C) 2001-2004 HorizonLive.com, Inc. All Rights Reserved.
2
// Copyright (C) 2001-2004 TightVNC Team. All Rights Reserved.
3
// Copyright (C) 1999 AT&T Laboratories Cambridge. All Rights Reserved.
5
// This file is part of the VNC system.
7
// The VNC system is free software; you can redistribute it and/or modify
8
// it under the terms of the GNU General Public License as published by
9
// the Free Software Foundation; either version 2 of the License, or
10
// (at your option) any later version.
12
// This program is distributed in the hope that it will be useful,
13
// but WITHOUT ANY WARRANTY; without even the implied warranty of
14
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15
// GNU General Public License for more details.
17
// You should have received a copy of the GNU General Public License
18
// along with this program; if not, write to the Free Software
19
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
22
// TightVNC distribution homepage on the Web: http://www.tightvnc.com/
24
// If the source code for the VNC system is not available from the place
25
// whence you received this file, check http://www.uk.research.att.com/vnc or contact
26
// the authors on vnc@uk.research.att.com for information on obtaining it.
28
// vncDesktop implementation
32
#include <omnithread.h>
36
#include "VNCHooks/VNCHooks.h"
37
#include "vncServer.h"
38
#include "vncRegion.h"
40
#include "vncDesktop.h"
41
#include "vncService.h"
42
#include "WallpaperUtils.h"
43
//#include "TsSessions.h"
52
const UINT RFB_SCREEN_UPDATE = RegisterWindowMessage("WinVNC.Update.DrawRect");
53
const UINT RFB_COPYRECT_UPDATE = RegisterWindowMessage("WinVNC.Update.CopyRect");
54
const UINT RFB_MOUSE_UPDATE = RegisterWindowMessage("WinVNC.Update.Mouse");
55
// Messages for blocking remote input events
56
const UINT RFB_LOCAL_KEYBOARD = RegisterWindowMessage("WinVNC.Local.Keyboard");
57
const UINT RFB_LOCAL_MOUSE = RegisterWindowMessage("WinVNC.Local.Mouse");
59
const char szDesktopSink[] = "WinVNC desktop sink";
62
const char *VNC_WINDOWPOS_ATOMNAME = "VNCHooks.CopyRect.WindowPos";
63
ATOM VNC_WINDOWPOS_ATOM = (ATOM) NULL;
65
// Static members to use with new polling algorithm
66
const int vncDesktop::m_pollingOrder[32] = {
67
0, 16, 8, 24, 4, 20, 12, 28,
68
10, 26, 18, 2, 22, 6, 30, 14,
69
1, 17, 9, 25, 7, 23, 15, 31,
70
19, 3, 27, 11, 29, 13, 5, 21
72
int vncDesktop::m_pollingStep = 0;
77
return vncService::IsWinNT();
80
BOOL IsWinVerOrHigher(ULONG mj, ULONG mn)
82
return vncService::VersionMajor() > mj ||
83
vncService::VersionMajor() == mj && vncService::VersionMinor() >= mn;
86
BOOL IsNtVer(ULONG mj, ULONG mn)
88
if (!vncService::IsWinNT())
90
return vncService::VersionMajor() == mj && vncService::VersionMinor() == mn;
93
BOOL vncDesktop::IsMultiMonDesktop()
95
if (!IsWinVerOrHigher(4, 10))
97
return GetSystemMetrics(SM_CMONITORS) > 1;
100
// The desktop handler thread
101
// This handles the messages posted by RFBLib to the vncDesktop window
103
class vncDesktopThread : public omni_thread
106
vncDesktopThread() { m_returnsig = NULL; }
108
~vncDesktopThread() { if (m_returnsig != NULL) delete m_returnsig; }
110
virtual BOOL Init(vncDesktop *desktop, vncServer *server);
111
virtual void *run_undetached(void *arg);
112
virtual void ReturnVal(BOOL result);
116
vncDesktop *m_desktop;
118
omni_mutex m_returnLock;
119
omni_condition *m_returnsig;
125
vncDesktopThread::Init(vncDesktop *desktop, vncServer *server)
127
// Save the server pointer
132
m_returnsig = new omni_condition(&m_returnLock);
137
// Wait for the thread to let us know if it failed to init
138
{ omni_mutex_lock l(m_returnLock);
150
vncDesktopThread::ReturnVal(BOOL result)
152
omni_mutex_lock l(m_returnLock);
156
m_returnsig->signal();
159
void *vncDesktopThread::run_undetached(void *arg)
161
// Save the thread's "home" desktop, under NT (no effect under 9x)
162
HDESK home_desktop = GetThreadDesktop(GetCurrentThreadId());
165
// Try to make session zero the console session
166
if (!inConsoleSession())
170
// Attempt to initialise and return success or failure
171
if (!m_desktop->Startup())
173
// vncDesktop::Startup might mave changed video mode in SetupDisplayForConnection.
174
// it has to be reverted then.
175
// TODO: review strong guarantee conditions for vncDesktop::Startup
176
m_desktop->ResetDisplayToNormal();
177
vncService::SelectHDESK(home_desktop);
182
RECT rect = m_desktop->GetSourceRect();
183
IntersectRect(&rect, &rect, &m_desktop->m_bmrect);
184
m_server->SetSharedRect(rect);
186
// Succeeded to initialise ok
189
// START PROCESSING DESKTOP MESSAGES
191
// We set a flag inside the desktop handler here, to indicate it's now safe
192
// to handle clipboard messages
193
m_desktop->SetClipboardActive(TRUE);
197
ULARGE_INTEGER now, droptime;
198
droptime.QuadPart = 0;
203
if (!PeekMessage(&msg, m_desktop->Window(), (UINT) NULL, (UINT) NULL, PM_REMOVE))
205
// Whenever the message queue becomes empty, we check to see whether
206
// there are updates to be passed to clients (first we make sure
207
// that scheduled wallpaper removal is complete).
208
if (!m_server->WallpaperWait()) {
209
if (!m_desktop->CheckUpdates())
213
// Now wait for more messages to be queued
216
vnclog.Print(LL_INTERR, VNCLOG("WaitMessage() failed\n"));
220
else if (msg.message == RFB_SCREEN_UPDATE)
222
// TODO: suppress this message from hook when driver is active
224
// An area of the screen has changed (ignore if we have a driver)
225
if (m_desktop->m_videodriver == NULL)
228
rect.left = (SHORT)LOWORD(msg.wParam);
229
rect.top = (SHORT)HIWORD(msg.wParam);
230
rect.right = (SHORT)LOWORD(msg.lParam);
231
rect.bottom = (SHORT)HIWORD(msg.lParam);
232
m_desktop->m_changed_rgn.AddRect(rect);
235
else if (msg.message == RFB_MOUSE_UPDATE)
237
// Save the cursor ID
238
m_desktop->SetCursor((HCURSOR) msg.wParam);
240
else if (msg.message == RFB_LOCAL_KEYBOARD)
242
// Block remote input events if necessary
243
if (vncService::IsWin95()) {
244
m_server->SetKeyboardCounter(-1);
245
if (m_server->KeyboardCounter() < 0) {
246
GetSystemTime(&systime);
247
SystemTimeToFileTime(&systime, &ftime);
248
droptime.LowPart = ftime.dwLowDateTime;
249
droptime.HighPart = ftime.dwHighDateTime;
250
droptime.QuadPart /= 10000000; // convert into seconds
251
m_server->BlockRemoteInput(true);
254
GetSystemTime(&systime);
255
SystemTimeToFileTime(&systime, &ftime);
256
droptime.LowPart = ftime.dwLowDateTime;
257
droptime.HighPart = ftime.dwHighDateTime;
258
droptime.QuadPart /= 10000000; // convert into seconds
259
m_server->BlockRemoteInput(true);
262
else if (msg.message == RFB_LOCAL_MOUSE)
264
// Block remote input events if necessary
265
if (vncService::IsWin95()) {
266
if (msg.wParam == WM_MOUSEMOVE) {
267
m_server->SetMouseCounter(-1, msg.pt, true);
269
m_server->SetMouseCounter(-1, msg.pt, false);
271
if (m_server->MouseCounter() < 0 && droptime.QuadPart == 0) {
272
GetSystemTime(&systime);
273
SystemTimeToFileTime(&systime, &ftime);
274
droptime.LowPart = ftime.dwLowDateTime;
275
droptime.HighPart = ftime.dwHighDateTime;
276
droptime.QuadPart /= 10000000; // convert into seconds
277
m_server->BlockRemoteInput(true);
280
GetSystemTime(&systime);
281
SystemTimeToFileTime(&systime, &ftime);
282
droptime.LowPart = ftime.dwLowDateTime;
283
droptime.HighPart = ftime.dwHighDateTime;
284
droptime.QuadPart /= 10000000; // convert into seconds
285
m_server->BlockRemoteInput(true);
288
else if (msg.message == WM_QUIT)
293
else if (msg.message == LS_QUIT)
295
// this is our custom quit message
296
vnclog.Print(LL_INTINFO, VNCLOG("Received LS_QUIT message.\n"));
302
// Process any other messages normally
303
DispatchMessage(&msg);
306
// Check timer to unblock remote input events if necessary
307
// FIXME: rewrite this stuff to eliminate code duplication (ses above).
308
// FIXME: Use time() instead of GetSystemTime().
309
// FIXME: It's not necessary to do this on receiving _each_ message.
310
if (m_server->LocalInputPriority() && droptime.QuadPart != 0) {
311
GetSystemTime(&systime);
312
SystemTimeToFileTime(&systime, &ftime);
313
now.LowPart = ftime.dwLowDateTime;
314
now.HighPart = ftime.dwHighDateTime;
315
now.QuadPart /= 10000000; // convert into seconds
317
if (now.QuadPart - m_server->DisableTime() >= droptime.QuadPart) {
318
m_server->BlockRemoteInput(false);
319
droptime.QuadPart = 0;
320
m_server->SetKeyboardCounter(0);
321
m_server->SetMouseCounter(0, msg.pt, false);
326
m_desktop->SetClipboardActive(FALSE);
328
vnclog.Print(LL_INTINFO, VNCLOG("quitting desktop server thread\n"));
330
// Clear all the hooks and close windows, etc.
331
m_desktop->Shutdown();
332
// Return display settings to previous values.
333
m_desktop->ResetDisplayToNormal();
334
// Turn on the screen.
335
m_desktop->BlankScreen(FALSE);
337
// Clear the shift modifier keys, now that there are no remote clients
338
vncKeymap::ClearShiftKeys();
340
// Switch back into our home desktop, under NT (no effect under 9x)
341
vncService::SelectHDESK(home_desktop);
346
// Implementation of the vncDesktop class
348
vncDesktop::vncDesktop()
353
m_polling_flag = FALSE;
355
m_timer_blank_screen = 0;
356
m_hnextviewer = NULL;
359
m_displaychanged = FALSE;
365
m_initialClipBoardSeen = FALSE;
367
// Vars for Will Dean's DIBsection patch
369
m_freemainbuff = FALSE;
370
m_formatmunged = FALSE;
374
m_clipboard_active = FALSE;
375
m_hooks_active = FALSE;
376
m_hooks_may_change = FALSE;
377
m_lpAlternateDevMode = NULL;
378
m_copyrect_set = FALSE;
380
m_videodriver = NULL;
382
m_timer_blank_screen = 0;
385
vncDesktop::~vncDesktop()
387
vnclog.Print(LL_INTINFO, VNCLOG("killing desktop server\n"));
389
// If we created a thread then here we delete it
390
// The thread itself does most of the cleanup
393
// Post a close message to quit our message handler thread
394
PostMessage(Window(), WM_QUIT, 0, 0);
396
// Join with the desktop handler thread
398
m_thread->join(&returnval);
402
// Let's call Shutdown just in case something went wrong...
404
_ASSERTE(!m_lpAlternateDevMode);
407
// Routine to startup and install all the hooks and stuff
409
vncDesktop::Startup()
412
// Currently, we just check whether we're in the console session, and
414
if (!inConsoleSession()) {
415
vnclog.Print(LL_INTERR, VNCLOG("Console is not session zero - reconnect to restore Console session"));
420
// Configure the display for optimal VNC performance.
421
SetupDisplayForConnection();
423
// Initialise the Desktop object
427
if (InitVideoDriver())
429
// this isn't really necessary
430
// InvalidateRect(NULL,NULL,TRUE);
436
if (!ThunkBitmapInfo())
442
if (!CreateBuffers())
454
// Add the system hook
456
m_hooks_may_change = true;
459
// Start up the keyboard and mouse filters
460
SetKeyboardFilterHook(m_server->LocalInputsDisabled());
461
SetMouseFilterHook(m_server->LocalInputsDisabled());
464
// Start up the keyboard and mouse hooks for
465
// local event priority over remote impl.
466
if (m_server->LocalInputPriority())
467
SetLocalInputPriorityHook(true);
469
// Start a timer to handle Polling Mode. The timer will cause
470
// an "idle" event, which is necessary if Polling Mode is being used,
471
// to cause TriggerUpdate to be called.
472
SetPollingFlag(FALSE);
475
// If necessary, start a separate timer to preserve the diplay turned off.
476
UpdateBlankScreenTimer();
478
// Get hold of the WindowPos atom!
479
if ((VNC_WINDOWPOS_ATOM = GlobalAddAtom(VNC_WINDOWPOS_ATOMNAME)) == 0) {
480
vnclog.Print(LL_INTERR, VNCLOG("GlobalAddAtom() failed.\n"));
484
// this member must be initialized: we cant assume the absence
485
// of clients when desktop is created.
486
m_cursorpos.left = 0;
488
m_cursorpos.right = 0;
489
m_cursorpos.bottom = 0;
491
// Everything is ok, so return TRUE
495
// Routine to shutdown all the hooks and stuff
496
BOOL vncDesktop::Shutdown()
498
// If we created timers then kill them
501
KillTimer(Window(), TIMER_POLL);
504
if (m_timer_blank_screen)
506
KillTimer(Window(), TIMER_BLANK_SCREEN);
507
m_timer_blank_screen = 0;
510
// If we created a window then kill it and the hooks
513
//Remove the system hooks
514
//Unset keyboard and mouse hooks
515
SetLocalInputPriorityHook(false);
516
m_hooks_may_change = false;
520
// Stop the keyboard and mouse filters
521
SetKeyboardFilterHook(false);
522
SetMouseFilterHook(false);
524
// The window is being closed - remove it from the viewer list
525
ChangeClipboardChain(m_hwnd, m_hnextviewer);
527
// Close the hook window
528
DestroyWindow(m_hwnd);
530
m_hnextviewer = NULL;
533
// Now free all the bitmap stuff
534
if (m_hrootdc != NULL)
536
// Release our device context
537
if(ReleaseDC(NULL, m_hrootdc) == 0)
539
vnclog.Print(LL_INTERR, VNCLOG("failed to ReleaseDC(m_hrootdc)\n"));
543
if (m_hmemdc != NULL)
545
// Release our device context
546
if (!DeleteDC(m_hmemdc))
548
vnclog.Print(LL_INTERR, VNCLOG("failed to DeleteDC(m_hmemdc)\n"));
552
if (m_membitmap != NULL)
554
// Release the custom bitmap, if any
555
if (!DeleteObject(m_membitmap))
557
vnclog.Print(LL_INTERR, VNCLOG("failed to DeleteObject\n"));
563
if (m_backbuff != NULL)
565
delete [] m_backbuff;
571
// Slow blits were enabled - free the slow blit buffer
572
if (m_mainbuff != NULL)
574
delete [] m_mainbuff;
579
// Free the WindowPos atom!
580
if (VNC_WINDOWPOS_ATOM != (ATOM) NULL)
582
if (GlobalDeleteAtom(VNC_WINDOWPOS_ATOM) != 0)
584
vnclog.Print(LL_INTERR, VNCLOG("failed to delete atom!\n"));
588
ShutdownVideoDriver();
593
// Routines to set/unset hooks via VNCHooks.dll
596
vncDesktop::ActivateHooks()
598
BOOL enable = !(m_server->DontSetHooks() && m_server->PollFullScreen());
599
if (enable && !m_hooks_active) {
600
m_hooks_active = SetHook(m_hwnd,
604
if (!m_hooks_active) {
605
vnclog.Print(LL_INTERR, VNCLOG("failed to set system hooks\n"));
606
// Switch on full screen polling, so they can see something, at least...
607
m_server->PollFullScreen(TRUE);
609
} else if (!enable) {
615
vncDesktop::ShutdownHooks()
618
m_hooks_active = !UnSetHook(m_hwnd);
622
vncDesktop::TryActivateHooks()
624
if (m_hooks_may_change)
628
// Routine to ensure we're on the correct NT desktop
631
vncDesktop::InitDesktop()
633
if (vncService::InputDesktopSelected())
636
// Ask for the current input desktop
637
return vncService::SelectDesktop(NULL);
640
// Routine used to close the screen saver, if it's active...
643
KillScreenSaverFunc(HWND hwnd, LPARAM lParam)
647
// - ONLY try to close Screen-saver windows!!!
648
if ((GetClassName(hwnd, buffer, 256) != 0) &&
649
(strcmp(buffer, "WindowsScreenSaverClass") == 0))
650
PostMessage(hwnd, WM_CLOSE, 0, 0);
655
vncDesktop::KillScreenSaver()
657
OSVERSIONINFO osversioninfo;
658
osversioninfo.dwOSVersionInfoSize = sizeof(osversioninfo);
660
// Get the current OS version
661
if (!GetVersionEx(&osversioninfo))
664
vnclog.Print(LL_INTINFO, VNCLOG("KillScreenSaver...\n"));
666
// How to kill the screen saver depends on the OS
667
switch (osversioninfo.dwPlatformId)
669
case VER_PLATFORM_WIN32_WINDOWS:
673
// Fidn the ScreenSaverClass window
674
HWND hsswnd = FindWindow ("WindowsScreenSaverClass", NULL);
676
PostMessage(hsswnd, WM_CLOSE, 0, 0);
679
case VER_PLATFORM_WIN32_NT:
683
// Find the screensaver desktop
684
HDESK hDesk = OpenDesktop(
688
DESKTOP_READOBJECTS | DESKTOP_WRITEOBJECTS
692
vnclog.Print(LL_INTINFO, VNCLOG("Killing ScreenSaver\n"));
694
// Close all windows on the screen saver desktop
695
EnumDesktopWindows(hDesk, (WNDENUMPROC) &KillScreenSaverFunc, 0);
697
// Pause long enough for the screen-saver to close
699
// Reset the screen saver so it can run again
700
SystemParametersInfo(SPI_SETSCREENSAVEACTIVE, (UINT) TRUE, 0, SPIF_SENDWININICHANGE);
707
void vncDesktop::ChangeResNow()
709
// IMPORTANT: Screen mode alteration may only take place on a single-mon system.
710
if (IsMultiMonDesktop())
715
BOOL settingsUpdated = false;
717
_ASSERTE(!m_lpAlternateDevMode);
718
m_lpAlternateDevMode = new DEVMODE; // *** create an instance of DEVMODE - Jeremy Peaks
719
if (!m_lpAlternateDevMode)
721
vnclog.Print(LL_INTINFO, VNCLOG("SCR-WBB: failed to allocate memory "
722
"for alternate DEVMODE representation!\n"));
726
// *** WBB - Obtain the current display settings.
728
if (! EnumDisplaySettings(0, ENUM_CURRENT_SETTINGS, m_lpAlternateDevMode))
730
vnclog.Print(LL_INTINFO,
731
VNCLOG("SCR-WBB: could not get current display settings!\n"));
732
delete m_lpAlternateDevMode;
733
m_lpAlternateDevMode = NULL;
738
vnclog.Print(LL_INTINFO,
739
VNCLOG("SCR-WBB: current display: w=%d h=%d bpp=%d vRfrsh=%d.\n"),
740
m_lpAlternateDevMode->dmPelsWidth,
741
m_lpAlternateDevMode->dmPelsHeight,
742
m_lpAlternateDevMode->dmBitsPerPel,
743
m_lpAlternateDevMode->dmDisplayFrequency);
745
origPelsWidth = m_lpAlternateDevMode->dmPelsWidth; // *** sets the original resolution for use later
746
origPelsHeight = m_lpAlternateDevMode->dmPelsHeight; // *** - Jeremy Peaks
748
// *** Open the registry key for resolution settings
749
HKEY checkdetails = 0;
750
RegOpenKeyEx(HKEY_LOCAL_MACHINE,
757
int slen=MAX_REG_ENTRY_LEN;
759
char inouttext[MAX_REG_ENTRY_LEN];
761
memset(inouttext, 0, MAX_REG_ENTRY_LEN);
763
// *** Get the registry values for resolution change - Jeremy Peaks
764
RegQueryValueEx(checkdetails,
772
if ((valType == REG_SZ) &&
773
atol(inouttext)) { // *** if width is 0, then this isn't a valid resolution, so do nothing - Jeremy Peaks
774
m_lpAlternateDevMode->dmPelsWidth = atol(inouttext);
776
memset(inouttext, 0, MAX_REG_ENTRY_LEN);
778
RegQueryValueEx(checkdetails,
785
m_lpAlternateDevMode->dmPelsHeight = atol(inouttext);
786
if ((valType == REG_SZ ) &&
787
(m_lpAlternateDevMode->dmPelsHeight > 0)) {
789
vnclog.Print(LL_INTINFO,
790
VNCLOG("SCR-WBB: attempting to change "
791
"resolution w=%d h=%d\n"),
792
m_lpAlternateDevMode->dmPelsWidth,
793
m_lpAlternateDevMode->dmPelsHeight);
795
// *** make res change - Jeremy Peaks
796
// testing: predefined Width/Height may become incompatible
797
// with new clrdepth/timings
798
long resultOfResChange = ChangeDisplaySettings(m_lpAlternateDevMode, CDS_TEST);
799
if (resultOfResChange == DISP_CHANGE_SUCCESSFUL) {
800
ChangeDisplaySettings(m_lpAlternateDevMode, CDS_UPDATEREGISTRY);
801
settingsUpdated = true;
806
RegCloseKey(checkdetails);
809
if (! settingsUpdated)
811
// Did not change the resolution.
812
delete m_lpAlternateDevMode;
813
m_lpAlternateDevMode = NULL;
818
vncDesktop::SetupDisplayForConnection()
822
ChangeResNow(); // *** - Jeremy Peaks
826
vncDesktop::ResetDisplayToNormal()
828
if (m_lpAlternateDevMode != NULL)
830
// *** In case the resolution was changed, revert to original settings now
831
m_lpAlternateDevMode->dmPelsWidth = origPelsWidth;
832
m_lpAlternateDevMode->dmPelsHeight = origPelsHeight;
834
long resultOfResChange = ChangeDisplaySettings(m_lpAlternateDevMode, CDS_TEST);
835
if (resultOfResChange == DISP_CHANGE_SUCCESSFUL)
836
ChangeDisplaySettings(m_lpAlternateDevMode, CDS_UPDATEREGISTRY);
838
delete m_lpAlternateDevMode;
839
m_lpAlternateDevMode = NULL;
843
RECT vncDesktop::GetSourceRect()
845
if (m_server->WindowShared())
848
GetWindowRect(m_server->GetWindowShared(), &wrect);
851
else if (m_server->ScreenAreaShared())
853
return m_server->GetScreenAreaRect();
855
else if (m_server->PrimaryDisplayOnlyShared())
857
RECT pdr = { 0, 0, GetSystemMetrics(SM_CXSCREEN), GetSystemMetrics(SM_CYSCREEN) };
864
_ASSERTE(GetSourceDisplayRect(rd));
865
_ASSERTE(EqualRect(&rd, &m_bmrect));
874
if (IsWinVerOrHigher(4, 10))
876
screenrect.left = GetSystemMetrics(SM_XVIRTUALSCREEN);
877
screenrect.top = GetSystemMetrics(SM_YVIRTUALSCREEN);
878
screenrect.right = screenrect.left + GetSystemMetrics(SM_CXVIRTUALSCREEN);
879
screenrect.bottom = screenrect.top + GetSystemMetrics(SM_CYVIRTUALSCREEN);
885
screenrect.right = GetSystemMetrics(SM_CXSCREEN);
886
screenrect.bottom = GetSystemMetrics(SM_CYSCREEN);
891
BOOL vncDesktop::GetSourceDisplayRect(RECT &rdisp_rect)
894
m_hrootdc = ::GetDC(NULL);
897
vnclog.Print(LL_INTERR, VNCLOG("GetDC() failed, error=%d\n"), GetLastError());
902
rdisp_rect = GetScreenRect();
906
BOOL vncDesktop::InitBitmap()
908
// IMPORTANT: here an optimization may be implemented
909
// when only a fixed rect is shared.
910
// then m_bmrect should be set to that rect.
911
if (!GetSourceDisplayRect(m_bmrect))
918
VNCLOG("source desktop metrics: (%d, %d, %d, %d)\n"),
926
VNCLOG("bitmap dimensions are %dx%d\n"),
927
m_bmrect.right - m_bmrect.left,
928
m_bmrect.bottom - m_bmrect.top);
930
// Create a compatible memory DC
931
m_hmemdc = CreateCompatibleDC(m_hrootdc);
932
if (m_hmemdc == NULL) {
933
vnclog.Print(LL_INTERR, VNCLOG("CreateCompatibleDC() failed, error=%d\n"),
938
// Check that the device capabilities are ok
939
if ((GetDeviceCaps(m_hrootdc, RASTERCAPS) & RC_BITBLT) == 0)
941
// FIXME: MessageBox in a service
944
"vncDesktop : root device doesn't support BitBlt\n"
945
"WinVNC cannot be used with this graphic device driver",
951
if ((GetDeviceCaps(m_hmemdc, RASTERCAPS) & RC_DI_BITMAP) == 0)
953
// FIXME: MessageBox in a service
956
"vncDesktop : memory device doesn't support GetDIBits\n"
957
"WinVNC cannot be used with this graphics device driver",
964
// Create the bitmap to be compatible with the ROOT DC!!!
965
m_membitmap = CreateCompatibleBitmap(
967
m_bmrect.right - m_bmrect.left,
968
m_bmrect.bottom - m_bmrect.top);
969
if (m_membitmap == NULL)
973
VNCLOG("failed to create memory bitmap, error=%d\n"),
977
vnclog.Print(LL_INTINFO, VNCLOG("created memory bitmap\n"));
979
// Get the bitmap's format and colour details
981
m_bminfo.bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
982
m_bminfo.bmi.bmiHeader.biBitCount = 0;
983
result = ::GetDIBits(m_hmemdc, m_membitmap, 0, 1, NULL, &m_bminfo.bmi, DIB_RGB_COLORS);
987
result = ::GetDIBits(m_hmemdc, m_membitmap, 0, 1, NULL, &m_bminfo.bmi, DIB_RGB_COLORS);
991
vnclog.Print(LL_INTINFO, VNCLOG("got bitmap format\n"));
992
vnclog.Print(LL_INTINFO, VNCLOG("DBG:display context has %d planes!\n"), GetDeviceCaps(m_hrootdc, PLANES));
993
vnclog.Print(LL_INTINFO, VNCLOG("DBG:memory context has %d planes!\n"), GetDeviceCaps(m_hmemdc, PLANES));
994
if (GetDeviceCaps(m_hmemdc, PLANES) != 1)
996
// FIXME: MessageBox in a service
999
"vncDesktop : current display is PLANAR, not CHUNKY!\n"
1000
"WinVNC cannot be used with this graphics device driver",
1007
// Henceforth we want to use a top-down scanning representation
1008
m_bminfo.bmi.bmiHeader.biHeight = - abs(m_bminfo.bmi.bmiHeader.biHeight);
1010
// Is the bitmap palette-based or truecolour?
1011
m_bminfo.truecolour = (GetDeviceCaps(m_hmemdc, RASTERCAPS) & RC_PALETTE) == 0;
1018
vncDesktop::ThunkBitmapInfo()
1020
// If we leave the pixel format intact, the blits can be optimised (Will Dean's patch)
1021
m_formatmunged = FALSE;
1023
// HACK ***. Optimised blits don't work with palette-based displays, yet
1024
if (!m_bminfo.truecolour) {
1025
m_formatmunged = TRUE;
1028
// Attempt to force the actual format into one we can handle
1029
// We can handle 8-bit-palette and 16/32-bit-truecolour modes
1030
switch (m_bminfo.bmi.bmiHeader.biBitCount)
1034
vnclog.Print(LL_INTINFO, VNCLOG("DBG:used/bits/planes/comp/size "
1035
"= %d/%d/%d/%d/%d\n"),
1036
(int)m_bminfo.bmi.bmiHeader.biClrUsed,
1037
(int)m_bminfo.bmi.bmiHeader.biBitCount,
1038
(int)m_bminfo.bmi.bmiHeader.biPlanes,
1039
(int)m_bminfo.bmi.bmiHeader.biCompression,
1040
(int)m_bminfo.bmi.bmiHeader.biSizeImage);
1042
// Correct the BITMAPINFO header to the format we actually want
1043
m_bminfo.bmi.bmiHeader.biClrUsed = 0;
1044
m_bminfo.bmi.bmiHeader.biPlanes = 1;
1045
m_bminfo.bmi.bmiHeader.biCompression = BI_RGB;
1046
m_bminfo.bmi.bmiHeader.biBitCount = 8;
1047
m_bminfo.bmi.bmiHeader.biSizeImage =
1048
abs((m_bminfo.bmi.bmiHeader.biWidth *
1049
m_bminfo.bmi.bmiHeader.biHeight *
1050
m_bminfo.bmi.bmiHeader.biBitCount)/ 8);
1051
m_bminfo.bmi.bmiHeader.biClrImportant = 0;
1052
m_bminfo.truecolour = FALSE;
1054
// Display format is non-VNC compatible - use the slow blit method
1055
m_formatmunged = TRUE;
1058
// Update the bitmapinfo header
1059
m_bminfo.bmi.bmiHeader.biBitCount = 32;
1060
m_bminfo.bmi.bmiHeader.biPlanes = 1;
1061
m_bminfo.bmi.bmiHeader.biCompression = BI_RGB;
1062
m_bminfo.bmi.bmiHeader.biSizeImage =
1063
abs((m_bminfo.bmi.bmiHeader.biWidth *
1064
m_bminfo.bmi.bmiHeader.biHeight *
1065
m_bminfo.bmi.bmiHeader.biBitCount)/ 8);
1066
// Display format is non-VNC compatible - use the slow blit method
1067
m_formatmunged = TRUE;
1075
vncDesktop::SetPixFormat()
1077
// Examine the bitmapinfo structure to obtain the current pixel format
1078
m_scrinfo.format.trueColour = m_bminfo.truecolour;
1079
m_scrinfo.format.bigEndian = 0;
1081
// Set up the native buffer width, height and format
1082
m_scrinfo.framebufferWidth = (CARD16) (m_bmrect.right - m_bmrect.left); // Swap endian before actually sending
1083
m_scrinfo.framebufferHeight = (CARD16) (m_bmrect.bottom - m_bmrect.top); // Swap endian before actually sending
1084
m_scrinfo.format.bitsPerPixel = (CARD8) m_bminfo.bmi.bmiHeader.biBitCount;
1085
m_scrinfo.format.depth = (CARD8) m_bminfo.bmi.bmiHeader.biBitCount;
1088
// Calculate the number of bytes per row
1089
m_bytesPerRow = m_scrinfo.framebufferWidth * m_scrinfo.format.bitsPerPixel / 8;
1095
vncDesktop::SetPixShifts()
1097
// Sort out the colour shifts, etc.
1098
DWORD redMask=0, blueMask=0, greenMask = 0;
1100
switch (m_bminfo.bmi.bmiHeader.biBitCount)
1103
if (m_videodriver&& m_videodriver->IsDirectAccessInEffect())
1105
// IMPORTANT: Mirage colormask is always 565
1110
else if (m_bminfo.bmi.bmiHeader.biCompression == BI_RGB)
1112
// Standard 16-bit display
1113
// each word single pixel 5-5-5
1114
redMask = 0x7c00; greenMask = 0x03e0; blueMask = 0x001f;
1118
if (m_bminfo.bmi.bmiHeader.biCompression == BI_BITFIELDS)
1120
redMask = *(DWORD *) &m_bminfo.bmi.bmiColors[0];
1121
greenMask = *(DWORD *) &m_bminfo.bmi.bmiColors[1];
1122
blueMask = *(DWORD *) &m_bminfo.bmi.bmiColors[2];
1128
// Standard 24/32 bit displays
1129
if (m_bminfo.bmi.bmiHeader.biCompression == BI_RGB ||
1130
m_videodriver && m_videodriver->IsDirectAccessInEffect())
1136
// The real color depth is 24 bits in this case. If the depth
1137
// is set to 32, the Tight encoder shows worse performance.
1138
m_scrinfo.format.depth = 24;
1142
if (m_bminfo.bmi.bmiHeader.biCompression == BI_BITFIELDS)
1144
redMask = *(DWORD *) &m_bminfo.bmi.bmiColors[0];
1145
greenMask = *(DWORD *) &m_bminfo.bmi.bmiColors[1];
1146
blueMask = *(DWORD *) &m_bminfo.bmi.bmiColors[2];
1152
// Other pixel formats are only valid if they're palette-based
1153
if (m_bminfo.truecolour)
1155
vnclog.Print(LL_INTERR, "unsupported truecolour pixel format for SetPixShifts()\n");
1158
vnclog.Print(LL_INTINFO, VNCLOG("DBG:palette-based desktop in SetPixShifts()\n"));
1162
// Convert the data we just retrieved
1163
MaskToMaxAndShift(redMask, m_scrinfo.format.redMax, m_scrinfo.format.redShift);
1164
MaskToMaxAndShift(greenMask, m_scrinfo.format.greenMax, m_scrinfo.format.greenShift);
1165
MaskToMaxAndShift(blueMask, m_scrinfo.format.blueMax, m_scrinfo.format.blueShift);
1167
vnclog.Print(LL_INTINFO, VNCLOG("DBG:true-color desktop in SetPixShifts()\n"));
1172
vncDesktop::SetPalette()
1174
// Lock the current display palette into the memory DC we're holding
1175
// *** CHECK THIS FOR LEAKS!
1176
if (!m_bminfo.truecolour)
1178
LOGPALETTE *palette;
1179
UINT size = sizeof(LOGPALETTE) + (sizeof(PALETTEENTRY) * 256);
1181
palette = (LOGPALETTE *) new char[size];
1182
if (palette == NULL) {
1183
vnclog.Print(LL_INTERR, VNCLOG("error allocating palette\n"));
1187
// Initialise the structure
1188
palette->palVersion = 0x300;
1189
palette->palNumEntries = 256;
1191
// Get the system colours
1192
if (GetSystemPaletteEntries(m_hrootdc, 0, 256, palette->palPalEntry) == 0)
1194
vnclog.Print(LL_INTERR, VNCLOG("GetSystemPaletteEntries() failed.\n"));
1199
// Create a palette from those
1200
HPALETTE pal = CreatePalette(palette);
1203
vnclog.Print(LL_INTERR, VNCLOG("CreatePalette() failed.\n"));
1208
// Select the palette into our memory DC
1209
HPALETTE oldpalette = SelectPalette(m_hmemdc, pal, FALSE);
1210
if (oldpalette == NULL)
1212
vnclog.Print(LL_INTERR, VNCLOG("SelectPalette() failed.\n"));
1218
// Worked, so realise the palette
1219
if (RealizePalette(m_hmemdc) == GDI_ERROR)
1220
vnclog.Print(LL_INTWARN, VNCLOG("warning - failed to RealizePalette\n"));
1224
DeleteObject(oldpalette);
1226
vnclog.Print(LL_INTINFO, VNCLOG("initialised palette OK\n"));
1230
// Not a palette based local screen - forget it!
1231
vnclog.Print(LL_INTINFO, VNCLOG("no palette data for truecolour display\n"));
1235
LRESULT CALLBACK DesktopWndProc(HWND hwnd, UINT iMsg, WPARAM wParam, LPARAM lParam);
1237
ATOM m_wndClass = 0;
1240
vncDesktop::InitWindow()
1242
if (m_wndClass == 0) {
1243
// Create the window class
1244
WNDCLASSEX wndclass;
1246
wndclass.cbSize = sizeof(wndclass);
1248
wndclass.lpfnWndProc = &DesktopWndProc;
1249
wndclass.cbClsExtra = 0;
1250
wndclass.cbWndExtra = 0;
1251
wndclass.hInstance = hAppInstance;
1252
wndclass.hIcon = NULL;
1253
wndclass.hCursor = NULL;
1254
wndclass.hbrBackground = (HBRUSH) GetStockObject(WHITE_BRUSH);
1255
wndclass.lpszMenuName = (const char *) NULL;
1256
wndclass.lpszClassName = szDesktopSink;
1257
wndclass.hIconSm = NULL;
1260
m_wndClass = RegisterClassEx(&wndclass);
1263
// And create a window
1264
m_hwnd = CreateWindow(szDesktopSink,
1266
WS_OVERLAPPEDWINDOW,
1275
if (m_hwnd == NULL) {
1276
vnclog.Print(LL_INTERR, VNCLOG("CreateWindow() failed.\n"));
1280
// Set the "this" pointer for the window
1281
SetWindowLong(m_hwnd, GWL_USERDATA, (long)this);
1283
// Enable clipboard hooking
1284
m_hnextviewer = SetClipboardViewer(m_hwnd);
1290
vncDesktop::CreateBuffers()
1292
vnclog.Print(LL_INTINFO, VNCLOG("attempting to create main and back buffers\n"));
1294
// Create a new DIB section ***
1295
HBITMAP tempbitmap = NULL;
1296
if (!m_formatmunged)
1298
tempbitmap = CreateDIBSection(
1305
if (tempbitmap == NULL)
1307
vnclog.Print(LL_INTWARN, VNCLOG("failed to build DIB section - reverting to slow blits\n"));
1311
m_freemainbuff = false;
1313
// NOTE m_mainbuff and m_backbuff allocation can not be supressed
1314
// even with direct access mirror surface view
1316
if (tempbitmap == NULL)
1319
// create our own buffer to copy blits through
1320
if ((m_mainbuff = new BYTE [ScreenBuffSize()]) == NULL) {
1321
vnclog.Print(LL_INTERR, VNCLOG("unable to allocate main buffer[%d]\n"), ScreenBuffSize());
1324
m_freemainbuff = true;
1325
if ((m_backbuff = new BYTE [ScreenBuffSize()]) == NULL) {
1326
vnclog.Print(LL_INTERR, VNCLOG("unable to allocate back buffer[%d]\n"), ScreenBuffSize());
1332
// Create our own buffer to copy blits through
1333
if ((m_backbuff = new BYTE [ScreenBuffSize()]) == NULL) {
1334
vnclog.Print(LL_INTERR, VNCLOG("unable to allocate back buffer[%d]\n"), ScreenBuffSize());
1335
if (tempbitmap!= NULL)
1336
DeleteObject(tempbitmap);
1340
// Delete the old memory bitmap
1341
if (m_membitmap != NULL) {
1342
DeleteObject(m_membitmap);
1346
// Replace old membitmap with DIB section
1347
m_membitmap = tempbitmap;
1348
m_mainbuff = (BYTE *)m_DIBbits;
1349
vnclog.Print(LL_INTINFO, VNCLOG("enabled fast DIBsection blits OK\n"));
1354
vncDesktop::Init(vncServer *server)
1356
vnclog.Print(LL_INTINFO, VNCLOG("initialising desktop server\n"));
1358
// Save the server pointer
1361
// Load in the arrow cursor
1362
m_hdefcursor = LoadCursor(NULL, IDC_ARROW);
1363
m_hcursor = m_hdefcursor;
1365
// Spawn a thread to handle that window's message queue
1366
vncDesktopThread *thread = new vncDesktopThread;
1370
return thread->Init(this, m_server);
1374
vncDesktop::RequestUpdate()
1376
PostMessage(m_hwnd, WM_TIMER, TIMER_POLL, 0);
1380
vncDesktop::ScreenBuffSize()
1382
return m_scrinfo.format.bitsPerPixel/8 *
1383
m_scrinfo.framebufferWidth *
1384
m_scrinfo.framebufferHeight;
1388
vncDesktop::FillDisplayInfo(rfbServerInitMsg *scrinfo)
1390
memcpy(scrinfo, &m_scrinfo, sz_rfbServerInitMsg);
1393
// Function to capture an area of the screen immediately prior to sending
1396
void vncDesktop::CaptureScreen(const RECT &UpdateArea, BYTE *scrBuff)
1398
// ASSUME rect related to virtual desktop
1399
if (m_videodriver && m_videodriver->IsDirectAccessInEffect())
1400
CaptureScreenFromMirage(UpdateArea, scrBuff);
1401
else CaptureScreenFromAdapterGeneral(UpdateArea, scrBuff);
1404
void vncDesktop::CaptureScreenFromAdapterGeneral(RECT rect, BYTE *scrBuff)
1406
// ASSUME rect related to virtual desktop
1407
// Protect the memory bitmap
1408
omni_mutex_lock l(m_bitbltlock);
1410
// Finish drawing anything in this thread
1411
// Wish we could do this for the whole system - maybe we should
1412
// do something with LockWindowUpdate here.
1415
// Select the memory bitmap into the memory DC
1417
if ((oldbitmap = (HBITMAP) SelectObject(m_hmemdc, m_membitmap)) == NULL)
1420
// Capture screen into bitmap
1421
BOOL blitok = BitBlt(
1423
// source in m_hrootdc is relative to a virtual desktop,
1424
// whereas dst coordinates of m_hmemdc are relative to its top-left corner (0, 0)
1425
rect.left - m_bmrect.left,
1426
rect.top - m_bmrect.top,
1427
rect.right - rect.left,
1428
rect.bottom - rect.top,
1430
rect.left, rect.top,
1433
// Select the old bitmap back into the memory DC
1434
SelectObject(m_hmemdc, oldbitmap);
1438
// Copy the new data to the screen buffer (CopyToBuffer optimises this if possible)
1439
CopyToBuffer(rect, scrBuff);
1444
void vncDesktop::CaptureScreenFromMirage(RECT UpdateArea, BYTE *scrBuff)
1446
// ASSUME rect related to virtual desktop
1447
_ASSERTE(m_videodriver);
1448
omni_mutex_lock l(m_bitbltlock);
1449
CopyToBuffer(UpdateArea, scrBuff, m_videodriver->GetScreenView());
1452
void vncDesktop::CaptureMouseRect()
1457
// If the mouse cursor handle is invalid then forget it
1458
if (m_hcursor == NULL)
1461
// Get the cursor position
1462
if (!GetCursorPos(&CursorPos))
1465
// Translate position for hotspot
1466
if (GetIconInfo(m_hcursor, &IconInfo))
1468
CursorPos.x -= ((int) IconInfo.xHotspot);
1469
CursorPos.y -= ((int) IconInfo.yHotspot);
1471
if (IconInfo.hbmMask != NULL)
1472
DeleteObject(IconInfo.hbmMask);
1473
if (IconInfo.hbmColor != NULL)
1474
DeleteObject(IconInfo.hbmColor);
1477
// Save the bounding rectangle
1478
m_cursorpos.left = CursorPos.x;
1479
m_cursorpos.top = CursorPos.y;
1480
m_cursorpos.right = CursorPos.x + GetSystemMetrics(SM_CXCURSOR);
1481
m_cursorpos.bottom = CursorPos.y + GetSystemMetrics(SM_CYCURSOR);
1484
// Add the mouse pointer to the buffer
1485
void vncDesktop::CaptureMouse(BYTE *scrBuff, UINT scrBuffSize)
1487
// Protect the memory bitmap
1488
omni_mutex_lock l(m_bitbltlock);
1492
// Select the memory bitmap into the memory DC
1494
if ((oldbitmap = (HBITMAP) SelectObject(m_hmemdc, m_membitmap)) == NULL)
1499
m_hmemdc, // handle to device context
1500
m_cursorpos.left - m_bmrect.left,
1501
m_cursorpos.top - m_bmrect.top,
1502
m_hcursor, // handle to icon to draw
1503
0,0, // width of the icon
1504
0, // index of frame in animated cursor
1505
NULL, // handle to background brush
1506
DI_NORMAL // icon-drawing flags
1509
// Select the old bitmap back into the memory DC
1510
SelectObject(m_hmemdc, oldbitmap);
1512
// Clip the bounding rect to the screen
1513
RECT screen = m_server->GetSharedRect();
1514
// Copy the mouse cursor into the screen buffer, if any of it is visible
1515
if (IntersectRect(&m_cursorpos, &m_cursorpos, &screen))
1516
CopyToBuffer(m_cursorpos, scrBuff);
1519
// Obtain cursor image data in server's local format.
1520
// The length of databuf[] should be at least (width * height * 4).
1522
vncDesktop::GetRichCursorData(BYTE *databuf, HCURSOR hcursor, int width, int height)
1524
// Protect the memory bitmap (is it really necessary here?)
1525
omni_mutex_lock l(m_bitbltlock);
1527
// Create bitmap, select it into memory DC
1528
HBITMAP membitmap = CreateCompatibleBitmap(m_hrootdc, width, height);
1529
if (membitmap == NULL) {
1532
HBITMAP oldbitmap = (HBITMAP) SelectObject(m_hmemdc, membitmap);
1533
if (oldbitmap == NULL) {
1534
DeleteObject(membitmap);
1539
DrawIconEx(m_hmemdc, 0, 0, hcursor, 0, 0, 0, NULL, DI_IMAGE);
1540
SelectObject(m_hmemdc, oldbitmap);
1542
// Prepare BITMAPINFO structure (copy most m_bminfo fields)
1543
BITMAPINFO *bmi = (BITMAPINFO *)calloc(1, sizeof(BITMAPINFO) + 256 * sizeof(RGBQUAD));
1544
memcpy(bmi, &m_bminfo.bmi, sizeof(BITMAPINFO) + 256 * sizeof(RGBQUAD));
1545
bmi->bmiHeader.biWidth = width;
1546
bmi->bmiHeader.biHeight = -height;
1548
// Clear data buffer and extract RGB data
1549
memset(databuf, 0x00, width * height * 4);
1550
int lines = GetDIBits(m_hmemdc, membitmap, 0, height, databuf, bmi, DIB_RGB_COLORS);
1554
DeleteObject(membitmap);
1556
return (lines != 0);
1559
// Return the current mouse pointer position
1561
vncDesktop::MouseRect()
1566
void vncDesktop::SetCursor(HCURSOR cursor)
1569
m_hcursor = m_hdefcursor;
1575
// DEBUG: Some visualization for LF->CRLF conversion code.
1578
static void ShowClipText(char *caption, char *text)
1580
int len = strlen(text);
1581
char *out_text = new char[len * 4 + 8];
1584
out_text[pos++] = '"';
1585
for (int i = 0; i < len; i++) {
1586
if (text[i] == '\r') {
1587
strcpy(&out_text[pos], "\\r");
1589
} else if (text[i] == '\n') {
1590
strcpy(&out_text[pos], "\\n");
1592
} else if (text[i] < ' ') {
1593
strcpy(&out_text[pos], "\\?");
1596
out_text[pos++] = text[i];
1599
out_text[pos++] = '"';
1600
out_text[pos++] = '\0';
1602
MessageBox(NULL, out_text, caption, MB_OK);
1609
// Convert text from Unix (LF only) format to CR+LF.
1610
// NOTE: The size of dst[] buffer must be at least (strlen(src) * 2 + 1).
1614
vncDesktop::ConvertClipText(char *dst, const char *src)
1616
const char *ptr0 = src;
1620
while ((ptr1 = strchr(ptr0, '\n')) != NULL) {
1621
// Copy the string before the LF
1623
memcpy(&dst[dst_pos], ptr0, ptr1 - ptr0);
1624
dst_pos += ptr1 - ptr0;
1626
// Don't insert CR if there is one already
1627
if (ptr1 == ptr0 || *(ptr1 - 1) != '\r') {
1628
dst[dst_pos++] = '\r';
1631
dst[dst_pos++] = '\n';
1632
// Next position in the source text
1635
// Copy the last string with no LF, but with '\0'
1636
memcpy(&dst[dst_pos], ptr0, &src[strlen(src)] - ptr0 + 1);
1640
// Manipulation of the clipboard
1644
vncDesktop::SetClipText(LPSTR text)
1646
// Open the system clipboard
1647
if (OpenClipboard(m_hwnd))
1650
if (EmptyClipboard())
1652
HANDLE hMem = GlobalAlloc(GMEM_MOVEABLE | GMEM_DDESHARE,
1653
strlen(text) * 2 + 1);
1657
LPSTR pMem = (char*)GlobalLock(hMem);
1659
// Get the data (with line endings converted to CR+LF)
1660
ConvertClipText(pMem, text);
1662
// Tell the clipboard
1664
SetClipboardData(CF_TEXT, hMem);
1676
vncDesktop::MaskToMaxAndShift(DWORD mask, CARD16 &max, CARD8 &shift)
1678
for (shift = 0; (mask & 1) == 0; shift++)
1680
max = (CARD16) mask;
1683
// Copy data from the memory bitmap into a buffer
1684
void vncDesktop::CopyToBuffer(RECT rect, BYTE *destbuff)
1686
// Are we being asked to blit from the DIBsection to itself?
1687
if (destbuff == m_DIBbits)
1689
// Yes. Ignore the request!
1693
// Protect the memory bitmap
1694
omni_mutex_lock l(m_bitbltlock);
1696
const int crect_re_vd_left = rect.left - m_bmrect.left;
1697
const int crect_re_vd_top = rect.top - m_bmrect.top;
1698
_ASSERTE(crect_re_vd_left >= 0);
1699
_ASSERTE(crect_re_vd_top >= 0);
1701
// Calculate the scanline-ordered y position to copy from
1702
// NB: m_membitmap is bottom2top
1703
const int y_inv_re_vd = m_bmrect.bottom - m_bmrect.top - rect.bottom;
1704
_ASSERTE(y_inv_re_vd >= 0);
1706
// Calculate where in the output buffer to put the data
1707
BYTE * destbuffpos = destbuff + (m_bytesPerRow * crect_re_vd_top);
1709
// Set the number of bytes for GetDIBits to actually write
1710
// NOTE : GetDIBits pads the destination buffer if biSizeImage < no. of bytes required
1711
m_bminfo.bmi.bmiHeader.biSizeImage = (rect.bottom-rect.top) * m_bytesPerRow;
1713
// Get the actual bits from the bitmap into the bit buffer
1714
// If fast (DIBsection) blits are disabled then use the old GetDIBits technique
1715
if (m_DIBbits == NULL)
1721
rect.bottom - rect.top,
1724
DIB_RGB_COLORS) == 0)
1727
_RPT1(_CRT_WARN, "vncDesktop : [1] GetDIBits failed! %d\n", GetLastError());
1728
_RPT3(_CRT_WARN, "vncDesktop : thread = %d, DC = %d, bitmap = %d\n", omni_thread::self(), m_hmemdc, m_membitmap);
1729
_RPT2(_CRT_WARN, "vncDesktop : y = %d, height = %d\n", y_inv_re_vd, (rect.bottom-rect.top));
1735
// Fast blits are enabled. [I have a sneaking suspicion this will never get used, unless
1736
// something weird goes wrong in the code. It's here to keep the function general, though!]
1738
const int bytesPerPixel = m_scrinfo.format.bitsPerPixel / 8;
1739
BYTE *srcbuffpos = (BYTE*)m_DIBbits;
1741
srcbuffpos += (m_bytesPerRow * crect_re_vd_top) + (bytesPerPixel * crect_re_vd_left);
1742
destbuffpos += bytesPerPixel * crect_re_vd_left;
1744
const int widthBytes = (rect.right - rect.left) * bytesPerPixel;
1746
for (int y = rect.top; y < rect.bottom; y++)
1748
memcpy(destbuffpos, srcbuffpos, widthBytes);
1749
srcbuffpos += m_bytesPerRow;
1750
destbuffpos += m_bytesPerRow;
1755
void vncDesktop::CopyToBuffer(RECT rect, BYTE *destbuff, const BYTE *srcbuffpos)
1757
const int crect_re_vd_left = rect.left - m_bmrect.left;
1758
const int crect_re_vd_top = rect.top - m_bmrect.top;
1759
_ASSERTE(crect_re_vd_left >= 0);
1760
_ASSERTE(crect_re_vd_top >= 0);
1762
const int bytesPerPixel = m_scrinfo.format.bitsPerPixel / 8;
1764
const int bmoffset = (m_bytesPerRow * crect_re_vd_top) + (bytesPerPixel * crect_re_vd_left);
1765
BYTE *destbuffpos = destbuff + bmoffset;
1766
srcbuffpos += bmoffset;
1768
const int widthBytes = (rect.right - rect.left) * bytesPerPixel;
1770
for (int y = rect.top; y < rect.bottom; y++)
1772
memcpy(destbuffpos, srcbuffpos, widthBytes);
1773
srcbuffpos += m_bytesPerRow;
1774
destbuffpos += m_bytesPerRow;
1778
// Callback routine used internally to catch window movement...
1780
EnumWindowsFnCopyRect(HWND hwnd, LPARAM arg)
1783
//For excluding the popup windows
1784
if ((GetWindowLong( hwnd, GWL_STYLE) & WS_POPUP) ==0)
1787
HANDLE prop = GetProp(hwnd, (LPCTSTR) MAKELONG(VNC_WINDOWPOS_ATOM, 0));
1790
if (IsWindowVisible(hwnd)) {
1795
// Get the window rectangle
1796
if (GetWindowRect(hwnd, &dest)) {
1798
source.x = (SHORT) LOWORD(prop);
1799
source.y = (SHORT) HIWORD(prop);
1801
// Got the destination position. Now send to clients!
1802
if ((source.x != dest.left) || (source.y != dest.top)) {
1803
// Update the property entry
1804
SHORT x = (SHORT) dest.left;
1805
SHORT y = (SHORT) dest.top;
1807
(LPCTSTR) MAKELONG(VNC_WINDOWPOS_ATOM, 0),
1808
(HANDLE) MAKELONG(x, y));
1810
// Store of the copyrect
1811
((vncDesktop*)arg)->CopyRect(dest, source);
1815
RemoveProp(hwnd, (LPCTSTR) MAKELONG(VNC_WINDOWPOS_ATOM, 0));
1818
RemoveProp(hwnd, (LPCTSTR) MAKELONG(VNC_WINDOWPOS_ATOM, 0));
1821
// If the window has become visible then save its position!
1822
if (IsWindowVisible(hwnd)) {
1825
if (GetWindowRect(hwnd, &dest)) {
1826
SHORT x = (SHORT) dest.left;
1827
SHORT y = (SHORT) dest.top;
1829
(LPCTSTR) MAKELONG(VNC_WINDOWPOS_ATOM, 0),
1830
(HANDLE) MAKELONG(x, y));
1840
vncDesktop::SetLocalInputDisableHook(BOOL enable)
1842
SetKeyboardFilterHook(enable);
1843
SetMouseFilterHook(enable);
1847
vncDesktop::SetLocalInputPriorityHook(BOOL enable)
1849
if (vncService::IsWin95()) {
1850
SetKeyboardPriorityHook(m_hwnd,enable,RFB_LOCAL_KEYBOARD);
1851
SetMousePriorityHook(m_hwnd,enable,RFB_LOCAL_MOUSE);
1853
SetKeyboardPriorityLLHook(m_hwnd,enable,RFB_LOCAL_KEYBOARD);
1854
SetMousePriorityLLHook(m_hwnd,enable,RFB_LOCAL_MOUSE);
1858
// FIXME: incremental semantics broken here;
1859
// that's why we're compelled to consume extra unlocks
1860
m_server->BlockRemoteInput(false);
1863
// Routine to find out which windows have moved
1865
vncDesktop::CalcCopyRects()
1867
// Enumerate all the desktop windows for movement
1868
EnumWindows((WNDENUMPROC)EnumWindowsFnCopyRect, (LPARAM) this);
1872
// Window procedure for the Desktop window
1874
DesktopWndProc(HWND hwnd, UINT iMsg, WPARAM wParam, LPARAM lParam)
1876
vncDesktop *_this = (vncDesktop*)GetWindowLong(hwnd, GWL_USERDATA);
1883
case WM_DISPLAYCHANGE:
1884
// The display resolution is changing
1886
// We must kick off any clients since their screen size will be wrong
1887
_this->m_displaychanged = TRUE;
1890
case WM_SYSCOLORCHANGE:
1891
case WM_PALETTECHANGED:
1892
// The palette colours have changed, so tell the server
1894
// Get the system palette
1895
if (!_this->SetPalette())
1897
// Update any palette-based clients, too
1898
_this->m_server->UpdatePalette();
1903
case vncDesktop::TIMER_POLL:
1904
_this->SetPollingFlag(true);
1906
case vncDesktop::TIMER_BLANK_SCREEN:
1907
if (_this->m_server->GetBlankScreen())
1908
_this->BlankScreen(TRUE);
1910
case vncDesktop::TIMER_RESTORE_SCREEN:
1911
_this->BlankScreen(FALSE);
1916
// CLIPBOARD MESSAGES
1918
case WM_CHANGECBCHAIN:
1919
// The clipboard chain has changed - check our nextviewer handle
1920
if ((HWND)wParam == _this->m_hnextviewer)
1921
_this->m_hnextviewer = (HWND)lParam;
1923
if (_this->m_hnextviewer != NULL)
1924
SendMessage(_this->m_hnextviewer,
1930
case WM_DRAWCLIPBOARD:
1931
// The clipboard contents have changed
1932
if((GetClipboardOwner() != _this->Window()) &&
1933
_this->m_initialClipBoardSeen &&
1934
_this->m_clipboard_active)
1936
LPSTR cliptext = NULL;
1938
// Open the clipboard
1939
if (OpenClipboard(_this->Window()))
1941
// Get the clipboard data
1942
HGLOBAL cliphandle = GetClipboardData(CF_TEXT);
1943
if (cliphandle != NULL)
1945
LPSTR clipdata = (LPSTR) GlobalLock(cliphandle);
1947
// Copy it into a new buffer
1948
if (clipdata == NULL)
1951
cliptext = strdup(clipdata);
1953
// Release the buffer and close the clipboard
1954
GlobalUnlock(cliphandle);
1960
if (cliptext != NULL)
1962
int cliplen = strlen(cliptext);
1963
LPSTR unixtext = (char *)malloc(cliplen+1);
1965
// Replace CR-LF with LF - never send CR-LF on the wire,
1966
// since Unix won't like it
1968
for (int x=0; x<cliplen; x++)
1970
if (cliptext[x] != '\x0d')
1972
unixtext[unixpos] = cliptext[x];
1976
unixtext[unixpos] = 0;
1978
// Free the clip text
1982
// Now send the unix text to the server
1983
_this->m_server->UpdateClipText(unixtext);
1989
_this->m_initialClipBoardSeen = TRUE;
1991
if (_this->m_hnextviewer != NULL)
1993
// Pass the message to the next window in clipboard viewer chain.
1994
return SendMessage(_this->m_hnextviewer, WM_DRAWCLIPBOARD, 0,0);
2000
return DefWindowProc(hwnd, iMsg, wParam, lParam);
2004
BOOL vncDesktop::CheckUpdates()
2010
// Re-install polling timer if necessary
2011
if (m_server->PollingCycleChanged())
2014
m_server->PollingCycleChanged(false);
2017
// Update the state of blank screen timer
2018
UpdateBlankScreenTimer();
2020
// Has the display resolution or desktop changed?
2021
if (m_displaychanged || !vncService::InputDesktopSelected() /*|| !inConsoleSession()*/)
2023
vnclog.Print(LL_STATE, VNCLOG("display resolution or desktop changed.\n"));
2025
rfbServerInitMsg oldscrinfo = m_scrinfo;
2026
m_displaychanged = FALSE;
2028
// Attempt to close the old hooks
2031
vnclog.Print(LL_INTERR, VNCLOG("failed to close desktop server.\n"));
2032
m_server->KillAuthClients();
2036
// Now attempt to re-install them!
2041
vnclog.Print(LL_INTERR, VNCLOG("failed to re-start desktop server.\n"));
2042
m_server->KillAuthClients();
2046
// Check if the screen info has changed
2047
vnclog.Print(LL_INTINFO,
2048
VNCLOG("SCR: old screen format %dx%dx%d\n"),
2049
oldscrinfo.framebufferWidth,
2050
oldscrinfo.framebufferHeight,
2051
oldscrinfo.format.bitsPerPixel);
2052
vnclog.Print(LL_INTINFO,
2053
VNCLOG("SCR: new screen format %dx%dx%d\n"),
2054
m_scrinfo.framebufferWidth,
2055
m_scrinfo.framebufferHeight,
2056
m_scrinfo.format.bitsPerPixel);
2057
if (memcmp(&m_scrinfo, &oldscrinfo, sizeof(oldscrinfo)) != 0)
2059
vnclog.Print(LL_INTINFO, VNCLOG("screen format has changed.\n"));
2062
// Call this regardless of screen format change
2063
m_server->UpdateLocalFormat();
2065
// Add a full screen update to all the clients
2066
m_changed_rgn.AddRect(m_bmrect);
2067
m_server->UpdatePalette();
2070
// TRIGGER THE UPDATE
2072
RECT rect = m_server->GetSharedRect();
2073
RECT new_rect = GetSourceRect();
2074
IntersectRect(&new_rect, &new_rect, &m_bmrect);
2076
// Update screen size if required
2077
if (!EqualRect(&new_rect, &rect))
2079
m_server->SetSharedRect(new_rect);
2080
bool sendnewfb = false;
2082
if (rect.right - rect.left != new_rect.right - new_rect.left ||
2083
rect.bottom - rect.top != new_rect.bottom - new_rect.top)
2086
// FIXME: We should not send NewFBSize if a client
2087
// did not send framebuffer update request.
2088
m_server->SetNewFBSize(sendnewfb);
2090
m_changed_rgn.Clear();
2092
if (sendnewfb && m_server->WindowShared())
2094
if (new_rect.right - new_rect.left == 0 &&
2095
new_rect.bottom - new_rect.top == 0)
2097
// window is minimized
2102
// window is restored
2103
// window is resized
2104
m_changed_rgn.AddRect(new_rect);
2113
// If we have clients full region requests
2114
if (m_server->FullRgnRequested())
2116
// Capture screen to main buffer
2117
CaptureScreen(rect, m_mainbuff);
2118
// If we have a video driver - reset counter
2119
if ( m_videodriver != NULL && m_videodriver->IsActive())
2121
m_videodriver->ResetCounter();
2125
// DEBUG: Continue auditing the code from this point.
2127
// If we have incremental update requests
2128
if (m_server->IncrRgnRequested())
2132
// Use either a mirror video driver, or perform polling
2133
if (m_videodriver != NULL && m_videodriver->IsActive())
2135
// FIXME: If there were no incremental update requests
2136
// for some time, we will loose updates.
2137
// IMPORTANT: Mirage outputs the regions re (0, 0)
2138
// so we have to offset them re virtual display
2141
BOOL bCursorShape = FALSE;
2143
m_videodriver->HandleDriverChanges(
2152
if (GetPollingFlag())
2154
SetPollingFlag(false);
2159
// Check for moved windows
2160
// PrimaryDisplayOnlyShared: check if any problems when
2161
// dragging from another display
2162
if ((m_server->FullScreen() || m_server->PrimaryDisplayOnlyShared()) &&
2163
!(m_videodriver && m_videodriver->IsHandlingScreen2ScreenBlt()))
2170
// Send copyrect to all clients
2171
m_server->CopyRect(m_copyrect_rect, m_copyrect_src);
2172
m_copyrect_set = false;
2174
// IMPORTANT: this order: CopyRectToBuffer, CaptureScreen, GetChangedRegion
2175
// Copy old window rect to back buffer
2176
CopyRectToBuffer(m_copyrect_rect, m_copyrect_src);
2178
// Copy new window rect to main buffer
2179
CaptureScreen(m_copyrect_rect, m_mainbuff);
2181
// Get changed pixels to rg
2182
GetChangedRegion(rgn, m_copyrect_rect);
2185
rect.left= m_copyrect_src.x;
2186
rect.top = m_copyrect_src.y;
2187
rect.right = rect.left + (m_copyrect_rect.right - m_copyrect_rect.left);
2188
rect.bottom = rect.top + (m_copyrect_rect.bottom - m_copyrect_rect.top);
2189
// Refresh old window rect
2190
m_changed_rgn.AddRect(rect);
2191
// Don't refresh new window rect
2192
m_changed_rgn.SubtractRect(m_copyrect_rect);
2195
// Get only desktop area
2198
temprgn.AddRect(rect);
2199
m_changed_rgn.Intersect(temprgn);
2201
// Get list of rectangles for checking
2202
rectlist rectsToScan;
2203
m_changed_rgn.Rectangles(rectsToScan);
2205
// Capture and check them
2206
CheckRects(rgn, rectsToScan);
2209
m_server->UpdateMouse();
2211
// Send changed region data to all clients
2212
m_server->UpdateRegion(rgn);
2214
// Clear changed region
2215
m_changed_rgn.Clear();
2218
// Trigger an update to be sent
2219
if (m_server->FullRgnRequested() || m_server->IncrRgnRequested())
2221
m_server->TriggerUpdate();
2228
vnclog.Print(LL_INTERR, VNCLOG("vncDesktop::CheckUpdates caught an exception.\n"));
2229
m_server->KillAuthClients();
2238
vncDesktop::SetPollingTimer()
2240
const UINT driverCycle = 30;
2241
const UINT minPollingCycle = 5;
2244
if (m_videodriver != NULL) {
2247
msec = m_server->GetPollingCycle() / 16;
2248
if (msec < minPollingCycle) {
2249
msec = minPollingCycle;
2252
m_timer_polling = SetTimer(Window(), TIMER_POLL, msec, NULL);
2255
inline void vncDesktop::CheckRects(vncRegion &rgn, rectlist &rects)
2261
rectlist::iterator i;
2263
for (i = rects.begin(); i != rects.end(); i++)
2265
// Copy data to the main buffer
2266
// FIXME: Maybe call CaptureScreen() just once?
2267
// Check what would be more efficient.
2268
CaptureScreen(*i, m_mainbuff);
2270
// Check for changes in the rectangle
2271
GetChangedRegion(rgn, *i);
2277
vnclog.Print(LL_INTERR, VNCLOG("vncDesktop::CheckRects caught an exception.\n"));
2284
// This notably improves performance when using Visual C++ 6.0 compiler
2285
#pragma function(memcpy, memcmp)
2288
static const int BLOCK_SIZE = 32;
2290
// created for troubleshoot purposes;
2291
// when GetChangedRegion_Normal et al are suspected for bugs/need changes.
2292
// the code below is as simple and clear as possible
2293
void vncDesktop::GetChangedRegion_Dummy(vncRegion &rgn, const RECT &rect)
2297
// Copy the changes to the back buffer
2298
const int c2rect_re_vd_top = rect.top - m_bmrect.top;
2299
const int c3rect_re_vd_left = rect.left - m_bmrect.left;
2300
_ASSERTE(c2rect_re_vd_top >= 0);
2301
_ASSERTE(c3rect_re_vd_left >= 0);
2303
const UINT bytesPerPixel = m_scrinfo.format.bitsPerPixel / 8;
2304
const int offset = c2rect_re_vd_top * m_bytesPerRow + c3rect_re_vd_left * bytesPerPixel;
2306
unsigned char *o_ptr = m_backbuff + offset;
2307
unsigned char *n_ptr = m_mainbuff + offset;
2308
const int bytes_in_row = (rect.right - rect.left) * bytesPerPixel;
2309
for (int y = rect.top; y < rect.bottom; y++)
2311
memcpy(o_ptr, n_ptr, bytes_in_row);
2312
n_ptr += m_bytesPerRow;
2313
o_ptr += m_bytesPerRow;
2317
void vncDesktop::GetChangedRegion_Normal(vncRegion &rgn, const RECT &rect)
2319
const UINT bytesPerPixel = m_scrinfo.format.bitsPerPixel / 8;
2320
const int bytes_per_scanline = (rect.right - rect.left) * bytesPerPixel;
2322
const int crect_re_vd_left = rect.left - m_bmrect.left;
2323
const int crect_re_vd_top = rect.top - m_bmrect.top;
2324
_ASSERTE(crect_re_vd_left >= 0);
2325
_ASSERTE(crect_re_vd_top >= 0);
2327
const int offset = crect_re_vd_top * m_bytesPerRow + crect_re_vd_left * bytesPerPixel;
2328
unsigned char *o_ptr = m_backbuff + offset;
2329
unsigned char *n_ptr = m_mainbuff + offset;
2331
RECT new_rect = rect;
2333
// Fast processing for small rectangles
2334
if (rect.right - rect.left <= BLOCK_SIZE &&
2335
rect.bottom - rect.top <= BLOCK_SIZE)
2337
for (int y = rect.top; y < rect.bottom; y++)
2339
if (memcmp(o_ptr, n_ptr, bytes_per_scanline) != 0)
2342
UpdateChangedSubRect(rgn, new_rect);
2345
o_ptr += m_bytesPerRow;
2346
n_ptr += m_bytesPerRow;
2351
// Process bigger rectangles
2352
BOOL bTop4Move = TRUE;
2353
for (int y = rect.top; y < rect.bottom; y++)
2355
if (memcmp(o_ptr, n_ptr, bytes_per_scanline) != 0)
2362
// Skip a number of lines after a non-matched one
2363
int n = BLOCK_SIZE / 2 - 1;
2365
o_ptr += n * m_bytesPerRow;
2366
n_ptr += n * m_bytesPerRow;
2372
new_rect.bottom = y;
2373
UpdateChangedRect(rgn, new_rect);
2377
o_ptr += m_bytesPerRow;
2378
n_ptr += m_bytesPerRow;
2382
new_rect.bottom = rect.bottom;
2383
UpdateChangedRect(rgn, new_rect);
2387
void vncDesktop::UpdateChangedRect(vncRegion &rgn, const RECT &rect)
2389
// Pass small rectangles directly to UpdateChangedSubRect
2390
if (rect.right - rect.left <= BLOCK_SIZE &&
2391
rect.bottom - rect.top <= BLOCK_SIZE)
2393
UpdateChangedSubRect(rgn, rect);
2397
const UINT bytesPerPixel = m_scrinfo.format.bitsPerPixel / 8;
2402
const int crect_re_vd_left = rect.left - m_bmrect.left;
2403
const int crect_re_vd_top = rect.top - m_bmrect.top;
2404
_ASSERTE(crect_re_vd_left >= 0);
2405
_ASSERTE(crect_re_vd_top >= 0);
2407
// Scan down the rectangle
2408
const int offset = crect_re_vd_top * m_bytesPerRow + crect_re_vd_left * bytesPerPixel;
2409
unsigned char *o_topleft_ptr = m_backbuff + offset;
2410
unsigned char *n_topleft_ptr = m_mainbuff + offset;
2412
for (y = rect.top; y < rect.bottom; y += BLOCK_SIZE)
2414
// Work out way down the bitmap
2415
unsigned char *o_row_ptr = o_topleft_ptr;
2416
unsigned char *n_row_ptr = n_topleft_ptr;
2418
const int blockbottom = Min(y + BLOCK_SIZE, rect.bottom);
2419
new_rect.bottom = blockbottom;
2421
BOOL bLeft4Move = TRUE;
2423
for (x = rect.left; x < rect.right; x += BLOCK_SIZE)
2425
// Work our way across the row
2426
unsigned char *n_block_ptr = n_row_ptr;
2427
unsigned char *o_block_ptr = o_row_ptr;
2429
const UINT blockright = Min(x + BLOCK_SIZE, rect.right);
2430
const UINT bytesPerBlockRow = (blockright-x) * bytesPerPixel;
2433
for (ay = y; ay < blockbottom; ay++)
2435
if (memcmp(n_block_ptr, o_block_ptr, bytesPerBlockRow) != 0)
2437
n_block_ptr += m_bytesPerRow;
2438
o_block_ptr += m_bytesPerRow;
2440
if (ay < blockbottom)
2442
// There were changes, so this block will need to be updated
2449
else if (ay < new_rect.top)
2456
// No changes in this block, process previous changed blocks if any
2460
UpdateChangedSubRect(rgn, new_rect);
2465
o_row_ptr += bytesPerBlockRow;
2466
n_row_ptr += bytesPerBlockRow;
2471
new_rect.right = rect.right;
2472
UpdateChangedSubRect(rgn, new_rect);
2475
o_topleft_ptr += m_bytesPerRow * BLOCK_SIZE;
2476
n_topleft_ptr += m_bytesPerRow * BLOCK_SIZE;
2480
void vncDesktop::UpdateChangedSubRect(vncRegion &rgn, const RECT &rect)
2482
const UINT bytesPerPixel = m_scrinfo.format.bitsPerPixel / 8;
2483
int bytes_in_row = (rect.right - rect.left) * bytesPerPixel;
2486
const int crect_re_vd_left = rect.left - m_bmrect.left;
2487
const int crect_re_vd_bottom = rect.bottom - m_bmrect.top;
2488
_ASSERTE(crect_re_vd_left >= 0);
2489
_ASSERTE(crect_re_vd_bottom >= 0);
2491
// Exclude unchanged scan lines at the bottom
2492
int offset = (crect_re_vd_bottom - 1) * m_bytesPerRow + crect_re_vd_left * bytesPerPixel;
2493
unsigned char *o_ptr = m_backbuff + offset;
2494
unsigned char *n_ptr = m_mainbuff + offset;
2495
RECT final_rect = rect;
2496
final_rect.bottom = rect.top + 1;
2497
for (y = rect.bottom - 1; y > rect.top; y--)
2499
if (memcmp(o_ptr, n_ptr, bytes_in_row) != 0)
2501
final_rect.bottom = y + 1;
2504
n_ptr -= m_bytesPerRow;
2505
o_ptr -= m_bytesPerRow;
2508
// Exclude unchanged pixels at left and right sides
2509
const int c2rect_re_vd_left = final_rect.left - m_bmrect.left;
2510
const int c2rect_re_vd_top = final_rect.top - m_bmrect.top;
2511
_ASSERTE(c2rect_re_vd_left >= 0);
2512
_ASSERTE(c2rect_re_vd_top >= 0);
2514
offset = c2rect_re_vd_top * m_bytesPerRow + c2rect_re_vd_left * bytesPerPixel;
2515
o_ptr = m_backbuff + offset;
2516
n_ptr = m_mainbuff + offset;
2517
int left_delta = bytes_in_row - 1;
2518
int right_delta = 0;
2519
for (y = final_rect.top; y < final_rect.bottom; y++)
2521
for (i = 0; i < bytes_in_row - 1; i++)
2523
if (n_ptr[i] != o_ptr[i])
2530
for (i = bytes_in_row - 1; i > 0; i--)
2532
if (n_ptr[i] != o_ptr[i])
2534
if (i > right_delta)
2539
n_ptr += m_bytesPerRow;
2540
o_ptr += m_bytesPerRow;
2542
final_rect.right = final_rect.left + right_delta / bytesPerPixel + 1;
2543
final_rect.left += left_delta / bytesPerPixel;
2545
// Update the rectangle
2546
rgn.AddRect(final_rect);
2548
// Copy the changes to the back buffer
2549
const int c3rect_re_vd_left = final_rect.left - m_bmrect.left;
2550
_ASSERTE(c3rect_re_vd_left >= 0);
2552
offset = c2rect_re_vd_top * m_bytesPerRow + c3rect_re_vd_left * bytesPerPixel;
2554
o_ptr = m_backbuff + offset;
2555
n_ptr = m_mainbuff + offset;
2556
bytes_in_row = (final_rect.right - final_rect.left) * bytesPerPixel;
2557
for (y = final_rect.top; y < final_rect.bottom; y++)
2559
memcpy(o_ptr, n_ptr, bytes_in_row);
2560
n_ptr += m_bytesPerRow;
2561
o_ptr += m_bytesPerRow;
2566
void vncDesktop::PerformPolling()
2568
if (m_server->PollFullScreen())
2571
RECT full_rect = m_server->GetSharedRect();
2572
PollArea(full_rect);
2577
if (m_server->PollForeground())
2579
// Get the window rectangle for the currently selected window
2580
HWND hwnd = GetForegroundWindow();
2584
if (m_server->PollUnderCursor())
2586
// Find the mouse position
2588
if (GetCursorPos(&mousepos))
2590
// Find the window under the mouse
2591
HWND hwnd = WindowFromPoint(mousepos);
2600
vncDesktop::PollWindow(HWND hwnd)
2602
// Are we set to low-load polling?
2603
if (m_server->PollOnEventOnly())
2605
// Yes, so only poll if the remote user has done something
2606
if (!m_server->RemoteEventReceived()) {
2611
// Does the client want us to poll only console windows?
2612
if (m_server->PollConsoleOnly())
2616
// Yes, so check that this is a console window...
2617
if (GetClassName(hwnd, classname, sizeof(classname))) {
2618
if ((strcmp(classname, "tty") != 0) &&
2619
(strcmp(classname, "ConsoleWindowClass") != 0)) {
2625
RECT full_rect = m_server->GetSharedRect();
2628
// Get the rectangle
2629
if (GetWindowRect(hwnd, &rect)) {
2630
if (IntersectRect(&rect, &rect, &full_rect)) {
2637
// Implementation of the polling algorithm.
2640
void vncDesktop::PollArea(const RECT &rect)
2642
const int scanLine = m_pollingOrder[m_pollingStep++ % 32];
2643
const UINT bytesPerPixel = m_scrinfo.format.bitsPerPixel / 8;
2645
// Align 32x32 tiles to the left top corner of the shared area
2646
const RECT shared = m_server->GetSharedRect();
2647
const int leftAligned = ((rect.left - shared.left) & 0xFFFFFFE0) + shared.left;
2648
const int topAligned = ((rect.top - shared.top) & 0xFFFFFFE0) + shared.top;
2650
RECT rowRect = rect; // we'll need left and right borders
2652
for (int y = topAligned; y < rect.bottom; y += 32)
2654
const int tile_h = Min(rect.bottom - y, 32);
2655
// TODO: refactor it
2657
// window captions suffer an arbitrary scanline...
2658
if (y == topAligned)
2660
sl = Min(sl, tile_h-1);
2661
const int scan_y = y + sl;
2663
_ASSERTE(scan_y >= rect.top);
2664
_ASSERTE(scan_y < rect.bottom);
2666
rowRect.top = scan_y;
2667
rowRect.bottom = scan_y + 1;
2668
CaptureScreen(rowRect, m_mainbuff);
2669
const int offset = (scan_y-m_bmrect.top) * m_bytesPerRow + (leftAligned-m_bmrect.left) * bytesPerPixel;
2670
const unsigned char *o_ptr = m_backbuff + offset;
2671
const unsigned char *n_ptr = m_mainbuff + offset;
2672
for (int x = leftAligned; x < rect.right; x += 32)
2674
const int tile_w = Min(rect.right - x, 32);
2675
const int nBytes = tile_w * bytesPerPixel;
2676
if (memcmp(o_ptr, n_ptr, nBytes) != 0)
2681
tileRect.right = x + tile_w;
2682
tileRect.bottom = y + tile_h;
2683
m_changed_rgn.AddRect(tileRect);
2691
inline RECT MoveRect(RECT const& sr, POINT const& mv)
2694
R.left = sr.left + mv.x;
2695
R.top = sr.top + mv.y;
2696
R.right = sr.right + mv.x;
2697
R.bottom = sr.bottom + mv.y;
2701
void vncDesktop::CopyRect(RECT const &rcDest, POINT ptSrc)
2705
mv2.x = rcDest.left - ptSrc.x;
2706
mv2.y = rcDest.top - ptSrc.y;
2708
// Clip the destination to the screen
2710
const RECT r = m_server->GetSharedRect();
2711
if (!IntersectRect(&rcDr2, &rcDest, &r))
2714
// NOTE: this is important.
2715
// each pixel in rcDr2 is either salvaged by copyrect
2717
m_changed_rgn.AddRect(rcDr2);
2719
// Adjust the source correspondingly
2720
ptSrc.x = rcDr2.left - mv2.x;
2721
ptSrc.y = rcDr2.top - mv2.y;
2723
// Work out the source rectangle
2725
rcSource.left = ptSrc.x;
2726
rcSource.top = ptSrc.y;
2727
rcSource.right = rcSource.left + rcDr2.right - rcDr2.left;
2728
rcSource.bottom = rcSource.top + rcDr2.bottom - rcDr2.top;
2730
// Clip the source to the screen
2732
if (!IntersectRect(&rcSr2, &rcSource, &r))
2735
rcDr2 = MoveRect(rcSr2, mv2);
2737
// we'd try to continue the chain
2740
// prev motion vector
2742
mv1.x = m_copyrect_rect.left - m_copyrect_src.x;
2743
mv1.y = m_copyrect_rect.top - m_copyrect_src.y;
2745
m_changed_rgn.AddRect(m_copyrect_rect);
2748
if (!IntersectRect(&CR1i2Dst, &m_copyrect_rect, &rcSr2))
2750
m_copyrect_set = FALSE;
2754
RECT rcDr3 = MoveRect(CR1i2Dst, mv2);
2755
if (rcDr3.right - rcDr3.left >= 16 &&
2756
rcDr3.bottom - rcDr3.top >= 16)
2758
m_changed_rgn.SubtractRect(rcDr3);
2761
ptCR1i2Src.x = CR1i2Dst.left - mv1.x;
2762
ptCR1i2Src.y = CR1i2Dst.top - mv1.y;
2764
m_copyrect_rect = rcDr3;
2765
m_copyrect_src = ptCR1i2Src;
2767
//DPF(("CopyRect-cont: (%d, %d) (%d, %d, %d, %d)\n",
2768
// m_copyrect_src.x,
2769
// m_copyrect_src.y,
2770
// m_copyrect_rect.left,
2771
// m_copyrect_rect.top,
2772
// m_copyrect_rect.right,
2773
// m_copyrect_rect.bottom));
2777
m_copyrect_set = FALSE;
2782
if (rcDr2.right - rcDr2.left >= 16 &&
2783
rcDr2.bottom - rcDr2.top >= 16)
2785
m_changed_rgn.SubtractRect(rcDr2);
2787
m_copyrect_rect = rcDr2;
2788
m_copyrect_src.x = rcSr2.left;
2789
m_copyrect_src.y = rcSr2.top;
2790
m_copyrect_set = TRUE;
2792
//DPF(("CopyRect: (%d, %d) (%d, %d, %d, %d)\n",
2793
// m_copyrect_src.x,
2794
// m_copyrect_src.y,
2795
// m_copyrect_rect.left,
2796
// m_copyrect_rect.top,
2797
// m_copyrect_rect.right,
2798
// m_copyrect_rect.bottom));
2803
void vncDesktop::CopyRectToBuffer(RECT dest, POINT source)
2805
const int ptsrc_re_vd_x = source.x - m_bmrect.left;
2806
const int ptsrc_re_vd_y = source.y - m_bmrect.top;
2807
_ASSERTE(ptsrc_re_vd_x >= 0);
2808
_ASSERTE(ptsrc_re_vd_y >= 0);
2810
// Copy the data from one region of the back-buffer to another!
2811
BYTE *srcptr = m_mainbuff + (ptsrc_re_vd_y * m_bytesPerRow) + (ptsrc_re_vd_x * m_scrinfo.format.bitsPerPixel/8);
2813
const int rcdest_re_vd_left = dest.left - m_bmrect.left;
2814
const int rcdest_re_vd_top = dest.top - m_bmrect.top;
2815
_ASSERTE(rcdest_re_vd_left >= 0);
2816
_ASSERTE(rcdest_re_vd_top >= 0);
2818
BYTE *destptr = m_backbuff + (rcdest_re_vd_top * m_bytesPerRow) + (rcdest_re_vd_left * m_scrinfo.format.bitsPerPixel/8);
2819
const UINT bytesPerLine = (dest.right - dest.left) * (m_scrinfo.format.bitsPerPixel/8);
2821
if (dest.top < source.y)
2823
for (int y = dest.top; y < dest.bottom; y++)
2825
memmove(destptr, srcptr, bytesPerLine);
2826
srcptr += m_bytesPerRow;
2827
destptr += m_bytesPerRow;
2832
srcptr += (m_bytesPerRow * ((dest.bottom - dest.top) - 1));
2833
destptr += (m_bytesPerRow * ((dest.bottom - dest.top) - 1));
2834
for (int y = dest.bottom; y > dest.top; y--)
2836
memmove(destptr, srcptr, bytesPerLine);
2837
srcptr -= m_bytesPerRow;
2838
destptr -= m_bytesPerRow;
2843
BOOL IsBadDirectAccessConfig()
2845
if (IsWinVerOrHigher(5, 1))
2847
if (GetSystemMetrics(SM_XVIRTUALSCREEN) < 0)
2849
if (GetSystemMetrics(SM_YVIRTUALSCREEN) < 0)
2855
BOOL vncDesktop::InitVideoDriver()
2857
// Mirror video drivers supported under Win2K, WinXP, WinVista
2858
// and Windows NT 4.0 SP3 (we assume SP6).
2859
if (!vncService::IsWinNT())
2862
// FIXME: Windows NT 4.0 support is broken and thus we disable it here.
2863
if (!IsWinVerOrHigher(5, 0))
2866
if (m_server->DontUseDriver())
2868
vnclog.Print(LL_STATE, VNCLOG("not activating video driver interface\n"));
2872
BOOL bIsBadDASDConfig = IsBadDirectAccessConfig();
2873
if (bIsBadDASDConfig)
2875
vnclog.Print(LL_INTINFO, VNCLOG("can't set direct access mode in this configuration of monitors due to a known Windows bug.\n"));
2878
BOOL bSolicitDASD = m_server->DriverDirectAccess() & !bIsBadDASDConfig;
2880
_ASSERTE(!m_videodriver);
2881
m_videodriver = new vncVideoDriver;
2884
vnclog.Print(LL_INTERR, VNCLOG("failed to create vncVideoDriver object\n"));
2888
if (IsWinVerOrHigher(5, 0))
2890
// restart the driver if left running.
2891
// NOTE that on NT4 it must be running beforehand
2892
if (m_videodriver->TestMapped())
2894
vnclog.Print(LL_INTINFO, VNCLOG("found abandoned Mirage driver running. restarting.\n"));
2895
m_videodriver->Deactivate();
2897
_ASSERTE(!m_videodriver->TestMapped());
2902
GetSourceDisplayRect(vdesk_rect);
2903
(BOOL) m_videodriver->Activate(bSolicitDASD, &vdesk_rect);
2906
if (!m_videodriver->CheckVersion())
2908
vnclog.Print(LL_INTINFO, VNCLOG("******** PLEASE INSTALL NEWER VERSION OF MIRAGE DRIVER! ********\n"));
2909
// IMPORTANT: fail on NT46
2914
if (m_videodriver->MapSharedbuffers(bSolicitDASD))
2916
vnclog.Print(LL_INTINFO, VNCLOG("video driver interface activated\n"));
2920
delete m_videodriver;
2921
m_videodriver = NULL;
2922
vnclog.Print(LL_INTERR, VNCLOG("failed to activate video driver interface\n"));
2925
_ASSERTE(bSolicitDASD == m_videodriver->IsDirectAccessInEffect());
2929
void vncDesktop::ShutdownVideoDriver()
2931
if (m_videodriver == NULL)
2933
delete m_videodriver;
2934
m_videodriver = NULL;
2935
vnclog.Print(LL_INTINFO, VNCLOG("video driver interface deactivated\n"));
2939
vncDesktop::UpdateBlankScreenTimer()
2941
BOOL active = m_server->GetBlankScreen();
2942
if (active && !m_timer_blank_screen) {
2943
m_timer_blank_screen = SetTimer(Window(), TIMER_BLANK_SCREEN, 50, NULL);
2944
} else if (!active && m_timer_blank_screen) {
2945
KillTimer(Window(), TIMER_BLANK_SCREEN);
2946
m_timer_blank_screen = 0;
2947
PostMessage(m_hwnd, WM_TIMER, TIMER_RESTORE_SCREEN, 0);
2952
vncDesktop::BlankScreen(BOOL set)
2955
SystemParametersInfo(SPI_SETPOWEROFFACTIVE, 1, NULL, 0);
2956
SendMessage(GetDesktopWindow(), WM_SYSCOMMAND, SC_MONITORPOWER, (LPARAM)2);
2958
SystemParametersInfo(SPI_SETPOWEROFFACTIVE, 0, NULL, 0);
2959
SendMessage(GetDesktopWindow(), WM_SYSCOMMAND, SC_MONITORPOWER, (LPARAM)-1);
2963
// created for debug purposes
2964
bool SaveBitmapToBMPFile(
2973
BITMAPINFOHEADER bih = {0};
2974
bih.biSize = sizeof(bih);
2975
bih.biWidth = bmwidth;
2976
bih.biHeight = bmheight;
2978
bih.biCompression = BI_RGB;
2980
DWORD bitFields[3] = {0, 0, 0};
2982
if (bmclrdepth == 1)
2987
else if (bmclrdepth == 2)
2992
else if (bmclrdepth == 4)
2995
bih.biClrUsed = 0x10;
2997
else if (bmclrdepth == 8)
3000
bih.biClrUsed = 0x100;
3002
else if (bmclrdepth == 16)
3004
bih.biBitCount = 16;
3005
bih.biCompression = BI_BITFIELDS;
3006
// TODO: use actual masks
3007
bitFields[0] = 0xF800;
3008
bitFields[1] = 0x07E0;
3009
bitFields[2] = 0x001F;
3011
else if (bmclrdepth == 24)
3013
bih.biBitCount = 24;
3015
else if (bmclrdepth == 32)
3017
bih.biBitCount = 32;
3022
BITMAPFILEHEADER bfh = {0};
3023
bfh.bfType = 0x4d42; // 0x42 = "B" 0x4d = "M"
3024
bfh.bfOffBits = sizeof(BITMAPFILEHEADER) + bih.biSize;
3028
bfh.bfOffBits += bih.biClrUsed * sizeof(RGBQUAD);
3030
else if (bitFields[0] || bitFields[1] || bitFields[2])
3032
bfh.bfOffBits += sizeof(bitFields);
3035
unsigned lineSize = (((bih.biWidth * bih.biBitCount) + 15) / 8) & ~1;
3036
bfh.bfSize = bfh.bfOffBits + lineSize * bih.biHeight;
3039
if (!WriteFile(hFile, &bfh, sizeof(bfh), &ulnWr, NULL) || ulnWr!=sizeof(bfh))
3041
if (!WriteFile(hFile, &bih, sizeof(bih), &ulnWr, NULL) || ulnWr!=sizeof(bih))
3046
if (!WriteFile(hFile, ptrPal, bih.biClrUsed * sizeof(RGBQUAD), &ulnWr, NULL) || ulnWr!=bih.biClrUsed * sizeof(RGBQUAD))
3049
else if (bih.biCompression == BI_BITFIELDS)
3051
if (!WriteFile(hFile, bitFields, sizeof(bitFields), &ulnWr, NULL) || ulnWr!=sizeof(bitFields))
3055
for (int i = 0; i < bih.biHeight; i++)
3057
char *pDWr = (char*)ptrBm + (bih.biHeight - i - 1) * bmstride;
3058
if (!WriteFile(hFile, pDWr, lineSize, &ulnWr, NULL) || ulnWr!=lineSize)
3065
// created for debug purposes
3073
if (bmclrdepth!=16 && bmclrdepth!=32)
3080
GetSystemTime(&stm);
3081
TCHAR szFileName[MAX_PATH];
3084
"%04u.%02u.%02u-%02u-%02u-%02u-0x%08x.bmp",
3085
stm.wYear, stm.wMonth, stm.wDay,
3086
stm.wHour, stm.wMinute, stm.wSecond,
3089
HANDLE hFile = CreateFile(
3095
FILE_ATTRIBUTE_NORMAL,
3097
if (hFile==INVALID_HANDLE_VALUE)
3102
bool b= SaveBitmapToBMPFile(
3115
// created for debug purposes
3116
bool vncDesktop::bDbgDumpSurfBuffers(const RECT &rcl)
3118
const int c2rect_re_vd_top = rcl.top - m_bmrect.top;
3119
const int c3rect_re_vd_left = rcl.left - m_bmrect.left;
3120
_ASSERTE(c2rect_re_vd_top >= 0);
3121
_ASSERTE(c3rect_re_vd_left >= 0);
3122
const UINT bytesPerPixel = m_scrinfo.format.bitsPerPixel / 8;
3123
const int offset = c2rect_re_vd_top * m_bytesPerRow + c3rect_re_vd_left * bytesPerPixel;
3125
bool b1 = bDbgBmDump(
3127
rcl.right - rcl.left,
3128
rcl.bottom - rcl.top,
3130
m_scrinfo.format.bitsPerPixel);
3132
bool b2 = bDbgBmDump(
3134
rcl.right - rcl.left,
3135
rcl.bottom - rcl.top,
3137
m_scrinfo.format.bitsPerPixel);