📄 vncdesktop.cpp
字号:
// Copyright (C) 2006 Teamviewer GmbH. All Rights Reserved.
// Copyright (C) 2002 Ultr@VNC Team Members. All Rights Reserved.
// Copyright (C) 2000-2002 Const Kaplinsky. All Rights Reserved.
// Copyright (C) 2002 RealVNC Ltd. All Rights Reserved.
// Copyright (C) 1999 AT&T Laboratories Cambridge. All Rights Reserved.
//
// This file is part of TeamViewer.
//
// TeamViewer 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.
//
// If the source code for TeamViewer is not available from the place
// whence you received this file, check http://www.teamviewer.com
// for information on obtaining it.
// vncDesktop implementation
// System headers
#include "stdhdrs.h"
// Custom headers
#include "VNCHooks\VNCHooks.h"
#include "vncServer.h"
#include "vncKeymap.h"
#include "vncDesktop.h"
#include "vncService.h"
#include "vncOSVersion.h"
#include "TextChat.h" // sf@2002
#include "vncdesktopthread.h"
// Constants
const UINT RFB_SCREEN_UPDATE = RegisterWindowMessage("TeamViewer.Update.DrawRect");
const UINT RFB_COPYRECT_UPDATE = RegisterWindowMessage("TeamViewer.Update.CopyRect");
const UINT RFB_MOUSE_UPDATE = RegisterWindowMessage("TeamViewer.Update.Mouse");
const UINT RFB_KEY = RegisterWindowMessage("TeamViewer.Keypressed");
const char szDesktopSink[] = "WinVNC desktop sink";
extern int counterwatch;//global var for driverwatch
bool g_Desktop_running;
extern bool g_DesktopThread_running;
extern bool g_update_triggered;
DWORD WINAPI BlackWindow(LPVOID lpParam);
// OS Version Info from vncService
extern DWORD g_platform_id;
extern DWORD g_version_major;
extern DWORD g_version_minor;
//
// // Modif sf@2002 - v1.1.0 - Optimization
//
// Here we try to speed up the Poll FullScreen function.
// Instead of adding the entire Rect to cached region,
// we divide it in 32/32 pixels blocks and only scan the top-left
// pixel of each block.
// If a pixel has changed, we find the smallest window containing this pixel
// and add the Window rect to the cached region
//
// This function is supposed to be a good compromise between
// speed and efficiency.
// The accuracy has been greatly improved and it now detects very
// small changes on screen that are not detected by the Hooks.
// This new accuracy is obtained by shifting the detection grid
// in diagonale (the function works with several grids that are tested
// alternatively). There's probably still room for improvements...
//
// NB: Changes generated by applications using hardware acceleration
// won't be detected (In order to see a video played on the server using
// Media Player, the vncviewer user must desactivate "Hardware accelation"
// in the Media Player options).
//
//
//
void
vncDesktop::FastDetectChanges(rfb::Region2D &rgn, rfb::Rect &rect, int nZone, bool fTurbo)
{
RGBPixelList::iterator iPixelColor;
RGBPixelList *pThePixelGrid;
bool fInitGrid = false;
bool fIncCycle = false;
// For more accuracy, we could use 24 or even 16
const int PIXEL_BLOCK_SIZE = 32; // Pixels Grid definition
const int GRID_OFFSET = 4; // Pixels Grid shifting during cycle (in pixels)
// The number of grid per zone is PIXEL_BLOCK_SIZE/GRID_OFFSET (must be int !)
int x, y;
int xo, yo;
// WindowsList lWList;
WindowsList::iterator iWindow;
// In turbo mode, we clear the list of windows at each iteration -> Lot of updates because
// the same windows can be detected several times during a complete cycle
// (To avoid this pb, we must update all the grids at the same coordinate when a pixel of
// one grid has changed -> later)
// Otherwise we only clear it each time the Grid cycle loops -> Less updates, less framerate,
// less CPU, less bandwidth
if (fTurbo)
m_lWList.clear();
else if (m_nGridCycle == 0)
m_lWList.clear();
// Create all the Grids (for all the 5 zones (4 quarter screens + 1 full screen)
// Actually, the quarter screens are not utilized in v1.1.0 but it does not
// generate any overhead neither additionnal memory consumption (see below)
if (m_lGridsList.empty())
{
for (int i = 0; i < (5 * PIXEL_BLOCK_SIZE / GRID_OFFSET); i++)
{
RGBPixelList *pList = new RGBPixelList;
if (pList != NULL)
{
m_lGridsList.push_back(pList);
}
}
vnclog.Print(LL_INTINFO, VNCLOG("%d PixelsGrids created"), 5 * PIXEL_BLOCK_SIZE / GRID_OFFSET);
}
// We test one zone at a time
// vnclog.Print(LL_INTINFO, VNCLOG("### Polling Grid %d - SubGrid %d"), nZone, m_nGridCycle);
GridsList::iterator iGrid;
int nIndex = 0;
int nGridPos = (nZone * PIXEL_BLOCK_SIZE / GRID_OFFSET) + m_nGridCycle;
iGrid = m_lGridsList.begin();
while (nIndex != nGridPos)
{
if(iGrid == m_lGridsList.end()) // shouldn't happen, but better check paranoid
{
vnclog.Print(LL_INTERR, VNCLOG("m_lGridsList.end()"));
return;
}
iGrid++;
nIndex++;
}
iPixelColor = ((RGBPixelList*)(*iGrid))->begin();
pThePixelGrid = (RGBPixelList*) *iGrid;
if (nZone == 0 || nZone == 4)
{
// vnclog.Print(LL_INTINFO, VNCLOG("### IncCycle Please !"));
fIncCycle = true;
}
if (pThePixelGrid->empty())
{
// vnclog.Print(LL_INTINFO, VNCLOG("### PixelsGrid Init"));
fInitGrid = true;
}
int nOffset = GRID_OFFSET * m_nGridCycle;
// Vista with DWM: read operations on the desktop DC (e.g. GetPixel(m_hrootDC,...) are _very_ slow because
// of high overhead of synchronization (see http://blogs.msdn.com/greg_schechter/archive/2006/05/02/588934.aspx)
// Therefore it's better to make just one call and copy the complete screen to a memory
// bitmap to get the pixel data
HDC grabDC = NULL;
HBITMAP membmp = NULL;
HBITMAP oldbmp = NULL;
bool vista = (g_platform_id==VER_PLATFORM_WIN32_NT && g_version_major>=6);
if(vista)
{
grabDC = CreateCompatibleDC(m_hrootdc);
membmp = CreateCompatibleBitmap(m_hrootdc, rect.br.x, rect.br.y);
oldbmp = (HBITMAP)SelectObject(grabDC, membmp);
BitBlt(grabDC, 0, 0, rect.br.x, rect.br.y, m_hrootdc, 0, 0, SRCCOPY);
}
else
grabDC = m_hrootdc;
// We walk our way through the Grids
for (y = rect.tl.y; y < rect.br.y; y += PIXEL_BLOCK_SIZE)
{
yo = y + nOffset;
for (x = rect.tl.x; x < rect.br.x; x += PIXEL_BLOCK_SIZE)
{
xo = x + nOffset;
// If init list
if (fInitGrid)
{
COLORREF PixelColor = GetPixel(grabDC, xo, yo);
pThePixelGrid->push_back(PixelColor);
// vnclog.Print(LL_INTINFO, VNCLOG("### PixelsGrid Init : Pixel xo=%d - yo=%d - C=%ld"), xo, yo, (long)PixelColor);
continue;
}
// Read the pixel's color on the screen
COLORREF PixelColor = GetPixel(grabDC, xo, yo);
// If the pixel has changed
if (*iPixelColor != PixelColor)
{
// Save the new Pixel in the list
*iPixelColor = PixelColor;
// Then find the corresponding Window
POINT point;
RECT rect;
point.x = xo;
point.y = yo;
// Find the smallest, non-hidden, non-disabled Window containing this pixel
// REM: We don't use ChildWindowFromPoint because we don't want of hidden windows
HWND hDeskWnd = GetDesktopWindow();
HWND hwnd = WindowFromPoint(point);
bool fAlready = false;
// Look if we've already detected this window
for (iWindow = m_lWList.begin(); iWindow != m_lWList.end(); iWindow++)
{
if (*iWindow == hwnd)
{
fAlready = true;
break;
}
}
// Add the corresponding rect to the cache region
if (!fAlready && (hwnd != hDeskWnd) && GetWindowRect(hwnd, &rect))
{
//Buffer coordinates
rect.left-=m_ScreenOffsetx;
rect.right-=m_ScreenOffsetx;
rect.top-=m_ScreenOffsety;
rect.bottom-=m_ScreenOffsety;
rfb::Rect wrect = rfb::Rect(rect).intersect(m_Cliprect);
if (!wrect.is_empty())
{
rgn = rgn.union_(wrect);
m_lWList.push_back(hwnd);
}
// char szName[64];
// GetWindowText(hwnd, szName, 64);
// vnclog.Print(LL_INTINFO, VNCLOG("### Changed Window : %s (at x=%d - y=%d)"), szName, x, y);
// return;
}
}
iPixelColor++; // Next PixelColor in the list
}
}
if(vista)
{
SelectObject(grabDC, oldbmp);
DeleteObject(membmp);
DeleteDC(grabDC);
}
if (fIncCycle)
{
m_nGridCycle = (m_nGridCycle + 1) % (PIXEL_BLOCK_SIZE / GRID_OFFSET);
}
}
// Implementation
vncDesktop::vncDesktop()
{
m_thread = NULL;
m_hwnd = NULL;
m_timerid = 0;
m_hnextviewer = NULL;
m_hcursor = NULL;
m_hOldcursor = NULL; // sf@2002
m_displaychanged = FALSE;
m_update_triggered = FALSE;
g_update_triggered = FALSE;
m_hrootdc = NULL;
m_hmemdc = NULL;
m_membitmap = NULL;
m_initialClipBoardSeen = FALSE;
m_foreground_window = NULL;
// Vars for Will Dean's DIBsection patch
m_DIBbits = NULL;
m_formatmunged = FALSE;
m_clipboard_active = FALSE;
m_pollingcycle = 0;
// Modif sf@2002 - v1.1.0
m_lWList.clear();
m_lGridsList.clear();
m_nGridCycle = 0;
// Modif sf@2002 - v1.1.0
// m_lLastTempo = 0L;
m_lLastMouseUpdateTime = 0L;
m_lLastSlowClientTestTime = timeGetTime();
// sf@2002 - TextChat - No more used for now
// m_fTextChatRunning = false;
// m_pCurrentTextChat = NULL;
// Modif rdv@2002 - v1.1.x - videodriver
m_videodriver=NULL;
m_ScreenOffsetx=0;
m_ScreenOffsety=0;
m_hookdriver=false;
OldPowerOffTimeout=0;
UnSetHooks=NULL;
SetMouseFilterHook=NULL;
SetKeyboardFilterHook=NULL;
SetHooks=NULL;
hModule=NULL;
char szCurrentDir[MAX_PATH];
if (vncService::IsWinNT() && GetModuleFileName(NULL, szCurrentDir, MAX_PATH)) // Win98: don't use vncHooks
{
char* p = strrchr(szCurrentDir, '\\');
if (p != NULL)
{
*p = '\0';
strcat (szCurrentDir,"\\vnchooks.dll");
hModule = LoadLibrary(szCurrentDir);
}
}
if (hModule)
{
UnSetHooks = (UnSetHooksFn) GetProcAddress( hModule, "UnSetHooks" );
SetMouseFilterHook = (SetMouseFilterHookFn) GetProcAddress( hModule, "SetMouseFilterHook" );
SetKeyboardFilterHook = (SetKeyboardFilterHookFn) GetProcAddress( hModule, "SetKeyboardFilterHook" );
SetHooks = (SetHooksFn) GetProcAddress( hModule, "SetHooks" );
}
On_Off_hookdll=false;
g_Desktop_running=true;
hUser32=LoadLibrary("USER32");
pbi = (pBlockInput)GetProcAddress( hUser32, "BlockInput");
Temp_Resolution=false;
m_OrigpollingSet=false;
m_Origpolling=false;
DriverWantedSet=false;
current_monitor=3;
ddihook = false;
memset(&m_scrinfo, 0, sizeof(m_scrinfo));
m_stop_input_while_sw = false;
}
vncDesktop::~vncDesktop()
{
vnclog.Print(LL_INTINFO, VNCLOG("killing screen server"));
// 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);
vncDesktopThread *thread=(vncDesktopThread*)m_thread;
while (g_DesktopThread_running!=false)
{
PostMessage(Window(), WM_QUIT, 0, 0);
Sleep(200);
}
// Join with the desktop handler thread
void *returnval;
m_thread->join(&returnval);
m_thread = NULL;
}
SetDisableInput(false);
// Let's call Shutdown just in case something went wrong...
Shutdown();
// Modif sf@2002
m_lWList.clear();
GridsList::iterator iGrid;
for (iGrid = m_lGridsList.begin(); iGrid != m_lGridsList.end(); iGrid++)
{
if (*iGrid)
{
// Since we've replaced this:
// "typedef std::list<RGBPixelList*> GridsList;"
// with this:
// "typedef std::list<void*> GridsList;"
// we must be carefull to avoid memory leaks...
((RGBPixelList*)(*iGrid))->clear();
delete ((RGBPixelList*)(*iGrid));
}
}
m_lGridsList.clear();
if (hModule)FreeLibrary(hModule);
if (hUser32) FreeLibrary(hUser32);
g_Desktop_running=false;
}
// Tell the desktop hooks to grab & update a particular rectangle
void
vncDesktop::QueueRect(const rfb::Rect &rect)
{
ULONG vwParam = MAKELONG(rect.tl.x, rect.tl.y);
ULONG vlParam = MAKELONG(rect.br.x, rect.br.y);
PostMessage(Window(), RFB_SCREEN_UPDATE, vwParam, vlParam);
}
// Kick the desktop hooks to perform an update
void
vncDesktop::TriggerUpdate()
{
// Note that we should really lock the update lock here,
// but there are periodic timer updates anyway, so
// we don't actually need to. Something to think about.
if (!m_update_triggered) {
m_update_triggered = TRUE;
g_update_triggered = TRUE;
if (m_timerid != NULL)
KillTimer(NULL, m_timerid);
m_timerid = NULL;
PostMessage(Window(), WM_TIMER, 0, 0);
}
}
// Routine to startup and install all the hooks and stuff
BOOL
vncDesktop::Startup(BOOL silent)
{
// Initialise the Desktop object
// ***
// vncService::SelectInputWinStation()
if (!InitDesktop(silent))
{
if(!silent)
vnclog.Print(LL_INTINFO, VNCLOG("InitDesktop Failed"));
return FALSE;
}
// Modif rdv@2002 - v1.1.x - videodriver
if (!Temp_Resolution)
{
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -