📄 vncdesktop.cpp
字号:
// 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 "WinVNC.h"
#include "VNCHooks\VNCHooks.h"
#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
// 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())
{
vnclog.Print(LL_INTERR, VNCLOG("WaitMessage() failed\n"));
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
vnclog.Print(LL_INTINFO, VNCLOG("Received LS_QUIT message.\n"));
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);
vnclog.Print(LL_INTINFO, VNCLOG("quitting desktop server thread\n"));
// 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()
{
vnclog.Print(LL_INTINFO, VNCLOG("killing desktop server\n"));
// 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()) {
vnclog.Print(LL_INTERR, VNCLOG("Console is not session zero - reconnect to restore Console session"));
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_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) {
vnclog.Print(LL_INTERR, VNCLOG("GlobalAddAtom() failed.\n"));
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
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -