2965 lines
74 KiB
C++
Executable File
2965 lines
74 KiB
C++
Executable File
// Copyright (C) 2001-2004 HorizonLive.com, Inc. All Rights Reserved.
|
|
// Copyright (C) 2001-2004 TightVNC Team. All Rights Reserved.
|
|
// Copyright (C) 1999 AT&T Laboratories Cambridge. All Rights Reserved.
|
|
//
|
|
// This file is part of the VNC system.
|
|
//
|
|
// The VNC system is free software; you can redistribute it and/or modify
|
|
// it under the terms of the GNU General Public License as published by
|
|
// the Free Software Foundation; either version 2 of the License, or
|
|
// (at your option) any later version.
|
|
//
|
|
// This program is distributed in the hope that it will be useful,
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
// GNU General Public License for more details.
|
|
//
|
|
// You should have received a copy of the GNU General Public License
|
|
// along with this program; if not, write to the Free Software
|
|
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
|
|
// USA.
|
|
//
|
|
// TightVNC distribution homepage on the Web: http://www.tightvnc.com/
|
|
//
|
|
// If the source code for the VNC system is not available from the place
|
|
// whence you received this file, check http://www.uk.research.att.com/vnc or contact
|
|
// the authors on vnc@uk.research.att.com for information on obtaining it.
|
|
|
|
// vncDesktop implementation
|
|
|
|
// System headers
|
|
#include "stdhdrs.h"
|
|
#include <omnithread.h>
|
|
|
|
// Custom headers
|
|
#include "vncServer.h"
|
|
#include "vncRegion.h"
|
|
#include "rectlist.h"
|
|
#include "vncDesktop.h"
|
|
#include "vncService.h"
|
|
#include "WallpaperUtils.h"
|
|
#include "TsSessions.h"
|
|
|
|
#if (_MSC_VER>= 1300)
|
|
#include <fstream>
|
|
#else
|
|
#include <fstream.h>
|
|
#endif
|
|
|
|
extern HINSTANCE hAppInstance;
|
|
|
|
// Constants
|
|
const UINT RFB_SCREEN_UPDATE = RegisterWindowMessage("WinVNC.Update.DrawRect");
|
|
const UINT RFB_COPYRECT_UPDATE = RegisterWindowMessage("WinVNC.Update.CopyRect");
|
|
const UINT RFB_MOUSE_UPDATE = RegisterWindowMessage("WinVNC.Update.Mouse");
|
|
// Messages for blocking remote input events
|
|
const UINT RFB_LOCAL_KEYBOARD = RegisterWindowMessage("WinVNC.Local.Keyboard");
|
|
const UINT RFB_LOCAL_MOUSE = RegisterWindowMessage("WinVNC.Local.Mouse");
|
|
|
|
const char szDesktopSink[] = "WinVNC desktop sink";
|
|
|
|
// Atoms
|
|
const char *VNC_WINDOWPOS_ATOMNAME = "VNCHooks.CopyRect.WindowPos";
|
|
ATOM VNC_WINDOWPOS_ATOM = NULL;
|
|
|
|
// Static members to use with new polling algorithm
|
|
const int vncDesktop::m_pollingOrder[32] = {
|
|
0, 16, 8, 24, 4, 20, 12, 28,
|
|
10, 26, 18, 2, 22, 6, 30, 14,
|
|
1, 17, 9, 25, 7, 23, 15, 31,
|
|
19, 3, 27, 11, 29, 13, 5, 21
|
|
};
|
|
int vncDesktop::m_pollingStep = 0;
|
|
|
|
|
|
BOOL IsWinNT()
|
|
{
|
|
return vncService::IsWinNT();
|
|
}
|
|
|
|
BOOL IsWinVerOrHigher(ULONG mj, ULONG mn)
|
|
{
|
|
return vncService::VersionMajor() > mj ||
|
|
vncService::VersionMajor() == mj && vncService::VersionMinor() >= mn;
|
|
}
|
|
|
|
BOOL IsNtVer(ULONG mj, ULONG mn)
|
|
{
|
|
if (!vncService::IsWinNT())
|
|
return FALSE;
|
|
return vncService::VersionMajor() == mj && vncService::VersionMinor() == mn;
|
|
}
|
|
|
|
BOOL vncDesktop::IsMultiMonDesktop()
|
|
{
|
|
if (!IsWinVerOrHigher(4, 10))
|
|
return FALSE;
|
|
return GetSystemMetrics(SM_CMONITORS) > 1;
|
|
}
|
|
|
|
// The desktop handler thread
|
|
// This handles the messages posted by RFBLib to the vncDesktop window
|
|
|
|
class vncDesktopThread : public omni_thread
|
|
{
|
|
public:
|
|
vncDesktopThread() { m_returnsig = NULL; }
|
|
protected:
|
|
~vncDesktopThread() { if (m_returnsig != NULL) delete m_returnsig; }
|
|
public:
|
|
virtual BOOL Init(vncDesktop *desktop, vncServer *server);
|
|
virtual void *run_undetached(void *arg);
|
|
virtual void ReturnVal(BOOL result);
|
|
|
|
protected:
|
|
vncServer *m_server;
|
|
vncDesktop *m_desktop;
|
|
|
|
omni_mutex m_returnLock;
|
|
omni_condition *m_returnsig;
|
|
BOOL m_return;
|
|
BOOL m_returnset;
|
|
};
|
|
|
|
BOOL
|
|
vncDesktopThread::Init(vncDesktop *desktop, vncServer *server)
|
|
{
|
|
// Save the server pointer
|
|
m_server = server;
|
|
m_desktop = desktop;
|
|
|
|
m_returnset = FALSE;
|
|
m_returnsig = new omni_condition(&m_returnLock);
|
|
|
|
// Start the thread
|
|
start_undetached();
|
|
|
|
// Wait for the thread to let us know if it failed to init
|
|
{ omni_mutex_lock l(m_returnLock);
|
|
|
|
while (!m_returnset)
|
|
{
|
|
m_returnsig->wait();
|
|
}
|
|
}
|
|
|
|
return m_return;
|
|
}
|
|
|
|
void
|
|
vncDesktopThread::ReturnVal(BOOL result)
|
|
{
|
|
omni_mutex_lock l(m_returnLock);
|
|
|
|
m_returnset = TRUE;
|
|
m_return = result;
|
|
m_returnsig->signal();
|
|
}
|
|
|
|
void *vncDesktopThread::run_undetached(void *arg)
|
|
{
|
|
// Save the thread's "home" desktop, under NT (no effect under 9x)
|
|
HDESK home_desktop = GetThreadDesktop(GetCurrentThreadId());
|
|
|
|
// Try to make session zero the console session
|
|
if (!inConsoleSession())
|
|
setConsoleSession();
|
|
|
|
// Attempt to initialise and return success or failure
|
|
if (!m_desktop->Startup())
|
|
{
|
|
// vncDesktop::Startup might mave changed video mode in SetupDisplayForConnection.
|
|
// it has to be reverted then.
|
|
// TODO: review strong guarantee conditions for vncDesktop::Startup
|
|
m_desktop->ResetDisplayToNormal();
|
|
vncService::SelectHDESK(home_desktop);
|
|
ReturnVal(FALSE);
|
|
return NULL;
|
|
}
|
|
|
|
RECT rect = m_desktop->GetSourceRect();
|
|
IntersectRect(&rect, &rect, &m_desktop->m_bmrect);
|
|
m_server->SetSharedRect(rect);
|
|
|
|
// Succeeded to initialise ok
|
|
ReturnVal(TRUE);
|
|
|
|
// START PROCESSING DESKTOP MESSAGES
|
|
|
|
// We set a flag inside the desktop handler here, to indicate it's now safe
|
|
// to handle clipboard messages
|
|
m_desktop->SetClipboardActive(TRUE);
|
|
|
|
SYSTEMTIME systime;
|
|
FILETIME ftime;
|
|
ULARGE_INTEGER now, droptime;
|
|
droptime.QuadPart = 0;
|
|
|
|
MSG msg;
|
|
while (TRUE)
|
|
{
|
|
if (!PeekMessage(&msg, m_desktop->Window(), NULL, NULL, PM_REMOVE))
|
|
{
|
|
// Whenever the message queue becomes empty, we check to see whether
|
|
// there are updates to be passed to clients (first we make sure
|
|
// that scheduled wallpaper removal is complete).
|
|
if (!m_server->WallpaperWait()) {
|
|
if (!m_desktop->CheckUpdates())
|
|
break;
|
|
}
|
|
|
|
// Now wait for more messages to be queued
|
|
if (!WaitMessage())
|
|
{
|
|
|
|
break;
|
|
}
|
|
}
|
|
else if (msg.message == RFB_SCREEN_UPDATE)
|
|
{
|
|
// TODO: suppress this message from hook when driver is active
|
|
|
|
// An area of the screen has changed (ignore if we have a driver)
|
|
if (m_desktop->m_videodriver == NULL)
|
|
{
|
|
RECT rect;
|
|
rect.left = (SHORT)LOWORD(msg.wParam);
|
|
rect.top = (SHORT)HIWORD(msg.wParam);
|
|
rect.right = (SHORT)LOWORD(msg.lParam);
|
|
rect.bottom = (SHORT)HIWORD(msg.lParam);
|
|
m_desktop->m_changed_rgn.AddRect(rect);
|
|
}
|
|
}
|
|
else if (msg.message == RFB_MOUSE_UPDATE)
|
|
{
|
|
// Save the cursor ID
|
|
m_desktop->SetCursor((HCURSOR) msg.wParam);
|
|
}
|
|
else if (msg.message == RFB_LOCAL_KEYBOARD)
|
|
{
|
|
// Block remote input events if necessary
|
|
if (vncService::IsWin95()) {
|
|
m_server->SetKeyboardCounter(-1);
|
|
if (m_server->KeyboardCounter() < 0) {
|
|
GetSystemTime(&systime);
|
|
SystemTimeToFileTime(&systime, &ftime);
|
|
droptime.LowPart = ftime.dwLowDateTime;
|
|
droptime.HighPart = ftime.dwHighDateTime;
|
|
droptime.QuadPart /= 10000000; // convert into seconds
|
|
m_server->BlockRemoteInput(true);
|
|
}
|
|
} else {
|
|
GetSystemTime(&systime);
|
|
SystemTimeToFileTime(&systime, &ftime);
|
|
droptime.LowPart = ftime.dwLowDateTime;
|
|
droptime.HighPart = ftime.dwHighDateTime;
|
|
droptime.QuadPart /= 10000000; // convert into seconds
|
|
m_server->BlockRemoteInput(true);
|
|
}
|
|
}
|
|
else if (msg.message == RFB_LOCAL_MOUSE)
|
|
{
|
|
// Block remote input events if necessary
|
|
if (vncService::IsWin95()) {
|
|
if (msg.wParam == WM_MOUSEMOVE) {
|
|
m_server->SetMouseCounter(-1, msg.pt, true);
|
|
} else {
|
|
m_server->SetMouseCounter(-1, msg.pt, false);
|
|
}
|
|
if (m_server->MouseCounter() < 0 && droptime.QuadPart == 0) {
|
|
GetSystemTime(&systime);
|
|
SystemTimeToFileTime(&systime, &ftime);
|
|
droptime.LowPart = ftime.dwLowDateTime;
|
|
droptime.HighPart = ftime.dwHighDateTime;
|
|
droptime.QuadPart /= 10000000; // convert into seconds
|
|
m_server->BlockRemoteInput(true);
|
|
}
|
|
} else {
|
|
GetSystemTime(&systime);
|
|
SystemTimeToFileTime(&systime, &ftime);
|
|
droptime.LowPart = ftime.dwLowDateTime;
|
|
droptime.HighPart = ftime.dwHighDateTime;
|
|
droptime.QuadPart /= 10000000; // convert into seconds
|
|
m_server->BlockRemoteInput(true);
|
|
}
|
|
}
|
|
else if (msg.message == WM_QUIT)
|
|
{
|
|
break;
|
|
}
|
|
#ifdef HORIZONLIVE
|
|
else if (msg.message == LS_QUIT)
|
|
{
|
|
// this is our custom quit message
|
|
break;
|
|
}
|
|
#endif
|
|
else
|
|
{
|
|
// Process any other messages normally
|
|
DispatchMessage(&msg);
|
|
}
|
|
|
|
// Check timer to unblock remote input events if necessary
|
|
// FIXME: rewrite this stuff to eliminate code duplication (ses above).
|
|
// FIXME: Use time() instead of GetSystemTime().
|
|
// FIXME: It's not necessary to do this on receiving _each_ message.
|
|
if (m_server->LocalInputPriority() && droptime.QuadPart != 0) {
|
|
GetSystemTime(&systime);
|
|
SystemTimeToFileTime(&systime, &ftime);
|
|
now.LowPart = ftime.dwLowDateTime;
|
|
now.HighPart = ftime.dwHighDateTime;
|
|
now.QuadPart /= 10000000; // convert into seconds
|
|
|
|
if (now.QuadPart - m_server->DisableTime() >= droptime.QuadPart) {
|
|
m_server->BlockRemoteInput(false);
|
|
droptime.QuadPart = 0;
|
|
m_server->SetKeyboardCounter(0);
|
|
m_server->SetMouseCounter(0, msg.pt, false);
|
|
}
|
|
}
|
|
}
|
|
|
|
m_desktop->SetClipboardActive(FALSE);
|
|
|
|
// Clear all the hooks and close windows, etc.
|
|
m_desktop->Shutdown();
|
|
// Return display settings to previous values.
|
|
m_desktop->ResetDisplayToNormal();
|
|
// Turn on the screen.
|
|
m_desktop->BlankScreen(FALSE);
|
|
|
|
// Clear the shift modifier keys, now that there are no remote clients
|
|
vncKeymap::ClearShiftKeys();
|
|
|
|
// Switch back into our home desktop, under NT (no effect under 9x)
|
|
vncService::SelectHDESK(home_desktop);
|
|
|
|
return NULL;
|
|
}
|
|
|
|
// Implementation of the vncDesktop class
|
|
|
|
vncDesktop::vncDesktop()
|
|
{
|
|
m_thread = NULL;
|
|
|
|
m_hwnd = NULL;
|
|
m_polling_flag = FALSE;
|
|
m_timer_polling = 0;
|
|
m_timer_blank_screen = 0;
|
|
m_hnextviewer = NULL;
|
|
m_hcursor = NULL;
|
|
|
|
m_displaychanged = FALSE;
|
|
|
|
m_hrootdc = NULL;
|
|
m_hmemdc = NULL;
|
|
m_membitmap = NULL;
|
|
|
|
m_initialClipBoardSeen = FALSE;
|
|
|
|
// Vars for Will Dean's DIBsection patch
|
|
m_DIBbits = NULL;
|
|
m_freemainbuff = FALSE;
|
|
m_formatmunged = FALSE;
|
|
m_mainbuff = NULL;
|
|
m_backbuff = NULL;
|
|
|
|
m_clipboard_active = FALSE;
|
|
m_hooks_active = FALSE;
|
|
m_hooks_may_change = FALSE;
|
|
m_lpAlternateDevMode = NULL;
|
|
m_copyrect_set = FALSE;
|
|
|
|
m_videodriver = NULL;
|
|
|
|
m_timer_blank_screen = 0;
|
|
}
|
|
|
|
vncDesktop::~vncDesktop()
|
|
{
|
|
|
|
// If we created a thread then here we delete it
|
|
// The thread itself does most of the cleanup
|
|
if(m_thread != NULL)
|
|
{
|
|
// Post a close message to quit our message handler thread
|
|
PostMessage(Window(), WM_QUIT, 0, 0);
|
|
|
|
// Join with the desktop handler thread
|
|
void *returnval;
|
|
m_thread->join(&returnval);
|
|
m_thread = NULL;
|
|
}
|
|
|
|
// Let's call Shutdown just in case something went wrong...
|
|
Shutdown();
|
|
_ASSERTE(!m_lpAlternateDevMode);
|
|
}
|
|
|
|
// Routine to startup and install all the hooks and stuff
|
|
BOOL
|
|
vncDesktop::Startup()
|
|
{
|
|
// Currently, we just check whether we're in the console session, and
|
|
// fail if not
|
|
if (!inConsoleSession()) {
|
|
return FALSE;
|
|
}
|
|
|
|
// Configure the display for optimal VNC performance.
|
|
SetupDisplayForConnection();
|
|
|
|
// Initialise the Desktop object
|
|
if (!InitDesktop())
|
|
return FALSE;
|
|
|
|
if (InitVideoDriver())
|
|
{
|
|
// this isn't really necessary
|
|
// InvalidateRect(NULL,NULL,TRUE);
|
|
}
|
|
|
|
if (!InitBitmap())
|
|
return FALSE;
|
|
|
|
if (!ThunkBitmapInfo())
|
|
return FALSE;
|
|
|
|
if (!SetPixFormat())
|
|
return FALSE;
|
|
|
|
if (!CreateBuffers())
|
|
return FALSE;
|
|
|
|
if (!SetPixShifts())
|
|
return FALSE;
|
|
|
|
if (!SetPalette())
|
|
return FALSE;
|
|
|
|
if (!InitWindow())
|
|
return FALSE;
|
|
|
|
// Add the system hook
|
|
//ActivateHooks();
|
|
m_server->PollFullScreen(TRUE);
|
|
m_hooks_may_change = true;
|
|
|
|
#ifndef HORIZONLIVE
|
|
// Start up the keyboard and mouse filters
|
|
//SetKeyboardFilterHook(m_server->LocalInputsDisabled());
|
|
//SetMouseFilterHook(m_server->LocalInputsDisabled());
|
|
#endif
|
|
|
|
// Start up the keyboard and mouse hooks for
|
|
// local event priority over remote impl.
|
|
if (m_server->LocalInputPriority())
|
|
SetLocalInputPriorityHook(true);
|
|
|
|
// Start a timer to handle Polling Mode. The timer will cause
|
|
// an "idle" event, which is necessary if Polling Mode is being used,
|
|
// to cause TriggerUpdate to be called.
|
|
SetPollingFlag(FALSE);
|
|
SetPollingTimer();
|
|
|
|
// If necessary, start a separate timer to preserve the diplay turned off.
|
|
UpdateBlankScreenTimer();
|
|
|
|
// Get hold of the WindowPos atom!
|
|
if ((VNC_WINDOWPOS_ATOM = GlobalAddAtom(VNC_WINDOWPOS_ATOMNAME)) == 0) {
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
// this member must be initialized: we cant assume the absence
|
|
// of clients when desktop is created.
|
|
m_cursorpos.left = 0;
|
|
m_cursorpos.top = 0;
|
|
m_cursorpos.right = 0;
|
|
m_cursorpos.bottom = 0;
|
|
|
|
// Everything is ok, so return TRUE
|
|
return TRUE;
|
|
}
|
|
|
|
// Routine to shutdown all the hooks and stuff
|
|
BOOL vncDesktop::Shutdown()
|
|
{
|
|
// If we created timers then kill them
|
|
if (m_timer_polling)
|
|
{
|
|
KillTimer(Window(), TIMER_POLL);
|
|
m_timer_polling = 0;
|
|
}
|
|
if (m_timer_blank_screen)
|
|
{
|
|
KillTimer(Window(), TIMER_BLANK_SCREEN);
|
|
m_timer_blank_screen = 0;
|
|
}
|
|
|
|
// If we created a window then kill it and the hooks
|
|
if (m_hwnd != NULL)
|
|
{
|
|
//Remove the system hooks
|
|
//Unset keyboard and mouse hooks
|
|
SetLocalInputPriorityHook(false);
|
|
m_hooks_may_change = false;
|
|
ShutdownHooks();
|
|
|
|
#ifndef HORIZONLIVE
|
|
// Stop the keyboard and mouse filters
|
|
//SetKeyboardFilterHook(false);
|
|
//SetMouseFilterHook(false);
|
|
#endif
|
|
// The window is being closed - remove it from the viewer list
|
|
ChangeClipboardChain(m_hwnd, m_hnextviewer);
|
|
|
|
// Close the hook window
|
|
DestroyWindow(m_hwnd);
|
|
m_hwnd = NULL;
|
|
m_hnextviewer = NULL;
|
|
}
|
|
|
|
// Now free all the bitmap stuff
|
|
if (m_hrootdc != NULL)
|
|
{
|
|
// Release our device context
|
|
ReleaseDC(NULL, m_hrootdc);
|
|
m_hrootdc = NULL;
|
|
}
|
|
if (m_hmemdc != NULL)
|
|
{
|
|
// Release our device context
|
|
DeleteDC(m_hmemdc);
|
|
m_hmemdc = NULL;
|
|
}
|
|
if (m_membitmap != NULL)
|
|
{
|
|
// Release the custom bitmap, if any
|
|
DeleteObject(m_membitmap);
|
|
m_membitmap = NULL;
|
|
}
|
|
|
|
// Free back buffer
|
|
if (m_backbuff != NULL)
|
|
{
|
|
delete [] m_backbuff;
|
|
m_backbuff = NULL;
|
|
}
|
|
|
|
if (m_freemainbuff)
|
|
{
|
|
// Slow blits were enabled - free the slow blit buffer
|
|
if (m_mainbuff != NULL)
|
|
{
|
|
delete [] m_mainbuff;
|
|
m_mainbuff = NULL;
|
|
}
|
|
}
|
|
|
|
// Free the WindowPos atom!
|
|
if (VNC_WINDOWPOS_ATOM != NULL)
|
|
{
|
|
GlobalDeleteAtom(VNC_WINDOWPOS_ATOM);
|
|
}
|
|
|
|
ShutdownVideoDriver();
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
// Routines to set/unset hooks via VNCHooks.dll
|
|
|
|
void
|
|
vncDesktop::ActivateHooks()
|
|
{
|
|
BOOL enable = !(m_server->DontSetHooks() && m_server->PollFullScreen());
|
|
if (enable && !m_hooks_active) {
|
|
//m_hooks_active = SetHook(m_hwnd,
|
|
// RFB_SCREEN_UPDATE,
|
|
// RFB_COPYRECT_UPDATE,
|
|
// RFB_MOUSE_UPDATE);
|
|
if (!m_hooks_active) {
|
|
|
|
// Switch on full screen polling, so they can see something, at least...
|
|
m_server->PollFullScreen(TRUE);
|
|
}
|
|
} else if (!enable) {
|
|
ShutdownHooks();
|
|
}
|
|
}
|
|
|
|
void
|
|
vncDesktop::ShutdownHooks()
|
|
{
|
|
//if (m_hooks_active)
|
|
// m_hooks_active = !UnSetHook(m_hwnd);
|
|
}
|
|
|
|
void
|
|
vncDesktop::TryActivateHooks()
|
|
{
|
|
if (m_hooks_may_change)
|
|
ActivateHooks();
|
|
}
|
|
|
|
// Routine to ensure we're on the correct NT desktop
|
|
|
|
BOOL
|
|
vncDesktop::InitDesktop()
|
|
{
|
|
if (vncService::InputDesktopSelected())
|
|
return TRUE;
|
|
|
|
// Ask for the current input desktop
|
|
return vncService::SelectDesktop(NULL);
|
|
}
|
|
|
|
// Routine used to close the screen saver, if it's active...
|
|
|
|
BOOL CALLBACK
|
|
KillScreenSaverFunc(HWND hwnd, LPARAM lParam)
|
|
{
|
|
char buffer[256];
|
|
|
|
// - ONLY try to close Screen-saver windows!!!
|
|
if ((GetClassName(hwnd, buffer, 256) != 0) &&
|
|
(strcmp(buffer, "WindowsScreenSaverClass") == 0))
|
|
PostMessage(hwnd, WM_CLOSE, 0, 0);
|
|
return TRUE;
|
|
}
|
|
|
|
void
|
|
vncDesktop::KillScreenSaver()
|
|
{
|
|
OSVERSIONINFO osversioninfo;
|
|
osversioninfo.dwOSVersionInfoSize = sizeof(osversioninfo);
|
|
|
|
// Get the current OS version
|
|
if (!GetVersionEx(&osversioninfo))
|
|
return;
|
|
|
|
|
|
// How to kill the screen saver depends on the OS
|
|
switch (osversioninfo.dwPlatformId)
|
|
{
|
|
case VER_PLATFORM_WIN32_WINDOWS:
|
|
{
|
|
// Windows 95
|
|
|
|
// Fidn the ScreenSaverClass window
|
|
HWND hsswnd = FindWindow ("WindowsScreenSaverClass", NULL);
|
|
if (hsswnd != NULL)
|
|
PostMessage(hsswnd, WM_CLOSE, 0, 0);
|
|
break;
|
|
}
|
|
case VER_PLATFORM_WIN32_NT:
|
|
{
|
|
// Windows NT
|
|
|
|
// Find the screensaver desktop
|
|
HDESK hDesk = OpenDesktop(
|
|
"Screen-saver",
|
|
0,
|
|
FALSE,
|
|
DESKTOP_READOBJECTS | DESKTOP_WRITEOBJECTS
|
|
);
|
|
if (hDesk != NULL)
|
|
{
|
|
// Close all windows on the screen saver desktop
|
|
EnumDesktopWindows(hDesk, (WNDENUMPROC) &KillScreenSaverFunc, 0);
|
|
CloseDesktop(hDesk);
|
|
// Pause long enough for the screen-saver to close
|
|
//Sleep(2000);
|
|
// Reset the screen saver so it can run again
|
|
SystemParametersInfo(SPI_SETSCREENSAVEACTIVE, TRUE, 0, SPIF_SENDWININICHANGE);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
void vncDesktop::ChangeResNow()
|
|
{
|
|
// IMPORTANT: Screen mode alteration may only take place on a single-mon system.
|
|
if (IsMultiMonDesktop())
|
|
{
|
|
return;
|
|
}
|
|
|
|
BOOL settingsUpdated = false;
|
|
int i = 0;
|
|
|
|
_ASSERTE(!m_lpAlternateDevMode);
|
|
m_lpAlternateDevMode = new DEVMODE; // *** create an instance of DEVMODE - Jeremy Peaks
|
|
if (!m_lpAlternateDevMode)
|
|
{
|
|
return;
|
|
}
|
|
|
|
// *** WBB - Obtain the current display settings.
|
|
// only on unimon
|
|
if (! EnumDisplaySettings(0, ENUM_CURRENT_SETTINGS, m_lpAlternateDevMode))
|
|
{
|
|
delete m_lpAlternateDevMode;
|
|
m_lpAlternateDevMode = NULL;
|
|
return;
|
|
|
|
}
|
|
|
|
origPelsWidth = m_lpAlternateDevMode->dmPelsWidth; // *** sets the original resolution for use later
|
|
origPelsHeight = m_lpAlternateDevMode->dmPelsHeight; // *** - Jeremy Peaks
|
|
|
|
// *** Open the registry key for resolution settings
|
|
/*HKEY checkdetails = 0;
|
|
RegOpenKeyEx(HKEY_LOCAL_MACHINE,
|
|
WINVNC_REGISTRY_KEY,
|
|
0,
|
|
KEY_READ,
|
|
&checkdetails);
|
|
if (checkdetails)
|
|
{
|
|
int slen=MAX_REG_ENTRY_LEN;
|
|
int valType;
|
|
char inouttext[MAX_REG_ENTRY_LEN];
|
|
|
|
memset(inouttext, 0, MAX_REG_ENTRY_LEN);
|
|
|
|
// *** Get the registry values for resolution change - Jeremy Peaks
|
|
RegQueryValueEx(checkdetails,
|
|
"ResWidth",
|
|
NULL,
|
|
(LPDWORD) &valType,
|
|
(LPBYTE) &inouttext,
|
|
(LPDWORD) &slen);
|
|
|
|
|
|
if ((valType == REG_SZ) &&
|
|
atol(inouttext)) { // *** if width is 0, then this isn't a valid resolution, so do nothing - Jeremy Peaks
|
|
m_lpAlternateDevMode->dmPelsWidth = atol(inouttext);
|
|
|
|
memset(inouttext, 0, MAX_REG_ENTRY_LEN);
|
|
|
|
RegQueryValueEx(checkdetails,
|
|
"ResHeight",
|
|
NULL,
|
|
(LPDWORD) &valType,
|
|
(LPBYTE) &inouttext,
|
|
(LPDWORD) &slen);
|
|
|
|
m_lpAlternateDevMode->dmPelsHeight = atol(inouttext);
|
|
if ((valType == REG_SZ ) &&
|
|
(m_lpAlternateDevMode->dmPelsHeight > 0)) {
|
|
|
|
// *** make res change - Jeremy Peaks
|
|
// testing: predefined Width/Height may become incompatible
|
|
// with new clrdepth/timings
|
|
long resultOfResChange = ChangeDisplaySettings(m_lpAlternateDevMode, CDS_TEST);
|
|
if (resultOfResChange == DISP_CHANGE_SUCCESSFUL) {
|
|
ChangeDisplaySettings(m_lpAlternateDevMode, CDS_UPDATEREGISTRY);
|
|
settingsUpdated = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
RegCloseKey(checkdetails);
|
|
}*/
|
|
|
|
if (! settingsUpdated)
|
|
{
|
|
// Did not change the resolution.
|
|
delete m_lpAlternateDevMode;
|
|
m_lpAlternateDevMode = NULL;
|
|
}
|
|
}
|
|
|
|
void
|
|
vncDesktop::SetupDisplayForConnection()
|
|
{
|
|
KillScreenSaver();
|
|
|
|
ChangeResNow(); // *** - Jeremy Peaks
|
|
}
|
|
|
|
void
|
|
vncDesktop::ResetDisplayToNormal()
|
|
{
|
|
if (m_lpAlternateDevMode != NULL)
|
|
{
|
|
// *** In case the resolution was changed, revert to original settings now
|
|
m_lpAlternateDevMode->dmPelsWidth = origPelsWidth;
|
|
m_lpAlternateDevMode->dmPelsHeight = origPelsHeight;
|
|
|
|
long resultOfResChange = ChangeDisplaySettings(m_lpAlternateDevMode, CDS_TEST);
|
|
if (resultOfResChange == DISP_CHANGE_SUCCESSFUL)
|
|
ChangeDisplaySettings(m_lpAlternateDevMode, CDS_UPDATEREGISTRY);
|
|
|
|
delete m_lpAlternateDevMode;
|
|
m_lpAlternateDevMode = NULL;
|
|
}
|
|
}
|
|
|
|
RECT vncDesktop::GetSourceRect()
|
|
{
|
|
if (m_server->WindowShared())
|
|
{
|
|
RECT wrect;
|
|
GetWindowRect(m_server->GetWindowShared(), &wrect);
|
|
return wrect;
|
|
}
|
|
else if (m_server->ScreenAreaShared())
|
|
{
|
|
return m_server->GetScreenAreaRect();
|
|
}
|
|
else if (m_server->PrimaryDisplayOnlyShared())
|
|
{
|
|
RECT pdr = { 0, 0, GetSystemMetrics(SM_CXSCREEN), GetSystemMetrics(SM_CYSCREEN) };
|
|
return pdr;
|
|
}
|
|
else
|
|
{
|
|
#ifdef _DEBUG
|
|
RECT rd;
|
|
_ASSERTE(GetSourceDisplayRect(rd));
|
|
_ASSERTE(EqualRect(&rd, &m_bmrect));
|
|
#endif
|
|
return m_bmrect;
|
|
}
|
|
}
|
|
|
|
RECT GetScreenRect()
|
|
{
|
|
RECT screenrect;
|
|
if (IsWinVerOrHigher(4, 10))
|
|
{
|
|
screenrect.left = GetSystemMetrics(SM_XVIRTUALSCREEN);
|
|
screenrect.top = GetSystemMetrics(SM_YVIRTUALSCREEN);
|
|
screenrect.right = screenrect.left + GetSystemMetrics(SM_CXVIRTUALSCREEN);
|
|
screenrect.bottom = screenrect.top + GetSystemMetrics(SM_CYVIRTUALSCREEN);
|
|
}
|
|
else
|
|
{
|
|
screenrect.left = 0;
|
|
screenrect.top = 0;
|
|
screenrect.right = GetSystemMetrics(SM_CXSCREEN);
|
|
screenrect.bottom = GetSystemMetrics(SM_CYSCREEN);
|
|
}
|
|
return screenrect;
|
|
}
|
|
|
|
BOOL vncDesktop::GetSourceDisplayRect(RECT &rdisp_rect)
|
|
{
|
|
if (!m_hrootdc)
|
|
m_hrootdc = ::GetDC(NULL);
|
|
if (!m_hrootdc)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
// TODO: refactor it
|
|
rdisp_rect = GetScreenRect();
|
|
return TRUE;
|
|
}
|
|
|
|
BOOL vncDesktop::InitBitmap()
|
|
{
|
|
// IMPORTANT: here an optimization may be implemented
|
|
// when only a fixed rect is shared.
|
|
// then m_bmrect should be set to that rect.
|
|
if (!GetSourceDisplayRect(m_bmrect))
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
// Create a compatible memory DC
|
|
m_hmemdc = CreateCompatibleDC(m_hrootdc);
|
|
if (m_hmemdc == NULL) {
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
// Check that the device capabilities are ok
|
|
if ((GetDeviceCaps(m_hrootdc, RASTERCAPS) & RC_BITBLT) == 0)
|
|
{
|
|
|
|
return FALSE;
|
|
}
|
|
if ((GetDeviceCaps(m_hmemdc, RASTERCAPS) & RC_DI_BITMAP) == 0)
|
|
{
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
// Create the bitmap to be compatible with the ROOT DC!!!
|
|
m_membitmap = CreateCompatibleBitmap(
|
|
m_hrootdc,
|
|
m_bmrect.right - m_bmrect.left,
|
|
m_bmrect.bottom - m_bmrect.top);
|
|
if (m_membitmap == NULL)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
// Get the bitmap's format and colour details
|
|
int result;
|
|
m_bminfo.bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
|
|
m_bminfo.bmi.bmiHeader.biBitCount = 0;
|
|
result = ::GetDIBits(m_hmemdc, m_membitmap, 0, 1, NULL, &m_bminfo.bmi, DIB_RGB_COLORS);
|
|
if (result == 0) {
|
|
return FALSE;
|
|
}
|
|
result = ::GetDIBits(m_hmemdc, m_membitmap, 0, 1, NULL, &m_bminfo.bmi, DIB_RGB_COLORS);
|
|
if (result == 0) {
|
|
return FALSE;
|
|
}
|
|
|
|
if (GetDeviceCaps(m_hmemdc, PLANES) != 1)
|
|
{
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
// Henceforth we want to use a top-down scanning representation
|
|
m_bminfo.bmi.bmiHeader.biHeight = - abs(m_bminfo.bmi.bmiHeader.biHeight);
|
|
|
|
// Is the bitmap palette-based or truecolour?
|
|
m_bminfo.truecolour = (GetDeviceCaps(m_hmemdc, RASTERCAPS) & RC_PALETTE) == 0;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
BOOL
|
|
vncDesktop::ThunkBitmapInfo()
|
|
{
|
|
// If we leave the pixel format intact, the blits can be optimised (Will Dean's patch)
|
|
m_formatmunged = FALSE;
|
|
|
|
// HACK ***. Optimised blits don't work with palette-based displays, yet
|
|
if (!m_bminfo.truecolour) {
|
|
m_formatmunged = TRUE;
|
|
}
|
|
|
|
// Attempt to force the actual format into one we can handle
|
|
// We can handle 8-bit-palette and 16/32-bit-truecolour modes
|
|
switch (m_bminfo.bmi.bmiHeader.biBitCount)
|
|
{
|
|
case 1:
|
|
case 4:
|
|
|
|
|
|
// Correct the BITMAPINFO header to the format we actually want
|
|
m_bminfo.bmi.bmiHeader.biClrUsed = 0;
|
|
m_bminfo.bmi.bmiHeader.biPlanes = 1;
|
|
m_bminfo.bmi.bmiHeader.biCompression = BI_RGB;
|
|
m_bminfo.bmi.bmiHeader.biBitCount = 8;
|
|
m_bminfo.bmi.bmiHeader.biSizeImage =
|
|
abs((m_bminfo.bmi.bmiHeader.biWidth *
|
|
m_bminfo.bmi.bmiHeader.biHeight *
|
|
m_bminfo.bmi.bmiHeader.biBitCount)/ 8);
|
|
m_bminfo.bmi.bmiHeader.biClrImportant = 0;
|
|
m_bminfo.truecolour = FALSE;
|
|
|
|
// Display format is non-VNC compatible - use the slow blit method
|
|
m_formatmunged = TRUE;
|
|
break;
|
|
case 24:
|
|
// Update the bitmapinfo header
|
|
m_bminfo.bmi.bmiHeader.biBitCount = 32;
|
|
m_bminfo.bmi.bmiHeader.biPlanes = 1;
|
|
m_bminfo.bmi.bmiHeader.biCompression = BI_RGB;
|
|
m_bminfo.bmi.bmiHeader.biSizeImage =
|
|
abs((m_bminfo.bmi.bmiHeader.biWidth *
|
|
m_bminfo.bmi.bmiHeader.biHeight *
|
|
m_bminfo.bmi.bmiHeader.biBitCount)/ 8);
|
|
// Display format is non-VNC compatible - use the slow blit method
|
|
m_formatmunged = TRUE;
|
|
break;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
BOOL
|
|
vncDesktop::SetPixFormat()
|
|
{
|
|
// Examine the bitmapinfo structure to obtain the current pixel format
|
|
m_scrinfo.format.trueColour = m_bminfo.truecolour;
|
|
m_scrinfo.format.bigEndian = 0;
|
|
|
|
// Set up the native buffer width, height and format
|
|
m_scrinfo.framebufferWidth = (CARD16) (m_bmrect.right - m_bmrect.left); // Swap endian before actually sending
|
|
m_scrinfo.framebufferHeight = (CARD16) (m_bmrect.bottom - m_bmrect.top); // Swap endian before actually sending
|
|
m_scrinfo.format.bitsPerPixel = (CARD8) m_bminfo.bmi.bmiHeader.biBitCount;
|
|
m_scrinfo.format.depth = (CARD8) m_bminfo.bmi.bmiHeader.biBitCount;
|
|
|
|
|
|
// Calculate the number of bytes per row
|
|
m_bytesPerRow = m_scrinfo.framebufferWidth * m_scrinfo.format.bitsPerPixel / 8;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
BOOL
|
|
vncDesktop::SetPixShifts()
|
|
{
|
|
// Sort out the colour shifts, etc.
|
|
DWORD redMask=0, blueMask=0, greenMask = 0;
|
|
|
|
switch (m_bminfo.bmi.bmiHeader.biBitCount)
|
|
{
|
|
case 16:
|
|
if (m_videodriver&& m_videodriver->IsDirectAccessInEffect())
|
|
{
|
|
// IMPORTANT: Mirage colormask is always 565
|
|
redMask = 0xf800;
|
|
greenMask = 0x07e0;
|
|
blueMask = 0x001f;
|
|
}
|
|
else if (m_bminfo.bmi.bmiHeader.biCompression == BI_RGB)
|
|
{
|
|
// Standard 16-bit display
|
|
// each word single pixel 5-5-5
|
|
redMask = 0x7c00; greenMask = 0x03e0; blueMask = 0x001f;
|
|
}
|
|
else
|
|
{
|
|
if (m_bminfo.bmi.bmiHeader.biCompression == BI_BITFIELDS)
|
|
{
|
|
redMask = *(DWORD *) &m_bminfo.bmi.bmiColors[0];
|
|
greenMask = *(DWORD *) &m_bminfo.bmi.bmiColors[1];
|
|
blueMask = *(DWORD *) &m_bminfo.bmi.bmiColors[2];
|
|
}
|
|
}
|
|
break;
|
|
|
|
case 32:
|
|
// Standard 24/32 bit displays
|
|
if (m_bminfo.bmi.bmiHeader.biCompression == BI_RGB ||
|
|
m_videodriver && m_videodriver->IsDirectAccessInEffect())
|
|
{
|
|
redMask = 0xff0000;
|
|
greenMask = 0xff00;
|
|
blueMask = 0x00ff;
|
|
|
|
// The real color depth is 24 bits in this case. If the depth
|
|
// is set to 32, the Tight encoder shows worse performance.
|
|
m_scrinfo.format.depth = 24;
|
|
}
|
|
else
|
|
{
|
|
if (m_bminfo.bmi.bmiHeader.biCompression == BI_BITFIELDS)
|
|
{
|
|
redMask = *(DWORD *) &m_bminfo.bmi.bmiColors[0];
|
|
greenMask = *(DWORD *) &m_bminfo.bmi.bmiColors[1];
|
|
blueMask = *(DWORD *) &m_bminfo.bmi.bmiColors[2];
|
|
}
|
|
}
|
|
break;
|
|
|
|
default:
|
|
// Other pixel formats are only valid if they're palette-based
|
|
if (m_bminfo.truecolour)
|
|
{
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
// Convert the data we just retrieved
|
|
MaskToMaxAndShift(redMask, m_scrinfo.format.redMax, m_scrinfo.format.redShift);
|
|
MaskToMaxAndShift(greenMask, m_scrinfo.format.greenMax, m_scrinfo.format.greenShift);
|
|
MaskToMaxAndShift(blueMask, m_scrinfo.format.blueMax, m_scrinfo.format.blueShift);
|
|
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
BOOL
|
|
vncDesktop::SetPalette()
|
|
{
|
|
// Lock the current display palette into the memory DC we're holding
|
|
// *** CHECK THIS FOR LEAKS!
|
|
if (!m_bminfo.truecolour)
|
|
{
|
|
LOGPALETTE *palette;
|
|
UINT size = sizeof(LOGPALETTE) + (sizeof(PALETTEENTRY) * 256);
|
|
|
|
palette = (LOGPALETTE *) new char[size];
|
|
if (palette == NULL) {
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
// Initialise the structure
|
|
palette->palVersion = 0x300;
|
|
palette->palNumEntries = 256;
|
|
|
|
// Get the system colours
|
|
if (GetSystemPaletteEntries(m_hrootdc, 0, 256, palette->palPalEntry) == 0)
|
|
{
|
|
|
|
delete [] palette;
|
|
return FALSE;
|
|
}
|
|
|
|
// Create a palette from those
|
|
HPALETTE pal = CreatePalette(palette);
|
|
if (pal == NULL)
|
|
{
|
|
|
|
delete [] palette;
|
|
return FALSE;
|
|
}
|
|
|
|
// Select the palette into our memory DC
|
|
HPALETTE oldpalette = SelectPalette(m_hmemdc, pal, FALSE);
|
|
if (oldpalette == NULL)
|
|
{
|
|
|
|
delete [] palette;
|
|
DeleteObject(pal);
|
|
return FALSE;
|
|
}
|
|
|
|
// Worked, so realise the palette
|
|
RealizePalette(m_hmemdc);
|
|
|
|
// It worked!
|
|
delete [] palette;
|
|
DeleteObject(oldpalette);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
// Not a palette based local screen - forget it!
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
LRESULT CALLBACK DesktopWndProc(HWND hwnd, UINT iMsg, WPARAM wParam, LPARAM lParam);
|
|
|
|
ATOM m_wndClass = 0;
|
|
|
|
BOOL vncDesktop::InitWindow()
|
|
{
|
|
if (m_wndClass == 0) {
|
|
// Create the window class
|
|
WNDCLASSEX wndclass;
|
|
|
|
wndclass.cbSize = sizeof(wndclass);
|
|
wndclass.style = 0;
|
|
wndclass.lpfnWndProc = &DesktopWndProc;
|
|
wndclass.cbClsExtra = 0;
|
|
wndclass.cbWndExtra = 0;
|
|
wndclass.hInstance = hAppInstance;
|
|
wndclass.hIcon = NULL;
|
|
wndclass.hCursor = NULL;
|
|
wndclass.hbrBackground = (HBRUSH) GetStockObject(WHITE_BRUSH);
|
|
wndclass.lpszMenuName = (const char *) NULL;
|
|
wndclass.lpszClassName = szDesktopSink;
|
|
wndclass.hIconSm = NULL;
|
|
|
|
// Register it
|
|
m_wndClass = RegisterClassEx(&wndclass);
|
|
}
|
|
|
|
// And create a window
|
|
m_hwnd = CreateWindow(szDesktopSink,
|
|
"WinVNC",
|
|
WS_OVERLAPPEDWINDOW,
|
|
CW_USEDEFAULT,
|
|
CW_USEDEFAULT,
|
|
400, 200,
|
|
NULL,
|
|
NULL,
|
|
hAppInstance,
|
|
NULL);
|
|
|
|
if (m_hwnd == NULL) {
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
// Set the "this" pointer for the window
|
|
SetWindowLong(m_hwnd, -21, (long)this); // #define GWL_USERDATA (-21)
|
|
|
|
// Enable clipboard hooking
|
|
m_hnextviewer = SetClipboardViewer(m_hwnd);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
BOOL
|
|
vncDesktop::CreateBuffers()
|
|
{
|
|
|
|
// Create a new DIB section ***
|
|
HBITMAP tempbitmap = NULL;
|
|
if (!m_formatmunged)
|
|
{
|
|
tempbitmap = CreateDIBSection(
|
|
m_hmemdc,
|
|
&m_bminfo.bmi,
|
|
DIB_RGB_COLORS,
|
|
&m_DIBbits,
|
|
NULL,
|
|
0);
|
|
}
|
|
|
|
m_freemainbuff = false;
|
|
|
|
// NOTE m_mainbuff and m_backbuff allocation can not be supressed
|
|
// even with direct access mirror surface view
|
|
|
|
if (tempbitmap == NULL)
|
|
{
|
|
m_DIBbits = NULL;
|
|
// create our own buffer to copy blits through
|
|
if ((m_mainbuff = new BYTE [ScreenBuffSize()]) == NULL) {
|
|
return FALSE;
|
|
}
|
|
m_freemainbuff = true;
|
|
if ((m_backbuff = new BYTE [ScreenBuffSize()]) == NULL) {
|
|
return FALSE;
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
// Create our own buffer to copy blits through
|
|
if ((m_backbuff = new BYTE [ScreenBuffSize()]) == NULL) {
|
|
if (tempbitmap!= NULL)
|
|
DeleteObject(tempbitmap);
|
|
return FALSE;
|
|
}
|
|
|
|
// Delete the old memory bitmap
|
|
if (m_membitmap != NULL) {
|
|
DeleteObject(m_membitmap);
|
|
m_membitmap = NULL;
|
|
}
|
|
|
|
// Replace old membitmap with DIB section
|
|
m_membitmap = tempbitmap;
|
|
m_mainbuff = (BYTE *)m_DIBbits;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
BOOL
|
|
vncDesktop::Init(vncServer *server)
|
|
{
|
|
|
|
// Save the server pointer
|
|
m_server = server;
|
|
|
|
// Load in the arrow cursor
|
|
m_hdefcursor = LoadCursor(NULL, IDC_ARROW);
|
|
m_hcursor = m_hdefcursor;
|
|
|
|
// Spawn a thread to handle that window's message queue
|
|
vncDesktopThread *thread = new vncDesktopThread;
|
|
if (thread == NULL)
|
|
return FALSE;
|
|
m_thread = thread;
|
|
return thread->Init(this, m_server);
|
|
}
|
|
|
|
void
|
|
vncDesktop::RequestUpdate()
|
|
{
|
|
PostMessage(m_hwnd, WM_TIMER, TIMER_POLL, 0);
|
|
}
|
|
|
|
int
|
|
vncDesktop::ScreenBuffSize()
|
|
{
|
|
return m_scrinfo.format.bitsPerPixel/8 *
|
|
m_scrinfo.framebufferWidth *
|
|
m_scrinfo.framebufferHeight;
|
|
}
|
|
|
|
void
|
|
vncDesktop::FillDisplayInfo(rfbServerInitMsg *scrinfo)
|
|
{
|
|
memcpy(scrinfo, &m_scrinfo, sz_rfbServerInitMsg);
|
|
}
|
|
|
|
// Function to capture an area of the screen immediately prior to sending
|
|
// an update.
|
|
|
|
void vncDesktop::CaptureScreen(RECT &UpdateArea, BYTE *scrBuff)
|
|
{
|
|
// ASSUME rect related to virtual desktop
|
|
if (m_videodriver && m_videodriver->IsDirectAccessInEffect())
|
|
CaptureScreenFromMirage(UpdateArea, scrBuff);
|
|
else CaptureScreenFromAdapterGeneral(UpdateArea, scrBuff);
|
|
}
|
|
|
|
void vncDesktop::CaptureScreenFromAdapterGeneral(RECT rect, BYTE *scrBuff)
|
|
{
|
|
// ASSUME rect related to virtual desktop
|
|
// Protect the memory bitmap
|
|
omni_mutex_lock l(m_bitbltlock);
|
|
|
|
// Finish drawing anything in this thread
|
|
// Wish we could do this for the whole system - maybe we should
|
|
// do something with LockWindowUpdate here.
|
|
GdiFlush();
|
|
|
|
// Select the memory bitmap into the memory DC
|
|
HBITMAP oldbitmap;
|
|
if ((oldbitmap = (HBITMAP) SelectObject(m_hmemdc, m_membitmap)) == NULL)
|
|
return;
|
|
|
|
// Capture screen into bitmap
|
|
BOOL blitok = BitBlt(
|
|
m_hmemdc,
|
|
// source in m_hrootdc is relative to a virtual desktop,
|
|
// whereas dst coordinates of m_hmemdc are relative to its top-left corner (0, 0)
|
|
rect.left - m_bmrect.left,
|
|
rect.top - m_bmrect.top,
|
|
rect.right - rect.left,
|
|
rect.bottom - rect.top,
|
|
m_hrootdc,
|
|
rect.left, rect.top,
|
|
SRCCOPY);
|
|
|
|
// Select the old bitmap back into the memory DC
|
|
SelectObject(m_hmemdc, oldbitmap);
|
|
|
|
if (blitok)
|
|
{
|
|
// Copy the new data to the screen buffer (CopyToBuffer optimises this if possible)
|
|
CopyToBuffer(rect, scrBuff);
|
|
}
|
|
|
|
}
|
|
|
|
void vncDesktop::CaptureScreenFromMirage(RECT UpdateArea, BYTE *scrBuff)
|
|
{
|
|
// ASSUME rect related to virtual desktop
|
|
_ASSERTE(m_videodriver);
|
|
omni_mutex_lock l(m_bitbltlock);
|
|
CopyToBuffer(UpdateArea, scrBuff, m_videodriver->GetScreenView());
|
|
}
|
|
|
|
void vncDesktop::CaptureMouseRect()
|
|
{
|
|
POINT CursorPos;
|
|
ICONINFO IconInfo;
|
|
|
|
// If the mouse cursor handle is invalid then forget it
|
|
if (m_hcursor == NULL)
|
|
return;
|
|
|
|
// Get the cursor position
|
|
if (!GetCursorPos(&CursorPos))
|
|
return;
|
|
|
|
// Translate position for hotspot
|
|
if (GetIconInfo(m_hcursor, &IconInfo))
|
|
{
|
|
CursorPos.x -= ((int) IconInfo.xHotspot);
|
|
CursorPos.y -= ((int) IconInfo.yHotspot);
|
|
|
|
if (IconInfo.hbmMask != NULL)
|
|
DeleteObject(IconInfo.hbmMask);
|
|
if (IconInfo.hbmColor != NULL)
|
|
DeleteObject(IconInfo.hbmColor);
|
|
}
|
|
|
|
// Save the bounding rectangle
|
|
m_cursorpos.left = CursorPos.x;
|
|
m_cursorpos.top = CursorPos.y;
|
|
m_cursorpos.right = CursorPos.x + GetSystemMetrics(SM_CXCURSOR);
|
|
m_cursorpos.bottom = CursorPos.y + GetSystemMetrics(SM_CYCURSOR);
|
|
}
|
|
|
|
// Add the mouse pointer to the buffer
|
|
void vncDesktop::CaptureMouse(BYTE *scrBuff, UINT scrBuffSize)
|
|
{
|
|
// Protect the memory bitmap
|
|
omni_mutex_lock l(m_bitbltlock);
|
|
|
|
CaptureMouseRect();
|
|
|
|
// Select the memory bitmap into the memory DC
|
|
HBITMAP oldbitmap;
|
|
if ((oldbitmap = (HBITMAP) SelectObject(m_hmemdc, m_membitmap)) == NULL)
|
|
return;
|
|
|
|
// Draw the cursor
|
|
DrawIconEx(
|
|
m_hmemdc, // handle to device context
|
|
m_cursorpos.left - m_bmrect.left,
|
|
m_cursorpos.top - m_bmrect.top,
|
|
m_hcursor, // handle to icon to draw
|
|
0,0, // width of the icon
|
|
0, // index of frame in animated cursor
|
|
NULL, // handle to background brush
|
|
DI_NORMAL // icon-drawing flags
|
|
);
|
|
|
|
// Select the old bitmap back into the memory DC
|
|
SelectObject(m_hmemdc, oldbitmap);
|
|
|
|
// Clip the bounding rect to the screen
|
|
RECT screen = m_server->GetSharedRect();
|
|
// Copy the mouse cursor into the screen buffer, if any of it is visible
|
|
if (IntersectRect(&m_cursorpos, &m_cursorpos, &screen))
|
|
CopyToBuffer(m_cursorpos, scrBuff);
|
|
}
|
|
|
|
// Obtain cursor image data in server's local format.
|
|
// The length of databuf[] should be at least (width * height * 4).
|
|
BOOL
|
|
vncDesktop::GetRichCursorData(BYTE *databuf, HCURSOR hcursor, int width, int height)
|
|
{
|
|
// Protect the memory bitmap (is it really necessary here?)
|
|
omni_mutex_lock l(m_bitbltlock);
|
|
|
|
// Create bitmap, select it into memory DC
|
|
HBITMAP membitmap = CreateCompatibleBitmap(m_hrootdc, width, height);
|
|
if (membitmap == NULL) {
|
|
return FALSE;
|
|
}
|
|
HBITMAP oldbitmap = (HBITMAP) SelectObject(m_hmemdc, membitmap);
|
|
if (oldbitmap == NULL) {
|
|
DeleteObject(membitmap);
|
|
return FALSE;
|
|
}
|
|
|
|
// Draw the cursor
|
|
DrawIconEx(m_hmemdc, 0, 0, hcursor, 0, 0, 0, NULL, DI_IMAGE);
|
|
SelectObject(m_hmemdc, oldbitmap);
|
|
|
|
// Prepare BITMAPINFO structure (copy most m_bminfo fields)
|
|
BITMAPINFO *bmi = (BITMAPINFO *)calloc(1, sizeof(BITMAPINFO) + 256 * sizeof(RGBQUAD));
|
|
memcpy(bmi, &m_bminfo.bmi, sizeof(BITMAPINFO) + 256 * sizeof(RGBQUAD));
|
|
bmi->bmiHeader.biWidth = width;
|
|
bmi->bmiHeader.biHeight = -height;
|
|
|
|
// Clear data buffer and extract RGB data
|
|
memset(databuf, 0x00, width * height * 4);
|
|
int lines = GetDIBits(m_hmemdc, membitmap, 0, height, databuf, bmi, DIB_RGB_COLORS);
|
|
|
|
// Cleanup
|
|
free(bmi);
|
|
DeleteObject(membitmap);
|
|
|
|
return (lines != 0);
|
|
}
|
|
|
|
// Return the current mouse pointer position
|
|
RECT
|
|
vncDesktop::MouseRect()
|
|
{
|
|
return m_cursorpos;
|
|
}
|
|
|
|
void vncDesktop::SetCursor(HCURSOR cursor)
|
|
{
|
|
if (cursor == NULL)
|
|
m_hcursor = m_hdefcursor;
|
|
else
|
|
m_hcursor = cursor;
|
|
}
|
|
|
|
//
|
|
// Convert text from Unix (LF only) format to CR+LF.
|
|
// NOTE: The size of dst[] buffer must be at least (strlen(src) * 2 + 1).
|
|
//
|
|
|
|
void
|
|
vncDesktop::ConvertClipText(char *dst, const char *src)
|
|
{
|
|
const char *ptr0 = src;
|
|
const char *ptr1;
|
|
#ifdef __x64__
|
|
__int64 dst_pos = 0;
|
|
#else
|
|
int dst_pos = 0;
|
|
#endif
|
|
|
|
while ((ptr1 = strchr(ptr0, '\n')) != NULL) {
|
|
// Copy the string before the LF
|
|
if (ptr1 != ptr0) {
|
|
memcpy(&dst[dst_pos], ptr0, ptr1 - ptr0);
|
|
dst_pos += ptr1 - ptr0;
|
|
}
|
|
// Don't insert CR if there is one already
|
|
if (ptr1 == ptr0 || *(ptr1 - 1) != '\r') {
|
|
dst[dst_pos++] = '\r';
|
|
}
|
|
// Append LF
|
|
dst[dst_pos++] = '\n';
|
|
// Next position in the source text
|
|
ptr0 = ptr1 + 1;
|
|
}
|
|
// Copy the last string with no LF, but with '\0'
|
|
memcpy(&dst[dst_pos], ptr0, &src[strlen(src)] - ptr0 + 1);
|
|
}
|
|
|
|
//
|
|
// Manipulation of the clipboard
|
|
//
|
|
|
|
void
|
|
vncDesktop::SetClipText(LPSTR text)
|
|
{
|
|
// Open the system clipboard
|
|
if (OpenClipboard(m_hwnd))
|
|
{
|
|
// Empty it
|
|
if (EmptyClipboard())
|
|
{
|
|
HANDLE hMem = GlobalAlloc(GMEM_MOVEABLE | GMEM_DDESHARE,
|
|
strlen(text) * 2 + 1);
|
|
|
|
if (hMem != NULL)
|
|
{
|
|
LPSTR pMem = (char*)GlobalLock(hMem);
|
|
|
|
// Get the data (with line endings converted to CR+LF)
|
|
ConvertClipText(pMem, text);
|
|
|
|
// Tell the clipboard
|
|
GlobalUnlock(hMem);
|
|
SetClipboardData(CF_TEXT, hMem);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Now close it
|
|
CloseClipboard();
|
|
}
|
|
|
|
// INTERNAL METHODS
|
|
|
|
inline void
|
|
vncDesktop::MaskToMaxAndShift(DWORD mask, CARD16 &max, CARD8 &shift)
|
|
{
|
|
for (shift = 0; (mask & 1) == 0; shift++)
|
|
mask >>= 1;
|
|
max = (CARD16) mask;
|
|
}
|
|
|
|
// Copy data from the memory bitmap into a buffer
|
|
void vncDesktop::CopyToBuffer(RECT rect, BYTE *destbuff)
|
|
{
|
|
// Are we being asked to blit from the DIBsection to itself?
|
|
if (destbuff == m_DIBbits)
|
|
{
|
|
// Yes. Ignore the request!
|
|
return;
|
|
}
|
|
|
|
// Protect the memory bitmap
|
|
omni_mutex_lock l(m_bitbltlock);
|
|
|
|
const int crect_re_vd_left = rect.left - m_bmrect.left;
|
|
const int crect_re_vd_top = rect.top - m_bmrect.top;
|
|
_ASSERTE(crect_re_vd_left >= 0);
|
|
_ASSERTE(crect_re_vd_top >= 0);
|
|
|
|
// Calculate the scanline-ordered y position to copy from
|
|
// NB: m_membitmap is bottom2top
|
|
const int y_inv_re_vd = m_bmrect.bottom - m_bmrect.top - rect.bottom;
|
|
_ASSERTE(y_inv_re_vd >= 0);
|
|
|
|
// Calculate where in the output buffer to put the data
|
|
BYTE * destbuffpos = destbuff + (m_bytesPerRow * crect_re_vd_top);
|
|
|
|
// Set the number of bytes for GetDIBits to actually write
|
|
// NOTE : GetDIBits pads the destination buffer if biSizeImage < no. of bytes required
|
|
m_bminfo.bmi.bmiHeader.biSizeImage = (rect.bottom-rect.top) * m_bytesPerRow;
|
|
|
|
// Get the actual bits from the bitmap into the bit buffer
|
|
// If fast (DIBsection) blits are disabled then use the old GetDIBits technique
|
|
if (m_DIBbits == NULL)
|
|
{
|
|
if (GetDIBits(
|
|
m_hmemdc,
|
|
m_membitmap,
|
|
y_inv_re_vd,
|
|
rect.bottom - rect.top,
|
|
destbuffpos,
|
|
&m_bminfo.bmi,
|
|
DIB_RGB_COLORS) == 0)
|
|
{
|
|
#ifdef _MSC_VER
|
|
_RPT1(_CRT_WARN, "vncDesktop : [1] GetDIBits failed! %d\n", GetLastError());
|
|
_RPT3(_CRT_WARN, "vncDesktop : thread = %d, DC = %d, bitmap = %d\n", omni_thread::self(), m_hmemdc, m_membitmap);
|
|
_RPT2(_CRT_WARN, "vncDesktop : y = %d, height = %d\n", y_inv_re_vd, (rect.bottom-rect.top));
|
|
#endif
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Fast blits are enabled. [I have a sneaking suspicion this will never get used, unless
|
|
// something weird goes wrong in the code. It's here to keep the function general, though!]
|
|
|
|
const int bytesPerPixel = m_scrinfo.format.bitsPerPixel / 8;
|
|
BYTE *srcbuffpos = (BYTE*)m_DIBbits;
|
|
|
|
srcbuffpos += (m_bytesPerRow * crect_re_vd_top) + (bytesPerPixel * crect_re_vd_left);
|
|
destbuffpos += bytesPerPixel * crect_re_vd_left;
|
|
|
|
const int widthBytes = (rect.right - rect.left) * bytesPerPixel;
|
|
|
|
for (int y = rect.top; y < rect.bottom; y++)
|
|
{
|
|
memcpy(destbuffpos, srcbuffpos, widthBytes);
|
|
srcbuffpos += m_bytesPerRow;
|
|
destbuffpos += m_bytesPerRow;
|
|
}
|
|
}
|
|
}
|
|
|
|
void vncDesktop::CopyToBuffer(RECT rect, BYTE *destbuff, const BYTE *srcbuffpos)
|
|
{
|
|
const int crect_re_vd_left = rect.left - m_bmrect.left;
|
|
const int crect_re_vd_top = rect.top - m_bmrect.top;
|
|
_ASSERTE(crect_re_vd_left >= 0);
|
|
_ASSERTE(crect_re_vd_top >= 0);
|
|
|
|
const int bytesPerPixel = m_scrinfo.format.bitsPerPixel / 8;
|
|
|
|
const int bmoffset = (m_bytesPerRow * crect_re_vd_top) + (bytesPerPixel * crect_re_vd_left);
|
|
BYTE *destbuffpos = destbuff + bmoffset;
|
|
srcbuffpos += bmoffset;
|
|
|
|
const int widthBytes = (rect.right - rect.left) * bytesPerPixel;
|
|
|
|
for (int y = rect.top; y < rect.bottom; y++)
|
|
{
|
|
memcpy(destbuffpos, srcbuffpos, widthBytes);
|
|
srcbuffpos += m_bytesPerRow;
|
|
destbuffpos += m_bytesPerRow;
|
|
}
|
|
}
|
|
|
|
// Callback routine used internally to catch window movement...
|
|
BOOL CALLBACK
|
|
EnumWindowsFnCopyRect(HWND hwnd, LPARAM arg)
|
|
{
|
|
|
|
//For excluding the popup windows
|
|
if ((GetWindowLong( hwnd, GWL_STYLE) & WS_POPUP) ==0)
|
|
{
|
|
|
|
HANDLE prop = GetProp(hwnd, (LPCTSTR) MAKELONG(VNC_WINDOWPOS_ATOM, 0));
|
|
if (prop != NULL) {
|
|
|
|
if (IsWindowVisible(hwnd)) {
|
|
|
|
RECT dest;
|
|
POINT source;
|
|
|
|
// Get the window rectangle
|
|
if (GetWindowRect(hwnd, &dest)) {
|
|
// Old position
|
|
source.x = (SHORT) LOWORD(prop);
|
|
source.y = (SHORT) HIWORD(prop);
|
|
|
|
// Got the destination position. Now send to clients!
|
|
if ((source.x != dest.left) || (source.y != dest.top)) {
|
|
// Update the property entry
|
|
SHORT x = (SHORT) dest.left;
|
|
SHORT y = (SHORT) dest.top;
|
|
SetProp(hwnd,
|
|
(LPCTSTR) MAKELONG(VNC_WINDOWPOS_ATOM, 0),
|
|
(HANDLE) MAKELONG(x, y));
|
|
|
|
// Store of the copyrect
|
|
((vncDesktop*)arg)->CopyRect(dest, source);
|
|
|
|
}
|
|
} else {
|
|
RemoveProp(hwnd, (LPCTSTR) MAKELONG(VNC_WINDOWPOS_ATOM, 0));
|
|
}
|
|
} else {
|
|
RemoveProp(hwnd, (LPCTSTR) MAKELONG(VNC_WINDOWPOS_ATOM, 0));
|
|
}
|
|
} else {
|
|
// If the window has become visible then save its position!
|
|
if (IsWindowVisible(hwnd)) {
|
|
RECT dest;
|
|
|
|
if (GetWindowRect(hwnd, &dest)) {
|
|
SHORT x = (SHORT) dest.left;
|
|
SHORT y = (SHORT) dest.top;
|
|
SetProp(hwnd,
|
|
(LPCTSTR) MAKELONG(VNC_WINDOWPOS_ATOM, 0),
|
|
(HANDLE) MAKELONG(x, y));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
void
|
|
vncDesktop::SetLocalInputDisableHook(BOOL enable)
|
|
{
|
|
//SetKeyboardFilterHook(enable);
|
|
//SetMouseFilterHook(enable);
|
|
}
|
|
|
|
void
|
|
vncDesktop::SetLocalInputPriorityHook(BOOL enable)
|
|
{
|
|
if (vncService::IsWin95()) {
|
|
//SetKeyboardPriorityHook(m_hwnd,enable,RFB_LOCAL_KEYBOARD);
|
|
//SetMousePriorityHook(m_hwnd,enable,RFB_LOCAL_MOUSE);
|
|
} else {
|
|
//SetKeyboardPriorityLLHook(m_hwnd,enable,RFB_LOCAL_KEYBOARD);
|
|
//SetMousePriorityLLHook(m_hwnd,enable,RFB_LOCAL_MOUSE);
|
|
}
|
|
|
|
if (!enable)
|
|
// FIXME: incremental semantics broken here;
|
|
// that's why we're compelled to consume extra unlocks
|
|
m_server->BlockRemoteInput(false);
|
|
}
|
|
|
|
// Routine to find out which windows have moved
|
|
void
|
|
vncDesktop::CalcCopyRects()
|
|
{
|
|
// Enumerate all the desktop windows for movement
|
|
EnumWindows((WNDENUMPROC)EnumWindowsFnCopyRect, (LPARAM) this);
|
|
}
|
|
|
|
|
|
// Window procedure for the Desktop window
|
|
LRESULT CALLBACK
|
|
DesktopWndProc(HWND hwnd, UINT iMsg, WPARAM wParam, LPARAM lParam)
|
|
{
|
|
vncDesktop *_this = (vncDesktop*)GetWindowLong(hwnd, -21);// #define GWL_USERDATA (-2
|
|
|
|
switch (iMsg)
|
|
{
|
|
|
|
// GENERAL
|
|
|
|
case WM_DISPLAYCHANGE:
|
|
// The display resolution is changing
|
|
|
|
// We must kick off any clients since their screen size will be wrong
|
|
_this->m_displaychanged = TRUE;
|
|
return 0;
|
|
|
|
case WM_SYSCOLORCHANGE:
|
|
case WM_PALETTECHANGED:
|
|
// The palette colours have changed, so tell the server
|
|
|
|
// Get the system palette
|
|
if (!_this->SetPalette())
|
|
PostQuitMessage(0);
|
|
// Update any palette-based clients, too
|
|
_this->m_server->UpdatePalette();
|
|
return 0;
|
|
|
|
case WM_TIMER:
|
|
switch (wParam) {
|
|
case vncDesktop::TIMER_POLL:
|
|
_this->SetPollingFlag(true);
|
|
break;
|
|
case vncDesktop::TIMER_BLANK_SCREEN:
|
|
if (_this->m_server->GetBlankScreen())
|
|
_this->BlankScreen(TRUE);
|
|
break;
|
|
case vncDesktop::TIMER_RESTORE_SCREEN:
|
|
_this->BlankScreen(FALSE);
|
|
break;
|
|
}
|
|
return 0;
|
|
|
|
// CLIPBOARD MESSAGES
|
|
|
|
case WM_CHANGECBCHAIN:
|
|
// The clipboard chain has changed - check our nextviewer handle
|
|
if ((HWND)wParam == _this->m_hnextviewer)
|
|
_this->m_hnextviewer = (HWND)lParam;
|
|
else
|
|
if (_this->m_hnextviewer != NULL)
|
|
SendMessage(_this->m_hnextviewer,
|
|
WM_CHANGECBCHAIN,
|
|
wParam, lParam);
|
|
|
|
return 0;
|
|
|
|
case WM_DRAWCLIPBOARD:
|
|
// The clipboard contents have changed
|
|
if((GetClipboardOwner() != _this->Window()) &&
|
|
_this->m_initialClipBoardSeen &&
|
|
_this->m_clipboard_active)
|
|
{
|
|
LPSTR cliptext = NULL;
|
|
|
|
// Open the clipboard
|
|
if (OpenClipboard(_this->Window()))
|
|
{
|
|
// Get the clipboard data
|
|
HGLOBAL cliphandle = GetClipboardData(CF_TEXT);
|
|
if (cliphandle != NULL)
|
|
{
|
|
LPSTR clipdata = (LPSTR) GlobalLock(cliphandle);
|
|
|
|
// Copy it into a new buffer
|
|
if (clipdata == NULL)
|
|
cliptext = NULL;
|
|
else
|
|
cliptext = strdup(clipdata);
|
|
|
|
// Release the buffer and close the clipboard
|
|
GlobalUnlock(cliphandle);
|
|
}
|
|
|
|
CloseClipboard();
|
|
}
|
|
|
|
if (cliptext != NULL)
|
|
{
|
|
size_t cliplen = strlen(cliptext);
|
|
LPSTR unixtext = (char *)malloc(cliplen+1);
|
|
|
|
// Replace CR-LF with LF - never send CR-LF on the wire,
|
|
// since Unix won't like it
|
|
int unixpos=0;
|
|
for (size_t x=0; x<cliplen; x++)
|
|
{
|
|
if (cliptext[x] != '\x0d')
|
|
{
|
|
unixtext[unixpos] = cliptext[x];
|
|
unixpos++;
|
|
}
|
|
}
|
|
unixtext[unixpos] = 0;
|
|
|
|
// Free the clip text
|
|
free(cliptext);
|
|
cliptext = NULL;
|
|
|
|
// Now send the unix text to the server
|
|
_this->m_server->UpdateClipText(unixtext);
|
|
|
|
free(unixtext);
|
|
}
|
|
}
|
|
|
|
_this->m_initialClipBoardSeen = TRUE;
|
|
|
|
if (_this->m_hnextviewer != NULL)
|
|
{
|
|
// Pass the message to the next window in clipboard viewer chain.
|
|
return SendMessage(_this->m_hnextviewer, WM_DRAWCLIPBOARD, 0,0);
|
|
}
|
|
|
|
return 0;
|
|
|
|
default:
|
|
return DefWindowProc(hwnd, iMsg, wParam, lParam);
|
|
}
|
|
}
|
|
|
|
BOOL vncDesktop::CheckUpdates()
|
|
{
|
|
#ifndef _DEBUG
|
|
try
|
|
{
|
|
#endif
|
|
// Re-install polling timer if necessary
|
|
if (m_server->PollingCycleChanged())
|
|
{
|
|
SetPollingTimer();
|
|
m_server->PollingCycleChanged(false);
|
|
}
|
|
|
|
// Update the state of blank screen timer
|
|
UpdateBlankScreenTimer();
|
|
|
|
// Has the display resolution or desktop changed?
|
|
if (m_displaychanged || !vncService::InputDesktopSelected() || !inConsoleSession())
|
|
{
|
|
|
|
rfbServerInitMsg oldscrinfo = m_scrinfo;
|
|
m_displaychanged = FALSE;
|
|
|
|
// Attempt to close the old hooks
|
|
if (!Shutdown())
|
|
{
|
|
|
|
m_server->KillAuthClients();
|
|
return FALSE;
|
|
}
|
|
|
|
// Now attempt to re-install them!
|
|
ChangeResNow();
|
|
|
|
if (!Startup())
|
|
{
|
|
|
|
m_server->KillAuthClients();
|
|
return FALSE;
|
|
}
|
|
|
|
// Check if the screen info has changed
|
|
|
|
// Call this regardless of screen format change
|
|
m_server->UpdateLocalFormat();
|
|
|
|
// Add a full screen update to all the clients
|
|
m_changed_rgn.AddRect(m_bmrect);
|
|
m_server->UpdatePalette();
|
|
}
|
|
|
|
// TRIGGER THE UPDATE
|
|
|
|
RECT rect = m_server->GetSharedRect();
|
|
RECT new_rect = GetSourceRect();
|
|
IntersectRect(&new_rect, &new_rect, &m_bmrect);
|
|
|
|
// Update screen size if required
|
|
if (!EqualRect(&new_rect, &rect))
|
|
{
|
|
m_server->SetSharedRect(new_rect);
|
|
bool sendnewfb = false;
|
|
|
|
if (rect.right - rect.left != new_rect.right - new_rect.left ||
|
|
rect.bottom - rect.top != new_rect.bottom - new_rect.top)
|
|
sendnewfb = true;
|
|
|
|
// FIXME: We should not send NewFBSize if a client
|
|
// did not send framebuffer update request.
|
|
m_server->SetNewFBSize(sendnewfb);
|
|
|
|
m_changed_rgn.Clear();
|
|
|
|
if (sendnewfb && m_server->WindowShared())
|
|
{
|
|
if (new_rect.right - new_rect.left == 0 &&
|
|
new_rect.bottom - new_rect.top == 0)
|
|
{
|
|
// window is minimized
|
|
return TRUE;
|
|
}
|
|
else
|
|
{
|
|
// window is restored
|
|
// window is resized
|
|
m_changed_rgn.AddRect(new_rect);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
// If we have clients full region requests
|
|
if (m_server->FullRgnRequested())
|
|
{
|
|
// Capture screen to main buffer
|
|
CaptureScreen(rect, m_mainbuff);
|
|
// If we have a video driver - reset counter
|
|
if ( m_videodriver != NULL && m_videodriver->IsActive())
|
|
{
|
|
m_videodriver->ResetCounter();
|
|
}
|
|
}
|
|
|
|
// If we have incremental update requests
|
|
if (m_server->IncrRgnRequested())
|
|
{
|
|
vncRegion rgn;
|
|
|
|
// Use either a mirror video driver, or perform polling
|
|
if (m_videodriver != NULL && m_videodriver->IsActive())
|
|
{
|
|
// FIXME: If there were no incremental update requests
|
|
// for some time, we will loose updates.
|
|
// IMPORTANT: Mirage outputs the regions re (0, 0)
|
|
// so we have to offset them re virtual display
|
|
|
|
// TODOTODO
|
|
BOOL bCursorShape = FALSE;
|
|
|
|
m_videodriver->HandleDriverChanges(
|
|
this,
|
|
m_changed_rgn,
|
|
m_bmrect.left,
|
|
m_bmrect.top,
|
|
bCursorShape);
|
|
}
|
|
else
|
|
{
|
|
if (GetPollingFlag())
|
|
{
|
|
SetPollingFlag(false);
|
|
PerformPolling();
|
|
}
|
|
}
|
|
|
|
// Check for moved windows
|
|
// PrimaryDisplayOnlyShared: check if any problems when
|
|
// dragging from another display
|
|
if ((m_server->FullScreen() || m_server->PrimaryDisplayOnlyShared()) &&
|
|
!(m_videodriver && m_videodriver->IsHandlingScreen2ScreenBlt()))
|
|
{
|
|
CalcCopyRects();
|
|
}
|
|
|
|
if (m_copyrect_set)
|
|
{
|
|
// Send copyrect to all clients
|
|
m_server->CopyRect(m_copyrect_rect, m_copyrect_src);
|
|
m_copyrect_set = false;
|
|
|
|
// DEBUG: Continue auditing the code from this point.
|
|
|
|
// IMPORTANT: this order: CopyRectToBuffer, CaptureScreen, GetChangedRegion
|
|
// Copy old window rect to back buffer
|
|
CopyRectToBuffer(m_copyrect_rect, m_copyrect_src);
|
|
|
|
// Copy new window rect to main buffer
|
|
CaptureScreen(m_copyrect_rect, m_mainbuff);
|
|
|
|
// Get changed pixels to rgn
|
|
GetChangedRegion(rgn, m_copyrect_rect);
|
|
|
|
RECT rect;
|
|
rect.left= m_copyrect_src.x;
|
|
rect.top = m_copyrect_src.y;
|
|
rect.right = rect.left + (m_copyrect_rect.right - m_copyrect_rect.left);
|
|
rect.bottom = rect.top + (m_copyrect_rect.bottom - m_copyrect_rect.top);
|
|
// Refresh old window rect
|
|
m_changed_rgn.AddRect(rect);
|
|
// Don't refresh new window rect
|
|
m_changed_rgn.SubtractRect(m_copyrect_rect);
|
|
}
|
|
|
|
// Get only desktop area
|
|
vncRegion temprgn;
|
|
temprgn.Clear();
|
|
temprgn.AddRect(rect);
|
|
m_changed_rgn.Intersect(temprgn);
|
|
|
|
// Get list of rectangles for checking
|
|
rectlist rectsToScan;
|
|
m_changed_rgn.Rectangles(rectsToScan);
|
|
|
|
// Capture and check them
|
|
CheckRects(rgn, rectsToScan);
|
|
|
|
// Update the mouse
|
|
m_server->UpdateMouse();
|
|
|
|
// Send changed region data to all clients
|
|
m_server->UpdateRegion(rgn);
|
|
|
|
// Clear changed region
|
|
m_changed_rgn.Clear();
|
|
}
|
|
|
|
// Trigger an update to be sent
|
|
if (m_server->FullRgnRequested() || m_server->IncrRgnRequested())
|
|
{
|
|
m_server->TriggerUpdate();
|
|
}
|
|
|
|
#ifndef _DEBUG
|
|
}
|
|
catch (...)
|
|
{
|
|
m_server->KillAuthClients();
|
|
return FALSE;
|
|
}
|
|
#endif
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
void
|
|
vncDesktop::SetPollingTimer()
|
|
{
|
|
const UINT driverCycle = 30;
|
|
const UINT minPollingCycle = 5;
|
|
|
|
UINT msec;
|
|
if (m_videodriver != NULL) {
|
|
msec = driverCycle;
|
|
} else {
|
|
msec = m_server->GetPollingCycle() / 16;
|
|
if (msec < minPollingCycle) {
|
|
msec = minPollingCycle;
|
|
}
|
|
}
|
|
m_timer_polling = (UINT)SetTimer(Window(), TIMER_POLL, msec, NULL);
|
|
}
|
|
|
|
inline void vncDesktop::CheckRects(vncRegion &rgn, rectlist &rects)
|
|
{
|
|
#ifndef _DEBUG
|
|
try
|
|
{
|
|
#endif
|
|
rectlist::iterator i;
|
|
|
|
for (i = rects.begin(); i != rects.end(); i++)
|
|
{
|
|
// Copy data to the main buffer
|
|
// FIXME: Maybe call CaptureScreen() just once?
|
|
// Check what would be more efficient.
|
|
CaptureScreen(*i, m_mainbuff);
|
|
|
|
// Check for changes in the rectangle
|
|
GetChangedRegion(rgn, *i);
|
|
}
|
|
#ifndef _DEBUG
|
|
}
|
|
catch (...)
|
|
{
|
|
throw;
|
|
}
|
|
#endif
|
|
}
|
|
|
|
// This notably improves performance when using Visual C++ 6.0 compiler
|
|
#pragma function(memcpy, memcmp)
|
|
|
|
static const int BLOCK_SIZE = 32;
|
|
|
|
/*
|
|
// A dummy version of GetChangedRegion() created for troubleshoot purposes
|
|
// when GetChangedRegion() et al are suspected for bugs/need changes.
|
|
// The code below is as simple and clear as possible.
|
|
void vncDesktop::GetChangedRegion(vncRegion &rgn, const RECT &rect)
|
|
{
|
|
rgn.AddRect(rect);
|
|
|
|
// Copy the changes to the back buffer
|
|
const int c2rect_re_vd_top = rect.top - m_bmrect.top;
|
|
const int c3rect_re_vd_left = rect.left - m_bmrect.left;
|
|
_ASSERTE(c2rect_re_vd_top >= 0);
|
|
_ASSERTE(c3rect_re_vd_left >= 0);
|
|
|
|
const UINT bytesPerPixel = m_scrinfo.format.bitsPerPixel / 8;
|
|
const int offset = c2rect_re_vd_top * m_bytesPerRow + c3rect_re_vd_left * bytesPerPixel;
|
|
|
|
unsigned char *o_ptr = m_backbuff + offset;
|
|
unsigned char *n_ptr = m_mainbuff + offset;
|
|
const int bytes_in_row = (rect.right - rect.left) * bytesPerPixel;
|
|
for (int y = rect.top; y < rect.bottom; y++)
|
|
{
|
|
memcpy(o_ptr, n_ptr, bytes_in_row);
|
|
n_ptr += m_bytesPerRow;
|
|
o_ptr += m_bytesPerRow;
|
|
}
|
|
}
|
|
*/
|
|
|
|
/*
|
|
// DEBUG: Another dumb and slow version of GetChangedRegion().
|
|
void vncDesktop::GetChangedRegion(vncRegion &rgn, const RECT &rect)
|
|
{
|
|
RECT newRect;
|
|
|
|
|
|
// Copy the changes to the back buffer
|
|
const int top = rect.top - m_bmrect.top;
|
|
const int left = rect.left - m_bmrect.left;
|
|
_ASSERTE(top >= 0);
|
|
_ASSERTE(left >= 0);
|
|
|
|
const UINT bytesPerPixel = m_scrinfo.format.bitsPerPixel / 8;
|
|
const int offset = top * m_bytesPerRow + left * bytesPerPixel;
|
|
|
|
unsigned char *o_ptr = m_backbuff + offset;
|
|
unsigned char *n_ptr = m_mainbuff + offset;
|
|
const int bytes_in_row = (rect.right - rect.left) * bytesPerPixel;
|
|
for (int y = rect.top; y < rect.bottom; y++) {
|
|
int x0 = rect.left;
|
|
while (x0 < rect.right) {
|
|
unsigned char *pNew = m_mainbuff + y * m_bytesPerRow + x0 * bytesPerPixel;
|
|
unsigned char *pOld = m_backbuff + y * m_bytesPerRow + x0 * bytesPerPixel;
|
|
if (memcmp(pOld, pNew, bytesPerPixel) != 0) {
|
|
break; // x0 points to the first difference at the left
|
|
}
|
|
x0++;
|
|
}
|
|
SetRect(&newRect, x0, y, rect.right, y + 1);
|
|
if (newRect.right - newRect.left > 0 && newRect.bottom - newRect.top > 0) {
|
|
rgn.AddRect(newRect);
|
|
}
|
|
|
|
memcpy(o_ptr, n_ptr, bytes_in_row);
|
|
n_ptr += m_bytesPerRow;
|
|
o_ptr += m_bytesPerRow;
|
|
}
|
|
}
|
|
*/
|
|
|
|
void vncDesktop::GetChangedRegion(vncRegion &rgn, const RECT &rect)
|
|
{
|
|
const UINT bytesPerPixel = m_scrinfo.format.bitsPerPixel / 8;
|
|
const int bytes_per_scanline = (rect.right - rect.left) * bytesPerPixel;
|
|
|
|
const int crect_re_vd_left = rect.left - m_bmrect.left;
|
|
const int crect_re_vd_top = rect.top - m_bmrect.top;
|
|
_ASSERTE(crect_re_vd_left >= 0);
|
|
_ASSERTE(crect_re_vd_top >= 0);
|
|
|
|
const int offset = crect_re_vd_top * m_bytesPerRow + crect_re_vd_left * bytesPerPixel;
|
|
unsigned char *o_ptr = m_backbuff + offset;
|
|
unsigned char *n_ptr = m_mainbuff + offset;
|
|
|
|
RECT new_rect = rect;
|
|
|
|
// Fast processing for small rectangles
|
|
if (rect.right - rect.left <= BLOCK_SIZE &&
|
|
rect.bottom - rect.top <= BLOCK_SIZE)
|
|
{
|
|
for (int y = rect.top; y < rect.bottom; y++)
|
|
{
|
|
if (memcmp(o_ptr, n_ptr, bytes_per_scanline) != 0)
|
|
{
|
|
new_rect.top = y;
|
|
UpdateChangedSubRect(rgn, new_rect);
|
|
break;
|
|
}
|
|
o_ptr += m_bytesPerRow;
|
|
n_ptr += m_bytesPerRow;
|
|
}
|
|
return;
|
|
}
|
|
|
|
// Process bigger rectangles
|
|
BOOL bTop4Move = TRUE;
|
|
for (int y = rect.top; y < rect.bottom; y++)
|
|
{
|
|
if (memcmp(o_ptr, n_ptr, bytes_per_scanline) != 0)
|
|
{
|
|
if (bTop4Move)
|
|
{
|
|
new_rect.top = y;
|
|
bTop4Move = FALSE;
|
|
}
|
|
// Skip a number of lines after a non-matched one
|
|
int n = BLOCK_SIZE / 2 - 1;
|
|
y += n;
|
|
o_ptr += n * m_bytesPerRow;
|
|
n_ptr += n * m_bytesPerRow;
|
|
}
|
|
else
|
|
{
|
|
if (!bTop4Move)
|
|
{
|
|
new_rect.bottom = y;
|
|
UpdateChangedRect(rgn, new_rect);
|
|
bTop4Move = TRUE;
|
|
}
|
|
}
|
|
o_ptr += m_bytesPerRow;
|
|
n_ptr += m_bytesPerRow;
|
|
}
|
|
if (!bTop4Move)
|
|
{
|
|
new_rect.bottom = rect.bottom;
|
|
UpdateChangedRect(rgn, new_rect);
|
|
}
|
|
}
|
|
|
|
void vncDesktop::UpdateChangedRect(vncRegion &rgn, const RECT &rect)
|
|
{
|
|
// Pass small rectangles directly to UpdateChangedSubRect
|
|
if (rect.right - rect.left <= BLOCK_SIZE &&
|
|
rect.bottom - rect.top <= BLOCK_SIZE)
|
|
{
|
|
UpdateChangedSubRect(rgn, rect);
|
|
return;
|
|
}
|
|
|
|
const UINT bytesPerPixel = m_scrinfo.format.bitsPerPixel / 8;
|
|
|
|
RECT new_rect;
|
|
int x, y, ay;
|
|
|
|
const int crect_re_vd_left = rect.left - m_bmrect.left;
|
|
const int crect_re_vd_top = rect.top - m_bmrect.top;
|
|
_ASSERTE(crect_re_vd_left >= 0);
|
|
_ASSERTE(crect_re_vd_top >= 0);
|
|
|
|
// Scan down the rectangle
|
|
const int offset = crect_re_vd_top * m_bytesPerRow + crect_re_vd_left * bytesPerPixel;
|
|
unsigned char *o_topleft_ptr = m_backbuff + offset;
|
|
unsigned char *n_topleft_ptr = m_mainbuff + offset;
|
|
|
|
for (y = rect.top; y < rect.bottom; y += BLOCK_SIZE)
|
|
{
|
|
// Work out way down the bitmap
|
|
unsigned char *o_row_ptr = o_topleft_ptr;
|
|
unsigned char *n_row_ptr = n_topleft_ptr;
|
|
|
|
const int blockbottom = Min(y + BLOCK_SIZE, rect.bottom);
|
|
new_rect.bottom = blockbottom;
|
|
|
|
BOOL bLeft4Move = TRUE;
|
|
|
|
for (x = rect.left; x < rect.right; x += BLOCK_SIZE)
|
|
{
|
|
// Work our way across the row
|
|
unsigned char *n_block_ptr = n_row_ptr;
|
|
unsigned char *o_block_ptr = o_row_ptr;
|
|
|
|
const UINT blockright = Min(x + BLOCK_SIZE, rect.right);
|
|
const UINT bytesPerBlockRow = (blockright-x) * bytesPerPixel;
|
|
|
|
// Scan this block
|
|
for (ay = y; ay < blockbottom; ay++)
|
|
{
|
|
if (memcmp(n_block_ptr, o_block_ptr, bytesPerBlockRow) != 0)
|
|
break;
|
|
n_block_ptr += m_bytesPerRow;
|
|
o_block_ptr += m_bytesPerRow;
|
|
}
|
|
if (ay < blockbottom)
|
|
{
|
|
// There were changes, so this block will need to be updated
|
|
if (bLeft4Move)
|
|
{
|
|
new_rect.left = x;
|
|
bLeft4Move = FALSE;
|
|
new_rect.top = ay;
|
|
}
|
|
else if (ay < new_rect.top)
|
|
{
|
|
new_rect.top = ay;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// No changes in this block, process previous changed blocks if any
|
|
if (!bLeft4Move)
|
|
{
|
|
new_rect.right = x;
|
|
UpdateChangedSubRect(rgn, new_rect);
|
|
bLeft4Move = TRUE;
|
|
}
|
|
}
|
|
|
|
o_row_ptr += bytesPerBlockRow;
|
|
n_row_ptr += bytesPerBlockRow;
|
|
}
|
|
|
|
if (!bLeft4Move)
|
|
{
|
|
new_rect.right = rect.right;
|
|
UpdateChangedSubRect(rgn, new_rect);
|
|
}
|
|
|
|
o_topleft_ptr += m_bytesPerRow * BLOCK_SIZE;
|
|
n_topleft_ptr += m_bytesPerRow * BLOCK_SIZE;
|
|
}
|
|
}
|
|
|
|
void vncDesktop::UpdateChangedSubRect(vncRegion &rgn, const RECT &rect)
|
|
{
|
|
const UINT bytesPerPixel = m_scrinfo.format.bitsPerPixel / 8;
|
|
int bytes_in_row = (rect.right - rect.left) * bytesPerPixel;
|
|
int y, i;
|
|
|
|
const int crect_re_vd_left = rect.left - m_bmrect.left;
|
|
const int crect_re_vd_bottom = rect.bottom - m_bmrect.top;
|
|
_ASSERTE(crect_re_vd_left >= 0);
|
|
_ASSERTE(crect_re_vd_bottom >= 0);
|
|
|
|
// Exclude unchanged scan lines at the bottom
|
|
int offset = (crect_re_vd_bottom - 1) * m_bytesPerRow + crect_re_vd_left * bytesPerPixel;
|
|
unsigned char *o_ptr = m_backbuff + offset;
|
|
unsigned char *n_ptr = m_mainbuff + offset;
|
|
RECT final_rect = rect;
|
|
final_rect.bottom = rect.top + 1;
|
|
for (y = rect.bottom - 1; y > rect.top; y--)
|
|
{
|
|
if (memcmp(o_ptr, n_ptr, bytes_in_row) != 0)
|
|
{
|
|
final_rect.bottom = y + 1;
|
|
break;
|
|
}
|
|
n_ptr -= m_bytesPerRow;
|
|
o_ptr -= m_bytesPerRow;
|
|
}
|
|
|
|
// Exclude unchanged pixels at left and right sides
|
|
const int c2rect_re_vd_left = final_rect.left - m_bmrect.left;
|
|
const int c2rect_re_vd_top = final_rect.top - m_bmrect.top;
|
|
_ASSERTE(c2rect_re_vd_left >= 0);
|
|
_ASSERTE(c2rect_re_vd_top >= 0);
|
|
|
|
offset = c2rect_re_vd_top * m_bytesPerRow + c2rect_re_vd_left * bytesPerPixel;
|
|
o_ptr = m_backbuff + offset;
|
|
n_ptr = m_mainbuff + offset;
|
|
int left_delta = bytes_in_row - 1;
|
|
int right_delta = 0;
|
|
for (y = final_rect.top; y < final_rect.bottom; y++)
|
|
{
|
|
for (i = 0; i < bytes_in_row - 1; i++)
|
|
{
|
|
if (n_ptr[i] != o_ptr[i])
|
|
{
|
|
if (i < left_delta)
|
|
left_delta = i;
|
|
break;
|
|
}
|
|
}
|
|
for (i = bytes_in_row - 1; i > 0; i--)
|
|
{
|
|
if (n_ptr[i] != o_ptr[i])
|
|
{
|
|
if (i > right_delta)
|
|
right_delta = i;
|
|
break;
|
|
}
|
|
}
|
|
n_ptr += m_bytesPerRow;
|
|
o_ptr += m_bytesPerRow;
|
|
}
|
|
final_rect.right = final_rect.left + right_delta / bytesPerPixel + 1;
|
|
final_rect.left += left_delta / bytesPerPixel;
|
|
|
|
// Update the rectangle
|
|
rgn.AddRect(final_rect);
|
|
|
|
// Copy the changes to the back buffer
|
|
const int c3rect_re_vd_left = final_rect.left - m_bmrect.left;
|
|
_ASSERTE(c3rect_re_vd_left >= 0);
|
|
|
|
offset = c2rect_re_vd_top * m_bytesPerRow + c3rect_re_vd_left * bytesPerPixel;
|
|
|
|
o_ptr = m_backbuff + offset;
|
|
n_ptr = m_mainbuff + offset;
|
|
bytes_in_row = (final_rect.right - final_rect.left) * bytesPerPixel;
|
|
for (y = final_rect.top; y < final_rect.bottom; y++)
|
|
{
|
|
memcpy(o_ptr, n_ptr, bytes_in_row);
|
|
n_ptr += m_bytesPerRow;
|
|
o_ptr += m_bytesPerRow;
|
|
}
|
|
}
|
|
|
|
|
|
void vncDesktop::PerformPolling()
|
|
{
|
|
if (m_server->PollFullScreen())
|
|
{
|
|
// Poll full screen
|
|
RECT full_rect = m_server->GetSharedRect();
|
|
PollArea(full_rect);
|
|
}
|
|
else
|
|
{
|
|
// Poll a window
|
|
if (m_server->PollForeground())
|
|
{
|
|
// Get the window rectangle for the currently selected window
|
|
HWND hwnd = GetForegroundWindow();
|
|
if (hwnd != NULL)
|
|
PollWindow(hwnd);
|
|
}
|
|
if (m_server->PollUnderCursor())
|
|
{
|
|
// Find the mouse position
|
|
POINT mousepos;
|
|
if (GetCursorPos(&mousepos))
|
|
{
|
|
// Find the window under the mouse
|
|
HWND hwnd = WindowFromPoint(mousepos);
|
|
if (hwnd != NULL)
|
|
PollWindow(hwnd);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
vncDesktop::PollWindow(HWND hwnd)
|
|
{
|
|
// Are we set to low-load polling?
|
|
if (m_server->PollOnEventOnly())
|
|
{
|
|
// Yes, so only poll if the remote user has done something
|
|
if (!m_server->RemoteEventReceived()) {
|
|
return;
|
|
}
|
|
}
|
|
|
|
// Does the client want us to poll only console windows?
|
|
if (m_server->PollConsoleOnly())
|
|
{
|
|
char classname[20];
|
|
|
|
// Yes, so check that this is a console window...
|
|
if (GetClassName(hwnd, classname, sizeof(classname))) {
|
|
if ((strcmp(classname, "tty") != 0) &&
|
|
(strcmp(classname, "ConsoleWindowClass") != 0)) {
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
RECT full_rect = m_server->GetSharedRect();
|
|
RECT rect;
|
|
|
|
// Get the rectangle
|
|
if (GetWindowRect(hwnd, &rect)) {
|
|
if (IntersectRect(&rect, &rect, &full_rect)) {
|
|
PollArea(rect);
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Implementation of the polling algorithm.
|
|
//
|
|
|
|
void vncDesktop::PollArea(const RECT &rect)
|
|
{
|
|
const int scanLine = m_pollingOrder[m_pollingStep++ % 32];
|
|
const UINT bytesPerPixel = m_scrinfo.format.bitsPerPixel / 8;
|
|
|
|
// Align 32x32 tiles to the left top corner of the shared area
|
|
const RECT shared = m_server->GetSharedRect();
|
|
const int leftAligned = ((rect.left - shared.left) & 0xFFFFFFE0) + shared.left;
|
|
const int topAligned = ((rect.top - shared.top) & 0xFFFFFFE0) + shared.top;
|
|
|
|
RECT rowRect = rect; // we'll need left and right borders
|
|
|
|
for (int y = topAligned; y < rect.bottom; y += 32)
|
|
{
|
|
const int tile_h = min(rect.bottom - y, 32);
|
|
// TODO: refactor it
|
|
int sl = scanLine;
|
|
// window captions suffer an arbitrary scanline...
|
|
if (y == topAligned)
|
|
sl = 31;
|
|
sl = min(sl, tile_h-1);
|
|
const int scan_y = y + sl;
|
|
|
|
_ASSERTE(scan_y >= rect.top);
|
|
_ASSERTE(scan_y < rect.bottom);
|
|
|
|
rowRect.top = scan_y;
|
|
rowRect.bottom = scan_y + 1;
|
|
CaptureScreen(rowRect, m_mainbuff);
|
|
const int offset = (scan_y-m_bmrect.top) * m_bytesPerRow + (leftAligned-m_bmrect.left) * bytesPerPixel;
|
|
const unsigned char *o_ptr = m_backbuff + offset;
|
|
const unsigned char *n_ptr = m_mainbuff + offset;
|
|
for (int x = leftAligned; x < rect.right; x += 32)
|
|
{
|
|
const int tile_w = min(rect.right - x, 32);
|
|
const int nBytes = tile_w * bytesPerPixel;
|
|
if (memcmp(o_ptr, n_ptr, nBytes) != 0)
|
|
{
|
|
RECT tileRect;
|
|
tileRect.left = x;
|
|
tileRect.top = y;
|
|
tileRect.right = x + tile_w;
|
|
tileRect.bottom = y + tile_h;
|
|
m_changed_rgn.AddRect(tileRect);
|
|
}
|
|
o_ptr += nBytes;
|
|
n_ptr += nBytes;
|
|
}
|
|
}
|
|
}
|
|
|
|
void vncDesktop::CopyRect(const RECT &rcDest, const POINT &ptSrc)
|
|
{
|
|
const int offset_x = rcDest.left - ptSrc.x;
|
|
const int offset_y = rcDest.top - ptSrc.y;
|
|
|
|
// Clip the destination to the screen
|
|
RECT destrect;
|
|
if (!IntersectRect(&destrect, &rcDest, &m_server->GetSharedRect()))
|
|
return;
|
|
|
|
// NOTE: This is important. Each pixel in destrect is either salvaged
|
|
// by copyrect or became dirty.
|
|
m_changed_rgn.AddRect(destrect);
|
|
|
|
// Work out the source rectangle
|
|
RECT srcrect;
|
|
srcrect.left = destrect.left - offset_x;
|
|
srcrect.top = destrect.top - offset_y;
|
|
srcrect.right = srcrect.left + destrect.right - destrect.left;
|
|
srcrect.bottom = srcrect.top + destrect.bottom - destrect.top;
|
|
|
|
// Clip the source to the screen
|
|
RECT srcrect2;
|
|
if (!IntersectRect(&srcrect2, &srcrect, &m_server->GetSharedRect()))
|
|
return;
|
|
|
|
destrect.left += (srcrect2.left - srcrect.left);
|
|
destrect.top += (srcrect2.top - srcrect.top);
|
|
destrect.right = srcrect2.right - srcrect2.left + destrect.left;
|
|
destrect.bottom = srcrect2.bottom - srcrect2.top + destrect.top;
|
|
|
|
if ( destrect.right - destrect.left >= 16 &&
|
|
destrect.bottom - destrect.top >= 16 ) {
|
|
m_changed_rgn.SubtractRect(destrect);
|
|
|
|
m_copyrect_rect = destrect;
|
|
m_copyrect_src.x = srcrect2.left;
|
|
m_copyrect_src.y = srcrect2.top;
|
|
m_copyrect_set = TRUE;
|
|
|
|
//DPF(("CopyRect: (%d, %d) (%d, %d, %d, %d)\n",
|
|
// m_copyrect_src.x,
|
|
// m_copyrect_src.y,
|
|
// m_copyrect_rect.left,
|
|
// m_copyrect_rect.top,
|
|
// m_copyrect_rect.right,
|
|
// m_copyrect_rect.bottom));
|
|
}
|
|
}
|
|
|
|
//
|
|
// Copy the data from one rectangle of the back buffer to another.
|
|
//
|
|
|
|
void vncDesktop::CopyRectToBuffer(const RECT &dest, const POINT &source)
|
|
{
|
|
const int src_x = source.x - m_bmrect.left;
|
|
const int src_y = source.y - m_bmrect.top;
|
|
_ASSERTE(src_x >= 0);
|
|
_ASSERTE(src_y >= 0);
|
|
|
|
const int dst_x = dest.left - m_bmrect.left;
|
|
const int dst_y = dest.top - m_bmrect.top;
|
|
_ASSERTE(dst_x >= 0);
|
|
_ASSERTE(dst_y >= 0);
|
|
|
|
const unsigned int bytesPerPixel = m_scrinfo.format.bitsPerPixel / 8;
|
|
const unsigned int bytesPerLine = (dest.right - dest.left) * bytesPerPixel;
|
|
|
|
BYTE *srcptr = m_backbuff + src_y * m_bytesPerRow + src_x * bytesPerPixel;
|
|
BYTE *destptr = m_backbuff + dst_y * m_bytesPerRow + dst_x * bytesPerPixel;
|
|
|
|
if (dst_y < src_y) {
|
|
for (int y = dest.top; y < dest.bottom; y++) {
|
|
memmove(destptr, srcptr, bytesPerLine);
|
|
srcptr += m_bytesPerRow;
|
|
destptr += m_bytesPerRow;
|
|
}
|
|
} else {
|
|
srcptr += m_bytesPerRow * (dest.bottom - dest.top - 1);
|
|
destptr += m_bytesPerRow * (dest.bottom - dest.top - 1);
|
|
for (int y = dest.bottom; y > dest.top; y--) {
|
|
memmove(destptr, srcptr, bytesPerLine);
|
|
srcptr -= m_bytesPerRow;
|
|
destptr -= m_bytesPerRow;
|
|
}
|
|
}
|
|
}
|
|
|
|
BOOL IsBadDirectAccessConfig()
|
|
{
|
|
if (IsWinVerOrHigher(5, 1))
|
|
{
|
|
if (GetSystemMetrics(SM_XVIRTUALSCREEN) < 0)
|
|
return TRUE;
|
|
if (GetSystemMetrics(SM_YVIRTUALSCREEN) < 0)
|
|
return TRUE;
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
BOOL vncDesktop::InitVideoDriver()
|
|
{
|
|
// Mirror video drivers supported under Win2K, WinXP, WinVista
|
|
// and Windows NT 4.0 SP3 (we assume SP6).
|
|
if (!vncService::IsWinNT())
|
|
return FALSE;
|
|
|
|
// FIXME: Windows NT 4.0 support is broken and thus we disable it here.
|
|
if (!IsWinVerOrHigher(5, 0))
|
|
return FALSE;
|
|
|
|
if (m_server->DontUseDriver())
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
BOOL bIsBadDASDConfig = IsBadDirectAccessConfig();
|
|
|
|
|
|
BOOL bSolicitDASD = m_server->DriverDirectAccess() & !bIsBadDASDConfig;
|
|
|
|
_ASSERTE(!m_videodriver);
|
|
m_videodriver = new vncVideoDriver;
|
|
if (!m_videodriver)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
if (IsWinVerOrHigher(5, 0))
|
|
{
|
|
// restart the driver if left running.
|
|
// NOTE that on NT4 it must be running beforehand
|
|
if (m_videodriver->TestMapped())
|
|
{
|
|
m_videodriver->Deactivate();
|
|
}
|
|
_ASSERTE(!m_videodriver->TestMapped());
|
|
}
|
|
|
|
{
|
|
RECT vdesk_rect;
|
|
GetSourceDisplayRect(vdesk_rect);
|
|
BOOL b = m_videodriver->Activate(bSolicitDASD, &vdesk_rect);
|
|
}
|
|
|
|
if (!m_videodriver->CheckVersion())
|
|
{
|
|
|
|
// IMPORTANT: fail on NT46
|
|
if (IsNtVer(4, 0))
|
|
return FALSE;
|
|
}
|
|
|
|
if (m_videodriver->MapSharedbuffers(bSolicitDASD))
|
|
{
|
|
|
|
}
|
|
else
|
|
{
|
|
delete m_videodriver;
|
|
m_videodriver = NULL;
|
|
|
|
return FALSE;
|
|
}
|
|
_ASSERTE(bSolicitDASD == m_videodriver->IsDirectAccessInEffect());
|
|
return TRUE;
|
|
}
|
|
|
|
void vncDesktop::ShutdownVideoDriver()
|
|
{
|
|
if (m_videodriver == NULL)
|
|
return;
|
|
delete m_videodriver;
|
|
m_videodriver = NULL;
|
|
|
|
}
|
|
|
|
void
|
|
vncDesktop::UpdateBlankScreenTimer()
|
|
{
|
|
BOOL active = m_server->GetBlankScreen();
|
|
if (active && !m_timer_blank_screen) {
|
|
m_timer_blank_screen = (UINT)SetTimer(Window(), TIMER_BLANK_SCREEN, 50, NULL);
|
|
} else if (!active && m_timer_blank_screen) {
|
|
KillTimer(Window(), TIMER_BLANK_SCREEN);
|
|
m_timer_blank_screen = 0;
|
|
PostMessage(m_hwnd, WM_TIMER, TIMER_RESTORE_SCREEN, 0);
|
|
}
|
|
}
|
|
|
|
void
|
|
vncDesktop::BlankScreen(BOOL set)
|
|
{
|
|
if (set) {
|
|
SystemParametersInfo(SPI_SETPOWEROFFACTIVE, 1, NULL, 0);
|
|
SendMessage(GetDesktopWindow(), WM_SYSCOMMAND, SC_MONITORPOWER, (LPARAM)2);
|
|
} else {
|
|
SystemParametersInfo(SPI_SETPOWEROFFACTIVE, 0, NULL, 0);
|
|
SendMessage(GetDesktopWindow(), WM_SYSCOMMAND, SC_MONITORPOWER, (LPARAM)-1);
|
|
}
|
|
}
|
|
|
|
// created for debug purposes
|
|
bool SaveBitmapToBMPFile(
|
|
HANDLE hFile,
|
|
void *ptrBm,
|
|
void *ptrPal,
|
|
int bmwidth,
|
|
int bmheight,
|
|
int bmstride,
|
|
int bmclrdepth)
|
|
{
|
|
BITMAPINFOHEADER bih = {0};
|
|
bih.biSize = sizeof(bih);
|
|
bih.biWidth = bmwidth;
|
|
bih.biHeight = bmheight;
|
|
bih.biPlanes = 1;
|
|
bih.biCompression = BI_RGB;
|
|
|
|
DWORD bitFields[3] = {0, 0, 0};
|
|
|
|
if (bmclrdepth == 1)
|
|
{
|
|
bih.biBitCount = 1;
|
|
bih.biClrUsed = 2;
|
|
}
|
|
else if (bmclrdepth == 2)
|
|
{
|
|
bih.biBitCount = 2;
|
|
bih.biClrUsed = 4;
|
|
}
|
|
else if (bmclrdepth == 4)
|
|
{
|
|
bih.biBitCount = 4;
|
|
bih.biClrUsed = 0x10;
|
|
}
|
|
else if (bmclrdepth == 8)
|
|
{
|
|
bih.biBitCount = 8;
|
|
bih.biClrUsed = 0x100;
|
|
}
|
|
else if (bmclrdepth == 16)
|
|
{
|
|
bih.biBitCount = 16;
|
|
bih.biCompression = BI_BITFIELDS;
|
|
// TODO: use actual masks
|
|
bitFields[0] = 0xF800;
|
|
bitFields[1] = 0x07E0;
|
|
bitFields[2] = 0x001F;
|
|
}
|
|
else if (bmclrdepth == 24)
|
|
{
|
|
bih.biBitCount = 24;
|
|
}
|
|
else if (bmclrdepth == 32)
|
|
{
|
|
bih.biBitCount = 32;
|
|
}
|
|
else
|
|
_ASSERTE(false);
|
|
|
|
BITMAPFILEHEADER bfh = {0};
|
|
bfh.bfType = 0x4d42; // 0x42 = "B" 0x4d = "M"
|
|
bfh.bfOffBits = sizeof(BITMAPFILEHEADER) + bih.biSize;
|
|
|
|
if (bih.biClrUsed)
|
|
{
|
|
bfh.bfOffBits += bih.biClrUsed * sizeof(RGBQUAD);
|
|
}
|
|
else if (bitFields[0] || bitFields[1] || bitFields[2])
|
|
{
|
|
bfh.bfOffBits += sizeof(bitFields);
|
|
}
|
|
|
|
unsigned lineSize = (((bih.biWidth * bih.biBitCount) + 15) / 8) & ~1;
|
|
bfh.bfSize = bfh.bfOffBits + lineSize * bih.biHeight;
|
|
|
|
ULONG ulnWr = 0;
|
|
if (!WriteFile(hFile, &bfh, sizeof(bfh), &ulnWr, NULL) || ulnWr!=sizeof(bfh))
|
|
return false;
|
|
if (!WriteFile(hFile, &bih, sizeof(bih), &ulnWr, NULL) || ulnWr!=sizeof(bih))
|
|
return false;
|
|
|
|
if (ptrPal)
|
|
{
|
|
if (!WriteFile(hFile, ptrPal, bih.biClrUsed * sizeof(RGBQUAD), &ulnWr, NULL) || ulnWr!=bih.biClrUsed * sizeof(RGBQUAD))
|
|
return false;
|
|
}
|
|
else if (bih.biCompression == BI_BITFIELDS)
|
|
{
|
|
if (!WriteFile(hFile, bitFields, sizeof(bitFields), &ulnWr, NULL) || ulnWr!=sizeof(bitFields))
|
|
return false;
|
|
}
|
|
|
|
for (int i = 0; i < bih.biHeight; i++)
|
|
{
|
|
char *pDWr = (char*)ptrBm + (bih.biHeight - i - 1) * bmstride;
|
|
if (!WriteFile(hFile, pDWr, lineSize, &ulnWr, NULL) || ulnWr!=lineSize)
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
// created for debug purposes
|
|
bool bDbgBmDump(
|
|
void *ptr,
|
|
int bmwidth,
|
|
int bmheight,
|
|
int bmstride,
|
|
int bmclrdepth)
|
|
{
|
|
if (bmclrdepth!=16 && bmclrdepth!=32)
|
|
{
|
|
// TODO: add 8 bpp
|
|
return false;
|
|
}
|
|
|
|
SYSTEMTIME stm;
|
|
GetSystemTime(&stm);
|
|
TCHAR szFileName[MAX_PATH];
|
|
sprintf(
|
|
szFileName,
|
|
"%04u.%02u.%02u-%02u-%02u-%02u-0x%08p.bmp",
|
|
stm.wYear, stm.wMonth, stm.wDay,
|
|
stm.wHour, stm.wMinute, stm.wSecond,
|
|
ptr);
|
|
|
|
HANDLE hFile = CreateFile(
|
|
szFileName,
|
|
FILE_WRITE_DATA,
|
|
0,
|
|
NULL,
|
|
CREATE_NEW,
|
|
FILE_ATTRIBUTE_NORMAL,
|
|
NULL);
|
|
if (hFile==INVALID_HANDLE_VALUE)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
bool b= SaveBitmapToBMPFile(
|
|
hFile,
|
|
ptr,
|
|
NULL,
|
|
bmwidth,
|
|
bmheight,
|
|
bmstride,
|
|
bmclrdepth);
|
|
|
|
CloseHandle(hFile);
|
|
return b;
|
|
}
|
|
|
|
// created for debug purposes
|
|
bool vncDesktop::bDbgDumpSurfBuffers(const RECT &rcl)
|
|
{
|
|
const int c2rect_re_vd_top = rcl.top - m_bmrect.top;
|
|
const int c3rect_re_vd_left = rcl.left - m_bmrect.left;
|
|
_ASSERTE(c2rect_re_vd_top >= 0);
|
|
_ASSERTE(c3rect_re_vd_left >= 0);
|
|
const UINT bytesPerPixel = m_scrinfo.format.bitsPerPixel / 8;
|
|
const int offset = c2rect_re_vd_top * m_bytesPerRow + c3rect_re_vd_left * bytesPerPixel;
|
|
|
|
bool b1 = bDbgBmDump(
|
|
m_mainbuff+offset,
|
|
rcl.right - rcl.left,
|
|
rcl.bottom - rcl.top,
|
|
m_bytesPerRow,
|
|
m_scrinfo.format.bitsPerPixel);
|
|
|
|
bool b2 = bDbgBmDump(
|
|
m_backbuff+offset,
|
|
rcl.right - rcl.left,
|
|
rcl.bottom - rcl.top,
|
|
m_bytesPerRow,
|
|
m_scrinfo.format.bitsPerPixel);
|
|
return b1 && b2;
|
|
}
|