Changes to vncviewer.exe and winvnc.exe
Michael Robellard
Michael.Robellard "at" ait-us.com
Mon, 24 Jan 2000 16:54:26 +0000
This message is in MIME format. Since your mail reader does not understand
this format, some or all of this message may not be legible.
------_=_NextPart_000_01BF6686.626FDDD4
Content-Type: text/plain;
charset="iso-8859-1"
Hello,
I work for a company called AIT and we use VNC all the time. We noticed that
you cannot use the alt down menu item and then the tab menu item and then
the alt menu item to alt-tab through the windows. The same is true for the
ctrl-esc key. I am email changes that I have made to the code to allow for
this.
The alt-tab is not perfect by any means because it only allows switch
between the two most recent windows because I haven't figured out how to do
an alt-tab-tab...
I am also thinking of adding a toolbar at the top of the window to speed use
of keyboard shortcuts.
Sincerly,
Michael Robellard
<<vncKeymap.cpp>> <<ClientConnection.cpp>> <<resource.h>>
------_=_NextPart_000_01BF6686.626FDDD4
Content-Type: application/octet-stream;
name="vncKeymap.cpp"
Content-Transfer-Encoding: quoted-printable
Content-Disposition: attachment;
filename="vncKeymap.cpp"
// 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=20
// whence you received this file, check =
http://www.uk.research.att.com/vnc or contact
// the authors on vnc "at" uk.research.att.com for information on obtaining =
it.
// vncKeymap.cpp
// This code originally just mapped between X keysyms and local Windows
// virtual keycodes. Now it actually does the local-end simulation of
// key presses, to keep this messy code on one place!
#include "vncKeymap.h"
#include "keysymdef.h"
#include "vncService.h"
// Define the keymap structure
typedef struct vncKeymapping_struct {
UINT wincode;
UINT Xcode;
} vncKeymapping;
const vncKeymapping keymap[] =3D {
{'`', XK_dead_grave},
{'=B4', XK_dead_acute},
{'~', XK_dead_tilde},
{'^', XK_dead_circumflex},
{VK_BACK, XK_BackSpace},
{VK_TAB, XK_Tab},
{VK_CLEAR, XK_Clear},
{VK_RETURN, XK_Return},
{VK_LSHIFT, XK_Shift_L},
{VK_RSHIFT, XK_Shift_R},
{VK_LCONTROL, XK_Control_L},
{VK_RCONTROL, XK_Control_R},
{VK_LMENU, XK_Alt_L},
{VK_RMENU, XK_Alt_R},
{VK_PAUSE, XK_Pause},
// {VK_CAPITAL, XK_Caps_Lock},
{VK_ESCAPE, XK_Escape},
{VK_SPACE, XK_space},
{VK_PRIOR, XK_Page_Up},
{VK_NEXT, XK_Page_Down},
{VK_END, XK_End},
{VK_HOME, XK_Home},
{VK_LEFT, XK_Left},
{VK_UP, XK_Up},
{VK_RIGHT, XK_Right},
{VK_DOWN, XK_Down},
{VK_SELECT, XK_Select},
{VK_EXECUTE, XK_Execute},
{VK_SNAPSHOT, XK_Print},
{VK_INSERT, XK_Insert},
{VK_DELETE, XK_Delete},
{VK_HELP, XK_Help},
{VK_NUMPAD0, XK_KP_0},
{VK_NUMPAD1, XK_KP_1},
{VK_NUMPAD2, XK_KP_2},
{VK_NUMPAD3, XK_KP_3},
{VK_NUMPAD4, XK_KP_4},
{VK_NUMPAD5, XK_KP_5},
{VK_NUMPAD6, XK_KP_6},
{VK_NUMPAD7, XK_KP_7},
{VK_NUMPAD8, XK_KP_8},
{VK_NUMPAD9, XK_KP_9},
{VK_SPACE, XK_KP_Space},
{VK_TAB, XK_KP_Tab},
{VK_RETURN, XK_KP_Enter},
{VK_F1, XK_KP_F1},
{VK_F2, XK_KP_F2},
{VK_F3, XK_KP_F3},
{VK_F4, XK_KP_F4},
{VK_HOME, XK_KP_Home},
{VK_LEFT, XK_KP_Left},
{VK_UP, XK_KP_Up},
{VK_RIGHT, XK_KP_Right},
{VK_DOWN, XK_KP_Down},
{VK_PRIOR, XK_KP_Prior},
{VK_PRIOR, XK_KP_Page_Up},
{VK_NEXT, XK_KP_Next},
{VK_NEXT, XK_KP_Page_Down},
{VK_END, XK_KP_End},
{VK_INSERT, XK_KP_Insert},
{VK_DELETE, XK_KP_Delete},
{VK_MULTIPLY, XK_KP_Multiply},
{VK_ADD, XK_KP_Add},
{VK_SEPARATOR, XK_KP_Separator},
{VK_SUBTRACT, XK_KP_Subtract},
{VK_DECIMAL, XK_KP_Decimal},
{VK_DIVIDE, XK_KP_Divide},
{VK_F1, XK_F1},
{VK_F2, XK_F2},
{VK_F3, XK_F3},
{VK_F4, XK_F4},
{VK_F5, XK_F5},
{VK_F6, XK_F6},
{VK_F7, XK_F7},
{VK_F8, XK_F8},
{VK_F9, XK_F9},
{VK_F10, XK_F10},
{VK_F11, XK_F11},
{VK_F12, XK_F12},
{VK_F13, XK_F13},
{VK_F14, XK_F14},
{VK_F15, XK_F15},
{VK_F16, XK_F16},
{VK_F17, XK_F17},
{VK_F18, XK_F18},
{VK_F19, XK_F19},
{VK_F20, XK_F20},
{VK_F21, XK_F21},
{VK_F22, XK_F22},
{VK_F23, XK_F23},
{VK_F24, XK_F24},
// {VK_NUMLOCK, XK_Num_Lock},
// {VK_SCROLL, XK_Scroll_Lock}
};
vncKeymap::vncKeymap()
{
};
void
KeybdEvent(BYTE keycode, DWORD flags)
{
// Send the desired keyboard event
keybd_event(keycode, MapVirtualKey(keycode, 0), flags, 0);
}
void
SetShiftState(BYTE key, BOOL down)
{
BOOL keystate =3D (GetAsyncKeyState(key) & 0x8000) !=3D 0;
// This routine sets the specified key to the desired value (up or =
down)
if ((keystate && down) || ((!keystate) && (!down)))
return;
log.Print(LL_INTINFO,
VNCLOG("setshiftstate %d - (%s->%s)\n"),
key, keystate ? "down" : "up",
down ? "down" : "up");
// Now send a key event to set the key to the new value
KeybdEvent(key, down ? 0 : KEYEVENTF_KEYUP);
keystate =3D (GetAsyncKeyState(key) & 0x8000) !=3D 0;
log.Print(LL_INTINFO,
VNCLOG("new state %d (%s)\n"),
key, keystate ? "down" : "up");
}
void
vncKeymap::DoXkeysym(CARD32 keysym, BOOL keydown)
{
log.Print(LL_INTINFO, VNCLOG("keysym =3D %x (%s)\n"), keysym, keydown =
? "down" : "up");
// First just try to map the virtual keycode using our lookup-table
for (int i =3D 0; i < (sizeof(keymap) / sizeof(vncKeymapping)); i++)
{
if (keymap[i].Xcode =3D=3D keysym)
{
UINT virtcode =3D keymap[i].wincode;
// *** NT-Only hack
if (vncService::IsWinNT())
{
log.Print(LL_INTINFO, VNCLOG("IsWinNT in keymap\n"));
// DELETE and DEL are used for control-alt-del commands
if (keydown && (virtcode =3D=3D VK_DELETE))
{
// *** NB : Under WinNT 3.51 these _always_ return zero, so
// Ctrl-Alt-Del doesn't work. No idea why...
BOOL ctrl =3D (GetAsyncKeyState(VK_CONTROL) & 0x8000) !=3D 0;
BOOL alt =3D (GetAsyncKeyState(VK_MENU) & 0x8000) !=3D 0;
if (ctrl && alt)
{
vncService::SimulateCtrlAltDel();
// This will keep the code from breaking if they ever fix the bug =
that=20
// required this hack in the first place.
// 24 Jan 2000 MJR
return;
}
}
}
// *** Win95-Only hack
if (vncService::IsWin95())
{
// On Windows 95, we have to munge LShift & RShift down to plain =
Shift:
switch(virtcode)
{
case VK_LSHIFT:
case VK_RSHIFT:
virtcode =3D VK_SHIFT;
break;
case VK_LCONTROL:
case VK_CONTROL:
virtcode =3D VK_CONTROL;
break;
case VK_LMENU:
case VK_RMENU:
virtcode =3D VK_MENU;
break;
};
}
// Code to support CTL-ESC
// 24 Jan 2000 MJR
if(keydown && (virtcode =3D=3D VK_ESCAPE))
{
BOOL ctrl =3D (GetAsyncKeyState(VK_CONTROL) & 0x8000) !=3D 0;
if(ctrl)
{
PostMessage(HWND_BROADCAST, WM_SYSCOMMAND, SC_TASKLIST, 0);
return;
}
}
// Now simulate the keyboard event
KeybdEvent((unsigned char) (virtcode & 255), keydown ? 0 : =
KEYEVENTF_KEYUP);
return;
}
}
// Otherwise, get the OS to tell us how to simulate it
SHORT keyval =3D VkKeyScan((char) (keysym & 255));
=09
// Retrieve the keycode and shift state
BYTE keycode =3D LOBYTE(keyval);
BYTE keymask =3D HIBYTE(keyval);
if (keycode =3D=3D -1)
return;
BOOL lshift, rshift;
BOOL ctrl;
BOOL alt;
// Correct the keymask shift state to cope with the capslock state
// Changed to 'Async' by qsf 27/4/99.
BOOL capslock =3D (GetAsyncKeyState(VK_CAPITAL) & 0x8000) !=3D 0;
keymask =3D capslock ? keymask ^ 1 : keymask;
// Are we running on NT?
if (vncService::IsWinNT())
{
// Yes, so the modifier keys come in Left and Right flavours:
// Toggle shift to the required position
lshift =3D (GetAsyncKeyState(VK_LSHIFT) & 0x8000) !=3D 0;
rshift =3D (GetAsyncKeyState(VK_RSHIFT) & 0x8000) !=3D 0;
// NT gets confused if you set the shift state in the wrong order,
// so now we clear both shift keys and use the generic shift instead.
SetShiftState(VK_RSHIFT, FALSE);
SetShiftState(VK_LSHIFT, FALSE);
SetShiftState(VK_SHIFT, keymask & 1);
// But only toggle Ctrl & Alt if they aught to be down
ctrl =3D (GetAsyncKeyState(VK_LCONTROL) & 0x8000) !=3D 0;
if (!ctrl) SetShiftState(VK_LCONTROL, keymask & 2);
alt =3D (GetAsyncKeyState(VK_LMENU) & 0x8000) !=3D 0;
if (!alt) SetShiftState(VK_LMENU, keymask & 4);
}
else
{
// No, so the modifier keys come in only one flavour
// Toggle shift to the required position
lshift =3D (GetAsyncKeyState(VK_SHIFT) & 0x8000) !=3D 0;
SetShiftState(VK_SHIFT, keymask & 1);
=09
// But only toggle Ctrl & Alt if they aught to be down
ctrl =3D (GetAsyncKeyState(VK_CONTROL) & 0x8000) !=3D 0;
if (!ctrl) SetShiftState(VK_CONTROL, keymask & 2);
alt =3D (GetAsyncKeyState(VK_MENU) & 0x8000) !=3D 0;
if (!alt) SetShiftState(VK_MENU, keymask & 4);
}
// Now send the desired keycode
log.Print(LL_INTINFO, VNCLOG("shift state (%s)\n"), =
(GetAsyncKeyState(VK_SHIFT) & 0x8000) ? "down" : "up");
KeybdEvent((unsigned char) (keycode & 255), keydown ? 0 : =
KEYEVENTF_KEYUP);
// Are we running on NT?
if (vncService::IsWinNT())
{
// Yes, so reset both Shift keys & the left Control & Alt
SetShiftState(VK_LSHIFT, lshift);
SetShiftState(VK_RSHIFT, rshift);
SetShiftState(VK_LCONTROL, ctrl);
SetShiftState(VK_LMENU, alt);
}
else
{
// No, so just reset the single versions
SetShiftState(VK_SHIFT, lshift);
SetShiftState(VK_CONTROL, ctrl);
SetShiftState(VK_MENU, alt);
}
}
void
vncKeymap::ClearShiftKeys()
{
if (vncService::IsWinNT())
{
// On NT, clear both sets of keys
// LEFT
SetShiftState(VK_LSHIFT, FALSE);
SetShiftState(VK_LCONTROL, FALSE);
SetShiftState(VK_LMENU, FALSE);
// RIGHT
SetShiftState(VK_RSHIFT, FALSE);
SetShiftState(VK_RCONTROL, FALSE);
SetShiftState(VK_RMENU, FALSE);
}
else
{
// Otherwise, we can't distinguish the keys anyway...
// Clear the shift key states
SetShiftState(VK_SHIFT, FALSE);
SetShiftState(VK_CONTROL, FALSE);
SetShiftState(VK_MENU, FALSE);
}
}
------_=_NextPart_000_01BF6686.626FDDD4
Content-Type: application/octet-stream;
name="ClientConnection.cpp"
Content-Transfer-Encoding: quoted-printable
Content-Disposition: attachment;
filename="ClientConnection.cpp"
// 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=20
// whence you received this file, check =
http://www.uk.research.att.com/vnc or contact
// the authors on vnc "at" uk.research.att.com for information on obtaining =
it.
// Many thanks to Randy Brown <rgb "at" 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.
#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"
}
#define INITIALNETBUFSIZE 4096
#define MAX_ENCODINGS 10
#define VWR_WND_CLASS_NAME _T("VNCviewer")
const rfbPixelFormat vnc8bitFormat =3D {8, 8, 1, 1, 7,7,3, 0,3,6,0,0};
const rfbPixelFormat vnc16bitFormat =3D {16, 16, 1, 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=20
// client-specific one which receives, decodes and draws output data=20
// from the remote server.
// This first section contains bits which are generally called by the =
main
// program thread.
// =
************************************************************************=
*
ClientConnection::ClientConnection(VNCviewerApp *pApp)=20
{
Init(pApp);
}
ClientConnection::ClientConnection(VNCviewerApp *pApp, SOCKET sock)=20
{
Init(pApp);
m_sock =3D sock;
m_serverInitiated =3D true;
struct sockaddr_in svraddr;
int sasize =3D sizeof(svraddr);
if (getpeername(sock, (struct sockaddr *) &svraddr,=20
&sasize) !=3D SOCKET_ERROR) {
_stprintf(m_host, _T("%d.%d.%d.%d"),=20
svraddr.sin_addr.S_un.S_un_b.s_b1,=20
svraddr.sin_addr.S_un.S_un_b.s_b2,=20
svraddr.sin_addr.S_un.S_un_b.s_b3,=20
svraddr.sin_addr.S_un.S_un_b.s_b4);
m_port =3D svraddr.sin_port;
} else {
_tcscpy(m_host,_T("(unknown)"));
m_port =3D 0;
};
}
ClientConnection::ClientConnection(VNCviewerApp *pApp, LPTSTR host, int =
port)
{
Init(pApp);
_tcsncpy(m_host, host, MAX_HOST_NAME_LEN);
m_port =3D port;
}
void ClientConnection::Init(VNCviewerApp *pApp)
{
m_hwnd =3D 0;
m_desktopName =3D NULL;
m_port =3D -1;
m_serverInitiated =3D false;
m_netbuf =3D NULL;
m_netbufsize =3D 0;
m_hwndNextViewer =3D NULL;=09
m_pApp =3D pApp;
m_dormant =3D false;
m_hBitmapDC =3D NULL;
m_hBitmap =3D NULL;
m_hPalette =3D NULL;
m_encPasswd[0] =3D '\0';
// We take the initial conn options from the application defaults
m_opts =3D m_pApp->m_options;
m_sock =3D INVALID_SOCKET;
m_bKillThread =3D false;
m_threadStarted =3D true;
m_running =3D false;
m_pendingFormatChange =3D false;
m_hScrollPos =3D 0; m_vScrollPos =3D 0;
m_waitingOnEmulateTimer =3D false;
m_emulatingMiddleButton =3D false;
// Create a buffer for various network operations
CheckBufferSize(INITIALNETBUFSIZE);
m_pApp->RegisterConnection(this);
}
//=20
// 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 =3D=3D -1)=20
GetConnectDetails();
// Connect if we're not already connected
if (m_sock =3D=3D INVALID_SOCKET)=20
Connect();
SetSocketOptions();
NegotiateProtocolVersion();
=09
Authenticate();
// Set up windows etc=20
CreateDisplay();
SendClientInit();
=09
ReadServerInit();
=09
CreateLocalFramebuffer();
=09
SetupPixelFormat();
=09
SetFormatAndEncodings();
// This starts the worker thread.
// The rest of the processing continues in run_undetached.
start_undetached();
}
void ClientConnection::CreateDisplay()=20
{
// Create the window
WNDCLASS wndclass;
wndclass.style =3D 0;
wndclass.lpfnWndProc =3D ClientConnection::WndProc;
wndclass.cbClsExtra =3D 0;
wndclass.cbWndExtra =3D 0;
wndclass.hInstance =3D m_pApp->m_instance;
wndclass.hIcon =3D LoadIcon(m_pApp->m_instance, =
MAKEINTRESOURCE(IDI_MAINICON));
switch (m_opts.m_localCursor) {
case NOCURSOR:
wndclass.hCursor =3D LoadCursor(m_pApp->m_instance, =
MAKEINTRESOURCE(IDC_NOCURSOR));
break;
case NORMALCURSOR:
wndclass.hCursor =3D LoadCursor(NULL, IDC_ARROW);
break;
case DOTCURSOR:
default:
wndclass.hCursor =3D LoadCursor(m_pApp->m_instance, =
MAKEINTRESOURCE(IDC_DOTCURSOR));
}
wndclass.hbrBackground =3D (HBRUSH) GetStockObject(BLACK_BRUSH);
wndclass.lpszMenuName =3D (const TCHAR *) NULL;
wndclass.lpszClassName =3D VWR_WND_CLASS_NAME;
RegisterClass(&wndclass);
#ifdef _WIN32_WCE
const DWORD winstyle =3D WS_VSCROLL | WS_HSCROLL | WS_CAPTION | =
WS_SYSMENU;
#else
const DWORD winstyle =3D WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU |=20
WS_MINIMIZEBOX | WS_THICKFRAME | WS_VSCROLL | WS_HSCROLL;
#endif
m_hwnd =3D 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 =3D 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 =3D (LOGPALETTE *) buf;
int pepos =3D 0;
for (int r =3D 5; r >=3D 0; r--) {
for (int g =3D 5; g >=3D 0; g--) {
for (int b =3D 5; b >=3D 0; b--) {
plp->palPalEntry[pepos].peRed =3D r * 255 / 5; =09
plp->palPalEntry[pepos].peGreen =3D g * 255 / 5;
plp->palPalEntry[pepos].peBlue =3D b * 255 / 5;
plp->palPalEntry[pepos].peFlags =3D NULL;
pepos++;
}
}
}
plp->palVersion =3D 0x300;
plp->palNumEntries =3D 216;
m_hPalette =3D CreatePalette(plp);
}
// Add stuff to System menu
HMENU hsysmenu =3D 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"));
//Attempting to Add Ctrl-Esc and Alt-Tab
//24 Jan 2000 MJR
AppendMenu(hsysmenu, MF_STRING, ID_CONN_CTLESC, _T("Ctrl-Esc"));
AppendMenu(hsysmenu, MF_STRING, ID_CONN_ALTTAB, _T("Alt-Tab"));
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 =3D false;
m_hwndNextViewer =3D SetClipboardViewer(m_hwnd); =09
#endif
}
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 =3D sessdlg.m_port;
}
// This is a bit of a hack:=20
// The config file may set various things in the app-level defaults =
which=20
// 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] =3D '\0';
m_pApp->m_options.m_port =3D -1;
m_pApp->m_options.m_connectionSpecified =3D false;
m_pApp->m_options.m_configSpecified =3D false;
}
void ClientConnection::Connect()
{
struct sockaddr_in thataddr;
int res;
=09
m_sock =3D socket(PF_INET, SOCK_STREAM, 0);
if (m_sock =3D=3D INVALID_SOCKET) throw WarningException(_T("Error =
creating socket"));
int one =3D 1;
=09
// The host may be specified as a dotted address "a.b.c.d"
// Try that first
thataddr.sin_addr.s_addr =3D inet_addr(m_host);
=09
// If it wasn't one of those, do gethostbyname
if (thataddr.sin_addr.s_addr =3D=3D INADDR_NONE) {
LPHOSTENT lphost;
lphost =3D gethostbyname(m_host);
=09
if (lphost =3D=3D NULL) {=20
throw WarningException("Failed to get server address.\n\r"
"Did you type the host name correctly?");=20
};
thataddr.sin_addr.s_addr =3D ((LPIN_ADDR) lphost->h_addr)->s_addr;
};
=09
thataddr.sin_family =3D AF_INET;
thataddr.sin_port =3D htons(m_port);
res =3D connect(m_sock, (LPSOCKADDR) &thataddr, sizeof(thataddr));
if (res =3D=3D 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 =3D TRUE;
if (setsockopt(m_sock, IPPROTO_TCP, TCP_NODELAY, (const char *) =
&nodelayval, sizeof(BOOL)))
throw WarningException("Error disabling Nagle's algorithm");
}
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);
throw QuietException(c.m_info);
}
pv[sz_rfbProtocolVersionMsg] =3D 0;
// XXX This is a hack. Under CE we just return to the server the
// version number it gives us without parsing it. =20
// Too much hassle replacing sscanf for now. Fix this!
#ifdef UNDER_CE
m_majorVersion =3D rfbProtocolMajorVersion;
m_minorVersion =3D rfbProtocolMinorVersion;
#else
if =
(sscanf(pv,rfbProtocolVersionFormat,&m_majorVersion,&m_minorVersion) =
!=3D 2) {
throw WarningException(_T("Invalid protocol"));
}
log.Print(0, _T("RFB server supports protocol version %d.%d\n"),
m_majorVersion,m_minorVersion);
if ((m_majorVersion =3D=3D 3) && (m_minorVersion < 3)) {
=09
/* if server is 3.2 we can't use the new authentication */
log.Print(0, _T("Can't use IDEA authentication\n"));
/* This will be reported later if authentication is requested*/
} else {
=09
/* any other server version, just tell the server what we want =
*/
m_majorVersion =3D rfbProtocolMajorVersion;
m_minorVersion =3D rfbProtocolMinorVersion;
}
sprintf(pv,rfbProtocolVersionFormat,m_majorVersion,m_minorVersion);
#endif
WriteExact(pv, sz_rfbProtocolVersionMsg);
log.Print(0, _T("Connected to RFB server, using protocol version =
%d.%d\n"),
rfbProtocolMajorVersion, rfbProtocolMinorVersion);
}
void ClientConnection::Authenticate()
{
CARD32 authScheme, reasonLen, authResult;
CARD8 challenge[CHALLENGESIZE];
=09
ReadExact((char *)&authScheme, 4);
authScheme =3D Swap32IfLE(authScheme);
=09
switch (authScheme) {
=09
case rfbConnFailed:
ReadExact((char *)&reasonLen, 4);
reasonLen =3D Swap32IfLE(reasonLen);
=09
CheckBufferSize(reasonLen+1);
ReadString(m_netbuf, reasonLen);
=09
log.Print(0, _T("RFB connection failed, reason: %s\n"), m_netbuf);
throw WarningException(m_netbuf);
break;
=09
case rfbNoAuth:
log.Print(0, _T("No authentication needed\n"));
break;
=09
case rfbVncAuth:
{
if ((m_majorVersion =3D=3D 3) && (m_minorVersion < 3)) {
/* if server is 3.2 we can't use the new authentication =
*/
log.Print(0, _T("Can't use IDEA authentication\n"));
MessageBox(NULL,=20
_T("Sorry - this server uses an older =
authentication scheme\n\r")
_T("which is no longer supported."),=20
_T("Protocol Version error"),=20
MB_OK | MB_ICONSTOP | MB_SETFOREGROUND | =
MB_TOPMOST);
throw WarningException("Can't use IDEA authentication =
any more!");
}
ReadExact((char *)challenge, CHALLENGESIZE);
=09
char passwd[256];
// Was the password already specified in a config file?
if (strlen((const char *) m_encPasswd)>0) {
char *pw =3D vncDecryptPasswd(m_encPasswd);
strcpy(passwd, pw);
free(pw);
} else {
AuthDialog ad;
ad.DoDialog();=09
#ifndef UNDER_CE
strcpy(passwd, ad.m_passwd);
#else
int origlen =3D _tcslen(ad.m_passwd);
int newlen =3D WideCharToMultiByte(
CP_ACP, // code page
0, // performance and mapping flags
ad.m_passwd, // address of wide-character string
origlen, // number of characters in string
passwd, // address of buffer for new string
255, // size of buffer
NULL, NULL );
=09
passwd[newlen]=3D '\0';
#endif
if (strlen(passwd) =3D=3D 0) {
log.Print(0, _T("Password had zero length\n"));
throw WarningException("Empty password");
}
if (strlen(passwd) > 8) {
passwd[8] =3D '\0';
}
vncEncryptPasswd(m_encPasswd, passwd);
} =09
=09
vncEncryptBytes(challenge, passwd);
/* Lose the plain-text password from memory */
for (int i=3D0; i< (int) strlen(passwd); i++) {
passwd[i] =3D '\0';
}
=09
WriteExact((char *) challenge, CHALLENGESIZE);
ReadExact((char *) &authResult, 4);
=09
authResult =3D Swap32IfLE(authResult);
=09
switch (authResult) {
case rfbVncAuthOK:
log.Print(0, _T("VNC authentication succeeded\n"));
break;
case rfbVncAuthFailed:
log.Print(0, _T("VNC authentication failed!"));
throw WarningException("VNC authentication failed!");
case rfbVncAuthTooMany:
throw WarningException(
"VNC authentication failed - too many tries!");
default:
log.Print(0, _T("Unknown VNC authentication result: %d\n"),
(int)authResult);
throw ErrorException("Unknown VNC authentication result!");
}
break;
}
=09
default:
log.Print(0, _T("Unknown authentication scheme from RFB server: =
%d\n"),
(int)authScheme);
throw ErrorException("Unknown authentication scheme!");
}
}
void ClientConnection::SendClientInit()
{
rfbClientInitMsg ci;
ci.shared =3D m_opts.m_Shared;
WriteExact((char *)&ci, sz_rfbClientInitMsg);
}
void ClientConnection::ReadServerInit()
{
ReadExact((char *)&m_si, sz_rfbServerInitMsg);
=09
m_si.framebufferWidth =3D Swap16IfLE(m_si.framebufferWidth);
m_si.framebufferHeight =3D Swap16IfLE(m_si.framebufferHeight);
m_si.format.redMax =3D Swap16IfLE(m_si.format.redMax);
m_si.format.greenMax =3D Swap16IfLE(m_si.format.greenMax);
m_si.format.blueMax =3D Swap16IfLE(m_si.format.blueMax);
m_si.nameLength =3D Swap32IfLE(m_si.nameLength);
=09
m_desktopName =3D new TCHAR[m_si.nameLength + 2];
#ifdef UNDER_CE
char *deskNameBuf =3D new char[m_si.nameLength + 2];
ReadString(deskNameBuf, m_si.nameLength);
=20
MultiByteToWideChar( CP_ACP, MB_PRECOMPOSED,=20
deskNameBuf, m_si.nameLength,
m_desktopName, m_si.nameLength+1);
delete deskNameBuf;
#else
ReadString(m_desktopName, m_si.nameLength);
#endif
=20
SetWindowText(m_hwnd, m_desktopName);=09
log.Print(0, _T("Desktop name \"%s\"\n"),m_desktopName);
log.Print(1, _T("Geometry %d x %d depth %d\n"),
m_si.framebufferWidth, m_si.framebufferHeight, m_si.format.depth );
SetWindowText(m_hwnd, m_desktopName);=09
SizeWindow();
}
void ClientConnection::SizeWindow()
{
// Find how large the desktop work area is
RECT workrect;
SystemParametersInfo(SPI_GETWORKAREA, 0, &workrect, 0);
int workwidth =3D workrect.right - workrect.left;
int workheight =3D workrect.bottom - workrect.top;
log.Print(2, _T("Screen work area is %d x %d\n"), workwidth, =
workheight);
// Size the window.
// Let's find out how big a window would be needed to display the
// whole desktop (assuming no scrollbars).
RECT fullwinrect;
SetRect(&fullwinrect, 0, 0, m_si.framebufferWidth * m_opts.m_scale_num =
/ m_opts.m_scale_den,=20
m_si.framebufferHeight* m_opts.m_scale_num / =
m_opts.m_scale_den);
AdjustWindowRectEx(&fullwinrect,=20
GetWindowLong(m_hwnd, GWL_STYLE) & ~WS_VSCROLL & ~WS_HSCROLL,=20
FALSE, GetWindowLong(m_hwnd, GWL_EXSTYLE));
m_fullwinwidth =3D fullwinrect.right - fullwinrect.left;
m_fullwinheight =3D fullwinrect.bottom - fullwinrect.top;
m_winwidth =3D min(m_fullwinwidth, workwidth);
m_winheight =3D min(m_fullwinheight, workheight);
SetWindowPos(m_hwnd, HWND_TOP,
workrect.left + (workwidth-m_winwidth) / 2,
workrect.top + (workheight-m_winheight) / 2,
m_winwidth, m_winheight, SWP_SHOWWINDOW);
SetForegroundWindow(m_hwnd);
}
// We keep a local copy of the whole screen. This is not strictly =
necessary
// for VNC, but makes scrolling & deiconifying much smoother.
void ClientConnection::CreateLocalFramebuffer() {
omni_mutex_lock l(m_bitmapdcMutex);
=09
// We create a bitmap which has the same pixel characteristics as
// the local display, in the hope that blitting will be faster.
=09
TempDC hdc(m_hwnd);
m_hBitmap =3D ::CreateCompatibleBitmap(hdc,=20
m_si.framebufferWidth,=20
m_si.framebufferHeight);
=09
if (m_hBitmap =3D=3D NULL)
throw WarningException("Error creating local image of screen.");
=09
// Select this bitmap into the DC with an appropriate palette
ObjectSelector b(m_hBitmapDC, m_hBitmap);
PaletteSelector p(m_hBitmapDC, m_hPalette);
=09
// Put a "please wait" message up initially
RECT rect;
SetRect(&rect, 0,0, m_si.framebufferWidth, m_si.framebufferHeight);
COLORREF bgcol =3D RGB(0xcc, 0xcc, 0xcc);
FillSolidRect(&rect, bgcol);
=09
COLORREF oldbgcol =3D SetBkColor( m_hBitmapDC, bgcol);
COLORREF oldtxtcol =3D SetTextColor(m_hBitmapDC, RGB(0,0,64));
rect.right =3D m_si.framebufferWidth / 2;
rect.bottom =3D m_si.framebufferHeight / 2;
=09
DrawText (m_hBitmapDC, _T("Please wait - initial screen loading"), -1, =
&rect,
DT_SINGLELINE | DT_CENTER | DT_VCENTER);
SetBkColor( m_hBitmapDC, oldbgcol);
SetTextColor(m_hBitmapDC, oldtxtcol);
=09
InvalidateRect(m_hwnd, NULL, FALSE);
}
void ClientConnection::SetupPixelFormat() {
// Have we requested a reduction to 8-bit?
if (m_opts.m_Use8Bit) { =09
=20
log.Print(2, _T("Requesting 8-bit truecolour\n")); =20
m_myFormat =3D vnc8bitFormat;
=20
// We don't support colormaps so we'll ask the server to convert
} else if (!m_si.format.trueColour) {
=20
// We'll just request a standard 16-bit truecolor
log.Print(2, _T("Requesting 16-bit truecolour\n"));
m_myFormat =3D vnc16bitFormat;
=20
} else {
// Normally we just use the sever's format suggestion
m_myFormat =3D m_si.format;
// It's silly requesting more bits than our current display has, but
// in fact it doesn't usually amount to much on the network.
// Windows doesn't support 8-bit truecolour.
// If our display is palette-based, we want more than 8 bit anyway,
// unless we're going to start doing palette stuff at the server.
// So the main use would be a 24-bit true-colour desktop being viewed
// on a 16-bit true-colour display, and unless you have lots of =
images
// and hence lots of raw-encoded stuff, the size of the pixel is not
// going to make much difference.
// We therefore don't bother with any restrictions, but here's the
// start of the code if we wanted to do it.
if (false) {
=09
// Get a DC for the root window
TempDC hrootdc(NULL);
int localBitsPerPixel =3D GetDeviceCaps(hrootdc, BITSPIXEL);
int localRasterCaps =3D GetDeviceCaps(hrootdc, RASTERCAPS);
log.Print(2, _T("Memory DC has depth of %d and %s =
pallete-based.\n"),=20
localBitsPerPixel, (localRasterCaps & RC_PALETTE) ? "is" : "is =
not");
=09
// If we're using truecolor, and the server has more bits than we do
if ( (localBitsPerPixel > m_myFormat.depth) &&=20
! (localRasterCaps & RC_PALETTE)) {
m_myFormat.depth =3D localBitsPerPixel;
// create a bitmap compatible with the current display
// call GetDIBits twice to get the colour info.
// set colour masks and shifts
=09
}
}
}
// The endian will be set before sending
}
void ClientConnection::SetFormatAndEncodings()
{
// Set pixel format to myFormat
=20
rfbSetPixelFormatMsg spf;
spf.type =3D rfbSetPixelFormat;
spf.format =3D m_myFormat;
spf.format.redMax =3D Swap16IfLE(spf.format.redMax);
spf.format.greenMax =3D Swap16IfLE(spf.format.greenMax);
spf.format.blueMax =3D Swap16IfLE(spf.format.blueMax);
spf.format.bigEndian =3D 0;
WriteExact((char *)&spf, sz_rfbSetPixelFormatMsg);
// The number of bytes required to hold at least one pixel.
m_minPixelBytes =3D (m_myFormat.bitsPerPixel + 7) >> 3;
// Set encodings
char buf[sz_rfbSetEncodingsMsg + MAX_ENCODINGS * 4];
rfbSetEncodingsMsg *se =3D (rfbSetEncodingsMsg *)buf;
CARD32 *encs =3D (CARD32 *)(&buf[sz_rfbSetEncodingsMsg]);
int len =3D 0;
=09
se->type =3D rfbSetEncodings;
se->nEncodings =3D 0;
// Put the preferred encoding first, and change it if the
// preferred encoding is not actually usable.
for (int i =3D LASTENCODING; i >=3D rfbEncodingRaw; i--)
{
if (m_opts.m_PreferredEncoding =3D=3D i) {
if (m_opts.m_UseEnc[i]) {
encs[se->nEncodings++] =3D Swap32IfLE(i);
} else {
m_opts.m_PreferredEncoding--;
}
}
}
// Now we go through and put in all the other encodings in order.
// We do rather assume that the most recent encoding is the most
// desirable!
for (i =3D LASTENCODING; i >=3D rfbEncodingRaw; i--)
{
if ( (m_opts.m_PreferredEncoding !=3D i) &&
(m_opts.m_UseEnc[i]))
{
encs[se->nEncodings++] =3D Swap32IfLE(i);
}
}
len =3D sz_rfbSetEncodingsMsg + se->nEncodings * 4;
=09
se->nEncodings =3D Swap16IfLE(se->nEncodings);
=09
WriteExact((char *) buf, len);
}
// Closing down the connection.
// Close the socket, kill the thread.
void ClientConnection::KillThread()
{
m_bKillThread =3D true;
m_running =3D false;
if (m_sock !=3D INVALID_SOCKET) {
shutdown(m_sock, SD_BOTH);
closesocket(m_sock);
m_sock =3D INVALID_SOCKET;
}
}
ClientConnection::~ClientConnection()
{
if (m_hwnd !=3D 0)
DestroyWindow(m_hwnd);
if (m_sock !=3D INVALID_SOCKET) {
shutdown(m_sock, SD_BOTH);
closesocket(m_sock);
m_sock =3D INVALID_SOCKET;
}
if (m_desktopName !=3D NULL) delete [] m_desktopName;
delete [] m_netbuf;
DeleteDC(m_hBitmapDC);
if (m_hBitmap !=3D NULL)
DeleteObject(m_hBitmap);
if (m_hBitmapDC !=3D NULL)
DeleteObject(m_hBitmapDC);
if (m_hPalette !=3D NULL)
DeleteObject(m_hPalette);
=09
m_pApp->DeregisterConnection(this);
}
// You can specify a dx & dy outside the limits; the return value will
// tell you whether it actually scrolled.
bool ClientConnection::ScrollScreen(int dx, int dy)=20
{
dx =3D max(dx, -m_hScrollPos);
//dx =3D min(dx, m_hScrollMax-(m_cliwidth-1)-m_hScrollPos);
dx =3D min(dx, m_hScrollMax-(m_cliwidth)-m_hScrollPos);
dy =3D max(dy, -m_vScrollPos);
//dy =3D min(dy, m_vScrollMax-(m_cliheight-1)-m_vScrollPos);
dy =3D min(dy, m_vScrollMax-(m_cliheight)-m_vScrollPos);
if (dx || dy) {
m_hScrollPos +=3D dx;
m_vScrollPos +=3D dy;
RECT clirect;
GetClientRect(m_hwnd, &clirect);
ScrollWindowEx(m_hwnd, -dx, -dy, NULL, &clirect, NULL, NULL, =
SW_INVALIDATE);
UpdateScrollbars();
UpdateWindow(m_hwnd);
return true;
}
return false;
}
// Process windows messages
LRESULT CALLBACK ClientConnection::WndProc(HWND hwnd, UINT iMsg,=20
WPARAM wParam, LPARAM lParam) {
=09
// This is a static method, so we don't know which instantiation we're =
// dealing with. But we've stored a 'pseudo-this' in the window data.
ClientConnection *_this =3D (ClientConnection *) GetWindowLong(hwnd, =
GWL_USERDATA);
switch (iMsg) {
case WM_CREATE:
return 0;
case WM_REGIONUPDATED:
_this->DoBlit();
_this->SendAppropriateFramebufferUpdateRequest();
return 0;
case WM_PAINT:
_this->DoBlit();
return 0;
case WM_TIMER:
if (wParam =3D=3D _this->m_emulate3ButtonsTimer)
{
_this->SubProcessPointerEvent(=20
_this->m_emulateButtonPressedX,
_this->m_emulateButtonPressedY,
_this->m_emulateKeyFlags);
KillTimer(_this->m_hwnd, _this->m_emulate3ButtonsTimer);
_this->m_waitingOnEmulateTimer =3D false;
}
return 0;
=20
case WM_LBUTTONDOWN:
case WM_LBUTTONUP:
case WM_MBUTTONDOWN:
case WM_MBUTTONUP:
case WM_RBUTTONDOWN:
case WM_RBUTTONUP:
case WM_MOUSEMOVE:
{
if (!_this->m_running) return 0;
if (GetFocus() !=3D hwnd) return 0;
int x =3D LOWORD(lParam);
int y =3D HIWORD(lParam);
if (_this->InFullScreenMode()) {
if (_this->BumpScroll(x,y))
return 0;
}
if ( _this->m_opts.m_ViewOnly) return 0;
_this->ProcessPointerEvent(x,y, wParam, iMsg);
return 0;
}
case WM_KEYDOWN:
case WM_KEYUP:
case WM_SYSKEYDOWN:
case WM_SYSKEYUP:
{
if (!_this->m_running) return 0;
if ( _this->m_opts.m_ViewOnly) return 0;
_this->ProcessKeyEvent((int) wParam, (DWORD) lParam);
return 0;
}
case WM_CHAR:
case WM_SYSCHAR:
#ifdef UNDER_CE
{
int key =3D wParam;
log.Print(4,_T("CHAR msg : %02x\n"), key);
// Control keys which are in the Keymap table will already
// have been handled.
if (key =3D=3D 0x0D || // return
key =3D=3D 0x20 || // space
key =3D=3D 0x08) // backspace
return 0;
if (key < 32) key +=3D 64; // map ctrl-keys onto alphabet
if (key > 32 && key < 127) {
_this->SendKeyEvent(wParam & 0xff, true);
_this->SendKeyEvent(wParam & 0xff, false);
}
return 0;
}
#endif
case WM_DEADCHAR:
case WM_SYSDEADCHAR:
return 0;
case WM_SETFOCUS:
if (_this->InFullScreenMode())
SetWindowPos(hwnd, HWND_TOPMOST, 0,0,100,100, SWP_NOMOVE | =
SWP_NOSIZE);
return 0;
// Cacnel modifiers when we lose focus
case WM_KILLFOCUS:
{
if (!_this->m_running) return 0;
if (_this->InFullScreenMode()) {
// We must top being topmost, but we want to choose our
// position carefully.
HWND foreground =3D GetForegroundWindow();
HWND hwndafter =3D NULL;
if ((foreground =3D=3D NULL) ||=20
(GetWindowLong(foreground, GWL_EXSTYLE) & WS_EX_TOPMOST)) {
hwndafter =3D HWND_NOTOPMOST;
} else {
hwndafter =3D GetNextWindow(foreground, GW_HWNDNEXT);=20
}
SetWindowPos(hwnd, hwndafter, 0,0,100,100, SWP_NOMOVE | SWP_NOSIZE =
| SWP_NOACTIVATE);
}
log.Print(6, _T("Losing focus - cancelling modifiers\n"));
_this->SendKeyEvent(XK_Alt_L, false);
_this->SendKeyEvent(XK_Control_L, false);
_this->SendKeyEvent(XK_Shift_L, false);
_this->SendKeyEvent(XK_Alt_R, false);
_this->SendKeyEvent(XK_Control_R, false);
_this->SendKeyEvent(XK_Shift_R, false);
return 0;
}
case WM_CLOSE:
{
// Close the worker thread as well
_this->KillThread();
DestroyWindow(hwnd);
return 0;
}
case WM_DESTROY:
{
#ifndef UNDER_CE
// Remove us from the clipboard viewer chain
BOOL res =3D ChangeClipboardChain( hwnd, _this->m_hwndNextViewer);
#endif
if (_this->m_waitingOnEmulateTimer)
{
KillTimer(_this->m_hwnd, _this->m_emulate3ButtonsTimer);
_this->m_waitingOnEmulateTimer =3D false;
}
=09
_this->m_hwnd =3D 0;
// We are currently in the main thread.
// The worker thread should be about to finish if
// it hasn't already. Wait for it.
try {
void *p;
_this->join(&p); // After joining, _this is no longer valid
} catch (omni_thread_invalid& e) {
// The thread probably hasn't been started yet,
}
=09
return 0;
}
=09
case WM_WINDOWPOSCHANGED:
case WM_SIZE:
{
// Calculate window dimensions
RECT rect;
GetWindowRect(hwnd, &rect);
// update these for the record
_this->m_winwidth =3D rect.right - rect.left;
_this->m_winheight =3D rect.bottom - rect.top;
// If the current window size would be large enough to hold the
// whole screen without scrollbars, or if we're full-screen,
// we turn them off. Under CE, the scroll bars are unchangeable.
#ifndef UNDER_CE
if (_this->InFullScreenMode() ||
_this->m_winwidth >=3D _this->m_fullwinwidth &&
_this->m_winheight >=3D _this->m_fullwinheight ) {
ShowScrollBar(hwnd, SB_HORZ, FALSE);
ShowScrollBar(hwnd, SB_VERT, FALSE);
} else {
ShowScrollBar(hwnd, SB_HORZ, TRUE);
ShowScrollBar(hwnd, SB_VERT, TRUE);
}
#endif
// Update these for the record
// And consider that in full-screen mode the window
// is actually bigger than the remote screen.
GetClientRect(hwnd, &rect);
_this->m_cliwidth =3D min( rect.right - rect.left,=20
_this->m_si.framebufferWidth );
_this->m_cliheight =3D min( rect.bottom - rect.top,
_this->m_si.framebufferHeight );
_this->m_hScrollMax =3D _this->m_si.framebufferWidth * =
_this->m_opts.m_scale_num / _this->m_opts.m_scale_den;
_this->m_vScrollMax =3D _this->m_si.framebufferHeight* =
_this->m_opts.m_scale_num / _this->m_opts.m_scale_den;
=20
int newhpos, newvpos;
newhpos =3D max(0, min(_this->m_hScrollPos,=20
_this->m_hScrollMax - max(_this->m_cliwidth, 0)));
newvpos =3D max(0, min(_this->m_vScrollPos,=20
_this->m_vScrollMax - max(_this->m_cliheight, =
0)));
ScrollWindowEx(hwnd, _this->m_hScrollPos-newhpos, =
_this->m_vScrollPos-newvpos,
NULL, &rect, NULL, NULL, SW_INVALIDATE);
=09
_this->m_hScrollPos =3D newhpos;
_this->m_vScrollPos =3D newvpos;
_this->UpdateScrollbars();
return 0;
}
case WM_HSCROLL:
{
int dx =3D 0;
int pos =3D HIWORD(wParam);
switch (LOWORD(wParam)) {
case SB_LINEUP:
dx =3D -2; break;
case SB_LINEDOWN:
dx =3D 2; break;
case SB_PAGEUP:
dx =3D _this->m_cliwidth * -1/4; break;
case SB_PAGEDOWN:
dx =3D _this->m_cliwidth * 1/4; break;
case SB_THUMBPOSITION:
dx =3D pos - _this->m_hScrollPos;
case SB_THUMBTRACK:
dx =3D pos - _this->m_hScrollPos;
}
_this->ScrollScreen(dx,0);
return 0;
}
case WM_VSCROLL:
{
int dy =3D 0;
int pos =3D HIWORD(wParam);
switch (LOWORD(wParam)) {
case SB_LINEUP:
dy =3D -2; break;
case SB_LINEDOWN:
dy =3D 2; break;
case SB_PAGEUP:
dy =3D _this->m_cliheight * -1/4; break;
case SB_PAGEDOWN:
dy =3D _this->m_cliheight * 1/4; break;
case SB_THUMBPOSITION:
dy =3D pos - _this->m_vScrollPos;
case SB_THUMBTRACK:
dy =3D pos - _this->m_vScrollPos;
}
_this->ScrollScreen(0,dy);
return 0;
}
case WM_QUERYNEWPALETTE:
{
TempDC hDC(hwnd);
=09
// Select and realize hPalette
PaletteSelector p(hDC, _this->m_hPalette);
InvalidateRect(hwnd, NULL, FALSE);
UpdateWindow(hwnd);
return TRUE;
}
case WM_PALETTECHANGED:
// If this application did not change the palette, select
// and realize this application's palette
if ((HWND) wParam !=3D hwnd)
{
// Need the window's DC for SelectPalette/RealizePalette
TempDC hDC(hwnd);
PaletteSelector p(hDC, _this->m_hPalette);
// When updating the colors for an inactive window,
// UpdateColors can be called because it is faster than
// redrawing the client area (even though the results are
// not as good)
#ifndef UNDER_CE
UpdateColors(hDC);
#else
InvalidateRect(hwnd, NULL, FALSE);
UpdateWindow(hwnd);
#endif
}
break;
#ifndef UNDER_CE
case WM_SIZING:
{
// Don't allow sizing larger than framebuffer
RECT *lprc =3D (LPRECT) lParam;
switch (wParam) {
case WMSZ_RIGHT:=20
case WMSZ_TOPRIGHT:
case WMSZ_BOTTOMRIGHT:
lprc->right =3D min(lprc->right, lprc->left + =
_this->m_fullwinwidth+1);
break;
case WMSZ_LEFT:
case WMSZ_TOPLEFT:
case WMSZ_BOTTOMLEFT:
lprc->left =3D max(lprc->left, lprc->right - =
_this->m_fullwinwidth);
break;
}
=09
switch (wParam) {
case WMSZ_TOP:
case WMSZ_TOPLEFT:
case WMSZ_TOPRIGHT:
lprc->top =3D max(lprc->top, lprc->bottom - =
_this->m_fullwinheight);
break;
case WMSZ_BOTTOM:
case WMSZ_BOTTOMLEFT:
case WMSZ_BOTTOMRIGHT:
lprc->bottom =3D min(lprc->bottom, lprc->top + =
_this->m_fullwinheight);
break;
}
return 0;
}
=09
case WM_SETCURSOR:
{
// if we have the focus, let the cursor change as normal
if (GetFocus() =3D=3D hwnd)=20
break;
// if not, set to default system cursor
SetCursor( LoadCursor(NULL, IDC_ARROW));
return 0;
}
case WM_SYSCOMMAND:
{
switch (LOWORD(wParam)) {
case SC_MINIMIZE:
_this->SetDormant(true);
break;
case SC_RESTORE:
_this->SetDormant(false);
break;
case ID_NEWCONN:
_this->m_pApp->NewConnection();
return 0;
case ID_CONN_SAVE_AS:
_this->SaveConnection();
return 0;
case IDC_OPTIONBUTTON:=20
if (_this->m_opts.DoDialog(true)) {
_this->m_pendingFormatChange =3D true;
// This isn't always needed
_this->SizeWindow();
InvalidateRect(hwnd, NULL, TRUE);
//_this->UpdateScrollbars();
//UpdateWindow(hwnd);
// Make the window correspond to the requested state
_this->RealiseFullScreenMode();=09
};=09
return 0;
case IDD_APP_ABOUT:
ShowAboutBox();
return 0;
case ID_CONN_ABOUT:
_this->ShowConnInfo();
return 0;
case ID_FULLSCREEN:=20
// Toggle full screen mode
_this->SetFullScreenMode(!_this->InFullScreenMode());
return 0;
case ID_REQUEST_REFRESH:=20
// Request a full-screen update
_this->SendFullFramebufferUpdateRequest();
return 0;
case ID_CONN_CTLALTDEL:
_this->SendKeyEvent(XK_Control_L, true);
_this->SendKeyEvent(XK_Alt_L, true);
_this->SendKeyEvent(XK_Delete, true);
_this->SendKeyEvent(XK_Delete, false);
_this->SendKeyEvent(XK_Alt_L, false);
_this->SendKeyEvent(XK_Control_L, false);
return 0;
case ID_CONN_CTLDOWN:
_this->SendKeyEvent(XK_Control_L, true);
return 0;
case ID_CONN_CTLUP:
_this->SendKeyEvent(XK_Control_L, false);
return 0;
case ID_CONN_ALTDOWN:
_this->SendKeyEvent(XK_Alt_L, true);
return 0;
case ID_CONN_ALTUP:
_this->SendKeyEvent(XK_Alt_L, false);
return 0;
case ID_CONN_CTLESC:
// This handles ctl-esc menu option
// 24 Jan 2000 MJR
_this->SendKeyEvent(XK_Control_L, true);
_this->SendKeyEvent(XK_Escape, true);
_this->SendKeyEvent(XK_Escape, false);
_this->SendKeyEvent(XK_Control_L, false);
return 0;
case ID_CONN_ALTTAB:
// This handles alt-tab menu option
//24 Jan 2000 MJR
_this->SendKeyEvent(XK_Alt_L, true);
_this->SendKeyEvent(XK_Tab, true);
_this->SendKeyEvent(XK_Tab, false);
_this->SendKeyEvent(XK_Alt_L, false);
return 0;
case ID_CLOSEDAEMON:
if (MessageBox(NULL, _T("Are you sure you want to exit?"),=20
_T("Closing VNCviewer"),=20
MB_YESNO | MB_ICONQUESTION | MB_DEFBUTTON2) =3D=3D IDYES)
PostQuitMessage(0);
return 0;
}
break;
}
case WM_DRAWCLIPBOARD:
_this->ProcessLocalClipboardChange();
return 0;
case WM_CHANGECBCHAIN:
{
// The clipboard chain is changing
HWND hWndRemove =3D (HWND) wParam; // handle of window being =
removed=20
HWND hWndNext =3D (HWND) lParam; // handle of next window in =
chain=20
// If next window is closing, update our pointer.
if (hWndRemove =3D=3D _this->m_hwndNextViewer) =20
_this->m_hwndNextViewer =3D hWndNext; =20
// Otherwise, pass the message to the next link. =20
else if (_this->m_hwndNextViewer !=3D NULL)=20
::SendMessage(_this->m_hwndNextViewer, WM_CHANGECBCHAIN,=20
(WPARAM) hWndRemove, (LPARAM) hWndNext ); =20
return 0;
}
#endif
}
return DefWindowProc(hwnd, iMsg, wParam, lParam);
=09
// We know about an unused variable here.
#pragma warning(disable : 4101)
}
#pragma warning(default : 4101)
// ProcessPointerEvent handles the delicate case of emulating 3 buttons
// on a two button mouse, then passes events off to =
SubProcessPointerEvent.
void
ClientConnection::ProcessPointerEvent(int x, int y, DWORD keyflags, =
UINT msg)=20
{
if (m_opts.m_Emul3Buttons) {
// XXX To be done:
// If this is a left or right press, the user may be=20
// about to press the other button to emulate a middle press.
// We need to start a timer, and if it expires without any
// further presses, then we send the button press.=20
// If a press of the other button, or any release, comes in
// before timer has expired, we cancel timer & take different action.
if (m_waitingOnEmulateTimer)
{
if (msg =3D=3D WM_LBUTTONUP || msg =3D=3D WM_RBUTTONUP ||
abs(x - m_emulateButtonPressedX) > m_opts.m_Emul3Fuzz ||
abs(y - m_emulateButtonPressedY) > m_opts.m_Emul3Fuzz)
{
// if button released or we moved too far then cancel.
// First let the remote know where the button was down
SubProcessPointerEvent(
m_emulateButtonPressedX,=20
m_emulateButtonPressedY,=20
m_emulateKeyFlags);
// Then tell it where we are now
SubProcessPointerEvent(x, y, keyflags);
}
else if (
(msg =3D=3D WM_LBUTTONDOWN && (m_emulateKeyFlags & =
MK_RBUTTON))
|| (msg =3D=3D WM_RBUTTONDOWN && (m_emulateKeyFlags & =
MK_LBUTTON)))
{
// Triggered an emulate; remove left and right buttons, put
// in middle one.
DWORD emulatekeys =3D keyflags & ~(MK_LBUTTON|MK_RBUTTON);
emulatekeys |=3D MK_MBUTTON;
SubProcessPointerEvent(x, y, emulatekeys);
=20
m_emulatingMiddleButton =3D true;
}
else
{
// handle movement normally & don't kill timer.
// just remove the pressed button from the mask.
DWORD keymask =3D m_emulateKeyFlags & (MK_LBUTTON|MK_RBUTTON);
DWORD emulatekeys =3D keyflags & ~keymask;
SubProcessPointerEvent(x, y, emulatekeys);
return;
}
=20
// if we reached here, we don't need the timer anymore.
KillTimer(m_hwnd, m_emulate3ButtonsTimer);
m_waitingOnEmulateTimer =3D false;
}
else if (m_emulatingMiddleButton)
{
if ((keyflags & MK_LBUTTON) =3D=3D 0 && (keyflags & MK_RBUTTON) =
=3D=3D 0)
{
// We finish emulation only when both buttons come back up.
m_emulatingMiddleButton =3D false;
SubProcessPointerEvent(x, y, keyflags);
}
else
{
// keep emulating.
DWORD emulatekeys =3D keyflags & ~(MK_LBUTTON|MK_RBUTTON);
emulatekeys |=3D MK_MBUTTON;
SubProcessPointerEvent(x, y, emulatekeys);
}
}
else
{
// Start considering emulation if we've pressed a button
// and the other isn't pressed.
if ( (msg =3D=3D WM_LBUTTONDOWN && !(keyflags & MK_RBUTTON))
|| (msg =3D=3D WM_RBUTTONDOWN && !(keyflags & MK_LBUTTON)))
{
// Start timer for emulation.
m_emulate3ButtonsTimer =3D=20
SetTimer(
m_hwnd,=20
IDT_EMULATE3BUTTONSTIMER,=20
m_opts.m_Emul3Timeout,=20
NULL);
=20
if (!m_emulate3ButtonsTimer)
{
log.Print(0, _T("Failed to create timer for emulating 3 =
buttons"));
PostMessage(m_hwnd, WM_CLOSE, 0, 0);
return;
}
=20
m_waitingOnEmulateTimer =3D true;
=20
// Note that we don't send the event here; we're batching it for
// later.
m_emulateKeyFlags =3D keyflags;
m_emulateButtonPressedX =3D x;
m_emulateButtonPressedY =3D y;
}
else
{
// just send event noramlly
SubProcessPointerEvent(x, y, keyflags);
}
}
}
else
{
SubProcessPointerEvent(x, y, keyflags);
}
}
// SubProcessPointerEvent takes windows positions and flags and =
converts=20
// them into VNC ones.
inline void=20
ClientConnection::SubProcessPointerEvent(int x, int y, DWORD keyflags)=20
{
int mask;
=20
if (m_opts.m_SwapMouse) {
mask =3D ( ((keyflags & MK_LBUTTON) ? rfbButton1Mask : 0) |
((keyflags & MK_MBUTTON) ? rfbButton3Mask : 0) |
((keyflags & MK_RBUTTON) ? rfbButton2Mask : 0) );
} else {
mask =3D ( ((keyflags & MK_LBUTTON) ? rfbButton1Mask : 0) |
((keyflags & MK_MBUTTON) ? rfbButton2Mask : 0) |
((keyflags & MK_RBUTTON) ? rfbButton3Mask : 0) );
}
=09
try {
SendPointerEvent((x + m_hScrollPos) * m_opts.m_scale_den / =
m_opts.m_scale_num,=20
(y + m_vScrollPos) * m_opts.m_scale_den / m_opts.m_scale_num, =
mask);
} catch (Exception &e) {
e.Report();
PostMessage(m_hwnd, WM_CLOSE, 0, 0);
}
}
//
// SendPointerEvent.
//
inline void
ClientConnection::SendPointerEvent(int x, int y, int buttonMask)
{
rfbPointerEventMsg pe;
pe.type =3D rfbPointerEvent;
pe.buttonMask =3D buttonMask;
if (x < 0) x =3D 0;
if (y < 0) y =3D 0;
pe.x =3D Swap16IfLE(x);
pe.y =3D Swap16IfLE(y);
WriteExact((char *)&pe, sz_rfbPointerEventMsg);
}
//
// ProcessKeyEvent
//
// Normally a single Windows key event will map onto a single RFB
// key message, but this is not always the case. Much of the stuff
// here is to handle AltGr (=3DCtrl-Alt) on international keyboards.
// Example cases:
//
// We want Ctrl-F to be sent as:
// Ctrl-Down, F-Down, F-Up, Ctrl-Up.
// because there is no keysym for ctrl-f, and because the ctrl
// will already have been sent by the time we get the F.
//
// On German keyboards, @ is produced using AltGr-Q, which is
// Ctrl-Alt-Q. But @ is a valid keysym in its own right, and when
// a German user types this combination, he doesn't mean Ctrl- "at" .
// So for this we will send, in total:
//
// Ctrl-Down, Alt-Down, =20
// (when we get the AltGr pressed)
//
// Alt-Up, Ctrl-Up, @-Down, Ctrl-Down, Alt-Down=20
// (when we discover that this is @ being pressed)
//
// Alt-Up, Ctrl-Up, @-Up, Ctrl-Down, Alt-Down
// (when we discover that this is @ being released)
//
// Alt-Up, Ctrl-Up
// (when the AltGr is released)
inline void ClientConnection::ProcessKeyEvent(int virtkey, DWORD =
keyData)
{
bool down =3D ((keyData & 0x80000000l) =3D=3D 0);
// if virtkey found in mapping table, send X equivalent
// else
// try to convert directly to ascii
// if result is in range supported by X keysyms,
// raise any modifiers, send it, then restore mods
// else
// calculate what the ascii would be without mods
// send that
#ifdef _DEBUG
#ifdef UNDER_CE
char *keyname=3D"";
#else
char keyname[32];
if (GetKeyNameText( keyData,keyname, 31)) {
log.Print(4, _T("Process key: %s (keyData %04x): "), keyname, =
keyData);
};
#endif
#endif
try {
KeyActionSpec kas =3D m_keymap.PCtoX(virtkey, keyData); =20
=09
if (kas.releaseModifiers & KEYMAP_LCONTROL) {
SendKeyEvent(XK_Control_L, false );
log.Print(5, _T("fake L Ctrl raised\n"));
}
if (kas.releaseModifiers & KEYMAP_LALT) {
SendKeyEvent(XK_Alt_L, false );
log.Print(5, _T("fake L Alt raised\n"));
}
if (kas.releaseModifiers & KEYMAP_RCONTROL) {
SendKeyEvent(XK_Control_R, false );
log.Print(5, _T("fake R Ctrl raised\n"));
}
if (kas.releaseModifiers & KEYMAP_RALT) {
SendKeyEvent(XK_Alt_R, false );
log.Print(5, _T("fake R Alt raised\n"));
}
=09
for (int i =3D 0; kas.keycodes[i] !=3D XK_VoidSymbol && i < =
MaxKeysPerKey; i++) {
SendKeyEvent(kas.keycodes[i], down );
log.Print(4, _T("Sent keysym %04x (%s)\n"),=20
kas.keycodes[i], down ? _T("press") : _T("release"));
}
=09
if (kas.releaseModifiers & KEYMAP_RALT) {
SendKeyEvent(XK_Alt_R, true );
log.Print(5, _T("fake R Alt pressed\n"));
}
if (kas.releaseModifiers & KEYMAP_RCONTROL) {
SendKeyEvent(XK_Control_R, true );
log.Print(5, _T("fake R Ctrl pressed\n"));
}
if (kas.releaseModifiers & KEYMAP_LALT) {
SendKeyEvent(XK_Alt_L, false );
log.Print(5, _T("fake L Alt pressed\n"));
}
if (kas.releaseModifiers & KEYMAP_LCONTROL) {
SendKeyEvent(XK_Control_L, false );
log.Print(5, _T("fake L Ctrl pressed\n"));
}
} catch (Exception &e) {
e.Report();
PostMessage(m_hwnd, WM_CLOSE, 0, 0);
}
}
//
// SendKeyEvent
//
inline void
ClientConnection::SendKeyEvent(CARD32 key, bool down)
{
rfbKeyEventMsg ke;
ke.type =3D rfbKeyEvent;
ke.down =3D down ? 1 : 0;
ke.key =3D Swap32IfLE(key);
WriteExact((char *)&ke, sz_rfbKeyEventMsg);
log.Print(6, _T("SendKeyEvent: key =3D x%04x status =3D %s\n"), =
key,=20
down ? _T("down") : _T("up"));
}
#ifndef UNDER_CE
//
// SendClientCutText
//
void ClientConnection::SendClientCutText(char *str, int len)
{
rfbClientCutTextMsg cct;
cct.type =3D rfbClientCutText;
cct.length =3D Swap32IfLE(len);
WriteExact((char *)&cct, sz_rfbClientCutTextMsg);
WriteExact(str, len);
log.Print(6, _T("Sent %d bytes of clipboard\n"), len);
}
#endif
// Copy any updated areas from the bitmap onto the screen.
inline void ClientConnection::DoBlit()=20
{
if (m_hBitmap =3D=3D NULL) return;
if (!m_running) return;
=09
// No other threads can use bitmap DC
omni_mutex_lock l(m_bitmapdcMutex);
PAINTSTRUCT ps;
HDC hdc =3D BeginPaint(m_hwnd, &ps);
// Select and realize hPalette
PaletteSelector p(hdc, m_hPalette);
ObjectSelector b(m_hBitmapDC, m_hBitmap);
=09
if (m_opts.m_delay) {
// Display the area to be updated for debugging purposes
COLORREF oldbgcol =3D SetBkColor(hdc, RGB(0,0,0));
::ExtTextOut(hdc, 0, 0, ETO_OPAQUE, &ps.rcPaint, NULL, 0, NULL);
SetBkColor(hdc,oldbgcol);
::Sleep(m_pApp->m_options.m_delay);
}
=09
if (m_opts.m_scaling) {
int n =3D m_opts.m_scale_num;
int d =3D m_opts.m_scale_den;
=09
// We're about to do some scaling on these values in the StretchBlt
// We want to make sure that they divide nicely by n so we round them
// down and up appropriately.
ps.rcPaint.left =3D ((ps.rcPaint.left + m_hScrollPos) / n * n) =
- m_hScrollPos;
ps.rcPaint.right =3D ((ps.rcPaint.right + m_hScrollPos + n - 1) / n =
* n) - m_hScrollPos;
ps.rcPaint.top =3D ((ps.rcPaint.top + m_vScrollPos) / n * n) =
- m_vScrollPos;
ps.rcPaint.bottom =3D ((ps.rcPaint.bottom + m_vScrollPos + n - 1) / n =
* n) - m_vScrollPos;
=09
// This is supposed to give better results. I think my driver =
ignores it?
SetStretchBltMode(hdc, HALFTONE);
// The docs say that you should call SetBrushOrgEx after =
SetStretchBltMode,=20
// but not what the arguments should be.
SetBrushOrgEx(hdc, 0,0, NULL);
=09
if (!StretchBlt(
hdc,=20
ps.rcPaint.left,=20
ps.rcPaint.top,=20
ps.rcPaint.right-ps.rcPaint.left,=20
ps.rcPaint.bottom-ps.rcPaint.top,=20
m_hBitmapDC,=20
(ps.rcPaint.left+m_hScrollPos) * d / n,=20
(ps.rcPaint.top+m_vScrollPos) * d / n,
(ps.rcPaint.right-ps.rcPaint.left) * d / n,=20
(ps.rcPaint.bottom-ps.rcPaint.top) * d / n,=20
SRCCOPY))=20
{
log.Print(0, _T("Blit error %d\n"), GetLastError());
// throw ErrorException("Error in blit!\n");
};
} else {
if (!BitBlt(hdc, ps.rcPaint.left, ps.rcPaint.top,=20
ps.rcPaint.right-ps.rcPaint.left, ps.rcPaint.bottom-ps.rcPaint.top,=20
m_hBitmapDC, ps.rcPaint.left+m_hScrollPos, =
ps.rcPaint.top+m_vScrollPos, SRCCOPY))=20
{
log.Print(0, _T("Blit error %d\n"), GetLastError());
// throw ErrorException("Error in blit!\n");
}
}
=09
EndPaint(m_hwnd, &ps);
}
inline void ClientConnection::UpdateScrollbars()=20
{
// We don't update the actual scrollbar info in full-screen mode
// because it causes them to flicker.
bool setInfo =3D !InFullScreenMode();
SCROLLINFO scri;
scri.cbSize =3D sizeof(scri);
scri.fMask =3D SIF_ALL;
scri.nMin =3D 0;
scri.nMax =3D m_hScrollMax;=20
scri.nPage=3D m_cliwidth;
scri.nPos =3D m_hScrollPos;=20
=09
if (setInfo)=20
SetScrollInfo(m_hwnd, SB_HORZ, &scri, TRUE);
=09
scri.cbSize =3D sizeof(scri);
scri.fMask =3D SIF_ALL;
scri.nMin =3D 0;
scri.nMax =3D m_vScrollMax; =20
scri.nPage=3D m_cliheight;
scri.nPos =3D m_vScrollPos;=20
=09
if (setInfo)=20
SetScrollInfo(m_hwnd, SB_VERT, &scri, TRUE);
}
void ClientConnection::ShowConnInfo()
{
TCHAR buf[2048];
#ifndef UNDER_CE
char kbdname[9];
GetKeyboardLayoutName(kbdname);
#else
TCHAR *kbdname =3D _T("(n/a)");
#endif
_stprintf(
buf,
_T("Connected to: %s:\n\r")
_T("Host: %s port: %d\n\r\n\r")
_T("Desktop geometry: %d x %d x %d\n\r")
_T("Using depth: %d\n\r")
_T("Current protocol version: %d.%d\n\r\n\r")
_T("Current keyboard name: %s\n\r"),
m_desktopName, m_host, m_port,
m_si.framebufferWidth, m_si.framebufferHeight, m_si.format.depth,
m_myFormat.depth,
m_majorVersion, m_minorVersion,
kbdname);
MessageBox(NULL, buf, _T("VNC connection info"), MB_ICONINFORMATION | =
MB_OK);
}
// ********************************************************************
// Methods after this point are generally called by the worker thread.
// They finish the initialisation, then chiefly read data from the =
server.
// ********************************************************************
void* ClientConnection::run_undetached(void* arg) {
log.Print(9,_T("Update-processing thread started\n"));
m_threadStarted =3D true;
try {
SendFullFramebufferUpdateRequest();
RealiseFullScreenMode();
m_running =3D true;
UpdateWindow(m_hwnd);
=09
while (!m_bKillThread) {
=09
// Look at the type of the message, but leave it in the buffer=20
CARD8 msgType;
{
omni_mutex_lock l(m_readMutex); // we need this if we're not =
using ReadExact
int bytes =3D recv(m_sock, (char *) &msgType, 1, MSG_PEEK);
if (bytes =3D=3D 0) {
log.Print(0, _T("Socket closed\n") );
throw QuietException(_T("SocketClosed"));
}
if (bytes < 0) {
log.Print(3, _T("Socket error reading message: %d\n"), =
WSAGetLastError() );
throw WarningException("Error while waiting for server =
message");
}
}
=09
switch (msgType) {
case rfbFramebufferUpdate:
ReadScreenUpdate();
break;
case rfbSetColourMapEntries:
log.Print(3, _T("rfbSetColourMapEntries read but not =
supported\n") );
throw WarningException("Unhandled SetColormap message type =
received!\n");
break;
case rfbBell:
ReadBell();
break;
case rfbServerCutText:
ReadServerCutText();
break;
default:
log.Print(3, _T("Unknown message type x%02x\n"), =
msgType );
throw WarningException("Unhandled message type received!\n");
}
}
=20
log.Print(4, _T("Update-processing thread finishing\n") );
} catch (WarningException) {
PostMessage(m_hwnd, WM_CLOSE, 0, 0);
} catch (QuietException &e) {
e.Report();
PostMessage(m_hwnd, WM_CLOSE, 0, 0);
}=20
return this;
}
//
// Requesting screen updates from the server
//
inline void
ClientConnection::SendFramebufferUpdateRequest(int x, int y, int w, int =
h, bool incremental)
{
rfbFramebufferUpdateRequestMsg fur;
fur.type =3D rfbFramebufferUpdateRequest;
fur.incremental =3D incremental ? 1 : 0;
fur.x =3D Swap16IfLE(x);
fur.y =3D Swap16IfLE(y);
fur.w =3D Swap16IfLE(w);
fur.h =3D Swap16IfLE(h);
log.Print(10, _T("Request %s update\n"), incremental ? =
_T("incremental") : _T("full"));
WriteExact((char *)&fur, sz_rfbFramebufferUpdateRequestMsg);
}
inline void ClientConnection::SendIncrementalFramebufferUpdateRequest()
{
SendFramebufferUpdateRequest(0, 0, m_si.framebufferWidth,
m_si.framebufferHeight, true);
}
inline void ClientConnection::SendFullFramebufferUpdateRequest()
{
SendFramebufferUpdateRequest(0, 0, m_si.framebufferWidth,
m_si.framebufferHeight, false);
}
void ClientConnection::SendAppropriateFramebufferUpdateRequest()
{
if (m_pendingFormatChange) {
log.Print(3, _T("Requesting new pixel format\n") );
rfbPixelFormat oldFormat =3D m_myFormat;
SetupPixelFormat();
SetFormatAndEncodings();
m_pendingFormatChange =3D false;
// If the pixel format has changed, request whole screen
if (memcmp(&m_myFormat, &oldFormat, sizeof(rfbPixelFormat)) !=3D 0) {
SendFullFramebufferUpdateRequest();=09
} else {
SendIncrementalFramebufferUpdateRequest();
}
} else {
if (!m_dormant)
SendIncrementalFramebufferUpdateRequest();
}
}
// A ScreenUpdate message has been received
void ClientConnection::ReadScreenUpdate() {
rfbFramebufferUpdateMsg sut;
ReadExact((char *) &sut, sz_rfbFramebufferUpdateMsg);
sut.nRects =3D Swap16IfLE(sut.nRects);
if (sut.nRects =3D=3D 0) return;
=09
for (UINT i=3D0; i < sut.nRects; i++) {
rfbFramebufferUpdateRectHeader surh;
ReadExact((char *) &surh, sz_rfbFramebufferUpdateRectHeader);
surh.r.x =3D Swap16IfLE(surh.r.x);
surh.r.y =3D Swap16IfLE(surh.r.y);
surh.r.w =3D Swap16IfLE(surh.r.w);
surh.r.h =3D Swap16IfLE(surh.r.h);
surh.encoding =3D Swap32IfLE(surh.encoding);
=09
switch (surh.encoding) {
case rfbEncodingRaw:
ReadRawRect(&surh);
break;
case rfbEncodingCopyRect:
ReadCopyRect(&surh);
break;
case rfbEncodingRRE:
ReadRRERect(&surh);
break;
case rfbEncodingCoRRE:
ReadCoRRERect(&surh);
break;
case rfbEncodingHextile:
ReadHextileRect(&surh);
break;
default:
log.Print(0, _T("Unknown encoding %d - not supported!\n"), =
surh.encoding);
break;
}
=09
RECT rect;
=09
if (m_opts.m_scaling) {
// If we're scaling, we transform the coordinates of the rectangle =
received
// into the corresponding window coords, and invalidate *that* =
region.
=09
// First, we adjust coords to avoid rounding down when scaling.
int n =3D m_opts.m_scale_num;
int d =3D m_opts.m_scale_den;
int left =3D (surh.r.x / d) * d;
int top =3D (surh.r.y / d) * d;
int right =3D (surh.r.x + surh.r.w + d - 1) / d * d; // round up
int bottom =3D (surh.r.y + surh.r.h + d - 1) / d * d; // round up
// Then we scale the rectangle, which should now give whole numbers
rect.left =3D (left * n / d) - m_hScrollPos;
rect.top =3D (top * n / d) - m_vScrollPos;
rect.right =3D (right * n / d) - m_hScrollPos;
rect.bottom =3D (bottom * n / d) - m_vScrollPos;;
} else {
rect.left =3D surh.r.x - m_hScrollPos;
rect.top =3D surh.r.y - m_vScrollPos;
rect.right =3D rect.left + surh.r.w;
rect.bottom =3D rect.top + surh.r.h;
}
InvalidateRect(m_hwnd, &rect, FALSE);
}=09
// Inform the other thread that an update is needed.
PostMessage(m_hwnd, WM_REGIONUPDATED, NULL, NULL);
}=09
void ClientConnection::SetDormant(bool newstate)
{
log.Print(5, _T("%s dormant mode\n"), newstate ? _T("Entering") : =
_T("Leaving"));
m_dormant =3D newstate;
if (!m_dormant)
SendIncrementalFramebufferUpdateRequest();
}
// The server has copied some text to the clipboard - put it=20
// in the local clipboard too.
void ClientConnection::ReadServerCutText() {
rfbServerCutTextMsg sctm;
log.Print(6, _T("Read remote clipboard change\n"));
ReadExact((char *) &sctm, sz_rfbServerCutTextMsg);
int len =3D Swap32IfLE(sctm.length);
=09
CheckBufferSize(len);
if (len =3D=3D 0) {
m_netbuf[0] =3D '\0';
} else {
ReadString(m_netbuf, len);
}
UpdateLocalClipboard(m_netbuf, len);
}
void ClientConnection::ReadBell() {
rfbBellMsg bm;
ReadExact((char *) &bm, sz_rfbBellMsg);
#ifdef UNDER_CE
MessageBeep( MB_OK );
#else
if (! ::PlaySound("VNCViewerBell", NULL,=20
SND_APPLICATION | SND_ALIAS | SND_NODEFAULT | SND_ASYNC) ) {
::Beep(440, 125);
}
#endif
if (m_opts.m_DeiconifyOnBell) {
if (IsIconic(m_hwnd)) {
SetDormant(false);
ShowWindow(m_hwnd, SW_SHOWNORMAL);
}
}
log.Print(6, _T("Bell!\n"));
}
// General utilities -------------------------------------------------
// Reads the number of bytes specified into the buffer given
inline void ClientConnection::ReadExact(char *inbuf, int wanted)
{
omni_mutex_lock l(m_readMutex);
int offset =3D 0;
log.Print(10, _T(" reading %d bytes\n"), wanted);
=09
while (wanted > 0) {
int bytes =3D recv(m_sock, inbuf+offset, wanted, 0);
if (bytes =3D=3D 0) throw WarningException("Connection closed.");
if (bytes =3D=3D SOCKET_ERROR) {
int err =3D ::GetLastError();
log.Print(1, _T("Socket error while reading %d\n"), err);
m_running =3D false;
throw WarningException("ReadExact: Socket error while reading.");
}
wanted -=3D bytes;
offset +=3D bytes;
}
}
// Read the number of bytes and return them zero terminated in the =
buffer=20
inline void ClientConnection::ReadString(char *buf, int length)
{
if (length > 0)
ReadExact(buf, length);
buf[length] =3D '\0';
log.Print(10, _T("Read a %d-byte string\n"), length);
}
// Sends the number of bytes specified from the buffer
inline void ClientConnection::WriteExact(char *buf, int bytes)
{
if (bytes =3D=3D 0) return;
omni_mutex_lock l(m_writeMutex);
log.Print(10, _T(" writing %d bytes\n"), bytes);
int i =3D 0;
int j;
while (i < bytes) {
j =3D send(m_sock, buf+i, bytes-i, 0);
if (j =3D=3D SOCKET_ERROR || j=3D=3D0) {
LPVOID lpMsgBuf;
int err =3D ::GetLastError();
FormatMessage( =20
FORMAT_MESSAGE_ALLOCATE_BUFFER |=20
FORMAT_MESSAGE_FROM_SYSTEM | =20
FORMAT_MESSAGE_IGNORE_INSERTS, NULL,
err, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language
(LPTSTR) &lpMsgBuf, 0, NULL ); // Process any inserts in lpMsgBuf.
log.Print(1, _T("Socket error %d: %s\n"), err, lpMsgBuf);
LocalFree( lpMsgBuf );
m_running =3D false;
throw WarningException("WriteExact: Socket error while writing.");
}
i +=3D j;
}
}
// Makes sure netbuf is at least as big as the specified size.
// Note that netbuf itself may change as a result of this call.
// Throws an exception on failure.
inline void ClientConnection::CheckBufferSize(int bufsize)
{
if (m_netbufsize > bufsize) return;
omni_mutex_lock l(m_bufferMutex);
char *newbuf =3D new char[bufsize+256];;
if (newbuf =3D=3D NULL) {
throw ErrorException("Insufficient memory to allocate network =
buffer.");
}
// Only if we're successful...
if (m_netbuf !=3D NULL)
delete [] m_netbuf;
m_netbuf =3D newbuf;
m_netbufsize=3Dbufsize + 256;
log.Print(4, _T("bufsize expanded to %d\n"), m_netbufsize);
}
------_=_NextPart_000_01BF6686.626FDDD4
Content-Type: application/octet-stream;
name="resource.h"
Content-Disposition: attachment;
filename="resource.h"
//{{NO_DEPENDENCIES}}
// Microsoft Developer Studio generated include file.
// Used by vncviewer.rc
//
#define IDR_TRAY 101
#define IDR_TRAYMENU 103
#define ID_CONN_ABOUT 103
#define IDI_MAINICON 104
#define ID_CONN_CTLDOWN 104
#define IDC_DOTCURSOR 105
#define ID_CONN_CTLUP 105
#define IDB_VNCBITMAP 106
#define ID_CONN_ALTDOWN 106
#define ID_CONN_ALTUP 107
#define IDB_VNC32 108
#define ID_CONN_CTLALTDEL 108
#define IDD_APP_ABOUT 109
#define IDT_TRAYTIMER 109
#define ID_FULLSCREEN 110
#define ID_REQUEST_REFRESH 111
#define IDD_SESSION_DLG 135
#define IDD_SERVINFO_DLG 137
#define IDD_AUTH_DIALOG 138
#define IDD_OPTIONDIALOG 151
#define IDT_EMULATE3BUTTONSTIMER 152
#define ID_CONN_SAVE_AS 153
#define IDC_NOCURSOR 153
// Added to support CTL-ESC and ALT-Tab
// 24 Jan 2000 MJR
#define ID_CONN_CTLESC 154
#define ID_CONN_ALTTAB 155
#define IDC_HOSTNAME_EDIT 1000
#define IDC_SHARED 1000
#define IDC_DPYNUM_EDIT 1001
#define IDC_BELLDEICONIFY 1001
#define IDC_SERVINFO_EDIT 1005
#define IDC_VIEWONLY 1005
#define IDC_PASSWD_EDIT 1009
#define IDC_OPTIONBUTTON 1012
#define IDC_8BITCHECK 1013
#define IDC_RAWRADIO 1018
#define IDC_CRECTRADIO 1019
#define IDC_RRERADIO 1020
#define IDC_RBSRADIO 1021
#define IDC_CORRERADIO 1022
#define IDC_HEXTILERADIO 1023
#define IDC_EMULATECHECK 1024
#define IDC_FULLSCREEN 1027
#define IDC_DISABLECLIPBOARD 1028
#define IDC_SCALING 1029
#define IDC_SCALE_NUM 1031
#define IDC_SCALE_DEN 1032
#define ID_SESSION_SET_CRECT 32777
#define ID_SESSION_SWAPMOUSE 32785
#define ID_CLOSEDAEMON 40001
#define ID_SHOWWINDOW 40004
#define ID_NEWCONN 40006
#define ID_MENUITEM40007 40007
// Next default values for new objects
//
#ifdef APSTUDIO_INVOKED
#ifndef APSTUDIO_READONLY_SYMBOLS
#define _APS_NEXT_RESOURCE_VALUE 154
#define _APS_NEXT_COMMAND_VALUE 40010
#define _APS_NEXT_CONTROL_VALUE 1032
#define _APS_NEXT_SYMED_VALUE 154
#endif
#endif
------_=_NextPart_000_01BF6686.626FDDD4--
---------------------------------------------------------------------
The VNC mailing list - see http://www.uk.research.att.com/vnc/intouch.html
---------------------------------------------------------------------