📄 clientconnection.cpp
字号:
//// Copyright (C) 2002-2003 RealVNC Ltd. 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.//// 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.// Many thanks to Randy Brown <rgb@inven.com> for providing the 3-button// emulation code.// This is the main source for a ClientConnection object.// It handles almost everything to do with a connection to a server.// The decoding of specific rectangle encodings is done in separate files.#define _WIN32_WINDOWS 0x0410#define WINVER 0x0400#include "stdhdrs.h"#include "vncviewer.h"#ifdef UNDER_CE#include "omnithreadce.h"#define SD_BOTH 0x02#else#include "omnithread.h"#endif#include "ClientConnection.h"#include "SessionDialog.h"#include "AuthDialog.h"#include "AboutBox.h"#include "Exception.h"extern "C" { #include "vncauth.h"}#include <rdr/FdInStream.h>#include <rdr/ZlibInStream.h>#include <rdr/Exception.h>#define INITIALNETBUFSIZE 4096#define MAX_ENCODINGS (LASTENCODING+1)#define VWR_WND_CLASS_NAME _T("VNCviewer")/* * Macro to compare pixel formats. */#define PF_EQ(x,y) \ ((x.bitsPerPixel == y.bitsPerPixel) && \ (x.depth == y.depth) && \ ((x.bigEndian == y.bigEndian) || (x.bitsPerPixel == 8)) && \ (x.trueColour == y.trueColour) && \ (!x.trueColour || ((x.redMax == y.redMax) && \ (x.greenMax == y.greenMax) && \ (x.blueMax == y.blueMax) && \ (x.redShift == y.redShift) && \ (x.greenShift == y.greenShift) && \ (x.blueShift == y.blueShift))))const rfbPixelFormat vnc8bitFormat = {8, 8, 0, 1, 7,7,3, 0,3,6,0,0};const rfbPixelFormat vnc16bitFormat = {16, 16, 0, 1, 63, 31, 31, 0,6,11,0,0};static LRESULT CALLBACK ClientConnection::WndProc(HWND hwnd, UINT iMsg, WPARAM wParam, LPARAM lParam);// *************************************************************************// A Client connection involves two threads - the main one which sets up// connections and processes window messages and inputs, and a // client-specific one which receives, decodes and draws output data // from the remote server.// This first section contains bits which are generally called by the main// program thread.// *************************************************************************ClientConnection::ClientConnection(VNCviewerApp *pApp) : fis(0), zis(0){ Init(pApp);}ClientConnection::ClientConnection(VNCviewerApp *pApp, SOCKET sock) : fis(0), zis(0){ Init(pApp); if (m_opts.autoDetect) m_opts.m_Use8Bit = true; m_sock = sock; m_serverInitiated = true; struct sockaddr_in svraddr; int sasize = sizeof(svraddr); if (getpeername(sock, (struct sockaddr *) &svraddr, &sasize) != SOCKET_ERROR) { _stprintf(m_host, _T("%d.%d.%d.%d"), svraddr.sin_addr.S_un.S_un_b.s_b1, svraddr.sin_addr.S_un.S_un_b.s_b2, svraddr.sin_addr.S_un.S_un_b.s_b3, svraddr.sin_addr.S_un.S_un_b.s_b4); m_port = svraddr.sin_port; } else { _tcscpy(m_host,_T("(unknown)")); m_port = 0; };}ClientConnection::ClientConnection(VNCviewerApp *pApp, LPTSTR host, int port) : fis(0), zis(0){ Init(pApp); if (m_opts.autoDetect) m_opts.m_Use8Bit = true; _tcsncpy(m_host, host, MAX_HOST_NAME_LEN); m_port = port;}void ClientConnection::Init(VNCviewerApp *pApp){ m_hwnd = 0; m_desktopName = NULL; m_port = -1; m_serverInitiated = false; m_netbuf = NULL; m_netbufsize = 0; m_hwndNextViewer = NULL; m_pApp = pApp; m_dormant = false; m_hBitmapDC = NULL; m_hBitmap = NULL; m_hPalette = NULL; m_encPasswd[0] = '\0'; // We take the initial conn options from the application defaults m_opts = m_pApp->m_options; m_sock = INVALID_SOCKET; m_bKillThread = false; m_threadStarted = true; m_running = false; m_pendingFormatChange = false; // Initialise a few fields that will be properly set when the // connection has been negotiated m_fullwinwidth = m_fullwinheight = 0; m_si.framebufferWidth = m_si.framebufferHeight = 0; m_hScrollPos = 0; m_vScrollPos = 0; m_waitingOnEmulateTimer = false; m_emulatingMiddleButton = false; oldPointerX = oldPointerY = oldButtonMask = 0; // Create a buffer for various network operations CheckBufferSize(INITIALNETBUFSIZE); m_pApp->RegisterConnection(this); kbitsPerSecond = 0; zis = new rdr::ZlibInStream;
}// // Run() creates the connection if necessary, does the initial negotiations// and then starts the thread running which does the output (update) processing.// If Run throws an Exception, the caller must delete the ClientConnection object.//void ClientConnection::Run(){ // Get the host name and port if we haven't got it if (m_port == -1) GetConnectDetails(); // Connect if we're not already connected if (m_sock == INVALID_SOCKET) Connect(); SetSocketOptions(); NegotiateProtocolVersion(); Authenticate(); // Set up windows etc CreateDisplay(); SendClientInit(); ReadServerInit(); CreateLocalFramebuffer(); SetupPixelFormat(); SetFormatAndEncodings(); // This starts the worker thread. // The rest of the processing continues in run_undetached. start_undetached();}void ClientConnection::CreateDisplay() { // Create the window WNDCLASS wndclass; wndclass.style = 0; wndclass.lpfnWndProc = ClientConnection::WndProc; wndclass.cbClsExtra = 0; wndclass.cbWndExtra = 0; wndclass.hInstance = m_pApp->m_instance; wndclass.hIcon = LoadIcon(m_pApp->m_instance, MAKEINTRESOURCE(IDI_MAINICON)); switch (m_opts.m_localCursor) { case NOCURSOR: wndclass.hCursor = LoadCursor(m_pApp->m_instance, MAKEINTRESOURCE(IDC_NOCURSOR)); break; case NORMALCURSOR: wndclass.hCursor = LoadCursor(NULL, IDC_ARROW); break; case DOTCURSOR: default: wndclass.hCursor = LoadCursor(m_pApp->m_instance, MAKEINTRESOURCE(IDC_DOTCURSOR)); } wndclass.hbrBackground = (HBRUSH) GetStockObject(BLACK_BRUSH); wndclass.lpszMenuName = (const TCHAR *) NULL; wndclass.lpszClassName = VWR_WND_CLASS_NAME; RegisterClass(&wndclass);#ifdef _WIN32_WCE const DWORD winstyle = WS_VSCROLL | WS_HSCROLL | WS_CAPTION | WS_SYSMENU;#else
const DWORD winstyle = WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_MAXIMIZEBOX | //Modified by: Lars Werner (http://lars.werner.no) WS_MINIMIZEBOX | WS_THICKFRAME | WS_VSCROLL | WS_HSCROLL;#endif m_hwnd = CreateWindow(VWR_WND_CLASS_NAME, _T("VNCviewer"), winstyle, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, // x-size CW_USEDEFAULT, // y-size NULL, // Parent handle NULL, // Menu handle m_pApp->m_instance, NULL); ShowWindow(m_hwnd, SW_HIDE);
// record which client created this window SetWindowLong(m_hwnd, GWL_USERDATA, (LONG) this); // Create a memory DC which we'll use for drawing to // the local framebuffer m_hBitmapDC = CreateCompatibleDC(NULL); // Set a suitable palette up if (GetDeviceCaps(m_hBitmapDC, RASTERCAPS) & RC_PALETTE) { log.Print(3, _T("Palette-based display - %d entries, %d reserved\n"), GetDeviceCaps(m_hBitmapDC, SIZEPALETTE), GetDeviceCaps(m_hBitmapDC, NUMRESERVED)); BYTE buf[sizeof(LOGPALETTE)+216*sizeof(PALETTEENTRY)]; LOGPALETTE *plp = (LOGPALETTE *) buf; int pepos = 0; for (int r = 5; r >= 0; r--) { for (int g = 5; g >= 0; g--) { for (int b = 5; b >= 0; b--) { plp->palPalEntry[pepos].peRed = r * 255 / 5; plp->palPalEntry[pepos].peGreen = g * 255 / 5; plp->palPalEntry[pepos].peBlue = b * 255 / 5; plp->palPalEntry[pepos].peFlags = NULL; pepos++; } } } plp->palVersion = 0x300; plp->palNumEntries = 216; m_hPalette = CreatePalette(plp); } // Add stuff to System menu HMENU hsysmenu = GetSystemMenu(m_hwnd, FALSE); if (!m_opts.m_restricted) { AppendMenu(hsysmenu, MF_STRING, IDC_OPTIONBUTTON, _T("Connection &options...")); AppendMenu(hsysmenu, MF_STRING, ID_CONN_ABOUT, _T("Connection &info")); AppendMenu(hsysmenu, MF_STRING, ID_REQUEST_REFRESH, _T("Request screen &refresh")); AppendMenu(hsysmenu, MF_SEPARATOR, NULL, NULL); AppendMenu(hsysmenu, MF_STRING, ID_FULLSCREEN, _T("&Full screen")); AppendMenu(hsysmenu, MF_SEPARATOR, NULL, NULL); AppendMenu(hsysmenu, MF_STRING, ID_CONN_CTLALTDEL, _T("Send Ctl-Alt-Del")); AppendMenu(hsysmenu, MF_STRING, ID_CONN_CTLDOWN, _T("Ctrl Down")); AppendMenu(hsysmenu, MF_STRING, ID_CONN_CTLUP, _T("Ctrl Up")); AppendMenu(hsysmenu, MF_STRING, ID_CONN_ALTDOWN, _T("Alt Down")); AppendMenu(hsysmenu, MF_STRING, ID_CONN_ALTUP, _T("Alt Up")); AppendMenu(hsysmenu, MF_SEPARATOR, NULL, NULL); AppendMenu(hsysmenu, MF_STRING, ID_NEWCONN, _T("Ne&w connection...")); AppendMenu(hsysmenu, MF_STRING | (m_serverInitiated ? MF_GRAYED : 0), ID_CONN_SAVE_AS, _T("Save connection info &as...")); } AppendMenu(hsysmenu, MF_SEPARATOR, NULL, NULL); AppendMenu(hsysmenu, MF_STRING, IDD_APP_ABOUT, _T("&About VNCviewer...")); if (m_opts.m_listening) { AppendMenu(hsysmenu, MF_SEPARATOR, NULL, NULL); AppendMenu(hsysmenu, MF_STRING, ID_CLOSEDAEMON, _T("Close listening &daemon")); } DrawMenuBar(m_hwnd); // Set up clipboard watching#ifndef _WIN32_WCE // We want to know when the clipboard changes, so // insert ourselves in the viewer chain. But doing // this will cause us to be notified immediately of // the current state. // We don't want to send that. m_initialClipboardSeen = false; m_hwndNextViewer = SetClipboardViewer(m_hwnd); #endif
//Added by: Lars Werner (http://lars.werner.no)
if(TitleBar.GetSafeHwnd()==NULL)
TitleBar.Create(m_pApp->m_instance, m_hwnd);
}void ClientConnection::GetConnectDetails(){ if (m_opts.m_configSpecified) { LoadConnection(m_opts.m_configFilename); } else { SessionDialog sessdlg(&m_opts); if (!sessdlg.DoDialog()) { throw QuietException("User Cancelled"); } _tcsncpy(m_host, sessdlg.m_host, MAX_HOST_NAME_LEN); m_port = sessdlg.m_port; if (m_opts.autoDetect) m_opts.m_Use8Bit = true; } // This is a bit of a hack: // The config file may set various things in the app-level defaults which // we don't want to be used except for the first connection. So we clear them // in the app defaults here. m_pApp->m_options.m_host[0] = '\0'; m_pApp->m_options.m_port = -1; m_pApp->m_options.m_connectionSpecified = false; m_pApp->m_options.m_configSpecified = false;}void ClientConnection::Connect(){ struct sockaddr_in thataddr; int res; m_sock = socket(PF_INET, SOCK_STREAM, 0); if (m_sock == INVALID_SOCKET) throw WarningException(_T("Error creating socket")); int one = 1; // The host may be specified as a dotted address "a.b.c.d" // Try that first thataddr.sin_addr.s_addr = inet_addr(m_host); // If it wasn't one of those, do gethostbyname if (thataddr.sin_addr.s_addr == INADDR_NONE) { LPHOSTENT lphost; lphost = gethostbyname(m_host); if (lphost == NULL) { throw WarningException("Failed to get server address.\n\r" "Did you type the host name correctly?"); }; thataddr.sin_addr.s_addr = ((LPIN_ADDR) lphost->h_addr)->s_addr; }; thataddr.sin_family = AF_INET; thataddr.sin_port = htons(m_port); res = connect(m_sock, (LPSOCKADDR) &thataddr, sizeof(thataddr)); if (res == SOCKET_ERROR) throw WarningException("Failed to connect to server"); log.Print(0, _T("Connected to %s port %d\n"), m_host, m_port);}void ClientConnection::SetSocketOptions() { // Disable Nagle's algorithm BOOL nodelayval = TRUE; if (setsockopt(m_sock, IPPROTO_TCP, TCP_NODELAY, (const char *) &nodelayval, sizeof(BOOL))) throw WarningException("Error disabling Nagle's algorithm"); fis = new rdr::FdInStream(m_sock);}void ClientConnection::NegotiateProtocolVersion(){ rfbProtocolVersionMsg pv; /* if the connection is immediately closed, don't report anything, so that pmw's monitor can make test connections */ try { ReadExact(pv, sz_rfbProtocolVersionMsg); } catch (Exception &c) { log.Print(0, _T("Error reading protocol version: %s\n"), c.m_info); throw QuietException(c.m_info); } pv[sz_rfbProtocolVersionMsg] = 0; // XXX This is a hack. Under CE we just return to the server the // version number it gives us without parsing it. // Too much hassle replacing sscanf for now. Fix this!#ifdef UNDER_CE m_majorVersion = rfbProtocolMajorVersion;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -