From 729598cb00d791bbdfe23ebe0023d3a1c3962f83 Mon Sep 17 00:00:00 2001 From: Constantin Kaplinsky Date: Thu, 25 May 2006 05:12:25 +0000 Subject: [PATCH] Migrating to new directory structure adopted from the RealVNC's source tree. More changes will follow. git-svn-id: svn://svn.code.sf.net/p/tigervnc/code/trunk@591 3789f03b-4d11-0410-bbf8-ca57d06f2519 --- win/README.txt | 128 ++ win/README_BINARY.txt | 126 ++ win/logmessages/logmessages.dsp | 202 +++ win/logmessages/messages.h | 47 + win/logmessages/messages.mc | 7 + win/logmessages/messages.rc | 2 + win/rfb_win32/AboutDialog.cxx | 49 + win/rfb_win32/AboutDialog.h | 55 + win/rfb_win32/BitmapInfo.h | 48 + win/rfb_win32/CKeyboard.cxx | 258 ++++ win/rfb_win32/CKeyboard.h | 51 + win/rfb_win32/CPointer.cxx | 186 +++ win/rfb_win32/CPointer.h | 74 ++ win/rfb_win32/CleanDesktop.cxx | 321 +++++ win/rfb_win32/CleanDesktop.h | 57 + win/rfb_win32/Clipboard.cxx | 200 +++ win/rfb_win32/Clipboard.h | 66 + win/rfb_win32/CompatibleBitmap.h | 46 + win/rfb_win32/ComputerName.h | 40 + win/rfb_win32/CurrentUser.cxx | 152 +++ win/rfb_win32/CurrentUser.h | 100 ++ win/rfb_win32/DIBSectionBuffer.cxx | 222 ++++ win/rfb_win32/DIBSectionBuffer.h | 85 ++ win/rfb_win32/DeviceContext.cxx | 188 +++ win/rfb_win32/DeviceContext.h | 86 ++ win/rfb_win32/DeviceFrameBuffer.cxx | 289 +++++ win/rfb_win32/DeviceFrameBuffer.h | 106 ++ win/rfb_win32/Dialog.cxx | 391 ++++++ win/rfb_win32/Dialog.h | 158 +++ win/rfb_win32/DynamicFn.cxx | 45 + win/rfb_win32/DynamicFn.h | 51 + win/rfb_win32/EventManager.cxx | 103 ++ win/rfb_win32/EventManager.h | 77 ++ win/rfb_win32/FolderManager.cxx | 279 ++++ win/rfb_win32/FolderManager.h | 66 + win/rfb_win32/Handle.h | 43 + win/rfb_win32/IconInfo.h | 44 + win/rfb_win32/IntervalTimer.h | 70 + win/rfb_win32/LaunchProcess.cxx | 103 ++ win/rfb_win32/LaunchProcess.h | 71 + win/rfb_win32/ListViewControl.cxx | 103 ++ win/rfb_win32/ListViewControl.h | 35 + win/rfb_win32/LocalMem.h | 45 + win/rfb_win32/LogicalPalette.h | 90 ++ win/rfb_win32/LowLevelKeyEvents.cxx | 96 ++ win/rfb_win32/LowLevelKeyEvents.h | 49 + win/rfb_win32/ModuleFileName.h | 40 + win/rfb_win32/MonitorInfo.cxx | 205 +++ win/rfb_win32/MonitorInfo.h | 72 ++ win/rfb_win32/MsgBox.h | 63 + win/rfb_win32/MsgWindow.cxx | 116 ++ win/rfb_win32/MsgWindow.h | 53 + win/rfb_win32/OSVersion.cxx | 47 + win/rfb_win32/OSVersion.h | 53 + win/rfb_win32/ProgressControl.cxx | 97 ++ win/rfb_win32/ProgressControl.h | 56 + win/rfb_win32/RegConfig.cxx | 114 ++ win/rfb_win32/RegConfig.h | 84 ++ win/rfb_win32/Registry.cxx | 316 +++++ win/rfb_win32/Registry.h | 112 ++ win/rfb_win32/SDisplay.cxx | 524 ++++++++ win/rfb_win32/SDisplay.h | 163 +++ win/rfb_win32/SDisplayCoreDriver.h | 52 + win/rfb_win32/SDisplayCorePolling.cxx | 81 ++ win/rfb_win32/SDisplayCorePolling.h | 75 ++ win/rfb_win32/SDisplayCoreWMHooks.cxx | 74 ++ win/rfb_win32/SDisplayCoreWMHooks.h | 68 + win/rfb_win32/SFileTransferManagerWin32.cxx | 71 + win/rfb_win32/SFileTransferManagerWin32.h | 48 + win/rfb_win32/SFileTransferWin32.cxx | 125 ++ win/rfb_win32/SFileTransferWin32.h | 59 + win/rfb_win32/SInput.cxx | 466 +++++++ win/rfb_win32/SInput.h | 68 + win/rfb_win32/ScaledDIBSectionBuffer.cxx | 124 ++ win/rfb_win32/ScaledDIBSectionBuffer.h | 78 ++ win/rfb_win32/Security.cxx | 192 +++ win/rfb_win32/Security.h | 123 ++ win/rfb_win32/Service.cxx | 645 +++++++++ win/rfb_win32/Service.h | 128 ++ win/rfb_win32/SocketManager.cxx | 213 +++ win/rfb_win32/SocketManager.h | 90 ++ win/rfb_win32/TCharArray.cxx | 85 ++ win/rfb_win32/TCharArray.h | 135 ++ win/rfb_win32/Threading.cxx | 151 +++ win/rfb_win32/Threading.h | 154 +++ win/rfb_win32/ToolBar.cxx | 216 ++++ win/rfb_win32/ToolBar.h | 132 ++ win/rfb_win32/TrayIcon.h | 89 ++ win/rfb_win32/TsSessions.cxx | 93 ++ win/rfb_win32/TsSessions.h | 61 + win/rfb_win32/WMCursor.cxx | 104 ++ win/rfb_win32/WMCursor.h | 61 + win/rfb_win32/WMHooks.cxx | 394 ++++++ win/rfb_win32/WMHooks.h | 92 ++ win/rfb_win32/WMNotifier.cxx | 57 + win/rfb_win32/WMNotifier.h | 68 + win/rfb_win32/WMPoller.cxx | 85 ++ win/rfb_win32/WMPoller.h | 62 + win/rfb_win32/WMShatter.cxx | 57 + win/rfb_win32/WMShatter.h | 50 + win/rfb_win32/WMWindowCopyRect.cxx | 67 + win/rfb_win32/WMWindowCopyRect.h | 53 + win/rfb_win32/Win32Util.cxx | 114 ++ win/rfb_win32/Win32Util.h | 55 + win/rfb_win32/keymap.h | 149 +++ win/rfb_win32/rfb_win32.dsp | 513 ++++++++ win/rfbplayer/ChoosePixelFormatDialog.h | 59 + win/rfbplayer/EditPixelFormatDialog.h | 104 ++ win/rfbplayer/FbsInputStream.cxx | 251 ++++ win/rfbplayer/FbsInputStream.h | 68 + win/rfbplayer/GotoPosDialog.h | 44 + win/rfbplayer/InfoDialog.h | 41 + win/rfbplayer/OptionsDialog.h | 112 ++ win/rfbplayer/PixelFormatList.cxx | 159 +++ win/rfbplayer/PixelFormatList.h | 85 ++ win/rfbplayer/PlayerOptions.cxx | 152 +++ win/rfbplayer/PlayerOptions.h | 77 ++ win/rfbplayer/PlayerToolBar.cxx | 248 ++++ win/rfbplayer/PlayerToolBar.h | 77 ++ win/rfbplayer/RfbProto.cxx | 142 ++ win/rfbplayer/RfbProto.h | 69 + win/rfbplayer/SessionInfoDialog.h | 100 ++ win/rfbplayer/UserPixelFormatsDialog.h | 105 ++ win/rfbplayer/buildTime.cxx | 1 + win/rfbplayer/resource.h | 81 ++ win/rfbplayer/rfbSessionReader.h | 59 + win/rfbplayer/rfbplayer.cxx | 1294 +++++++++++++++++++ win/rfbplayer/rfbplayer.dsp | 207 +++ win/rfbplayer/rfbplayer.h | 195 +++ win/rfbplayer/rfbplayer.ico | Bin 0 -> 766 bytes win/rfbplayer/rfbplayer.rc | 466 +++++++ win/rfbplayer/toolbar.bmp | Bin 0 -> 630 bytes win/vnc.dsw | 248 ++++ win/vncconfig/Authentication.h | 142 ++ win/vncconfig/Connections.h | 298 +++++ win/vncconfig/Desktop.h | 94 ++ win/vncconfig/Hooking.h | 88 ++ win/vncconfig/Inputs.h | 84 ++ win/vncconfig/Legacy.cxx | 248 ++++ win/vncconfig/Legacy.h | 85 ++ win/vncconfig/PasswordDialog.cxx | 52 + win/vncconfig/PasswordDialog.h | 40 + win/vncconfig/Sharing.h | 59 + win/vncconfig/resource.h | 102 ++ win/vncconfig/vncconfig.cxx | 191 +++ win/vncconfig/vncconfig.dsp | 196 +++ win/vncconfig/vncconfig.exe.manifest | 22 + win/vncconfig/vncconfig.ico | Bin 0 -> 1078 bytes win/vncconfig/vncconfig.rc | 496 +++++++ win/vncviewer/CConn.cxx | 712 ++++++++++ win/vncviewer/CConn.h | 165 +++ win/vncviewer/CConnOptions.cxx | 450 +++++++ win/vncviewer/CConnOptions.h | 97 ++ win/vncviewer/CConnThread.cxx | 198 +++ win/vncviewer/CConnThread.h | 57 + win/vncviewer/ConnectingDialog.cxx | 160 +++ win/vncviewer/ConnectingDialog.h | 65 + win/vncviewer/ConnectionDialog.cxx | 79 ++ win/vncviewer/ConnectionDialog.h | 52 + win/vncviewer/DesktopWindow.cxx | 1103 ++++++++++++++++ win/vncviewer/DesktopWindow.h | 254 ++++ win/vncviewer/FTBrowseDlg.cxx | 196 +++ win/vncviewer/FTBrowseDlg.h | 70 + win/vncviewer/FTDialog.cxx | 1162 +++++++++++++++++ win/vncviewer/FTDialog.h | 243 ++++ win/vncviewer/FTListView.cxx | 211 +++ win/vncviewer/FTListView.h | 69 + win/vncviewer/FTProgress.cxx | 151 +++ win/vncviewer/FTProgress.h | 66 + win/vncviewer/FileTransfer.cxx | 803 ++++++++++++ win/vncviewer/FileTransfer.h | 136 ++ win/vncviewer/InfoDialog.cxx | 65 + win/vncviewer/InfoDialog.h | 48 + win/vncviewer/ListenServer.h | 56 + win/vncviewer/ListenTrayIcon.h | 95 ++ win/vncviewer/MRU.h | 133 ++ win/vncviewer/OptionsDialog.cxx | 337 +++++ win/vncviewer/OptionsDialog.h | 48 + win/vncviewer/UserPasswdDialog.cxx | 85 ++ win/vncviewer/UserPasswdDialog.h | 58 + win/vncviewer/ViewerToolBar.cxx | 117 ++ win/vncviewer/ViewerToolBar.h | 38 + win/vncviewer/buildTime.cxx | 18 + win/vncviewer/cursor1.cur | Bin 0 -> 326 bytes win/vncviewer/ftdir.ico | Bin 0 -> 318 bytes win/vncviewer/ftfile.ico | Bin 0 -> 318 bytes win/vncviewer/ftreload.ico | Bin 0 -> 318 bytes win/vncviewer/ftup.ico | Bin 0 -> 318 bytes win/vncviewer/resource.h | 154 +++ win/vncviewer/toolbar.bmp | Bin 0 -> 1654 bytes win/vncviewer/vncviewer.bmp | Bin 0 -> 2006 bytes win/vncviewer/vncviewer.cxx | 295 +++++ win/vncviewer/vncviewer.dsp | 321 +++++ win/vncviewer/vncviewer.exe.manifest | 22 + win/vncviewer/vncviewer.ico | Bin 0 -> 1078 bytes win/vncviewer/vncviewer.rc | 708 ++++++++++ win/winvnc/AddNewClientDialog.h | 56 + win/winvnc/ControlPanel.cxx | 161 +++ win/winvnc/ControlPanel.h | 45 + win/winvnc/JavaViewer.cxx | 98 ++ win/winvnc/JavaViewer.h | 55 + win/winvnc/ManagedListener.cxx | 94 ++ win/winvnc/ManagedListener.h | 57 + win/winvnc/QueryConnectDialog.cxx | 100 ++ win/winvnc/QueryConnectDialog.h | 60 + win/winvnc/STrayIcon.cxx | 280 ++++ win/winvnc/STrayIcon.h | 61 + win/winvnc/VNCServerService.cxx | 52 + win/winvnc/VNCServerService.h | 41 + win/winvnc/VNCServerWin32.cxx | 333 +++++ win/winvnc/VNCServerWin32.h | 130 ++ win/winvnc/buildTime.cxx | 18 + win/winvnc/connecte.ico | Bin 0 -> 766 bytes win/winvnc/connected.ico | Bin 0 -> 1078 bytes win/winvnc/icon_dis.ico | Bin 0 -> 766 bytes win/winvnc/resource.h | 54 + win/winvnc/winvnc.bmp | Bin 0 -> 2018 bytes win/winvnc/winvnc.cxx | 262 ++++ win/winvnc/winvnc.dsp | 252 ++++ win/winvnc/winvnc.ico | Bin 0 -> 1078 bytes win/winvnc/winvnc.rc | 328 +++++ win/winvnc/winvnc4.exe.manifest | 22 + win/wm_hooks/resource.h | 15 + win/wm_hooks/wm_hooks.cxx | 465 +++++++ win/wm_hooks/wm_hooks.def | 5 + win/wm_hooks/wm_hooks.dsp | 149 +++ win/wm_hooks/wm_hooks.h | 102 ++ win/wm_hooks/wm_hooks.rc | 109 ++ 228 files changed, 32244 insertions(+) create mode 100644 win/README.txt create mode 100644 win/README_BINARY.txt create mode 100644 win/logmessages/logmessages.dsp create mode 100644 win/logmessages/messages.h create mode 100644 win/logmessages/messages.mc create mode 100644 win/logmessages/messages.rc create mode 100644 win/rfb_win32/AboutDialog.cxx create mode 100644 win/rfb_win32/AboutDialog.h create mode 100644 win/rfb_win32/BitmapInfo.h create mode 100644 win/rfb_win32/CKeyboard.cxx create mode 100644 win/rfb_win32/CKeyboard.h create mode 100644 win/rfb_win32/CPointer.cxx create mode 100644 win/rfb_win32/CPointer.h create mode 100644 win/rfb_win32/CleanDesktop.cxx create mode 100644 win/rfb_win32/CleanDesktop.h create mode 100644 win/rfb_win32/Clipboard.cxx create mode 100644 win/rfb_win32/Clipboard.h create mode 100644 win/rfb_win32/CompatibleBitmap.h create mode 100644 win/rfb_win32/ComputerName.h create mode 100644 win/rfb_win32/CurrentUser.cxx create mode 100644 win/rfb_win32/CurrentUser.h create mode 100644 win/rfb_win32/DIBSectionBuffer.cxx create mode 100644 win/rfb_win32/DIBSectionBuffer.h create mode 100644 win/rfb_win32/DeviceContext.cxx create mode 100644 win/rfb_win32/DeviceContext.h create mode 100644 win/rfb_win32/DeviceFrameBuffer.cxx create mode 100644 win/rfb_win32/DeviceFrameBuffer.h create mode 100644 win/rfb_win32/Dialog.cxx create mode 100644 win/rfb_win32/Dialog.h create mode 100644 win/rfb_win32/DynamicFn.cxx create mode 100644 win/rfb_win32/DynamicFn.h create mode 100644 win/rfb_win32/EventManager.cxx create mode 100644 win/rfb_win32/EventManager.h create mode 100644 win/rfb_win32/FolderManager.cxx create mode 100644 win/rfb_win32/FolderManager.h create mode 100644 win/rfb_win32/Handle.h create mode 100644 win/rfb_win32/IconInfo.h create mode 100644 win/rfb_win32/IntervalTimer.h create mode 100644 win/rfb_win32/LaunchProcess.cxx create mode 100644 win/rfb_win32/LaunchProcess.h create mode 100644 win/rfb_win32/ListViewControl.cxx create mode 100644 win/rfb_win32/ListViewControl.h create mode 100644 win/rfb_win32/LocalMem.h create mode 100644 win/rfb_win32/LogicalPalette.h create mode 100644 win/rfb_win32/LowLevelKeyEvents.cxx create mode 100644 win/rfb_win32/LowLevelKeyEvents.h create mode 100644 win/rfb_win32/ModuleFileName.h create mode 100644 win/rfb_win32/MonitorInfo.cxx create mode 100644 win/rfb_win32/MonitorInfo.h create mode 100644 win/rfb_win32/MsgBox.h create mode 100644 win/rfb_win32/MsgWindow.cxx create mode 100644 win/rfb_win32/MsgWindow.h create mode 100644 win/rfb_win32/OSVersion.cxx create mode 100644 win/rfb_win32/OSVersion.h create mode 100644 win/rfb_win32/ProgressControl.cxx create mode 100644 win/rfb_win32/ProgressControl.h create mode 100644 win/rfb_win32/RegConfig.cxx create mode 100644 win/rfb_win32/RegConfig.h create mode 100644 win/rfb_win32/Registry.cxx create mode 100644 win/rfb_win32/Registry.h create mode 100644 win/rfb_win32/SDisplay.cxx create mode 100644 win/rfb_win32/SDisplay.h create mode 100644 win/rfb_win32/SDisplayCoreDriver.h create mode 100644 win/rfb_win32/SDisplayCorePolling.cxx create mode 100644 win/rfb_win32/SDisplayCorePolling.h create mode 100644 win/rfb_win32/SDisplayCoreWMHooks.cxx create mode 100644 win/rfb_win32/SDisplayCoreWMHooks.h create mode 100644 win/rfb_win32/SFileTransferManagerWin32.cxx create mode 100644 win/rfb_win32/SFileTransferManagerWin32.h create mode 100644 win/rfb_win32/SFileTransferWin32.cxx create mode 100644 win/rfb_win32/SFileTransferWin32.h create mode 100644 win/rfb_win32/SInput.cxx create mode 100644 win/rfb_win32/SInput.h create mode 100644 win/rfb_win32/ScaledDIBSectionBuffer.cxx create mode 100644 win/rfb_win32/ScaledDIBSectionBuffer.h create mode 100644 win/rfb_win32/Security.cxx create mode 100644 win/rfb_win32/Security.h create mode 100644 win/rfb_win32/Service.cxx create mode 100644 win/rfb_win32/Service.h create mode 100644 win/rfb_win32/SocketManager.cxx create mode 100644 win/rfb_win32/SocketManager.h create mode 100644 win/rfb_win32/TCharArray.cxx create mode 100644 win/rfb_win32/TCharArray.h create mode 100644 win/rfb_win32/Threading.cxx create mode 100644 win/rfb_win32/Threading.h create mode 100644 win/rfb_win32/ToolBar.cxx create mode 100644 win/rfb_win32/ToolBar.h create mode 100644 win/rfb_win32/TrayIcon.h create mode 100644 win/rfb_win32/TsSessions.cxx create mode 100644 win/rfb_win32/TsSessions.h create mode 100644 win/rfb_win32/WMCursor.cxx create mode 100644 win/rfb_win32/WMCursor.h create mode 100644 win/rfb_win32/WMHooks.cxx create mode 100644 win/rfb_win32/WMHooks.h create mode 100644 win/rfb_win32/WMNotifier.cxx create mode 100644 win/rfb_win32/WMNotifier.h create mode 100644 win/rfb_win32/WMPoller.cxx create mode 100644 win/rfb_win32/WMPoller.h create mode 100644 win/rfb_win32/WMShatter.cxx create mode 100644 win/rfb_win32/WMShatter.h create mode 100644 win/rfb_win32/WMWindowCopyRect.cxx create mode 100644 win/rfb_win32/WMWindowCopyRect.h create mode 100644 win/rfb_win32/Win32Util.cxx create mode 100644 win/rfb_win32/Win32Util.h create mode 100644 win/rfb_win32/keymap.h create mode 100644 win/rfb_win32/rfb_win32.dsp create mode 100644 win/rfbplayer/ChoosePixelFormatDialog.h create mode 100644 win/rfbplayer/EditPixelFormatDialog.h create mode 100644 win/rfbplayer/FbsInputStream.cxx create mode 100644 win/rfbplayer/FbsInputStream.h create mode 100644 win/rfbplayer/GotoPosDialog.h create mode 100644 win/rfbplayer/InfoDialog.h create mode 100644 win/rfbplayer/OptionsDialog.h create mode 100644 win/rfbplayer/PixelFormatList.cxx create mode 100644 win/rfbplayer/PixelFormatList.h create mode 100644 win/rfbplayer/PlayerOptions.cxx create mode 100644 win/rfbplayer/PlayerOptions.h create mode 100644 win/rfbplayer/PlayerToolBar.cxx create mode 100644 win/rfbplayer/PlayerToolBar.h create mode 100644 win/rfbplayer/RfbProto.cxx create mode 100644 win/rfbplayer/RfbProto.h create mode 100644 win/rfbplayer/SessionInfoDialog.h create mode 100644 win/rfbplayer/UserPixelFormatsDialog.h create mode 100644 win/rfbplayer/buildTime.cxx create mode 100644 win/rfbplayer/resource.h create mode 100644 win/rfbplayer/rfbSessionReader.h create mode 100644 win/rfbplayer/rfbplayer.cxx create mode 100644 win/rfbplayer/rfbplayer.dsp create mode 100644 win/rfbplayer/rfbplayer.h create mode 100644 win/rfbplayer/rfbplayer.ico create mode 100644 win/rfbplayer/rfbplayer.rc create mode 100644 win/rfbplayer/toolbar.bmp create mode 100644 win/vnc.dsw create mode 100644 win/vncconfig/Authentication.h create mode 100644 win/vncconfig/Connections.h create mode 100644 win/vncconfig/Desktop.h create mode 100644 win/vncconfig/Hooking.h create mode 100644 win/vncconfig/Inputs.h create mode 100644 win/vncconfig/Legacy.cxx create mode 100644 win/vncconfig/Legacy.h create mode 100644 win/vncconfig/PasswordDialog.cxx create mode 100644 win/vncconfig/PasswordDialog.h create mode 100644 win/vncconfig/Sharing.h create mode 100644 win/vncconfig/resource.h create mode 100644 win/vncconfig/vncconfig.cxx create mode 100644 win/vncconfig/vncconfig.dsp create mode 100644 win/vncconfig/vncconfig.exe.manifest create mode 100644 win/vncconfig/vncconfig.ico create mode 100644 win/vncconfig/vncconfig.rc create mode 100644 win/vncviewer/CConn.cxx create mode 100644 win/vncviewer/CConn.h create mode 100644 win/vncviewer/CConnOptions.cxx create mode 100644 win/vncviewer/CConnOptions.h create mode 100644 win/vncviewer/CConnThread.cxx create mode 100644 win/vncviewer/CConnThread.h create mode 100644 win/vncviewer/ConnectingDialog.cxx create mode 100644 win/vncviewer/ConnectingDialog.h create mode 100644 win/vncviewer/ConnectionDialog.cxx create mode 100644 win/vncviewer/ConnectionDialog.h create mode 100644 win/vncviewer/DesktopWindow.cxx create mode 100644 win/vncviewer/DesktopWindow.h create mode 100644 win/vncviewer/FTBrowseDlg.cxx create mode 100644 win/vncviewer/FTBrowseDlg.h create mode 100644 win/vncviewer/FTDialog.cxx create mode 100644 win/vncviewer/FTDialog.h create mode 100644 win/vncviewer/FTListView.cxx create mode 100644 win/vncviewer/FTListView.h create mode 100644 win/vncviewer/FTProgress.cxx create mode 100644 win/vncviewer/FTProgress.h create mode 100644 win/vncviewer/FileTransfer.cxx create mode 100644 win/vncviewer/FileTransfer.h create mode 100644 win/vncviewer/InfoDialog.cxx create mode 100644 win/vncviewer/InfoDialog.h create mode 100644 win/vncviewer/ListenServer.h create mode 100644 win/vncviewer/ListenTrayIcon.h create mode 100644 win/vncviewer/MRU.h create mode 100644 win/vncviewer/OptionsDialog.cxx create mode 100644 win/vncviewer/OptionsDialog.h create mode 100644 win/vncviewer/UserPasswdDialog.cxx create mode 100644 win/vncviewer/UserPasswdDialog.h create mode 100644 win/vncviewer/ViewerToolBar.cxx create mode 100644 win/vncviewer/ViewerToolBar.h create mode 100644 win/vncviewer/buildTime.cxx create mode 100644 win/vncviewer/cursor1.cur create mode 100644 win/vncviewer/ftdir.ico create mode 100644 win/vncviewer/ftfile.ico create mode 100644 win/vncviewer/ftreload.ico create mode 100644 win/vncviewer/ftup.ico create mode 100644 win/vncviewer/resource.h create mode 100644 win/vncviewer/toolbar.bmp create mode 100644 win/vncviewer/vncviewer.bmp create mode 100644 win/vncviewer/vncviewer.cxx create mode 100644 win/vncviewer/vncviewer.dsp create mode 100644 win/vncviewer/vncviewer.exe.manifest create mode 100644 win/vncviewer/vncviewer.ico create mode 100644 win/vncviewer/vncviewer.rc create mode 100644 win/winvnc/AddNewClientDialog.h create mode 100644 win/winvnc/ControlPanel.cxx create mode 100644 win/winvnc/ControlPanel.h create mode 100644 win/winvnc/JavaViewer.cxx create mode 100644 win/winvnc/JavaViewer.h create mode 100644 win/winvnc/ManagedListener.cxx create mode 100644 win/winvnc/ManagedListener.h create mode 100644 win/winvnc/QueryConnectDialog.cxx create mode 100644 win/winvnc/QueryConnectDialog.h create mode 100644 win/winvnc/STrayIcon.cxx create mode 100644 win/winvnc/STrayIcon.h create mode 100644 win/winvnc/VNCServerService.cxx create mode 100644 win/winvnc/VNCServerService.h create mode 100644 win/winvnc/VNCServerWin32.cxx create mode 100644 win/winvnc/VNCServerWin32.h create mode 100644 win/winvnc/buildTime.cxx create mode 100644 win/winvnc/connecte.ico create mode 100644 win/winvnc/connected.ico create mode 100644 win/winvnc/icon_dis.ico create mode 100644 win/winvnc/resource.h create mode 100644 win/winvnc/winvnc.bmp create mode 100644 win/winvnc/winvnc.cxx create mode 100644 win/winvnc/winvnc.dsp create mode 100644 win/winvnc/winvnc.ico create mode 100644 win/winvnc/winvnc.rc create mode 100644 win/winvnc/winvnc4.exe.manifest create mode 100644 win/wm_hooks/resource.h create mode 100644 win/wm_hooks/wm_hooks.cxx create mode 100644 win/wm_hooks/wm_hooks.def create mode 100644 win/wm_hooks/wm_hooks.dsp create mode 100644 win/wm_hooks/wm_hooks.h create mode 100644 win/wm_hooks/wm_hooks.rc diff --git a/win/README.txt b/win/README.txt new file mode 100644 index 00000000..a834b21e --- /dev/null +++ b/win/README.txt @@ -0,0 +1,128 @@ + +TightVNC Source Distribution for Windows platforms +================================================== + +Copyright (C) 2002-2004 RealVNC Ltd. All Rights Reserved. +Copyright (C) 2000-2004 Constantin Kaplinsky. +Copyright (C) 2004 Peter Astrand, Cendio AB + +This software is distributed under the GNU General Public Licence as +published by the Free Software Foundation. See the file LICENCE.TXT +for the conditions under which this software is made available. +TightVNC also contains code from other sources. See the +Acknowledgements section below, and the individual files for details +of the conditions under which they are made available. + +The source tree contains a number of directories, and is most easily +built by loading the VNC workspace file (vnc.dsw) into Microsoft +Visual Studio 6/7. This will preserve the required dependencies +between the sub-projects. + +There are three main executable projects: + + vncviewer - The VNC Viewer for Win32. + + winvnc - The VNC Server for Win32 (command-line operation + only). + + vncconfig - The configuration applet and GUI front-end for VNC + Server. + +These projects are designed to be built using Microsoft Visual C++ +6.0, and should also compile with Microsoft Visual Studio .NET +(version 7). Other compilers have not been tested but the code base +is extremely portable. + + +ACKNOWLEDGEMENTS +================ + +This distribution contains zlib software by Jean-loup Gailly and Mark Adler. +This is: + + Copyright (C) 1995-2002 Jean-loup Gailly and Mark Adler. + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. + + Jean-loup Gailly Mark Adler + jloup@gzip.org madler@alumni.caltech.edu + + + The data format used by the zlib library is described by RFCs (Request for + Comments) 1950 to 1952 in the files ftp://ds.internic.net/rfc/rfc1950.txt + (zlib format), rfc1951.txt (deflate format) and rfc1952.txt (gzip format). + + +This distribution contains public domain DES software by Richard Outerbridge. +This is: + + Copyright (c) 1988,1989,1990,1991,1992 by Richard Outerbridge. + (GEnie : OUTER; CIS : [71755,204]) Graven Imagery, 1992. + + +This distribution contains Java DES software by Dave Zimmerman + and Jef Poskanzer . This is: + + Copyright (c) 1996 Widget Workshop, Inc. All Rights Reserved. + + Permission to use, copy, modify, and distribute this software and its + documentation for NON-COMMERCIAL or COMMERCIAL purposes and without fee + is hereby granted, provided that this copyright notice is kept intact. + + WIDGET WORKSHOP MAKES NO REPRESENTATIONS OR WARRANTIES ABOUT THE + SUITABILITY OF THE SOFTWARE, EITHER EXPRESS OR IMPLIED, INCLUDING BUT + NOT LIMITED TO THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A + PARTICULAR PURPOSE, OR NON-INFRINGEMENT. WIDGET WORKSHOP SHALL NOT BE + LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, + MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. + + THIS SOFTWARE IS NOT DESIGNED OR INTENDED FOR USE OR RESALE AS ON-LINE + CONTROL EQUIPMENT IN HAZARDOUS ENVIRONMENTS REQUIRING FAIL-SAFE + PERFORMANCE, SUCH AS IN THE OPERATION OF NUCLEAR FACILITIES, AIRCRAFT + NAVIGATION OR COMMUNICATION SYSTEMS, AIR TRAFFIC CONTROL, DIRECT LIFE + SUPPORT MACHINES, OR WEAPONS SYSTEMS, IN WHICH THE FAILURE OF THE + SOFTWARE COULD LEAD DIRECTLY TO DEATH, PERSONAL INJURY, OR SEVERE + PHYSICAL OR ENVIRONMENTAL DAMAGE ("HIGH RISK ACTIVITIES"). WIDGET + WORKSHOP SPECIFICALLY DISCLAIMS ANY EXPRESS OR IMPLIED WARRANTY OF + FITNESS FOR HIGH RISK ACTIVITIES. + + Copyright (C) 1996 by Jef Poskanzer . All rights + reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS + BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + Visit the ACME Labs Java page for up-to-date versions of this and other + fine Java utilities: http://www.acme.com/java/ diff --git a/win/README_BINARY.txt b/win/README_BINARY.txt new file mode 100644 index 00000000..4c77e027 --- /dev/null +++ b/win/README_BINARY.txt @@ -0,0 +1,126 @@ + +TightVNC Binary Distribution for Windows platforms +================================================== + +Copyright (C) 2002-2004 RealVNC Ltd. All Rights Reserved. +Copyright (C) 2000-2004 Constantin Kaplinsky. +Copyright (C) 2004-2005 Peter Astrand, Cendio AB + +This software is distributed under the GNU General Public Licence as +published by the Free Software Foundation. See the file LICENCE.TXT +for the conditions under which this software is made available. +TightVNC also contains code from other sources. See the +Acknowledgements section below, and the individual files for details +of the conditions under which they are made available. + +The installer package contains two VNC components: + + VNC Viewer - this is the VNC Viewer, or client, program for + Win32. + [Win9x, WinME, NT4, Win2000, WinXP, + Windows 2003 Server] + + VNC Server - this is the VNC Server for Win32. It allows a + Windows desktop to be accessed remotely using a + VNC Viewer. + [Win9x, WinME, NT4, Win2000, WinXP(*), + Windows 2003 Server] + +(*) May not work if the in-built Fast User Switching or Remote + Administration features are in use. + +Both components were built using Microsoft Visual C++ 6.0, and are +designed to operate upon the Win32 platforms listed above. + +ACKNOWLEDGEMENTS +================ + +This distribution contains zlib software by Jean-loup Gailly and Mark Adler. +This is: + + Copyright (C) 1995-2002 Jean-loup Gailly and Mark Adler. + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. + + Jean-loup Gailly Mark Adler + jloup@gzip.org madler@alumni.caltech.edu + + + The data format used by the zlib library is described by RFCs (Request for + Comments) 1950 to 1952 in the files ftp://ds.internic.net/rfc/rfc1950.txt + (zlib format), rfc1951.txt (deflate format) and rfc1952.txt (gzip format). + + +This distribution contains public domain DES software by Richard Outerbridge. +This is: + + Copyright (c) 1988,1989,1990,1991,1992 by Richard Outerbridge. + (GEnie : OUTER; CIS : [71755,204]) Graven Imagery, 1992. + + +This distribution contains Java DES software by Dave Zimmerman + and Jef Poskanzer . This is: + + Copyright (c) 1996 Widget Workshop, Inc. All Rights Reserved. + + Permission to use, copy, modify, and distribute this software and its + documentation for NON-COMMERCIAL or COMMERCIAL purposes and without fee + is hereby granted, provided that this copyright notice is kept intact. + + WIDGET WORKSHOP MAKES NO REPRESENTATIONS OR WARRANTIES ABOUT THE + SUITABILITY OF THE SOFTWARE, EITHER EXPRESS OR IMPLIED, INCLUDING BUT + NOT LIMITED TO THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A + PARTICULAR PURPOSE, OR NON-INFRINGEMENT. WIDGET WORKSHOP SHALL NOT BE + LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, + MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. + + THIS SOFTWARE IS NOT DESIGNED OR INTENDED FOR USE OR RESALE AS ON-LINE + CONTROL EQUIPMENT IN HAZARDOUS ENVIRONMENTS REQUIRING FAIL-SAFE + PERFORMANCE, SUCH AS IN THE OPERATION OF NUCLEAR FACILITIES, AIRCRAFT + NAVIGATION OR COMMUNICATION SYSTEMS, AIR TRAFFIC CONTROL, DIRECT LIFE + SUPPORT MACHINES, OR WEAPONS SYSTEMS, IN WHICH THE FAILURE OF THE + SOFTWARE COULD LEAD DIRECTLY TO DEATH, PERSONAL INJURY, OR SEVERE + PHYSICAL OR ENVIRONMENTAL DAMAGE ("HIGH RISK ACTIVITIES"). WIDGET + WORKSHOP SPECIFICALLY DISCLAIMS ANY EXPRESS OR IMPLIED WARRANTY OF + FITNESS FOR HIGH RISK ACTIVITIES. + + Copyright (C) 1996 by Jef Poskanzer . All rights + reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS + BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + Visit the ACME Labs Java page for up-to-date versions of this and other + fine Java utilities: http://www.acme.com/java/ diff --git a/win/logmessages/logmessages.dsp b/win/logmessages/logmessages.dsp new file mode 100644 index 00000000..06ed8c4f --- /dev/null +++ b/win/logmessages/logmessages.dsp @@ -0,0 +1,202 @@ +# Microsoft Developer Studio Project File - Name="logmessages" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Dynamic-Link Library" 0x0102 + +CFG=logmessages - Win32 Debug Unicode +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "logmessages.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "logmessages.mak" CFG="logmessages - Win32 Debug Unicode" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "logmessages - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library") +!MESSAGE "logmessages - Win32 Debug" (based on "Win32 (x86) Dynamic-Link Library") +!MESSAGE "logmessages - Win32 Debug Unicode" (based on "Win32 (x86) Dynamic-Link Library") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +CPP=cl.exe +MTL=midl.exe +RSC=rc.exe + +!IF "$(CFG)" == "logmessages - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "..\Release" +# PROP Intermediate_Dir "..\Release\logmessages" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /MT /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "LOGMESSAGES_EXPORTS" /YX /FD /c +# ADD CPP /nologo /MT /W3 /GX /O2 /D "NDEBUG" /D "_WINDOWS" /D "_USRDLL" /D "LOGMESSAGES_EXPORTS" /D "WIN32" /D "_MBCS" /YX /FD /c +# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32 +# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32 +# ADD BASE RSC /l 0x809 /d "NDEBUG" +# ADD RSC /l 0x809 /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /machine:I386 +# ADD LINK32 /nologo /dll /machine:I386 /out:"messages.mc" +# Begin Custom Build +InputPath=.\messages.mc +SOURCE="$(InputPath)" + +BuildCmds= \ + mc messages.mc \ + rc -r -fo messages.res messages.rc \ + link -dll -noentry -out:..\Release\logmessages.dll messages.res \ + + +"messages.res" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" + $(BuildCmds) + +"messages.rc" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" + $(BuildCmds) + +"messages.h" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" + $(BuildCmds) + +"..\Release\logmessages.dll" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" + $(BuildCmds) +# End Custom Build + +!ELSEIF "$(CFG)" == "logmessages - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Debug" +# PROP BASE Intermediate_Dir "Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "../Debug" +# PROP Intermediate_Dir "..\Debug\logmessages" +# PROP Ignore_Export_Lib 1 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "LOGMESSAGES_EXPORTS" /YX /FD /GZ /c +# ADD CPP /nologo /MTd /W3 /Gm /GX /Zi /Od /D "_DEBUG" /D "_WINDOWS" /D "_USRDLL" /D "LOGMESSAGES_EXPORTS" /D "WIN32" /D "_MBCS" /YX /FD /GZ /c +# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /win32 +# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32 +# ADD BASE RSC /l 0x809 /d "_DEBUG" +# ADD RSC /l 0x809 /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /debug /machine:I386 /pdbtype:sept +# ADD LINK32 /nologo /dll /debug /machine:I386 /nodefaultlib /out:"messages.mc" /pdbtype:sept +# Begin Custom Build +InputPath=.\messages.mc +SOURCE="$(InputPath)" + +BuildCmds= \ + mc messages.mc \ + rc -r -fo messages.res messages.rc \ + link -dll -noentry -out:..\Debug\logmessages.dll messages.res \ + + +"messages.res" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" + $(BuildCmds) + +"messages.rc" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" + $(BuildCmds) + +"messages.h" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" + $(BuildCmds) + +"..\Debug\logmessages.dll" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" + $(BuildCmds) +# End Custom Build + +!ELSEIF "$(CFG)" == "logmessages - Win32 Debug Unicode" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "logmessages___Win32_Debug_Unicode" +# PROP BASE Intermediate_Dir "logmessages___Win32_Debug_Unicode" +# PROP BASE Ignore_Export_Lib 1 +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "..\Debug_Unicode" +# PROP Intermediate_Dir "..\Debug_Unicode\logmessages" +# PROP Ignore_Export_Lib 1 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /MTd /W3 /Gm /GX /Zi /Od /D "_DEBUG" /D "_WINDOWS" /D "_USRDLL" /D "LOGMESSAGES_EXPORTS" /D "WIN32" /D "_MBCS" /YX /FD /GZ /c +# ADD CPP /nologo /MTd /W3 /Gm /GX /Zi /Od /D "_WINDOWS" /D "_USRDLL" /D "LOGMESSAGES_EXPORTS" /D "_DEBUG" /D "WIN32" /D "_UNICODE" /D "UNICODE" /YX /FD /GZ /c +# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /win32 +# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32 +# ADD BASE RSC /l 0x809 /d "_DEBUG" +# ADD RSC /l 0x809 /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 /nologo /dll /debug /machine:I386 /nodefaultlib /out:"messages.mc" /pdbtype:sept +# ADD LINK32 /nologo /dll /debug /machine:I386 /nodefaultlib /out:"messages.mc" /pdbtype:sept +# Begin Custom Build +InputPath=.\messages.mc +SOURCE="$(InputPath)" + +BuildCmds= \ + mc messages.mc \ + rc -r -fo messages.res messages.rc \ + link -dll -noentry -out:..\Debug_Unicode\logmessages.dll messages.res \ + + +"messages.res" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" + $(BuildCmds) + +"messages.rc" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" + $(BuildCmds) + +"messages.h" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" + $(BuildCmds) + +"..\Debug_Unicode\logmessages.dll" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" + $(BuildCmds) +# End Custom Build + +!ENDIF + +# Begin Target + +# Name "logmessages - Win32 Release" +# Name "logmessages - Win32 Debug" +# Name "logmessages - Win32 Debug Unicode" +# Begin Group "Source Files" + +# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" +# End Group +# Begin Group "Header Files" + +# PROP Default_Filter "h;hpp;hxx;hm;inl" +# End Group +# Begin Group "Resource Files" + +# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe" +# End Group +# Begin Source File + +SOURCE=.\messages.mc +# End Source File +# End Target +# End Project diff --git a/win/logmessages/messages.h b/win/logmessages/messages.h new file mode 100644 index 00000000..bfb8c56d --- /dev/null +++ b/win/logmessages/messages.h @@ -0,0 +1,47 @@ +// +// Values are 32 bit values layed out as follows: +// +// 3 3 2 2 2 2 2 2 2 2 2 2 1 1 1 1 1 1 1 1 1 1 +// 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 +// +---+-+-+-----------------------+-------------------------------+ +// |Sev|C|R| Facility | Code | +// +---+-+-+-----------------------+-------------------------------+ +// +// where +// +// Sev - is the severity code +// +// 00 - Success +// 01 - Informational +// 10 - Warning +// 11 - Error +// +// C - is the Customer code flag +// +// R - is a reserved bit +// +// Facility - is the facility code +// +// Code - is the facility's status code +// +// +// Define the facility codes +// + + +// +// Define the severity codes +// + + +// +// MessageId: VNC4LogMessage +// +// MessageText: +// +// %1: %2 +// +// +// +#define VNC4LogMessage 0x00000001L + diff --git a/win/logmessages/messages.mc b/win/logmessages/messages.mc new file mode 100644 index 00000000..0bc8329e --- /dev/null +++ b/win/logmessages/messages.mc @@ -0,0 +1,7 @@ +MessageId=0x1 +Severity=Success +SymbolicName=VNC4LogMessage +Language=English +%1: %2 + + diff --git a/win/logmessages/messages.rc b/win/logmessages/messages.rc new file mode 100644 index 00000000..0885a897 --- /dev/null +++ b/win/logmessages/messages.rc @@ -0,0 +1,2 @@ +LANGUAGE 0x9,0x1 +1 11 MSG00001.bin diff --git a/win/rfb_win32/AboutDialog.cxx b/win/rfb_win32/AboutDialog.cxx new file mode 100644 index 00000000..030be1b3 --- /dev/null +++ b/win/rfb_win32/AboutDialog.cxx @@ -0,0 +1,49 @@ +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * + * This 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 software 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 software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +#include +#include +#include +#include + +using namespace rfb; +using namespace rfb::win32; + +static LogWriter vlog("AboutDialog"); + +AboutDialog AboutDialog::instance; + + +AboutDialog::AboutDialog() : Dialog(GetModuleHandle(0)) { +} + +bool AboutDialog::showDialog() { + return Dialog::showDialog(MAKEINTRESOURCE(DialogId)); +} + +void AboutDialog::initDialog() { + // Set the build time field + SetWindowText(GetDlgItem(handle, BuildTime), TStr(buildTime)); + + // Get our executable's version info + FileVersionInfo verInfo; + + SetWindowText(GetDlgItem(handle, Version), verInfo.getVerString(_T("ProductVersion"))); + SetWindowText(GetDlgItem(handle, Copyright), verInfo.getVerString(_T("LegalCopyright"))); + SetWindowText(GetDlgItem(handle, Description), verInfo.getVerString(_T("ProductName"))); +} diff --git a/win/rfb_win32/AboutDialog.h b/win/rfb_win32/AboutDialog.h new file mode 100644 index 00000000..0dd9d494 --- /dev/null +++ b/win/rfb_win32/AboutDialog.h @@ -0,0 +1,55 @@ +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * + * This 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 software 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 software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +// -=- AboutDialog.h + +#ifndef __RFB_WIN32_ABOUT_DIALOG_H__ +#define __RFB_WIN32_ABOUT_DIALOG_H__ + +#include +#include + +extern const char* buildTime; + +namespace rfb { + + namespace win32 { + + class AboutDialog : Dialog { + public: + AboutDialog(); + virtual bool showDialog(); + virtual void initDialog(); + + static AboutDialog instance; + + typedef WORD LabelId; + static const LabelId DialogId; // Resource ID of the About dialog + static const LabelId BuildTime; // Resource ID of the BuildTime label in the dialog + static const LabelId Version; // etc... + static const LabelId Copyright; + static const LabelId Description; + protected: + WORD dialogId; + }; + + }; + +}; + +#endif diff --git a/win/rfb_win32/BitmapInfo.h b/win/rfb_win32/BitmapInfo.h new file mode 100644 index 00000000..6a6f0d24 --- /dev/null +++ b/win/rfb_win32/BitmapInfo.h @@ -0,0 +1,48 @@ +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * + * This 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 software 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 software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +#ifndef __RFB_WIN32_BITMAP_INFO_H__ +#define __RFB_WIN32_BITMAP_INFO_H__ + +#include +#include + +namespace rfb { + namespace win32 { + + struct BitmapInfo { + BITMAPINFOHEADER bmiHeader; + union { + struct { + DWORD red; + DWORD green; + DWORD blue; + } mask; + RGBQUAD color[256]; + }; + }; + + inline void initMaxAndShift(DWORD mask, int* max, int* shift) { + for ((*shift) = 0; (mask & 1) == 0; (*shift)++) mask >>= 1; + (*max) = (rdr::U16)mask; + } + + }; +}; + +#endif diff --git a/win/rfb_win32/CKeyboard.cxx b/win/rfb_win32/CKeyboard.cxx new file mode 100644 index 00000000..28aceab7 --- /dev/null +++ b/win/rfb_win32/CKeyboard.cxx @@ -0,0 +1,258 @@ +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * + * This 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 software 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 software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +#include + +#define XK_MISCELLANY +#define XK_LATIN1 +#define XK_CURRENCY +#include + +#include +#include +#include +#include "keymap.h" + +using namespace rfb; + +static LogWriter vlog("CKeyboard"); + + +// Client-side RFB keyboard event sythesis + +class CKeymapper { + +public: + CKeymapper() + { + for (int i = 0; i < sizeof(keymap) / sizeof(keymap_t); i++) { + int extendedVkey = keymap[i].vk + (keymap[i].extended ? 256 : 0); + if (keysymMap.find(extendedVkey) == keysymMap.end()) { + keysymMap[extendedVkey] = keymap[i].keysym; + } + } + } + + // lookup() tries to find a match for vkey with the extended flag. We check + // first for an exact match including the extended flag, then try without the + // extended flag. + rdr::U32 lookup(int extendedVkey) { + if (keysymMap.find(extendedVkey) != keysymMap.end()) + return keysymMap[extendedVkey]; + if (keysymMap.find(extendedVkey ^ 256) != keysymMap.end()) + return keysymMap[extendedVkey ^ 256]; + return 0; + } + +private: + std::map keysymMap; +} ckeymapper; + + +class ModifierKeyReleaser { +public: + ModifierKeyReleaser(InputHandler* writer_, int vkCode, bool extended) + : writer(writer_), extendedVkey(vkCode + (extended ? 256 : 0)), + keysym(0) + {} + void release(std::map* downKeysym) { + if (downKeysym->find(extendedVkey) != downKeysym->end()) { + keysym = (*downKeysym)[extendedVkey]; + vlog.debug("fake release extendedVkey 0x%x, keysym 0x%x", + extendedVkey, keysym); + writer->keyEvent(keysym, false); + } + } + ~ModifierKeyReleaser() { + if (keysym) { + vlog.debug("fake press extendedVkey 0x%x, keysym 0x%x", + extendedVkey, keysym); + writer->keyEvent(keysym, true); + } + } + InputHandler* writer; + int extendedVkey; + rdr::U32 keysym; +}; + +// IS_PRINTABLE_LATIN1 tests if a character is either a printable latin1 +// character, or 128, which is the Euro symbol on Windows. +#define IS_PRINTABLE_LATIN1(c) (((c) >= 32 && (c) <= 126) || (c) == 128 || \ + ((c) >= 160 && (c) <= 255)) + +void win32::CKeyboard::keyEvent(InputHandler* writer, rdr::U8 vkey, + rdr::U32 flags, bool down) +{ + bool extended = (flags & 0x1000000); + int extendedVkey = vkey + (extended ? 256 : 0); + + // If it's a release, just release whichever keysym corresponded to the same + // key being pressed, regardless of how it would be interpreted in the + // current keyboard state. + if (!down) { + releaseKey(writer, extendedVkey); + return; + } + + // We should always pass every down event to ToAscii() otherwise it can get + // out of sync. + + // XXX should we pass CapsLock, ScrollLock or NumLock to ToAscii - they + // actually alter the lock state on the keyboard? + + BYTE keystate[256]; + GetKeyboardState(keystate); + rdr::U8 chars[2]; + + int nchars = ToAscii(vkey, 0, keystate, (WORD*)&chars, 0); + + // See if it's in the Windows VK code -> X keysym map. We do this before + // looking at the result of ToAscii so that e.g. we recognise that it's + // XK_KP_Add rather than '+'. + + rdr::U32 keysym = ckeymapper.lookup(extendedVkey); + if (keysym) { + vlog.debug("mapped key: extendedVkey 0x%x", extendedVkey); + pressKey(writer, extendedVkey, keysym); + return; + } + + if (nchars < 0) { + // Dead key - the next call to ToAscii() will give us either the accented + // character or two characters. + vlog.debug("ToAscii dead key (1): extendedVkey 0x%x", extendedVkey); + return; + } + + if (nchars > 0 && IS_PRINTABLE_LATIN1(chars[0])) { + // Got a printable latin1 character. We must release Control and Alt + // (AltGr) if they were both pressed, so that the latin1 character is seen + // without them by the VNC server. + ModifierKeyReleaser lctrl(writer, VK_CONTROL, 0); + ModifierKeyReleaser rctrl(writer, VK_CONTROL, 1); + ModifierKeyReleaser lalt(writer, VK_MENU, 0); + ModifierKeyReleaser ralt(writer, VK_MENU, 1); + + if ((keystate[VK_CONTROL] & 0x80) && (keystate[VK_MENU] & 0x80)) { + lctrl.release(&downKeysym); + rctrl.release(&downKeysym); + lalt.release(&downKeysym); + ralt.release(&downKeysym); + } + + for (int i = 0; i < nchars; i++) { + vlog.debug("ToAscii key (1): extendedVkey 0x%x", extendedVkey); + if (chars[i] == 128) { // special hack for euro! + pressKey(writer, extendedVkey, XK_EuroSign); + } else { + pressKey(writer, extendedVkey, chars[i]); + } + } + return; + } + + // Either no chars were generated, or something outside the printable + // character range. Try ToAscii() without the Control and Alt keys down to + // see if that yields an ordinary character. + + keystate[VK_CONTROL] = keystate[VK_LCONTROL] = keystate[VK_RCONTROL] = 0; + keystate[VK_MENU] = keystate[VK_LMENU] = keystate[VK_RMENU] = 0; + + nchars = ToAscii(vkey, 0, keystate, (WORD*)&chars, 0); + + if (nchars < 0) { + // So it would be a dead key if neither control nor alt were pressed. + // However, we know that at least one of control and alt must be pressed. + // We can't leave it at this stage otherwise the next call to ToAscii() + // with a valid character will get wrongly interpreted in the context of + // this bogus dead key. Working on the assumption that a dead key followed + // by space usually returns the dead character itself, try calling ToAscii + // with VK_SPACE. + vlog.debug("ToAscii dead key (2): extendedVkey 0x%x", extendedVkey); + nchars = ToAscii(VK_SPACE, 0, keystate, (WORD*)&chars, 0); + if (nchars < 0) { + vlog.debug("ToAscii dead key (3): extendedVkey 0x%x - giving up!", + extendedVkey); + return; + } + } + + if (nchars > 0 && IS_PRINTABLE_LATIN1(chars[0])) { + for (int i = 0; i < nchars; i++) { + vlog.debug("ToAscii key (2) (no ctrl/alt): extendedVkey 0x%x", + extendedVkey); + if (chars[i] == 128) { // special hack for euro! + pressKey(writer, extendedVkey, XK_EuroSign); + } else { + pressKey(writer, extendedVkey, chars[i]); + } + } + return; + } + + vlog.debug("no chars regardless of control and alt: extendedVkey 0x%x", + extendedVkey); +} + +// releaseAllKeys() - write key release events to the server for all keys +// that are currently regarded as being down. +void win32::CKeyboard::releaseAllKeys(InputHandler* writer) { + std::map::iterator i, next_i; + for (i=downKeysym.begin(); i!=downKeysym.end(); i=next_i) { + next_i = i; next_i++; + writer->keyEvent((*i).second, false); + downKeysym.erase(i); + } +} + +// releaseKey() - write a key up event to the server, but only if we've +// actually sent a key down event for the given key. The key up event always +// contains the same keysym we used in the key down event, regardless of what +// it would look up as using the current keyboard state. +void win32::CKeyboard::releaseKey(InputHandler* writer, int extendedVkey) +{ + if (downKeysym.find(extendedVkey) != downKeysym.end()) { + vlog.debug("release extendedVkey 0x%x, keysym 0x%x", + extendedVkey, downKeysym[extendedVkey]); + writer->keyEvent(downKeysym[extendedVkey], false); + downKeysym.erase(extendedVkey); + } +} + +// pressKey() - write a key down event to the server, and record which keysym +// was sent as corresponding to the given extendedVkey. The only tricky bit is +// that if we are trying to press an extendedVkey which is already marked as +// down but with a different keysym, then we need to release the old keysym +// first. This can happen in two cases: (a) when a single key press results in +// more than one character, and (b) when shift is released while another key is +// autorepeating. +void win32::CKeyboard::pressKey(InputHandler* writer, int extendedVkey, + rdr::U32 keysym) +{ + if (downKeysym.find(extendedVkey) != downKeysym.end()) { + if (downKeysym[extendedVkey] != keysym) { + vlog.debug("release extendedVkey 0x%x, keysym 0x%x", + extendedVkey, downKeysym[extendedVkey]); + writer->keyEvent(downKeysym[extendedVkey], false); + } + } + vlog.debug("press extendedVkey 0x%x, keysym 0x%x", + extendedVkey, keysym); + writer->keyEvent(keysym, true); + downKeysym[extendedVkey] = keysym; +} diff --git a/win/rfb_win32/CKeyboard.h b/win/rfb_win32/CKeyboard.h new file mode 100644 index 00000000..666ebce5 --- /dev/null +++ b/win/rfb_win32/CKeyboard.h @@ -0,0 +1,51 @@ +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * + * This 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 software 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 software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +// -=- CKeyboard.h +// +// Client-side keyboard handling for Win32 + +#ifndef __RFB_WIN32_CKEYBOARD_H__ +#define __RFB_WIN32_CKEYBOARD_H__ + +#include +#include + +namespace rfb { + + namespace win32 { + + class CKeyboard { + public: + void keyEvent(InputHandler* writer, rdr::U8 vkey, rdr::U32 flags, + bool down); + void releaseAllKeys(InputHandler* writer); + const std::map& pressedKeys() const {return downKeysym;}; + bool keyPressed(int k) const {return downKeysym.find(k)!=downKeysym.end();} + private: + void win32::CKeyboard::releaseKey(InputHandler* writer, int extendedVkey); + void win32::CKeyboard::pressKey(InputHandler* writer, int extendedVkey, + rdr::U32 keysym); + std::map downKeysym; + }; + + }; // win32 + +}; // rfb + +#endif // __RFB_WIN32_CKEYBOARD_H__ diff --git a/win/rfb_win32/CPointer.cxx b/win/rfb_win32/CPointer.cxx new file mode 100644 index 00000000..3d0d9342 --- /dev/null +++ b/win/rfb_win32/CPointer.cxx @@ -0,0 +1,186 @@ +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * + * This 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 software 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 software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +#include +#include +#include + +using namespace rfb; +using namespace win32; + +static LogWriter vlog("CPointer"); + + +CPointer::CPointer() : currButtonMask(0), intervalQueued(false), threeEmulating(false) { +} + +CPointer::~CPointer() { + intervalTimer.stop(); + threeTimer.stop(); +} + + +void CPointer::pointerEvent(InputHandler* writer, const Point& pos, int buttonMask) { + // + // - Duplicate Event Filtering + // + + bool maskChanged = buttonMask != currButtonMask; + bool posChanged = !pos.equals(currPos); + if (!(posChanged || maskChanged)) + return; + + // Pass on the event to the event-interval handler + threePointerEvent(writer, pos, buttonMask); + + // Save the position and mask + currPos = pos; + currButtonMask = buttonMask; +} + + +inline abs(int x) {return x>0 ? x : 0;} + +int emulate3Mask(int buttonMask) { + // - Release left & right and press middle + vlog.debug("emulate3: active"); + buttonMask &= ~5; + buttonMask |= 2; + return buttonMask; +} + +void CPointer::threePointerEvent(InputHandler* writer, const Point& pos, int buttonMask) { + // + // - 3-Button Mouse Emulation + // + + if (emulate3) { + + bool leftChanged = (buttonMask & 1) != (currButtonMask & 1); + bool rightChanged = (buttonMask & 4) != (currButtonMask & 4); + + if (leftChanged || rightChanged) { + // - One of left or right have changed + + if ((buttonMask & 5) == 1 || (buttonMask & 5) == 4) { + // - One is up, one is down. Start a timer, so that if it + // expires then we know we should actually send this event + vlog.debug("emulate3: start timer"); + threeTimer.start(100); + threePos = pos; + threeMask = buttonMask; + return; + + } else if (threeTimer.isActive()) { + // - Both are up or both are down, and we were timing for an emulation event + // Stop the timer and flush the stored event + vlog.debug("emulate3: stop timer (state)"); + threeTimer.stop(); + if (threeEmulating == ((buttonMask & 5) == 5)) + intervalPointerEvent(writer, threePos, threeMask); + else + threeEmulating = ((buttonMask & 5) == 5); + } + + } else { + + if (threeTimer.isActive()) { + // - We are timing for an emulation event + + if (abs(threePos.x - pos.x) <= 4 || abs(threePos.y - pos.y) <= 4) { + // If the mouse has moved too far since the button-change event then flush + vlog.debug("emulate3: stop timer (moved)"); + threeTimer.stop(); + intervalPointerEvent(writer, threePos, threeMask); + + } else { + // Otherwise, we ignore the new event + return; + } + } + + } + + // - If neither left nor right are down, stop emulating + if ((buttonMask & 5) == 0) + threeEmulating = false; + + // - If emulating, release left & right and press middle + if (threeEmulating) + buttonMask = emulate3Mask(buttonMask); + + } + + // - Let the event pass through to the next stage of processing + intervalPointerEvent(writer, pos, buttonMask); +} + +void CPointer::intervalPointerEvent(InputHandler* writer, const Point& pos, int buttonMask) { + // + // - Pointer Event Interval + // + vlog.write(101, "ptrEvent: %d,%d (%lx)", pos.x, pos.y, buttonMask); + + // Send the event immediately if we haven't sent one for a while + bool sendNow = !intervalTimer.isActive(); + + if (intervalMask != buttonMask) { + // If the buttons have changed then flush queued events and send now + sendNow = true; + if (intervalQueued) + writer->pointerEvent(intervalPos, intervalMask); + intervalQueued = false; + } + + if (!sendNow) { + // If we're not sending now then just queue the event + intervalQueued = true; + intervalPos = pos; + intervalMask = buttonMask; + } else { + // Start the interval timer if required, and send the event + intervalQueued = false; + intervalMask = buttonMask; + if (pointerEventInterval) + intervalTimer.start(pointerEventInterval); + writer->pointerEvent(pos, buttonMask); + } +} + +void CPointer::handleTimer(InputHandler* writer, int timerId) { + if (timerId == intervalTimer.getId()) { + // Pointer interval has expired - send any queued events + if (intervalQueued) { + writer->pointerEvent(intervalPos, intervalMask); + intervalQueued = false; + } else { + intervalTimer.stop(); + } + + } else if (timerId = threeTimer.getId()) { + // 3-Button emulation timer has expired - send what we've got + vlog.debug("emulate3: timeout"); + threeTimer.stop(); + + // If emulating, release left & right and press middle + if (threeEmulating) + threeMask = emulate3Mask(threeMask); + + intervalPointerEvent(writer, threePos, threeMask); + } +} diff --git a/win/rfb_win32/CPointer.h b/win/rfb_win32/CPointer.h new file mode 100644 index 00000000..b5916010 --- /dev/null +++ b/win/rfb_win32/CPointer.h @@ -0,0 +1,74 @@ +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * + * This 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 software 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 software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +// -=- CPointer.h +// +// Client-side pointer event handling for Win32 + +#ifndef __RFB_WIN32_CPOINTER_H__ +#define __RFB_WIN32_CPOINTER_H__ + +#include +#include +#include +#include +#include + +namespace rfb { + + namespace win32 { + + class CPointer { + public: + CPointer(); + ~CPointer(); + + void pointerEvent(InputHandler* writer, const Point& pos, int buttonMask); + void handleTimer(InputHandler* writer, int timerId); + + void setHWND(HWND w) {intervalTimer.setHWND(w); threeTimer.setHWND(w);} + void setIntervalTimerId(int id) {intervalTimer.setId(id);} + void set3ButtonTimerId(int id) {threeTimer.setId(id);} + + void enableEmulate3(bool enable) {emulate3 = enable;} + void enableInterval(int millis) {pointerEventInterval = millis;} + private: + Point currPos; + int currButtonMask; + + bool emulate3; + int pointerEventInterval; + + void intervalPointerEvent(InputHandler* writer, const Point& pos, int buttonMask); + IntervalTimer intervalTimer; + bool intervalQueued; + Point intervalPos; + int intervalMask; + + void threePointerEvent(InputHandler* writer, const Point& pos, int buttonMask); + IntervalTimer threeTimer; + Point threePos; + int threeMask; + bool threeEmulating; + }; + + }; // win32 + +}; // rfb + +#endif // __RFB_WIN32_CPOINTER_H__ diff --git a/win/rfb_win32/CleanDesktop.cxx b/win/rfb_win32/CleanDesktop.cxx new file mode 100644 index 00000000..39cca119 --- /dev/null +++ b/win/rfb_win32/CleanDesktop.cxx @@ -0,0 +1,321 @@ +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * + * This 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 software 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 software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +// -=- CleanDesktop.cxx + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef SPI_GETUIEFFECTS +#define RFB_HAVE_SPI_UIEFFECTS +#else +#pragma message(" NOTE: Not building Get/Set UI Effects support.") +#endif + +using namespace rfb; +using namespace rfb::win32; + +static LogWriter vlog("CleanDesktop"); + + +struct ActiveDesktop { + ActiveDesktop() : handle(0) { + // - Contact Active Desktop + HRESULT result = CoCreateInstance(CLSID_ActiveDesktop, NULL, CLSCTX_INPROC_SERVER, + IID_IActiveDesktop, (PVOID*)&handle); + if (result != S_OK) + throw rdr::SystemException("failed to contact Active Desktop", result); + } + ~ActiveDesktop() { + if (handle) + handle->Release(); + } + + // enableItem + // enables or disables the Nth Active Desktop item + bool enableItem(int i, bool enable_) { + COMPONENT item; + memset(&item, 0, sizeof(item)); + item.dwSize = sizeof(item); + + HRESULT hr = handle->GetDesktopItem(i, &item, 0); + if (hr != S_OK) { + vlog.error("unable to GetDesktopItem %d: %ld", i, hr); + return false; + } + item.fChecked = enable_; + vlog.debug("%sbling %d: \"%s\"", enable_ ? "ena" : "disa", i, (const char*)CStr(item.wszFriendlyName)); + + hr = handle->ModifyDesktopItem(&item, COMP_ELEM_CHECKED); + return hr == S_OK; + } + + // enable + // Attempts to enable/disable Active Desktop, returns true if the setting changed, + // false otherwise. + // If Active Desktop *can* be enabled/disabled then that is done. + // If Active Desktop is always on (XP/2K3) then instead the individual items are + // disabled, and true is returned to indicate that they need to be restored later. + bool enable(bool enable_) { + bool modifyComponents = false; + + vlog.debug("ActiveDesktop::enable"); + + // - Firstly, try to disable Active Desktop entirely + HRESULT hr; + COMPONENTSOPT adOptions; + memset(&adOptions, 0, sizeof(adOptions)); + adOptions.dwSize = sizeof(adOptions); + + // Attempt to actually disable/enable AD + hr = handle->GetDesktopItemOptions(&adOptions, 0); + if (hr == S_OK) { + // If Active Desktop is already in the desired state then return false (no change) + // NB: If AD is enabled AND restoreItems is set then we regard it as disabled... + if (((adOptions.fActiveDesktop==0) && restoreItems.empty()) == (enable_==false)) + return false; + adOptions.fActiveDesktop = enable_; + hr = handle->SetDesktopItemOptions(&adOptions, 0); + } + // Apply the change, then test whether it actually took effect + if (hr == S_OK) + hr = handle->ApplyChanges(AD_APPLY_REFRESH); + if (hr == S_OK) + hr = handle->GetDesktopItemOptions(&adOptions, 0); + if (hr == S_OK) + modifyComponents = (adOptions.fActiveDesktop==0) != (enable_==false); + if (hr != S_OK) { + vlog.error("failed to get/set Active Desktop options: %ld", hr); + return false; + } + + if (enable_) { + // - We are re-enabling Active Desktop. If there are components in restoreItems + // then restore them! + std::set::const_iterator i; + for (i=restoreItems.begin(); i!=restoreItems.end(); i++) { + enableItem(*i, true); + } + restoreItems.clear(); + } else if (modifyComponents) { + // - Disable all currently enabled items, and add the disabled ones to restoreItems + int itemCount = 0; + hr = handle->GetDesktopItemCount(&itemCount, 0); + if (hr != S_OK) { + vlog.error("failed to get desktop item count: %ld", hr); + return false; + } + for (unsigned int i=0; iApplyChanges(AD_APPLY_REFRESH); + return hr == S_OK; + } + IActiveDesktop* handle; + std::set restoreItems; +}; + + +DWORD SysParamsInfo(UINT action, UINT param, PVOID ptr, UINT ini) { + DWORD r = ERROR_SUCCESS; + if (!SystemParametersInfo(action, param, ptr, ini)) { + r = GetLastError(); + vlog.info("SPI error: %d", r); + } + return r; +} + + +CleanDesktop::CleanDesktop() : restoreActiveDesktop(false), restoreWallpaper(false), + restorePattern(false), restoreEffects(false) { + CoInitialize(0); +} + +CleanDesktop::~CleanDesktop() { + enableEffects(); + enablePattern(); + enableWallpaper(); + CoUninitialize(); +} + +void CleanDesktop::disableWallpaper() { + try { + ImpersonateCurrentUser icu; + + vlog.debug("disable desktop wallpaper/Active Desktop"); + + // -=- First attempt to remove the wallpaper using Active Desktop + try { + ActiveDesktop ad; + if (ad.enable(false)) + restoreActiveDesktop = true; + } catch (rdr::Exception& e) { + vlog.error(e.str()); + } + + // -=- Switch of normal wallpaper and notify apps + SysParamsInfo(SPI_SETDESKWALLPAPER, 0, "", SPIF_SENDCHANGE); + restoreWallpaper = true; + + } catch (rdr::Exception& e) { + vlog.info(e.str()); + } +} + +void CleanDesktop::enableWallpaper() { + try { + ImpersonateCurrentUser icu; + + if (restoreActiveDesktop) { + vlog.debug("restore Active Desktop"); + + // -=- First attempt to re-enable Active Desktop + try { + ActiveDesktop ad; + ad.enable(true); + restoreActiveDesktop = false; + } catch (rdr::Exception& e) { + vlog.error(e.str()); + } + } + + if (restoreWallpaper) { + vlog.debug("restore desktop wallpaper"); + + // -=- Then restore the standard wallpaper if required + SysParamsInfo(SPI_SETDESKWALLPAPER, 0, NULL, SPIF_SENDCHANGE); + restoreWallpaper = false; + } + + } catch (rdr::Exception& e) { + vlog.info(e.str()); + } +} + + +void CleanDesktop::disablePattern() { + try { + ImpersonateCurrentUser icu; + + vlog.debug("disable desktop pattern"); + SysParamsInfo(SPI_SETDESKPATTERN, 0, "", SPIF_SENDCHANGE); + restorePattern = true; + + } catch (rdr::Exception& e) { + vlog.info(e.str()); + } +} + +void CleanDesktop::enablePattern() { + try { + if (restorePattern) { + ImpersonateCurrentUser icu; + + vlog.debug("restoring pattern..."); + + TCharArray pattern; + if (osVersion.isPlatformWindows) { + RegKey cfgKey; + cfgKey.openKey(HKEY_CURRENT_USER, _T("Control Panel\\Desktop")); + pattern.buf = cfgKey.getString(_T("Pattern")); + } + SysParamsInfo(SPI_SETDESKPATTERN, 0, pattern.buf, SPIF_SENDCHANGE); + restorePattern = false; + } + + } catch (rdr::Exception& e) { + vlog.info(e.str()); + } +} + + +void CleanDesktop::disableEffects() { + try { + ImpersonateCurrentUser icu; + + vlog.debug("disable desktop effects"); + + SysParamsInfo(SPI_SETFONTSMOOTHING, FALSE, 0, SPIF_SENDCHANGE); +#ifdef RFB_HAVE_SPI_UIEFFECTS + if (SysParamsInfo(SPI_GETUIEFFECTS, 0, &uiEffects, 0) == ERROR_CALL_NOT_IMPLEMENTED) { + SysParamsInfo(SPI_GETCOMBOBOXANIMATION, 0, &comboBoxAnim, 0); + SysParamsInfo(SPI_GETGRADIENTCAPTIONS, 0, &gradientCaptions, 0); + SysParamsInfo(SPI_GETHOTTRACKING, 0, &hotTracking, 0); + SysParamsInfo(SPI_GETLISTBOXSMOOTHSCROLLING, 0, &listBoxSmoothScroll, 0); + SysParamsInfo(SPI_GETMENUANIMATION, 0, &menuAnim, 0); + SysParamsInfo(SPI_SETCOMBOBOXANIMATION, 0, FALSE, SPIF_SENDCHANGE); + SysParamsInfo(SPI_SETGRADIENTCAPTIONS, 0, FALSE, SPIF_SENDCHANGE); + SysParamsInfo(SPI_SETHOTTRACKING, 0, FALSE, SPIF_SENDCHANGE); + SysParamsInfo(SPI_SETLISTBOXSMOOTHSCROLLING, 0, FALSE, SPIF_SENDCHANGE); + SysParamsInfo(SPI_SETMENUANIMATION, 0, FALSE, SPIF_SENDCHANGE); + } else { + SysParamsInfo(SPI_SETUIEFFECTS, 0, FALSE, SPIF_SENDCHANGE); + + // We *always* restore UI effects overall, since there is no Windows GUI to do it + uiEffects = TRUE; + } +#else + vlog.debug(" not supported"); +#endif + restoreEffects = true; + + } catch (rdr::Exception& e) { + vlog.info(e.str()); + } +} + +void CleanDesktop::enableEffects() { + try { + if (restoreEffects) { + ImpersonateCurrentUser icu; + + vlog.debug("restore desktop effects"); + + RegKey desktopCfg; + desktopCfg.openKey(HKEY_CURRENT_USER, _T("Control Panel\\Desktop")); + SysParamsInfo(SPI_SETFONTSMOOTHING, desktopCfg.getInt(_T("FontSmoothing"), 0) != 0, 0, SPIF_SENDCHANGE); +#ifdef RFB_HAVE_SPI_UIEFFECTS + if (SysParamsInfo(SPI_SETUIEFFECTS, 0, (void*)uiEffects, SPIF_SENDCHANGE) == ERROR_CALL_NOT_IMPLEMENTED) { + SysParamsInfo(SPI_SETCOMBOBOXANIMATION, 0, (void*)comboBoxAnim, SPIF_SENDCHANGE); + SysParamsInfo(SPI_SETGRADIENTCAPTIONS, 0, (void*)gradientCaptions, SPIF_SENDCHANGE); + SysParamsInfo(SPI_SETHOTTRACKING, 0, (void*)hotTracking, SPIF_SENDCHANGE); + SysParamsInfo(SPI_SETLISTBOXSMOOTHSCROLLING, 0, (void*)listBoxSmoothScroll, SPIF_SENDCHANGE); + SysParamsInfo(SPI_SETMENUANIMATION, 0, (void*)menuAnim, SPIF_SENDCHANGE); + } + restoreEffects = false; +#else + vlog.info(" not supported"); +#endif + } + + } catch (rdr::Exception& e) { + vlog.info(e.str()); + } +} diff --git a/win/rfb_win32/CleanDesktop.h b/win/rfb_win32/CleanDesktop.h new file mode 100644 index 00000000..22e246fa --- /dev/null +++ b/win/rfb_win32/CleanDesktop.h @@ -0,0 +1,57 @@ +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * + * This 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 software 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 software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +// -=- CleanDesktop.h + +#ifndef __RFB_WIN32_CLEANDESKTOP_H__ +#define __RFB_WIN32_CLEANDESKTOP_H__ + +#include + +namespace rfb { + + namespace win32 { + + class CleanDesktop { + public: + CleanDesktop(); + ~CleanDesktop(); + + void disableWallpaper(); + void enableWallpaper(); + + void disablePattern(); + void enablePattern(); + + void disableEffects(); + void enableEffects(); + + private: + bool restoreActiveDesktop; + bool restoreWallpaper; + bool restorePattern; + bool restoreEffects; + BOOL uiEffects; + BOOL comboBoxAnim, gradientCaptions, hotTracking, listBoxSmoothScroll, menuAnim; + }; + + }; // win32 + +}; // rfb + +#endif // __RFB_WIN32_CLEANDESKTOP_H__ diff --git a/win/rfb_win32/Clipboard.cxx b/win/rfb_win32/Clipboard.cxx new file mode 100644 index 00000000..a4c43f04 --- /dev/null +++ b/win/rfb_win32/Clipboard.cxx @@ -0,0 +1,200 @@ +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * + * This 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 software 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 software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +// -=- Clipboard.cxx + +#include +#include +#include + +#include + +using namespace rfb; +using namespace rfb::win32; + +static LogWriter vlog("Clipboard"); + + +// +// -=- CR/LF handlers +// + +char* +dos2unix(const char* text) { + int len = strlen(text)+1; + char* unix = new char[strlen(text)+1]; + int i, j=0; + for (i=0; i= 1) && (text[i] <= 127)) || + ((text[i] >= 160) && (text[i] <= 255))) + text[j++] = text[i]; + } + text[j] = 0; +} + +// +// -=- Clipboard object +// + +Clipboard::Clipboard() + : MsgWindow(_T("Clipboard")), notifier(0), next_window(0) { + next_window = SetClipboardViewer(getHandle()); + vlog.debug("registered clipboard handler"); +} + +Clipboard::~Clipboard() { + vlog.debug("removing %x from chain (next is %x)", getHandle(), next_window); + ChangeClipboardChain(getHandle(), next_window); +} + +LRESULT +Clipboard::processMessage(UINT msg, WPARAM wParam, LPARAM lParam) { + switch (msg) { + + case WM_CHANGECBCHAIN: + vlog.debug("change clipboard chain (%x, %x)", wParam, lParam); + if ((HWND) wParam == next_window) + next_window = (HWND) lParam; + else if (next_window != 0) + SendMessage(next_window, msg, wParam, lParam); + else + vlog.error("bad clipboard chain change!"); + break; + + case WM_DRAWCLIPBOARD: + { + HWND owner = GetClipboardOwner(); + if (owner == getHandle()) { + vlog.debug("local clipboard changed by me"); + } else { + vlog.debug("local clipboard changed by %x", owner); + + // Open the clipboard + if (OpenClipboard(getHandle())) { + // Get the clipboard data + HGLOBAL cliphandle = GetClipboardData(CF_TEXT); + if (cliphandle) { + char* clipdata = (char*) GlobalLock(cliphandle); + + // Notify clients + if (notifier) { + if (!clipdata) { + notifier->notifyClipboardChanged(0, 0); + } else { + CharArray unix_text; + unix_text.buf = dos2unix(clipdata); + removeNonISOLatin1Chars(unix_text.buf); + notifier->notifyClipboardChanged(unix_text.buf, strlen(unix_text.buf)); + } + } else { + vlog.debug("no clipboard notifier registered"); + } + + // Release the buffer and close the clipboard + GlobalUnlock(cliphandle); + } + + CloseClipboard(); + } + } + } + if (next_window) + SendMessage(next_window, msg, wParam, lParam); + return 0; + + }; + return MsgWindow::processMessage(msg, wParam, lParam); +}; + +void +Clipboard::setClipText(const char* text) { + HANDLE clip_handle = 0; + + try { + + // - Firstly, we must open the clipboard + if (!OpenClipboard(getHandle())) + throw rdr::SystemException("unable to open Win32 clipboard", GetLastError()); + + // - Pre-process the supplied clipboard text into DOS format + CharArray dos_text; + dos_text.buf = unix2dos(text); + removeNonISOLatin1Chars(dos_text.buf); + int dos_text_len = strlen(dos_text.buf); + + // - Allocate global memory for the data + clip_handle = ::GlobalAlloc(GMEM_MOVEABLE, dos_text_len+1); + + char* data = (char*) GlobalLock(clip_handle); + memcpy(data, dos_text.buf, dos_text_len+1); + data[dos_text_len] = 0; + GlobalUnlock(clip_handle); + + // - Next, we must clear out any existing data + if (!EmptyClipboard()) + throw rdr::SystemException("unable to empty Win32 clipboard", GetLastError()); + + // - Set the new clipboard data + if (!SetClipboardData(CF_TEXT, clip_handle)) + throw rdr::SystemException("unable to set Win32 clipboard", GetLastError()); + clip_handle = 0; + + vlog.debug("set clipboard"); + } catch (rdr::Exception& e) { + vlog.debug(e.str()); + } + + // - Close the clipboard + if (!CloseClipboard()) + vlog.debug("unable to close Win32 clipboard: %u", GetLastError()); + else + vlog.debug("closed clipboard"); + if (clip_handle) { + vlog.debug("freeing clipboard handle"); + GlobalFree(clip_handle); + } +} diff --git a/win/rfb_win32/Clipboard.h b/win/rfb_win32/Clipboard.h new file mode 100644 index 00000000..b79768f6 --- /dev/null +++ b/win/rfb_win32/Clipboard.h @@ -0,0 +1,66 @@ +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * + * This 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 software 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 software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +// -=- Clipboard.h +// +// The Clipboard is used to set the system clipboard, and to get callbacks +// when the system clipboard has changed. + +#ifndef __RFB_WIN32_CLIPBOARD_H__ +#define __RFB_WIN32_CLIPBOARD_H__ + +#include +#include +#include +#include + +namespace rfb { + + namespace win32 { + + class Clipboard : MsgWindow { + public: + + // -=- Abstract base class for callback recipients + class Notifier { + public: + virtual void notifyClipboardChanged(const char* text, int len) = 0; + }; + + Clipboard(); + ~Clipboard(); + + // - Set the notifier to use + void setNotifier(Notifier* cbn) {notifier = cbn;} + + // - Set the clipboard contents + void setClipText(const char* text); + + protected: + // - Internal MsgWindow callback + virtual LRESULT processMessage(UINT msg, WPARAM wParam, LPARAM lParam); + + Notifier* notifier; + HWND next_window; + }; + + }; + +}; + +#endif // __RFB_WIN32_CLIPBOARD_H__ diff --git a/win/rfb_win32/CompatibleBitmap.h b/win/rfb_win32/CompatibleBitmap.h new file mode 100644 index 00000000..4beed8dc --- /dev/null +++ b/win/rfb_win32/CompatibleBitmap.h @@ -0,0 +1,46 @@ +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * + * This 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 software 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 software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +#ifndef __RFB_WIN32_COMPAT_BITMAP_H__ +#define __RFB_WIN32_COMPAT_BITMAP_H__ + +#include +#include + +namespace rfb { + namespace win32 { + + class CompatibleBitmap { + public: + CompatibleBitmap(HDC hdc, int width, int height) { + hbmp = CreateCompatibleBitmap(hdc, width, height); + if (!hbmp) + throw rdr::SystemException("CreateCompatibleBitmap() failed", GetLastError()); + } + virtual ~CompatibleBitmap() { + if (hbmp) DeleteObject(hbmp); + } + operator HBITMAP() const {return hbmp;} + protected: + HBITMAP hbmp; + }; + + }; +}; + +#endif diff --git a/win/rfb_win32/ComputerName.h b/win/rfb_win32/ComputerName.h new file mode 100644 index 00000000..110caa59 --- /dev/null +++ b/win/rfb_win32/ComputerName.h @@ -0,0 +1,40 @@ +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * + * This 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 software 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 software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +#ifndef __RFB_WIN32_COMPUTERNAME_H__ +#define __RFB_WIN32_COMPUTERNAME_H__ + +#include +#include + +namespace rfb { + namespace win32 { + + // Get the computer name + struct ComputerName : TCharArray { + ComputerName() : TCharArray(MAX_COMPUTERNAME_LENGTH+1) { + ULONG namelength = MAX_COMPUTERNAME_LENGTH+1; + if (!GetComputerName(buf, &namelength)) + _tcscpy(buf, _T("")); + } + }; + + }; +}; + +#endif diff --git a/win/rfb_win32/CurrentUser.cxx b/win/rfb_win32/CurrentUser.cxx new file mode 100644 index 00000000..7562d29b --- /dev/null +++ b/win/rfb_win32/CurrentUser.cxx @@ -0,0 +1,152 @@ +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * + * This 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 software 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 software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +// -=- Currentuser.cxx + +#include +#include +#include +#include +#include +#include +#include + +using namespace rfb; +using namespace win32; + +static LogWriter vlog("CurrentUser"); + + +const TCHAR* shellIconClass = _T("Shell_TrayWnd"); + +BOOL CALLBACK enumWindows(HWND hwnd, LPARAM lParam) { + TCHAR className[16]; + if (GetClassName(hwnd, className, sizeof(className)) && + (_tcscmp(className, shellIconClass) == 0)) { + vlog.debug("located tray icon window (%s)", (const char*)CStr(className)); + DWORD processId = 0; + GetWindowThreadProcessId(hwnd, &processId); + if (!processId) + return TRUE; + Handle process = OpenProcess(MAXIMUM_ALLOWED, FALSE, processId); + if (!process.h) + return TRUE; + if (!OpenProcessToken(process, MAXIMUM_ALLOWED, (HANDLE*)lParam)) + return TRUE; + vlog.debug("obtained user token"); + return FALSE; + } + return TRUE; +} + +BOOL CALLBACK enumDesktops(LPTSTR lpszDesktop, LPARAM lParam) { + HDESK desktop = OpenDesktop(lpszDesktop, 0, FALSE, DESKTOP_ENUMERATE); + vlog.debug("opening \"%s\"", lpszDesktop); + if (!desktop) { + vlog.info("desktop \"%s\" inaccessible", (const char*)CStr(lpszDesktop)); + return TRUE; + } + BOOL result = EnumDesktopWindows(desktop, enumWindows, lParam); + if (!CloseDesktop(desktop)) + vlog.info("unable to close desktop: %ld", GetLastError()); + return result; +} + + +CurrentUserToken::CurrentUserToken() : isSafe_(false) { + if (isServiceProcess()) { + // If the platform is Windows 95/98/Me then we must fake the token's presence + if (osVersion.isPlatformWindows) { + try { + UserName un; + h = INVALID_HANDLE_VALUE; + } catch (rdr::SystemException& e) { + if (e.err != ERROR_NOT_LOGGED_ON) + throw; + if (FindWindow(shellIconClass, 0)) + h = INVALID_HANDLE_VALUE; + } + isSafe_ = (h != 0); + return; + } + + // Try to get the user token using the Terminal Services APIs + // NB: This will only work under XP/2003 and later + typedef BOOL (WINAPI *WTSQueryUserToken_proto)(ULONG, PHANDLE); + DynamicFn _WTSQueryUserToken(_T("wtsapi32.dll"), "WTSQueryUserToken"); + if (_WTSQueryUserToken.isValid()) { + (*_WTSQueryUserToken)(-1, &h); + isSafe_ = true; + return; + } + + // Try to find the Shell Tray Icon window and take its token + // NB: This will only work under NT/2K (and later, but they're dealt with above) + // NB: If the shell is not running then this will return an Unsafe Null token. + EnumDesktops(GetProcessWindowStation(), enumDesktops, (LONG)&h); + isSafe_ = (h != 0); + } else { + // Try to open the security token for the User-Mode process + if (!OpenProcessToken(GetCurrentProcess(), GENERIC_ALL, &h)) { + DWORD err = GetLastError(); + if (err != ERROR_CALL_NOT_IMPLEMENTED) + throw rdr::SystemException("OpenProcessToken failed", err); + // Under Windows 95/98/Me, we fake the handle value... + h = INVALID_HANDLE_VALUE; + } + isSafe_ = true; + } +} + + +ImpersonateCurrentUser::ImpersonateCurrentUser() { + RegCloseKey(HKEY_CURRENT_USER); + if (!isServiceProcess()) + return; + if (!token.canImpersonate()) + throw rdr::Exception("Cannot impersonate unsafe or null token"); + if (!ImpersonateLoggedOnUser(token)) { + DWORD err = GetLastError(); + if (err != ERROR_CALL_NOT_IMPLEMENTED) + throw rdr::SystemException("Failed to impersonate user", GetLastError()); + } +} + +ImpersonateCurrentUser::~ImpersonateCurrentUser() { + if (!RevertToSelf()) { + DWORD err = GetLastError(); + if (err != ERROR_CALL_NOT_IMPLEMENTED) + exit(err); + } + RegCloseKey(HKEY_CURRENT_USER); +} + + +UserName::UserName() : TCharArray(UNLEN+1) { + DWORD len = UNLEN+1; + if (!GetUserName(buf, &len)) + throw rdr::SystemException("GetUserName failed", GetLastError()); +} + + +UserSID::UserSID() { + CurrentUserToken token; + if (!token.canImpersonate()) + return; + setSID(Sid::FromToken(token.h)); +} diff --git a/win/rfb_win32/CurrentUser.h b/win/rfb_win32/CurrentUser.h new file mode 100644 index 00000000..794f27c6 --- /dev/null +++ b/win/rfb_win32/CurrentUser.h @@ -0,0 +1,100 @@ +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * + * This 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 software 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 software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +// CurrentUser.h + +// Helper class providing the session's logged on username, if +// a user is logged on. Also allows processes running under +// XP/2K3 etc to masquerade as the logged on user for security +// purposes + +#ifndef __RFB_WIN32_CURRENT_USER_H__ +#define __RFB_WIN32_CURRENT_USER_H__ + +#include +#include + +namespace rfb { + + namespace win32 { + + // CurrentUserToken + // CurrentUserToken is a Handle containing the security token + // for the currently logged-on user, or null if no user is + // logged on. + // + // Under Windows 95/98/Me, which don't support security tokens, + // the token will be INVALID_HANDLE_VALUE if a user is logged on. + // + // Under Windows NT/2K, it may be the case that the token is + // null even when a user *is* logged on, because we use some hacks + // to detect the user's token and sometimes they fail. On these + // platforms, isSafe() will return False if the token is null. + // + // Under Windows XP, etc, isSafe() will always be True, and the token + // will always be set to the currently logged on user's token. + // + // canImpersonate() tests whether there is a user token that is safe + // to impersonate. + // + // noUserLoggedOn() tests whether there is *definitely* no user logged on. + + struct CurrentUserToken : public Handle { + CurrentUserToken(); + bool isSafe() const { return isSafe_; }; + bool canImpersonate() const { return h && isSafe(); } + bool noUserLoggedOn() const { return !h && isSafe(); } + private: + bool isSafe_; + }; + + // ImpersonateCurrentUser + // Throws an exception on failure. + // Succeeds (trivially) if process is not running as service. + // Fails if CurrentUserToken is not valid. + // Fails if platform is NT AND cannot impersonate token. + // Succeeds otherwise. + + struct ImpersonateCurrentUser { + ImpersonateCurrentUser(); + ~ImpersonateCurrentUser(); + CurrentUserToken token; + }; + + // UserName + // Returns the name of the user the thread is currently running as. + // Raises a SystemException in case of error. + // NB: Raises a SystemException with err == ERROR_NOT_LOGGED_ON if + // running under Windows 9x/95/Me and no user is logged on. + + struct UserName : public TCharArray { + UserName(); + }; + + // UserSID + // Returns the SID of the currently logged-on user (i.e. the session user) + + struct UserSID : public Sid { + UserSID(); + }; + + } + +} + +#endif diff --git a/win/rfb_win32/DIBSectionBuffer.cxx b/win/rfb_win32/DIBSectionBuffer.cxx new file mode 100644 index 00000000..18ce3ea4 --- /dev/null +++ b/win/rfb_win32/DIBSectionBuffer.cxx @@ -0,0 +1,222 @@ +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * + * This 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 software 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 software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +#include +#include +#include +#include + +using namespace rfb; +using namespace win32; + +static LogWriter vlog("DIBSectionBuffer"); + + +DIBSectionBuffer::DIBSectionBuffer(HWND window_) + : bitmap(0), device(0), window(window_) { + memset(&format, 0, sizeof(format)); + memset(palette, 0, sizeof(palette)); +} + +DIBSectionBuffer::DIBSectionBuffer(HDC device_) + : bitmap(0), window(0), device(device_) { + memset(&format, 0, sizeof(format)); + memset(palette, 0, sizeof(palette)); +} + +DIBSectionBuffer::~DIBSectionBuffer() { + if (bitmap) + DeleteObject(bitmap); +} + + +void DIBSectionBuffer::setPF(const PixelFormat& pf) { + if (memcmp(&getPF(), &pf, sizeof(pf)) == 0) { + vlog.debug("pixel format unchanged by setPF()"); + return; + } + format = pf; + recreateBuffer(); + if ((pf.bpp <= 8) && pf.trueColour) { + vlog.info("creating %d-bit TrueColour palette", pf.depth); + for (int i=0; i < (1<<(pf.depth)); i++) { + palette[i].b = ((((i >> pf.blueShift) & pf.blueMax) * 65535) + pf.blueMax/2) / pf.blueMax; + palette[i].g = ((((i >> pf.greenShift) & pf.greenMax) * 65535) + pf.greenMax/2) / pf.greenMax; + palette[i].r = ((((i >> pf.redShift) & pf.redMax) * 65535) + pf.redMax/2) / pf.redMax; + } + refreshPalette(); + } +} + +void DIBSectionBuffer::setSize(int w, int h) { + if (width_ == w && height_ == h) { + vlog.debug("size unchanged by setSize()"); + return; + } + width_ = w; + height_ = h; + recreateBuffer(); +} + + +// * copyPaletteToDIB MUST NEVER be called on a truecolour DIB! * + +void copyPaletteToDIB(Colour palette[256], HDC wndDC, HBITMAP dib) { + BitmapDC dibDC(wndDC, dib); + RGBQUAD rgb[256]; + for (unsigned int i=0;i<256;i++) { + rgb[i].rgbRed = palette[i].r >> 8; + rgb[i].rgbGreen = palette[i].g >> 8; + rgb[i].rgbBlue = palette[i].b >> 8; + } + if (!SetDIBColorTable(dibDC, 0, 256, (RGBQUAD*) rgb)) + throw rdr::SystemException("unable to SetDIBColorTable", GetLastError()); +} + +inline void initMaxAndShift(DWORD mask, int* max, int* shift) { + for ((*shift) = 0; (mask & 1) == 0; (*shift)++) mask >>= 1; + (*max) = (rdr::U16)mask; +} + +void DIBSectionBuffer::recreateBuffer() { + HBITMAP new_bitmap = 0; + rdr::U8* new_data = 0; + + if (width_ && height_ && (format.depth != 0)) { + BitmapInfo bi; + memset(&bi, 0, sizeof(bi)); + // *** wrong? + UINT iUsage = format.trueColour ? DIB_RGB_COLORS : DIB_PAL_COLORS; + // *** + bi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER); + bi.bmiHeader.biBitCount = format.bpp; + bi.bmiHeader.biSizeImage = (format.bpp / 8) * width_ * height_; + bi.bmiHeader.biPlanes = 1; + bi.bmiHeader.biWidth = width_; + bi.bmiHeader.biHeight = -height_; + bi.bmiHeader.biCompression = (format.bpp > 8) ? BI_BITFIELDS : BI_RGB; + bi.mask.red = format.redMax << format.redShift; + bi.mask.green = format.greenMax << format.greenShift; + bi.mask.blue = format.blueMax << format.blueShift; + + // Create a DIBSection to draw into + if (device) + new_bitmap = ::CreateDIBSection(device, (BITMAPINFO*)&bi.bmiHeader, iUsage, + (void**)&new_data, NULL, 0); + else + new_bitmap = ::CreateDIBSection(WindowDC(window), (BITMAPINFO*)&bi.bmiHeader, iUsage, + (void**)&new_data, NULL, 0); + + if (!new_bitmap) { + int err = GetLastError(); + throw rdr::SystemException("unable to create DIB section", err); + } + + vlog.debug("recreateBuffer()"); + } else { + vlog.debug("one of area or format not set"); + } + + if (new_bitmap && bitmap) { + vlog.debug("preserving bitmap contents"); + + // Copy the contents across + if (device) { + if (format.bpp <= 8) + copyPaletteToDIB(palette, device, new_bitmap); + BitmapDC src_dev(device, bitmap); + BitmapDC dest_dev(device, new_bitmap); + BitBlt(dest_dev, 0, 0, width_, height_, src_dev, 0, 0, SRCCOPY); + } else { + WindowDC wndDC(window); + if (format.bpp <= 8) + copyPaletteToDIB(palette, wndDC, new_bitmap); + BitmapDC src_dev(wndDC, bitmap); + BitmapDC dest_dev(wndDC, new_bitmap); + BitBlt(dest_dev, 0, 0, width_, height_, src_dev, 0, 0, SRCCOPY); + } + } + + if (bitmap) { + // Delete the old bitmap + DeleteObject(bitmap); + bitmap = 0; + data = 0; + } + + if (new_bitmap) { + // Set up the new bitmap + bitmap = new_bitmap; + data = new_data; + + // Determine the *actual* DIBSection format + DIBSECTION ds; + if (!GetObject(bitmap, sizeof(ds), &ds)) + throw rdr::SystemException("GetObject", GetLastError()); + + // Correct the "stride" of the DIB + // *** This code DWORD aligns each row - is that right??? + stride = width_; + int bytesPerRow = stride * format.bpp/8; + if (bytesPerRow % 4) { + bytesPerRow += 4 - (bytesPerRow % 4); + stride = (bytesPerRow * 8) / format.bpp; + vlog.info("adjusting DIB stride: %d to %d", width_, stride); + } + + // Calculate the PixelFormat for the DIB + format.bigEndian = 0; + format.bpp = format.depth = ds.dsBm.bmBitsPixel; + format.trueColour = format.trueColour || format.bpp > 8; + if (format.bpp > 8) { + + // Get the truecolour format used by the DIBSection + initMaxAndShift(ds.dsBitfields[0], &format.redMax, &format.redShift); + initMaxAndShift(ds.dsBitfields[1], &format.greenMax, &format.greenShift); + initMaxAndShift(ds.dsBitfields[2], &format.blueMax, &format.blueShift); + + // Calculate the effective depth + format.depth = 0; + Pixel bits = ds.dsBitfields[0] | ds.dsBitfields[1] | ds.dsBitfields[2]; + while (bits) { + format.depth++; + bits = bits >> 1; + } + if (format.depth > format.bpp) + throw Exception("Bad DIBSection format (depth exceeds bpp)"); + } else { + // Set the DIBSection's palette + refreshPalette(); + } + + } +} + +void DIBSectionBuffer::refreshPalette() { + if (format.bpp > 8) { + vlog.error("refresh palette called for truecolor DIB"); + return; + } + vlog.debug("refreshing palette"); + if (device) + copyPaletteToDIB(palette, device, bitmap); + else + copyPaletteToDIB(palette, WindowDC(window), bitmap); +} + + diff --git a/win/rfb_win32/DIBSectionBuffer.h b/win/rfb_win32/DIBSectionBuffer.h new file mode 100644 index 00000000..ad1a310c --- /dev/null +++ b/win/rfb_win32/DIBSectionBuffer.h @@ -0,0 +1,85 @@ +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * + * This 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 software 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 software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +// -=- DIBSectionBuffer.h + +// A DIBSectionBuffer acts much like a standard PixelBuffer, but is associated +// with a particular window on-screen and can be drawn into that window if +// required, using the standard Win32 drawing operations. + +#ifndef __RFB_WIN32_DIB_SECTION_BUFFER_H__ +#define __RFB_WIN32_DIB_SECTION_BUFFER_H__ + +#include +#include +#include +#include +#include + +namespace rfb { + + namespace win32 { + + // + // -=- DIBSectionBuffer + // + + class DIBSectionBuffer : public FullFramePixelBuffer, ColourMap { + public: + DIBSectionBuffer(HWND window); + DIBSectionBuffer(HDC device); + virtual ~DIBSectionBuffer(); + + virtual void setPF(const PixelFormat &pf); + virtual void setSize(int w, int h); + + virtual int getStride() const {return stride;} + + virtual ColourMap* getColourMap() const {return (ColourMap*)this;} + + // - ColourMap interface + virtual void lookup(int index, int* r, int *g, int* b) { + *r = palette[index].r; + *g = palette[index].g; + *b = palette[index].b; + } + + // Custom colourmap interface + void setColour(int index, int r, int g, int b) { + palette[index].r = r; + palette[index].g = g; + palette[index].b = b; + } + void refreshPalette(); + + // *** virtual void copyRect(const Rect &dest, const Point &move_by_delta); + public: + HBITMAP bitmap; + protected: + void recreateBuffer(); + Colour palette[256]; + int stride; + HWND window; + HDC device; + }; + + }; + +}; + +#endif // __RFB_WIN32_DIB_SECTION_BUFFER_H__ diff --git a/win/rfb_win32/DeviceContext.cxx b/win/rfb_win32/DeviceContext.cxx new file mode 100644 index 00000000..4f70a1bf --- /dev/null +++ b/win/rfb_win32/DeviceContext.cxx @@ -0,0 +1,188 @@ +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * + * This 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 software 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 software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +#include +#include +#include +#include +#include + +using namespace rfb; +using namespace win32; + + +static LogWriter vlog("DeviceContext"); + +PixelFormat DeviceContext::getPF() const { + return getPF(dc); +} + +PixelFormat DeviceContext::getPF(HDC dc) { + PixelFormat format; + CompatibleBitmap bitmap(dc, 1, 1); + + // -=- Get the bitmap format information + BitmapInfo bi; + memset(&bi, 0, sizeof(bi)); + bi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER); + bi.bmiHeader.biBitCount = 0; + if (!::GetDIBits(dc, bitmap, 0, 1, NULL, (BITMAPINFO*)&bi, DIB_RGB_COLORS)) { + throw rdr::SystemException("unable to determine device pixel format", GetLastError()); + } + if (!::GetDIBits(dc, bitmap, 0, 1, NULL, (BITMAPINFO*)&bi, DIB_RGB_COLORS)) { + throw rdr::SystemException("unable to determine pixel shifts/palette", GetLastError()); + } + + // Set the initial format information + format.trueColour = bi.bmiHeader.biBitCount > 8; + format.bigEndian = 0; + format.bpp = bi.bmiHeader.biBitCount; + + if (format.trueColour) { + DWORD rMask=0, gMask=0, bMask=0; + + // Which true colour format is the DIB section using? + switch (bi.bmiHeader.biCompression) { + case BI_RGB: + // Default RGB layout + switch (bi.bmiHeader.biBitCount) { + case 16: + // RGB 555 - High Colour + vlog.info("16-bit High Colour"); + rMask = 0x7c00; + bMask = 0x001f; + gMask = 0x03e0; + break; + case 24: + case 32: + // RGB 888 - True Colour + vlog.info("24/32-bit High Colour"); + rMask = 0xff0000; + gMask = 0x00ff00; + bMask = 0x0000ff; + break; + default: + vlog.error("bits per pixel %u not supported", bi.bmiHeader.biBitCount); + throw rdr::Exception("unknown bits per pixel specified"); + }; + break; + case BI_BITFIELDS: + // Custom RGB layout + rMask = bi.mask.red; + gMask = bi.mask.green; + bMask = bi.mask.blue; + vlog.info("%lu-bit BitFields: (%lx, %lx, %lx)", + bi.bmiHeader.biBitCount, rMask, gMask, bMask); + break; + }; + + // Convert the data we just retrieved + initMaxAndShift(rMask, &format.redMax, &format.redShift); + initMaxAndShift(gMask, &format.greenMax, &format.greenShift); + initMaxAndShift(bMask, &format.blueMax, &format.blueShift); + + // Calculate the depth from the colour shifts + format.depth = 0; + Pixel bits = rMask | gMask | bMask; + while (bits) { + format.depth++; + bits = bits >> 1; + } + + // Check that the depth & bpp are valid + if (format.depth > format.bpp) { + vlog.error("depth exceeds bits per pixel!"); + format.bpp = format.depth; + } + + // Correct the bits-per-pixel to something we're happy with + if (format.bpp <= 16) + format.bpp = 16; + else if (format.bpp <= 32) + format.bpp = 32; + } else { + // Palettised format - depth reflects number of colours, + // but bits-per-pixel is ALWAYS 8 + format.depth = format.bpp; + if (format.bpp < 8) + format.bpp = 8; + vlog.info("%d-colour palettised", 1< +#include +#include +#include + +namespace rfb { + + namespace win32 { + + // Base class, providing methods to get the bounding (clip) box, + // and the pixel format, and access to the HDC itself. + class DeviceContext { + public: + DeviceContext() : dc(0) {} + virtual ~DeviceContext() {} + operator HDC() const {return dc;} + PixelFormat getPF() const; + static PixelFormat getPF(HDC dc); + Rect getClipBox() const; + static Rect getClipBox(HDC dc); + protected: + HDC dc; + }; + + // -=- DeviceContext that opens a specific display device + class DeviceDC : public DeviceContext { + public: + DeviceDC(const TCHAR* deviceName); + ~DeviceDC(); + }; + + // Get a DC for a particular window's client area. + class WindowDC : public DeviceContext { + public: + WindowDC(HWND wnd); + virtual ~WindowDC(); + protected: + HWND hwnd; + }; + + // Create a new DC, compatible with an existing one. + class CompatibleDC : public DeviceContext { + public: + CompatibleDC(HDC existing); + virtual ~CompatibleDC(); + }; + + // Create a new DC, compatible with an existing one, and + // select the specified bitmap into it. + class BitmapDC : public CompatibleDC { + public: + BitmapDC(HDC hdc, HBITMAP hbitmap); + ~BitmapDC(); + protected: + HBITMAP oldBitmap; + }; + + }; +}; + +#endif diff --git a/win/rfb_win32/DeviceFrameBuffer.cxx b/win/rfb_win32/DeviceFrameBuffer.cxx new file mode 100644 index 00000000..8da894e0 --- /dev/null +++ b/win/rfb_win32/DeviceFrameBuffer.cxx @@ -0,0 +1,289 @@ +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * + * This 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 software 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 software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +// -=- DeviceFrameBuffer.cxx +// +// The DeviceFrameBuffer class encapsulates the pixel data of the system +// display. + +#include +#include +#include +#include +#include +#include +#include + +using namespace rfb; +using namespace win32; + +static LogWriter vlog("DeviceFrameBuffer"); + +BoolParameter DeviceFrameBuffer::useCaptureBlt("UseCaptureBlt", + "Use a slower capture method that ensures that alpha blended windows appear correctly", + true); + + +// -=- DeviceFrameBuffer class + +DeviceFrameBuffer::DeviceFrameBuffer(HDC deviceContext, const Rect& wRect) + : DIBSectionBuffer(deviceContext), device(deviceContext), cursorBm(deviceContext), + ignoreGrabErrors(false) +{ + + // -=- Firstly, let's check that the device has suitable capabilities + + int capabilities = GetDeviceCaps(device, RASTERCAPS); + if (!(capabilities & RC_BITBLT)) { + throw Exception("device does not support BitBlt"); + } + if (!(capabilities & RC_DI_BITMAP)) { + throw Exception("device does not support GetDIBits"); + } + /* + if (GetDeviceCaps(device, PLANES) != 1) { + throw Exception("device does not support planar displays"); + } + */ + + // -=- Get the display dimensions and pixel format + + // Get the display dimensions + deviceCoords = DeviceContext::getClipBox(device); + if (!wRect.is_empty()) + deviceCoords = wRect.translate(deviceCoords.tl); + int w = deviceCoords.width(); + int h = deviceCoords.height(); + + // We can't handle uneven widths :( + if (w % 2) w--; + + // Configure the underlying DIB to match the device + DIBSectionBuffer::setPF(DeviceContext::getPF(device)); + DIBSectionBuffer::setSize(w, h); + + // Configure the cursor buffer + cursorBm.setPF(format); + + // Set up a palette if required + if (!format.trueColour) + updateColourMap(); +} + +DeviceFrameBuffer::~DeviceFrameBuffer() { +} + + +void +DeviceFrameBuffer::setPF(const PixelFormat &pf) { + throw Exception("setPF not supported"); +} + +void +DeviceFrameBuffer::setSize(int w, int h) { + throw Exception("setSize not supported"); +} + + +#ifndef CAPTUREBLT +#define CAPTUREBLT 0x40000000 +#endif + +void +DeviceFrameBuffer::grabRect(const Rect &rect) { + BitmapDC tmpDC(device, bitmap); + + // Map the rectangle coords from VNC Desktop-relative to device relative - usually (0,0) + Point src = desktopToDevice(rect.tl); + + // Note: Microsoft's documentation lies directly about CAPTUREBLT and claims it works on 98/ME + // If you try CAPTUREBLT on 98 then you get blank output... + if (!::BitBlt(tmpDC, rect.tl.x, rect.tl.y, rect.width(), rect.height(), device, src.x, src.y, + (osVersion.isPlatformNT && useCaptureBlt) ? (CAPTUREBLT | SRCCOPY) : SRCCOPY)) { + if (ignoreGrabErrors) + vlog.error("BitBlt failed:%ld", GetLastError()); + else + throw rdr::SystemException("BitBlt failed", GetLastError()); + } +} + +void +DeviceFrameBuffer::grabRegion(const Region &rgn) { + std::vector rects; + std::vector::const_iterator i; + rgn.get_rects(&rects); + for(i=rects.begin(); i!=rects.end(); i++) { + grabRect(*i); + } + ::GdiFlush(); +} + + +void copyDevicePaletteToDIB(HDC dc, DIBSectionBuffer* dib) { + // - Fetch the system palette for the framebuffer + PALETTEENTRY syspalette[256]; + UINT entries = ::GetSystemPaletteEntries(dc, 0, 256, syspalette); + + if (entries == 0) { + vlog.info("resorting to standard 16 color palette"); + for (unsigned int i=0;i<256;i++) { + int v = (i%16) >= 8 ? 127 : 255; + syspalette[i].peRed = i & 1 ? v : 0; + syspalette[i].peGreen = i & 2 ? v : 0; + syspalette[i].peBlue = i & 4 ? v : 0; + } + } else { + vlog.info("framebuffer has %u palette entries", entries); + } + + // - Update the bitmap's stored copy of the palette + for (unsigned int i=0;i<256;i++) { + int r, g, b; + r = (syspalette[i].peRed << 8) + 0x80; + g = (syspalette[i].peGreen << 8) + 0x80; + b = (syspalette[i].peBlue << 8) + 0x80; + dib->setColour(i, r, g, b); + } + + // - Update the DIB section to use the palette + dib->refreshPalette(); +} + + +void DeviceFrameBuffer::setCursor(HCURSOR hCursor, VNCServer* server) +{ + // - If hCursor is null then there is no cursor - clear the old one + + if (hCursor == 0) { + server->setCursor(0, 0, Point(), 0, 0); + return; + } + + try { + + // - Get the size and other details about the cursor. + + IconInfo iconInfo((HICON)hCursor); + + BITMAP maskInfo; + if (!GetObject(iconInfo.hbmMask, sizeof(BITMAP), &maskInfo)) + throw rdr::SystemException("GetObject() failed", GetLastError()); + if (maskInfo.bmPlanes != 1) + throw rdr::Exception("unsupported multi-plane cursor"); + if (maskInfo.bmBitsPixel != 1) + throw rdr::Exception("unsupported cursor mask format"); + + // - Create the cursor pixel buffer and mask storage + // NB: The cursor pixel buffer is NOT used here. Instead, we + // pass the cursorBm.data pointer directly, to save overhead. + + cursor.setSize(maskInfo.bmWidth, maskInfo.bmHeight); + cursor.setPF(format); + cursor.hotspot = Point(iconInfo.xHotspot, iconInfo.yHotspot); + + // - Get the AND and XOR masks. There is only an XOR mask if this is not a + // colour cursor. + + if (!iconInfo.hbmColor) + cursor.setSize(cursor.width(), cursor.height() / 2); + rdr::U8Array mask(maskInfo.bmWidthBytes * maskInfo.bmHeight); + rdr::U8* xorMask = mask.buf + cursor.height() * maskInfo.bmWidthBytes; + + if (!GetBitmapBits(iconInfo.hbmMask, + maskInfo.bmWidthBytes * maskInfo.bmHeight, mask.buf)) + throw rdr::SystemException("GetBitmapBits failed", GetLastError()); + + // Configure the cursor bitmap + cursorBm.setSize(cursor.width(), cursor.height()); + + // Copy the palette into it if required + if (format.bpp <= 8) + copyDevicePaletteToDIB(device, &cursorBm); + + // Draw the cursor into the bitmap + BitmapDC dc(device, cursorBm.bitmap); + if (!DrawIconEx(dc, 0, 0, hCursor, 0, 0, 0, NULL, DI_NORMAL | DI_COMPAT)) + throw rdr::SystemException("unable to render cursor", GetLastError()); + + // Replace any XORed pixels with xorColour, because RFB doesn't support + // XORing of cursors. XORing is used for the I-beam cursor, which is most + // often used over a white background, but also sometimes over a black + // background. We set the XOR'd pixels to black, then draw a white outline + // around the whole cursor. + + // *** should we replace any pixels not set in mask to zero, to ensure + // that irrelevant data doesn't screw compression? + + bool doOutline = false; + if (!iconInfo.hbmColor) { + Pixel xorColour = format.pixelFromRGB(0, 0, 0, cursorBm.getColourMap()); + for (int y = 0; y < cursor.height(); y++) { + bool first = true; + for (int x = 0; x < cursor.width(); x++) { + int byte = y * maskInfo.bmWidthBytes + x / 8; + int bit = 7 - x % 8; + if ((mask.buf[byte] & (1 << bit)) && (xorMask[byte] & (1 << bit))) + { + mask.buf[byte] &= ~(1 << bit); + + switch (format.bpp) { + case 8: + ((rdr::U8*)cursorBm.data)[y * cursor.width() + x] = xorColour; break; + case 16: + ((rdr::U16*)cursorBm.data)[y * cursor.width() + x] = xorColour; break; + case 32: + ((rdr::U32*)cursorBm.data)[y * cursor.width() + x] = xorColour; break; + } + + doOutline = true; + } + } + } + } + + // Finally invert the AND mask so it's suitable for RFB and pack it into + // the minimum number of bytes per row. + + int maskBytesPerRow = (cursor.width() + 7) / 8; + + for (int j = 0; j < cursor.height(); j++) { + for (int i = 0; i < maskBytesPerRow; i++) + cursor.mask.buf[j * maskBytesPerRow + i] + = ~mask.buf[j * maskInfo.bmWidthBytes + i]; + } + + if (doOutline) { + vlog.debug("drawing cursor outline!"); + memcpy(cursor.data, cursorBm.data, cursor.dataLen()); + cursor.drawOutline(format.pixelFromRGB(0xffff, 0xffff, 0xffff, cursorBm.getColourMap())); + memcpy(cursorBm.data, cursor.data, cursor.dataLen()); + } + + server->setCursor(cursor.width(), cursor.height(), cursor.hotspot, + cursorBm.data, cursor.mask.buf); + } catch (rdr::Exception& e) { + vlog.error(e.str()); + } +} + + +void +DeviceFrameBuffer::updateColourMap() { + if (!format.trueColour) + copyDevicePaletteToDIB(device, this); +} diff --git a/win/rfb_win32/DeviceFrameBuffer.h b/win/rfb_win32/DeviceFrameBuffer.h new file mode 100644 index 00000000..7718c339 --- /dev/null +++ b/win/rfb_win32/DeviceFrameBuffer.h @@ -0,0 +1,106 @@ +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * + * This 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 software 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 software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +// -=- DeviceFrameBuffer.h +// +// The DeviceFrameBuffer class encapsulates the pixel data of a supplied +// Device Context Handle (HDC) + +// *** THIS INTERFACE NEEDS TIDYING TO SEPARATE COORDINATE SYSTEMS BETTER *** + +#ifndef __RFB_WIN32_DEVICE_FRAME_BUFFER_H__ +#define __RFB_WIN32_DEVICE_FRAME_BUFFER_H__ + +#include +#include +#include +#include +#include +#include + +namespace rfb { + + class VNCServer; + + namespace win32 { + + // -=- DeviceFrameBuffer interface + + // DeviceFrameBuffer is passed an HDC referring to a window or to + // the entire display. It may also be passed a rectangle specifying + // the Device-relative coordinates of the actual rectangle to treat + // as the desktop. + + // Coordinate systems start getting really annoying here. There are + // three different "origins" to which coordinates might be relative: + // + // Desktop - VNC coordinates, top-left always (0,0) + // Device - DC coordinates. Top-left *usually (0,0) but could be other. + // Window - coordinates relative to the specified sub-rectangle within + // the supplied DC. + // Screen - Coordinates relative to the entire Windows virtual screen. + // The virtual screen includes all monitors that are part of + // the Windows desktop. + + // The data member is made to point to an internal mirror of the + // current display data. Individual rectangles or regions of the + // buffer can be brought up to date by calling the grab functions. + + class DeviceFrameBuffer : public DIBSectionBuffer { + public: + DeviceFrameBuffer(HDC deviceContext, const Rect& area_=Rect()); + virtual ~DeviceFrameBuffer(); + + // - FrameBuffer overrides + + virtual void grabRect(const Rect &rect); + virtual void grabRegion(const Region ®ion); + + // - DIBSectionBuffer overrides + + virtual void setPF(const PixelFormat& pf); + virtual void setSize(int w, int h); + + // - DeviceFrameBuffer specific methods + + void setCursor(HCURSOR c, VNCServer* server); + void updateColourMap(); + + // Set whether grabRect should ignore errors or throw exceptions + // Only set this if you are sure you'll capture the errors some other way! + void setIgnoreGrabErrors(bool ie) {ignoreGrabErrors=ie;} + + static BoolParameter useCaptureBlt; + + protected: + // Translate supplied Desktop coordinates into Device-relative coordinates + // This translation may have been affected at start-time by the supplied sub-rect. + Point desktopToDevice(const Point p) const {return p.translate(deviceCoords.tl);} + + HDC device; + DIBSectionBuffer cursorBm; + Cursor cursor; + Rect deviceCoords; + bool ignoreGrabErrors; + }; + + }; + +}; + +#endif // __RFB_WIN32_DEVICE_FRAME_BUFFER_H__ diff --git a/win/rfb_win32/Dialog.cxx b/win/rfb_win32/Dialog.cxx new file mode 100644 index 00000000..398334f0 --- /dev/null +++ b/win/rfb_win32/Dialog.cxx @@ -0,0 +1,391 @@ +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * + * This 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 software 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 software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +// -=- Dialog.cxx + +// Base-class for any Dialog classes we might require + +#include +#include +#include +#include +#include + +#ifdef _DIALOG_CAPTURE +#ifdef PropSheet_IndexToId +#include +#include +#else +#undef _DIALOG_CAPTURE +#pragma message(" NOTE: Not building Dialog Capture support.") +#endif +#endif + +using namespace rfb; +using namespace rfb::win32; + +static LogWriter dlog("Dialog"); +static LogWriter plog("PropSheet"); + + +Dialog::Dialog(HINSTANCE inst_) +: inst(inst_), alreadyShowing(false), handle(0) +{ +} + +Dialog::~Dialog() +{ +} + + +bool Dialog::showDialog(const TCHAR* resource, HWND owner) +{ + if (alreadyShowing) return false; + handle = 0; + alreadyShowing = true; + INT_PTR result = DialogBoxParam(inst, resource, owner, + staticDialogProc, (LPARAM)this); + if (result<0) + throw rdr::SystemException("DialogBoxParam failed", GetLastError()); + alreadyShowing = false; + return (result == 1); +} + + +bool Dialog::isItemChecked(int id) { + return SendMessage(GetDlgItem(handle, id), BM_GETCHECK, 0, 0) == BST_CHECKED; +} +int Dialog::getItemInt(int id) { + BOOL trans; + int result = GetDlgItemInt(handle, id, &trans, TRUE); + if (!trans) + throw rdr::Exception("unable to read dialog Int"); + return result; +} +TCHAR* Dialog::getItemString(int id) { + TCharArray tmp(256); + if (!GetDlgItemText(handle, id, tmp.buf, 256)) + tmp.buf[0] = 0; + return tmp.takeBuf(); +} + +void Dialog::setItemChecked(int id, bool state) { + dlog.debug("bool[%d]=%d", id, (int)state); + SendMessage(GetDlgItem(handle, id), BM_SETCHECK, state ? BST_CHECKED : BST_UNCHECKED, 0); +} +void Dialog::setItemInt(int id, int value) { + dlog.debug("int[%d]=%d", id, value); + SetDlgItemInt(handle, id, value, TRUE); +} +void Dialog::setItemString(int id, const TCHAR* s) { + dlog.debug("string[%d]=%s", id, (const char*)CStr(s)); + SetDlgItemText(handle, id, s); +} + + +void Dialog::enableItem(int id, bool state) { + dlog.debug("enable[%d]=%d", id, (int)state); + EnableWindow(GetDlgItem(handle, id), state); +} + + + + +BOOL CALLBACK Dialog::staticDialogProc(HWND hwnd, UINT msg, + WPARAM wParam, LPARAM lParam) +{ + if (msg == WM_INITDIALOG) + SetWindowLong(hwnd, GWL_USERDATA, (LONG)lParam); + + LONG self = GetWindowLong(hwnd, GWL_USERDATA); + if (!self) return FALSE; + + return ((Dialog*)self)->dialogProc(hwnd, msg, wParam, lParam); +} + +BOOL Dialog::dialogProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) +{ + switch (msg) { + + case WM_INITDIALOG: + handle = hwnd; + initDialog(); + return TRUE; + + case WM_COMMAND: + switch (LOWORD(wParam)) { + case IDOK: + if (onOk()) { + EndDialog(hwnd, 1); + return TRUE; + } + return FALSE; + case IDCANCEL: + EndDialog(hwnd, 0); + return TRUE; + default: + return onCommand(LOWORD(wParam), HIWORD(wParam)); + }; + + case WM_HELP: + return onHelp(((HELPINFO*)lParam)->iCtrlId); + + } + + return FALSE; +} + + +PropSheetPage::PropSheetPage(HINSTANCE inst, const TCHAR* id) : Dialog(inst), propSheet(0) { + page.dwSize = sizeof(page); + page.dwFlags = 0; // PSP_USECALLBACK; + page.hInstance = inst; + page.pszTemplate = id; + page.pfnDlgProc = staticPageProc; + page.lParam = (LPARAM)this; + page.pfnCallback = 0; // staticPageProc; +} + +PropSheetPage::~PropSheetPage() { +} + + +BOOL CALLBACK PropSheetPage::staticPageProc(HWND hwnd, UINT msg, + WPARAM wParam, LPARAM lParam) +{ + if (msg == WM_INITDIALOG) + SetWindowLong(hwnd, GWL_USERDATA, ((PROPSHEETPAGE*)lParam)->lParam); + + LONG self = GetWindowLong(hwnd, GWL_USERDATA); + if (!self) return FALSE; + + return ((PropSheetPage*)self)->dialogProc(hwnd, msg, wParam, lParam); +} + +BOOL PropSheetPage::dialogProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) +{ + switch (msg) { + + case WM_INITDIALOG: + handle = hwnd; + initDialog(); + return TRUE; + + case WM_NOTIFY: + switch (((NMHDR*)lParam)->code) { + case PSN_APPLY: + onOk(); + return FALSE; + }; + return FALSE; + + case WM_COMMAND: + return onCommand(LOWORD(wParam), HIWORD(wParam)); + + case WM_HELP: + return onHelp(((HELPINFO*)lParam)->iCtrlId); + + } + + return FALSE; +} + + +PropSheet::PropSheet(HINSTANCE inst_, const TCHAR* title_, std::list pages_, HICON icon_) +: title(tstrDup(title_)), inst(inst_), pages(pages_), alreadyShowing(0), handle(0), icon(icon_) { +} + +PropSheet::~PropSheet() { +} + + +// For some reason, DLGTEMPLATEEX isn't defined in the Windows headers - go figure... +struct DLGTEMPLATEEX { + WORD dlgVer; + WORD signature; + DWORD helpID; + DWORD exStyle; + DWORD style; + WORD cDlgItems; + short x; + short y; + short cx; + short cy; +}; + +static int CALLBACK removeCtxtHelp(HWND hwnd, UINT message, LPARAM lParam) { + if (message == PSCB_PRECREATE) { + // Remove the context-help style, to remove the titlebar ? button + // *** Nasty hack to cope with new & old dialog template formats... + if (((DLGTEMPLATEEX*)lParam)->signature == 0xffff) + ((DLGTEMPLATEEX*)lParam)->style &= ~DS_CONTEXTHELP; + else + ((LPDLGTEMPLATE)lParam)->style &= ~DS_CONTEXTHELP; + } + return TRUE; +} + + +bool PropSheet::showPropSheet(HWND owner, bool showApply, bool showCtxtHelp, bool capture) { + if (alreadyShowing) return false; + alreadyShowing = true; + int count = pages.size(); + + HPROPSHEETPAGE* hpages = new HPROPSHEETPAGE[count]; + try { + // Create the PropertSheet page GDI objects. + std::list::iterator pspi; + int i = 0; + for (pspi=pages.begin(); pspi!=pages.end(); pspi++) { + hpages[i] = CreatePropertySheetPage(&((*pspi)->page)); + (*pspi)->setPropSheet(this); + i++; + } + + // Initialise and create the PropertySheet itself + PROPSHEETHEADER header; + header.dwSize = PROPSHEETHEADER_V1_SIZE; + header.dwFlags = PSH_MODELESS | (showApply ? 0 : PSH_NOAPPLYNOW) | (showCtxtHelp ? 0 : PSH_USECALLBACK); + header.pfnCallback = removeCtxtHelp; + header.hwndParent = owner; + header.hInstance = inst; + header.pszCaption = title.buf; + header.nPages = count; + header.nStartPage = 0; + header.phpage = hpages; + if (icon) { + header.hIcon = icon; + header.dwFlags |= PSH_USEHICON; + } + + handle = (HWND)PropertySheet(&header); + if ((handle == 0) || (handle == (HWND)-1)) + throw rdr::SystemException("PropertySheet failed", GetLastError()); + centerWindow(handle, owner); + plog.info("created %lx", handle); + +#ifdef _DIALOG_CAPTURE + if (capture) { + plog.info("capturing \"%s\"", (const char*)CStr(title.buf)); + char* tmpdir = getenv("TEMP"); + HDC dc = GetWindowDC(handle); + DeviceFrameBuffer fb(dc); + int i=0; + while (true) { + int id = PropSheet_IndexToId(handle, i); + if (!id) break; + PropSheet_SetCurSelByID(handle, id); + MSG msg; + while (PeekMessage(&msg, handle, 0, 0, PM_REMOVE)) { + if (!PropSheet_IsDialogMessage(handle, &msg)) + DispatchMessage(&msg); + } + fb.grabRect(fb.getRect()); + TCHAR title[128]; + if (!GetWindowText(PropSheet_GetCurrentPageHwnd(handle), title, sizeof(title))) + _stprintf(title, _T("capture%d"), i); + CharArray pageTitle(strDup(title)); + for (int j=0; jsetPropSheet(0); + delete [] hpages; hpages = 0; + + return true; + } catch (rdr::Exception) { + alreadyShowing = false; + + std::list::iterator pspi; + for (pspi=pages.begin(); pspi!=pages.end(); pspi++) + (*pspi)->setPropSheet(0); + delete [] hpages; hpages = 0; + + throw; + } +} + +void PropSheet::reInitPages() { + plog.debug("reInitPages %lx", handle); + std::list::iterator pspi; + for (pspi=pages.begin(); pspi!=pages.end(); pspi++) { + if ((*pspi)->handle) + (*pspi)->initDialog(); + } +} + +bool PropSheet::commitPages() { + plog.debug("commitPages %lx", handle); + bool result = true; + std::list::iterator pspi; + for (pspi=pages.begin(); pspi!=pages.end(); pspi++) { + if ((*pspi)->handle) + result = result && (*pspi)->onOk(); + } + return result; +} + + +void PropSheetPage::setChanged(bool changed) { + if (propSheet) { + plog.debug("setChanged[%lx(%lx)]=%d", handle, propSheet->handle, (int)changed); + if (changed) + PropSheet_Changed(propSheet->handle, handle); + else + PropSheet_UnChanged(propSheet->handle, handle); + } +} diff --git a/win/rfb_win32/Dialog.h b/win/rfb_win32/Dialog.h new file mode 100644 index 00000000..9784ba46 --- /dev/null +++ b/win/rfb_win32/Dialog.h @@ -0,0 +1,158 @@ +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * + * This 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 software 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 software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +// -=- RegConfig.h + +// Class which monitors the registry and reads in the registry settings +// whenever they change, or are added or removed. + +#ifndef __RFB_WIN32_DIALOG_H__ +#define __RFB_WIN32_DIALOG_H__ + +#include +#include +#include +#include + +namespace rfb { + + namespace win32 { + + // Dialog - A simple Win32 Dialog box. A derived class of Dialog overrides the + // initDialog(), command() and ok() methods to take appropriate action. A + // simple dialog box can be displayed by creating a Dialog object and calling + // show(). + + class Dialog { + public: + + Dialog(HINSTANCE inst); + virtual ~Dialog(); + + // showDialog() displays the dialog box. It returns when it has been dismissed, + // returning true if "OK" was pressed, false otherwise. The resource + // argument identifies the dialog resource (often a MAKEINTRESOURCE macro + // expansion), and owner is an optional window handle - the corresponding + // window is disabled while the dialog box is displayed. + + bool showDialog(const TCHAR* resource, HWND owner=0); + + // initDialog() is called upon receipt of the WM_INITDIALOG message. + + virtual void initDialog() {} + + // onCommand() is called upon receipt of a WM_COMMAND message item other than IDOK + // or IDCANCEL. It should return true if the command has been handled. + + virtual bool onCommand(int item, int cmd) { return false; } + + // onHelp() is called upon receipt of a WM_MENU message. This indicates that + // context-specific help should be displayed, for a dialog control, for example. + // It should return true if the command has been handled. + + virtual bool onHelp(int item) { return false; } + + // onOk() is called when the OK button is pressed. The hwnd argument is the + // dialog box's window handle. + + virtual bool onOk() { return true; } + + // Read the states of items + bool isItemChecked(int id); + int getItemInt(int id); + TCHAR* getItemString(int id); // Recipient owns string storage + + // Set the states of items + void setItemChecked(int id, bool state); + void setItemInt(int id, int value); + void setItemString(int id, const TCHAR* s); + + // enableItem is used to grey out an item, making it inaccessible, or to + // re-enable it. + void enableItem(int id, bool state); + + protected: + static BOOL CALLBACK staticDialogProc(HWND hwnd, UINT msg, + WPARAM wParam, LPARAM lParam); + virtual BOOL dialogProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam); + HINSTANCE inst; + HWND handle; + bool alreadyShowing; + }; + + // PropertySheetPage + // Class used to define property pages within a PropertySheet. + // Each page is associated with a particular dialog resource, indicated by + // the "id" parameter supplied to the constructor. + + class PropSheetPage; + + class PropSheet { + public: + PropSheet(HINSTANCE inst, const TCHAR* title, std::list pages, HICON icon=0); + virtual ~PropSheet(); + + // Display the PropertySheet + bool showPropSheet(HWND owner, bool showApply = false, bool showCtxtHelp = false, bool capture=false); + + // Calls initDialog again for each page that has already had it called. + // Note: If a page hasn't been seen yet, it won't have been called. + // Note: This must only be called while the property sheet is visible. + void reInitPages(); + + // Calls onOk for each page that has had initDialog called, and returns + // false if any one of them returns false, or true otherwise. ALL the + // onOk() methods will be called, even if one of them fails. + // Note: If a page hasn't been seen yet, it won't have been called. + // Note: This must only be called while the property sheet is visible. + bool commitPages(); + + friend class PropSheetPage; + + protected: + HWND owner; + HICON icon; + std::list pages; + HINSTANCE inst; + TCharArray title; + HWND handle; + bool alreadyShowing; + }; + + class PropSheetPage : public Dialog { + public: + PropSheetPage(HINSTANCE inst, const TCHAR* id); + virtual ~PropSheetPage(); + + void setChanged(bool changed); + + friend class PropSheet; + + protected: + void setPropSheet(PropSheet* ps) {propSheet = ps;}; + static BOOL CALLBACK staticPageProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam); + virtual BOOL dialogProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam); + PROPSHEETPAGE page; + PropSheet* propSheet; + }; + + }; + +}; + +#endif // __RFB_WIN32_DIALOG_H__ diff --git a/win/rfb_win32/DynamicFn.cxx b/win/rfb_win32/DynamicFn.cxx new file mode 100644 index 00000000..e933f249 --- /dev/null +++ b/win/rfb_win32/DynamicFn.cxx @@ -0,0 +1,45 @@ +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * + * This 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 software 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 software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +#include +#include +#include + +using namespace rfb; +using namespace win32; + +static LogWriter vlog("DynamicFn"); + + +DynamicFnBase::DynamicFnBase(const TCHAR* dllName, const char* fnName) : dllHandle(0), fnPtr(0) { + dllHandle = LoadLibrary(dllName); + if (!dllHandle) { + vlog.info("DLL %s not found (%d)", (const char*)CStr(dllName), GetLastError()); + return; + } + fnPtr = GetProcAddress(dllHandle, fnName); + if (!fnPtr) + vlog.info("proc %s not found in %s (%d)", fnName, (const char*)CStr(dllName), GetLastError()); +} + +DynamicFnBase::~DynamicFnBase() { + if (dllHandle) + FreeLibrary(dllHandle); +} + + diff --git a/win/rfb_win32/DynamicFn.h b/win/rfb_win32/DynamicFn.h new file mode 100644 index 00000000..57fdbec5 --- /dev/null +++ b/win/rfb_win32/DynamicFn.h @@ -0,0 +1,51 @@ +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * + * This 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 software 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 software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +// Helper class managing dynamic linkage to DLL functions. + +#ifndef __RFB_WIN32_DYNAMICFN_H__ +#define __RFB_WIN32_DYNAMICFN_H__ + +#include + +namespace rfb { + namespace win32 { + + class DynamicFnBase { + public: + DynamicFnBase(const TCHAR* dllName, const char* fnName); + ~DynamicFnBase(); + bool isValid() const {return fnPtr != 0;} + protected: + void* fnPtr; + HMODULE dllHandle; + private: + DynamicFnBase(const DynamicFnBase&); + DynamicFnBase operator=(const DynamicFnBase&); + }; + + template class DynamicFn : public DynamicFnBase { + public: + DynamicFn(const TCHAR* dllName, const char* fnName) : DynamicFnBase(dllName, fnName) {} + T operator *() const {return (T)fnPtr;}; + }; + + }; +}; + +#endif diff --git a/win/rfb_win32/EventManager.cxx b/win/rfb_win32/EventManager.cxx new file mode 100644 index 00000000..0f9993b7 --- /dev/null +++ b/win/rfb_win32/EventManager.cxx @@ -0,0 +1,103 @@ +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * + * This 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 software 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 software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +#include +#include +#include + +using namespace rfb; +using namespace rfb::win32; + +static LogWriter vlog("EventManager"); + + +EventManager::EventManager() : eventCount(0) { +} + +EventManager::~EventManager() { +} + + +bool EventManager::addEvent(HANDLE event, EventHandler* ecb) { + if (eventCount >= MAXIMUM_WAIT_OBJECTS-1) + return false; + events[eventCount] = event; + handlers[eventCount] = ecb; + eventCount++; + return true; +} + +void EventManager::removeEvent(HANDLE event) { + for (int i=0; imessage != WM_QUIT; + + // - Block waiting for an event to be set, or a message + result = MsgWaitForMultipleObjects(eventCount, events, FALSE, timeout, + QS_ALLINPUT); + if (result == WAIT_OBJECT_0 + eventCount) { + // - Return the message, if any + if (PeekMessage(msg, hwnd, minMsg, maxMsg, PM_REMOVE)) + return msg->message != WM_QUIT; + continue; + } + } + } else + return GetMessage(msg, hwnd, minMsg, maxMsg); + + if ((result >= WAIT_OBJECT_0) && (result < (WAIT_OBJECT_0 + eventCount))) { + // - An event was set - call the handler + int index = result - WAIT_OBJECT_0; + handlers[index]->processEvent(events[index]); + } else if (result == WAIT_FAILED) { + // - An error has occurred, so return the error status code + return -1; + } + } +} diff --git a/win/rfb_win32/EventManager.h b/win/rfb_win32/EventManager.h new file mode 100644 index 00000000..bb66e340 --- /dev/null +++ b/win/rfb_win32/EventManager.h @@ -0,0 +1,77 @@ +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * + * This 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 software 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 software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +// -=- EventManager.h + +// Win32 event manager. Caller supplies event & handler pairs and +// then uses getMessage() in place of ::GetMessage() in the main +// loop. EventManager calls the event handler whenever the event +// is set. +// Ownership of events remains with the caller. +// It is the responsibility of handlers to reset events. + +#ifndef __RFB_WIN32_EVENT_MGR_H__ +#define __RFB_WIN32_EVENT_MGR_H__ + +#include + +namespace rfb { + namespace win32 { + + class EventHandler { + public: + virtual ~EventHandler() {} + virtual void processEvent(HANDLE event) = 0; + }; + + class EventManager { + public: + EventManager(); + virtual ~EventManager(); + + // Add a Win32 event & handler for it + // NB: The handler must call ResetEvent on the event. + // NB: The caller retains ownership of the event. + virtual bool addEvent(HANDLE event, EventHandler* ecb); + + // Remove a Win32 event + virtual void removeEvent(HANDLE event); + + // getMessage + // Waits for a message to become available on the thread's message queue, + // and returns it. If any registered events become set while waiting then + // their handlers are called before returning. + // Returns zero if the message is WM_QUIT, -1 in case of error, >0 otherwise. + virtual BOOL getMessage(MSG* msg, HWND hwnd, UINT minMsg, UINT maxMsg); + + protected: + // checkTimeouts + // Derived classes should override this to perform any extra processing, + // returning the maximum number of milliseconds after which the callback + // should be called again. + virtual int checkTimeouts(); + + HANDLE events[MAXIMUM_WAIT_OBJECTS]; + EventHandler* handlers[MAXIMUM_WAIT_OBJECTS-1]; + int eventCount; + }; + + }; +}; + +#endif diff --git a/win/rfb_win32/FolderManager.cxx b/win/rfb_win32/FolderManager.cxx new file mode 100644 index 00000000..a2fa08d3 --- /dev/null +++ b/win/rfb_win32/FolderManager.cxx @@ -0,0 +1,279 @@ +/* Copyright (C) 2005 TightVNC Team. All Rights Reserved. + * + * Developed by Dennis Syrovatsky. + * + * This 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 software 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 software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + * + * TightVNC distribution homepage on the Web: http://www.tightvnc.com/ + * + */ + +// -=- FolderManager.cxx + +#include + +using namespace rfb; +using namespace rfb::win32; + +FolderManager::FolderManager() +{ + +} + +FolderManager::~FolderManager() +{ + +} + +bool +FolderManager::createDir(char *pFullPath) +{ + if (CreateDirectory(pFullPath, NULL) == 0) return false; + + return true; +} + +bool +FolderManager::renameIt(char *pPath, char *pOldName, char *pNewName) +{ + char fullOldName[FT_FILENAME_SIZE]; + char fullNewName[FT_FILENAME_SIZE]; + + sprintf(fullOldName, "%s\\%s", pPath, pOldName); + sprintf(fullNewName, "%s\\%s", pPath, pNewName); + + return renameIt(fullOldName, fullNewName); +} + +bool +FolderManager::renameIt(char *pOldName, char *pNewName) +{ + if (MoveFile(pOldName, pNewName)) return true; + + return false; +} + +bool +FolderManager::deleteIt(char *pFullPath) +{ + FileInfo fileInfo; + + FILEINFO FIStruct; + if (!getInfo(pFullPath, &FIStruct)) return false; + + fileInfo.add(&FIStruct); + + return deleteIt(&fileInfo); +} + +bool +FolderManager::deleteIt(char *pPrefix, FileInfo *pFI) +{ + char buf[FT_FILENAME_SIZE]; + for (unsigned int i = 0; i < pFI->getNumEntries(); i++) { + sprintf(buf, "%s\\%s", pPrefix, pFI->getNameAt(i)); + pFI->setNameAt(i,buf); + } + return deleteIt(pFI); +} + +bool +FolderManager::deleteIt(FileInfo *pFI) +{ + unsigned int num = pFI->getNumEntries(); + unsigned int last = num - 1; + + while (num > 0) { + if (pFI->getFlagsAt(last) & FT_ATTR_DIR) { + if (RemoveDirectory(pFI->getNameAt(last)) == 0) { + if (GetLastError() == ERROR_DIR_NOT_EMPTY) { + if (!getFolderInfoWithPrefix(pFI->getNameAt(last), pFI)) { + pFI->free(); + return false; + } + } + } else { + pFI->deleteAt(last); + } + } else { + if (DeleteFile(pFI->getNameAt(last)) == 0) { + pFI->free(); + return false; + } else { + pFI->deleteAt(last); + } + } + num = pFI->getNumEntries(); + last = num - 1; + } + + return true; +} + +bool +FolderManager::getFolderInfoWithPrefix(char *pPrefix, FileInfo *pFileInfo) +{ + char prefix[FT_FILENAME_SIZE]; + strcpy(prefix, pPrefix); + + FileInfo tmpFileInfo; + if (!getDirInfo(prefix, &tmpFileInfo, 0)) { + tmpFileInfo.free(); + return false; + } else { + char buf[FT_FILENAME_SIZE]; + for (unsigned int i = 0; i < tmpFileInfo.getNumEntries(); i++) { + sprintf(buf, "%s\\%s", prefix, tmpFileInfo.getNameAt(i)); + pFileInfo->add(buf, tmpFileInfo.getSizeAt(i), tmpFileInfo.getDataAt(i), tmpFileInfo.getFlagsAt(i)); + } + } + tmpFileInfo.free(); + return true; +} + +bool +FolderManager::getDirInfo(char *pPath, FileInfo *pFileInfo, unsigned int dirOnly) +{ + if (strlen(pPath) == 0) return getDrivesInfo(pFileInfo); + + char path[FT_FILENAME_SIZE]; + sprintf(path, "%s\\*", pPath); + + WIN32_FIND_DATA FindFileData; + SetErrorMode(SEM_FAILCRITICALERRORS); + HANDLE handle = FindFirstFile(path, &FindFileData); + DWORD lastError = GetLastError(); + SetErrorMode(0); + + if (handle != INVALID_HANDLE_VALUE) { + do { + if (strcmp(FindFileData.cFileName, ".") != 0 && + strcmp(FindFileData.cFileName, "..") != 0) { + unsigned int lastWriteTime = getTime70(FindFileData.ftLastWriteTime); + if ((FindFileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) { + pFileInfo->add(FindFileData.cFileName, 0, lastWriteTime, FT_ATTR_DIR); + } else { + if (!dirOnly) + pFileInfo->add(FindFileData.cFileName, FindFileData.nFileSizeLow, lastWriteTime, FT_ATTR_FILE); + } + } + + } while (FindNextFile(handle, &FindFileData)); + } else { + return false; + } + FindClose(handle); + return true; +} + +bool +FolderManager::getDrivesInfo(FileInfo *pFileInfo) +{ + TCHAR szDrivesList[256]; + if (GetLogicalDriveStrings(255, szDrivesList) == 0) + return false; + + int i = 0; + while (szDrivesList[i] != '\0') { + char *drive = strdup(&szDrivesList[i]); + char *backslash = strrchr(drive, '\\'); + if (backslash != NULL) + *backslash = '\0'; + pFileInfo->add(drive, 0, 0, FT_ATTR_DIR); + free(drive); + i += strcspn(&szDrivesList[i], "\0") + 1; + } + return true; +} + +bool +FolderManager::getInfo(char *pFullPath, FILEINFO *pFIStruct) +{ + WIN32_FIND_DATA FindFileData; + SetErrorMode(SEM_FAILCRITICALERRORS); + HANDLE hFile = FindFirstFile(pFullPath, &FindFileData); + DWORD lastError = GetLastError(); + SetErrorMode(0); + if (hFile != INVALID_HANDLE_VALUE) { + FindClose(hFile); + strcpy(pFIStruct->name, pFullPath); + pFIStruct->info.data = getTime70(FindFileData.ftLastWriteTime); + if (FindFileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) { + pFIStruct->info.size = 0; + pFIStruct->info.flags = FT_ATTR_DIR; + return true; + } else { + pFIStruct->info.size = FindFileData.nFileSizeLow; + pFIStruct->info.flags = FT_ATTR_FILE; + return true; + } + } + return false; +} + +unsigned int +FolderManager::getTime70(FILETIME ftime) +{ + LARGE_INTEGER uli; + uli.LowPart = ftime.dwLowDateTime; + uli.HighPart = ftime.dwHighDateTime; + uli.QuadPart = (uli.QuadPart - 116444736000000000) / 10000000; + return uli.LowPart; +} + +void +FolderManager::getFiletime(unsigned int time70, FILETIME *pftime) +{ + LONGLONG ll = Int32x32To64(time70, 10000000) + 116444736000000000; + pftime->dwLowDateTime = (DWORD) ll; + pftime->dwHighDateTime = (DWORD) (ll >> 32); +} + +bool +FolderManager::getDirSize(char *pFullPath, DWORD64 *dirSize) +{ + char fullPath[FT_FILENAME_SIZE]; + FileInfo fi; + fi.add(pFullPath, 0, 0, FT_ATTR_DIR); + DWORD64 dirFileSize64 = 0; + do { + sprintf(fullPath, "%s\\*", fi.getNameAt(0)); + WIN32_FIND_DATA FindFileData; + SetErrorMode(SEM_FAILCRITICALERRORS); + HANDLE hFile = FindFirstFile(fullPath, &FindFileData); + SetErrorMode(0); + + if (hFile != INVALID_HANDLE_VALUE) { + do { + if (strcmp(FindFileData.cFileName, ".") != 0 && + strcmp(FindFileData.cFileName, "..") != 0) { + char buff[MAX_PATH]; + if ((FindFileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) { + sprintf(buff, "%s\\%s", fi.getNameAt(0), FindFileData.cFileName); + fi.add(buff, 0, 0, FT_ATTR_DIR); + } else { + dirFileSize64 += FindFileData.nFileSizeLow; + } + } + } while (FindNextFile(hFile, &FindFileData)); + FindClose(hFile); + } + fi.deleteAt(0); + } while (fi.getNumEntries() > 0); + + *dirSize = dirFileSize64; + return true; +} \ No newline at end of file diff --git a/win/rfb_win32/FolderManager.h b/win/rfb_win32/FolderManager.h new file mode 100644 index 00000000..24923dd6 --- /dev/null +++ b/win/rfb_win32/FolderManager.h @@ -0,0 +1,66 @@ +/* Copyright (C) 2005 TightVNC Team. All Rights Reserved. + * + * Developed by Dennis Syrovatsky. + * + * This 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 software 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 software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + * + * TightVNC distribution homepage on the Web: http://www.tightvnc.com/ + * + */ + +// -=- FolderManager.h + +#ifndef __RFB_WIN32_FOLDERMANAGER_H__ +#define __RFB_WIN32_FOLDERMANAGER_H__ + +#include + +#include +#include + +namespace rfb { + namespace win32{ + class FolderManager : public DirManager { + public: + FolderManager(); + ~FolderManager(); + + bool createDir(char *pFullPath); + + bool renameIt(char *pOldName, char *pNewName); + bool renameIt(char *pPath, char *pOldName, char *pNewName); + + bool deleteIt(char *pPrefix, FileInfo *pFI); + bool deleteIt(char *pFullPath); + bool deleteIt(FileInfo *pFI); + + bool getInfo(char *pFullPath, FILEINFO *pFIStruct); + + bool getDirInfo(char *pPath, FileInfo *pFileInfo, unsigned int dirOnly); + bool getDrivesInfo(FileInfo *pFI); + + unsigned int getTime70(FILETIME ftime); + void getFiletime(unsigned int time70, FILETIME *pftime); + + bool getDirSize(char *pFullPath, DWORD64 *dirSize); + + private: + bool getFolderInfoWithPrefix(char *pPrefix, FileInfo *pFileInfo); + }; + } +} + +#endif // __RFB_WIN32_FOLDERMANAGER_H__ \ No newline at end of file diff --git a/win/rfb_win32/Handle.h b/win/rfb_win32/Handle.h new file mode 100644 index 00000000..d3baa580 --- /dev/null +++ b/win/rfb_win32/Handle.h @@ -0,0 +1,43 @@ +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * + * This 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 software 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 software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +// Wrapper for Win32 HANDLEs that can/must be CloseHandle()d. + +#ifndef __RFB_WIN32_HANDLE_H__ +#define __RFB_WIN32_HANDLE_H__ + +#include + +namespace rfb { + namespace win32 { + + + class Handle { + public: + Handle(HANDLE h_=0) : h(h_) {} + ~Handle() { + if (h) CloseHandle(h); + } + operator HANDLE() {return h;} + HANDLE h; + }; + + }; +}; + +#endif diff --git a/win/rfb_win32/IconInfo.h b/win/rfb_win32/IconInfo.h new file mode 100644 index 00000000..cb33a42d --- /dev/null +++ b/win/rfb_win32/IconInfo.h @@ -0,0 +1,44 @@ +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * + * This 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 software 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 software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +#ifndef __RFB_WIN32_ICONINFO_H__ +#define __RFB_WIN32_ICONINFO_H__ + +#include +#include + +namespace rfb { + namespace win32 { + + struct IconInfo : public ICONINFO { + IconInfo(HICON icon) { + if (!GetIconInfo(icon, this)) + throw rdr::SystemException("GetIconInfo() failed", GetLastError()); + } + ~IconInfo() { + if (hbmColor) + DeleteObject(hbmColor); + if (hbmMask) + DeleteObject(hbmMask); + } + }; + + }; +}; + +#endif diff --git a/win/rfb_win32/IntervalTimer.h b/win/rfb_win32/IntervalTimer.h new file mode 100644 index 00000000..ddfae493 --- /dev/null +++ b/win/rfb_win32/IntervalTimer.h @@ -0,0 +1,70 @@ +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * + * This 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 software 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 software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +// -=- IntervalTimer.h +// +// Simple wrapper for standard Win32 timers + +#ifndef __RFB_WIN32_INTERVAL_TIMER_H__ +#define __RFB_WIN32_INTERVAL_TIMER_H__ + +namespace rfb { + + namespace win32 { + + struct IntervalTimer { + IntervalTimer(HWND hwnd_, int id_) + : active(false), hwnd(hwnd_), id(id_) { + } + IntervalTimer() : active(false), hwnd(0), id(0) { + } + ~IntervalTimer() { + stop(); + } + + void start(int interval_) { + if (!active || interval_ != interval) { + interval = interval_; + if (!SetTimer(hwnd, id, interval, 0)) + throw rdr::SystemException("SetTimer", GetLastError()); + active = true; + } + } + void stop() { + if (active) + KillTimer(hwnd, id); + active = false; + } + + void setHWND(HWND hwnd_) {hwnd=hwnd_;} + void setId(int id_) {id = id_;} + int getId() const {return id;} + bool isActive() const {return active;} + + private: + HWND hwnd; + int id; + bool active; + int interval; + }; + + }; // win32 + +}; // rfb + +#endif // __RFB_WIN32_INTERVAL_TIMER_H__ diff --git a/win/rfb_win32/LaunchProcess.cxx b/win/rfb_win32/LaunchProcess.cxx new file mode 100644 index 00000000..56a712e6 --- /dev/null +++ b/win/rfb_win32/LaunchProcess.cxx @@ -0,0 +1,103 @@ +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * + * This 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 software 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 software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +// -=- LaunchProcess.cxx + +#include +#include +#include +#include +#include + +using namespace rfb; +using namespace win32; + + +LaunchProcess::LaunchProcess(const TCHAR* exeName_, const TCHAR* params_) +: exeName(tstrDup(exeName_)), params(tstrDup(params_)) { + memset(&procInfo, 0, sizeof(procInfo)); +} + +LaunchProcess::~LaunchProcess() { + await(); +} + + +void LaunchProcess::start(HANDLE userToken, bool createConsole) { + if (procInfo.hProcess && (WaitForSingleObject(procInfo.hProcess, 0) != WAIT_OBJECT_0)) + return; + await(); + returnCode = STILL_ACTIVE; + + // - Create storage for the process startup information + STARTUPINFO sinfo; + memset(&sinfo, 0, sizeof(sinfo)); + sinfo.cb = sizeof(sinfo); + + // - Concoct a suitable command-line + TCharArray exePath; + if (!tstrContains(exeName.buf, _T('\\'))) { + ModuleFileName filename; + TCharArray path; splitPath(filename.buf, &path.buf, 0); + exePath.buf = new TCHAR[_tcslen(path.buf) + _tcslen(exeName.buf) + 2]; + _stprintf(exePath.buf, _T("%s\\%s"), path.buf, exeName.buf); + } else { + exePath.buf = tstrDup(exeName.buf); + } + + // - Start the process + // Note: We specify the exe's precise path in the ApplicationName parameter, + // AND include the name as the first part of the CommandLine parameter, + // because CreateProcess doesn't make ApplicationName argv[0] in C programs. + TCharArray cmdLine(_tcslen(exeName.buf) + 3 + _tcslen(params.buf) + 1); + _stprintf(cmdLine.buf, _T("\"%s\" %s"), exeName.buf, params.buf); + DWORD flags = createConsole ? CREATE_NEW_CONSOLE : CREATE_NO_WINDOW; + BOOL success; + if (userToken != INVALID_HANDLE_VALUE) + success = CreateProcessAsUser(userToken, exePath.buf, cmdLine.buf, 0, 0, FALSE, flags, 0, 0, &sinfo, &procInfo); + else + success = CreateProcess(exePath.buf, cmdLine.buf, 0, 0, FALSE, flags, 0, 0, &sinfo, &procInfo); + if (!success) + throw rdr::SystemException("unable to launch process", GetLastError()); + + // Wait for it to finish initialising + WaitForInputIdle(procInfo.hProcess, 15000); +} + +void LaunchProcess::detach() +{ + if (!procInfo.hProcess) + return; + CloseHandle(procInfo.hProcess); + CloseHandle(procInfo.hThread); + memset(&procInfo, 0, sizeof(procInfo)); +} + +bool LaunchProcess::await(DWORD timeoutMs) { + if (!procInfo.hProcess) + return true; + DWORD result = WaitForSingleObject(procInfo.hProcess, timeoutMs); + if (result == WAIT_OBJECT_0) { + GetExitCodeProcess(procInfo.hProcess, &returnCode); + detach(); + return true; + } else if (result == WAIT_FAILED) { + throw rdr::SystemException("await() failed", GetLastError()); + } + return false; +} diff --git a/win/rfb_win32/LaunchProcess.h b/win/rfb_win32/LaunchProcess.h new file mode 100644 index 00000000..38521dcd --- /dev/null +++ b/win/rfb_win32/LaunchProcess.h @@ -0,0 +1,71 @@ +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * + * This 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 software 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 software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +// -=- LaunchProcess.h + +// Helper class to launch a names process from the same directory as +// the current process executable resides in. + +#ifndef __RFB_WIN32_LAUNCHPROCESS_H__ +#define __RFB_WIN32_LAUNCHPROCESS_H__ + +#include +#include + +namespace rfb { + + namespace win32 { + + class LaunchProcess { + public: + LaunchProcess(const TCHAR* exeName_, const TCHAR* params); + ~LaunchProcess(); + + // start() starts the specified process with the supplied + // command-line. + // If userToken is INVALID_HANDLE_VALUE then starts the process + // as the current user, otherwise as the specified user. + // If createConsole is true then CREATE_CONSOLE_WINDOW is passed + // as an extra flag to the process creation call. + void start(HANDLE userToken, bool createConsole=false); + + // Detatch from the child process. After detatching from a child + // process, no other methods should be called on the object + // that started it + void detach(); + + // Wait for the process to quit, up to the specified timeout, and + // close the handles to it once it has quit. + // If the process quits within the timeout then true is returned + // and returnCode is set. If it has not quit then false is returned. + // If an error occurs then an exception will be thrown. + bool await(DWORD timeoutMs=INFINITE); + + PROCESS_INFORMATION procInfo; + DWORD returnCode; + protected: + TCharArray exeName; + TCharArray params; + }; + + + }; + +}; + +#endif diff --git a/win/rfb_win32/ListViewControl.cxx b/win/rfb_win32/ListViewControl.cxx new file mode 100644 index 00000000..12e04003 --- /dev/null +++ b/win/rfb_win32/ListViewControl.cxx @@ -0,0 +1,103 @@ +// ListViewControl.cxx: implementation of the ListViewControl class. +// +////////////////////////////////////////////////////////////////////// +#include +#include "ListViewControl.h" +#include "commctrl.h" +////////////////////////////////////////////////////////////////////// +// Construction/Destruction +////////////////////////////////////////////////////////////////////// +using namespace rfb; +using namespace rfb::win32; + +ListViewControl::ListViewControl() +{ +} + +bool ListViewControl::IsSelectedLVItem(DWORD idListView, + HWND hDlg, int numberItem) +{ + return (ListView_GetItemState(GetDlgItem(hDlg, idListView), + numberItem, LVIS_SELECTED) == LVIS_SELECTED); +} + +void ListViewControl::SelectLVItem(DWORD idListView, HWND hDlg, int numberItem) +{ + ListView_SetItemState(GetDlgItem(hDlg, idListView), + numberItem, LVIS_SELECTED, LVIS_SELECTED); +} + +BOOL ListViewControl::InitLVColumns(DWORD idListView, HWND hDlg, int width, int columns, + TCHAR *title[], DWORD mask, DWORD LVStyle, DWORD format) +{ + ListView_SetExtendedListViewStyle(GetDlgItem(hDlg, idListView), LVStyle); + TCHAR szText[256]; + LVCOLUMN lvc; + int iCol; + + lvc.mask = mask; + + for (iCol = 0; iCol < columns; iCol++) { + lvc.iSubItem = iCol; + lvc.pszText = szText; + lvc.cx = width; + lvc.fmt = format; + + _tcscpy(szText, title[iCol]); + if (ListView_InsertColumn(GetDlgItem(hDlg, idListView), iCol, &lvc) == -1) + return FALSE; + } + return TRUE; +} + +BOOL ListViewControl::InsertLVItem(DWORD idListView, HWND hDlg, int number, TCHAR * texts[], + int columns) +{ + int i; + LVITEM lvI; + lvI.mask = LVIF_TEXT| LVIF_STATE; + lvI.state = 0; + lvI.stateMask = 0; + lvI.iItem = number; + lvI.iSubItem = 0; + lvI.pszText = texts[0]; + + if(ListView_InsertItem(GetDlgItem(hDlg, idListView), &lvI) == -1) + return NULL; + + for (i =1; i < columns; i++) { + SetLVItemText( + idListView, hDlg, + number, i, texts[i]); + } + return TRUE; +} + +void ListViewControl::SetLVItemText(DWORD idListView, HWND hDlg, int numberItem, + int namberColumn, TCHAR * text) +{ + ListView_SetItemText( + GetDlgItem(hDlg, idListView), + numberItem, namberColumn, text); +} + +void ListViewControl::GetLVItemText(DWORD idListView, HWND hDlg, int numberItem, + int namberColumn, TCHAR * text) +{ + ListView_GetItemText(GetDlgItem(hDlg, idListView), numberItem, + namberColumn, text, 256); +} + +void ListViewControl::DeleteLVItem(DWORD idListView, HWND hDlg, int number) +{ + ListView_DeleteItem(GetDlgItem(hDlg, idListView), number); +} + +void ListViewControl::DeleteAllLVItem(DWORD idListView, HWND hDlg) +{ + ListView_DeleteAllItems(GetDlgItem(hDlg, idListView)); +} + +ListViewControl::~ListViewControl() +{ +} diff --git a/win/rfb_win32/ListViewControl.h b/win/rfb_win32/ListViewControl.h new file mode 100644 index 00000000..8a163738 --- /dev/null +++ b/win/rfb_win32/ListViewControl.h @@ -0,0 +1,35 @@ +// ListViewControl.h: interface for the ListViewControl class. +// +////////////////////////////////////////////////////////////////////// + +#ifndef AFX_LISTVIEWCONTROL_H__ +#define AFX_LISTVIEWCONTROL_H__ + +#include +#include "commctrl.h" + +namespace rfb { + + namespace win32 { + class ListViewControl + { + public: + ListViewControl(); + bool IsSelectedLVItem(DWORD idListView, HWND hDlg, int numberItem); + void SelectLVItem(DWORD idListView, HWND hDlg, int numberItem); + BOOL InitLVColumns(DWORD idListView, HWND hDlg, int width, int columns, + TCHAR * title[], DWORD mask, DWORD style, DWORD format); + BOOL InsertLVItem(DWORD idListView, HWND hDlg, int number, TCHAR * texts[], + int columns); + void SetLVItemText(DWORD idListView, HWND hDlg, int numberItem, + int namberColumn, TCHAR * text); + void GetLVItemText(DWORD idListView, HWND hDlg, int numberItem, + int namberColumn, TCHAR * text); + void DeleteLVItem(DWORD idListView, HWND hDlg, int number); + void DeleteAllLVItem(DWORD idListView, HWND hDlg); + virtual ~ListViewControl(); + }; + }; +}; + +#endif; \ No newline at end of file diff --git a/win/rfb_win32/LocalMem.h b/win/rfb_win32/LocalMem.h new file mode 100644 index 00000000..a99d3241 --- /dev/null +++ b/win/rfb_win32/LocalMem.h @@ -0,0 +1,45 @@ +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * + * This 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 software 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 software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +#ifndef __RFB_WIN32_LOCALMEM_H__ +#define __RFB_WIN32_LOCALMEM_H__ + +#include +#include + +namespace rfb { + namespace win32 { + + // Allocate and/or manage LocalAlloc memory. + struct LocalMem { + LocalMem(int size) : ptr(LocalAlloc(LMEM_FIXED, size)) { + if (!ptr) throw rdr::SystemException("LocalAlloc", GetLastError()); + } + LocalMem(void* p) : ptr(p) {} + ~LocalMem() {LocalFree(ptr);} + operator void*() {return ptr;} + void* takePtr() { + void* t = ptr; ptr = 0; return t; + } + void* ptr; + }; + + }; +}; + +#endif diff --git a/win/rfb_win32/LogicalPalette.h b/win/rfb_win32/LogicalPalette.h new file mode 100644 index 00000000..204f1081 --- /dev/null +++ b/win/rfb_win32/LogicalPalette.h @@ -0,0 +1,90 @@ +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * + * This 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 software 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 software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +#ifndef __RFB_WIN32_LOGPALETTE_H__ +#define __RFB_WIN32_LOGPALETTE_H__ + +#include +#include + +namespace rfb { + namespace win32 { + + class LogicalPalette { + public: + LogicalPalette() { + BYTE buf[sizeof(LOGPALETTE)+256*sizeof(PALETTEENTRY)]; + LOGPALETTE* logpal = (LOGPALETTE*)buf; + logpal->palVersion = 0x300; + logpal->palNumEntries = 256; + for (int i=0; i<256;i++) { + logpal->palPalEntry[i].peRed = 0; + logpal->palPalEntry[i].peGreen = 0; + logpal->palPalEntry[i].peBlue = 0; + logpal->palPalEntry[i].peFlags = 0; + } + palette = CreatePalette(logpal); + if (!palette) + throw rdr::SystemException("failed to CreatePalette", GetLastError()); + } + ~LogicalPalette() { + if (palette && !DeleteObject(palette)) + throw rdr::SystemException("del palette failed", GetLastError()); + } + void setEntries(int start, int count, const Colour* cols) { + if (numEntries < count) { + ResizePalette(palette, start+count); + numEntries = start+count; + } + PALETTEENTRY* logpal = new PALETTEENTRY[count]; + for (int i=0; i> 8; + logpal[i].peGreen = cols[i].g >> 8; + logpal[i].peBlue = cols[i].b >> 8; + logpal[i].peFlags = 0; + } + UnrealizeObject(palette); + SetPaletteEntries(palette, start, count, logpal); + delete [] logpal; + } + HPALETTE getHandle() {return palette;} + protected: + HPALETTE palette; + int numEntries; + }; + + class PaletteSelector { + public: + PaletteSelector(HDC dc, HPALETTE pal) : device(dc), redrawRequired(false) { + oldPal = SelectPalette(dc, pal, FALSE); + redrawRequired = RealizePalette(dc) > 0; + } + ~PaletteSelector() { + if (oldPal) SelectPalette(device, oldPal, TRUE); + } + bool isRedrawRequired() {return redrawRequired;} + protected: + HPALETTE oldPal; + HDC device; + bool redrawRequired; + }; + + }; +}; + +#endif diff --git a/win/rfb_win32/LowLevelKeyEvents.cxx b/win/rfb_win32/LowLevelKeyEvents.cxx new file mode 100644 index 00000000..322d1f40 --- /dev/null +++ b/win/rfb_win32/LowLevelKeyEvents.cxx @@ -0,0 +1,96 @@ +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * + * This 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 software 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 software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +#include +#include +#include +#include +#include + +using namespace rfb; +using namespace win32; + +static LogWriter vlog("LowLevelKeyEvents"); + + +HHOOK hook = 0; +std::list windows; +Mutex windowLock; + + +static bool filterKeyEvent(int vkCode) { + switch (vkCode) { + case VK_LWIN: + case VK_RWIN: + case VK_SNAPSHOT: + return true; + case VK_TAB: + if (GetAsyncKeyState(VK_MENU) & 0x8000) + return true; + case VK_ESCAPE: + if (GetAsyncKeyState(VK_MENU) & 0x8000) + return true; + if (GetAsyncKeyState(VK_CONTROL) & 0x8000) + return true; + } + return false; +} + +LRESULT CALLBACK LowLevelKeyEventProc(int nCode, + WPARAM wParam, + LPARAM lParam) { + if (nCode >= 0) { + Lock l(windowLock); + HWND foreground = GetForegroundWindow(); + std::list::iterator i; + for (i=windows.begin(); i!=windows.end(); i++) { + if (*i == foreground) { + UINT msgType = wParam; + KBDLLHOOKSTRUCT* msgInfo = (KBDLLHOOKSTRUCT*)lParam; + if (filterKeyEvent(msgInfo->vkCode)) { + vlog.debug("filtered event %lx(%lu) %lu", msgInfo->vkCode, msgInfo->vkCode, wParam); + PostMessage(*i, wParam, msgInfo->vkCode, (msgInfo->scanCode & 0xff) << 16); + return 1; + } + } + } + } + return CallNextHookEx(hook, nCode, wParam, lParam); +} + + +bool rfb::win32::enableLowLevelKeyEvents(HWND hwnd) { +// *** return false; // *** THIS CODE IS EXPERIMENTAL, SO DISABLED BY DEFAULT! + Lock l(windowLock); + if (windows.empty() && !hook) + hook = SetWindowsHookEx(WH_KEYBOARD_LL, &LowLevelKeyEventProc, GetModuleHandle(0), 0); + if (hook) + windows.push_back(hwnd); + vlog.debug("enable %p -> %s", hwnd, hook ? "success" : "failure"); + return hook != 0; +} + +void rfb::win32::disableLowLevelKeyEvents(HWND hwnd) { + vlog.debug("disable %p", hwnd); + Lock l(windowLock); + windows.remove(hwnd); + if (windows.empty() && hook) { + UnhookWindowsHookEx(hook); + hook = 0; + } +} diff --git a/win/rfb_win32/LowLevelKeyEvents.h b/win/rfb_win32/LowLevelKeyEvents.h new file mode 100644 index 00000000..40d2ecfa --- /dev/null +++ b/win/rfb_win32/LowLevelKeyEvents.h @@ -0,0 +1,49 @@ +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * + * This 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 software 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 software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +// -=- LowLevelKeyEvents.h +// +// This interface allows keyboard events destined for a particular window +// to be intercepted early in the keyboard message queue and posted directly +// to the window. This is used to avoid having the operating system process +// keys such as VK_LWIN, VK_RWIN, etc. +// + +#ifndef __RFB_WIN32_LOW_LEVEL_KEY_EVENTS_H__ +#define __RFB_WIN32_LOW_LEVEL_KEY_EVENTS_H__ + +namespace rfb { + + namespace win32 { + + // enableLowLevelKeyEvents + // Specifies that keyboard events destined for the specified window should + // be posted directly to the window, rather than being passed via the normal + // Windows keyboard message queue. + bool enableLowLevelKeyEvents(HWND hwnd); + + // disableLowLevelKeyEvents + // Causes the specified window to revert to the normal Windows keyboard + // event processing mechanism. + void disableLowLevelKeyEvents(HWND hwnd); + + }; + +}; + +#endif // __RFB_WIN32_LOW_LEVEL_KEY_EVENTS_H__ diff --git a/win/rfb_win32/ModuleFileName.h b/win/rfb_win32/ModuleFileName.h new file mode 100644 index 00000000..2264e89d --- /dev/null +++ b/win/rfb_win32/ModuleFileName.h @@ -0,0 +1,40 @@ +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * + * This 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 software 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 software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +#ifndef __RFB_WIN32_MODULE_FILENAME_H__ +#define __RFB_WIN32_MODULE_FILENAME_H__ + +#include +#include + +namespace rfb { + namespace win32 { + + struct ModuleFileName : public TCharArray { + ModuleFileName(HMODULE module=0) : TCharArray(MAX_PATH) { + if (!module) + module = GetModuleHandle(0); + if (!GetModuleFileName(module, buf, MAX_PATH)) + buf[0] = 0; + } + }; + + }; +}; + +#endif diff --git a/win/rfb_win32/MonitorInfo.cxx b/win/rfb_win32/MonitorInfo.cxx new file mode 100644 index 00000000..03772e97 --- /dev/null +++ b/win/rfb_win32/MonitorInfo.cxx @@ -0,0 +1,205 @@ +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * + * This 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 software 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 software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +#include +#include +#include +#include +#include + +using namespace rfb; +using namespace win32; + +static LogWriter vlog("MonitorInfo"); + + +// If we are building in multi-monitor support (i.e. the headers support it) +// then do dynamic imports of the required system calls, and provide any +// other code that wouldn't otherwise compile. +#ifdef RFB_HAVE_MONITORINFO +#include +typedef HMONITOR (WINAPI *_MonitorFromWindow_proto)(HWND,DWORD); +static rfb::win32::DynamicFn<_MonitorFromWindow_proto> _MonitorFromWindow(_T("user32.dll"), "MonitorFromWindow"); +typedef HMONITOR (WINAPI *_MonitorFromRect_proto)(LPCRECT,DWORD); +static rfb::win32::DynamicFn<_MonitorFromRect_proto> _MonitorFromRect(_T("user32.dll"), "MonitorFromRect"); +typedef BOOL (WINAPI *_GetMonitorInfo_proto)(HMONITOR,LPMONITORINFO); +static rfb::win32::DynamicFn<_GetMonitorInfo_proto> _GetMonitorInfo(_T("user32.dll"), "GetMonitorInfoA"); +typedef BOOL (WINAPI *_EnumDisplayMonitors_proto)(HDC, LPCRECT, MONITORENUMPROC, LPARAM); +static rfb::win32::DynamicFn<_EnumDisplayMonitors_proto> _EnumDisplayMonitors(_T("user32.dll"), "EnumDisplayMonitors"); +static void fillMonitorInfo(HMONITOR monitor, MonitorInfo* mi) { + vlog.debug("monitor=%lx", monitor); + if (!_GetMonitorInfo.isValid()) + throw rdr::Exception("no GetMonitorInfo"); + memset(mi, 0, sizeof(MONITORINFOEXA)); + mi->cbSize = sizeof(MONITORINFOEXA); + if (!(*_GetMonitorInfo)(monitor, mi)) + throw rdr::SystemException("failed to GetMonitorInfo", GetLastError()); + vlog.debug("monitor is %d,%d-%d,%d", mi->rcMonitor.left, mi->rcMonitor.top, mi->rcMonitor.right, mi->rcMonitor.bottom); + vlog.debug("work area is %d,%d-%d,%d", mi->rcWork.left, mi->rcWork.top, mi->rcWork.right, mi->rcWork.bottom); + vlog.debug("device is \"%s\"", mi->szDevice); +} +#else +#pragma message(" NOTE: Not building Multi-Monitor support.") +#endif + + +MonitorInfo::MonitorInfo(HWND window) { + cbSize = sizeof(MonitorInfo); + szDevice[0] = 0; + +#ifdef RFB_HAVE_MONITORINFO + try { + if (_MonitorFromWindow.isValid()) { + HMONITOR monitor = (*_MonitorFromWindow)(window, MONITOR_DEFAULTTONEAREST); + if (!monitor) + throw rdr::SystemException("failed to get monitor", GetLastError()); + fillMonitorInfo(monitor, this); + return; + } + } catch (rdr::Exception& e) { + vlog.error(e.str()); + } +#endif + + // Legacy fallbacks - just return the desktop settings + vlog.debug("using legacy fall-backs"); + HWND desktop = GetDesktopWindow(); + GetWindowRect(desktop, &rcMonitor); + SystemParametersInfo(SPI_GETWORKAREA, 0, &rcWork, 0); + dwFlags = 0; +} + +MonitorInfo::MonitorInfo(const RECT& r) { + cbSize = sizeof(MonitorInfo); + szDevice[0] = 0; + +#ifdef RFB_HAVE_MONITORINFO + try { + if (_MonitorFromRect.isValid()) { + HMONITOR monitor = (*_MonitorFromRect)(&r, MONITOR_DEFAULTTONEAREST); + if (!monitor) + throw rdr::SystemException("failed to get monitor", GetLastError()); + fillMonitorInfo(monitor, this); + return; + } + } catch (rdr::Exception& e) { + vlog.error(e.str()); + } +#endif + + // Legacy fallbacks - just return the desktop settings + vlog.debug("using legacy fall-backs"); + HWND desktop = GetDesktopWindow(); + GetWindowRect(desktop, &rcMonitor); + SystemParametersInfo(SPI_GETWORKAREA, 0, &rcWork, 0); + dwFlags = 0; +} + + +#ifdef RFB_HAVE_MONITORINFO + +struct monitorByNameData { + MonitorInfo* info; + const char* monitorName; +}; + +static BOOL CALLBACK monitorByNameEnumProc(HMONITOR monitor, + HDC dc, + LPRECT pos, + LPARAM d) { + monitorByNameData* data = (monitorByNameData*)d; + memset(data->info, 0, sizeof(MONITORINFOEXA)); + data->info->cbSize = sizeof(MONITORINFOEXA); + if ((*_GetMonitorInfo)(monitor, data->info)) { + if (stricmp(data->monitorName, data->info->szDevice) == 0) + return FALSE; + } + + return TRUE; +} + +#endif + +MonitorInfo::MonitorInfo(const char* devName) { +#ifdef RFB_HAVE_MONITORINFO + if (!_EnumDisplayMonitors.isValid()) { + vlog.debug("EnumDisplayMonitors not found"); + } else { + monitorByNameData data; + data.info = this; + data.monitorName = devName; + + (*_EnumDisplayMonitors)(0, 0, &monitorByNameEnumProc, (LPARAM)&data); + if (stricmp(data.monitorName, szDevice) == 0) + return; + } +#endif + // If multi-monitor is not built, or not supported by the OS, + // or if the named monitor is not found, revert to the primary monitor. + vlog.debug("reverting to primary monitor"); + cbSize = sizeof(MonitorInfo); + szDevice[0] = 0; + + HWND desktop = GetDesktopWindow(); + GetWindowRect(desktop, &rcMonitor); + SystemParametersInfo(SPI_GETWORKAREA, 0, &rcWork, 0); + dwFlags = 0; +} + +void MonitorInfo::moveTo(HWND handle) { + vlog.debug("moveTo monitor=%s", szDevice); + +#ifdef RFB_HAVE_MONITORINFO + MonitorInfo mi(handle); + if (strcmp(szDevice, mi.szDevice) != 0) { + centerWindow(handle, rcWork); + clipTo(handle); + } +#endif +} + +void MonitorInfo::clipTo(RECT* r) { + vlog.debug("clipTo monitor=%s", szDevice); + + if (r->top < rcWork.top) { + r->bottom += rcWork.top - r->top; r->top = rcWork.top; + } + if (r->left < rcWork.left) { + r->right += rcWork.left - r->left; r->left = rcWork.left; + } + if (r->bottom > rcWork.bottom) { + r->top += rcWork.bottom - r->bottom; r->bottom = rcWork.bottom; + } + if (r->right > rcWork.right) { + r->left += rcWork.right - r->right; r->right = rcWork.right; + } + r->left = max(r->left, rcWork.left); + r->right = min(r->right, rcWork.right); + r->top = max(r->top, rcWork.top); + r->bottom = min(r->bottom, rcWork.bottom); +} + +void MonitorInfo::clipTo(HWND handle) { + RECT r; + GetWindowRect(handle, &r); + clipTo(&r); + SetWindowPos(handle, 0, r.left, r.top, r.right-r.left, r.bottom-r.top, + SWP_NOZORDER | SWP_NOACTIVATE | SWP_NOOWNERZORDER); +} + + diff --git a/win/rfb_win32/MonitorInfo.h b/win/rfb_win32/MonitorInfo.h new file mode 100644 index 00000000..acf27755 --- /dev/null +++ b/win/rfb_win32/MonitorInfo.h @@ -0,0 +1,72 @@ +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * + * This 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 software 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 software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +// Helper class used to obtain information about a particular monitor. +// This class wraps the Windows MONITORINFOEX ASCII structure, providing +// methods that can safely be called on both multi-monitor aware systems +// and older "legacy" systems. + + +#ifndef __RFB_WIN32_MONITORINFO_H__ +#define __RFB_WIN32_MONITORINFO_H__ + +#include +#ifdef MONITOR_DEFAULTTONULL +#define RFB_HAVE_MONITORINFO +#endif + +namespace rfb { + namespace win32 { + + // Structure containing info on the monitor nearest the window. + // Copes with multi-monitor OSes and older ones. +#ifdef RFB_HAVE_MONITORINFO + struct MonitorInfo : MONITORINFOEXA { +#else + struct MonitorInfo { + DWORD cbSize; + RECT rcMonitor; + RECT rcWork; + DWORD dwFlags; + char szDevice[1]; // Always null... +#endif + + // Constructor: Obtains monitor info for the monitor that has the + // greatest overlap with the supplied window or rectangle. + MonitorInfo(HWND hwnd); + MonitorInfo(const RECT& r); + + // Constructor: Obtains monitor info for the name monitor. Monitor + // names should be those obtained from the MonitorInfo + // szDevice field, and usually look like "\\.\DISPLAY" + MonitorInfo(const char* devName); + + // Move the specified window to reside on the monitor. + void moveTo(HWND handle); + + // Clip the specified rectangle or window to the monitor's working area. + // The rectangle/window is moved so that as much as possible resides + // on the working area of the monitor, and is then intersected with it. + void clipTo(HWND handle); + void clipTo(RECT* r); + }; + + }; +}; + +#endif diff --git a/win/rfb_win32/MsgBox.h b/win/rfb_win32/MsgBox.h new file mode 100644 index 00000000..59571395 --- /dev/null +++ b/win/rfb_win32/MsgBox.h @@ -0,0 +1,63 @@ +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * + * This 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 software 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 software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +#ifndef __RFB_WIN32_MSGBOX_H__ +#define __RFB_WIN32_MSGBOX_H__ + +#include +#include + +namespace rfb { + namespace win32 { + + // Define rfb::win32::AppName somewhere in the application. + // The MsgBox function will use the specified application name + // as the prefix for the message box title. + // Message box titles are based on the (standard Win32) flags + // passed to the MsgBox helper function. + + extern TStr AppName; + + // Wrapper around Win32 MessageBox() + static int MsgBox(HWND parent, const TCHAR* msg, UINT flags) { + const TCHAR* msgType = 0; + UINT tflags = flags & 0x70; + if (tflags == MB_ICONHAND) + msgType = _T("Error"); + else if (tflags == MB_ICONQUESTION) + msgType = _T("Question"); + else if (tflags == MB_ICONEXCLAMATION) + msgType = _T("Warning"); + else if (tflags == MB_ICONASTERISK) + msgType = _T("Information"); + flags |= MB_TOPMOST | MB_SETFOREGROUND; + int len = _tcslen(AppName.buf) + 1; + if (msgType) len += _tcslen(msgType) + 3; + TCharArray title = new TCHAR[len]; + _tcscpy(title.buf, AppName.buf); + if (msgType) { + _tcscat(title.buf, _T(" : ")); + _tcscat(title.buf, msgType); + } + return MessageBox(parent, msg, title.buf, flags); + } + + }; +}; + +#endif diff --git a/win/rfb_win32/MsgWindow.cxx b/win/rfb_win32/MsgWindow.cxx new file mode 100644 index 00000000..95bd5237 --- /dev/null +++ b/win/rfb_win32/MsgWindow.cxx @@ -0,0 +1,116 @@ +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * + * This 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 software 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 software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +// -=- MsgWindow.cxx + +#include +#include +#include +#include +#include +#include + +using namespace rfb; +using namespace rfb::win32; + +static LogWriter vlog("MsgWindow"); + +// +// -=- MsgWindowClass +// + +class MsgWindowClass { +public: + MsgWindowClass(); + ~MsgWindowClass(); + ATOM classAtom; + HINSTANCE instance; +}; + +LRESULT CALLBACK MsgWindowProc(HWND wnd, UINT msg, WPARAM wParam, LPARAM lParam) { + LRESULT result; + + if (msg == WM_CREATE) + SetWindowLong(wnd, GWL_USERDATA, (long)((CREATESTRUCT*)lParam)->lpCreateParams); + else if (msg == WM_DESTROY) + SetWindowLong(wnd, GWL_USERDATA, 0); + MsgWindow* _this = (MsgWindow*) GetWindowLong(wnd, GWL_USERDATA); + if (!_this) { + vlog.info("null _this in %x, message %x", wnd, msg); + return SafeDefWindowProc(wnd, msg, wParam, lParam); + } + + try { + result = _this->processMessage(msg, wParam, lParam); + } catch (rdr::Exception& e) { + vlog.error("untrapped: %s", e.str()); + } + + return result; +}; + +MsgWindowClass::MsgWindowClass() : classAtom(0) { + WNDCLASS wndClass; + wndClass.style = 0; + wndClass.lpfnWndProc = MsgWindowProc; + wndClass.cbClsExtra = 0; + wndClass.cbWndExtra = 0; + wndClass.hInstance = instance = GetModuleHandle(0); + wndClass.hIcon = 0; + wndClass.hCursor = 0; + wndClass.hbrBackground = 0; + wndClass.lpszMenuName = 0; + wndClass.lpszClassName = _T("rfb::win32::MsgWindowClass"); + classAtom = RegisterClass(&wndClass); + if (!classAtom) { + throw rdr::SystemException("unable to register MsgWindow window class", GetLastError()); + } +} + +MsgWindowClass::~MsgWindowClass() { + if (classAtom) { + UnregisterClass((const TCHAR*)classAtom, instance); + } +} + +MsgWindowClass baseClass; + +// +// -=- MsgWindow +// + +MsgWindow::MsgWindow(const TCHAR* name_) : name(tstrDup(name_)), handle(0) { + vlog.debug("creating window \"%s\"", (const char*)CStr(name.buf)); + handle = CreateWindow((const TCHAR*)baseClass.classAtom, name.buf, WS_OVERLAPPED, + 0, 0, 10, 10, 0, 0, baseClass.instance, this); + if (!handle) { + throw rdr::SystemException("unable to create WMNotifier window instance", GetLastError()); + } + vlog.debug("created window \"%s\" (%x)", (const char*)CStr(name.buf), handle); +} + +MsgWindow::~MsgWindow() { + if (handle) + DestroyWindow(handle); + vlog.debug("destroyed window \"%s\" (%x)", (const char*)CStr(name.buf), handle); +} + +LRESULT +MsgWindow::processMessage(UINT msg, WPARAM wParam, LPARAM lParam) { + return SafeDefWindowProc(getHandle(), msg, wParam, lParam); +} diff --git a/win/rfb_win32/MsgWindow.h b/win/rfb_win32/MsgWindow.h new file mode 100644 index 00000000..92b6cf20 --- /dev/null +++ b/win/rfb_win32/MsgWindow.h @@ -0,0 +1,53 @@ +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * + * This 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 software 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 software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +// -=- MsgWindow.h + +// Base-class for any hidden message-handling windows used in the rfb::win32 +// implementation. + +#ifndef __RFB_WIN32_MSG_WINDOW_H__ +#define __RFB_WIN32_MSG_WINDOW_H__ + +#include +#include + +namespace rfb { + + namespace win32 { + + class MsgWindow { + public: + MsgWindow(const TCHAR* _name); + virtual ~MsgWindow(); + + const TCHAR* getName() {return name.buf;} + HWND getHandle() const {return handle;} + + virtual LRESULT processMessage(UINT msg, WPARAM wParam, LPARAM lParam); + + protected: + TCharArray name; + HWND handle; + }; + + }; + +}; + +#endif // __RFB_WIN32_MSG_WINDOW_H__ diff --git a/win/rfb_win32/OSVersion.cxx b/win/rfb_win32/OSVersion.cxx new file mode 100644 index 00000000..3d74c956 --- /dev/null +++ b/win/rfb_win32/OSVersion.cxx @@ -0,0 +1,47 @@ +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * + * This 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 software 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 software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +// -=- OSVersion.cxx + +#include +#include +#include + +using namespace rfb; +using namespace win32; + + +OSVersionInfo::OSVersionInfo() { + // Get OS Version Info + ZeroMemory(static_cast(this), sizeof(this)); + dwOSVersionInfoSize = sizeof(OSVERSIONINFO); + if (!GetVersionEx(this)) + throw rdr::SystemException("unable to get system version info", GetLastError()); + + // Set the special extra flags + isPlatformNT = dwPlatformId == VER_PLATFORM_WIN32_NT; + isPlatformWindows = dwPlatformId == VER_PLATFORM_WIN32_WINDOWS; + + cannotSwitchDesktop = isPlatformNT && (dwMajorVersion==4) && + ((_tcscmp(szCSDVersion, _T("")) == 0) || + (_tcscmp(szCSDVersion, _T("Service Pack 1")) == 0) || + (_tcscmp(szCSDVersion, _T("Service Pack 2")) == 0)); + +} + +OSVersionInfo rfb::win32::osVersion; diff --git a/win/rfb_win32/OSVersion.h b/win/rfb_win32/OSVersion.h new file mode 100644 index 00000000..18ec003e --- /dev/null +++ b/win/rfb_win32/OSVersion.h @@ -0,0 +1,53 @@ +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * + * This 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 software 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 software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +// -=- OSVersion.h + +// Operating system version info. +// GetVersionInfo is called once at process initialisation, and any +// extra flags (such as isWinNT) are calculated and saved at that +// point. It is assumed that the OS Version seldom changes during a +// program's execution... + +#ifndef __RFB_WIN32_OS_VERSION_H__ +#define __RFB_WIN32_OS_VERSION_H__ + +#include + +namespace rfb { + + namespace win32 { + + extern struct OSVersionInfo : OSVERSIONINFO { + OSVersionInfo(); + + // Is the OS one of the NT family (NT 3.51, NT4.0, 2K, XP, etc.)? + bool isPlatformNT; + // Is one of the Windows family? + bool isPlatformWindows; + + // Is this OS one of those that blue-screens when grabbing another desktop (NT4 pre SP3)? + bool cannotSwitchDesktop; + + } osVersion; + + }; + +}; + +#endif // __RFB_WIN32_OS_VERSION_H__ diff --git a/win/rfb_win32/ProgressControl.cxx b/win/rfb_win32/ProgressControl.cxx new file mode 100644 index 00000000..85bd15f3 --- /dev/null +++ b/win/rfb_win32/ProgressControl.cxx @@ -0,0 +1,97 @@ +/* Copyright (C) 2005 TightVNC Team. All Rights Reserved. + * + * This 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 software 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 software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + * + * TightVNC distribution homepage on the Web: http://www.tightvnc.com/ + * + */ + +// -=- ProgressControl.cxx + +#include + +using namespace rfb; +using namespace rfb::win32; + +#define MAX_RANGE 0xFFFF + +ProgressControl::ProgressControl(HWND hwndProgress) +{ + m_hwndProgress = hwndProgress; + + m_dw64MaxValue = 0; + m_dw64CurrentValue = 0; +} + +ProgressControl::~ProgressControl() +{ +} + +bool +ProgressControl::init(DWORD64 maxValue, DWORD64 position) +{ + if (m_dw64CurrentValue > m_dw64MaxValue) return false; + + m_dw64CurrentValue = position; + m_dw64MaxValue = maxValue; + + if (!SendMessage(m_hwndProgress, PBM_SETRANGE, (WPARAM) 0, MAKELPARAM(0, MAX_RANGE))) + return false; + + return true; +} + +bool +ProgressControl::clear() +{ + m_dw64CurrentValue = 0; + return show(); +} + +bool +ProgressControl::increase(DWORD64 value) +{ + if ((m_dw64MaxValue - m_dw64CurrentValue) > value) { + m_dw64CurrentValue += value; + } else { + m_dw64CurrentValue = m_dw64MaxValue; + } + return show(); +} + +bool +ProgressControl::show() +{ + DWORD curPos; + if (m_dw64MaxValue != 0) { + curPos = (DWORD) ((m_dw64CurrentValue * MAX_RANGE) / m_dw64MaxValue); + } else { + curPos = 0; + } + + if (!SendMessage(m_hwndProgress, PBM_SETPOS, (WPARAM) curPos, (LPARAM) 0)) + return false; + + return true; +} + +int +ProgressControl::getCurrentPercent() +{ + if (m_dw64MaxValue == 0) return 0; + + return ((int) ((m_dw64CurrentValue * 100) / m_dw64MaxValue)); +} \ No newline at end of file diff --git a/win/rfb_win32/ProgressControl.h b/win/rfb_win32/ProgressControl.h new file mode 100644 index 00000000..ceeb153f --- /dev/null +++ b/win/rfb_win32/ProgressControl.h @@ -0,0 +1,56 @@ +/* Copyright (C) 2005 TightVNC Team. All Rights Reserved. + * + * This 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 software 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 software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + * + * TightVNC distribution homepage on the Web: http://www.tightvnc.com/ + * + */ + +// -=- ProgressControl.h + +#ifndef __RFB_WIN32_PROGRESSCONTROL_H__ +#define __RFB_WIN32_PROGRESSCONTROL_H__ + +#include +#include + +namespace rfb { + namespace win32 { + class ProgressControl + { + public: + ProgressControl(HWND hwndProgress); + ~ProgressControl(); + + bool init(DWORD64 maxValue, DWORD64 position); + + bool increase(DWORD64 value); + bool clear(); + + int getCurrentPercent(); + + private: + HWND m_hwndProgress; + + DWORD64 m_dw64MaxValue; + DWORD64 m_dw64CurrentValue; + + bool show(); + }; + } +} + +#endif // __RFB_WIN32_PROGRESSCONTROL_H__ diff --git a/win/rfb_win32/RegConfig.cxx b/win/rfb_win32/RegConfig.cxx new file mode 100644 index 00000000..dd1c3b06 --- /dev/null +++ b/win/rfb_win32/RegConfig.cxx @@ -0,0 +1,114 @@ +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * + * This 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 software 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 software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +// -=- RegConfig.cxx + +#include + +#include +#include +#include +//#include + +using namespace rfb; +using namespace rfb::win32; + + +static LogWriter vlog("RegConfig"); + + +RegConfig::RegConfig(EventManager* em) : eventMgr(em), event(CreateEvent(0, TRUE, FALSE, 0)), callback(0) { + if (em->addEvent(event, this)) + eventMgr = em; +} + +RegConfig::~RegConfig() { + if (eventMgr) + eventMgr->removeEvent(event); +} + +bool RegConfig::setKey(const HKEY rootkey, const TCHAR* keyname) { + try { + key.createKey(rootkey, keyname); + processEvent(event); + return true; + } catch (rdr::Exception& e) { + vlog.debug(e.str()); + return false; + } +} + +void RegConfig::loadRegistryConfig(RegKey& key) { + DWORD i = 0; + try { + while (1) { + TCharArray name = tstrDup(key.getValueName(i++)); + if (!name.buf) break; + TCharArray value = key.getRepresentation(name.buf); + if (!value.buf || !Configuration::setParam(CStr(name.buf), CStr(value.buf))) + vlog.info("unable to process %s", CStr(name.buf)); + } + } catch (rdr::SystemException& e) { + if (e.err != 6) + vlog.error(e.str()); + } +} + +void RegConfig::processEvent(HANDLE event_) { + vlog.info("registry changed"); + + // Reinstate the registry change notifications + ResetEvent(event); + key.awaitChange(true, REG_NOTIFY_CHANGE_NAME | REG_NOTIFY_CHANGE_LAST_SET, event); + + // Load settings + loadRegistryConfig(key); + + // Notify the callback, if supplied + if (callback) + callback->regConfigChanged(); +} + + +RegConfigThread::RegConfigThread() : Thread("RegConfigThread"), config(&eventMgr) { +} + +RegConfigThread::~RegConfigThread() { + join(); +} + +bool RegConfigThread::start(const HKEY rootKey, const TCHAR* keyname) { + if (config.setKey(rootKey, keyname)) { + Thread::start(); + return true; + } + return false; +} + +void RegConfigThread::run() { + DWORD result = 0; + MSG msg; + while ((result = eventMgr.getMessage(&msg, 0, 0, 0)) > 0) {} + if (result < 0) + throw rdr::SystemException("RegConfigThread failed", GetLastError()); +} + +Thread* RegConfigThread::join() { + PostThreadMessage(getThreadId(), WM_QUIT, 0, 0); + return Thread::join(); +} diff --git a/win/rfb_win32/RegConfig.h b/win/rfb_win32/RegConfig.h new file mode 100644 index 00000000..e9c01b1d --- /dev/null +++ b/win/rfb_win32/RegConfig.h @@ -0,0 +1,84 @@ +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * + * This 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 software 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 software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +// -=- RegConfig.h + +// Class which monitors the registry and reads in the registry settings +// whenever they change, or are added or removed. + +#ifndef __RFB_WIN32_REG_CONFIG_H__ +#define __RFB_WIN32_REG_CONFIG_H__ + +#include +#include +#include +#include +#include + +namespace rfb { + + namespace win32 { + + class RegConfig : EventHandler { + public: + RegConfig(EventManager* em); + ~RegConfig(); + + // Specify the registry key to read Configuration items from + bool setKey(const HKEY rootkey, const TCHAR* keyname); + + // Support for a callback, run in the RegConfig host thread whenever + // the registry configuration changes + class Callback { + public: + virtual ~Callback() {} + virtual void regConfigChanged() = 0; + }; + void setCallback(Callback* cb) { callback = cb; } + + // Read entries from the specified key into the Configuration + static void loadRegistryConfig(RegKey& key); + protected: + // EventHandler interface and trigger event + virtual void processEvent(HANDLE event); + + EventManager* eventMgr; + Handle event; + Callback* callback; + RegKey key; + }; + + class RegConfigThread : Thread { + public: + RegConfigThread(); + ~RegConfigThread(); + + // Start the thread, reading from the specified key + bool start(const HKEY rootkey, const TCHAR* keyname); + protected: + void run(); + Thread* join(); + EventManager eventMgr; + RegConfig config; + }; + + }; + +}; + +#endif // __RFB_WIN32_REG_CONFIG_H__ diff --git a/win/rfb_win32/Registry.cxx b/win/rfb_win32/Registry.cxx new file mode 100644 index 00000000..4ece4bac --- /dev/null +++ b/win/rfb_win32/Registry.cxx @@ -0,0 +1,316 @@ +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * + * This 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 software 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 software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +// -=- Registry.cxx + +#include +#include +#include +#include +#include +#include +#include +#include + +// These flags are required to control access control inheritance, +// but are not defined by VC6's headers. These definitions comes +// from the Microsoft Platform SDK. +#ifndef PROTECTED_DACL_SECURITY_INFORMATION +#define PROTECTED_DACL_SECURITY_INFORMATION (0x80000000L) +#endif +#ifndef UNPROTECTED_DACL_SECURITY_INFORMATION +#define UNPROTECTED_DACL_SECURITY_INFORMATION (0x20000000L) +#endif + + +using namespace rfb; +using namespace rfb::win32; + + +static LogWriter vlog("Registry"); + + +RegKey::RegKey() : key(0), freeKey(false), valueNameBufLen(0) {} + +RegKey::RegKey(const HKEY k) : key(0), freeKey(false), valueNameBufLen(0) { + LONG result = RegOpenKeyEx(k, 0, 0, KEY_ALL_ACCESS, &key); + if (result != ERROR_SUCCESS) + throw rdr::SystemException("RegOpenKeyEx(HKEY)", result); + vlog.debug("duplicated %x to %x", k, key); + freeKey = true; +} + +RegKey::RegKey(const RegKey& k) : key(0), freeKey(false), valueNameBufLen(0) { + LONG result = RegOpenKeyEx(k.key, 0, 0, KEY_ALL_ACCESS, &key); + if (result != ERROR_SUCCESS) + throw rdr::SystemException("RegOpenKeyEx(RegKey&)", result); + vlog.debug("duplicated %x to %x", k.key, key); + freeKey = true; +} + +RegKey::~RegKey() { + close(); +} + + +void RegKey::setHKEY(HKEY k, bool fK) { + vlog.debug("setHKEY(%x,%d)", k, (int)fK); + close(); + freeKey = fK; + key = k; +} + + +bool RegKey::createKey(const RegKey& root, const TCHAR* name) { + close(); + LONG result = RegCreateKey(root.key, name, &key); + if (result != ERROR_SUCCESS) { + vlog.error("RegCreateKey(%x, %s): %x", root.key, name, result); + throw rdr::SystemException("RegCreateKeyEx", result); + } + vlog.debug("createKey(%x,%s) = %x", root.key, (const char*)CStr(name), key); + freeKey = true; + return true; +} + +void RegKey::openKey(const RegKey& root, const TCHAR* name, bool readOnly) { + close(); + LONG result = RegOpenKeyEx(root.key, name, 0, readOnly ? KEY_READ : KEY_ALL_ACCESS, &key); + if (result != ERROR_SUCCESS) + throw rdr::SystemException("RegOpenKeyEx (open)", result); + vlog.debug("openKey(%x,%s,%s) = %x", root.key, (const char*)CStr(name), + readOnly ? "ro" : "rw", key); + freeKey = true; +} + +void RegKey::setDACL(const PACL acl, bool inherit) { + DWORD result; + typedef DWORD (WINAPI *_SetSecurityInfo_proto) (HANDLE, SE_OBJECT_TYPE, SECURITY_INFORMATION, PSID, PSID, PACL, PACL); + DynamicFn<_SetSecurityInfo_proto> _SetSecurityInfo(_T("advapi32.dll"), "SetSecurityInfo"); + if (!_SetSecurityInfo.isValid()) + throw rdr::SystemException("RegKey::setDACL failed", ERROR_CALL_NOT_IMPLEMENTED); + if ((result = (*_SetSecurityInfo)(key, SE_REGISTRY_KEY, + DACL_SECURITY_INFORMATION | + (inherit ? UNPROTECTED_DACL_SECURITY_INFORMATION : PROTECTED_DACL_SECURITY_INFORMATION), + 0, 0, acl, 0)) != ERROR_SUCCESS) + throw rdr::SystemException("RegKey::setDACL failed", result); +} + +void RegKey::close() { + if (freeKey) { + vlog.debug("RegCloseKey(%x)", key); + RegCloseKey(key); + key = 0; + } +} + +void RegKey::deleteKey(const TCHAR* name) const { + LONG result = RegDeleteKey(key, name); + if (result != ERROR_SUCCESS) + throw rdr::SystemException("RegDeleteKey", result); +} + +void RegKey::deleteValue(const TCHAR* name) const { + LONG result = RegDeleteValue(key, name); + if (result != ERROR_SUCCESS) + throw rdr::SystemException("RegDeleteValue", result); +} + +void RegKey::awaitChange(bool watchSubTree, DWORD filter, HANDLE event) const { + LONG result = RegNotifyChangeKeyValue(key, watchSubTree, filter, event, event != 0); + if (result != ERROR_SUCCESS) + throw rdr::SystemException("RegNotifyChangeKeyValue", result); +} + + +RegKey::operator HKEY() const {return key;} + + +void RegKey::setExpandString(const TCHAR* valname, const TCHAR* value) const { + LONG result = RegSetValueEx(key, valname, 0, REG_EXPAND_SZ, (const BYTE*)value, (_tcslen(value)+1)*sizeof(TCHAR)); + if (result != ERROR_SUCCESS) throw rdr::SystemException("setExpandString", result); +} + +void RegKey::setString(const TCHAR* valname, const TCHAR* value) const { + LONG result = RegSetValueEx(key, valname, 0, REG_SZ, (const BYTE*)value, (_tcslen(value)+1)*sizeof(TCHAR)); + if (result != ERROR_SUCCESS) throw rdr::SystemException("setString", result); +} + +void RegKey::setBinary(const TCHAR* valname, const void* value, int length) const { + LONG result = RegSetValueEx(key, valname, 0, REG_BINARY, (const BYTE*)value, length); + if (result != ERROR_SUCCESS) throw rdr::SystemException("setBinary", result); +} + +void RegKey::setInt(const TCHAR* valname, int value) const { + LONG result = RegSetValueEx(key, valname, 0, REG_DWORD, (const BYTE*)&value, sizeof(value)); + if (result != ERROR_SUCCESS) throw rdr::SystemException("setInt", result); +} + +void RegKey::setBool(const TCHAR* valname, bool value) const { + setInt(valname, value ? 1 : 0); +} + +TCHAR* RegKey::getString(const TCHAR* valname) const {return getRepresentation(valname);} +TCHAR* RegKey::getString(const TCHAR* valname, const TCHAR* def) const { + try { + return getString(valname); + } catch(rdr::Exception) { + return tstrDup(def); + } +} + +void RegKey::getBinary(const TCHAR* valname, void** data, int* length) const { + TCharArray hex = getRepresentation(valname); + if (!rdr::HexInStream::hexStrToBin(CStr(hex.buf), (char**)data, length)) + throw rdr::Exception("getBinary failed"); +} +void RegKey::getBinary(const TCHAR* valname, void** data, int* length, void* def, int deflen) const { + try { + getBinary(valname, data, length); + } catch(rdr::Exception) { + if (deflen) { + *data = new char[deflen]; + memcpy(*data, def, deflen); + } else + *data = 0; + *length = deflen; + } +} + +int RegKey::getInt(const TCHAR* valname) const { + TCharArray tmp = getRepresentation(valname); + return _ttoi(tmp.buf); +} +int RegKey::getInt(const TCHAR* valname, int def) const { + try { + return getInt(valname); + } catch(rdr::Exception) { + return def; + } +} + +bool RegKey::getBool(const TCHAR* valname) const { + return getInt(valname) > 0; +} +bool RegKey::getBool(const TCHAR* valname, bool def) const { + return getInt(valname, def ? 1 : 0) > 0; +} + +static inline TCHAR* terminateData(char* data, int length) +{ + // We must terminate the string, just to be sure. Stupid Win32... + int len = length/sizeof(TCHAR); + TCharArray str(len+1); + memcpy(str.buf, data, length); + str.buf[len] = 0; + return str.takeBuf(); +} + +TCHAR* RegKey::getRepresentation(const TCHAR* valname) const { + DWORD type, length; + LONG result = RegQueryValueEx(key, valname, 0, &type, 0, &length); + if (result != ERROR_SUCCESS) + throw rdr::SystemException("get registry value length", result); + CharArray data(length); + result = RegQueryValueEx(key, valname, 0, &type, (BYTE*)data.buf, &length); + if (result != ERROR_SUCCESS) + throw rdr::SystemException("get registry value", result); + + switch (type) { + case REG_BINARY: + { + TCharArray hex = rdr::HexOutStream::binToHexStr(data.buf, length); + return hex.takeBuf(); + } + case REG_SZ: + if (length) { + return terminateData(data.buf, length); + } else { + return tstrDup(_T("")); + } + case REG_DWORD: + { + TCharArray tmp(16); + _stprintf(tmp.buf, _T("%u"), *((DWORD*)data.buf)); + return tmp.takeBuf(); + } + case REG_EXPAND_SZ: + { + if (length) { + TCharArray str(terminateData(data.buf, length)); + DWORD required = ExpandEnvironmentStrings(str.buf, 0, 0); + if (required==0) + throw rdr::SystemException("ExpandEnvironmentStrings", GetLastError()); + TCharArray result(required); + length = ExpandEnvironmentStrings(str.buf, result.buf, required); + if (required +#include +#include + +namespace rfb { + + namespace win32 { + + class RegKey { + public: + // No key open + RegKey(); + + // Duplicate the specified existing key + RegKey(const HKEY k); + RegKey(const RegKey& k); + + // Calls close() internally + ~RegKey(); + + void setHKEY(HKEY key, bool freeKey); + private: + RegKey& operator=(const RegKey& k); + HKEY& operator=(const HKEY& k); + public: + + // Returns true if key was created, false if already existed + bool createKey(const RegKey& root, const TCHAR* name); + + // Opens key if it exists, or raises an exception if not + void openKey(const RegKey& root, const TCHAR* name, bool readOnly=false); + + // Set the (discretionary) access control list for the key + void setDACL(const PACL acl, bool inheritFromParent=true); + + // Closes current key, if required + void close(); + + // Delete a subkey/value + void deleteKey(const TCHAR* name) const; + void deleteValue(const TCHAR* name) const; + + + // Block waiting for a registry change, OR return immediately and notify the + // event when there is a change, if specified + void awaitChange(bool watchSubTree, DWORD filter, HANDLE event=0) const; + + void setExpandString(const TCHAR* valname, const TCHAR* s) const; + void setString(const TCHAR* valname, const TCHAR* s) const; + void setBinary(const TCHAR* valname, const void* data, int length) const; + void setInt(const TCHAR* valname, int i) const; + void setBool(const TCHAR* valname, bool b) const; + + TCHAR* getString(const TCHAR* valname) const; + TCHAR* getString(const TCHAR* valname, const TCHAR* def) const; + + void getBinary(const TCHAR* valname, void** data, int* length) const; + void getBinary(const TCHAR* valname, void** data, int* length, void* def, int deflength) const; + + int getInt(const TCHAR* valname) const; + int getInt(const TCHAR* valname, int def) const; + + bool getBool(const TCHAR* valname) const; + bool getBool(const TCHAR* valname, bool def) const; + + TCHAR* getRepresentation(const TCHAR* valname) const; + + bool isValue(const TCHAR* valname) const; + + // Get the name of value/key number "i" + // If there are fewer than "i" values then return 0 + // NAME IS OWNED BY RegKey OBJECT! + const TCHAR* getValueName(int i); + const TCHAR* getKeyName(int i); + + operator HKEY() const; + protected: + HKEY key; + bool freeKey; + TCharArray valueName; + DWORD valueNameBufLen; + }; + + }; + +}; + +#endif // __RFB_WIN32_REG_CONFIG_H__ diff --git a/win/rfb_win32/SDisplay.cxx b/win/rfb_win32/SDisplay.cxx new file mode 100644 index 00000000..0af50649 --- /dev/null +++ b/win/rfb_win32/SDisplay.cxx @@ -0,0 +1,524 @@ +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * + * This 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 software 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 software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +// -=- SDisplay.cxx +// +// The SDisplay class encapsulates a particular system display. + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +using namespace rdr; +using namespace rfb; +using namespace rfb::win32; + +static LogWriter vlog("SDisplay"); + +// - SDisplay-specific configuration options + +IntParameter rfb::win32::SDisplay::updateMethod("UpdateMethod", + "How to discover desktop updates; 0 - Polling, 1 - Application hooking, 2 - Driver hooking.", 1); +BoolParameter rfb::win32::SDisplay::disableLocalInputs("DisableLocalInputs", + "Disable local keyboard and pointer input while the server is in use", false); +StringParameter rfb::win32::SDisplay::disconnectAction("DisconnectAction", + "Action to perform when all clients have disconnected. (None, Lock, Logoff)", "None"); +StringParameter displayDevice("DisplayDevice", + "Display device name of the monitor to be remoted, or empty to export the whole desktop.", ""); +BoolParameter rfb::win32::SDisplay::removeWallpaper("RemoveWallpaper", + "Remove the desktop wallpaper when the server is in use.", false); +BoolParameter rfb::win32::SDisplay::removePattern("RemovePattern", + "Remove the desktop background pattern when the server is in use.", false); +BoolParameter rfb::win32::SDisplay::disableEffects("DisableEffects", + "Disable desktop user interface effects when the server is in use.", false); + + +////////////////////////////////////////////////////////////////////////////// +// +// SDisplay +// + +typedef BOOL (WINAPI *_LockWorkStation_proto)(); +DynamicFn<_LockWorkStation_proto> _LockWorkStation(_T("user32.dll"), "LockWorkStation"); + +// -=- Constructor/Destructor + +SDisplay::SDisplay() + : server(0), pb(0), device(0), + core(0), ptr(0), kbd(0), clipboard(0), + inputs(0), monitor(0), cleanDesktop(0), cursor(0), + statusLocation(0) +{ + updateEvent.h = CreateEvent(0, TRUE, FALSE, 0); +} + +SDisplay::~SDisplay() +{ + // XXX when the VNCServer has been deleted with clients active, stop() + // doesn't get called - this ought to be fixed in VNCServerST. In any event, + // we should never call any methods on VNCServer once we're being deleted. + // This is because it is supposed to be guaranteed that the SDesktop exists + // throughout the lifetime of the VNCServer. So if we're being deleted, then + // the VNCServer ought not to exist and therefore we shouldn't invoke any + // methods on it. Setting server to zero here ensures that stop() doesn't + // call setPixelBuffer(0) on the server. + server = 0; + if (core) stop(); +} + + +// -=- SDesktop interface + +void SDisplay::start(VNCServer* vs) +{ + vlog.debug("starting"); + + // Try to make session zero the console session + if (!inConsoleSession()) + setConsoleSession(); + + // Start the SDisplay core + server = vs; + startCore(); + + vlog.debug("started"); + + if (statusLocation) *statusLocation = true; +} + +void SDisplay::stop() +{ + vlog.debug("stopping"); + + // If we successfully start()ed then perform the DisconnectAction + if (core) { + CurrentUserToken cut; + CharArray action = disconnectAction.getData(); + if (stricmp(action.buf, "Logoff") == 0) { + if (!cut.h) + vlog.info("ignoring DisconnectAction=Logoff - no current user"); + else + ExitWindowsEx(EWX_LOGOFF, 0); + } else if (stricmp(action.buf, "Lock") == 0) { + if (!cut.h) { + vlog.info("ignoring DisconnectAction=Lock - no current user"); + } else { + if (_LockWorkStation.isValid()) + (*_LockWorkStation)(); + else + ExitWindowsEx(EWX_LOGOFF, 0); + } + } + } + + // Stop the SDisplayCore + if (server) + server->setPixelBuffer(0); + stopCore(); + server = 0; + + vlog.debug("stopped"); + + if (statusLocation) *statusLocation = false; +} + + +void SDisplay::startCore() { + + // Currently, we just check whether we're in the console session, and + // fail if not + if (!inConsoleSession()) + throw rdr::Exception("Console is not session zero - oreconnect to restore Console sessin"); + + // Switch to the current input desktop + if (rfb::win32::desktopChangeRequired()) { + if (!rfb::win32::changeDesktop()) + throw rdr::Exception("unable to switch into input desktop"); + } + + // Initialise the change tracker and clipper + updates.clear(); + clipper.setUpdateTracker(server); + + // Create the framebuffer object + recreatePixelBuffer(true); + + // Create the SDisplayCore + updateMethod_ = updateMethod; + int tryMethod = updateMethod_; + while (!core) { + try { + if (tryMethod == 2) + core = new SDisplayCoreDriver(this, &updates); + else if (tryMethod == 1) + core = new SDisplayCoreWMHooks(this, &updates); + else + core = new SDisplayCorePolling(this, &updates); + core->setScreenRect(screenRect); + } catch (rdr::Exception& e) { + delete core; core = 0; + if (tryMethod == 0) + throw rdr::Exception("unable to access desktop"); + tryMethod--; + vlog.error(e.str()); + } + } + vlog.info("Started %s", core->methodName()); + + // Start display monitor, clipboard handler and input handlers + monitor = new WMMonitor; + monitor->setNotifier(this); + clipboard = new Clipboard; + clipboard->setNotifier(this); + ptr = new SPointer; + kbd = new SKeyboard; + inputs = new WMBlockInput; + cursor = new WMCursor; + + // Apply desktop optimisations + cleanDesktop = new CleanDesktop; + if (removePattern) + cleanDesktop->disablePattern(); + if (removeWallpaper) + cleanDesktop->disableWallpaper(); + if (disableEffects) + cleanDesktop->disableEffects(); + isWallpaperRemoved = removeWallpaper; + isPatternRemoved = removePattern; + areEffectsDisabled = disableEffects; +} + +void SDisplay::stopCore() { + if (core) + vlog.info("Stopping %s", core->methodName()); + delete core; core = 0; + delete pb; pb = 0; + delete device; device = 0; + delete monitor; monitor = 0; + delete clipboard; clipboard = 0; + delete inputs; inputs = 0; + delete ptr; ptr = 0; + delete kbd; kbd = 0; + delete cleanDesktop; cleanDesktop = 0; + delete cursor; cursor = 0; + ResetEvent(updateEvent); +} + + +bool SDisplay::areHooksAvailable() { + return WMHooks::areAvailable(); +} + +bool SDisplay::isDriverAvailable() { + return SDisplayCoreDriver::isAvailable(); +} + + +bool SDisplay::isRestartRequired() { + // - We must restart the SDesktop if: + // 1. We are no longer in the input desktop. + // 2. The any setting has changed. + + // - Check that our session is the Console + if (!inConsoleSession()) + return true; + + // - Check that we are in the input desktop + if (rfb::win32::desktopChangeRequired()) + return true; + + // - Check that the update method setting hasn't changed + // NB: updateMethod reflects the *selected* update method, not + // necessarily the one in use, since we fall back to simpler + // methods if more advanced ones fail! + if (updateMethod_ != updateMethod) + return true; + + // - Check that the desktop optimisation settings haven't changed + // This isn't very efficient, but it shouldn't change very often! + if ((isWallpaperRemoved != removeWallpaper) || + (isPatternRemoved != removePattern) || + (areEffectsDisabled != disableEffects)) + return true; + + return false; +} + + +void SDisplay::restartCore() { + vlog.info("restarting"); + + // Stop the existing Core related resources + stopCore(); + try { + // Start a new Core if possible + startCore(); + vlog.info("restarted"); + } catch (rdr::Exception& e) { + // If startCore() fails then we MUST disconnect all clients, + // to cause the server to stop() the desktop. + // Otherwise, the SDesktop is in an inconsistent state + // and the server will crash. + server->closeClients(e.str()); + } +} + + +void SDisplay::pointerEvent(const Point& pos, int buttonmask) { + if (pb->getRect().contains(pos)) { + Point screenPos = pos.translate(screenRect.tl); + // - Check that the SDesktop doesn't need restarting + if (isRestartRequired()) + restartCore(); + if (ptr) + ptr->pointerEvent(screenPos, buttonmask); + } +} + +void SDisplay::keyEvent(rdr::U32 key, bool down) { + // - Check that the SDesktop doesn't need restarting + if (isRestartRequired()) + restartCore(); + if (kbd) + kbd->keyEvent(key, down); +} + +void SDisplay::clientCutText(const char* text, int len) { + CharArray clip_sz(len+1); + memcpy(clip_sz.buf, text, len); + clip_sz.buf[len] = 0; + clipboard->setClipText(clip_sz.buf); +} + + +void SDisplay::framebufferUpdateRequest() +{ + SetEvent(updateEvent); +} + +Point SDisplay::getFbSize() { + bool startAndStop = !core; + + // If not started, do minimal initialisation to get desktop size. + if (startAndStop) + recreatePixelBuffer(); + Point result = Point(pb->width(), pb->height()); + + // Destroy the initialised structures. + if (startAndStop) + stopCore(); + return result; +} + + +void +SDisplay::notifyClipboardChanged(const char* text, int len) { + vlog.debug("clipboard text changed"); + if (server) + server->serverCutText(text, len); +} + + +void +SDisplay::notifyDisplayEvent(WMMonitor::Notifier::DisplayEventType evt) { + switch (evt) { + case WMMonitor::Notifier::DisplaySizeChanged: + vlog.debug("desktop size changed"); + recreatePixelBuffer(); + break; + case WMMonitor::Notifier::DisplayPixelFormatChanged: + vlog.debug("desktop format changed"); + recreatePixelBuffer(); + break; + case WMMonitor::Notifier::DisplayColourMapChanged: + vlog.debug("desktop colormap changed"); + pb->updateColourMap(); + if (server) + server->setColourMapEntries(); + break; + default: + vlog.error("unknown display event received"); + } +} + +void +SDisplay::processEvent(HANDLE event) { + if (event == updateEvent) { + vlog.write(120, "processEvent"); + ResetEvent(updateEvent); + + // - If the SDisplay isn't even started then quit now + if (!core) { + vlog.error("not start()ed"); + return; + } + + // - Ensure that the disableLocalInputs flag is respected + inputs->blockInputs(disableLocalInputs); + + // - Only process updates if the server is ready + if (server && server->clientsReadyForUpdate()) { + bool try_update = false; + + // - Check that the SDesktop doesn't need restarting + if (isRestartRequired()) { + restartCore(); + return; + } + + // - Flush any updates from the core + try { + core->flushUpdates(); + } catch (rdr::Exception& e) { + vlog.error(e.str()); + restartCore(); + return; + } + + // Ensure the cursor is up to date + WMCursor::Info info = cursor->getCursorInfo(); + if (old_cursor != info) { + // Update the cursor shape if the visibility has changed + bool set_cursor = info.visible != old_cursor.visible; + // OR if the cursor is visible and the shape has changed. + set_cursor |= info.visible && (old_cursor.cursor != info.cursor); + + // Update the cursor shape + if (set_cursor) + pb->setCursor(info.visible ? info.cursor : 0, server); + + // Update the cursor position + // NB: First translate from Screen coordinates to Desktop + Point desktopPos = info.position.translate(screenRect.tl.negate()); + server->setCursorPos(desktopPos); + try_update = true; + + old_cursor = info; + } + + // Flush any changes to the server + try_update = flushChangeTracker() || try_update; + if (try_update) { + server->tryUpdate(); + } + } + return; + } + throw rdr::Exception("No such event"); +} + + +// -=- Protected methods + +void +SDisplay::recreatePixelBuffer(bool force) { + // Open the specified display device + // If no device is specified, open entire screen using GetDC(). + // Opening the whole display with CreateDC doesn't work on multi-monitor + // systems for some reason. + DeviceContext* new_device = 0; + TCharArray deviceName(displayDevice.getData()); + if (deviceName.buf[0]) { + vlog.info("Attaching to device %s", (const char*)CStr(deviceName.buf)); + new_device = new DeviceDC(deviceName.buf); + } + if (!new_device) { + vlog.info("Attaching to virtual desktop"); + new_device = new WindowDC(0); + } + + // Get the coordinates of the specified dispay device + Rect newScreenRect; + if (deviceName.buf[0]) { + MonitorInfo info(CStr(deviceName.buf)); + newScreenRect = Rect(info.rcMonitor.left, info.rcMonitor.top, + info.rcMonitor.right, info.rcMonitor.bottom); + } else { + newScreenRect = new_device->getClipBox(); + } + + // If nothing has changed & a recreate has not been forced, delete + // the new device context and return + if (pb && !force && + newScreenRect.equals(screenRect) && + new_device->getPF().equal(pb->getPF())) { + delete new_device; + return; + } + + // Flush any existing changes to the server + flushChangeTracker(); + + // Delete the old pixelbuffer and device context + vlog.debug("deleting old pixel buffer & device"); + if (pb) + delete pb; + if (device) + delete device; + + // Create a DeviceFrameBuffer attached to the new device + vlog.debug("creating pixel buffer"); + DeviceFrameBuffer* new_buffer = new DeviceFrameBuffer(*new_device); + + // Replace the old PixelBuffer + screenRect = newScreenRect; + pb = new_buffer; + device = new_device; + + // Initialise the pixels + pb->grabRegion(pb->getRect()); + + // Prevent future grabRect operations from throwing exceptions + pb->setIgnoreGrabErrors(true); + + // Update the clipping update tracker + clipper.setClipRect(pb->getRect()); + + // Inform the core of the changes + if (core) + core->setScreenRect(screenRect); + + // Inform the server of the changes + if (server) + server->setPixelBuffer(pb); +} + +bool SDisplay::flushChangeTracker() { + if (updates.is_empty()) + return false; + + vlog.write(120, "flushChangeTracker"); + + // Translate the update coordinates from Screen coords to Desktop + updates.translate(screenRect.tl.negate()); + + // Clip the updates & flush them to the server + updates.copyTo(&clipper); + updates.clear(); + return true; +} diff --git a/win/rfb_win32/SDisplay.h b/win/rfb_win32/SDisplay.h new file mode 100644 index 00000000..6dbb50a5 --- /dev/null +++ b/win/rfb_win32/SDisplay.h @@ -0,0 +1,163 @@ +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * + * This 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 software 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 software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +// -=- SDisplay.h +// +// The SDisplay class encapsulates a system display. + +#ifndef __RFB_SDISPLAY_H__ +#define __RFB_SDISPLAY_H__ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace rfb { + + namespace win32 { + + // + // -=- SDisplay + // + + class SDisplayCore { + public: + virtual ~SDisplayCore() {}; + virtual void setScreenRect(const Rect& screenRect_) = 0; + virtual void flushUpdates() = 0; + virtual const char* methodName() const = 0; + }; + + class SDisplay : public SDesktop, + WMMonitor::Notifier, + Clipboard::Notifier, + public EventHandler + { + public: + SDisplay(); + virtual ~SDisplay(); + + // -=- SDesktop interface + + virtual void start(VNCServer* vs); + virtual void stop(); + virtual void pointerEvent(const Point& pos, int buttonmask); + virtual void keyEvent(rdr::U32 key, bool down); + virtual void clientCutText(const char* str, int len); + virtual void framebufferUpdateRequest(); + virtual Point getFbSize(); + + // -=- Clipboard + + virtual void notifyClipboardChanged(const char* text, int len); + + // -=- Display events + + virtual void notifyDisplayEvent(WMMonitor::Notifier::DisplayEventType evt); + + // -=- EventHandler interface + + HANDLE getUpdateEvent() {return updateEvent;} + virtual void processEvent(HANDLE event); + + // -=- Notification of whether or not SDisplay is started + + void setStatusLocation(bool* status) {statusLocation = status;} + + friend class SDisplayCore; + + static IntParameter updateMethod; + static BoolParameter disableLocalInputs; + static StringParameter disconnectAction; + static BoolParameter removeWallpaper; + static BoolParameter removePattern; + static BoolParameter disableEffects; + + // -=- Use by VNC Config to determine whether hooks, driver, etc are available + static bool areHooksAvailable(); + static bool isDriverAvailable(); + + + protected: + bool isRestartRequired(); + void startCore(); + void stopCore(); + void restartCore(); + void recreatePixelBuffer(bool force=false); + bool flushChangeTracker(); // true if flushed, false if empty + + VNCServer* server; + + // -=- Display pixel buffer + DeviceFrameBuffer* pb; + DeviceContext* device; + + // -=- The coordinates of Window's entire virtual Screen + Rect screenRect; + + // -=- All changes are collected in UN-CLIPPED Display coords and merged + // When they are to be flushed to the VNCServer, they are changed + // to server coords and clipped appropriately. + SimpleUpdateTracker updates; + ClippingUpdateTracker clipper; + + // -=- Internal SDisplay implementation + SDisplayCore* core; + int updateMethod_; + + // Inputs + SPointer* ptr; + SKeyboard* kbd; + Clipboard* clipboard; + WMBlockInput* inputs; + + // Desktop state + WMMonitor* monitor; + + // Desktop optimisation + CleanDesktop* cleanDesktop; + bool isWallpaperRemoved; + bool isPatternRemoved; + bool areEffectsDisabled; + + // Cursor + WMCursor* cursor; + WMCursor::Info old_cursor; + Region old_cursor_region; + Point cursor_renderpos; + + // -=- Event signalled to trigger an update to be flushed + Handle updateEvent; + + // -=- Where to write the active/inactive indicator to + bool* statusLocation; + }; + + } +} + +#endif // __RFB_SDISPLAY_H__ diff --git a/win/rfb_win32/SDisplayCoreDriver.h b/win/rfb_win32/SDisplayCoreDriver.h new file mode 100644 index 00000000..5fea75cc --- /dev/null +++ b/win/rfb_win32/SDisplayCoreDriver.h @@ -0,0 +1,52 @@ +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * + * This 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 software 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 software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +// -=- SDisplayCoreDriver.h +// +// Placeholder for SDisplayCore mirror-driver implementation. + +#ifndef __RFB_SDISPLAY_CORE_DRIVER_H__ +#define __RFB_SDISPLAY_CORE_DRIVER_H__ + +#include + +namespace rfb { + namespace win32 { + + class SDisplayCoreDriver: public SDisplayCore { + public: + SDisplayCoreDriver(SDisplay* display, UpdateTracker* ut) { + throw rdr::Exception("Not supported"); + } + + // - Called by SDisplay to inform Core of the screen size + virtual void setScreenRect(const Rect& screenRect_) {} + + // - Called by SDisplay to flush updates to the specified tracker + virtual void flushUpdates() {} + + virtual const char* methodName() const { return "VNC Mirror Driver"; } + + // - Determine whether the display driver is installed & usable + static bool isAvailable() { return false; } + }; + + }; +}; + +#endif diff --git a/win/rfb_win32/SDisplayCorePolling.cxx b/win/rfb_win32/SDisplayCorePolling.cxx new file mode 100644 index 00000000..fc57ecd0 --- /dev/null +++ b/win/rfb_win32/SDisplayCorePolling.cxx @@ -0,0 +1,81 @@ +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * + * This 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 software 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 software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +// -=- SDisplayCorePolling.cxx + +#include +#include + +using namespace rfb; +using namespace rfb::win32; + +static LogWriter vlog("SDisplayCorePolling"); + +const int POLLING_SEGMENTS = 16; + +const int SDisplayCorePolling::pollTimerId = 1; + +SDisplayCorePolling::SDisplayCorePolling(SDisplay* d, UpdateTracker* ut, int pollInterval_) + : MsgWindow(_T("rfb::win32::SDisplayCorePolling")), updateTracker(ut), + pollTimer(getHandle(), pollTimerId), pollNextStrip(false), display(d) { + pollInterval = max(10, (pollInterval_ / POLLING_SEGMENTS)); + copyrect.setUpdateTracker(ut); +} + +SDisplayCorePolling::~SDisplayCorePolling() { +} + +LRESULT SDisplayCorePolling::processMessage(UINT msg, WPARAM wParam, LPARAM lParam) { + if (msg == WM_TIMER && wParam == pollTimerId) { + pollNextStrip = true; + SetEvent(display->getUpdateEvent()); + return 0; + } + return MsgWindow::processMessage(msg, wParam, lParam); +} + +void SDisplayCorePolling::setScreenRect(const Rect& screenRect_) { + vlog.info("setScreenRect"); + screenRect = screenRect_; + pollIncrementY = (screenRect.height()+POLLING_SEGMENTS-1)/POLLING_SEGMENTS; + pollNextY = screenRect.tl.y; + pollTimer.start(pollInterval); +} + +void SDisplayCorePolling::flushUpdates() { + vlog.write(120, "flushUpdates"); + + // Check for window movement + while (copyrect.processEvent()) {} + + if (pollNextStrip) { + // Poll the next strip of the screen (in Screen coordinates) + pollNextStrip = false; + Rect pollrect = screenRect; + if (pollNextY >= pollrect.br.y) { + // Yes. Reset the counter and return + pollNextY = pollrect.tl.y; + } else { + // No. Poll the next section + pollrect.tl.y = pollNextY; + pollNextY += pollIncrementY; + pollrect.br.y = min(pollNextY, pollrect.br.y); + updateTracker->add_changed(pollrect); + } + } +} diff --git a/win/rfb_win32/SDisplayCorePolling.h b/win/rfb_win32/SDisplayCorePolling.h new file mode 100644 index 00000000..9e1b5ad1 --- /dev/null +++ b/win/rfb_win32/SDisplayCorePolling.h @@ -0,0 +1,75 @@ +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * + * This 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 software 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 software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +// -=- SDisplayCorePolling.h +// +// SDisplayCore implementation that simply polls the screen, in sections, +// in order to detect changes. This Core will signal the SDisplay's +// updateEvent regularly, causing it to call the Core back to propagate +// changes to the VNC Server. + + +#ifndef __RFB_SDISPLAY_CORE_POLLING_H__ +#define __RFB_SDISPLAY_CORE_POLLING_H__ + +#include +#include +#include + +namespace rfb { + namespace win32 { + + class SDisplayCorePolling : public SDisplayCore, protected MsgWindow { + public: + SDisplayCorePolling(SDisplay* display, UpdateTracker* ut, int pollIntervalMs=50); + ~SDisplayCorePolling(); + + // - Called by SDisplay to inform Core of the screen size + virtual void setScreenRect(const Rect& screenRect_); + + // - Called by SDisplay to flush updates to the specified tracker + virtual void flushUpdates(); + + virtual const char* methodName() const { return "Polling"; } + + protected: + // - MsgWindow overrides + // processMessage is used to service the cursor & polling timers + virtual LRESULT processMessage(UINT msg, WPARAM wParam, LPARAM lParam); + + // - Hooking subcomponents used to track the desktop state + WMCopyRect copyrect; + + // - Background full screen polling fields + IntervalTimer pollTimer; + static const int pollTimerId; + Rect screenRect; + int pollInterval; + int pollNextY; + int pollIncrementY; + bool pollNextStrip; + + // - Handle back to the owning SDisplay, and to the UpdateTracker to flush to + SDisplay* display; + UpdateTracker* updateTracker; + }; + + }; +}; + +#endif \ No newline at end of file diff --git a/win/rfb_win32/SDisplayCoreWMHooks.cxx b/win/rfb_win32/SDisplayCoreWMHooks.cxx new file mode 100644 index 00000000..10b88e08 --- /dev/null +++ b/win/rfb_win32/SDisplayCoreWMHooks.cxx @@ -0,0 +1,74 @@ +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * + * This 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 software 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 software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +// -=- SDisplayCoreWMHooks.cxx + +#include +#include + +using namespace rfb; +using namespace rfb::win32; + +static LogWriter vlog("SDisplayCoreWMHooks"); + +const int SDisplayCoreWMHooks::cursorTimerId = 2; +const int SDisplayCoreWMHooks::consolePollTimerId = 3; + + +SDisplayCoreWMHooks::SDisplayCoreWMHooks(SDisplay* d, UpdateTracker* ut) + : SDisplayCorePolling(d, ut, 5000), + cursorTimer(getHandle(), cursorTimerId), + consolePollTimer(getHandle(), consolePollTimerId), + pollConsoles(false) { + if (!hooks.setEvent(display->getUpdateEvent())) + throw rdr::Exception("hook subsystem failed to initialise"); + poller.setUpdateTracker(updateTracker); + cursorTimer.start(20); + consolePollTimer.start(200); +} + +SDisplayCoreWMHooks::~SDisplayCoreWMHooks() { +} + +LRESULT SDisplayCoreWMHooks::processMessage(UINT msg, WPARAM wParam, LPARAM lParam) { + if (msg == WM_TIMER) { + if (wParam == cursorTimerId) { + SetEvent(display->getUpdateEvent()); + return 0; + } else if (wParam == consolePollTimerId) { + pollConsoles = true; + SetEvent(display->getUpdateEvent()); + return 0; + } + } + return SDisplayCorePolling::processMessage(msg, wParam, lParam); +} + +void SDisplayCoreWMHooks::flushUpdates() { + // Poll any visible console windows + if (pollConsoles) { + pollConsoles = false; + poller.processEvent(); + } + + // Check for updates from the hooks + hooks.getUpdates(updateTracker); + + // Check for updates from the polling Core + SDisplayCorePolling::flushUpdates(); +} diff --git a/win/rfb_win32/SDisplayCoreWMHooks.h b/win/rfb_win32/SDisplayCoreWMHooks.h new file mode 100644 index 00000000..24fa5cdc --- /dev/null +++ b/win/rfb_win32/SDisplayCoreWMHooks.h @@ -0,0 +1,68 @@ +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * + * This 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 software 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 software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +// -=- SDisplayCoreWMHooks.h +// +// SDisplayCore implementation that uses WMHooks to capture changes to +// the display. +// Whenever changes are detected, the SDisplay's updateEvent is signalled, +// so that it can perform housekeeping tasks (like ensuring the currently +// active desktop is the correct one), before flushing changes from the +// Core to the VNC Server. The SDisplay will clip the changes before they +// reach the VNC Server. + + +#ifndef __RFB_SDISPLAY_CORE_WMHOOKS_H__ +#define __RFB_SDISPLAY_CORE_WMHOOKS_H__ + +#include +#include +#include + +namespace rfb { + namespace win32 { + + class SDisplayCoreWMHooks : public SDisplayCorePolling { + public: + SDisplayCoreWMHooks(SDisplay* display, UpdateTracker* ut); + ~SDisplayCoreWMHooks(); + + // - Called by SDisplay to flush updates to the specified tracker + virtual void flushUpdates(); + + virtual const char* methodName() const { return "VNC Hooks"; } + + protected: + // - MsgWindow overrides + // processMessage is used to service the cursor & polling timers + virtual LRESULT processMessage(UINT msg, WPARAM wParam, LPARAM lParam); + + // - Hooking subcomponents used to track the desktop state + WMHooks hooks; + WMPoller poller; + IntervalTimer cursorTimer; + IntervalTimer consolePollTimer; + bool pollConsoles; + static const int consolePollTimerId; + static const int cursorTimerId; + }; + + }; +}; + +#endif diff --git a/win/rfb_win32/SFileTransferManagerWin32.cxx b/win/rfb_win32/SFileTransferManagerWin32.cxx new file mode 100644 index 00000000..edc898be --- /dev/null +++ b/win/rfb_win32/SFileTransferManagerWin32.cxx @@ -0,0 +1,71 @@ +/* Copyright (C) 2006 TightVNC Team. All Rights Reserved. + * + * Developed by Dennis Syrovatsky. + * + * This 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 software 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 software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + * + * TightVNC distribution homepage on the Web: http://www.tightvnc.com/ + * + */ + +// -=- SFileTransferManagerWin32.cxx + +#include + +using namespace rfb; +using namespace win32; + +SFileTransferManagerWin32::SFileTransferManagerWin32() +{ + +} + +SFileTransferManagerWin32::~SFileTransferManagerWin32() +{ + +} + +SFileTransfer * +SFileTransferManagerWin32::createObject(network::Socket *sock) +{ + rfb::SFileTransfer *pFT = 0; + rfb::win32::SFileTransferWin32 *pFTWin32 = 0; + + pFTWin32 = new SFileTransferWin32(sock); + if (pFTWin32 == NULL) return NULL; + + pFT = (SFileTransfer *) pFTWin32; + + m_lstFTObjects.push_front(pFT); + + return pFT; +} + +void +SFileTransferManagerWin32::processDownloadMsg(MSG msg) +{ + SFileTransfer *pFT = (SFileTransfer *)msg.lParam; + + if (pFT != NULL) { + std::list::iterator i; + for (i=m_lstFTObjects.begin(); i!=m_lstFTObjects.end(); i++) { + if ((*i) == pFT) { + (*i)->sendFileDownloadPortion(); + return; + } + } + } +} diff --git a/win/rfb_win32/SFileTransferManagerWin32.h b/win/rfb_win32/SFileTransferManagerWin32.h new file mode 100644 index 00000000..ed1f997a --- /dev/null +++ b/win/rfb_win32/SFileTransferManagerWin32.h @@ -0,0 +1,48 @@ +/* Copyright (C) 2006 TightVNC Team. All Rights Reserved. + * + * Developed by Dennis Syrovatsky. + * + * This 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 software 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 software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + * + * TightVNC distribution homepage on the Web: http://www.tightvnc.com/ + * + */ + +// -=- SFileTransferManagerWin32.h + +#ifndef __RFB_WIN32_SFILETRANSFERMANAGERWIN32_H__ +#define __RFB_WIN32_SFILETRANSFERMANAGERWIN32_H__ + +#include +#include +#include + +namespace rfb { + namespace win32 { + class SFileTransferManagerWin32 : public rfb::SFileTransferManager + { + public: + SFileTransferManagerWin32(); + virtual ~SFileTransferManagerWin32(); + + void processDownloadMsg(MSG msg); + + virtual SFileTransfer *createObject(network::Socket *sock); + }; + }; +} + +#endif // __RFB_WIN32_SFILETRANSFERMANAGERWIN32_H__ diff --git a/win/rfb_win32/SFileTransferWin32.cxx b/win/rfb_win32/SFileTransferWin32.cxx new file mode 100644 index 00000000..5aea4126 --- /dev/null +++ b/win/rfb_win32/SFileTransferWin32.cxx @@ -0,0 +1,125 @@ +/* Copyright (C) 2006 TightVNC Team. All Rights Reserved. + * + * Developed by Dennis Syrovatsky. + * + * This 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 software 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 software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + * + * TightVNC distribution homepage on the Web: http://www.tightvnc.com/ + * + */ + +// -=- SFileTransferWin32.cxx + +#include +#include +#include + +using namespace rfb; +using namespace rfb::win32; + +SFileTransferWin32::SFileTransferWin32(network::Socket *sock) : SFileTransfer(sock) +{ +} + +SFileTransferWin32::~SFileTransferWin32() +{ +} + +bool +SFileTransferWin32::initDownloadCallback() +{ + PostThreadMessage(GetCurrentThreadId(), VNCM_FT_DOWNLOAD, (WPARAM) 0, (LPARAM) this); + return true; +} + +bool +SFileTransferWin32::processDownloadCallback() +{ + return sendFileDownloadPortion(); +} + +bool +SFileTransferWin32::convertPathFromNet(char *pszPath) +{ + int len = strlen(pszPath); + if (pszPath[0] == '/') { + if (len == 1) { + pszPath[0] = '\0'; + return true; + } + } else { + return false; + } + + for(int i = 0; i < (len - 1); i++) { + if(pszPath[i+1] == '/') pszPath[i+1] = '\\'; + pszPath[i] = pszPath[i+1]; + } + + pszPath[len-1] = '\0'; + return true; +} + +bool +SFileTransferWin32::makeFileList(char *pszPath, FileInfo *pFI, bool bDirOnly) +{ + FolderManager fm; + if (fm.getDirInfo(pszPath, pFI, bDirOnly)) + return true; + else + return false; +} + +bool +SFileTransferWin32::deleteIt(char *pszPath) +{ + FolderManager fm; + + return fm.deleteIt(pszPath); +} + +bool +SFileTransferWin32::renameIt(char *pszOldPath, char *pszNewPath) +{ + FolderManager fm; + + return fm.renameIt(pszOldPath, pszNewPath); +} + +bool +SFileTransferWin32::createDir(char *pszPath) +{ + FolderManager fm; + + return fm.createDir(pszPath); +} + +bool +SFileTransferWin32::getDirSize(char *pszName, unsigned short *pHighSize16, + unsigned int *pLowSize32) +{ + FolderManager fm; + DWORD64 dw64DirSize = 0; + + if (!fm.getDirSize(pszName, &dw64DirSize)) return false; + + if (dw64DirSize & 0xFFFF000000000000) return false; + + *pHighSize16 = ((dw64DirSize & 0x0000FFFF00000000) >> 32); + *pLowSize32 = (dw64DirSize & 0x00000000FFFFFFFF); + + return true; +} diff --git a/win/rfb_win32/SFileTransferWin32.h b/win/rfb_win32/SFileTransferWin32.h new file mode 100644 index 00000000..5f682a44 --- /dev/null +++ b/win/rfb_win32/SFileTransferWin32.h @@ -0,0 +1,59 @@ +/* Copyright (C) 2006 TightVNC Team. All Rights Reserved. + * + * Developed by Dennis Syrovatsky. + * + * This 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 software 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 software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + * + * TightVNC distribution homepage on the Web: http://www.tightvnc.com/ + * + */ + +// -=- SFileTransferWin32.h + +#ifndef __RFB_SFILETRANSFERWIN32_H__ +#define __RFB_SFILETRANSFERWIN32_H__ + +#include + +#include + +const UINT VNCM_FT_DOWNLOAD = WM_USER + 2; + +namespace rfb { + namespace win32 { + class SFileTransferWin32 : public rfb::SFileTransfer + { + public: + SFileTransferWin32(network::Socket *sock); + virtual ~SFileTransferWin32(); + + bool processDownloadCallback(); + virtual bool initDownloadCallback(); + + virtual bool convertPathFromNet(char *pszPath); + virtual bool makeFileList(char *pszPath, FileInfo *pFI, bool bDirOnly); + + virtual bool deleteIt(char *pszPath); + virtual bool renameIt(char *pszOldPath, char *pszNewPath); + virtual bool createDir(char *pszPath); + + virtual bool getDirSize(char *pszName, unsigned short *pHighSize16, unsigned int *pLowSize32); + + }; + }; +} + +#endif // __RFB_SFILETRANSFERWIN32_H__ diff --git a/win/rfb_win32/SInput.cxx b/win/rfb_win32/SInput.cxx new file mode 100644 index 00000000..db59287a --- /dev/null +++ b/win/rfb_win32/SInput.cxx @@ -0,0 +1,466 @@ +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * + * This 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 software 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 software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +// -=- SInput.cxx +// +// A number of routines that accept VNC input event data and perform +// the appropriate actions under Win32 + +#define XK_MISCELLANY +#define XK_LATIN1 +#define XK_CURRENCY +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if(defined(INPUT_MOUSE) && defined(RFB_HAVE_MONITORINFO)) +#define RFB_HAVE_SENDINPUT +#else +#pragma message(" NOTE: Not building SendInput support.") +#endif + +using namespace rfb; + +static LogWriter vlog("SInput"); + +#ifdef RFB_HAVE_SENDINPUT +typedef UINT (WINAPI *_SendInput_proto)(UINT, LPINPUT, int); +static win32::DynamicFn<_SendInput_proto> _SendInput(_T("user32.dll"), "SendInput"); +#endif + +// +// -=- Pointer implementation for Win32 +// + +static DWORD buttonDownMapping[8] = { + MOUSEEVENTF_LEFTDOWN, MOUSEEVENTF_MIDDLEDOWN, MOUSEEVENTF_RIGHTDOWN, + MOUSEEVENTF_WHEEL, MOUSEEVENTF_WHEEL, 0, 0, 0 +}; + +static DWORD buttonUpMapping[8] = { + MOUSEEVENTF_LEFTUP, MOUSEEVENTF_MIDDLEUP, MOUSEEVENTF_RIGHTUP, + MOUSEEVENTF_WHEEL, MOUSEEVENTF_WHEEL, 0, 0, 0 +}; + +static DWORD buttonDataMapping[8] = { + 0, 0, 0, 120, -120, 0, 0, 0 +}; + +win32::SPointer::SPointer() + : last_buttonmask(0) +{ +} + +void +win32::SPointer::pointerEvent(const Point& pos, int buttonmask) +{ + // - We are specifying absolute coordinates + DWORD flags = MOUSEEVENTF_ABSOLUTE; + + // - Has the pointer moved since the last event? + if (!last_position.equals(pos)) + flags |= MOUSEEVENTF_MOVE; + + // - If the system swaps left and right mouse buttons then we must + // swap them here to negate the effect, so that we do the actual + // action we mean to do + if (::GetSystemMetrics(SM_SWAPBUTTON)) { + bool leftDown = buttonmask & 1; + bool rightDown = buttonmask & 4; + buttonmask = (buttonmask & ~(1 | 4)); + if (leftDown) buttonmask |= 4; + if (rightDown) buttonmask |= 1; + } + + DWORD data = 0; + for (int i = 0; i < 8; i++) { + if ((buttonmask & (1<= 32 && keysym <= 126) || + (keysym >= 160 && keysym <= 255)) + { + // ordinary Latin-1 character + + if (deadKeyAware) { + // Detect dead chars and generate the dead char followed by space so + // that we'll end up with the original char. + for (int i = 0; i < deadChars.size(); i++) { + if (keysym == deadChars[i]) { + SHORT dc = VkKeyScan(keysym); + if (dc != -1) { + if (down) { + vlog.info("latin-1 dead key: 0x%x vkCode 0x%x mod 0x%x " + "followed by space", keysym, LOBYTE(dc), HIBYTE(dc)); + doKeyEventWithModifiers(LOBYTE(dc), HIBYTE(dc), true); + doKeyEventWithModifiers(LOBYTE(dc), HIBYTE(dc), false); + doKeyEventWithModifiers(VK_SPACE, 0, true); + doKeyEventWithModifiers(VK_SPACE, 0, false); + } + return; + } + } + } + } + + SHORT s = VkKeyScan(keysym); + if (s == -1) { + if (down) { + // not a single keypress - try synthesizing dead chars. + for (int j = 0; + j < sizeof(latin1ToDeadChars) / sizeof(latin1ToDeadChars_t); + j++) { + if (keysym == latin1ToDeadChars[j].latin1Char) { + for (int i = 0; i < deadChars.size(); i++) { + if (deadChars[i] == latin1ToDeadChars[j].deadChar) { + SHORT dc = VkKeyScan(latin1ToDeadChars[j].deadChar); + SHORT bc = VkKeyScan(latin1ToDeadChars[j].baseChar); + if (dc != -1 && bc != -1) { + vlog.info("latin-1 key: 0x%x dead key vkCode 0x%x mod 0x%x " + "followed by vkCode 0x%x mod 0x%x", + keysym, LOBYTE(dc), HIBYTE(dc), + LOBYTE(bc), HIBYTE(bc)); + doKeyEventWithModifiers(LOBYTE(dc), HIBYTE(dc), true); + doKeyEventWithModifiers(LOBYTE(dc), HIBYTE(dc), false); + doKeyEventWithModifiers(LOBYTE(bc), HIBYTE(bc), true); + doKeyEventWithModifiers(LOBYTE(bc), HIBYTE(bc), false); + return; + } + break; + } + } + break; + } + } + vlog.info("ignoring unrecognised Latin-1 keysym 0x%x",keysym); + } + return; + } + + BYTE vkCode = LOBYTE(s); + BYTE modifierState = HIBYTE(s); + vlog.debug("latin-1 key: 0x%x vkCode 0x%x mod 0x%x down %d", + keysym, vkCode, modifierState, down); + doKeyEventWithModifiers(vkCode, modifierState, down); + + } else { + + // see if it's a recognised keyboard key, otherwise ignore it + + if (vkMap.find(keysym) == vkMap.end()) { + vlog.info("ignoring unknown keysym 0x%x",keysym); + return; + } + BYTE vkCode = vkMap[keysym]; + DWORD flags = 0; + if (extendedMap[keysym]) flags |= KEYEVENTF_EXTENDEDKEY; + if (!down) flags |= KEYEVENTF_KEYUP; + + vlog.debug("keyboard key: keysym 0x%x vkCode 0x%x ext %d down %d", + keysym, vkCode, extendedMap[keysym], down); + if (down && (vkCode == VK_DELETE) && + ((GetAsyncKeyState(VK_CONTROL) & 0x8000) != 0) && + ((GetAsyncKeyState(VK_MENU) & 0x8000) != 0)) + { + rfb::win32::emulateCtrlAltDel(); + return; + } + + doKeyboardEvent(vkCode, flags); + } +} diff --git a/win/rfb_win32/SInput.h b/win/rfb_win32/SInput.h new file mode 100644 index 00000000..2a0b3e67 --- /dev/null +++ b/win/rfb_win32/SInput.h @@ -0,0 +1,68 @@ +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * + * This 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 software 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 software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +// -=- Input.h +// +// A number of routines that accept VNC-style input event data and perform +// the appropriate actions under Win32 + +#ifndef __RFB_WIN32_INPUT_H__ +#define __RFB_WIN32_INPUT_H__ + +#include +#include +#include +#include +#include + +namespace rfb { + + namespace win32 { + + // -=- Pointer event handling + + class SPointer { + public: + SPointer(); + // - Create a pointer event at a the given coordinates, with the + // specified button state. The event must be specified using + // Screen coordinates. + void pointerEvent(const Point& pos, int buttonmask); + protected: + Point last_position; + rdr::U8 last_buttonmask; + }; + + // -=- Keyboard event handling + + class SKeyboard { + public: + SKeyboard(); + void keyEvent(rdr::U32 key, bool down); + static BoolParameter deadKeyAware; + private: + std::map vkMap; + std::map extendedMap; + std::vector deadChars; + }; + + }; // win32 + +}; // rfb + +#endif // __RFB_WIN32_INPUT_H__ diff --git a/win/rfb_win32/ScaledDIBSectionBuffer.cxx b/win/rfb_win32/ScaledDIBSectionBuffer.cxx new file mode 100644 index 00000000..e6c15b8e --- /dev/null +++ b/win/rfb_win32/ScaledDIBSectionBuffer.cxx @@ -0,0 +1,124 @@ +/* Copyright (C) 2006 TightVNC Team. All Rights Reserved. + * + * This 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 software 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 software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + * + * TightVNC distribution homepage on the Web: http://www.tightvnc.com/ + * + */ + +// -=- ScaledDIBSectionBuffer.cxx + +#include + +#include + +using namespace rfb; +using namespace win32; + +ScaledDIBSectionBuffer::ScaledDIBSectionBuffer(HWND window) + : src_buffer(0), scaling(false), DIBSectionBuffer(window) { + scaled_data = data; +} + +ScaledDIBSectionBuffer::~ScaledDIBSectionBuffer() { + if (src_buffer) delete src_buffer; +} + +void ScaledDIBSectionBuffer::setScale(int scale_) { + if (scale_ == getScale()) return; + + if (src_buffer) { + delete src_buffer; + src_buffer = 0; + } + if (scale_ != 100) { + scaling = true; + src_buffer = new ManagedPixelBuffer(format, src_width, src_height); + src_data = &(src_buffer->data); + } else { + scaling = false; + } + ScaledPixelBuffer::setScale(scale_); +} + +void ScaledDIBSectionBuffer::setPF(const PixelFormat &pf) { + if (scaling) src_buffer->setPF(pf); + DIBSectionBuffer::setPF(pf); + scaled_data = data; +} + +void ScaledDIBSectionBuffer::setSize(int src_width_, int src_height_) { + src_width = src_width_; + src_height = src_height_; + if (scaling) { + src_buffer->setSize(src_width, src_height); + } + calculateScaledBufferSize(); + recreateScaledBuffer(); + scaled_data = data; +} + +void ScaledDIBSectionBuffer::recreateScaledBuffer() { + width_ = scaled_width; + height_ = scaled_height; + DIBSectionBuffer::recreateBuffer(); + scaled_data = data; +} + +void ScaledDIBSectionBuffer::fillRect(const Rect &dest, Pixel pix) { + if (scaling) { + src_buffer->fillRect(dest, pix); + scaleRect(dest); + } else { + DIBSectionBuffer::fillRect(dest, pix); + } +} + +void ScaledDIBSectionBuffer::imageRect(const Rect &dest, const void* pixels, int stride) { + if (scaling) { + src_buffer->imageRect(dest, pixels, stride); + scaleRect(dest); + } else { + DIBSectionBuffer::imageRect(dest, pixels, stride); + } +} + +void ScaledDIBSectionBuffer::copyRect(const Rect &dest, const Point &move_by_delta) { + if (scaling) { + src_buffer->copyRect(dest, move_by_delta); + scaleRect(dest); + } else { + DIBSectionBuffer::copyRect(dest, move_by_delta); + } +} + +void ScaledDIBSectionBuffer::maskRect(const Rect& r, const void* pixels, const void* mask_) { + if (scaling) { + src_buffer->maskRect(r, pixels, mask_); + scaleRect(r); + } else { + DIBSectionBuffer::maskRect(r, pixels, mask_); + } +} + +void ScaledDIBSectionBuffer::maskRect(const Rect& r, Pixel pixel, const void* mask_) { + if (scaling) { + src_buffer->maskRect(r, pixel, mask_); + scaleRect(r); + } else { + DIBSectionBuffer::maskRect(r, pixel, mask_); + } +} diff --git a/win/rfb_win32/ScaledDIBSectionBuffer.h b/win/rfb_win32/ScaledDIBSectionBuffer.h new file mode 100644 index 00000000..3cc267b2 --- /dev/null +++ b/win/rfb_win32/ScaledDIBSectionBuffer.h @@ -0,0 +1,78 @@ +/* Copyright (C) 2006 TightVNC Team. All Rights Reserved. + * + * This 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 software 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 software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + * + * TightVNC distribution homepage on the Web: http://www.tightvnc.com/ + * + */ + +// -=- ScaledDIBSectionBuffer.h + +#ifndef __RFB_WIN32_SCALED_DIB_SECTION_BUFFER_H__ +#define __RFB_WIN32_SCALED_DIB_SECTION_BUFFER_H__ + +#include + +#include + +namespace rfb { + + namespace win32 { + + // + // -=- ScaledDIBSectionBuffer + // + + class ScaledDIBSectionBuffer : public ScaledPixelBuffer, public DIBSectionBuffer { + public: + ScaledDIBSectionBuffer(HWND window); + virtual ~ScaledDIBSectionBuffer(); + + int width() const { return scaled_width; } + int height() const { return scaled_height; } + bool isScaling() const { return scaling; } + + virtual void setPF(const PixelFormat &pf); + virtual void setSize(int w, int h); + virtual void setScale(int scale); + + Rect getRect() const { return ScaledPixelBuffer::getRect(); } + Rect getRect(const Point& pos) const { return ScaledPixelBuffer::getRect(pos); } + + // -=- Overrides basic rendering operations of + // FullFramePixelBuffer class + + virtual void fillRect(const Rect &dest, Pixel pix); + virtual void imageRect(const Rect &dest, const void* pixels, int stride=0); + virtual void copyRect(const Rect &dest, const Point &move_by_delta); + virtual void maskRect(const Rect& r, const void* pixels, const void* mask_); + virtual void maskRect(const Rect& r, Pixel pixel, const void* mask_); + + protected: + virtual void recreateScaledBuffer(); + virtual void recreateBuffer() { + recreateScaledBuffer(); + }; + + ManagedPixelBuffer *src_buffer; + bool scaling; + }; + + }; + +}; + +#endif // __RFB_WIN32_SCALED_DIB_SECTION_BUFFER_H__ diff --git a/win/rfb_win32/Security.cxx b/win/rfb_win32/Security.cxx new file mode 100644 index 00000000..985f00cb --- /dev/null +++ b/win/rfb_win32/Security.cxx @@ -0,0 +1,192 @@ +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * + * This 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 software 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 software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +// -=- Security.cxx + +#include +#include +#include + +#include +#include +#include + +using namespace rfb; +using namespace rfb::win32; + +static LogWriter vlog("SecurityWin32"); + + +Trustee::Trustee(const TCHAR* name, + TRUSTEE_FORM form, + TRUSTEE_TYPE type) { + pMultipleTrustee = 0; + MultipleTrusteeOperation = NO_MULTIPLE_TRUSTEE; + TrusteeForm = form; + TrusteeType = type; + ptstrName = (TCHAR*)name; +} + + +ExplicitAccess::ExplicitAccess(const TCHAR* name, + TRUSTEE_FORM type, + DWORD perms, + ACCESS_MODE mode, + DWORD inherit) { + Trustee = rfb::win32::Trustee(name, type); + grfAccessPermissions = perms; + grfAccessMode = mode; + grfInheritance = inherit; +} + + +AccessEntries::AccessEntries() : entries(0), entry_count(0) {} + +AccessEntries::~AccessEntries() { + delete [] entries; +} + +void AccessEntries::allocMinEntries(int count) { + if (count > entry_count) { + EXPLICIT_ACCESS* new_entries = new EXPLICIT_ACCESS[entry_count+1]; + if (entries) { + memcpy(new_entries, entries, sizeof(EXPLICIT_ACCESS) * entry_count); + delete entries; + } + entries = new_entries; + } +} + +void AccessEntries::addEntry(const TCHAR* trusteeName, + DWORD permissions, + ACCESS_MODE mode) { + allocMinEntries(entry_count+1); + ZeroMemory(&entries[entry_count], sizeof(EXPLICIT_ACCESS)); + entries[entry_count] = ExplicitAccess(trusteeName, TRUSTEE_IS_NAME, permissions, mode); + entry_count++; +} + +void AccessEntries::addEntry(const PSID sid, + DWORD permissions, + ACCESS_MODE mode) { + allocMinEntries(entry_count+1); + ZeroMemory(&entries[entry_count], sizeof(EXPLICIT_ACCESS)); + entries[entry_count] = ExplicitAccess((TCHAR*)sid, TRUSTEE_IS_SID, permissions, mode); + entry_count++; +} + + +PSID Sid::copySID(const PSID sid) { + if (!IsValidSid(sid)) + throw rdr::Exception("invalid SID in copyPSID"); + PSID buf = (PSID)new rdr::U8[GetLengthSid(sid)]; + if (!CopySid(GetLengthSid(sid), buf, sid)) + throw rdr::SystemException("CopySid failed", GetLastError()); + return buf; +} + +void Sid::setSID(const PSID sid) { + delete [] buf; + buf = (rdr::U8*)copySID(sid); +} + +void Sid::getUserNameAndDomain(TCHAR** name, TCHAR** domain) { + DWORD nameLen = 0; + DWORD domainLen = 0; + SID_NAME_USE use; + LookupAccountSid(0, (PSID)buf, 0, &nameLen, 0, &domainLen, &use); + if (GetLastError() != ERROR_INSUFFICIENT_BUFFER) + throw rdr::SystemException("Unable to determine SID name lengths", GetLastError()); + vlog.info("nameLen=%d, domainLen=%d, use=%d", nameLen, domainLen, use); + *name = new TCHAR[nameLen]; + *domain = new TCHAR[domainLen]; + if (!LookupAccountSid(0, (PSID)buf, *name, &nameLen, *domain, &domainLen, &use)) + throw rdr::SystemException("Unable to lookup account SID", GetLastError()); +} + + +Sid::Administrators::Administrators() { + PSID sid = 0; + SID_IDENTIFIER_AUTHORITY ntAuth = SECURITY_NT_AUTHORITY; + if (!AllocateAndInitializeSid(&ntAuth, 2, + SECURITY_BUILTIN_DOMAIN_RID, + DOMAIN_ALIAS_RID_ADMINS, + 0, 0, 0, 0, 0, 0, &sid)) + throw rdr::SystemException("Sid::Administrators", GetLastError()); + setSID(sid); + FreeSid(sid); +} + +Sid::SYSTEM::SYSTEM() { + PSID sid = 0; + SID_IDENTIFIER_AUTHORITY ntAuth = SECURITY_NT_AUTHORITY; + if (!AllocateAndInitializeSid(&ntAuth, 1, + SECURITY_LOCAL_SYSTEM_RID, + 0, 0, 0, 0, 0, 0, 0, &sid)) + throw rdr::SystemException("Sid::SYSTEM", GetLastError()); + setSID(sid); + FreeSid(sid); +} + +Sid::FromToken::FromToken(HANDLE h) { + DWORD required = 0; + GetTokenInformation(h, TokenUser, 0, 0, &required); + rdr::U8Array tmp(required); + if (!GetTokenInformation(h, TokenUser, tmp.buf, required, &required)) + throw rdr::SystemException("GetTokenInformation", GetLastError()); + TOKEN_USER* tokenUser = (TOKEN_USER*)tmp.buf; + setSID(tokenUser->User.Sid); +} + + +PACL rfb::win32::CreateACL(const AccessEntries& ae, PACL existing_acl) { + typedef DWORD (WINAPI *_SetEntriesInAcl_proto) (ULONG, PEXPLICIT_ACCESS, PACL, PACL*); +#ifdef UNICODE + const char* fnName = "SetEntriesInAclW"; +#else + const char* fnName = "SetEntriesInAclA"; +#endif + DynamicFn<_SetEntriesInAcl_proto> _SetEntriesInAcl(_T("advapi32.dll"), fnName); + if (!_SetEntriesInAcl.isValid()) + throw rdr::SystemException("CreateACL failed; no SetEntriesInAcl", ERROR_CALL_NOT_IMPLEMENTED); + PACL new_dacl; + DWORD result; + if ((result = (*_SetEntriesInAcl)(ae.entry_count, ae.entries, existing_acl, &new_dacl)) != ERROR_SUCCESS) + throw rdr::SystemException("SetEntriesInAcl", result); + return new_dacl; +} + + +PSECURITY_DESCRIPTOR rfb::win32::CreateSdWithDacl(const PACL dacl) { + SECURITY_DESCRIPTOR absSD; + if (!InitializeSecurityDescriptor(&absSD, SECURITY_DESCRIPTOR_REVISION)) + throw rdr::SystemException("InitializeSecurityDescriptor", GetLastError()); + Sid::SYSTEM owner; + if (!SetSecurityDescriptorOwner(&absSD, owner, FALSE)) + throw rdr::SystemException("SetSecurityDescriptorOwner", GetLastError()); + Sid::Administrators group; + if (!SetSecurityDescriptorGroup(&absSD, group, FALSE)) + throw rdr::SystemException("SetSecurityDescriptorGroupp", GetLastError()); + if (!SetSecurityDescriptorDacl(&absSD, TRUE, dacl, FALSE)) + throw rdr::SystemException("SetSecurityDescriptorDacl", GetLastError()); + DWORD sdSize = GetSecurityDescriptorLength(&absSD); + SecurityDescriptorPtr sd(sdSize); + if (!MakeSelfRelativeSD(&absSD, sd, &sdSize)) + throw rdr::SystemException("MakeSelfRelativeSD", GetLastError()); + return sd.takeSD(); +} diff --git a/win/rfb_win32/Security.h b/win/rfb_win32/Security.h new file mode 100644 index 00000000..1e2e9068 --- /dev/null +++ b/win/rfb_win32/Security.h @@ -0,0 +1,123 @@ +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * + * This 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 software 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 software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +// Security.h + +// Wrapper classes for a few Windows NT security structures/functions +// that are used by VNC + +#ifndef __RFB_WIN32_SECURITY_H__ +#define __RFB_WIN32_SECURITY_H__ + +#include +#include +#include +#include + +namespace rfb { + + namespace win32 { + + struct Trustee : public TRUSTEE { + Trustee(const TCHAR* name, + TRUSTEE_FORM form=TRUSTEE_IS_NAME, + TRUSTEE_TYPE type=TRUSTEE_IS_UNKNOWN); + }; + + struct ExplicitAccess : public EXPLICIT_ACCESS { + ExplicitAccess(const TCHAR* name, + TRUSTEE_FORM type, + DWORD perms, + ACCESS_MODE mode, + DWORD inherit=0); + }; + + // Helper class for building access control lists + struct AccessEntries { + AccessEntries(); + ~AccessEntries(); + void allocMinEntries(int count); + void addEntry(const TCHAR* trusteeName, + DWORD permissions, + ACCESS_MODE mode); + void addEntry(const PSID sid, + DWORD permissions, + ACCESS_MODE mode); + + EXPLICIT_ACCESS* entries; + int entry_count; + }; + + // Helper class for handling SIDs + struct Sid : rdr::U8Array { + Sid() {} + operator PSID() const {return (PSID)buf;} + PSID takePSID() {PSID r = (PSID)buf; buf = 0; return r;} + + static PSID copySID(const PSID sid); + + void setSID(const PSID sid); + + void getUserNameAndDomain(TCHAR** name, TCHAR** domain); + + struct Administrators; + struct SYSTEM; + struct FromToken; + + private: + Sid(const Sid&); + Sid& operator=(const Sid&); + }; + + struct Sid::Administrators : public Sid { + Administrators(); + }; + struct Sid::SYSTEM : public Sid { + SYSTEM(); + }; + struct Sid::FromToken : public Sid { + FromToken(HANDLE h); + }; + + // Helper class for handling & freeing ACLs + struct AccessControlList : public LocalMem { + AccessControlList(int size) : LocalMem(size) {} + AccessControlList(PACL acl_=0) : LocalMem(acl_) {} + operator PACL() {return (PACL)ptr;} + }; + + // Create a new ACL based on supplied entries and, if supplied, existing ACL + PACL CreateACL(const AccessEntries& ae, PACL existing_acl=0); + + // Helper class for memory-management of self-relative SecurityDescriptors + struct SecurityDescriptorPtr : LocalMem { + SecurityDescriptorPtr(int size) : LocalMem(size) {} + SecurityDescriptorPtr(PSECURITY_DESCRIPTOR sd_=0) : LocalMem(sd_) {} + PSECURITY_DESCRIPTOR takeSD() {return takePtr();} + }; + + // Create a new self-relative Security Descriptor, owned by SYSTEM/Administrators, + // with the supplied DACL and no SACL. The returned value can be assigned + // to a SecurityDescriptorPtr to be managed. + PSECURITY_DESCRIPTOR CreateSdWithDacl(const PACL dacl); + + } + +} + +#endif diff --git a/win/rfb_win32/Service.cxx b/win/rfb_win32/Service.cxx new file mode 100644 index 00000000..2b11a22d --- /dev/null +++ b/win/rfb_win32/Service.cxx @@ -0,0 +1,645 @@ +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * + * This 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 software 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 software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +// -=- Service.cxx + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +using namespace rdr; +using namespace rfb; +using namespace win32; + +static LogWriter vlog("Service"); + + +// - Internal service implementation functions + +Service* service = 0; + +VOID WINAPI serviceHandler(DWORD control) { + switch (control) { + case SERVICE_CONTROL_INTERROGATE: + vlog.info("cmd: report status"); + service->setStatus(); + return; + case SERVICE_CONTROL_PARAMCHANGE: + vlog.info("cmd: param change"); + service->readParams(); + return; + case SERVICE_CONTROL_SHUTDOWN: + vlog.info("cmd: OS shutdown"); + service->osShuttingDown(); + return; + case SERVICE_CONTROL_STOP: + vlog.info("cmd: stop"); + service->setStatus(SERVICE_STOP_PENDING); + service->stop(); + return; + }; + vlog.debug("cmd: unknown %lu", control); +} + + +// -=- Message window derived class used under Win9x to implement stopService + +#define WM_SMSG_SERVICE_STOP WM_USER + +class ServiceMsgWindow : public MsgWindow { +public: + ServiceMsgWindow(const TCHAR* name) : MsgWindow(name) {} + LRESULT processMessage(UINT msg, WPARAM wParam, LPARAM lParam) { + switch (msg) { + case WM_SMSG_SERVICE_STOP: + service->stop(); + return TRUE; + } + return MsgWindow::processMessage(msg, wParam, lParam); + } + + static const TCHAR* baseName; +}; + +const TCHAR* ServiceMsgWindow::baseName = _T("ServiceWindow:"); + + +// -=- Service main procedure, used under WinNT/2K/XP by the SCM + +VOID WINAPI serviceProc(DWORD dwArgc, LPTSTR* lpszArgv) { + vlog.debug("entering %s serviceProc", service->getName()); + vlog.info("registering handler..."); + service->status_handle = RegisterServiceCtrlHandler(service->getName(), serviceHandler); + if (!service->status_handle) { + DWORD err = GetLastError(); + vlog.error("failed to register handler: %lu", err); + ExitProcess(err); + } + vlog.debug("registered handler (%lx)", service->status_handle); + service->setStatus(SERVICE_START_PENDING); + vlog.debug("entering %s serviceMain", service->getName()); + service->status.dwWin32ExitCode = service->serviceMain(dwArgc, lpszArgv); + vlog.debug("leaving %s serviceMain", service->getName()); + service->setStatus(SERVICE_STOPPED); +} + + +// -=- Service + +Service::Service(const TCHAR* name_) : name(name_) { + vlog.debug("Service"); + status_handle = 0; + status.dwControlsAccepted = SERVICE_CONTROL_INTERROGATE | SERVICE_ACCEPT_SHUTDOWN | SERVICE_ACCEPT_STOP; + status.dwServiceType = SERVICE_WIN32_OWN_PROCESS | SERVICE_INTERACTIVE_PROCESS; + status.dwWin32ExitCode = NO_ERROR; + status.dwServiceSpecificExitCode = 0; + status.dwCheckPoint = 0; + status.dwWaitHint = 30000; + status.dwCurrentState = SERVICE_STOPPED; +} + +void +Service::start() { + if (osVersion.isPlatformNT) { + SERVICE_TABLE_ENTRY entry[2]; + entry[0].lpServiceName = (TCHAR*)name; + entry[0].lpServiceProc = serviceProc; + entry[1].lpServiceName = NULL; + entry[1].lpServiceProc = NULL; + vlog.debug("entering dispatcher"); + if (!SetProcessShutdownParameters(0x100, 0)) + vlog.error("unable to set shutdown parameters: %d", GetLastError()); + service = this; + if (!StartServiceCtrlDispatcher(entry)) + throw SystemException("unable to start service", GetLastError()); + } else { + + // - Create the service window, so the service can be stopped + TCharArray wndName(_tcslen(getName()) + _tcslen(ServiceMsgWindow::baseName) + 1); + _tcscpy(wndName.buf, ServiceMsgWindow::baseName); + _tcscat(wndName.buf, getName()); + ServiceMsgWindow service_window(wndName.buf); + + // - Locate the RegisterServiceProcess function + typedef DWORD (WINAPI * _RegisterServiceProcess_proto)(DWORD, DWORD); + DynamicFn<_RegisterServiceProcess_proto> _RegisterServiceProcess(_T("kernel32.dll"), "RegisterServiceProcess"); + if (!_RegisterServiceProcess.isValid()) + throw Exception("unable to find RegisterServiceProcess"); + + // - Run the service + (*_RegisterServiceProcess)(NULL, 1); + service = this; + serviceMain(0, 0); + (*_RegisterServiceProcess)(NULL, 0); + } +} + +void +Service::setStatus() { + setStatus(status.dwCurrentState); +} + +void +Service::setStatus(DWORD state) { + if (!osVersion.isPlatformNT) + return; + if (status_handle == 0) { + vlog.debug("warning - cannot setStatus"); + return; + } + status.dwCurrentState = state; + status.dwCheckPoint++; + if (!SetServiceStatus(status_handle, &status)) { + status.dwCurrentState = SERVICE_STOPPED; + status.dwWin32ExitCode = GetLastError(); + vlog.error("unable to set service status:%u", status.dwWin32ExitCode); + } + vlog.debug("set status to %u(%u)", state, status.dwCheckPoint); +} + +Service::~Service() { + vlog.debug("~Service"); + service = 0; +} + + +// Find out whether this process is running as the WinVNC service +bool thisIsService() { + return service && (service->status.dwCurrentState != SERVICE_STOPPED); +} + + +// -=- Desktop handling code + +// Switch the current thread to the specified desktop +static bool +switchToDesktop(HDESK desktop) { + HDESK old_desktop = GetThreadDesktop(GetCurrentThreadId()); + if (!SetThreadDesktop(desktop)) { + vlog.debug("switchToDesktop failed:%u", GetLastError()); + return false; + } + if (!CloseDesktop(old_desktop)) + vlog.debug("unable to close old desktop:%u", GetLastError()); + return true; +} + +// Determine whether the thread's current desktop is the input one +static bool +inputDesktopSelected() { + HDESK current = GetThreadDesktop(GetCurrentThreadId()); + HDESK input = OpenInputDesktop(0, FALSE, + DESKTOP_CREATEMENU | DESKTOP_CREATEWINDOW | + DESKTOP_ENUMERATE | DESKTOP_HOOKCONTROL | + DESKTOP_WRITEOBJECTS | DESKTOP_READOBJECTS | + DESKTOP_SWITCHDESKTOP | GENERIC_WRITE); + if (!input) { + vlog.debug("unable to OpenInputDesktop(1):%u", GetLastError()); + return false; + } + + DWORD size; + char currentname[256]; + char inputname[256]; + + if (!GetUserObjectInformation(current, UOI_NAME, currentname, 256, &size)) { + vlog.debug("unable to GetUserObjectInformation(1):%u", GetLastError()); + CloseDesktop(input); + return false; + } + if (!GetUserObjectInformation(input, UOI_NAME, inputname, 256, &size)) { + vlog.debug("unable to GetUserObjectInformation(2):%u", GetLastError()); + CloseDesktop(input); + return false; + } + if (!CloseDesktop(input)) + vlog.debug("unable to close input desktop:%u", GetLastError()); + + // *** vlog.debug("current=%s, input=%s", currentname, inputname); + bool result = strcmp(currentname, inputname) == 0; + return result; +} + +// Switch the current thread into the input desktop +static bool +selectInputDesktop() { + // - Open the input desktop + HDESK desktop = OpenInputDesktop(0, FALSE, + DESKTOP_CREATEMENU | DESKTOP_CREATEWINDOW | + DESKTOP_ENUMERATE | DESKTOP_HOOKCONTROL | + DESKTOP_WRITEOBJECTS | DESKTOP_READOBJECTS | + DESKTOP_SWITCHDESKTOP | GENERIC_WRITE); + if (!desktop) { + vlog.debug("unable to OpenInputDesktop(2):%u", GetLastError()); + return false; + } + + // - Switch into it + if (!switchToDesktop(desktop)) { + CloseDesktop(desktop); + return false; + } + + // *** + DWORD size = 256; + char currentname[256]; + if (GetUserObjectInformation(desktop, UOI_NAME, currentname, 256, &size)) { + vlog.debug("switched to %s", currentname); + } + // *** + + vlog.debug("switched to input desktop"); + + return true; +} + + +// -=- Access points to desktop-switching routines + +bool +rfb::win32::desktopChangeRequired() { + if (!osVersion.isPlatformNT) + return false; + + return !inputDesktopSelected(); +} + +bool +rfb::win32::changeDesktop() { + if (!osVersion.isPlatformNT) + return true; + if (osVersion.cannotSwitchDesktop) + return false; + + return selectInputDesktop(); +} + + +// -=- Ctrl-Alt-Del emulation + +class CADThread : public Thread { +public: + CADThread() : Thread("CtrlAltDel Emulator"), result(false) {} + virtual void run() { + HDESK old_desktop = GetThreadDesktop(GetCurrentThreadId()); + + if (switchToDesktop(OpenDesktop(_T("Winlogon"), 0, FALSE, DESKTOP_CREATEMENU | DESKTOP_CREATEWINDOW | + DESKTOP_ENUMERATE | DESKTOP_HOOKCONTROL | + DESKTOP_WRITEOBJECTS | DESKTOP_READOBJECTS | + DESKTOP_SWITCHDESKTOP | GENERIC_WRITE))) { + PostMessage(HWND_BROADCAST, WM_HOTKEY, 0, MAKELONG(MOD_ALT | MOD_CONTROL, VK_DELETE)); + switchToDesktop(old_desktop); + result = true; + } + } + bool result; +}; + +bool +rfb::win32::emulateCtrlAltDel() { + if (!osVersion.isPlatformNT) + return false; + + CADThread* cad_thread = new CADThread(); + vlog.debug("emulate Ctrl-Alt-Del"); + if (cad_thread) { + cad_thread->start(); + cad_thread->join(); + bool result = cad_thread->result; + delete cad_thread; + return result; + } + return false; +} + + +// -=- Application Event Log target Logger class + +class Logger_EventLog : public Logger { +public: + Logger_EventLog(const TCHAR* srcname) : Logger("EventLog") { + eventlog = RegisterEventSource(NULL, srcname); + if (!eventlog) + printf("Unable to open event log:%ld\n", GetLastError()); + } + ~Logger_EventLog() { + if (eventlog) + DeregisterEventSource(eventlog); + } + + virtual void write(int level, const char *logname, const char *message) { + if (!eventlog) return; + TStr log(logname), msg(message); + const TCHAR* strings[] = {log, msg}; + WORD type = EVENTLOG_INFORMATION_TYPE; + if (level == 0) type = EVENTLOG_ERROR_TYPE; + if (!ReportEvent(eventlog, type, 0, VNC4LogMessage, NULL, 2, 0, strings, NULL)) { + // *** It's not at all clear what is the correct behaviour if this fails... + printf("ReportEvent failed:%ld\n", GetLastError()); + } + } + +protected: + HANDLE eventlog; +}; + +static Logger_EventLog* logger = 0; + +bool rfb::win32::initEventLogLogger(const TCHAR* srcname) { + if (logger) + return false; + if (osVersion.isPlatformNT) { + logger = new Logger_EventLog(srcname); + logger->registerLogger(); + return true; + } else { + return false; + } +} + + +// -=- Registering and unregistering the service + +bool rfb::win32::registerService(const TCHAR* name, const TCHAR* desc, + int argc, const char* argv[]) { + + // - Initialise the default service parameters + const TCHAR* defaultcmdline; + if (osVersion.isPlatformNT) + defaultcmdline = _T("-service"); + else + defaultcmdline = _T("-noconsole -service"); + + // - Get the full pathname of our executable + ModuleFileName buffer; + + // - Calculate the command-line length + int cmdline_len = _tcslen(buffer.buf) + 4; + int i; + for (i=0; i0; i--) { + if (buffer.buf[i] == _T('\\')) { + buffer.buf[i+1] = 0; + break; + } + } + + const TCHAR* dllFilename = _T("logmessages.dll"); + TCharArray dllPath(_tcslen(buffer.buf) + _tcslen(dllFilename) + 1); + _tcscpy(dllPath.buf, buffer.buf); + _tcscat(dllPath.buf, dllFilename); + + hk.setExpandString(_T("EventMessageFile"), dllPath.buf); + hk.setInt(_T("TypesSupported"), EVENTLOG_ERROR_TYPE | EVENTLOG_INFORMATION_TYPE); + + } else { + + RegKey services; + services.createKey(HKEY_LOCAL_MACHINE, _T("Software\\Microsoft\\Windows\\CurrentVersion\\RunServices")); + services.setString(name, cmdline.buf); + + } + + Sleep(500); + + return true; +} + +bool rfb::win32::unregisterService(const TCHAR* name) { + if (osVersion.isPlatformNT) { + + // - Open the SCM + ServiceHandle scm = OpenSCManager(NULL, NULL, SC_MANAGER_CREATE_SERVICE); + if (!scm) + throw rdr::SystemException("unable to open Service Control Manager", GetLastError()); + + // - Create the service + ServiceHandle service = OpenService(scm, name, SC_MANAGER_ALL_ACCESS); + if (!service) + throw rdr::SystemException("unable to locate the service", GetLastError()); + if (!DeleteService(service)) + throw rdr::SystemException("unable to remove the service", GetLastError()); + + // - Register the event log source + RegKey hk; + hk.openKey(HKEY_LOCAL_MACHINE, _T("SYSTEM\\CurrentControlSet\\Services\\EventLog\\Application")); + hk.deleteKey(name); + + } else { + + RegKey services; + services.openKey(HKEY_LOCAL_MACHINE, _T("Software\\Microsoft\\Windows\\CurrentVersion\\RunServices")); + services.deleteValue(name); + + } + + Sleep(500); + + return true; +} + + +// -=- Starting and stopping the service + +HWND findServiceWindow(const TCHAR* name) { + TCharArray wndName(_tcslen(ServiceMsgWindow::baseName)+_tcslen(name)+1); + _tcscpy(wndName.buf, ServiceMsgWindow::baseName); + _tcscat(wndName.buf, name); + vlog.debug("searching for %s window", CStr(wndName.buf)); + return FindWindow(0, wndName.buf); +} + +bool rfb::win32::startService(const TCHAR* name) { + + if (osVersion.isPlatformNT) { + // - Open the SCM + ServiceHandle scm = OpenSCManager(NULL, NULL, SC_MANAGER_CONNECT); + if (!scm) + throw rdr::SystemException("unable to open Service Control Manager", GetLastError()); + + // - Locate the service + ServiceHandle service = OpenService(scm, name, SERVICE_START); + if (!service) + throw rdr::SystemException("unable to open the service", GetLastError()); + + // - Start the service + if (!StartService(service, 0, NULL)) + throw rdr::SystemException("unable to start the service", GetLastError()); + } else { + // - Check there is no service window + if (findServiceWindow(name)) + throw rdr::Exception("the service is already running"); + + // - Find the RunServices registry key + RegKey services; + services.openKey(HKEY_LOCAL_MACHINE, _T("Software\\Microsoft\\Windows\\CurrentVersion\\RunServices")); + + // - Read the command-line from it + TCharArray cmdLine = services.getString(name); + + // - Start the service + PROCESS_INFORMATION proc_info; + STARTUPINFO startup_info; + ZeroMemory(&startup_info, sizeof(startup_info)); + startup_info.cb = sizeof(startup_info); + if (!CreateProcess(0, cmdLine.buf, 0, 0, FALSE, CREATE_NEW_CONSOLE, 0, 0, &startup_info, &proc_info)) { + throw SystemException("unable to start service", GetLastError()); + } + } + + Sleep(500); + + return true; +} + +bool rfb::win32::stopService(const TCHAR* name) { + if (osVersion.isPlatformNT) { + // - Open the SCM + ServiceHandle scm = OpenSCManager(NULL, NULL, SC_MANAGER_CONNECT); + if (!scm) + throw rdr::SystemException("unable to open Service Control Manager", GetLastError()); + + // - Locate the service + ServiceHandle service = OpenService(scm, name, SERVICE_STOP); + if (!service) + throw rdr::SystemException("unable to open the service", GetLastError()); + + // - Start the service + SERVICE_STATUS status; + if (!ControlService(service, SERVICE_CONTROL_STOP, &status)) + throw rdr::SystemException("unable to stop the service", GetLastError()); + + } else { + // - Find the service window + HWND service_window = findServiceWindow(name); + if (!service_window) + throw Exception("unable to locate running service"); + + // Tell it to quit + vlog.debug("sending service stop request"); + if (!SendMessage(service_window, WM_SMSG_SERVICE_STOP, 0, 0)) + throw Exception("unable to stop service"); + + // Check it's quitting... + DWORD process_id = 0; + HANDLE process = 0; + if (!GetWindowThreadProcessId(service_window, &process_id)) + throw SystemException("unable to verify service has quit", GetLastError()); + process = OpenProcess(SYNCHRONIZE | PROCESS_TERMINATE, FALSE, process_id); + if (!process) + throw SystemException("unable to obtain service handle", GetLastError()); + int retries = 5; + vlog.debug("checking status"); + while (retries-- && (WaitForSingleObject(process, 1000) != WAIT_OBJECT_0)) {} + if (!retries) { + vlog.debug("failed to quit - terminating"); + // May not have quit because of silly Win9x registry watching bug.. + if (!TerminateProcess(process, 1)) + throw SystemException("unable to terminate process!", GetLastError()); + throw Exception("service failed to quit - called TerminateProcess"); + } + } + + Sleep(500); + + return true; +} + +DWORD rfb::win32::getServiceState(const TCHAR* name) { + if (osVersion.isPlatformNT) { + // - Open the SCM + ServiceHandle scm = OpenSCManager(NULL, NULL, SC_MANAGER_CONNECT); + if (!scm) + throw rdr::SystemException("unable to open Service Control Manager", GetLastError()); + + // - Locate the service + ServiceHandle service = OpenService(scm, name, SERVICE_INTERROGATE); + if (!service) + throw rdr::SystemException("unable to open the service", GetLastError()); + + // - Get the service status + SERVICE_STATUS status; + if (!ControlService(service, SERVICE_CONTROL_INTERROGATE, (SERVICE_STATUS*)&status)) + throw rdr::SystemException("unable to query the service", GetLastError()); + + return status.dwCurrentState; + } else { + HWND service_window = findServiceWindow(name); + return service_window ? SERVICE_RUNNING : SERVICE_STOPPED; + } +} + +char* rfb::win32::serviceStateName(DWORD state) { + switch (state) { + case SERVICE_RUNNING: return strDup("Running"); + case SERVICE_STOPPED: return strDup("Stopped"); + case SERVICE_STOP_PENDING: return strDup("Stopping"); + }; + CharArray tmp(32); + sprintf(tmp.buf, "Unknown (%lu)", state); + return tmp.takeBuf(); +} + + +bool rfb::win32::isServiceProcess() { + return service != 0; +} diff --git a/win/rfb_win32/Service.h b/win/rfb_win32/Service.h new file mode 100644 index 00000000..00abe108 --- /dev/null +++ b/win/rfb_win32/Service.h @@ -0,0 +1,128 @@ +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * + * This 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 software 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 software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +// -=- Service.h +// +// Win32 service-mode code. +// Derive your service from this code and let it handle the annoying Win32 +// service API. +// The underlying implementation takes care of the differences between +// Windows NT and Windows 95 based systems + +#ifndef __RFB_WIN32_SERVICE_H__ +#define __RFB_WIN32_SERVICE_H__ + +#include + +namespace rfb { + + namespace win32 { + + // + // -=- Service + // + + // Application base-class for services. + + class Service { + public: + + Service(const TCHAR* name_); + virtual ~Service(); + + const TCHAR* getName() {return name;} + SERVICE_STATUS& getStatus() {return status;} + + void setStatus(DWORD status); + void setStatus(); + + // - Start the service, having initialised it + void start(); + + // - Service main procedure - override to implement a service + virtual DWORD serviceMain(int argc, TCHAR* argv[]) = 0; + + // - Service control notifications + + // To get notified when the OS is shutting down + virtual void osShuttingDown() {}; + + // To get notified when the service parameters change + virtual void readParams() {}; + + // To cause the serviceMain() routine to return + virtual void stop() {}; + + public: + SERVICE_STATUS_HANDLE status_handle; + SERVICE_STATUS status; + protected: + const TCHAR* name; + }; + + class ServiceHandle { + public: + ServiceHandle(SC_HANDLE h) : handle(h) {} + ~ServiceHandle() {CloseServiceHandle(handle);} + operator SC_HANDLE() const {return handle;} + protected: + SC_HANDLE handle; + }; + + // -=- Routines used by desktop back-end code to manage desktops/window stations + + // Returns false under Win9x + bool desktopChangeRequired(); + + // Returns true under Win9x + bool changeDesktop(); + + // -=- Routines used by the SInput Keyboard class to emulate Ctrl-Alt-Del + // Returns false under Win9x + bool emulateCtrlAltDel(); + + // -=- Routines to initialise the Event Log target Logger + // Returns false under Win9x + bool initEventLogLogger(const TCHAR* srcname); + + // -=- Routines to register/unregister the service + // These routines also take care of registering the required + // event source information, etc. + // *** should really accept TCHAR argv + + bool registerService(const TCHAR* name, const TCHAR* desc, int argc, const char* argv[]); + bool unregisterService(const TCHAR* name); + + bool startService(const TCHAR* name); + bool stopService(const TCHAR* name); + + // -=- Get the state of the named service (one of the NT service state values) + DWORD getServiceState(const TCHAR* name); + + // -=- Convert a supplied service state value to a printable string e.g. Running, Stopped... + // The caller must delete the returned string buffer + char* serviceStateName(DWORD state); + + // -=- Routine to determine whether the host process is running a service + bool isServiceProcess(); + + }; + +}; + +#endif // __RFB_WIN32_SERVICE_NT_H__ diff --git a/win/rfb_win32/SocketManager.cxx b/win/rfb_win32/SocketManager.cxx new file mode 100644 index 00000000..1d52bc86 --- /dev/null +++ b/win/rfb_win32/SocketManager.cxx @@ -0,0 +1,213 @@ +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * + * This 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 software 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 software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +// -=- SocketManager.cxx + +#include +#include +#include +#include + +using namespace rfb; +using namespace rfb::win32; + +static LogWriter vlog("SocketManager"); + + +// -=- SocketManager + +SocketManager::SocketManager() { +} + +SocketManager::~SocketManager() { +} + + +static requestAddressChangeEvents(network::SocketListener* sock_) { + DWORD dummy = 0; + if (WSAIoctl(sock_->getFd(), SIO_ADDRESS_LIST_CHANGE, 0, 0, 0, 0, &dummy, 0, 0) == SOCKET_ERROR) { + DWORD err = WSAGetLastError(); + if (err != WSAEWOULDBLOCK) + vlog.error("Unable to track address changes", err); + } +} + + +void SocketManager::addListener(network::SocketListener* sock_, + network::SocketServer* srvr, + AddressChangeNotifier* acn) { + WSAEVENT event = WSACreateEvent(); + long flags = FD_ACCEPT | FD_CLOSE; + if (acn) + flags |= FD_ADDRESS_LIST_CHANGE; + try { + if (event && (WSAEventSelect(sock_->getFd(), event, flags) == SOCKET_ERROR)) + throw rdr::SystemException("Unable to select on listener", WSAGetLastError()); + + // requestAddressChangeEvents MUST happen after WSAEventSelect, so that the socket is non-blocking + if (acn) + requestAddressChangeEvents(sock_); + + // addEvent is the last thing we do, so that the event is NOT registered if previous steps fail + if (!event || !addEvent(event, this)) + throw rdr::Exception("Unable to add listener"); + } catch (rdr::Exception& e) { + if (event) + WSACloseEvent(event); + delete sock_; + vlog.error(e.str()); + throw; + } + + ListenInfo li; + li.sock = sock_; + li.server = srvr; + li.notifier = acn; + listeners[event] = li; +} + +void SocketManager::remListener(network::SocketListener* sock) { + std::map::iterator i; + for (i=listeners.begin(); i!=listeners.end(); i++) { + if (i->second.sock == sock) { + removeEvent(i->first); + WSACloseEvent(i->first); + delete sock; + listeners.erase(i); + return; + } + } + throw rdr::Exception("Listener not registered"); +} + + +void SocketManager::addSocket(network::Socket* sock_, network::SocketServer* srvr, bool outgoing) { + WSAEVENT event = WSACreateEvent(); + if (!event || !addEvent(event, this) || + (WSAEventSelect(sock_->getFd(), event, FD_READ | FD_CLOSE) == SOCKET_ERROR)) { + if (event) + WSACloseEvent(event); + delete sock_; + vlog.error("Unable to add connection"); + return; + } + ConnInfo ci; + ci.sock = sock_; + ci.server = srvr; + connections[event] = ci; + srvr->addSocket(sock_, outgoing); +} + +void SocketManager::remSocket(network::Socket* sock_) { + std::map::iterator i; + for (i=connections.begin(); i!=connections.end(); i++) { + if (i->second.sock == sock_) { + i->second.server->removeSocket(sock_); + removeEvent(i->first); + WSACloseEvent(i->first); + delete sock_; + connections.erase(i); + return; + } + } + throw rdr::Exception("Socket not registered"); +} + + +int SocketManager::checkTimeouts() { + network::SocketServer* server = 0; + int timeout = EventManager::checkTimeouts(); + + std::map::iterator i; + for (i=listeners.begin(); i!=listeners.end(); i++) + soonestTimeout(&timeout, i->second.server->checkTimeouts()); + + std::list shutdownSocks; + std::map::iterator j, j_next; + for (j=connections.begin(); j!=connections.end(); j=j_next) { + j_next = j; j_next++; + if (j->second.sock->isShutdown()) + shutdownSocks.push_back(j->second.sock); + } + + std::list::iterator k; + for (k=shutdownSocks.begin(); k!=shutdownSocks.end(); k++) + remSocket(*k); + + return timeout; +} + + +void SocketManager::processEvent(HANDLE event) { + if (listeners.count(event)) { + ListenInfo li = listeners[event]; + + // Accept an incoming connection + vlog.debug("accepting incoming connection"); + + // What kind of event is this? + WSANETWORKEVENTS network_events; + WSAEnumNetworkEvents(li.sock->getFd(), event, &network_events); + if (network_events.lNetworkEvents & FD_ACCEPT) { + network::Socket* new_sock = li.sock->accept(); + if (new_sock && li.server->getDisable()) { + delete new_sock; + new_sock = 0; + } + if (new_sock) + addSocket(new_sock, li.server, false); + } else if (network_events.lNetworkEvents & FD_CLOSE) { + vlog.info("deleting listening socket"); + remListener(li.sock); + } else if (network_events.lNetworkEvents & FD_ADDRESS_LIST_CHANGE) { + li.notifier->processAddressChange(li.sock); + DWORD dummy = 0; + requestAddressChangeEvents(li.sock); + } else { + vlog.error("unknown listener event: %lx", network_events.lNetworkEvents); + } + } else if (connections.count(event)) { + ConnInfo ci = connections[event]; + + try { + // Process data from an active connection + + // Cancel event notification for this socket + if (WSAEventSelect(ci.sock->getFd(), event, 0) == SOCKET_ERROR) + throw rdr::SystemException("unable to disable WSAEventSelect:%u", WSAGetLastError()); + + // Reset the event object + WSAResetEvent(event); + + // Call the socket server to process the event + ci.server->processSocketEvent(ci.sock); + if (ci.sock->isShutdown()) { + remSocket(ci.sock); + return; + } + + // Re-instate the required socket event + // If the read event is still valid, the event object gets set here + if (WSAEventSelect(ci.sock->getFd(), event, FD_READ | FD_CLOSE) == SOCKET_ERROR) + throw rdr::SystemException("unable to re-enable WSAEventSelect:%u", WSAGetLastError()); + } catch (rdr::Exception& e) { + vlog.error(e.str()); + remSocket(ci.sock); + } + } +} diff --git a/win/rfb_win32/SocketManager.h b/win/rfb_win32/SocketManager.h new file mode 100644 index 00000000..ef359749 --- /dev/null +++ b/win/rfb_win32/SocketManager.h @@ -0,0 +1,90 @@ +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * + * This 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 software 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 software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +// -=- SocketManager.h + +// Socket manager class for Win32. +// Passed a network::SocketListener and a network::SocketServer when +// constructed. Uses WSAAsyncSelect to get notifications of network +// connection attempts. When an incoming connection is received, +// the manager will call network::SocketServer::addClient(). If +// addClient returns true then the manager registers interest in +// network events on that socket, and calls +// network::SocketServer::processSocketEvent(). + +#ifndef __RFB_WIN32_SOCKET_MGR_H__ +#define __RFB_WIN32_SOCKET_MGR_H__ + +#include +#include +#include + +namespace rfb { + namespace win32 { + + class SocketManager : public EventManager, EventHandler { + public: + SocketManager(); + virtual ~SocketManager(); + + // AddressChangeNotifier callback interface + // If an object implementing this is passed to addListener then it will be + // called whenever the SocketListener's address list changes + class AddressChangeNotifier { + public: + virtual ~AddressChangeNotifier() {} + virtual void processAddressChange(network::SocketListener* sl) = 0; + }; + + // Add a listening socket. Incoming connections will be added to the supplied + // SocketServer. + void addListener(network::SocketListener* sock_, + network::SocketServer* srvr, + AddressChangeNotifier* acn = 0); + + // Remove and delete a listening socket. + void remListener(network::SocketListener* sock); + + // Add an already-connected socket. Socket events will cause the supplied + // SocketServer to be called. The socket must ALREADY BE REGISTERED with + // the SocketServer. + void addSocket(network::Socket* sock_, network::SocketServer* srvr, bool outgoing=true); + + protected: + virtual int checkTimeouts(); + virtual void processEvent(HANDLE event); + virtual void remSocket(network::Socket* sock); + + struct ConnInfo { + network::Socket* sock; + network::SocketServer* server; + }; + struct ListenInfo { + network::SocketListener* sock; + network::SocketServer* server; + AddressChangeNotifier* notifier; + }; + std::map listeners; + std::map connections; + }; + + } + +} + +#endif diff --git a/win/rfb_win32/TCharArray.cxx b/win/rfb_win32/TCharArray.cxx new file mode 100644 index 00000000..fd4c0783 --- /dev/null +++ b/win/rfb_win32/TCharArray.cxx @@ -0,0 +1,85 @@ +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * + * This 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 software 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 software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +#include + +namespace rfb { + + WCHAR* wstrDup(const WCHAR* s) { + if (!s) return 0; + WCHAR* t = new WCHAR[wcslen(s)+1]; + memcpy(t, s, sizeof(WCHAR)*(wcslen(s)+1)); + return t; + } + void wstrFree(WCHAR* s) {delete [] s;} + + char* strDup(const WCHAR* s) { + if (!s) return 0; + int len = wcslen(s); + char* t = new char[len+1]; + t[WideCharToMultiByte(CP_ACP, 0, s, len, t, len, 0, 0)] = 0; + return t; + } + + WCHAR* wstrDup(const char* s) { + if (!s) return 0; + int len = strlen(s); + WCHAR* t = new WCHAR[len+1]; + t[MultiByteToWideChar(CP_ACP, 0, s, len, t, len)] = 0; + return t; + } + + + bool wstrSplit(const WCHAR* src, const WCHAR limiter, WCHAR** out1, WCHAR** out2, bool fromEnd) { + WCharArray out1old, out2old; + if (out1) out1old.buf = *out1; + if (out2) out2old.buf = *out2; + int len = wcslen(src); + int i=0, increment=1, limit=len; + if (fromEnd) { + i=len-1; increment = -1; limit = -1; + } + while (i!=limit) { + if (src[i] == limiter) { + if (out1) { + *out1 = new WCHAR[i+1]; + if (i) memcpy(*out1, src, sizeof(WCHAR)*i); + (*out1)[i] = 0; + } + if (out2) { + *out2 = new WCHAR[len-i]; + if (len-i-1) memcpy(*out2, &src[i+1], sizeof(WCHAR)*(len-i-1)); + (*out2)[len-i-1] = 0; + } + return true; + } + i+=increment; + } + if (out1) *out1 = wstrDup(src); + if (out2) *out2 = 0; + return false; + } + + bool wstrContains(const WCHAR* src, WCHAR c) { + int l=wcslen(src); + for (int i=0; i +#include +#include +#include + +namespace rfb { + + // -=- String duplication and cleanup functions. + // These routines also handle conversion between WCHAR* and char* + + char* strDup(const WCHAR* s); + WCHAR* wstrDup(const WCHAR* s); + WCHAR* wstrDup(const char* s); + void wstrFree(WCHAR* s); + + bool wstrSplit(const WCHAR* src, const WCHAR limiter, WCHAR** out1, WCHAR** out2, bool fromEnd=false); + bool wstrContains(const WCHAR* src, WCHAR c); + + // -=- Temporary format conversion classes + // CStr accepts WCHAR* or char* and behaves like a char* + // WStr accepts WCHAR* or char* and behaves like a WCHAR* + + struct WStr { + WStr(const char* s) : buf(wstrDup(s)), free_(true) {} + WStr(const WCHAR* s) : buf(s), free_(false) {} + ~WStr() {if (free_) wstrFree((WCHAR*)buf);} + operator const WCHAR*() {return buf;} + const WCHAR* buf; + bool free_; + }; + + struct CStr { + CStr(const char* s) : buf(s), free_(false) {} + CStr(const WCHAR* s) : buf(strDup(s)), free_(true) {} + ~CStr() {if (free_) strFree((char*)buf);} + operator const char*() {return buf;} + const char* buf; + bool free_; + }; + + // -=- Class to handle cleanup of arrays of native Win32 characters + class WCharArray { + public: + WCharArray() : buf(0) {} + WCharArray(char* str) : buf(wstrDup(str)) {strFree(str);} // note: assumes ownership + WCharArray(WCHAR* str) : buf(str) {} // note: assumes ownership + WCharArray(int len) { + buf = new WCHAR[len]; + } + ~WCharArray() { + delete [] buf; + } + // Get the buffer pointer & clear it (i.e. caller takes ownership) + WCHAR* takeBuf() {WCHAR* tmp = buf; buf = 0; return tmp;} + void replaceBuf(WCHAR* str) {delete [] buf; buf = str;} + WCHAR* buf; + }; + + // -=- Wide-character-based password-buffer handler. Zeroes the password + // buffer when deleted or replaced. + class WPlainPasswd : public WCharArray { + public: + WPlainPasswd() {} + WPlainPasswd(WCHAR* str) : WCharArray(str) {} + ~WPlainPasswd() {replaceBuf(0);} + void replaceBuf(WCHAR* str) { + if (buf) + memset(buf, 0, sizeof(WCHAR)*wcslen(buf)); + WCharArray::replaceBuf(str); + } + }; + +#ifdef _UNICODE +#define tstrDup wstrDup +#define tstrFree wstrFree +#define tstrSplit wstrSplit +#define tstrContains wstrContains + typedef WCharArray TCharArray; + typedef WStr TStr; + typedef WPlainPasswd TPlainPasswd; +#else +#define tstrDup strDup +#define tstrFree strFree +#define tstrSplit strSplit +#define tstrContains strContains + typedef CharArray TCharArray; + typedef CStr TStr; + typedef PlainPasswd TPlainPasswd; +#endif + +}; + +#endif diff --git a/win/rfb_win32/Threading.cxx b/win/rfb_win32/Threading.cxx new file mode 100644 index 00000000..c41ac38b --- /dev/null +++ b/win/rfb_win32/Threading.cxx @@ -0,0 +1,151 @@ +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * + * This 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 software 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 software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +// -=- Threading.cxx +// Win32 Threading interface implementation + +#include + +#include +#include +#include +#include + +using namespace rfb; + +static LogWriter vlog("Threading"); + +static DWORD threadStorage = TlsAlloc(); + + +inline void logAction(Thread* t, const char* action) { + vlog.debug("%-16.16s %s(%lx)", action, t->getName(), t); +} + +inline void logError(Thread* t, const char* err) { + vlog.error("%-16.16s %s(%lx):%s", "failed", t->getName(), t, err); +} + + +DWORD WINAPI +Thread::threadProc(LPVOID lpParameter) { + Thread* thread = (Thread*) lpParameter; + TlsSetValue(threadStorage, thread); + logAction(thread, "started"); + try { + thread->run(); + logAction(thread, "stopped"); + } catch (rdr::Exception& e) { + logError(thread, e.str()); + } + bool deleteThread = false; + { + Lock l(thread->mutex); + thread->state = ThreadStopped; + thread->sig->signal(); + deleteThread = thread->deleteAfterRun; + } + if (deleteThread) + delete thread; + return 0; +} + +Thread::Thread(const char* name_) : name(strDup(name_ ? name_ : "Unnamed")), sig(0), deleteAfterRun(false) { + sig = new Condition(mutex); + cond_event.h = CreateEvent(NULL, TRUE, FALSE, NULL); + thread.h = CreateThread(NULL, 0, threadProc, this, CREATE_SUSPENDED, &thread_id); + state = ThreadCreated; + logAction(this, "created"); +} + +Thread::Thread(HANDLE thread_, DWORD thread_id_) : name(strDup("Native")), sig(0), deleteAfterRun(false), + thread(thread_), thread_id(thread_id_) { + cond_event.h = CreateEvent(NULL, TRUE, FALSE, NULL); + state = ThreadNative; + logAction(this, "created"); +} + +Thread::~Thread() { + logAction(this, "destroying"); + if (!deleteAfterRun && state != ThreadNative) + this->join(); + if (sig) + delete sig; + logAction(this, "destroyed"); +} + +void +Thread::run() { +} + +void +Thread::start() { + Lock l(mutex); + if (state == ThreadCreated) { + state = ThreadStarted; + sig->signal(); + ResumeThread(thread); + } +} + +Thread* +Thread::join() { + if (deleteAfterRun) + throw rdr::Exception("attempt to join() with deleteAfterRun thread"); + Lock l(mutex); + if (state == ThreadJoined) { + logAction(this, "already joined"); + } else { + logAction(this, "joining"); + while (state == ThreadStarted) { + sig->wait(); + logAction(this, "checking"); + } + state = ThreadJoined; + logAction(this, "joined"); + } + return this; +} + +const char* +Thread::getName() const { + return name.buf; +} + +ThreadState +Thread::getState() const { + return state; +} + +unsigned long +Thread::getThreadId() const { + return thread_id; +} + + +Thread* +Thread::self() { + Thread* thread = (Thread*) TlsGetValue(threadStorage); + if (!thread) { + // *** memory leak - could use GetExitCodeThread to lazily detect when + // to clean up native thread objects + thread = new Thread(GetCurrentThread(), GetCurrentThreadId()); + TlsSetValue(threadStorage, thread); + } + return thread; +} diff --git a/win/rfb_win32/Threading.h b/win/rfb_win32/Threading.h new file mode 100644 index 00000000..850f04dd --- /dev/null +++ b/win/rfb_win32/Threading.h @@ -0,0 +1,154 @@ +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * + * This 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 software 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 software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +// -=- Threading_win32.h +// Win32 Threading interface implementation + +#ifndef __RFB_THREADING_IMPL_WIN32 +#define __RFB_THREADING_IMPL_WIN32 + +#define __RFB_THREADING_IMPL WIN32 + +#include +#include +#include +//#include + + +namespace rfb { + + class Mutex { + public: + Mutex() { + InitializeCriticalSection(&crit); + } + ~Mutex() { + DeleteCriticalSection(&crit); + } + friend class Lock; + friend class Condition; + protected: + void enter() {EnterCriticalSection(&crit);} + void exit() {LeaveCriticalSection(&crit);} + CRITICAL_SECTION crit; + }; + + class Lock { + public: + Lock(Mutex& m) : mutex(m) {m.enter();} + ~Lock() {mutex.exit();} + protected: + Mutex& mutex; + }; + + enum ThreadState {ThreadCreated, ThreadStarted, ThreadStopped, ThreadJoined, ThreadNative}; + + class Thread { + public: + Thread(const char* name_=0); + virtual ~Thread(); + + virtual void run(); + + virtual void start(); + virtual Thread* join(); + + const char* getName() const; + ThreadState getState() const; + + // Determines whether the thread should delete itself when run() returns + // If you set this, you must NEVER call join()! + void setDeleteAfterRun() {deleteAfterRun = true;}; + + unsigned long getThreadId() const; + + static Thread* self(); + + friend class Condition; + + protected: + Thread(HANDLE thread_, DWORD thread_id_); + static DWORD WINAPI threadProc(LPVOID lpParameter); + + win32::Handle thread; + DWORD thread_id; + CharArray name; + ThreadState state; + Condition* sig; + Mutex mutex; + + win32::Handle cond_event; + Thread* cond_next; + + bool deleteAfterRun; + }; + + class Condition { + public: + Condition(Mutex& m) : mutex(m), waiting(0) { + } + ~Condition() { + } + + // Wake up the specified number of threads that are waiting + // on this Condition, or all of them if -1 is specified. + void signal(int howMany=1) { + Lock l(cond_lock); + while (waiting && howMany!=0) { + SetEvent(waiting->cond_event); + waiting = waiting->cond_next; + if (howMany>0) --howMany; + } + } + + // NB: Must hold "mutex" to call wait() + // Wait until either the Condition is signalled or the timeout + // expires. + void wait(DWORD timeout=INFINITE) { + Thread* self = Thread::self(); + ResetEvent(self->cond_event); + { Lock l(cond_lock); + self->cond_next = waiting; + waiting = self; + } + mutex.exit(); + DWORD result = WaitForSingleObject(self->cond_event, timeout); + mutex.enter(); + if (result == WAIT_TIMEOUT) { + Lock l(cond_lock); + // Remove this thread from the Condition + for (Thread** removeFrom = &waiting; *removeFrom; removeFrom = &(*removeFrom)->cond_next) { + if (*removeFrom == self) { + *removeFrom = self->cond_next; + break; + } + } + } else if (result == WAIT_FAILED) { + throw rdr::SystemException("failed to wait on Condition", GetLastError()); + } + } + + protected: + Mutex& mutex; + Mutex cond_lock; + Thread* waiting; + }; + +}; + +#endif // __RFB_THREADING_IMPL diff --git a/win/rfb_win32/ToolBar.cxx b/win/rfb_win32/ToolBar.cxx new file mode 100644 index 00000000..6392ebdc --- /dev/null +++ b/win/rfb_win32/ToolBar.cxx @@ -0,0 +1,216 @@ +/* Copyright (C) 2004 TightVNC Team. All Rights Reserved. + * + * This 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 software 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 software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +// -=- ToolBar control class. + +#include "ToolBar.h" + +using namespace rfb::win32; + +ToolBar::ToolBar() : hwndToolBar(0), tbID(-1) { + INITCOMMONCONTROLSEX icex; + + // Ensure that the common control DLL is loaded + icex.dwSize = sizeof(INITCOMMONCONTROLSEX); + icex.dwICC = ICC_BAR_CLASSES; + InitCommonControlsEx(&icex); +} + +ToolBar::~ToolBar() { + DestroyWindow(getHandle()); +} + +bool ToolBar::create(int _tbID, HWND _parentHwnd, DWORD dwStyle) { + parentHwnd = _parentHwnd; + dwStyle |= WS_CHILD; + + // Create the ToolBar window + hwndToolBar = CreateWindowEx(0, TOOLBARCLASSNAME, 0, dwStyle, + 0, 0, 25, 25, parentHwnd, (HMENU)_tbID, GetModuleHandle(0), 0); + + if (hwndToolBar) { + tbID = _tbID; + + // It's required for backward compatibility + SendMessage(hwndToolBar, TB_BUTTONSTRUCTSIZE, (WPARAM)sizeof(TBBUTTON), 0); + } + return (hwndToolBar ? true : false); +}; + +int ToolBar::addBitmap(int nButtons, UINT bitmapID) { + assert(nButtons > 0); + TBADDBITMAP resBitmap; + resBitmap.hInst = GetModuleHandle(0); + resBitmap.nID = bitmapID; + return SendMessage(getHandle(), TB_ADDBITMAP, nButtons, (LPARAM)&resBitmap); +} + +int ToolBar::addSystemBitmap(UINT stdBitmapID) { + TBADDBITMAP resBitmap; + resBitmap.hInst = HINST_COMMCTRL; + resBitmap.nID = stdBitmapID; + return SendMessage(getHandle(), TB_ADDBITMAP, 0, (LPARAM)&resBitmap); +} + +bool ToolBar::setBitmapSize(int width, int height) { + int result = SendMessage(getHandle(), TB_SETBITMAPSIZE, + 0, MAKELONG(width, height)); + return (result ? true : false); +} + +bool ToolBar::addButton(int iBitmap, int idCommand, BYTE state, BYTE style, UINT dwData, int iString) { + TBBUTTON tbb; + tbb.iBitmap = iBitmap; + tbb.idCommand = idCommand; + tbb.fsState = state; + tbb.fsStyle = style; + tbb.dwData = dwData; + tbb.iString = iString; + + int result = SendMessage(getHandle(), TB_ADDBUTTONS, 1, (LPARAM)&tbb); + if (result) { + SendMessage(getHandle(), TB_AUTOSIZE, 0, 0); + } + return (result ? true : false); +} + +bool ToolBar::addNButton(int nButtons, LPTBBUTTON tbb) { + assert(nButtons > 0); + assert(tbb > 0); + int result = SendMessage(getHandle(), TB_ADDBUTTONS, nButtons, (LPARAM)tbb); + if (result) { + SendMessage(getHandle(), TB_AUTOSIZE, 0, 0); + } + return (result ? true : false); +} + +bool ToolBar::deleteButton(int indexButton) { + assert(indexButton >= 0); + int result = SendMessage(getHandle(), TB_DELETEBUTTON, indexButton, 0); + + if (result) { + SendMessage(getHandle(), TB_AUTOSIZE, 0, 0); + } + return (result ? true : false); +} + +bool ToolBar::insertButton(int indexButton, LPTBBUTTON tbb) { + assert(indexButton >= 0); + assert(tbb > 0); + int result = SendMessage(getHandle(), TB_INSERTBUTTON, + indexButton, (LPARAM)tbb); + + if (result) { + SendMessage(getHandle(), TB_AUTOSIZE, 0, 0); + } + return (result ? true : false); +} + +int ToolBar::getButtonInfo(int idButton, TBBUTTONINFO *btnInfo) { + assert(idButton >= 0); + assert(btnInfo > 0); + return SendMessage(getHandle(), TB_GETBUTTONINFO, idButton, (LPARAM)btnInfo); +} + +int ToolBar::getButtonsHeight() { + return HIWORD(SendMessage(getHandle(), TB_GETBUTTONSIZE, 0, 0)); +} + +int ToolBar::getButtonsWidth() { + return LOWORD(SendMessage(getHandle(), TB_GETBUTTONSIZE, 0, 0)); +} + +bool ToolBar::setButtonInfo(int idButton, TBBUTTONINFO* btnInfo) { + assert(idButton >= 0); + assert(btnInfo > 0); + int result = SendMessage(getHandle(), TB_SETBUTTONINFO, + idButton, (LPARAM)(LPTBBUTTONINFO)btnInfo); + return (result ? true : false); +} + +bool ToolBar::checkButton(int idButton, bool check) { + assert(idButton >= 0); + int result = SendMessage(getHandle(), TB_CHECKBUTTON, + idButton, MAKELONG(check, 0)); + return (result ? true : false); +} + +bool ToolBar::enableButton(int idButton, bool enable) { + assert(idButton >= 0); + int result = SendMessage(getHandle(), TB_ENABLEBUTTON, + idButton, MAKELONG(enable, 0)); + return (result ? true : false); +} + +bool ToolBar::pressButton(int idButton, bool press) { + assert(idButton >= 0); + int result = SendMessage(getHandle(), TB_PRESSBUTTON, + idButton, MAKELONG(press, 0)); + return (result ? true : false); +} + +bool ToolBar::getButtonRect(int nIndex, LPRECT buttonRect) { + int result = SendMessage(getHandle(), TB_GETITEMRECT, + nIndex, (LPARAM)buttonRect); + return (result ? true : false); +} + +bool ToolBar::setButtonSize(int width, int height) { + assert(width > 0); + assert(height > 0); + int result = SendMessage(getHandle(), TB_SETBUTTONSIZE, + 0, MAKELONG(width, height)); + if (result) { + SendMessage(getHandle(), TB_AUTOSIZE, 0, 0); + return true; + } + return false; +} + +void ToolBar::autoSize() { + DWORD style = SendMessage(getHandle(), TB_GETSTYLE, 0, 0); + if (style & CCS_NORESIZE) { + RECT r, btnRect; + GetClientRect(parentHwnd, &r); + getButtonRect(0, &btnRect); + int height = getButtonsHeight() + btnRect.top * 2 + 2; + SetWindowPos(getHandle(), HWND_TOP, 0, 0, r.right - r.left, height, + SWP_NOMOVE); + } else { + SendMessage(getHandle(), TB_AUTOSIZE, 0, 0); + } +} + +int ToolBar::getHeight() { + RECT r; + GetWindowRect(getHandle(), &r); + return r.bottom - r.top; +} + +void ToolBar::show() { + ShowWindow(getHandle(), SW_SHOW); +} + +void ToolBar::hide() { + ShowWindow(getHandle(), SW_HIDE); +} + +bool ToolBar::isVisible() { + DWORD style = GetWindowLong(getHandle(), GWL_STYLE); + return (bool)(style & WS_VISIBLE); +} diff --git a/win/rfb_win32/ToolBar.h b/win/rfb_win32/ToolBar.h new file mode 100644 index 00000000..2242c2a4 --- /dev/null +++ b/win/rfb_win32/ToolBar.h @@ -0,0 +1,132 @@ +/* Copyright (C) 2004 TightVNC Team. All Rights Reserved. + * + * This 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 software 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 software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +// -=- ToolBar control class. + +#include +#include +#include + +namespace rfb { + + namespace win32 { + + class ToolBar { + public: + ToolBar(); + virtual ~ToolBar(); + + // create() creates a windows toolbar. dwStyle is a combination of + // the toolbar control and button styles. It returns TRUE if successful, + // or FALSE otherwise. + bool create(int tbID, HWND parentHwnd, + DWORD dwStyle = WS_CHILD | WS_VISIBLE | TBSTYLE_FLAT); + + // -=- Button images operations + + // addBitmap() adds one or more images from resources to + // the list of button images available for a toolbar. + // Returns the index of the first new image if successful, + // or -1 otherwise. + int addBitmap(int nButtons, UINT bitmapID); + + // addSystemBitmap() adds the system-defined button bitmaps to the list + // of the toolbar button specifying by stdBitmapID. Returns the index of + // the first new image if successful, or -1 otherwise. + int addSystemBitmap(UINT stdBitmapID); + + // setBitmapSize() sets the size of the bitmapped images to be added + // to a toolbar. It returns TRUE if successful, or FALSE otherwise. + // You must call it before addBitmap(). + bool setBitmapSize(int width, int height); + + // -=- Button operations + + // addButton() adds one button. + bool addButton(int iBitmap, int idCommand, BYTE state=TBSTATE_ENABLED, + BYTE style=TBSTYLE_BUTTON, UINT dwData=0, int iString=0); + + // addNButton() adds nButtons buttons to a toolbar. + bool addNButton(int nButtons, LPTBBUTTON tbb); + + // deleteButton() removes a button from the toolbar. + bool deleteButton(int nIndex); + + // insertButton() inserts a button in a toolbar control by index. + bool insertButton(int nIndex, LPTBBUTTON tbb); + + // getButtonInfo() retrieves extended information about a toolbar's + // button. It returns index of the button if successful, or -1 otherwise. + int getButtonInfo(int idButton, TBBUTTONINFO *btnInfo); + + // getButtonsHeight() retrieves the height of the toolbar buttons. + int getButtonsHeight(); + + // getButtonsWidth() retrieves the width of the toolbar buttons. + int getButtonsWidth(); + + // setButtonInfo() sets the information for an existing button + // in a toolbar. + bool setButtonInfo(int idButton, TBBUTTONINFO* ptbbi); + + // checkButton() checks or unchecks a given button in a toolbar control. + bool checkButton(int idButton, bool check); + + // enableButton() enables or disables the specified button + // in the toolbar. + bool enableButton(int idButton, bool enable); + + // pressButton() presses or releases the specified button in the toolbar. + bool pressButton(int idButton, bool press); + + // getButtonRect() gets the bounding rectangle of a button in a toolbar. + bool getButtonRect(int nIndex, LPRECT buttonRect); + + // setButtonSize() sets the size of the buttons to be added to a toolbar. + // Button size must be largen the button bitmap. + bool setButtonSize(int width, int height); + + // -=- ToolBar operations + + // autoSize() resizes the toolbar window. + void autoSize(); + + // getHandle() returns handle to a toolbar window. + HWND getHandle() { return hwndToolBar; } + + // getHeight() returns the toolbar window height. + int getHeight(); + + // show() displays the toolbar window. + void show(); + + // hide() hides the toolbar window. + void hide(); + + // isVisible() check the toolbar window on visible. + bool isVisible(); + + protected: + HWND hwndToolBar; + HWND parentHwnd; + int tbID; + }; + + }; // win32 + +}; // rfb diff --git a/win/rfb_win32/TrayIcon.h b/win/rfb_win32/TrayIcon.h new file mode 100644 index 00000000..dc5102a9 --- /dev/null +++ b/win/rfb_win32/TrayIcon.h @@ -0,0 +1,89 @@ +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * + * This 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 software 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 software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +// -=- CView.h + +// An instance of the CView class is created for each VNC Viewer connection. + +#ifndef __RFB_WIN32_TRAY_ICON_H__ +#define __RFB_WIN32_TRAY_ICON_H__ + +#include +#include +#include +#include + +namespace rfb { + + namespace win32 { + + class TrayIcon : public MsgWindow { + public: + TrayIcon() : MsgWindow(_T("VNCTray")) { +#ifdef NOTIFYICONDATA_V1_SIZE + nid.cbSize = NOTIFYICONDATA_V1_SIZE; +#else + nid.cbSize = sizeof(NOTIFYICONDATA); +#endif + + nid.hWnd = getHandle(); + nid.uID = 0; + nid.hIcon = 0; + nid.uFlags = NIF_ICON | NIF_MESSAGE; + nid.uCallbackMessage = WM_USER; + } + virtual ~TrayIcon() { + remove(); + } + bool setIcon(UINT icon) { + if (icon == 0) { + return remove(); + } else { + nid.hIcon = (HICON)LoadImage(GetModuleHandle(0), MAKEINTRESOURCE(icon), + IMAGE_ICON, 0, 0, LR_DEFAULTSIZE | LR_SHARED); + return refresh(); + } + } + bool setToolTip(const TCHAR* text) { + if (text == 0) { + nid.uFlags &= ~NIF_TIP; + } else { + const int tipLen = sizeof(nid.szTip)/sizeof(TCHAR); + _tcsncpy(nid.szTip, text, tipLen); + nid.szTip[tipLen-1] = 0; + nid.uFlags |= NIF_TIP; + } + return refresh(); + } + bool remove() { + return Shell_NotifyIcon(NIM_DELETE, &nid) != 0; + } + bool refresh() { + return Shell_NotifyIcon(NIM_MODIFY, &nid) || Shell_NotifyIcon(NIM_ADD, &nid); + } + protected: + NOTIFYICONDATA nid; + }; + + }; + +}; + +#endif + + diff --git a/win/rfb_win32/TsSessions.cxx b/win/rfb_win32/TsSessions.cxx new file mode 100644 index 00000000..efe75640 --- /dev/null +++ b/win/rfb_win32/TsSessions.cxx @@ -0,0 +1,93 @@ +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * + * This 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 software 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 software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +#include +#include +#include +#include +#include + +#ifdef ERROR_CTX_WINSTATION_BUSY +#define RFB_HAVE_WINSTATION_CONNECT +#else +#pragma message(" NOTE: Not building WinStationConnect support.") +#endif + +static rfb::LogWriter vlog("TsSessions"); + +namespace rfb { +namespace win32 { + + // Windows XP (and later) functions used to handle session Ids + typedef BOOLEAN (WINAPI *_WinStationConnect_proto) (HANDLE,ULONG,ULONG,PCWSTR,ULONG); + DynamicFn<_WinStationConnect_proto> _WinStationConnect(_T("winsta.dll"), "WinStationConnectW"); + typedef DWORD (WINAPI *_WTSGetActiveConsoleSessionId_proto) (); + DynamicFn<_WTSGetActiveConsoleSessionId_proto> _WTSGetActiveConsoleSessionId(_T("kernel32.dll"), "WTSGetActiveConsoleSessionId"); + typedef BOOL (WINAPI *_ProcessIdToSessionId_proto) (DWORD, DWORD*); + DynamicFn<_ProcessIdToSessionId_proto> _ProcessIdToSessionId(_T("kernel32.dll"), "ProcessIdToSessionId"); + typedef BOOL (WINAPI *_LockWorkStation_proto)(); + DynamicFn<_LockWorkStation_proto> _LockWorkStation(_T("user32.dll"), "LockWorkStation"); + + + ProcessSessionId::ProcessSessionId(DWORD processId) { + id = 0; + if (!_ProcessIdToSessionId.isValid()) + return; + if (processId == -1) + processId = GetCurrentProcessId(); + if (!(*_ProcessIdToSessionId)(GetCurrentProcessId(), &id)) + throw rdr::SystemException("ProcessIdToSessionId", GetLastError()); + } + + ProcessSessionId mySessionId; + + ConsoleSessionId::ConsoleSessionId() { + if (_WTSGetActiveConsoleSessionId.isValid()) + id = (*_WTSGetActiveConsoleSessionId)(); + else + id = 0; + } + + bool inConsoleSession() { + ConsoleSessionId console; + return console.id == mySessionId.id; + } + + void setConsoleSession(DWORD sessionId) { +#ifdef RFB_HAVE_WINSTATION_CONNECT + if (!_WinStationConnect.isValid()) + throw rdr::Exception("WinSta APIs missing"); + if (sessionId == -1) + sessionId = mySessionId.id; + + // Try to reconnect our session to the console + ConsoleSessionId console; + vlog.info("Console session is %d", console.id); + if (!(*_WinStationConnect)(0, sessionId, console.id, L"", 0)) + throw rdr::SystemException("Unable to connect session to Console", GetLastError()); + + // Lock the newly connected session, for security + if (_LockWorkStation.isValid()) + (*_LockWorkStation)(); +#else + throw rdr::Exception("setConsoleSession not implemented"); +#endif + } + +}; +}; diff --git a/win/rfb_win32/TsSessions.h b/win/rfb_win32/TsSessions.h new file mode 100644 index 00000000..b15ada71 --- /dev/null +++ b/win/rfb_win32/TsSessions.h @@ -0,0 +1,61 @@ +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * + * This 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 software 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 software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +// Windows version-independent Terminal Services Session discovery +// and manipulation API. This code will eventually be replaced +// by the full TS-compatibility scheme. + +#ifndef __RFB_WIN32_TSSESSIONS_H__ +#define __RFB_WIN32_TSSESSIONS_H__ + +#include + +namespace rfb { + + namespace win32 { + + struct SessionId { + DWORD id; + }; + + // Session Id for a given process + struct ProcessSessionId : SessionId { + ProcessSessionId(DWORD processId = -1); + }; + + // Session Id for current process + extern ProcessSessionId mySessionId; + + // Current console Session Id + struct ConsoleSessionId : SessionId { + ConsoleSessionId(); + }; + + // Check whether the process is in the Console session at present + bool inConsoleSession(); + + // Make the specified session the Console session. + // If sessionId is -1 then the process' session is + // made the Console session. + void setConsoleSession(DWORD sessionId = -1); + + }; + +}; + +#endif diff --git a/win/rfb_win32/WMCursor.cxx b/win/rfb_win32/WMCursor.cxx new file mode 100644 index 00000000..4d696cbc --- /dev/null +++ b/win/rfb_win32/WMCursor.cxx @@ -0,0 +1,104 @@ +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * + * This 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 software 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 software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +// -=- WMCursor.cxx + +// *** DOESN'T SEEM TO WORK WITH GetCursorInfo POS CODE BUILT-IN UNDER NT4SP6 +// *** INSTEAD, WE LOOK FOR Win2000/Win98 OR ABOVE + +#include +#include +#include +#include +#include + +using namespace rdr; +using namespace rfb; +using namespace rfb::win32; + +static LogWriter vlog("WMCursor"); + + +#ifdef CURSOR_SHOWING +#define RFB_HAVE_GETCURSORINFO +#else +#pragma message(" NOTE: Not building GetCursorInfo support.") +#endif + +#ifdef RFB_HAVE_GETCURSORINFO +typedef BOOL (WINAPI *_GetCursorInfo_proto)(PCURSORINFO pci); +DynamicFn<_GetCursorInfo_proto> _GetCursorInfo(_T("user32.dll"), "GetCursorInfo"); +#endif + +WMCursor::WMCursor() : hooks(0), use_getCursorInfo(false), cursor(0) { +#ifdef RFB_HAVE_GETCURSORINFO + // Check the OS version + bool is_win98 = (osVersion.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS) && + (osVersion.dwMajorVersion > 4) || ((osVersion.dwMajorVersion == 4) && (osVersion.dwMinorVersion > 0)); + bool is_win2K = (osVersion.dwPlatformId == VER_PLATFORM_WIN32_NT) && (osVersion.dwMajorVersion >= 5); + + // Use GetCursorInfo if OS version is sufficient + use_getCursorInfo = (is_win98 || is_win2K) && _GetCursorInfo.isValid(); +#endif + cursor = (HCURSOR)LoadImage(0, IDC_ARROW, IMAGE_CURSOR, 0, 0, LR_DEFAULTSIZE | LR_SHARED); + if (!use_getCursorInfo) { + hooks = new WMCursorHooks(); + if (hooks && hooks->start()) { + vlog.info("falling back to cursor hooking: %p", hooks); + } else { + delete hooks; + hooks = 0; + vlog.error("unable to monitor cursor shape"); + } + } else { + vlog.info("using GetCursorInfo"); + } +} + +WMCursor::~WMCursor() { + vlog.debug("deleting WMCursorHooks (%p)", hooks); + if (hooks) + delete hooks; +} + +WMCursor::Info +WMCursor::getCursorInfo() { + Info result; +#ifdef RFB_HAVE_GETCURSORINFO + if (use_getCursorInfo) { + CURSORINFO info; + info.cbSize = sizeof(CURSORINFO); + if ((*_GetCursorInfo)(&info)) { + result.cursor = info.hCursor; + result.position = Point(info.ptScreenPos.x, info.ptScreenPos.y); + result.visible = info.flags & CURSOR_SHOWING; + return result; + } + } +#endif + // Fall back to the old way of doing things + POINT pos; + if (hooks) + cursor = hooks->getCursor(); + result.cursor = cursor; + result.visible = cursor != 0; + GetCursorPos(&pos); + result.position.x = pos.x; + result.position.y = pos.y; + return result; +} diff --git a/win/rfb_win32/WMCursor.h b/win/rfb_win32/WMCursor.h new file mode 100644 index 00000000..41f9ee84 --- /dev/null +++ b/win/rfb_win32/WMCursor.h @@ -0,0 +1,61 @@ +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * + * This 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 software 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 software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +// -=- WMCursor.h + +// WMCursor provides a single API through which the cursor state can be obtained +// The underlying implementation will use either GetCursorInfo, or use the +// wm_hooks library if GetCursorInfo is not available. + +#ifndef __RFB_WIN32_WM_CURSOR_H__ +#define __RFB_WIN32_WM_CURSOR_H__ + +#include +#include + +namespace rfb { + namespace win32 { + + class WMCursor { + public: + WMCursor(); + ~WMCursor(); + + struct Info { + HCURSOR cursor; + Point position; + bool visible; + Info() : cursor(0), visible(false) {} + bool operator!=(const Info& info) { + return ((cursor != info.cursor) || + (!position.equals(info.position)) || + (visible != info.visible)); + } + }; + + Info getCursorInfo(); + protected: + WMCursorHooks* hooks; + bool use_getCursorInfo; + HCURSOR cursor; + }; + + }; +}; + +#endif // __RFB_WIN32_WM_CURSOR_H__ diff --git a/win/rfb_win32/WMHooks.cxx b/win/rfb_win32/WMHooks.cxx new file mode 100644 index 00000000..2d690538 --- /dev/null +++ b/win/rfb_win32/WMHooks.cxx @@ -0,0 +1,394 @@ +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * + * This 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 software 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 software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +// -=- WMHooks.cxx + +#include +#include +#include +#include +#include +#include +#include + +#include + +using namespace rfb; +using namespace rfb::win32; + +static LogWriter vlog("WMHooks"); + + +typedef UINT (*WM_Hooks_WMVAL_proto)(); +typedef BOOL (*WM_Hooks_Install_proto)(DWORD owner, DWORD thread); +typedef BOOL (*WM_Hooks_Remove_proto)(DWORD owner); +typedef BOOL (*WM_Hooks_EnableCursorShape_proto)(BOOL enable); +#ifdef _DEBUG +typedef void (*WM_Hooks_SetDiagnosticRange_proto)(UINT min, UINT max); +DynamicFn WM_Hooks_SetDiagnosticRange(_T("wm_hooks.dll"), "WM_Hooks_SetDiagnosticRange"); +#endif + + +class WMHooksThread : public Thread { +public: + WMHooksThread() : Thread("WMHookThread"), active(true), + WM_Hooks_Install(_T("wm_hooks.dll"), "WM_Hooks_Install"), + WM_Hooks_Remove(_T("wm_hooks.dll"), "WM_Hooks_Remove"), + WM_Hooks_EnableCursorShape(_T("wm_hooks.dll"), "WM_Hooks_EnableCursorShape") { + } + virtual void run(); + virtual Thread* join(); + DynamicFn WM_Hooks_Install;; + DynamicFn WM_Hooks_Remove; + DynamicFn WM_Hooks_EnableCursorShape; +protected: + bool active; +}; + +WMHooksThread* hook_mgr = 0; +std::list hooks; +std::list cursor_hooks; +Mutex hook_mgr_lock; +HCURSOR hook_cursor = (HCURSOR)LoadImage(0, IDC_ARROW, IMAGE_CURSOR, 0, 0, LR_DEFAULTSIZE | LR_SHARED); + + +static bool StartHookThread() { + if (hook_mgr) + return true; + vlog.debug("creating thread"); + hook_mgr = new WMHooksThread(); + if (!hook_mgr->WM_Hooks_Install.isValid() || + !hook_mgr->WM_Hooks_Remove.isValid()) { + vlog.debug("hooks not available"); + return false; + } + vlog.debug("installing hooks"); + if (!(*hook_mgr->WM_Hooks_Install)(hook_mgr->getThreadId(), 0)) { + vlog.error("failed to initialise hooks"); + delete hook_mgr->join(); + hook_mgr = 0; + return false; + } + vlog.debug("starting thread"); + hook_mgr->start(); + return true; +} + +static void StopHookThread() { + if (!hook_mgr) + return; + if (!hooks.empty() || !cursor_hooks.empty()) + return; + vlog.debug("closing thread"); + delete hook_mgr->join(); + hook_mgr = 0; +} + + +static bool AddHook(WMHooks* hook) { + vlog.debug("adding hook"); + Lock l(hook_mgr_lock); + if (!StartHookThread()) + return false; + hooks.push_back(hook); + return true; +} + +static bool AddCursorHook(WMCursorHooks* hook) { + vlog.debug("adding cursor hook"); + Lock l(hook_mgr_lock); + if (!StartHookThread()) + return false; + if (!hook_mgr->WM_Hooks_EnableCursorShape.isValid()) + return false; + if (cursor_hooks.empty() && !(*hook_mgr->WM_Hooks_EnableCursorShape)(TRUE)) + return false; + cursor_hooks.push_back(hook); + return true; +} + +static bool RemHook(WMHooks* hook) { + { + vlog.debug("removing hook"); + Lock l(hook_mgr_lock); + hooks.remove(hook); + } + StopHookThread(); + return true; +} + +static bool RemCursorHook(WMCursorHooks* hook) { + { + vlog.debug("removing cursor hook"); + Lock l(hook_mgr_lock); + cursor_hooks.remove(hook); + if (hook_mgr->WM_Hooks_EnableCursorShape.isValid() && + cursor_hooks.empty()) + (*hook_mgr->WM_Hooks_EnableCursorShape)(FALSE); + } + StopHookThread(); + return true; +} + +static void NotifyHooksRegion(const Region& r) { + Lock l(hook_mgr_lock); + std::list::iterator i; + for (i=hooks.begin(); i!=hooks.end(); i++) + (*i)->NotifyHooksRegion(r); +} + +static void NotifyHooksCursor(HCURSOR c) { + Lock l(hook_mgr_lock); + hook_cursor = c; +} + + +static UINT GetMsgVal(DynamicFn& fn) { + if (fn.isValid()) + return (*fn)(); + return WM_NULL; +} + +void +WMHooksThread::run() { + // Obtain message ids for all supported hook messages + DynamicFn WM_Hooks_WindowChanged(_T("wm_hooks.dll"), "WM_Hooks_WindowChanged"); + DynamicFn WM_Hooks_WindowBorderChanged(_T("wm_hooks.dll"), "WM_Hooks_WindowBorderChanged"); + DynamicFn WM_Hooks_WindowClientAreaChanged(_T("wm_hooks.dll"), "WM_Hooks_WindowClientAreaChanged"); + DynamicFn WM_Hooks_RectangleChanged(_T("wm_hooks.dll"), "WM_Hooks_RectangleChanged"); + DynamicFn WM_Hooks_CursorChanged(_T("wm_hooks.dll"), "WM_Hooks_CursorChanged"); + UINT windowMsg = GetMsgVal(WM_Hooks_WindowChanged); + UINT clientAreaMsg = GetMsgVal(WM_Hooks_WindowClientAreaChanged); + UINT borderMsg = GetMsgVal(WM_Hooks_WindowBorderChanged); + UINT rectangleMsg = GetMsgVal(WM_Hooks_RectangleChanged); + UINT cursorMsg = GetMsgVal(WM_Hooks_CursorChanged); +#ifdef _DEBUG + DynamicFn WM_Hooks_Diagnostic(_T("wm_hooks.dll"), "WM_Hooks_Diagnostic"); + UINT diagnosticMsg = GetMsgVal(WM_Hooks_Diagnostic); +#endif + MSG msg; + RECT wrect; + HWND hwnd; + int count = 0; + + // Update delay handling + // We delay updates by 40-80ms, so that the triggering application has time to + // actually complete them before we notify the hook callbacks & they go off + // capturing screen state. + const int updateDelayMs = 40; + MsgWindow updateDelayWnd(_T("WMHooks::updateDelay")); + IntervalTimer updateDelayTimer(updateDelayWnd.getHandle(), 1); + Region updates[2]; + int activeRgn = 0; + + vlog.debug("starting hook thread"); + + while (active && GetMessage(&msg, NULL, 0, 0)) { + count++; + + if (msg.message == WM_TIMER) { + // Actually notify callbacks of graphical updates + NotifyHooksRegion(updates[1-activeRgn]); + if (updates[activeRgn].is_empty()) + updateDelayTimer.stop(); + activeRgn = 1-activeRgn; + updates[activeRgn].clear(); + + } else if (msg.message == windowMsg) { + // An entire window has (potentially) changed + hwnd = (HWND) msg.lParam; + if (IsWindow(hwnd) && IsWindowVisible(hwnd) && !IsIconic(hwnd) && + GetWindowRect(hwnd, &wrect) && !IsRectEmpty(&wrect)) { + updates[activeRgn].assign_union(Rect(wrect.left, wrect.top, + wrect.right, wrect.bottom)); + updateDelayTimer.start(updateDelayMs); + } + + } else if (msg.message == clientAreaMsg) { + // The client area of a window has (potentially) changed + hwnd = (HWND) msg.lParam; + if (IsWindow(hwnd) && IsWindowVisible(hwnd) && !IsIconic(hwnd) && + GetClientRect(hwnd, &wrect) && !IsRectEmpty(&wrect)) + { + POINT pt = {0,0}; + if (ClientToScreen(hwnd, &pt)) { + updates[activeRgn].assign_union(Rect(wrect.left+pt.x, wrect.top+pt.y, + wrect.right+pt.x, wrect.bottom+pt.y)); + updateDelayTimer.start(updateDelayMs); + } + } + + } else if (msg.message == borderMsg) { + hwnd = (HWND) msg.lParam; + if (IsWindow(hwnd) && IsWindowVisible(hwnd) && !IsIconic(hwnd) && + GetWindowRect(hwnd, &wrect) && !IsRectEmpty(&wrect)) + { + Region changed(Rect(wrect.left, wrect.top, wrect.right, wrect.bottom)); + RECT crect; + POINT pt = {0,0}; + if (GetClientRect(hwnd, &crect) && ClientToScreen(hwnd, &pt) && + !IsRectEmpty(&crect)) + { + changed.assign_subtract(Rect(crect.left+pt.x, crect.top+pt.y, + crect.right+pt.x, crect.bottom+pt.y)); + } + if (!changed.is_empty()) { + updates[activeRgn].assign_union(changed); + updateDelayTimer.start(updateDelayMs); + } + } + } else if (msg.message == rectangleMsg) { + Rect r = Rect(LOWORD(msg.wParam), HIWORD(msg.wParam), + LOWORD(msg.lParam), HIWORD(msg.lParam)); + if (!r.is_empty()) { + updates[activeRgn].assign_union(r); + updateDelayTimer.start(updateDelayMs); + } + + } else if (msg.message == cursorMsg) { + NotifyHooksCursor((HCURSOR)msg.lParam); +#ifdef _DEBUG + } else if (msg.message == diagnosticMsg) { + vlog.info("DIAG msg=%x(%d) wnd=%lx", msg.wParam, msg.wParam, msg.lParam); +#endif + } + } + + vlog.debug("stopping hook thread - processed %d events", count); + (*WM_Hooks_Remove)(getThreadId()); +} + +Thread* +WMHooksThread::join() { + vlog.debug("stopping WMHooks thread"); + active = false; + PostThreadMessage(thread_id, WM_QUIT, 0, 0); + vlog.debug("joining WMHooks thread"); + return Thread::join(); +} + +// -=- WMHooks class + +rfb::win32::WMHooks::WMHooks() : updateEvent(0) { +} + +rfb::win32::WMHooks::~WMHooks() { + RemHook(this); +} + +bool rfb::win32::WMHooks::setEvent(HANDLE ue) { + if (updateEvent) + RemHook(this); + updateEvent = ue; + return AddHook(this); +} + +bool rfb::win32::WMHooks::getUpdates(UpdateTracker* ut) { + if (!updatesReady) return false; + Lock l(hook_mgr_lock); + updates.copyTo(ut); + updates.clear(); + updatesReady = false; + return true; +} + +bool rfb::win32::WMHooks::areAvailable() { + WMHooksThread wmht; + return wmht.WM_Hooks_Install.isValid(); +} + +#ifdef _DEBUG +void +rfb::win32::WMHooks::setDiagnosticRange(UINT min, UINT max) { + if (WM_Hooks_SetDiagnosticRange.isValid()) + (*WM_Hooks_SetDiagnosticRange)(min, max); +} +#endif + +void rfb::win32::WMHooks::NotifyHooksRegion(const Region& r) { + // hook_mgr_lock is already held at this point + updates.add_changed(r); + updatesReady = true; + SetEvent(updateEvent); +} + + +// -=- WMBlockInput class + +rfb::win32::WMBlockInput::WMBlockInput() : active(false) { +} + +rfb::win32::WMBlockInput::~WMBlockInput() { + blockInputs(false); +} + +typedef BOOL (*WM_Hooks_EnableRealInputs_proto)(BOOL pointer, BOOL keyboard); +DynamicFn* WM_Hooks_EnableRealInputs = 0; +static bool blockRealInputs(bool block_) { + // NB: Requires blockMutex to be held! + if (block_) { + if (WM_Hooks_EnableRealInputs) + return true; + // Enable blocking + WM_Hooks_EnableRealInputs = new DynamicFn(_T("wm_hooks.dll"), "WM_Hooks_EnableRealInputs"); + if (WM_Hooks_EnableRealInputs->isValid() && (**WM_Hooks_EnableRealInputs)(false, false)) + return true; + } + if (WM_Hooks_EnableRealInputs) { + // Clean up the DynamicFn, either if init failed, or block_ is false + if (WM_Hooks_EnableRealInputs->isValid()) + (**WM_Hooks_EnableRealInputs)(true, true); + delete WM_Hooks_EnableRealInputs; + WM_Hooks_EnableRealInputs = 0; + } + return block_ == (WM_Hooks_EnableRealInputs != 0); +} + +Mutex blockMutex; +int blockCount = 0; + +bool rfb::win32::WMBlockInput::blockInputs(bool on) { + if (active == on) return true; + Lock l(blockMutex); + int newCount = on ? blockCount+1 : blockCount-1; + if (!blockRealInputs(newCount > 0)) + return false; + blockCount = newCount; + active = on; + return true; +} + + +// -=- WMCursorHooks class + +rfb::win32::WMCursorHooks::WMCursorHooks() { +} + +rfb::win32::WMCursorHooks::~WMCursorHooks() { + RemCursorHook(this); +} + +bool +rfb::win32::WMCursorHooks::start() { + return AddCursorHook(this); +} + +HCURSOR +rfb::win32::WMCursorHooks::getCursor() const { + return hook_cursor; +} diff --git a/win/rfb_win32/WMHooks.h b/win/rfb_win32/WMHooks.h new file mode 100644 index 00000000..4713b416 --- /dev/null +++ b/win/rfb_win32/WMHooks.h @@ -0,0 +1,92 @@ +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * + * This 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 software 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 software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +// -=- WMHooks.h + +#ifndef __RFB_WIN32_WM_HOOKS_H__ +#define __RFB_WIN32_WM_HOOKS_H__ + +#include +#include +#include +#include + +namespace rfb { + + namespace win32 { + + // -=- WMHooks + // Uses the wm_hooks DLL to intercept window messages, to get _hints_ as + // to what may have changed on-screen. Updates are notified via a Win32 + // event, and retrieved using the getUpdates method, which is thread-safe. + class WMHooks { + public: + WMHooks(); + ~WMHooks(); + + // Specify the event object to notify. Starts the hook subsystem if it is + // not already active, and returns false if the hooks fail to start. + bool setEvent(HANDLE updateEvent); + + // Copies any new updates to the UpdateTracker. Returns true if new updates + // were added, false otherwise. + bool getUpdates(UpdateTracker* ut); + + // Determine whether the hooks DLL is installed on the system + static bool areAvailable(); + +#ifdef _DEBUG + // Get notifications of any messages in the given range, to any hooked window + void setDiagnosticRange(UINT min, UINT max); +#endif + + // * INTERNAL NOTIFICATION FUNCTION * + void NotifyHooksRegion(const Region& r); + protected: + HANDLE updateEvent; + bool updatesReady; + SimpleUpdateTracker updates; + }; + + // -=- Support for filtering out local input events while remote connections are + // active. Implemented using SetWindowsHookEx for portability. + class WMBlockInput { + public: + WMBlockInput(); + ~WMBlockInput(); + bool blockInputs(bool block); + protected: + bool active; + }; + + // - Legacy cursor handling support + class WMCursorHooks { + public: + WMCursorHooks(); + ~WMCursorHooks(); + + bool start(); + + HCURSOR getCursor() const; + }; + + }; + +}; + +#endif // __RFB_WIN32_WM_HOOKS_H__ diff --git a/win/rfb_win32/WMNotifier.cxx b/win/rfb_win32/WMNotifier.cxx new file mode 100644 index 00000000..20a5445f --- /dev/null +++ b/win/rfb_win32/WMNotifier.cxx @@ -0,0 +1,57 @@ +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * + * This 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 software 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 software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +// -=- WMNotifier.cxx + +#include +#include +#include + +#include + +using namespace rfb; +using namespace rfb::win32; + +static LogWriter vlog("WMMonitor"); + + +WMMonitor::WMMonitor() : MsgWindow(_T("WMMonitor")), notifier(0) { +} + +WMMonitor::~WMMonitor() { +} + + +LRESULT +WMMonitor::processMessage(UINT msg, WPARAM wParam, LPARAM lParam) { + switch (msg) { + case WM_DISPLAYCHANGE: + if (notifier) { + notifier->notifyDisplayEvent(Notifier::DisplaySizeChanged); + notifier->notifyDisplayEvent(Notifier::DisplayPixelFormatChanged); + } + break; + case WM_SYSCOLORCHANGE: + case WM_PALETTECHANGED: + if (notifier) { + notifier->notifyDisplayEvent(Notifier::DisplayColourMapChanged); + } + break; + }; + return MsgWindow::processMessage(msg, wParam, lParam); +} diff --git a/win/rfb_win32/WMNotifier.h b/win/rfb_win32/WMNotifier.h new file mode 100644 index 00000000..a7609642 --- /dev/null +++ b/win/rfb_win32/WMNotifier.h @@ -0,0 +1,68 @@ +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * + * This 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 software 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 software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +// -=- WMNotifier.h +// +// The WMNotifier is used to get callbacks indicating changes in the state +// of the system, for instance in the size/format/palette of the display. +// The WMNotifier contains a Win32 window, which receives notifications of +// system events and stores them. Whenever processEvent is called, any +// incoming events are processed and the appropriate notifier called. + +#ifndef __RFB_WIN32_NOTIFIER_H__ +#define __RFB_WIN32_NOTIFIER_H__ + +#include +#include +#include +#include +#include + +namespace rfb { + + namespace win32 { + + // -=- Window Message Monitor implementation + + class WMMonitor : MsgWindow { + public: + + class Notifier { + public: + typedef enum {DisplaySizeChanged, DisplayColourMapChanged, + DisplayPixelFormatChanged} DisplayEventType; + virtual void notifyDisplayEvent(DisplayEventType evt) = 0; + }; + + WMMonitor(); + virtual ~WMMonitor(); + + void setNotifier(Notifier* wmn) {notifier=wmn;} + + protected: + // - Internal MsgWindow callback + virtual LRESULT processMessage(UINT msg, WPARAM wParam, LPARAM lParam); + + Notifier* notifier; + }; + + }; + +}; + +#endif // __RFB_WIN32_WMNOTIFIER_H__ diff --git a/win/rfb_win32/WMPoller.cxx b/win/rfb_win32/WMPoller.cxx new file mode 100644 index 00000000..f8505342 --- /dev/null +++ b/win/rfb_win32/WMPoller.cxx @@ -0,0 +1,85 @@ +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * + * This 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 software 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 software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +// -=- WMPoller.cxx + +#include +#include +#include +#include + +#include + +using namespace rfb; +using namespace rfb::win32; + +static LogWriter vlog("WMPoller"); + +BoolParameter rfb::win32::WMPoller::poll_console_windows("PollConsoleWindows", + "Server should poll console windows for updates", true); + +// -=- WMPoller class + +bool +rfb::win32::WMPoller::processEvent() { + PollInfo info; + if (poll_console_windows && ut) { + ::EnumWindows(WMPoller::enumWindowProc, (LPARAM) &info); + ut->add_changed(info.poll_include); + } + return false; +} + +bool +rfb::win32::WMPoller::setUpdateTracker(UpdateTracker* ut_) { + ut = ut_; + return true; +} + +bool +rfb::win32::WMPoller::checkPollWindow(HWND w) { + TCHAR buffer[128]; + if (!GetClassName(w, buffer, 128)) + throw rdr::SystemException("unable to get window class:%u", GetLastError()); + if ((_tcscmp(buffer, _T("tty")) != 0) && + (_tcscmp(buffer, _T("ConsoleWindowClass")) != 0)) { + return false; + } + return true; +} + +void +rfb::win32::WMPoller::pollWindow(HWND w, PollInfo* i) { + RECT r; + if (IsWindowVisible(w) && GetWindowRect(w, &r)) { + if (IsRectEmpty(&r)) return; + Region wrgn(Rect(r.left, r.top, r.right, r.bottom)); + if (checkPollWindow(w)) { + wrgn.assign_subtract(i->poll_exclude); + i->poll_include.assign_union(wrgn); + } else { + i->poll_exclude.assign_union(wrgn); + } + } +} + +BOOL CALLBACK +rfb::win32::WMPoller::enumWindowProc(HWND w, LPARAM lp) { + pollWindow(w, (PollInfo*)lp); + return TRUE; +} diff --git a/win/rfb_win32/WMPoller.h b/win/rfb_win32/WMPoller.h new file mode 100644 index 00000000..851b69f4 --- /dev/null +++ b/win/rfb_win32/WMPoller.h @@ -0,0 +1,62 @@ +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * + * This 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 software 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 software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +// -=- WMPoller.h +// +// Polls the foreground window. If the pollOnlyConsoles flag is set, +// then checks the window class of the foreground window first and +// only polls it if it's a console. +// If the pollAllWindows flag is set then iterates through visible +// windows, and polls the visible bits. If pollOnlyConsoles is also +// set then only visible parts of console windows will be polled. + +#ifndef __RFB_WIN32_WM_POLLER_H__ +#define __RFB_WIN32_WM_POLLER_H__ + +#include +#include +#include + +namespace rfb { + + namespace win32 { + + class WMPoller { + public: + WMPoller() : ut(0) {} + + bool processEvent(); + bool setUpdateTracker(UpdateTracker* ut); + + static BoolParameter poll_console_windows; + protected: + struct PollInfo { + Region poll_include; + Region poll_exclude; + }; + static bool checkPollWindow(HWND w); + static void pollWindow(HWND w, PollInfo* info); + static BOOL CALLBACK enumWindowProc(HWND w, LPARAM lp); + UpdateTracker* ut; + }; + + }; + +}; + +#endif // __RFB_WIN32_WM_POLLER_H__ diff --git a/win/rfb_win32/WMShatter.cxx b/win/rfb_win32/WMShatter.cxx new file mode 100644 index 00000000..e68abfb1 --- /dev/null +++ b/win/rfb_win32/WMShatter.cxx @@ -0,0 +1,57 @@ +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * + * This 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 software 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 software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +// -=- WMShatter.cxx + +#include + +#include + +using namespace rfb; +using namespace rfb::win32; + +static LogWriter vlog("WMShatter"); + +bool +rfb::win32::IsSafeWM(HWND window, UINT msg, WPARAM wParam, LPARAM lParam) { + bool result = true; + switch (msg) { + // - UNSAFE MESSAGES + case WM_TIMER: + result = lParam == 0; + break; + }; + if (!result) { + vlog.info("IsSafeWM: 0x%x received 0x%x(%u, %lu) - not safe", window, msg, wParam, lParam); + } + return result; +} + +LRESULT +rfb::win32::SafeDefWindowProc(HWND window, UINT msg, WPARAM wParam, LPARAM lParam) { + if (IsSafeWM(window, msg, wParam, lParam)) + return DefWindowProc(window, msg, wParam, lParam); + return 0; +} + +LRESULT +rfb::win32::SafeDispatchMessage(const MSG* msg) { + if (IsSafeWM(msg->hwnd, msg->message, msg->wParam, msg->lParam)) + return DispatchMessage(msg); + return 0; +} diff --git a/win/rfb_win32/WMShatter.h b/win/rfb_win32/WMShatter.h new file mode 100644 index 00000000..3ea63b1a --- /dev/null +++ b/win/rfb_win32/WMShatter.h @@ -0,0 +1,50 @@ +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * + * This 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 software 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 software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +// -=- WMShatter.h +// +// WMShatter provides the IsSafeWM routine, which returns true iff the +// supplied window message is safe to pass to DispatchMessage, or to +// process in the window procedure. +// +// This is only required, of course, to avoid so-called "shatter" attacks +// to be made against the VNC server, which take advantage of the noddy +// design of the Win32 window messaging system. +// +// The API here is designed to hopefully be future proof, so that if they +// ever come up with a proper way to determine whether a message is safe +// or not then it can just be reimplemented here... + +#ifndef __RFB_WIN32_SHATTER_H__ +#define __RFB_WIN32_SHATTER_H__ + +#include + +namespace rfb { + namespace win32 { + + bool IsSafeWM(HWND window, UINT msg, WPARAM wParam, LPARAM lParam); + + LRESULT SafeDefWindowProc(HWND wnd, UINT msg, WPARAM wParam, LPARAM lParam); + + LRESULT SafeDispatchMessage(const MSG* msg); + + }; +}; + +#endif // __RFB_WIN32_SHATTER_H__ diff --git a/win/rfb_win32/WMWindowCopyRect.cxx b/win/rfb_win32/WMWindowCopyRect.cxx new file mode 100644 index 00000000..63c1da2e --- /dev/null +++ b/win/rfb_win32/WMWindowCopyRect.cxx @@ -0,0 +1,67 @@ +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * + * This 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 software 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 software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +// -=- WMCopyRect.cxx + +#include +#include +#include + +using namespace rfb; +using namespace rfb::win32; + +static LogWriter vlog("WMCopyRect"); + +// -=- WMHooks class + +rfb::win32::WMCopyRect::WMCopyRect() : ut(0), fg_window(0) { +} + +bool +rfb::win32::WMCopyRect::processEvent() { + // See if the foreground window has moved + HWND window = GetForegroundWindow(); + if (window) { + RECT wrect; + if (IsWindow(window) && IsWindowVisible(window) && GetWindowRect(window, &wrect)) { + Rect winrect(wrect.left, wrect.top, wrect.right, wrect.bottom); + if (fg_window == window) { + if (!fg_window_rect.tl.equals(winrect.tl) && ut) { + // Window has moved - send a copyrect event to the client + Point delta = Point(winrect.tl.x-fg_window_rect.tl.x, winrect.tl.y-fg_window_rect.tl.y); + Region copy_dest = winrect; + ut->add_copied(copy_dest, delta); + ut->add_changed(Region(fg_window_rect).subtract(copy_dest)); + } + } + fg_window = window; + fg_window_rect = winrect; + } else { + fg_window = 0; + } + } else { + fg_window = 0; + } + return false; +} + +bool +rfb::win32::WMCopyRect::setUpdateTracker(UpdateTracker* ut_) { + ut = ut_; + return true; +} diff --git a/win/rfb_win32/WMWindowCopyRect.h b/win/rfb_win32/WMWindowCopyRect.h new file mode 100644 index 00000000..5a0e876d --- /dev/null +++ b/win/rfb_win32/WMWindowCopyRect.h @@ -0,0 +1,53 @@ +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * + * This 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 software 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 software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +// -=- WMWindowCopyRect.h +// +// Helper class which produces copyRect actions by monitoring the location +// of the current foreground window. +// Whenever processEvent is called, the foreground window's position is +// recalculated and a copy event flushed to the supplied UpdateTracker +// if appropriate. + +#ifndef __RFB_WIN32_WM_WINDOW_COPYRECT_H__ +#define __RFB_WIN32_WM_WINDOW_COPYRECT_H__ + +#include + +namespace rfb { + + namespace win32 { + + class WMCopyRect { + public: + WMCopyRect(); + + bool processEvent(); + bool setUpdateTracker(UpdateTracker* ut); + + protected: + UpdateTracker* ut; + void* fg_window; + Rect fg_window_rect; + }; + + }; + +}; + +#endif // __RFB_WIN32_WM_WINDOW_COPYRECT_H__ diff --git a/win/rfb_win32/Win32Util.cxx b/win/rfb_win32/Win32Util.cxx new file mode 100644 index 00000000..ef8039ab --- /dev/null +++ b/win/rfb_win32/Win32Util.cxx @@ -0,0 +1,114 @@ +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * + * This 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 software 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 software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +// Win32Util.cxx + +#include +#include +#include +#include +#include +#include + +namespace rfb { +namespace win32 { + + +FileVersionInfo::FileVersionInfo(const TCHAR* filename) { + // Get executable name + ModuleFileName exeName; + if (!filename) + filename = exeName.buf; + + // Attempt to open the file, to cause Access Denied, etc, errors + // to be correctly reported, since the GetFileVersionInfoXXX calls lie... + { + Handle file(CreateFile(filename, GENERIC_READ, FILE_SHARE_READ, 0, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0)); + if (file.h == INVALID_HANDLE_VALUE) + throw rdr::SystemException("Failed to open file", GetLastError()); + } + + // Get version info size + DWORD handle; + int size = GetFileVersionInfoSize((TCHAR*)filename, &handle); + if (!size) + throw rdr::SystemException("GetVersionInfoSize failed", GetLastError()); + + // Get version info + buf = new TCHAR[size]; + if (!GetFileVersionInfo((TCHAR*)filename, handle, size, buf)) + throw rdr::SystemException("GetVersionInfo failed", GetLastError()); +} + +const TCHAR* FileVersionInfo::getVerString(const TCHAR* name, DWORD langId) { + char langIdBuf[sizeof(langId)]; + for (int i=sizeof(langIdBuf)-1; i>=0; i--) { + langIdBuf[i] = (char) (langId & 0xff); + langId = langId >> 8; + } + + TCharArray langIdStr = rdr::HexOutStream::binToHexStr(langIdBuf, sizeof(langId)); + TCharArray infoName(_tcslen(_T("StringFileInfo")) + 4 + _tcslen(name) + _tcslen(langIdStr.buf)); + _stprintf(infoName.buf, _T("\\StringFileInfo\\%s\\%s"), langIdStr.buf, name); + + // Locate the required version string within the version info + TCHAR* buffer = 0; + UINT length = 0; + if (!VerQueryValue(buf, infoName.buf, (void**)&buffer, &length)) { + printf("unable to find %s version string", CStr(infoName.buf)); + throw rdr::Exception("VerQueryValue failed"); + } + return buffer; +} + + +bool splitPath(const TCHAR* path, TCHAR** dir, TCHAR** file) { + return tstrSplit(path, '\\', dir, file, true); +} + + +void centerWindow(HWND handle, HWND parent) { + RECT r; + MonitorInfo mi(parent ? parent : handle); + if (!parent || !IsWindowVisible(parent) || !GetWindowRect(parent, &r)) + r=mi.rcWork; + centerWindow(handle, r); + mi.clipTo(handle); +} + +void centerWindow(HWND handle, const RECT& r) { + RECT wr; + if (!GetWindowRect(handle, &wr)) return; + int w = wr.right-wr.left; + int h = wr.bottom-wr.top; + int x = (r.left + r.right - w)/2; + int y = (r.top + r.bottom - h)/2; + UINT flags = SWP_NOACTIVATE | SWP_NOOWNERZORDER | SWP_NOZORDER | SWP_NOSIZE; + SetWindowPos(handle, 0, x, y, 0, 0, flags); +} + +void resizeWindow(HWND handle, int width, int height) { + RECT r; + GetWindowRect(handle, &r); + SetWindowPos(handle, 0, 0, 0, width, height, SWP_NOACTIVATE | SWP_NOOWNERZORDER | SWP_NOZORDER | SWP_NOMOVE); + centerWindow(handle, r); +} + + +}; +}; diff --git a/win/rfb_win32/Win32Util.h b/win/rfb_win32/Win32Util.h new file mode 100644 index 00000000..8cc1a2b9 --- /dev/null +++ b/win/rfb_win32/Win32Util.h @@ -0,0 +1,55 @@ +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * + * This 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 software 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 software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +// -=- Win32Util.h + +// Miscellaneous but useful Win32 API utility functions & classes. +// In particular, a set of classes which wrap GDI objects, +// and some to handle palettes. + +#ifndef __RFB_WIN32_GDIUTIL_H__ +#define __RFB_WIN32_GDIUTIL_H__ + +#include + +namespace rfb { + + namespace win32 { + + struct FileVersionInfo : public TCharArray { + FileVersionInfo(const TCHAR* filename=0); + const TCHAR* getVerString(const TCHAR* name, DWORD langId = 0x080904b0); + }; + + bool splitPath(const TCHAR* path, TCHAR** dir, TCHAR** file); + + // Center the window to a rectangle, or to a parent window. + // Optionally, resize the window to lay within the rect or parent window + // If the parent window is NULL then the working area if the window's + // current monitor is used instead. + void centerWindow(HWND handle, const RECT& r); + void centerWindow(HWND handle, HWND parent); + + // resizeWindow resizes a window about its center + void resizeWindow(HWND handle, int width, int height); + + }; + +}; + +#endif diff --git a/win/rfb_win32/keymap.h b/win/rfb_win32/keymap.h new file mode 100644 index 00000000..a340d09d --- /dev/null +++ b/win/rfb_win32/keymap.h @@ -0,0 +1,149 @@ +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * + * This 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 software 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 software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +// keymap.h - this file is shared between SInput.cxx and CKeyboard.cxx +// +// Mapping of X keysyms to and from Windows VK codes. Ordering here must be +// such that when we look up a Windows VK code we get the preferred X keysym. +// Going the other way there is no problem because an X keysym always maps to +// exactly one Windows VK code. This map only contain keys which are not the +// normal keys for printable ASCII characters. For example it does not contain +// VK_SPACE (note that things like VK_ADD are for the plus key on the keypad, +// not on the main keyboard). + +struct keymap_t { + rdr::U32 keysym; + rdr::U8 vk; + bool extended; +}; + +static keymap_t keymap[] = { + + { XK_BackSpace, VK_BACK, 0 }, + { XK_Tab, VK_TAB, 0 }, + { XK_Clear, VK_CLEAR, 0 }, + { XK_Return, VK_RETURN, 0 }, + { XK_Pause, VK_PAUSE, 0 }, + { XK_Escape, VK_ESCAPE, 0 }, + { XK_Delete, VK_DELETE, 1 }, + + // Cursor control & motion + + { XK_Home, VK_HOME, 1 }, + { XK_Left, VK_LEFT, 1 }, + { XK_Up, VK_UP, 1 }, + { XK_Right, VK_RIGHT, 1 }, + { XK_Down, VK_DOWN, 1 }, + { XK_Page_Up, VK_PRIOR, 1 }, + { XK_Page_Down, VK_NEXT, 1 }, + { XK_End, VK_END, 1 }, + + // Misc functions + + { XK_Select, VK_SELECT, 0 }, + { XK_Print, VK_SNAPSHOT, 0 }, + { XK_Execute, VK_EXECUTE, 0 }, + { XK_Insert, VK_INSERT, 1 }, + { XK_Help, VK_HELP, 0 }, + { XK_Break, VK_CANCEL, 1 }, + + // Auxilliary Functions - must come before XK_KP_F1, etc + + { XK_F1, VK_F1, 0 }, + { XK_F2, VK_F2, 0 }, + { XK_F3, VK_F3, 0 }, + { XK_F4, VK_F4, 0 }, + { XK_F5, VK_F5, 0 }, + { XK_F6, VK_F6, 0 }, + { XK_F7, VK_F7, 0 }, + { XK_F8, VK_F8, 0 }, + { XK_F9, VK_F9, 0 }, + { XK_F10, VK_F10, 0 }, + { XK_F11, VK_F11, 0 }, + { XK_F12, VK_F12, 0 }, + { XK_F13, VK_F13, 0 }, + { XK_F14, VK_F14, 0 }, + { XK_F15, VK_F15, 0 }, + { XK_F16, VK_F16, 0 }, + { XK_F17, VK_F17, 0 }, + { XK_F18, VK_F18, 0 }, + { XK_F19, VK_F19, 0 }, + { XK_F20, VK_F20, 0 }, + { XK_F21, VK_F21, 0 }, + { XK_F22, VK_F22, 0 }, + { XK_F23, VK_F23, 0 }, + { XK_F24, VK_F24, 0 }, + + // Keypad Functions, keypad numbers + + { XK_KP_Tab, VK_TAB, 0 }, + { XK_KP_Enter, VK_RETURN, 1 }, + { XK_KP_F1, VK_F1, 0 }, + { XK_KP_F2, VK_F2, 0 }, + { XK_KP_F3, VK_F3, 0 }, + { XK_KP_F4, VK_F4, 0 }, + { XK_KP_Home, VK_HOME, 0 }, + { XK_KP_Left, VK_LEFT, 0 }, + { XK_KP_Up, VK_UP, 0 }, + { XK_KP_Right, VK_RIGHT, 0 }, + { XK_KP_Down, VK_DOWN, 0 }, + { XK_KP_End, VK_END, 0 }, + { XK_KP_Page_Up, VK_PRIOR, 0 }, + { XK_KP_Page_Down, VK_NEXT, 0 }, + { XK_KP_Begin, VK_CLEAR, 0 }, + { XK_KP_Insert, VK_INSERT, 0 }, + { XK_KP_Delete, VK_DELETE, 0 }, + { XK_KP_Multiply, VK_MULTIPLY, 0 }, + { XK_KP_Add, VK_ADD, 0 }, + { XK_KP_Separator, VK_SEPARATOR, 0 }, + { XK_KP_Subtract, VK_SUBTRACT, 0 }, + { XK_KP_Decimal, VK_DECIMAL, 0 }, + { XK_KP_Divide, VK_DIVIDE, 1 }, + + { XK_KP_0, VK_NUMPAD0, 0 }, + { XK_KP_1, VK_NUMPAD1, 0 }, + { XK_KP_2, VK_NUMPAD2, 0 }, + { XK_KP_3, VK_NUMPAD3, 0 }, + { XK_KP_4, VK_NUMPAD4, 0 }, + { XK_KP_5, VK_NUMPAD5, 0 }, + { XK_KP_6, VK_NUMPAD6, 0 }, + { XK_KP_7, VK_NUMPAD7, 0 }, + { XK_KP_8, VK_NUMPAD8, 0 }, + { XK_KP_9, VK_NUMPAD9, 0 }, + + // Modifiers + + { XK_Shift_L, VK_SHIFT, 0 }, + { XK_Shift_R, VK_SHIFT, 0 }, + { XK_Control_L, VK_CONTROL, 0 }, + { XK_Control_R, VK_CONTROL, 1 }, + { XK_Alt_L, VK_MENU, 0 }, + { XK_Alt_R, VK_MENU, 1 }, + + // Left & Right Windows keys & Windows Menu Key + + { XK_Super_L, VK_LWIN, 0 }, + { XK_Super_R, VK_RWIN, 0 }, + { XK_Menu, VK_APPS, 0 }, + + // Japanese stuff - almost certainly wrong... + + { XK_Kanji, VK_KANJI, 0 }, + { XK_Kana_Shift, VK_KANA, 0 }, + +}; diff --git a/win/rfb_win32/rfb_win32.dsp b/win/rfb_win32/rfb_win32.dsp new file mode 100644 index 00000000..acfb5aaf --- /dev/null +++ b/win/rfb_win32/rfb_win32.dsp @@ -0,0 +1,513 @@ +# Microsoft Developer Studio Project File - Name="rfb_win32" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Static Library" 0x0104 + +CFG=rfb_win32 - Win32 Debug Unicode +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "rfb_win32.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "rfb_win32.mak" CFG="rfb_win32 - Win32 Debug Unicode" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "rfb_win32 - Win32 Release" (based on "Win32 (x86) Static Library") +!MESSAGE "rfb_win32 - Win32 Debug" (based on "Win32 (x86) Static Library") +!MESSAGE "rfb_win32 - Win32 Debug Unicode" (based on "Win32 (x86) Static Library") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +CPP=cl.exe +RSC=rc.exe + +!IF "$(CFG)" == "rfb_win32 - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "..\Release" +# PROP Intermediate_Dir "..\Release\rfb_win32" +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /YX /FD /c +# ADD CPP /nologo /MT /W3 /GX /O2 /I ".." /I "../../common" /FI"rdr/msvcwarning.h" /D "NDEBUG" /D "_MBCS" /D "_LIB" /D "WIN32" /YX /FD /c +# ADD BASE RSC /l 0x809 /d "NDEBUG" +# ADD RSC /l 0x809 /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LIB32=link.exe -lib +# ADD BASE LIB32 /nologo +# ADD LIB32 /nologo + +!ELSEIF "$(CFG)" == "rfb_win32 - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Debug" +# PROP BASE Intermediate_Dir "Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "..\Debug" +# PROP Intermediate_Dir "..\Debug\rfb_win32" +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /YX /FD /GZ /c +# ADD CPP /nologo /MTd /W3 /Gm /GX /Zi /Od /I ".." /I "../../common" /FI"rdr/msvcwarning.h" /D "_DEBUG" /D "_MBCS" /D "_LIB" /D "WIN32" /YX /FD /GZ /c +# ADD BASE RSC /l 0x809 /d "_DEBUG" +# ADD RSC /l 0x809 /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LIB32=link.exe -lib +# ADD BASE LIB32 /nologo +# ADD LIB32 /nologo + +!ELSEIF "$(CFG)" == "rfb_win32 - Win32 Debug Unicode" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "rfb_win32___Win32_Debug_Unicode" +# PROP BASE Intermediate_Dir "rfb_win32___Win32_Debug_Unicode" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "..\Debug_Unicode" +# PROP Intermediate_Dir "..\Debug_Unicode\rfb_win32" +# PROP Target_Dir "" +# ADD BASE CPP /nologo /MTd /W3 /Gm /GX /Zi /Od /I ".." /FI"msvcwarning.h" /D "_DEBUG" /D "_LIB" /D "WIN32" /D "_MBCS" /YX /FD /GZ /c +# ADD CPP /nologo /MTd /W3 /Gm /GX /Zi /Od /I ".." /I "../../common" /FI"rdr/msvcwarning.h" /D "_DEBUG" /D "_UNICODE" /D "UNICODE" /D "_LIB" /D "WIN32" /YX /FD /GZ /c +# ADD BASE RSC /l 0x809 /d "_DEBUG" +# ADD RSC /l 0x809 /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LIB32=link.exe -lib +# ADD BASE LIB32 /nologo +# ADD LIB32 /nologo + +!ENDIF + +# Begin Target + +# Name "rfb_win32 - Win32 Release" +# Name "rfb_win32 - Win32 Debug" +# Name "rfb_win32 - Win32 Debug Unicode" +# Begin Group "Source Files" + +# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" +# Begin Source File + +SOURCE=.\AboutDialog.cxx +# End Source File +# Begin Source File + +SOURCE=.\CKeyboard.cxx +# End Source File +# Begin Source File + +SOURCE=.\CleanDesktop.cxx +# End Source File +# Begin Source File + +SOURCE=.\Clipboard.cxx +# End Source File +# Begin Source File + +SOURCE=.\CPointer.cxx +# End Source File +# Begin Source File + +SOURCE=.\CurrentUser.cxx +# End Source File +# Begin Source File + +SOURCE=.\DeviceContext.cxx +# End Source File +# Begin Source File + +SOURCE=.\DeviceFrameBuffer.cxx +# End Source File +# Begin Source File + +SOURCE=.\Dialog.cxx +# End Source File +# Begin Source File + +SOURCE=.\DIBSectionBuffer.cxx +# End Source File +# Begin Source File + +SOURCE=.\DynamicFn.cxx +# End Source File +# Begin Source File + +SOURCE=.\EventManager.cxx +# End Source File +# Begin Source File + +SOURCE=.\FolderManager.cxx +# End Source File +# Begin Source File + +SOURCE=.\LaunchProcess.cxx +# End Source File +# Begin Source File + +SOURCE=.\ListViewControl.cxx +# End Source File +# Begin Source File + +SOURCE=.\LowLevelKeyEvents.cxx +# End Source File +# Begin Source File + +SOURCE=.\MonitorInfo.cxx +# End Source File +# Begin Source File + +SOURCE=.\MsgWindow.cxx +# End Source File +# Begin Source File + +SOURCE=.\OSVersion.cxx +# End Source File +# Begin Source File + +SOURCE=.\ProgressControl.cxx +# End Source File +# Begin Source File + +SOURCE=.\RegConfig.cxx +# End Source File +# Begin Source File + +SOURCE=.\Registry.cxx +# End Source File +# Begin Source File + +SOURCE=.\ScaledDIBSectionBuffer.cxx +# End Source File +# Begin Source File + +SOURCE=.\SDisplay.cxx +# End Source File +# Begin Source File + +SOURCE=.\SDisplayCorePolling.cxx +# End Source File +# Begin Source File + +SOURCE=.\SDisplayCoreWMHooks.cxx +# End Source File +# Begin Source File + +SOURCE=.\Security.cxx +# End Source File +# Begin Source File + +SOURCE=.\Service.cxx +# End Source File +# Begin Source File + +SOURCE=.\SFileTransferManagerWin32.cxx +# End Source File +# Begin Source File + +SOURCE=.\SFileTransferWin32.cxx +# End Source File +# Begin Source File + +SOURCE=.\SInput.cxx +# End Source File +# Begin Source File + +SOURCE=.\SocketManager.cxx +# End Source File +# Begin Source File + +SOURCE=.\TCharArray.cxx +# End Source File +# Begin Source File + +SOURCE=.\Threading.cxx +# End Source File +# Begin Source File + +SOURCE=.\ToolBar.cxx +# End Source File +# Begin Source File + +SOURCE=.\TsSessions.cxx +# End Source File +# Begin Source File + +SOURCE=.\Win32Util.cxx +# End Source File +# Begin Source File + +SOURCE=.\WMCursor.cxx +# End Source File +# Begin Source File + +SOURCE=.\WMHooks.cxx +# End Source File +# Begin Source File + +SOURCE=.\WMNotifier.cxx +# End Source File +# Begin Source File + +SOURCE=.\WMPoller.cxx +# End Source File +# Begin Source File + +SOURCE=.\WMShatter.cxx +# End Source File +# Begin Source File + +SOURCE=.\WMWindowCopyRect.cxx +# End Source File +# End Group +# Begin Group "Header Files" + +# PROP Default_Filter "h;hpp;hxx;hm;inl" +# Begin Source File + +SOURCE=.\AboutDialog.h +# End Source File +# Begin Source File + +SOURCE=.\BitmapInfo.h +# End Source File +# Begin Source File + +SOURCE=.\CKeyboard.h +# End Source File +# Begin Source File + +SOURCE=.\CleanDesktop.h +# End Source File +# Begin Source File + +SOURCE=.\Clipboard.h +# End Source File +# Begin Source File + +SOURCE=.\CompatibleBitmap.h +# End Source File +# Begin Source File + +SOURCE=.\ComputerName.h +# End Source File +# Begin Source File + +SOURCE=.\CPointer.h +# End Source File +# Begin Source File + +SOURCE=.\CurrentUser.h +# End Source File +# Begin Source File + +SOURCE=.\DeviceContext.h +# End Source File +# Begin Source File + +SOURCE=.\DeviceFrameBuffer.h +# End Source File +# Begin Source File + +SOURCE=.\Dialog.h +# End Source File +# Begin Source File + +SOURCE=.\DIBSectionBuffer.h +# End Source File +# Begin Source File + +SOURCE=.\DynamicFn.h +# End Source File +# Begin Source File + +SOURCE=.\EventManager.h +# End Source File +# Begin Source File + +SOURCE=.\FolderManager.h +# End Source File +# Begin Source File + +SOURCE=.\Handle.h +# End Source File +# Begin Source File + +SOURCE=.\IconInfo.h +# End Source File +# Begin Source File + +SOURCE=.\IntervalTimer.h +# End Source File +# Begin Source File + +SOURCE=.\keymap.h +# End Source File +# Begin Source File + +SOURCE=.\LaunchProcess.h +# End Source File +# Begin Source File + +SOURCE=.\ListViewControl.h +# End Source File +# Begin Source File + +SOURCE=.\LocalMem.h +# End Source File +# Begin Source File + +SOURCE=.\LogicalPalette.h +# End Source File +# Begin Source File + +SOURCE=.\LowLevelKeyEvents.h +# End Source File +# Begin Source File + +SOURCE=.\ModuleFileName.h +# End Source File +# Begin Source File + +SOURCE=.\MonitorInfo.h +# End Source File +# Begin Source File + +SOURCE=.\MsgBox.h +# End Source File +# Begin Source File + +SOURCE=.\MsgWindow.h +# End Source File +# Begin Source File + +SOURCE=.\OSVersion.h +# End Source File +# Begin Source File + +SOURCE=.\ProgressControl.h +# End Source File +# Begin Source File + +SOURCE=.\RegConfig.h +# End Source File +# Begin Source File + +SOURCE=.\Registry.h +# End Source File +# Begin Source File + +SOURCE=.\ScaledDIBSectionBuffer.h +# End Source File +# Begin Source File + +SOURCE=.\SDisplay.h +# End Source File +# Begin Source File + +SOURCE=.\SDisplayCoreDriver.h +# End Source File +# Begin Source File + +SOURCE=.\SDisplayCorePolling.h +# End Source File +# Begin Source File + +SOURCE=.\SDisplayCoreWMHooks.h +# End Source File +# Begin Source File + +SOURCE=.\Security.h +# End Source File +# Begin Source File + +SOURCE=.\Service.h +# End Source File +# Begin Source File + +SOURCE=.\SFileTransferManagerWin32.h +# End Source File +# Begin Source File + +SOURCE=.\SFileTransferWin32.h +# End Source File +# Begin Source File + +SOURCE=.\SInput.h +# End Source File +# Begin Source File + +SOURCE=.\SocketManager.h +# End Source File +# Begin Source File + +SOURCE=.\TCharArray.h +# End Source File +# Begin Source File + +SOURCE=.\Threading.h +# End Source File +# Begin Source File + +SOURCE=.\ToolBar.h +# End Source File +# Begin Source File + +SOURCE=.\TrayIcon.h +# End Source File +# Begin Source File + +SOURCE=.\TsSessions.h +# End Source File +# Begin Source File + +SOURCE=.\Win32Util.h +# End Source File +# Begin Source File + +SOURCE=.\WMCursor.h +# End Source File +# Begin Source File + +SOURCE=.\WMHooks.h +# End Source File +# Begin Source File + +SOURCE=.\WMNotifier.h +# End Source File +# Begin Source File + +SOURCE=.\WMPoller.h +# End Source File +# Begin Source File + +SOURCE=.\WMShatter.h +# End Source File +# Begin Source File + +SOURCE=.\WMWindowCopyRect.h +# End Source File +# End Group +# End Target +# End Project diff --git a/win/rfbplayer/ChoosePixelFormatDialog.h b/win/rfbplayer/ChoosePixelFormatDialog.h new file mode 100644 index 00000000..ada820b3 --- /dev/null +++ b/win/rfbplayer/ChoosePixelFormatDialog.h @@ -0,0 +1,59 @@ +/* Copyright (C) 2004 TightVNC Team. All Rights Reserved. + * + * This 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 software 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 software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +// -=- ChoosePixelFormatDialog.h + +#include + +class ChoosePixelFormatDialog : public rfb::win32::Dialog { +public: + ChoosePixelFormatDialog(PixelFormatList *_supportedPF) + : Dialog(GetModuleHandle(0)), supportedPF(_supportedPF), combo(0) {} + // - Show the dialog and return true if OK was clicked, + // false in case of error or Cancel + virtual bool showDialog(HWND parent) { + return Dialog::showDialog(MAKEINTRESOURCE(IDD_PIXELFORMAT), parent); + } + const long getPFIndex() const {return pfIndex;} + bool isBigEndian() {return isItemChecked(IDC_BIG_ENDIAN);} +protected: + + // Dialog methods (protected) + virtual void initDialog() { + combo = GetDlgItem(handle, IDC_PIXELFORMAT); + SendMessage(combo, CB_ADDSTRING, 0, (LPARAM)(LPCTSTR)("Auto")); + for (int i = 0; i < supportedPF->count(); i++) { + SendMessage(combo, CB_ADDSTRING, + 0, (LPARAM)(LPCTSTR)(((*supportedPF)[i])->format_name)); + } + SendMessage(combo, CB_SETCURSEL, pfIndex + 1, 0); + setItemChecked(IDC_BIG_ENDIAN, bigEndian); + } + virtual bool onOk() { + pfIndex = SendMessage(combo, CB_GETCURSEL, 0, 0) - 1; + bigEndian = isItemChecked(IDC_BIG_ENDIAN); + return true; + } + virtual bool onCancel() { + return false; + } + static long pfIndex; + static bool bigEndian; + PixelFormatList *supportedPF; + HWND combo; +}; \ No newline at end of file diff --git a/win/rfbplayer/EditPixelFormatDialog.h b/win/rfbplayer/EditPixelFormatDialog.h new file mode 100644 index 00000000..0765285d --- /dev/null +++ b/win/rfbplayer/EditPixelFormatDialog.h @@ -0,0 +1,104 @@ +/* Copyright (C) 2004 TightVNC Team. All Rights Reserved. + * + * This 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 software 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 software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +// -=- EditPixelFormatDialog.h + +#include + +#define MAX_STR_LEN 256 + +class EditPixelFormatDialog : public rfb::win32::Dialog { +public: + EditPixelFormatDialog(PixelFormatList *_supportedPF, char *_format_name, + PixelFormat *_pf) + : Dialog(GetModuleHandle(0)), format_name(_format_name), + supportedPF(_supportedPF), pf(_pf) {} + // - Show the dialog and return true if OK was clicked, + // false in case of error or Cancel + virtual bool showDialog(HWND parent) { + return Dialog::showDialog(MAKEINTRESOURCE(IDD_UPF_EDIT), parent); + } + +protected: + // Dialog methods (protected) + virtual void initDialog() { + HWND bppCombo = GetDlgItem(handle, IDC_BPP_COMBO); + SendMessage(bppCombo, CB_ADDSTRING, 0, (LPARAM)(LPCTSTR)("8")); + SendMessage(bppCombo, CB_ADDSTRING, 0, (LPARAM)(LPCTSTR)("16")); + SendMessage(bppCombo, CB_ADDSTRING, 0, (LPARAM)(LPCTSTR)("32")); + SendMessage(bppCombo, CB_SETCURSEL, min(2, (pf->bpp - 8) / 8), 0); + + HWND bigendianCombo = GetDlgItem(handle, IDC_BIGENDIAN_COMBO); + SendMessage(bigendianCombo, CB_ADDSTRING, 0, (LPARAM)(LPCTSTR)("No")); + SendMessage(bigendianCombo, CB_ADDSTRING, 0, (LPARAM)(LPCTSTR)("Yes")); + SendMessage(bigendianCombo, CB_SETCURSEL, pf->bigEndian, 0); + + setItemString(IDC_NAME_EDIT, format_name); + setItemInt(IDC_DEPTH_EDIT, pf->depth); + setItemInt(IDC_REDMAX_EDIT, pf->redMax); + setItemInt(IDC_GREENMAX_EDIT, pf->greenMax); + setItemInt(IDC_BLUEMAX_EDIT, pf->blueMax); + setItemInt(IDC_REDSHIFT_EDIT, pf->redShift); + setItemInt(IDC_GREENSHIFT_EDIT, pf->greenShift); + setItemInt(IDC_BLUESHIFT_EDIT, pf->blueShift); + } + virtual bool onOk() { + // Check for the errors + char err_msg[256] = "\0"; + if (((getItemString(IDC_NAME_EDIT))[0] == '\0') || + ((getItemString(IDC_DEPTH_EDIT))[0] == '\0') || + ((getItemString(IDC_REDMAX_EDIT))[0] == '\0') || + ((getItemString(IDC_GREENMAX_EDIT))[0] == '\0') || + ((getItemString(IDC_BLUEMAX_EDIT))[0] == '\0') || + ((getItemString(IDC_REDSHIFT_EDIT))[0] == '\0') || + ((getItemString(IDC_GREENSHIFT_EDIT))[0] == '\0') || + ((getItemString(IDC_BLUESHIFT_EDIT))[0] == '\0')) { + strcpy(err_msg, "Please fill the all fields in the dialog."); + } + int newIndex = supportedPF->getIndexByPFName(getItemString(IDC_NAME_EDIT)); + if ((supportedPF->getIndexByPFName(format_name) != newIndex) && + (newIndex != -1)) { + strcpy(err_msg, "The pixel format with that name is already exist."); + } + if (getItemInt(IDC_DEPTH_EDIT) <= 0) { + strcpy(err_msg, "The pixel depth must be larger than 0."); + } + if (err_msg[0] != 0) { + MessageBox(handle, err_msg, "RfbPlayer", MB_OK | MB_ICONWARNING); + return false; + } + // Fill the pixel format structure + strCopy(format_name, getItemString(IDC_NAME_EDIT), MAX_STR_LEN); + pf->bpp = getItemInt(IDC_BPP_COMBO); + pf->depth = getItemInt(IDC_DEPTH_EDIT); + pf->bigEndian = (SendMessage(GetDlgItem(handle, IDC_BIGENDIAN_COMBO), + CB_GETCURSEL, 0, 0)) ? true : false; + pf->trueColour = true; + pf->redMax = getItemInt(IDC_REDMAX_EDIT); + pf->greenMax = getItemInt(IDC_GREENMAX_EDIT); + pf->blueMax = getItemInt(IDC_BLUEMAX_EDIT); + pf->redShift = getItemInt(IDC_REDSHIFT_EDIT); + pf->greenShift = getItemInt(IDC_GREENSHIFT_EDIT); + pf->blueShift = getItemInt(IDC_BLUESHIFT_EDIT); + return true; + } + + char *format_name; + PixelFormatList *supportedPF; + PixelFormat *pf; +}; diff --git a/win/rfbplayer/FbsInputStream.cxx b/win/rfbplayer/FbsInputStream.cxx new file mode 100644 index 00000000..6381a166 --- /dev/null +++ b/win/rfbplayer/FbsInputStream.cxx @@ -0,0 +1,251 @@ +/* Copyright (C) 2004 TightVNC Team. All Rights Reserved. + * + * This 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 software 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 software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +// -=- FbsInputStream class + +#include + +#include + +#include + +FbsInputStream::FbsInputStream(char* FileName) { + bufferSize = 0; + ptr = end = start = NULL; + + timeOffset = 0; + seekOffset = -1; + startTime = GetTickCount(); + + playbackSpeed = 1.0; + seekBackwards = false; + paused = false; + + interruptDelay = false; + + fbsFile = fopen(FileName, "rb"); + if (fbsFile == NULL) { + char *msg = new char[12 + sizeof(FileName)]; + strcpy(msg, "Can't open "); + strcat(msg, FileName); + throw rfb::Exception(msg); + } + + U8 b[12]; + readNByte(b, 12); + + if (b[0] != 'F' || b[1] != 'B' || b[2] != 'S' || b[3] != ' ' || + b[4] != '0' || b[5] != '0' || b[6] != '1' || b[7] != '.' || + b[8] < '0' || b[8] > '9' || b[9] < '0' || b[9] > '9' || + b[10] < '0' || b[10] > '9' || b[11] != '\n') { + throw rfb::Exception("Incorrect protocol version"); + } +} + +FbsInputStream::~FbsInputStream() { + if (start != NULL) + delete [] start; + ptr = end = start = NULL; + fclose(fbsFile); +} + +int FbsInputStream::pos() { + return ptr - start; +} + +// +// Close FbsInputStream and free data buffer +// + +void FbsInputStream::close() { + fclose(fbsFile); + + startTime = -1; + timeOffset = 0; + seekOffset = -1; + seekBackwards = false; + paused = false; + playbackSpeed = 1.0; + + if (start != NULL) + delete [] start; + ptr = end = start = NULL; + bufferSize = 0; +} + +// +// Fill data buffer from the session file (InStream::overrun() override) +// + +int FbsInputStream::overrun(int itemSize, int nItems, bool wait=true) { + // Just wait unless we are performing playback OR seeking. + waitWhilePaused(); + + // Perform backwardSeek (throws the special exception) + if (seekBackwards) { + throw rfb::Exception("[REWIND]"); + } + + // Save a tail of data + U8 *tmp; + int n = end - ptr; + if (n) { + tmp = new U8[n]; + memmove(tmp, ptr, n); + } + + bufferSize = (int)readUnsigned32(); + if (bufferSize >= 0) { + if (start != NULL) + delete [] start; + int realSize = (bufferSize + 3) & 0xFFFFFFFC; // padding to multiple of 32-bits + ptr = start = new U8[realSize + n]; + end = ptr + bufferSize + n; + if (n) { + memmove(start, tmp, n); + delete [] tmp; + } + readNByte(start + n, realSize); + timeOffset = (long)(readUnsigned32() / playbackSpeed); + + if (itemSize * nItems > bufferSize) + nItems = bufferSize / itemSize; + } + + if (bufferSize < 0 || timeOffset < 0) { + if (start != NULL) + delete [] start; + ptr = end = start = NULL; + bufferSize = 0; + return 0; + } + + if (seekOffset >= 0) { + if (timeOffset >= seekOffset) { + startTime = GetTickCount() - seekOffset; + seekOffset = -1; + } else { + return nItems; + } + } + + while (!interruptDelay) { + long timeDiff = startTime + timeOffset - GetTickCount(); + if (timeDiff <= 0) { + break; + } + Sleep(min(20, timeDiff)); + waitWhilePaused(); + } + interruptDelay = false; + + return nItems; +} + +int FbsInputStream::readUnsigned32() { + U8 buf[4]; + if (!readNByte(buf, 4)) + return -1; + + return ((long)(buf[0] & 0xFF) << 24 | + (buf[1] & 0xFF) << 16 | + (buf[2] & 0xFF) << 8 | + (buf[3] & 0xFF)); +} + +// +// Read n-bytes from the session file +// + +bool FbsInputStream::readNByte(U8 b[], int n) { + int off = 0; + + while (off != n) { + int count = fread(b, 1, n - off, fbsFile); + if (count < n) { + if (ferror(fbsFile)) + throw rfb::Exception("Read error from session file"); + if (feof(fbsFile)) + throw rfb::Exception("[End Of File]"); + } + off += count; + } + return true; +} + +void FbsInputStream::waitWhilePaused() { + while (paused && !isSeeking()) { + // A small delay helps to decrease the cpu usage + Sleep(20); + } +} + +void FbsInputStream::interruptFrameDelay() { + interruptDelay = true; +} + +// +// Methods providing additional functionality. +// + +long FbsInputStream::getTimeOffset() { + //long off = max(seekOffset, timeOffset); + return (long)(timeOffset * playbackSpeed); +} + +void FbsInputStream::setTimeOffset(long pos) { + seekOffset = (long)(pos / playbackSpeed); + if (seekOffset < timeOffset) { + seekBackwards = true; + } +} + +void FbsInputStream::setSpeed(double newSpeed) { + long newOffset = (long)(timeOffset * playbackSpeed / newSpeed); + startTime += timeOffset - newOffset; + timeOffset = newOffset; + if (isSeeking()) { + seekOffset = (long)(seekOffset * playbackSpeed / newSpeed); + } + playbackSpeed = newSpeed; +} + +double FbsInputStream::getSpeed() { + return playbackSpeed; +} + +bool FbsInputStream::isSeeking() { + return (seekOffset >= 0); +} + +long FbsInputStream::getSeekOffset() { + return (long)(seekOffset * playbackSpeed); +} + +bool FbsInputStream::isPaused() { + return paused; +} + +void FbsInputStream::pausePlayback() { + paused = true; +} + +void FbsInputStream::resumePlayback() { + paused = false; + startTime = GetTickCount() - timeOffset; +} diff --git a/win/rfbplayer/FbsInputStream.h b/win/rfbplayer/FbsInputStream.h new file mode 100644 index 00000000..96492739 --- /dev/null +++ b/win/rfbplayer/FbsInputStream.h @@ -0,0 +1,68 @@ +/* Copyright (C) 2004 TightVNC Team. All Rights Reserved. + * + * This 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 software 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 software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +// -=- FbsInputStream.h + +#include + +using namespace rdr; + +class FbsInputStream : public InStream { + public: + FbsInputStream(char *FileName); + ~FbsInputStream(); + + // Methods are used to contol the Rfb Stream + long getTimeOffset(); + void setTimeOffset(long pos); + double getSpeed(); + void setSpeed(double newSpeed); + bool isSeeking(); + long getSeekOffset(); + bool isPaused(); + void pausePlayback(); + void resumePlayback(); + void interruptFrameDelay(); + void close(); + int pos(); + + private: + U8 *start; + int bufferSize; + long startTime; + long timeOffset; + long seekOffset; + double playbackSpeed; + bool seekBackwards; + bool paused; + bool interruptDelay; + + FILE *fbsFile; + + // overrun() - overrides InStream::overrun(). + // It is implemented to fill the data buffer from the session file. + // It ensures there are at least itemSize bytes of buffer data. Returns + // the number of items in the buffer (up to a maximum of nItems). itemSize + // is supposed to be "small" (a few bytes). + + int overrun(int itemSize, int nItems, bool wait); + + int readUnsigned32(); + bool readNByte(U8 *b, int n); + void waitWhilePaused(); +}; diff --git a/win/rfbplayer/GotoPosDialog.h b/win/rfbplayer/GotoPosDialog.h new file mode 100644 index 00000000..ddbbe53a --- /dev/null +++ b/win/rfbplayer/GotoPosDialog.h @@ -0,0 +1,44 @@ +/* Copyright (C) 2004 TightVNC Team. All Rights Reserved. + * + * This 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 software 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 software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +// -=- GotoPosDialog.h + +#include + +class GotoPosDialog : public rfb::win32::Dialog { +public: + GotoPosDialog() : Dialog(GetModuleHandle(0)) {} + // - Show the dialog and return true if OK was clicked, + // false in case of error or Cancel + virtual bool showDialog(HWND parent) { + return Dialog::showDialog(MAKEINTRESOURCE(IDD_GOTO), parent); + } + const long getPos() const {return pos;} +protected: + + // Dialog methods (protected) + virtual void initDialog() { + setItemString(IDC_GOTO_EDIT, rfb::TStr("0")); + } + virtual bool onOk() { + pos = atol(rfb::CStr(getItemString(IDC_GOTO_EDIT))); + return true; + } + + long pos; +}; \ No newline at end of file diff --git a/win/rfbplayer/InfoDialog.h b/win/rfbplayer/InfoDialog.h new file mode 100644 index 00000000..bbe9b26f --- /dev/null +++ b/win/rfbplayer/InfoDialog.h @@ -0,0 +1,41 @@ +/* Copyright (C) 2004 TightVNC Team. All Rights Reserved. + * + * This 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 software 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 software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +// -=- InfoDialog.h + +#include + +class InfoDialog : public rfb::win32::Dialog { +public: + InfoDialog(char *_info_message, char *_title = "Information") + : Dialog(GetModuleHandle(0)), info_message(_info_message), title(_title) {} + // - Show the dialog and return true if OK was clicked, + // false in case of error or Cancel + virtual bool showDialog(HWND parent = 0) { + return Dialog::showDialog(MAKEINTRESOURCE(IDD_INFO), parent); + } +protected: + + // Dialog methods (protected) + virtual void initDialog() { + SetWindowText(handle, title); + setItemString(IDC_INFO_EDIT, info_message); + } + char *info_message; + char *title; +}; \ No newline at end of file diff --git a/win/rfbplayer/OptionsDialog.h b/win/rfbplayer/OptionsDialog.h new file mode 100644 index 00000000..8c3a87d7 --- /dev/null +++ b/win/rfbplayer/OptionsDialog.h @@ -0,0 +1,112 @@ +/* Copyright (C) 2004 TightVNC Team. All Rights Reserved. + * + * This 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 software 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 software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +// -=- OptionsDialog.h + +#include +#include + +class OptionsDialog : public rfb::win32::Dialog { +public: + OptionsDialog(PlayerOptions *_options, PixelFormatList *_supportedPF) + : Dialog(GetModuleHandle(0)), options(_options), combo(0), + supportedPF(_supportedPF) {} + // - Show the dialog and return true if OK was clicked, + // false in case of error or Cancel + virtual bool showDialog(HWND parent) { + return Dialog::showDialog(MAKEINTRESOURCE(IDD_OPTIONS), parent); + } +protected: + + // Dialog methods (protected) + virtual void initDialog() { + combo = GetDlgItem(handle, IDC_PIXELFORMAT); + SendMessage(combo, CB_ADDSTRING, 0, (LPARAM)(LPCTSTR)("Auto")); + for (int i = 0; i < supportedPF->count(); i++) { + SendMessage(combo, CB_ADDSTRING, + 0, (LPARAM)(LPCTSTR)(((*supportedPF)[i])->format_name)); + } + SendMessage(combo, CB_SETCURSEL, options->pixelFormatIndex + 1, 0); + setItemChecked(IDC_ACCEPT_BELL, options->acceptBell); + setItemChecked(IDC_ACCEPT_CUT_TEXT, options->acceptCutText); + setItemChecked(IDC_AUTOPLAY, options->autoPlay); + setItemChecked(IDC_BIG_ENDIAN, options->bigEndianFlag); + if (options->askPixelFormat) { + setItemChecked(IDC_ASK_PF, true); + enableItem(IDC_PIXELFORMAT, false); + enableItem(IDC_BIG_ENDIAN, false); + } + } + virtual bool onOk() { + options->askPixelFormat = isItemChecked(IDC_ASK_PF); + options->acceptBell = isItemChecked(IDC_ACCEPT_BELL); + options->acceptCutText = isItemChecked(IDC_ACCEPT_CUT_TEXT); + options->autoPlay = isItemChecked(IDC_AUTOPLAY); + options->bigEndianFlag = isItemChecked(IDC_BIG_ENDIAN); + if (!options->askPixelFormat) { + options->pixelFormatIndex = int(SendMessage(combo, CB_GETCURSEL, 0, 0)) - 1; + if (options->pixelFormatIndex < 0) { + options->autoDetectPF = true; + } else { + options->setPF(&((*supportedPF)[options->pixelFormatIndex])->PF); + options->pixelFormat.bigEndian = options->bigEndianFlag; + options->autoDetectPF = false; + } + } + options->writeToRegistry(); + return true; + } + virtual bool onCommand(int item, int cmd) { + if (item == IDC_ASK_PF) { + enableItem(IDC_PIXELFORMAT, !isItemChecked(IDC_ASK_PF)); + enableItem(IDC_BIG_ENDIAN, !isItemChecked(IDC_ASK_PF)); + } + if (item == IDC_DEFAULT) { + SendMessage(combo, CB_SETCURSEL, DEFAULT_PF_INDEX + 1, 0); + enableItem(IDC_PIXELFORMAT, !DEFAULT_ASK_PF); + enableItem(IDC_BIG_ENDIAN, !DEFAULT_ASK_PF); + setItemChecked(IDC_ASK_PF, DEFAULT_ASK_PF); + setItemChecked(IDC_ACCEPT_BELL, DEFAULT_ACCEPT_BELL); + setItemChecked(IDC_ACCEPT_CUT_TEXT, DEFAULT_ACCEPT_CUT_TEXT); + setItemChecked(IDC_AUTOPLAY, DEFAULT_AUTOPLAY); + setItemChecked(IDC_BIG_ENDIAN, DEFAULT_BIG_ENDIAN); + } + if (item == IDC_EDIT_UPF) { + UserPixelFormatsDialog UpfListDialog(supportedPF); + if (UpfListDialog.showDialog(handle)) { + int index = SendMessage(combo, CB_GETCURSEL, 0, 0); + SendMessage(combo, CB_RESETCONTENT, 0, 0); + SendMessage(combo, CB_ADDSTRING, 0, (LPARAM)(LPCTSTR)("Auto")); + for (int i = 0; i < supportedPF->count(); i++) { + SendMessage(combo, CB_ADDSTRING, + 0, (LPARAM)(LPCTSTR)(((*supportedPF)[i])->format_name)); + } + if ( index > (SendMessage(combo, CB_GETCOUNT, 0, 0) - 1)) { + index = SendMessage(combo, CB_GETCOUNT, 0, 0) - 1; + } + SendMessage(combo, CB_SETCURSEL, index, 0); + options->pixelFormatIndex = index - 1; + } + } + return false; + } + + HWND combo; + PlayerOptions *options; + PixelFormatList *supportedPF; +}; \ No newline at end of file diff --git a/win/rfbplayer/PixelFormatList.cxx b/win/rfbplayer/PixelFormatList.cxx new file mode 100644 index 00000000..25996f65 --- /dev/null +++ b/win/rfbplayer/PixelFormatList.cxx @@ -0,0 +1,159 @@ +/* Copyright (C) 2004 TightVNC Team. All Rights Reserved. + * + * This 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 software 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 software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +// -=- PixelFormatList class + +#include + +using namespace rfb; +using namespace rfb::win32; + +PixelFormatList::PixelFormatList() { + PixelFormatListElement PFElem; + + // -=- Add the default pixel formats to list + // PF_BGR233 + PFElem.setName("8-bits depth (BGR233)"); + PFElem.setPF(&PixelFormat(8,8,0,1,7,7,3,0,3,6)); + PFList.push_back(PFElem); + // PF_RGB555 + PFElem.setName("15-bits depth (RGB555)"); + PFElem.setPF(&PixelFormat(16,15,0,1,31,31,31,10,5,0)); + PFList.push_back(PFElem); + // PF_BGR555 + PFElem.setName("15-bits depth (BGR555)"); + PFElem.setPF(&PixelFormat(16,15,0,1,31,31,31,0,5,10)); + PFList.push_back(PFElem); + // PF_RGB565 + PFElem.setName("16-bits depth (RGB565)"); + PFElem.setPF(&PixelFormat(16,16,0,1,31,63,31,11,5,0)); + PFList.push_back(PFElem); + // PF_BGR565 + PFElem.setName("16-bits depth (BGR565)"); + PFElem.setPF(&PixelFormat(16,16,0,1,31,63,31,0,5,11)); + PFList.push_back(PFElem); + // PF_RGB888 + PFElem.setName("24-bits depth (RGB888)"); + PFElem.setPF(&PixelFormat(32,24,0,1,255,255,255,16,8,0)); + PFList.push_back(PFElem); + // PF_BGR888 + PFElem.setName("24-bits depth (BGR888)"); + PFElem.setPF(&PixelFormat(32,24,0,1,255,255,255,0,8,16)); + PFList.push_back(PFElem); + + PF_DEFAULT_COUNT = PFList.size(); +} + +PixelFormatListElement *PixelFormatList::operator[](int index) { + return &(*getIterator(index)); +} + +void PixelFormatList::add(char *format_name, PixelFormat PF) { + PixelFormatListElement PFElem; + PFElem.setName(format_name); + PFElem.setPF(&PF); + PFList.push_back(PFElem); +} + +void PixelFormatList::insert(int index, char *format_name, PixelFormat PF) { + if (isDefaultPF(index)) + rdr::Exception("PixelFormatList:can't insert to the default pixel format place"); + + PixelFormatListElement PFElem; + PFElem.setName(format_name); + PFElem.setPF(&PF); + PFList.insert(getIterator(index), PFElem); +} + +void PixelFormatList::remove(int index) { + if (isDefaultPF(index)) + rdr::Exception("PixelFormatList:can't remove the default pixel format"); + PFList.erase(getIterator(index)); +} + +list ::iterator PixelFormatList::getIterator(int index) { + if ((index >= PFList.size()) || (index < 0)) + rdr::Exception("PixelFormatList:out of range"); + + int i = 0; + list ::iterator iter; + for (iter = PFList.begin(); iter != PFList.end(); iter++) { + if (i++ == index) break; + } + return iter; +} + +bool PixelFormatList::isDefaultPF(int index) { + if (index < PF_DEFAULT_COUNT) return true; + return false; +} + +void PixelFormatList::readUserDefinedPF(HKEY root, const char *keypath) { + RegKey regKey; + regKey.createKey(root, keypath); + int count = regKey.getInt(_T("PixelFormatCount"), 0); + if (count > 0) { + // Erase all user defined pixel formats + int upf_count = getUserPFCount(); + if (upf_count > 0) { + for(int i = 0; i < upf_count; i++) { + remove(PF_DEFAULT_COUNT); + } + } + // Add the user defined pixel formats from the registry + for(int i = 0; i < count; i++) { + char upf_name[20] = "\0"; + sprintf(upf_name, "%s%i", "Upf", i); + int size = sizeof(PixelFormatListElement); + PixelFormatListElement *pPFElem = 0;// = &PFElem; + regKey.getBinary(upf_name, (void**)&pPFElem, &size); + PFList.push_back(*pPFElem); + if (pPFElem) delete pPFElem; + } + } +} + +void PixelFormatList::writeUserDefinedPF(HKEY root, const char *keypath) { + RegKey regKey; + + // Delete all user defined pixel formats from the regisry + regKey.createKey(root, keypath);//_T("Software\\TightVnc\\RfbPlayer\\UserDefinedPF")); + int count = regKey.getInt(_T("PixelFormatCount"), 0); + for (int i = 0; i < count; i++) { + char upf_name[20] = "\0"; + sprintf(upf_name, "%s%i", "Upf", i); + regKey.deleteValue(upf_name); + } + regKey.setInt(_T("PixelFormatCount"), 0); + + // Write new user defined pixel formats to the registry + regKey.setInt(_T("PixelFormatCount"), getUserPFCount()); + for (i = 0; i < getUserPFCount(); i++) { + char upf_name[20] = "\0"; + sprintf(upf_name, "%s%i", "Upf", i); + regKey.setBinary(upf_name, (void *)operator[](i+getDefaultPFCount()), + sizeof(PixelFormatListElement)); + } +} + +int PixelFormatList::getIndexByPFName(const char *format_name) { + for (int i = 0; i < PixelFormatList::count(); i++) { + if (_stricmp(operator[](i)->format_name, format_name) == 0) return i; + } + return -1; +} \ No newline at end of file diff --git a/win/rfbplayer/PixelFormatList.h b/win/rfbplayer/PixelFormatList.h new file mode 100644 index 00000000..cd7c50aa --- /dev/null +++ b/win/rfbplayer/PixelFormatList.h @@ -0,0 +1,85 @@ +/* Copyright (C) 2004 TightVNC Team. All Rights Reserved. + * + * This 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 software 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 software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +// -=- PixelFormatList.h + +// Definition of the PixelFormatList class, responsible for +// controlling the list of supported pixel formats. + +#include + +#include +#include + +#include + +// Definition indexes of the default pixel formats +#define PF_BGR233 0 +#define PF_RGB555 1 +#define PF_BGR555 2 +#define PF_RGB565 3 +#define PF_BGR565 4 +#define PF_RGB888 5 +#define PF_BGR888 6 + +using namespace rfb; +using namespace std; + +// PixelFormatListElement class, it is +// an item of the PixelFormatList list. + +class PixelFormatListElement { +public: + PixelFormatListElement() { + format_name[0] = 0; + } + char format_name[256]; + PixelFormat PF; + void setName(const char *name) { + format_name[0] = '\0'; + strcpy(format_name, name); + format_name[strlen(name)] = '\0'; + } + void setPF(PixelFormat *_PF) { + memcpy(&PF, _PF, sizeof(PixelFormat)); + } +}; + +class PixelFormatList { +public: + PixelFormatList(); + + PixelFormatListElement* operator[](int index); + void add(char *format_name, PixelFormat PF); + void insert(int index, char *format_name, PixelFormat PF); + void remove(int index); + + void readUserDefinedPF(HKEY root, const char *keypath); + void writeUserDefinedPF(HKEY root, const char *keypath); + + int count() { return PFList.size(); } + int getDefaultPFCount() { return PF_DEFAULT_COUNT; } + int getUserPFCount() { return max(0, count() - PF_DEFAULT_COUNT); } + int getIndexByPFName(const char *format_name); + bool isDefaultPF(int index); + +protected: + list ::iterator getIterator(int index); + list PFList; + int PF_DEFAULT_COUNT; +}; \ No newline at end of file diff --git a/win/rfbplayer/PlayerOptions.cxx b/win/rfbplayer/PlayerOptions.cxx new file mode 100644 index 00000000..5384c6ed --- /dev/null +++ b/win/rfbplayer/PlayerOptions.cxx @@ -0,0 +1,152 @@ +/* Copyright (C) 2004 TightVNC Team. All Rights Reserved. + * + * This 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 software 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 software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +// -=- PlayerOptions class + +#include + +using namespace rfb::win32; + +PlayerOptions::PlayerOptions() { + writeDefaults(); +}; + +void PlayerOptions::readFromRegistry() { + try { + PixelFormat *pPF = 0; + int pfSize = sizeof(PixelFormat); + RegKey regKey; + regKey.createKey(HKEY_CURRENT_USER, _T("Software\\TightVnc\\RfbPlayer")); + autoPlay = regKey.getBool(_T("AutoPlay"), DEFAULT_AUTOPLAY); + autoDetectPF = regKey.getBool(_T("AutoDetectPixelFormat"), DEFAULT_AUTOPF); + bigEndianFlag = regKey.getBool(_T("BigEndianFlag"), DEFAULT_BIG_ENDIAN); + pixelFormatIndex = regKey.getInt(_T("PixelFormatIndex"), DEFAULT_PF_INDEX); + regKey.getBinary(_T("PixelFormat"), (void**)&pPF, &pfSize, + &PixelFormat(32,24,0,1,255,255,255,16,8,0), sizeof(PixelFormat)); + setPF(pPF); + acceptBell = regKey.getBool(_T("AcceptBell"), DEFAULT_ACCEPT_BELL); + acceptCutText = regKey.getBool(_T("AcceptCutText"), DEFAULT_ACCEPT_CUT_TEXT); + autoPlay = regKey.getBool(_T("AutoPlay"), DEFAULT_AUTOPLAY); + askPixelFormat = regKey.getBool(_T("AskPixelFormat"), DEFAULT_ASK_PF); + if (pPF) delete pPF; + } catch (rdr::Exception e) { + MessageBox(0, e.str(), "RFB Player", MB_OK | MB_ICONERROR); + } +} + +void PlayerOptions::writeToRegistry() { + try { + RegKey regKey; + regKey.createKey(HKEY_CURRENT_USER, _T("Software\\TightVnc\\RfbPlayer")); + regKey.setBool(_T("AutoPlay"), autoPlay); + regKey.setBool(_T("AutoDetectPixelFormat"), autoDetectPF); + regKey.setBool(_T("BigEndianFlag"), bigEndianFlag); + regKey.setInt(_T("PixelFormatIndex"), pixelFormatIndex); + regKey.setBinary(_T("PixelFormat"), &pixelFormat, sizeof(PixelFormat)); + regKey.setBool(_T("AcceptBell"), acceptBell); + regKey.setBool(_T("AcceptCutText"), acceptCutText); + regKey.setBool(_T("AutoPlay"), autoPlay); + regKey.setBool(_T("AskPixelFormat"), askPixelFormat); + } catch (rdr::Exception e) { + MessageBox(0, e.str(), "RFB Player", MB_OK | MB_ICONERROR); + } +} + +void PlayerOptions::writeDefaults() { + initTime = DEFAULT_INIT_TIME; + playbackSpeed = DEFAULT_SPEED; + autoDetectPF = DEFAULT_AUTOPF; + bigEndianFlag = DEFAULT_BIG_ENDIAN; + pixelFormatIndex = DEFAULT_PF_INDEX; + pixelFormat = PixelFormat(32,24,0,1,255,255,255,16,8,0); + frameScale = DEFAULT_FRAME_SCALE; + autoPlay = DEFAULT_AUTOPLAY; + fullScreen = DEFAULT_FULL_SCREEN; + acceptBell = DEFAULT_ACCEPT_BELL; + acceptCutText = DEFAULT_ACCEPT_CUT_TEXT; + loopPlayback = DEFAULT_LOOP_PLAYBACK; + askPixelFormat = DEFAULT_ASK_PF; + commandLineParam = false; +} + +void PlayerOptions::setPF(PixelFormat *newPF) { + memcpy(&pixelFormat, newPF, sizeof(PixelFormat)); +} + +bool PlayerOptions::setPF(int rgb_order, int rm, int gm, int bm, bool big_endian) { + PixelFormat newPF; + + // Calculate the colour bits per pixel + int bpp = rm + gm + bm; + if (bpp < 0) { + return false; + } else if (bpp <= 8 ) { + newPF.bpp = 8; + } else if (bpp <= 16) { + newPF.bpp = 16; + } else if (bpp <= 32) { + newPF.bpp = 32; + } else { + return false; + } + newPF.depth = bpp; + + // Calculate the r, g and b bits shifts + switch (rgb_order) { + case RGB_ORDER: + newPF.redShift = gm + bm; + newPF.greenShift = bm; + newPF.blueShift = 0; + break; + case RBG_ORDER: + newPF.redShift = bm + gm; + newPF.blueShift = gm; + newPF.greenShift = 0; + break; + case GRB_ORDER: + newPF.greenShift = rm + bm; + newPF.redShift = bm; + newPF.blueShift = 0; + break; + case GBR_ORDER: + newPF.greenShift = bm + rm; + newPF.blueShift = rm; + newPF.redShift = 0; + break; + case BGR_ORDER: + newPF.blueShift = gm + rm; + newPF.greenShift = rm; + newPF.redShift = 0; + break; + case BRG_ORDER: + newPF.blueShift = rm + gm; + newPF.redShift = gm; + newPF.greenShift = 0; + break; + default: + return false; + } + + newPF.trueColour = true; + newPF.bigEndian = big_endian; + newPF.redMax = (1 << rm) - 1; + newPF.greenMax = (1 << gm) - 1; + newPF.blueMax = (1 << bm) - 1; + setPF(&newPF); + return true; +} \ No newline at end of file diff --git a/win/rfbplayer/PlayerOptions.h b/win/rfbplayer/PlayerOptions.h new file mode 100644 index 00000000..83618a75 --- /dev/null +++ b/win/rfbplayer/PlayerOptions.h @@ -0,0 +1,77 @@ +/* Copyright (C) 2004 TightVNC Team. All Rights Reserved. + * + * This 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 software 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 software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +// -=- PlayerOptions.h + +// Definition of the PlayerOptions class, responsible for +// storing & retrieving the RfbPlayer's options. + +#include + +#include + +#define PF_MODES 3 + +// The R, G and B values order in the pixel +#define RGB_ORDER 0 +#define RBG_ORDER 1 +#define GRB_ORDER 2 +#define GBR_ORDER 3 +#define BGR_ORDER 4 +#define BRG_ORDER 5 + +// Default options values +//#define DEFAULT_PF 0 +#define DEFAULT_PF_INDEX -1 +#define DEFAULT_AUTOPF TRUE +#define DEFAULT_INIT_TIME -1 +#define DEFAULT_SPEED 1.0 +#define DEFAULT_FRAME_SCALE 100 +#define DEFAULT_ACCEPT_BELL FALSE +#define DEFAULT_ACCEPT_CUT_TEXT FALSE +#define DEFAULT_BIG_ENDIAN FALSE +#define DEFAULT_LOOP_PLAYBACK FALSE +#define DEFAULT_ASK_PF FALSE +#define DEFAULT_AUTOPLAY FALSE +#define DEFAULT_FULL_SCREEN FALSE + +using namespace rfb; + +class PlayerOptions { +public: + PlayerOptions(); + void readFromRegistry(); + void writeToRegistry(); + void writeDefaults(); + void setPF(PixelFormat *pf); + bool setPF(int rgb_order, int rm, int gm, int bm, bool big_endian=false); + long initTime; + double playbackSpeed; + bool autoPlay; + bool fullScreen; + bool autoDetectPF; + bool bigEndianFlag; + long pixelFormatIndex; + PixelFormat pixelFormat; + bool acceptBell; + bool acceptCutText; + bool loopPlayback; + bool askPixelFormat; + long frameScale; + bool commandLineParam; +}; \ No newline at end of file diff --git a/win/rfbplayer/PlayerToolBar.cxx b/win/rfbplayer/PlayerToolBar.cxx new file mode 100644 index 00000000..3056f283 --- /dev/null +++ b/win/rfbplayer/PlayerToolBar.cxx @@ -0,0 +1,248 @@ +/* Copyright (C) 2005 TightVNC Team. All Rights Reserved. + * + * This 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 software 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 software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +// -=- PlayerToolBar.cxx + +#include +#include + +PlayerToolBar::PlayerToolBar() +: ToolBar(), hFont(0), timeStatic(0), speedEdit(0), posTrackBar(0), + speedUpDown(0), sliderDragging(false), sliderStepMs(0) +{ +} + +void PlayerToolBar::create(RfbPlayer *player_, HWND parentHwnd_) { + HDC hdc; + SIZE sz; + RECT tRect; + NONCLIENTMETRICS nonClientMetrics; + + player = player_; + + // Get the default font for the main menu + nonClientMetrics.cbSize = sizeof(NONCLIENTMETRICS); + if (!SystemParametersInfo(SPI_GETNONCLIENTMETRICS, sizeof(NONCLIENTMETRICS), &nonClientMetrics, 0)) + MessageBox(0, "Can't access to the system font.", + "RfbPlayer", MB_OK | MB_ICONERROR); + nonClientMetrics.lfMenuFont.lfHeight = 16; + hFont = CreateFontIndirect(&nonClientMetrics.lfMenuFont); + + // Create the toolbar panel + ToolBar::create(ID_TOOLBAR, parentHwnd_, + WS_CHILD | WS_VISIBLE | TBSTYLE_FLAT | CCS_NORESIZE); + addBitmap(4, IDB_TOOLBAR); + + // Create the control buttons + addButton(0, ID_PLAY); + addButton(1, ID_PAUSE); + addButton(2, ID_STOP); + addButton(0, 0, TBSTATE_ENABLED, TBSTYLE_SEP); + + // Create the static control for the time output + timeStatic = CreateWindowEx(0, "Static", "00m:00s (00m:00s)", + WS_CHILD | WS_VISIBLE, 0, 0, 20, 20, getHandle(), (HMENU)ID_TIME_STATIC, + GetModuleHandle(0), 0); + SendMessage(timeStatic, WM_SETFONT,(WPARAM) hFont, TRUE); + hdc = GetDC(timeStatic); + SelectObject(hdc, hFont); + GetTextExtentPoint32(hdc, "00m:00s (00m:00s)", 16, &sz); + addButton(sz.cx + 10, 0, TBSTATE_ENABLED, TBSTYLE_SEP); + addButton(0, 10, TBSTATE_ENABLED, TBSTYLE_SEP); + getButtonRect(4, &tRect); + MoveWindow(timeStatic, tRect.left, tRect.top+2, tRect.right-tRect.left, + tRect.bottom-tRect.top, FALSE); + + // Create the trackbar control for the time position + addButton(200, 0, TBSTATE_ENABLED, TBSTYLE_SEP); + getButtonRect(6, &tRect); + posTrackBar = CreateWindowEx(0, TRACKBAR_CLASS, "Trackbar Control", + WS_CHILD | WS_VISIBLE | TBS_AUTOTICKS | TBS_ENABLESELRANGE, + tRect.left, tRect.top, tRect.right-tRect.left, tRect.bottom-tRect.top, + parentHwnd, (HMENU)ID_POS_TRACKBAR, GetModuleHandle(0), 0); + // It's need to send notify messages to toolbar parent window + SetParent(posTrackBar, getHandle()); + addButton(0, 10, TBSTATE_ENABLED, TBSTYLE_SEP); + + // Create the label with "Speed:" caption + HWND speedStatic = CreateWindowEx(0, "Static", "Speed:", WS_CHILD | WS_VISIBLE, + 0, 0, 5, 5, getHandle(), (HMENU)ID_SPEED_STATIC, GetModuleHandle(0), 0); + SendMessage(speedStatic, WM_SETFONT,(WPARAM) hFont, TRUE); + hdc = GetDC(speedStatic); + SelectObject(hdc, hFont); + GetTextExtentPoint32(hdc, "Speed:", 6, &sz); + addButton(sz.cx + 10, 0, TBSTATE_ENABLED, TBSTYLE_SEP); + getButtonRect(8, &tRect); + MoveWindow(speedStatic, tRect.left, tRect.top+2, tRect.right-tRect.left, + tRect.bottom-tRect.top, FALSE); + + // Create the edit control and the spin for the speed managing + addButton(60, 0, TBSTATE_ENABLED, TBSTYLE_SEP); + getButtonRect(9, &tRect); + speedEdit = CreateWindowEx(WS_EX_CLIENTEDGE, "Edit", "1.00", + WS_CHILD | WS_VISIBLE | ES_RIGHT, tRect.left, tRect.top, + tRect.right-tRect.left, tRect.bottom-tRect.top, parentHwnd, + (HMENU)ID_SPEED_EDIT, GetModuleHandle(0), 0); + SendMessage(speedEdit, WM_SETFONT,(WPARAM) hFont, TRUE); + // It's need to send notify messages to toolbar parent window + SetParent(speedEdit, getHandle()); + + speedUpDown = CreateUpDownControl(WS_CHILD | WS_VISIBLE + | WS_BORDER | UDS_ALIGNRIGHT, 0, 0, 0, 0, getHandle(), + ID_SPEED_UPDOWN, GetModuleHandle(0), speedEdit, 20, 1, 2); + + // Resize the toolbar window + autoSize(); +} + +void PlayerToolBar::init(long sessionTimeMs_) { + sessionTimeMs = sessionTimeMs_; + + setSessionTimeStr(sessionTimeMs); + SendMessage(posTrackBar, TBM_SETRANGE, + TRUE, MAKELONG(0, min(sessionTimeMs / 1000, MAX_POS_TRACKBAR_RANGE))); + if (sessionTimeMs == 0) { + sliderStepMs = 1; + } else { + sliderStepMs = sessionTimeMs / SendMessage(posTrackBar, TBM_GETRANGEMAX, 0, 0); + } + updatePos(0); +} + +void PlayerToolBar::enable() { + enableButton(ID_PLAY, true); + enableButton(ID_PAUSE, true); + enableButton(ID_STOP, true); + enableButton(ID_FULLSCREEN, true); + EnableWindow(posTrackBar, true); + EnableWindow(speedEdit, true); + EnableWindow(speedUpDown, true); +} + +void PlayerToolBar::disable() { + enableButton(ID_PLAY, false); + enableButton(ID_PAUSE, false); + enableButton(ID_STOP, false); + enableButton(ID_FULLSCREEN, false); + EnableWindow(posTrackBar, false); + EnableWindow(speedEdit, false); + EnableWindow(speedUpDown, false); +} + +LRESULT PlayerToolBar::processWM_COMMAND(WPARAM wParam, LPARAM lParam) { + switch (LOWORD(wParam)) { + + case ID_RETURN: + // Update the speed if return pressed in speedEdit + if (getSpeedEditHwnd() == GetFocus()) { + char speedStr[20], *stopStr; + GetWindowText(getSpeedEditHwnd(), speedStr, sizeof(speedStr)); + double speed = strtod(speedStr, &stopStr); + if (speed > 0) { + speed = min(MAX_SPEED, speed); + } else { + speed = player->getSpeed(); + } + player->setSpeed(speed); + return FALSE; + } + } + + return TRUE; +} + +LRESULT PlayerToolBar::processWM_HSCROLL(WPARAM wParam, LPARAM lParam) { + long Pos = SendMessage(posTrackBar, TBM_GETPOS, 0, 0); + Pos *= sliderStepMs; + + switch (LOWORD(wParam)) { + case TB_PAGEUP: + case TB_PAGEDOWN: + case TB_LINEUP: + case TB_LINEDOWN: + case TB_THUMBTRACK: + sliderDragging = true; + updatePos(Pos); + return FALSE; + case TB_THUMBPOSITION: + case TB_ENDTRACK: + player->setPos(Pos); + player->setPaused(player->isPaused());; + updatePos(Pos); + sliderDragging = false; + return FALSE; + default: + break; + } + + return TRUE; +} + +LRESULT PlayerToolBar::processWM_NOTIFY(WPARAM wParam, LPARAM lParam) { + switch (((NMHDR*)lParam)->code) { + case UDN_DELTAPOS: + if ((int)wParam == ID_SPEED_UPDOWN) { + char speedStr[20] = "\0"; + DWORD speedRange = SendMessage(speedUpDown, UDM_GETRANGE, 0, 0); + LPNM_UPDOWN upDown = (LPNM_UPDOWN)lParam; + double speed; + + // The out of range checking + if (upDown->iDelta > 0) { + speed = min(upDown->iPos + upDown->iDelta, LOWORD(speedRange)) * 0.5; + } else { + // It's need to round the UpDown position + if ((upDown->iPos * 0.5) != player->getSpeed()) { + upDown->iDelta = 0; + } + speed = max(upDown->iPos + upDown->iDelta, HIWORD(speedRange)) * 0.5; + } + player->setSpeed(speed); + } + } + + // We always return TRUE to prevent the change in the updown contol + // position. The control's position changes in the RfbPlayer::setSpeed(). + return TRUE; +} + +void PlayerToolBar::updatePos(long newPos) { + // Update time pos in static control + char timePos[30] = "\0"; + long time = newPos / 1000; + sprintf(timePos, "%.2um:%.2us (%s)", time/60, time%60, fullSessionTimeStr); + SetWindowText(timeStatic, timePos); + + // Update the position of slider + if (!sliderDragging) { + double error = SendMessage(posTrackBar, TBM_GETPOS, 0, 0) * + sliderStepMs / double(newPos); + if (!((error > 1 - CALCULATION_ERROR) && (error <= 1 + CALCULATION_ERROR))) { + SendMessage(posTrackBar, TBM_SETPOS, TRUE, newPos / sliderStepMs); + } + } +} + +void PlayerToolBar::setSessionTimeStr(long sessionTimeMs) { + sprintf(fullSessionTimeStr, "%.2um:%.2us", + sessionTimeMs / 1000 / 60, sessionTimeMs / 1000 % 60); +} + +void PlayerToolBar::setTimePos(long pos) { + SendMessage(posTrackBar, TBM_SETPOS, TRUE, pos); +} \ No newline at end of file diff --git a/win/rfbplayer/PlayerToolBar.h b/win/rfbplayer/PlayerToolBar.h new file mode 100644 index 00000000..17ab82e4 --- /dev/null +++ b/win/rfbplayer/PlayerToolBar.h @@ -0,0 +1,77 @@ +/* Copyright (C) 2005 TightVNC Team. All Rights Reserved. + * + * This 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 software 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 software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +// -=- PlayerToolBar.h + +// ToolBar for the RfbPlayer + +#include + +using namespace rfb::win32; + +#define ID_TOOLBAR 500 +#define ID_PLAY 510 +#define ID_PAUSE 520 +#define ID_TIME_STATIC 530 +#define ID_SPEED_STATIC 540 +#define ID_SPEED_EDIT 550 +#define ID_POS_TRACKBAR 560 +#define ID_SPEED_UPDOWN 570 + +#define MAX_SPEED 10.00 +#define CALCULATION_ERROR MAX_SPEED / 1000 +#define MAX_POS_TRACKBAR_RANGE 50 + +class RfbPlayer; + +class PlayerToolBar : public ToolBar { +public: + PlayerToolBar(); + ~PlayerToolBar() {} + + void create(RfbPlayer *player, HWND parentHwnd); + + void init(long sessionTimeMs); + + void enable(); + void disable(); + + LRESULT processWM_COMMAND(WPARAM wParam, LPARAM lParam); + LRESULT processWM_HSCROLL(WPARAM wParam, LPARAM lParam); + LRESULT processWM_NOTIFY(WPARAM wParam, LPARAM lParam); + + HWND getSpeedEditHwnd() { return speedEdit; } + HWND getSpeedUpDownHwnd() { return speedUpDown; } + + bool isPosSliderDragging() { return sliderDragging; }; + void updatePos(long newPos); + void setSessionTimeStr(long sessionTimeMs); + void setTimePos(long newPos); + +protected: + RfbPlayer *player; + HFONT hFont; + HWND timeStatic; + HWND speedEdit; + HWND posTrackBar; + HWND speedUpDown; + char fullSessionTimeStr[20]; + long sessionTimeMs; + bool sliderDragging; + long sliderStepMs; +}; diff --git a/win/rfbplayer/RfbProto.cxx b/win/rfbplayer/RfbProto.cxx new file mode 100644 index 00000000..5a7ff7f9 --- /dev/null +++ b/win/rfbplayer/RfbProto.cxx @@ -0,0 +1,142 @@ +/* Copyright (C) 2004 TightVNC Team. All Rights Reserved. + * + * This 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 software 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 software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +// -=- RFB Protocol + +#include +#include + +#include + +using namespace rfb; + +static LogWriter vlog("RfbProto"); + +// +// Constructor +// + +RfbProto::RfbProto(char *fileName) { + is = NULL; + reader = NULL; + newSession(fileName); +} + +// +// Destructor +// + +RfbProto::~RfbProto() { + if (is) delete is; + is = 0; + if (reader) delete reader; + reader = 0; +} + +void RfbProto::newSession(char *fileName) { + // Close the previous session + if (is) { + delete is; + is = NULL; + delete reader; + reader = NULL; + } + + // Begin the new session + if (fileName != NULL) { + is = new FbsInputStream(fileName); + reader = new CMsgReaderV3(this, is); + initialiseProtocol(); + } +} + +void RfbProto::initialiseProtocol() { + state_ = RFBSTATE_PROTOCOL_VERSION; +} + +void RfbProto::processMsg() +{ + switch (state_) { + + case RFBSTATE_PROTOCOL_VERSION: processVersionMsg(); break; + case RFBSTATE_SECURITY: processSecurityMsg(); break; + case RFBSTATE_INITIALISATION: processInitMsg(); break; + case RFBSTATE_NORMAL: reader->readMsg(); break; + default: + throw rfb::Exception("RfbProto::processMsg: invalid state"); + } +} + +void RfbProto::processVersionMsg() +{ + vlog.debug("reading protocol version"); + memset(&cp, 0, sizeof(cp)); + bool done; + if (!cp.readVersion(is, &done)) { + state_ = RFBSTATE_INVALID; + throw rfb::Exception("reading version failed: wrong file format?"); + } + if (!done) return; + + // The only official RFB protocol versions are currently 3.3, 3.7 and 3.8 + if (!cp.isVersion(3,3) && !cp.isVersion(3,7) && !cp.isVersion(3,8)) { + char msg[256]; + sprintf(msg,"File have unsupported RFB protocol version %d.%d", + cp.majorVersion, cp.minorVersion); + state_ = RFBSTATE_INVALID; + throw rfb::Exception(msg); + } + + state_ = RFBSTATE_SECURITY; + + vlog.info("Using RFB protocol version %d.%d", + cp.majorVersion, cp.minorVersion); +} + +void RfbProto::processSecurityMsg() +{ + vlog.debug("processing security types message"); + + int secType = secTypeInvalid; + + // legacy 3.3 server may only offer "vnc authentication" or "none" + secType = is->readU32(); + if (secType == secTypeInvalid) { + int reasonLen = is->readU32(); + char *reason = new char[reasonLen]; + is->readBytes(reason, reasonLen); + throw rfb::Exception(reason); + } + + if (secType != secTypeNone) { + throw rfb::Exception("Wrong authentication type in the session file"); + } + + state_ = RFBSTATE_INITIALISATION; +} + +void RfbProto::processInitMsg() { + vlog.debug("reading server initialisation"); + reader->readServerInit(); +} + +void RfbProto::serverInit() +{ + state_ = RFBSTATE_NORMAL; + vlog.debug("initialisation done"); +} diff --git a/win/rfbplayer/RfbProto.h b/win/rfbplayer/RfbProto.h new file mode 100644 index 00000000..316ea269 --- /dev/null +++ b/win/rfbplayer/RfbProto.h @@ -0,0 +1,69 @@ +/* Copyright (C) 2004 TightVNC Team. All Rights Reserved. + * + * This 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 software 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 software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +// -=- RfbProto.h + +#include +#include +#include +#include + +#include + +using namespace rfb; + +class RfbProto : public CMsgHandler { + public: + + RfbProto(char *fileName); + ~RfbProto(); + + void newSession(char *filename); + void initialiseProtocol(); + void interruptFrameDelay() { is->interruptFrameDelay(); }; + const rdr::InStream* getInStream() { return is; } + + virtual void processMsg(); + + // serverInit() is called when the ServerInit message is received. The + // derived class must call on to CMsgHandler::serverInit(). + void serverInit(); + + enum stateEnum { + RFBSTATE_PROTOCOL_VERSION, + RFBSTATE_SECURITY, + RFBSTATE_INITIALISATION, + RFBSTATE_NORMAL, + RFBSTATE_INVALID + }; + + stateEnum state() { return state_; } + + protected: + void setState(stateEnum s) { state_ = s; } + virtual void framebufferUpdateEnd() {}; + + FbsInputStream* is; + CMsgReaderV3* reader; + stateEnum state_; + + private: + void processVersionMsg(); + void processSecurityMsg(); + void processInitMsg(); +}; diff --git a/win/rfbplayer/SessionInfoDialog.h b/win/rfbplayer/SessionInfoDialog.h new file mode 100644 index 00000000..2c036db0 --- /dev/null +++ b/win/rfbplayer/SessionInfoDialog.h @@ -0,0 +1,100 @@ +/* Copyright (C) 2004 TightVNC Team. All Rights Reserved. + * + * This 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 software 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 software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +// -=- SessionInfoDialog.h + +#include + +#include + +#include + +#define log2(n) log(n) / 0.693147180559945 + +int max3(int v1, int v2, int v3) { + return max(v1, max(v2, v3)); +} + +class SessionInfoDialog : public rfb::win32::Dialog { +public: + SessionInfoDialog(ConnParams *_cp, int _currentEncoding) + : Dialog(GetModuleHandle(0)), cp(_cp), currentEncoding(_currentEncoding) {} + // - Show the dialog and return true if OK was clicked, + // false in case of error or Cancel + virtual bool showDialog(HWND parent = 0) { + return Dialog::showDialog(MAKEINTRESOURCE(IDD_SESSION_INFO), parent); + } +protected: + // Dialog methods (protected) + virtual void initDialog() { + char strValue[255] = "\0"; + setItemString(IDC_DESKTOP_NAME, cp->name()); + + sprintf(strValue, "%ix%i", cp->width, cp->height); + setItemString(IDC_DESKTOP_SIZE, strValue); + + int r = cp->pf().redShift, g = cp->pf().greenShift, b = cp->pf().blueShift; + int i = 3; + char buffer[10]; + sprintf(strValue, "depth %i(%ibpp), ", cp->pf().depth, cp->pf().bpp); + while (i) { + if (r == max3(r, g, b)) { + strcat(strValue, "r"); + _itoa(ceil(log2(cp->pf().redMax)), buffer, 10); + strcat(strValue, buffer); + r = -1; + i--; + continue; + } else if (g == max3(r, g, b)) { + strcat(strValue, "g"); + _itoa(ceil(log2(cp->pf().greenMax)), buffer, 10); + strcat(strValue, buffer); + g = -1; + i--; + continue; + } else if (b == max3(r, g, b)) { + strcat(strValue, "b"); + _itoa(ceil(log2(cp->pf().blueMax)), buffer, 10); + strcat(strValue, buffer); + b = -1; + i--; + continue; + } else break; + } + if (cp->pf().bigEndian) strcat(strValue, ", big-endian"); + else strcat(strValue, ", little-endian"); + setItemString(IDC_PIXEL_FORMAT, strValue); + + switch (currentEncoding) { + case encodingRaw: strcpy(strValue, "Raw"); break; + case encodingCopyRect: strcpy(strValue, "CopyRect"); break; + case encodingRRE: strcpy(strValue, "RRE"); break; + case encodingCoRRE: strcpy(strValue, "CoRRE"); break; + case encodingHextile: strcpy(strValue, "Hextile"); break; + case encodingTight: strcpy(strValue, "Tight"); break; + case encodingZRLE: strcpy(strValue, "ZRLE"); break; + default: strcpy(strValue, "Unknown"); + } + setItemString(IDC_CURRENT_ENCODING, strValue); + + sprintf(strValue, "%i.%i", cp->majorVersion, cp->minorVersion); + setItemString(IDC_VERSION, strValue); + } + ConnParams *cp; + int currentEncoding; +}; \ No newline at end of file diff --git a/win/rfbplayer/UserPixelFormatsDialog.h b/win/rfbplayer/UserPixelFormatsDialog.h new file mode 100644 index 00000000..fe2ad223 --- /dev/null +++ b/win/rfbplayer/UserPixelFormatsDialog.h @@ -0,0 +1,105 @@ +/* Copyright (C) 2004 TightVNC Team. All Rights Reserved. + * + * This 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 software 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 software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +// -=- UserPixelFormatsDialog.h + +#include + +#define UPF_REGISTRY_PATH "Software\\TightVnc\\RfbPlayer\\UserDefinedPF" + +class UserPixelFormatsDialog : public rfb::win32::Dialog { +public: + UserPixelFormatsDialog(PixelFormatList *_supportedPF) + : Dialog(GetModuleHandle(0)), supportedPF(_supportedPF), pfList(0) {} + // - Show the dialog and return true if OK was clicked, + // false in case of error or Cancel + virtual bool showDialog(HWND parent) { + return Dialog::showDialog(MAKEINTRESOURCE(IDD_USERPF_LIST), parent); + } + +protected: + // Dialog methods (protected) + virtual void initDialog() { + pfList = GetDlgItem(handle, IDC_PF_LIST); + for (int i = supportedPF->getDefaultPFCount(); i < supportedPF->count(); i++) { + SendMessage(pfList, LB_ADDSTRING, + 0, (LPARAM)(LPCTSTR)(((*supportedPF)[i])->format_name)); + } + SendMessage(pfList, LB_SETCURSEL, 0, 0); + } + virtual bool onCommand(int item, int cmd) { + switch (item) { + case IDC_ADD_BUTTON: + { + char format_name[MAX_STR_LEN] = ""; + PixelFormat pf(32, 24, 0, 1, 0, 0, 0, 0, 0, 0); + EditPixelFormatDialog edit(supportedPF, format_name, &pf); + if (edit.showDialog(handle)) { + supportedPF->add(format_name, pf); + SendMessage(pfList, LB_ADDSTRING, 0, (LPARAM)(LPCTSTR)format_name); + } + } + break; + case IDC_REMOVE_BUTTON: + { + int index = SendMessage(pfList, LB_GETCURSEL, 0, 0); + if (index == LB_ERR) { + MessageBox(handle, "You must select the pixel format for remove.", + "RfbPlayer", MB_OK | MB_ICONWARNING); + return false; + } else { + supportedPF->remove(supportedPF->getDefaultPFCount() + index); + SendMessage(pfList, LB_DELETESTRING, index, 0); + } + } + break; + case IDC_PF_LIST: + if (cmd != LBN_DBLCLK) break; + case IDC_EDIT_BUTTON: + { + int index = SendMessage(pfList, LB_GETCURSEL, 0, 0); + if (index == LB_ERR) { + MessageBox(handle, "You must select the pixel format for remove.", + "RfbPlayer", MB_OK | MB_ICONWARNING); + return false; + } + PixelFormat *pf = + &(supportedPF->operator[](index + supportedPF->getDefaultPFCount())->PF); + char *format_name = + (supportedPF)->operator[](index + supportedPF->getDefaultPFCount())->format_name; + EditPixelFormatDialog edit(supportedPF, format_name, pf); + if (edit.showDialog(handle)) { + SendMessage(pfList, LB_DELETESTRING, index, 0); + SendMessage(pfList, LB_INSERTSTRING, index, (LPARAM)(LPCTSTR)format_name); + SendMessage(pfList, LB_SETCURSEL, index, 0); + } + } + break; + default: + break; + } + return false; + } + virtual bool onOk() { + supportedPF->writeUserDefinedPF(HKEY_CURRENT_USER, UPF_REGISTRY_PATH); + return true; + } + + HWND pfList; + PixelFormatList *supportedPF; +}; \ No newline at end of file diff --git a/win/rfbplayer/buildTime.cxx b/win/rfbplayer/buildTime.cxx new file mode 100644 index 00000000..bab2e137 --- /dev/null +++ b/win/rfbplayer/buildTime.cxx @@ -0,0 +1 @@ +const char* buildTime = "Built on " __DATE__ " at " __TIME__; \ No newline at end of file diff --git a/win/rfbplayer/resource.h b/win/rfbplayer/resource.h new file mode 100644 index 00000000..90a057fe --- /dev/null +++ b/win/rfbplayer/resource.h @@ -0,0 +1,81 @@ +//{{NO_DEPENDENCIES}} +// Microsoft Developer Studio generated include file. +// Used by rfbplayer.rc +// +#define IDI_ICON 105 +#define IDR_MENU 128 +#define IDR_ACCELERATOR 131 +#define IDB_TOOLBAR 132 +#define IDD_GOTO 133 +#define IDD_PIXELFORMAT 134 +#define IDD_OPTIONS 137 +#define IDD_ABOUT 138 +#define IDD_USERPF_LIST 139 +#define IDD_UPF_EDIT 140 +#define IDD_INFO 141 +#define IDD_SESSION_INFO 142 +#define IDC_GOTO_EDIT 1003 +#define IDC_PIXELFORMAT 1004 +#define IDC_ASK_PF 1006 +#define IDC_DEFAULT 1008 +#define IDC_VERSION 1008 +#define IDC_ACCEPT_BELL 1009 +#define IDC_BUILDTIME 1009 +#define IDC_ACCEPT_CUT_TEXT 1010 +#define IDC_AUTO_STORE_PARAM 1011 +#define IDC_AUTOPLAY 1012 +#define IDC_BIG_ENDIAN 1013 +#define IDC_EDIT_UPF 1015 +#define IDC_PF_LIST 1016 +#define IDC_BPP_COMBO 1016 +#define IDC_ADD_BUTTON 1017 +#define IDC_NAME_EDIT 1017 +#define IDC_REMOVE_BUTTON 1018 +#define IDC_EDIT_BUTTON 1019 +#define IDC_COPYRIGHT 1021 +#define IDC_DEPTH_EDIT 1021 +#define IDC_DESCRIPTION 1022 +#define IDC_BIGENDIAN_COMBO 1022 +#define IDC_REDMAX_EDIT 1023 +#define IDC_GREENMAX_EDIT 1024 +#define IDC_BLUEMAX_EDIT 1025 +#define IDC_REDSHIFT_EDIT 1026 +#define IDC_DESKTOP_NAME 1026 +#define IDC_GREENSHIFT_EDIT 1027 +#define IDC_DESKTOP_SIZE 1027 +#define IDC_BLUESHIFT_EDIT 1028 +#define IDC_CURRENT_ENCODING 1029 +#define IDC_PIXEL_FORMAT 1030 +#define IDC_INFO_EDIT 1076 +#define ID_OPENFILE 40011 +#define ID_CLOSEFILE 40012 +#define ID_EXIT 40013 +#define ID_FULLSCREEN 40014 +#define ID_ZOOM_50 40015 +#define ID_ZOOM_100 40016 +#define ID_ZOOM_200 40017 +#define ID_PLAYPAUSE 40018 +#define ID_STOP 40019 +#define ID_GOTO 40020 +#define ID_LOOP 40022 +#define ID_COPYTOCLIPBOARD 40023 +#define ID_FRAMEEXTRACT 40024 +#define ID_HELP_HOMEPAGE 40025 +#define ID_HOMEPAGE 40025 +#define ID_HELP_COMMANDLINESWITCHES 40026 +#define ID_HELP_ABOUT 40027 +#define ID_ABOUT 40027 +#define ID_OPTIONS 40029 +#define ID_RETURN 40044 +#define ID_SESSION_INFO 40045 + +// Next default values for new objects +// +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS +#define _APS_NEXT_RESOURCE_VALUE 143 +#define _APS_NEXT_COMMAND_VALUE 40046 +#define _APS_NEXT_CONTROL_VALUE 1031 +#define _APS_NEXT_SYMED_VALUE 101 +#endif +#endif diff --git a/win/rfbplayer/rfbSessionReader.h b/win/rfbplayer/rfbSessionReader.h new file mode 100644 index 00000000..31985bc5 --- /dev/null +++ b/win/rfbplayer/rfbSessionReader.h @@ -0,0 +1,59 @@ +/* Copyright (C) 2004 TightVNC Team. All Rights Reserved. + * + * This 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 software 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 software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +// -=- rfbSessionReader.h + +#include + +#include + +using namespace rfb; + +class rfbSessionReader : public Thread { +public: + rfbSessionReader(RfbProto *_rfbSession) { + rfbSession = _rfbSession; + fStop = false; + rfbSession->interruptFrameDelay(); + }; + + ~rfbSessionReader() { + } + + virtual Thread* join() { + ((FbsInputStream*)(rfbSession->getInStream()))->resumePlayback(); + fStop = true; + return Thread::join(); + } + + virtual void rfbSessionReader::run() { + // Process the rfb messages + while (!fStop) { + try { + rfbSession->processMsg(); + } catch (rdr::Exception e) { + MessageBox(0, e.str(), "RFB Player", MB_OK | MB_ICONERROR); + break; + } + } + } + +protected: + bool fStop; + RfbProto *rfbSession; +}; \ No newline at end of file diff --git a/win/rfbplayer/rfbplayer.cxx b/win/rfbplayer/rfbplayer.cxx new file mode 100644 index 00000000..b304aece --- /dev/null +++ b/win/rfbplayer/rfbplayer.cxx @@ -0,0 +1,1294 @@ +/* Copyright (C) 2004 TightVNC Team. All Rights Reserved. + * + * This 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 software 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 software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +// -=- RFB Player for Win32 +#include +#include +#include + +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +using namespace rfb; +using namespace rfb::win32; + +// -=- Variables & consts + +static LogWriter vlog("RfbPlayer"); + +TStr rfb::win32::AppName("RfbPlayer"); +extern const char* buildTime; +HFONT hFont = 0; + +char wrong_cmd_msg[] = + "Wrong command-line parameters!\n" + "Use for help: rfbplayer -help"; + +char usage_msg[] = + "usage: rfbplayer \r\n" + "Command-line options:\r\n" + " -help \t- Provide usage information.\r\n" + " -pf \t- Forces the pixel format for the session.\r\n" + " \t =rgb[le|be],\r\n" + " \t r_bits - size the red component, in bits,\r\n" + " \t g_bits - size the green component, in bits,\r\n" + " \t b_bits - size the blue component, in bits,\r\n" + " \t le - little endian byte order (default),\r\n" + " \t be - big endian byte order.\r\n" + " \t The r, g, b component is in any order.\r\n" + " \t Default: auto detect the pixel format.\r\n" + " -upf \t- Forces the user defined pixel format for\r\n" + " \t the session. If is empty then application\r\n" + " \t shows the list of user defined pixel formats.\r\n" + " \t Don't use this option with -pf.\r\n" + " -speed \t- Sets playback speed, where 1 is normal speed,\r\n" + " \t is double speed, 0.5 is half speed. Default: 1.0.\r\n" + " -pos \t- Sets initial time position in the session file,\r\n" + " \t in milliseconds. Default: 0.\r\n" + " -autoplay \t- Runs the player in the playback mode.\r\n" + " -loop \t- Replays the rfb session."; + +// -=- RfbPlayer's defines and consts + +#define strcasecmp _stricmp +#define DEFAULT_PLAYER_WIDTH 640 +#define DEFAULT_PLAYER_HEIGHT 480 + +// +// -=- AboutDialog global values +// + +const WORD rfb::win32::AboutDialog::DialogId = IDD_ABOUT; +const WORD rfb::win32::AboutDialog::Copyright = IDC_COPYRIGHT; +const WORD rfb::win32::AboutDialog::Version = IDC_VERSION; +const WORD rfb::win32::AboutDialog::BuildTime = IDC_BUILDTIME; +const WORD rfb::win32::AboutDialog::Description = IDC_DESCRIPTION; + +// +// -=- RfbPlayerClass + +// +// Window class used as the basis for RfbPlayer instance +// + +class RfbPlayerClass { +public: + RfbPlayerClass(); + ~RfbPlayerClass(); + ATOM classAtom; + HINSTANCE instance; +}; + +LRESULT CALLBACK RfbPlayerProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) { + LRESULT result; + + if (msg == WM_CREATE) + SetWindowLong(hwnd, GWL_USERDATA, (long)((CREATESTRUCT*)lParam)->lpCreateParams); + else if (msg == WM_DESTROY) { + RfbPlayer* _this = (RfbPlayer*) GetWindowLong(hwnd, GWL_USERDATA); + SetWindowLong(hwnd, GWL_USERDATA, 0); + } + RfbPlayer* _this = (RfbPlayer*) GetWindowLong(hwnd, GWL_USERDATA); + if (!_this) { + vlog.info("null _this in %x, message %u", hwnd, msg); + return DefWindowProc(hwnd, msg, wParam, lParam); + } + + try { + result = _this->processMainMessage(hwnd, msg, wParam, lParam); + } catch (rdr::Exception& e) { + vlog.error("untrapped: %s", e.str()); + } + + return result; +}; + +RfbPlayerClass::RfbPlayerClass() : classAtom(0) { + WNDCLASS wndClass; + wndClass.style = 0; + wndClass.lpfnWndProc = RfbPlayerProc; + wndClass.cbClsExtra = 0; + wndClass.cbWndExtra = 0; + wndClass.hInstance = instance = GetModuleHandle(0); + wndClass.hIcon = (HICON)LoadImage(GetModuleHandle(0), + MAKEINTRESOURCE(IDI_ICON), IMAGE_ICON, 0, 0, LR_SHARED); + if (!wndClass.hIcon) + printf("unable to load icon:%ld", GetLastError()); + wndClass.hCursor = LoadCursor(NULL, IDC_ARROW); + wndClass.hbrBackground = HBRUSH(COLOR_WINDOW); + wndClass.lpszMenuName = MAKEINTRESOURCE(IDR_MENU); + wndClass.lpszClassName = _T("RfbPlayerClass"); + classAtom = RegisterClass(&wndClass); + if (!classAtom) { + throw rdr::SystemException("unable to register RfbPlayer window class", + GetLastError()); + } +} + +RfbPlayerClass::~RfbPlayerClass() { + if (classAtom) { + UnregisterClass((const TCHAR*)classAtom, instance); + } +} + +RfbPlayerClass baseClass; + +// +// -=- RfbFrameClass + +// +// Window class used to displaying the rfb data +// + +class RfbFrameClass { +public: + RfbFrameClass(); + ~RfbFrameClass(); + ATOM classAtom; + HINSTANCE instance; +}; + +LRESULT CALLBACK FrameProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) { + LRESULT result; + + if (msg == WM_CREATE) + SetWindowLong(hwnd, GWL_USERDATA, (long)((CREATESTRUCT*)lParam)->lpCreateParams); + else if (msg == WM_DESTROY) + SetWindowLong(hwnd, GWL_USERDATA, 0); + RfbPlayer* _this = (RfbPlayer*) GetWindowLong(hwnd, GWL_USERDATA); + if (!_this) { + vlog.info("null _this in %x, message %u", hwnd, msg); + return DefWindowProc(hwnd, msg, wParam, lParam); + } + + try { + result = _this->processFrameMessage(hwnd, msg, wParam, lParam); + } catch (rdr::Exception& e) { + vlog.error("untrapped: %s", e.str()); + } + + return result; +} + +RfbFrameClass::RfbFrameClass() : classAtom(0) { + WNDCLASS wndClass; + wndClass.style = 0; + wndClass.lpfnWndProc = FrameProc; + wndClass.cbClsExtra = 0; + wndClass.cbWndExtra = 0; + wndClass.hInstance = instance = GetModuleHandle(0); + wndClass.hIcon = 0; + wndClass.hCursor = LoadCursor(NULL, IDC_ARROW); + wndClass.hbrBackground = 0; + wndClass.lpszMenuName = 0; + wndClass.lpszClassName = _T("RfbPlayerClass1"); + classAtom = RegisterClass(&wndClass); + if (!classAtom) { + throw rdr::SystemException("unable to register RfbPlayer window class", + GetLastError()); + } +} + +RfbFrameClass::~RfbFrameClass() { + if (classAtom) { + UnregisterClass((const TCHAR*)classAtom, instance); + } +} + +RfbFrameClass frameClass; + +// +// -=- RfbPlayer instance implementation +// + +RfbPlayer::RfbPlayer(char *_fileName, PlayerOptions *_options) +: RfbProto(_fileName), fileName(_fileName), buffer(0), client_size(0, 0, 32, 32), + window_size(0, 0, 32, 32), cutText(0), seekMode(false), lastPos(0), + rfbReader(0), sessionTimeMs(0), sliderStepMs(0), imageDataStartTime(0), + rewindFlag(false), stopped(false), currentEncoding(-1) { + + // Save the player options + memcpy(&options, _options, sizeof(options)); + + // Reset the full session time + strcpy(fullSessionTime, "00m:00s"); + + // Load the user defined pixel formats from the registry + supportedPF.readUserDefinedPF(HKEY_CURRENT_USER, UPF_REGISTRY_PATH); + + // Create the main window + const TCHAR* name = _T("RfbPlayer"); + int x = max(0, (GetSystemMetrics(SM_CXSCREEN) - DEFAULT_PLAYER_WIDTH) / 2); + int y = max(0, (GetSystemMetrics(SM_CYSCREEN) - DEFAULT_PLAYER_HEIGHT) / 2); + mainHwnd = CreateWindow((const TCHAR*)baseClass.classAtom, name, WS_OVERLAPPEDWINDOW, + x, y, DEFAULT_PLAYER_WIDTH, DEFAULT_PLAYER_HEIGHT, 0, 0, baseClass.instance, this); + if (!mainHwnd) { + throw rdr::SystemException("unable to create WMNotifier window instance", GetLastError()); + } + vlog.debug("created window \"%s\" (%x)", (const char*)CStr(name), getMainHandle()); + + // Create the backing buffer + buffer = new win32::DIBSectionBuffer(getFrameHandle()); + setVisible(true); + + // If run with command-line parameters, + // open the session file with default settings, otherwise + // restore player settings from the registry + if (fileName) { + openSessionFile(fileName); + if (options.initTime > 0) setPos(options.initTime); + setSpeed(options.playbackSpeed); + } else { + options.readFromRegistry(); + disableTBandMenuItems(); + setTitle("None"); + } + init(); +} + +RfbPlayer::~RfbPlayer() { + vlog.debug("~RfbPlayer"); + if (rfbReader) { + delete rfbReader->join(); + rfbReader = 0; + } + if (mainHwnd) { + setVisible(false); + DestroyWindow(mainHwnd); + mainHwnd = 0; + } + if (buffer) delete buffer; + if (cutText) delete [] cutText; + if (fileName) delete [] fileName; + if (hFont) DeleteObject(hFont); + vlog.debug("~RfbPlayer done"); +} + +LRESULT +RfbPlayer::processMainMessage(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) { + + switch (msg) { + + // -=- Process standard window messages + + case WM_CREATE: + { + tb.create(this, hwnd); + + // Create the frame window + frameHwnd = CreateWindowEx(WS_EX_CLIENTEDGE, (const TCHAR*)frameClass.classAtom, + 0, WS_CHILD | WS_VISIBLE, 0, tb.getHeight(), 10, tb.getHeight() + 10, + hwnd, 0, frameClass.instance, this); + + hMenu = GetMenu(hwnd); + + return 0; + } + + // Process the main menu and toolbar's messages + + case WM_COMMAND: + + switch (LOWORD(wParam)) { + case ID_OPENFILE: + { + char curDir[_MAX_DIR]; + static char filename[_MAX_PATH]; + OPENFILENAME ofn; + memset((void *) &ofn, 0, sizeof(OPENFILENAME)); + GetCurrentDirectory(sizeof(curDir), curDir); + + ofn.lStructSize = sizeof(OPENFILENAME); + ofn.hwndOwner = getMainHandle(); + ofn.lpstrFile = filename; + ofn.nMaxFile = sizeof(filename); + ofn.lpstrInitialDir = curDir; + ofn.lpstrFilter = "Rfb Session files (*.rfb, *.fbs)\0*.rfb;*.fbs\0" \ + "All files (*.*)\0*.*\0"; + ofn.lpstrDefExt = "rfb"; + ofn.Flags = OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST; + if (GetOpenFileName(&ofn)) { + openSessionFile(filename); + } + } + break; + case ID_CLOSEFILE: + closeSessionFile(); + break; + case ID_SESSION_INFO: + { + SessionInfoDialog sessionInfo(&cp, currentEncoding); + sessionInfo.showDialog(getMainHandle()); + } + break; + case ID_PLAY: + setPaused(false); + break; + case ID_PAUSE: + setPaused(true); + break; + case ID_STOP: + stopPlayback(); + break; + case ID_PLAYPAUSE: + if (rfbReader) { + if (isPaused()) { + setPaused(false); + } else { + setPaused(true); + } + } + break; + case ID_GOTO: + { + GotoPosDialog gotoPosDlg; + if (gotoPosDlg.showDialog(getMainHandle())) { + long gotoTime = min(gotoPosDlg.getPos(), sessionTimeMs); + setPos(gotoTime); + tb.updatePos(gotoTime); + setPaused(isPaused());; + } + } + break; + case ID_LOOP: + options.loopPlayback = !options.loopPlayback; + if (options.loopPlayback) CheckMenuItem(hMenu, ID_LOOP, MF_CHECKED); + else CheckMenuItem(hMenu, ID_LOOP, MF_UNCHECKED); + break; + case ID_RETURN: + tb.processWM_COMMAND(wParam, lParam); + break; + case ID_OPTIONS: + { + OptionsDialog optionsDialog(&options, &supportedPF); + optionsDialog.showDialog(getMainHandle()); + } + break; + case ID_EXIT: + PostQuitMessage(0); + break; + case ID_HOMEPAGE: + ShellExecute(getMainHandle(), _T("open"), _T("http://www.tightvnc.com/"), + NULL, NULL, SW_SHOWDEFAULT); + break; + case ID_HELP_COMMANDLINESWITCHES: + { + InfoDialog usageDialog(usage_msg); + usageDialog.showDialog(getMainHandle()); + } + break; + case ID_ABOUT: + AboutDialog::instance.showDialog(); + break; + } + break; + + // Update frame's window size and add scrollbars if required + + case WM_SIZE: + { + + Point old_offset = bufferToClient(Point(0, 0)); + + // Update the cached sizing information + RECT r; + GetClientRect(getMainHandle(), &r); + MoveWindow(getFrameHandle(), 0, tb.getHeight(), r.right - r.left, + r.bottom - r.top - tb.getHeight(), TRUE); + + GetWindowRect(getFrameHandle(), &r); + window_size = Rect(r.left, r.top, r.right, r.bottom); + GetClientRect(getFrameHandle(), &r); + client_size = Rect(r.left, r.top, r.right, r.bottom); + + // Determine whether scrollbars are required + calculateScrollBars(); + + // Resize the ToolBar + tb.autoSize(); + + // Redraw if required + if (!old_offset.equals(bufferToClient(Point(0, 0)))) + InvalidateRect(getFrameHandle(), 0, TRUE); + } + break; + + case WM_HSCROLL: + tb.processWM_HSCROLL(wParam, lParam); + break; + + case WM_NOTIFY: + return tb.processWM_NOTIFY(wParam, lParam); + + case WM_CLOSE: + vlog.debug("WM_CLOSE %x", getMainHandle()); + PostQuitMessage(0); + break; + } + + return rfb::win32::SafeDefWindowProc(getMainHandle(), msg, wParam, lParam); +} + +LRESULT RfbPlayer::processFrameMessage(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) { + switch (msg) { + + case WM_PAINT: + { + if (isSeeking() || rewindFlag) { + seekMode = true; + return 0; + } else { + if (seekMode) { + seekMode = false; + InvalidateRect(getFrameHandle(), 0, true); + UpdateWindow(getFrameHandle()); + return 0; + } + } + + PAINTSTRUCT ps; + HDC paintDC = BeginPaint(getFrameHandle(), &ps); + if (!paintDC) + throw SystemException("unable to BeginPaint", GetLastError()); + Rect pr = Rect(ps.rcPaint.left, ps.rcPaint.top, ps.rcPaint.right, ps.rcPaint.bottom); + + if (!pr.is_empty()) { + + if (buffer->bitmap) { + + // Get device context + BitmapDC bitmapDC(paintDC, buffer->bitmap); + + // Blit the border if required + Rect bufpos = bufferToClient(buffer->getRect()); + if (!pr.enclosed_by(bufpos)) { + vlog.debug("draw border"); + HBRUSH black = (HBRUSH) GetStockObject(BLACK_BRUSH); + RECT r; + SetRect(&r, 0, 0, bufpos.tl.x, client_size.height()); FillRect(paintDC, &r, black); + SetRect(&r, bufpos.tl.x, 0, bufpos.br.x, bufpos.tl.y); FillRect(paintDC, &r, black); + SetRect(&r, bufpos.br.x, 0, client_size.width(), client_size.height()); FillRect(paintDC, &r, black); + SetRect(&r, bufpos.tl.x, bufpos.br.y, bufpos.br.x, client_size.height()); FillRect(paintDC, &r, black); + } + + // Do the blit + Point buf_pos = clientToBuffer(pr.tl); + if (!BitBlt(paintDC, pr.tl.x, pr.tl.y, pr.width(), pr.height(), + bitmapDC, buf_pos.x, buf_pos.y, SRCCOPY)) + throw SystemException("unable to BitBlt to window", GetLastError()); + + } else { + // Blit a load of black + if (!BitBlt(paintDC, pr.tl.x, pr.tl.y, pr.width(), pr.height(), + 0, 0, 0, BLACKNESS)) + throw SystemException("unable to BitBlt to blank window", GetLastError()); + } + } + EndPaint(getFrameHandle(), &ps); + } + return 0; + + // Process play/pause by the left mouse button + case WM_LBUTTONDOWN: + SendMessage(getMainHandle(), WM_COMMAND, ID_PLAYPAUSE, 0); + return 0; + + case WM_VSCROLL: + case WM_HSCROLL: + { + Point delta; + int newpos = (msg == WM_VSCROLL) ? scrolloffset.y : scrolloffset.x; + + switch (LOWORD(wParam)) { + case SB_PAGEUP: newpos -= 50; break; + case SB_PAGEDOWN: newpos += 50; break; + case SB_LINEUP: newpos -= 5; break; + case SB_LINEDOWN: newpos += 5; break; + case SB_THUMBTRACK: + case SB_THUMBPOSITION: newpos = HIWORD(wParam); break; + default: vlog.info("received unknown scroll message"); + }; + + if (msg == WM_HSCROLL) + setViewportOffset(Point(newpos, scrolloffset.y)); + else + setViewportOffset(Point(scrolloffset.x, newpos)); + + SCROLLINFO si; + si.cbSize = sizeof(si); + si.fMask = SIF_POS; + si.nPos = newpos; + SetScrollInfo(getFrameHandle(), (msg == WM_VSCROLL) ? SB_VERT : SB_HORZ, &si, TRUE); + } + break; + } + + return DefWindowProc(hwnd, msg, wParam, lParam); +} + +void RfbPlayer::disableTBandMenuItems() { + // Disable the menu items + EnableMenuItem(hMenu, ID_CLOSEFILE, MF_GRAYED | MF_BYCOMMAND); + EnableMenuItem(hMenu, ID_SESSION_INFO, MF_GRAYED | MF_BYCOMMAND); + ///EnableMenuItem(hMenu, ID_FULLSCREEN, MF_GRAYED | MF_BYCOMMAND); + ///EnableMenuItem(GetSubMenu(hMenu, 1), 1, MF_GRAYED | MF_BYPOSITION); + EnableMenuItem(hMenu, ID_PLAYPAUSE, MF_GRAYED | MF_BYCOMMAND); + EnableMenuItem(hMenu, ID_STOP, MF_GRAYED | MF_BYCOMMAND); + EnableMenuItem(hMenu, ID_GOTO, MF_GRAYED | MF_BYCOMMAND); + EnableMenuItem(hMenu, ID_LOOP, MF_GRAYED | MF_BYCOMMAND); + ///EnableMenuItem(hMenu, ID_COPYTOCLIPBOARD, MF_GRAYED | MF_BYCOMMAND); + ///EnableMenuItem(hMenu, ID_FRAMEEXTRACT, MF_GRAYED | MF_BYCOMMAND); + + // Disable the toolbar buttons and child controls + tb.disable(); +} + +void RfbPlayer::enableTBandMenuItems() { + // Enable the menu items + EnableMenuItem(hMenu, ID_CLOSEFILE, MF_ENABLED | MF_BYCOMMAND); + EnableMenuItem(hMenu, ID_SESSION_INFO, MF_ENABLED | MF_BYCOMMAND); + ///EnableMenuItem(hMenu, ID_FULLSCREEN, MF_ENABLED | MF_BYCOMMAND); + ///EnableMenuItem(GetSubMenu(hMenu, 1), 1, MF_ENABLED | MF_BYPOSITION); + EnableMenuItem(hMenu, ID_PLAYPAUSE, MF_ENABLED | MF_BYCOMMAND); + EnableMenuItem(hMenu, ID_STOP, MF_ENABLED | MF_BYCOMMAND); + EnableMenuItem(hMenu, ID_GOTO, MF_ENABLED | MF_BYCOMMAND); + EnableMenuItem(hMenu, ID_LOOP, MF_ENABLED | MF_BYCOMMAND); + ///EnableMenuItem(hMenu, ID_COPYTOCLIPBOARD, MF_ENABLED | MF_BYCOMMAND); + ///EnableMenuItem(hMenu, ID_FRAMEEXTRACT, MF_ENABLED | MF_BYCOMMAND); + + // Enable the toolbar buttons and child controls + tb.enable(); +} + +void RfbPlayer::setVisible(bool visible) { + ShowWindow(getMainHandle(), visible ? SW_SHOW : SW_HIDE); + if (visible) { + // When the window becomes visible, make it active + SetForegroundWindow(getMainHandle()); + SetActiveWindow(getMainHandle()); + } +} + +void RfbPlayer::setTitle(const char *title) { + char _title[256]; + strcpy(_title, AppName); + strcat(_title, " - "); + strcat(_title, title); + SetWindowText(getMainHandle(), _title); +} + +void RfbPlayer::setFrameSize(int width, int height) { + // Calculate and set required size for main window + RECT r = {0, 0, width, height}; + AdjustWindowRectEx(&r, GetWindowLong(getFrameHandle(), GWL_STYLE), TRUE, + GetWindowLong(getFrameHandle(), GWL_EXSTYLE)); + r.bottom += tb.getHeight(); // Include RfbPlayr's controls area + AdjustWindowRect(&r, GetWindowLong(getMainHandle(), GWL_STYLE), FALSE); + int x = max(0, (GetSystemMetrics(SM_CXSCREEN) - (r.right - r.left)) / 2); + int y = max(0, (GetSystemMetrics(SM_CYSCREEN) - (r.bottom - r.top)) / 2); + SetWindowPos(getMainHandle(), 0, x, y, r.right-r.left, r.bottom-r.top, + SWP_NOZORDER | SWP_NOACTIVATE | SWP_NOOWNERZORDER); + + // Enable/disable scrollbars as appropriate + calculateScrollBars(); +} + +void RfbPlayer::calculateScrollBars() { + // Calculate the required size of window + DWORD current_style = GetWindowLong(getFrameHandle(), GWL_STYLE); + DWORD style = current_style & ~(WS_VSCROLL | WS_HSCROLL); + DWORD old_style; + RECT r; + SetRect(&r, 0, 0, buffer->width(), buffer->height()); + AdjustWindowRectEx(&r, style, FALSE, GetWindowLong(getFrameHandle(), GWL_EXSTYLE)); + Rect reqd_size = Rect(r.left, r.top, r.right, r.bottom); + + // Work out whether scroll bars are required + do { + old_style = style; + + if (!(style & WS_HSCROLL) && (reqd_size.width() > window_size.width())) { + style |= WS_HSCROLL; + reqd_size.br.y += GetSystemMetrics(SM_CXHSCROLL); + } + if (!(style & WS_VSCROLL) && (reqd_size.height() > window_size.height())) { + style |= WS_VSCROLL; + reqd_size.br.x += GetSystemMetrics(SM_CXVSCROLL); + } + } while (style != old_style); + + // Tell Windows to update the window style & cached settings + if (style != current_style) { + SetWindowLong(getFrameHandle(), GWL_STYLE, style); + SetWindowPos(getFrameHandle(), NULL, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_FRAMECHANGED); + } + + // Update the scroll settings + SCROLLINFO si; + if (style & WS_VSCROLL) { + si.cbSize = sizeof(si); + si.fMask = SIF_RANGE | SIF_PAGE | SIF_POS; + si.nMin = 0; + si.nMax = buffer->height(); + si.nPage = buffer->height() - (reqd_size.height() - window_size.height()); + maxscrolloffset.y = max(0, si.nMax-si.nPage); + scrolloffset.y = min(maxscrolloffset.y, scrolloffset.y); + si.nPos = scrolloffset.y; + SetScrollInfo(getFrameHandle(), SB_VERT, &si, TRUE); + } + if (style & WS_HSCROLL) { + si.cbSize = sizeof(si); + si.fMask = SIF_RANGE | SIF_PAGE | SIF_POS; + si.nMin = 0; + si.nMax = buffer->width(); + si.nPage = buffer->width() - (reqd_size.width() - window_size.width()); + maxscrolloffset.x = max(0, si.nMax-si.nPage); + scrolloffset.x = min(maxscrolloffset.x, scrolloffset.x); + si.nPos = scrolloffset.x; + SetScrollInfo(getFrameHandle(), SB_HORZ, &si, TRUE); + } + + // Update the cached client size + GetClientRect(getFrameHandle(), &r); + client_size = Rect(r.left, r.top, r.right, r.bottom); +} + +bool RfbPlayer::setViewportOffset(const Point& tl) { +/* *** + Point np = Point(max(0, min(maxscrolloffset.x, tl.x)), + max(0, min(maxscrolloffset.y, tl.y))); + */ + Point np = Point(max(0, min(tl.x, buffer->width()-client_size.width())), + max(0, min(tl.y, buffer->height()-client_size.height()))); + Point delta = np.translate(scrolloffset.negate()); + if (!np.equals(scrolloffset)) { + scrolloffset = np; + ScrollWindowEx(getFrameHandle(), -delta.x, -delta.y, 0, 0, 0, 0, SW_INVALIDATE); + UpdateWindow(getFrameHandle()); + return true; + } + return false; +} + +void RfbPlayer::close(const char* reason) { + setVisible(false); + if (reason) { + vlog.info("closing - %s", reason); + MessageBox(NULL, TStr(reason), "RfbPlayer", MB_ICONINFORMATION | MB_OK); + } + SendMessage(getFrameHandle(), WM_CLOSE, 0, 0); +} + +void RfbPlayer::blankBuffer() { + fillRect(buffer->getRect(), 0); +} + +void RfbPlayer::rewind() { + bool paused = isPaused(); + blankBuffer(); + newSession(fileName); + skipHandshaking(); + is->setSpeed(options.playbackSpeed); + if (paused) is->pausePlayback(); + else is->resumePlayback(); +} + +void RfbPlayer::processMsg() { + // Perform return if waitWhilePaused processed because + // rfbReader thread could receive the signal to close + if (waitWhilePaused()) return; + + static long update_time = GetTickCount(); + try { + if ((!isSeeking()) && ((GetTickCount() - update_time) > 250) + && (!tb.isPosSliderDragging())) { + // Update pos in the toolbar 4 times in 1 second + tb.updatePos(getTimeOffset()); + update_time = GetTickCount(); + } + RfbProto::processMsg(); + } catch (rdr::Exception e) { + if (strcmp(e.str(), "[End Of File]") == 0) { + rewind(); + setPaused(!options.loopPlayback); + tb.updatePos(getTimeOffset()); + return; + } + // It's a special exception to perform backward seeking. + // We only rewind the stream and seek the offset + if (strcmp(e.str(), "[REWIND]") == 0) { + rewindFlag = true; + long seekOffset = max(getSeekOffset(), imageDataStartTime); + rewind(); + if (!stopped) setPos(seekOffset); + else stopped = false; + tb.updatePos(seekOffset); + rewindFlag = false; + return; + } + // It's a special exception which is used to terminate the playback + if (strcmp(e.str(), "[TERMINATE]") == 0) { + sessionTerminateThread *terminate = new sessionTerminateThread(this); + terminate->start(); + } else { + // Show the exception message and close the session playback + is->pausePlayback(); + char message[256] = "\0"; + strcat(message, e.str()); + strcat(message, "\nMaybe you force wrong the pixel format for this session"); + MessageBox(getMainHandle(), message, "RFB Player", MB_OK | MB_ICONERROR); + sessionTerminateThread *terminate = new sessionTerminateThread(this); + terminate->start(); + return; + } + } +} + +long ChoosePixelFormatDialog::pfIndex = DEFAULT_PF_INDEX; +bool ChoosePixelFormatDialog::bigEndian = false; + +void RfbPlayer::serverInit() { + RfbProto::serverInit(); + + // Save the image data start time + imageDataStartTime = is->getTimeOffset(); + + // Resize the backing buffer + buffer->setSize(cp.width, cp.height); + + // Check on the true colour mode + if (!(cp.pf()).trueColour) + throw rdr::Exception("This version plays only true color session!"); + + // Set the session pixel format + if (options.askPixelFormat) { + ChoosePixelFormatDialog choosePixelFormatDialog(&supportedPF); + if (choosePixelFormatDialog.showDialog(getMainHandle())) { + long pixelFormatIndex = choosePixelFormatDialog.getPFIndex(); + if (pixelFormatIndex < 0) { + options.autoDetectPF = true; + options.setPF((PixelFormat *)&cp.pf()); + } else { + options.autoDetectPF = false; + options.setPF(&supportedPF[pixelFormatIndex]->PF); + options.pixelFormat.bigEndian = choosePixelFormatDialog.isBigEndian(); + } + } else { + is->pausePlayback(); + throw rdr::Exception("[TERMINATE]"); + } + } else { + if (!options.commandLineParam) { + if (options.autoDetectPF) { + options.setPF((PixelFormat *)&cp.pf()); + } else { + options.setPF(&supportedPF[options.pixelFormatIndex]->PF); + options.pixelFormat.bigEndian = options.bigEndianFlag; + } + } else if (options.autoDetectPF) { + options.setPF((PixelFormat *)&cp.pf()); + } + } + cp.setPF(options.pixelFormat); + buffer->setPF(options.pixelFormat); + + // If the window is not maximised then resize it + if (!(GetWindowLong(getMainHandle(), GWL_STYLE) & WS_MAXIMIZE)) + setFrameSize(cp.width, cp.height); + + // Set the window title and show it + setTitle(cp.name()); + + // Calculate the full session time and update posTrackBar control in toolbar + sessionTimeMs = calculateSessionTime(fileName); + tb.init(sessionTimeMs); + tb.updatePos(getTimeOffset()); + + setPaused(!options.autoPlay); + // Restore the parameters from registry, + // which was replaced by command-line parameters. + if (options.commandLineParam) { + options.readFromRegistry(); + options.commandLineParam = false; + } +} + +void RfbPlayer::setColourMapEntries(int first, int count, U16* rgbs) { + vlog.debug("setColourMapEntries: first=%d, count=%d", first, count); + throw rdr::Exception("Can't handle SetColourMapEntries message"); +/* int i; + for (i=0;isetColour(i+first, rgbs[i*3], rgbs[i*3+1], rgbs[i*3+2]); + } + // *** change to 0, 256? + refreshWindowPalette(first, count); + palette_changed = true; + InvalidateRect(getFrameHandle(), 0, FALSE);*/ +} + +void RfbPlayer::bell() { + if (options.acceptBell) + MessageBeep(-1); +} + +void RfbPlayer::serverCutText(const char* str, int len) { + if (cutText != NULL) + delete [] cutText; + cutText = new char[len + 1]; + memcpy(cutText, str, len); + cutText[len] = '\0'; +} + +void RfbPlayer::frameBufferUpdateEnd() { +}; + +void RfbPlayer::beginRect(const Rect& r, unsigned int encoding) { + currentEncoding = encoding; +} + +void RfbPlayer::endRect(const Rect& r, unsigned int encoding) { +} + + +void RfbPlayer::fillRect(const Rect& r, Pixel pix) { + buffer->fillRect(r, pix); + invalidateBufferRect(r); +} + +void RfbPlayer::imageRect(const Rect& r, void* pixels) { + buffer->imageRect(r, pixels); + invalidateBufferRect(r); +} + +void RfbPlayer::copyRect(const Rect& r, int srcX, int srcY) { + buffer->copyRect(r, Point(r.tl.x-srcX, r.tl.y-srcY)); + invalidateBufferRect(r); +} + +bool RfbPlayer::invalidateBufferRect(const Rect& crect) { + Rect rect = bufferToClient(crect); + if (rect.intersect(client_size).is_empty()) return false; + RECT invalid = {rect.tl.x, rect.tl.y, rect.br.x, rect.br.y}; + InvalidateRect(getFrameHandle(), &invalid, FALSE); + return true; +} + +bool RfbPlayer::waitWhilePaused() { + bool result = false; + while(isPaused() && !isSeeking()) { + Sleep(20); + result = true; + } + return result; +} + +long RfbPlayer::calculateSessionTime(char *filename) { + FbsInputStream sessionFile(filename); + sessionFile.setTimeOffset(100000000); + try { + while (TRUE) { + sessionFile.skip(1024); + } + } catch (rdr::Exception e) { + if (strcmp(e.str(), "[End Of File]") == 0) { + return sessionFile.getTimeOffset(); + } else { + MessageBox(getMainHandle(), e.str(), "RFB Player", MB_OK | MB_ICONERROR); + return 0; + } + } + return 0; +} + +void RfbPlayer::init() { + if (options.loopPlayback) CheckMenuItem(hMenu, ID_LOOP, MF_CHECKED); + else CheckMenuItem(hMenu, ID_LOOP, MF_UNCHECKED); +} + +void RfbPlayer::closeSessionFile() { + DWORD dwStyle; + RECT r; + + // Uncheck all toolbar buttons + if (tb.getHandle()) { + tb.checkButton(ID_PLAY, false); + tb.checkButton(ID_PAUSE, false); + tb.checkButton(ID_STOP, false); + } + + // Stop playback and update the player state + disableTBandMenuItems(); + if (rfbReader) { + delete rfbReader->join(); + rfbReader = 0; + delete [] fileName; + fileName = 0; + } + blankBuffer(); + setTitle("None"); + options.playbackSpeed = 1.0; + tb.init(0); + + // Change the player window size and frame size to default + if ((dwStyle = GetWindowLong(getMainHandle(), GWL_STYLE)) & WS_MAXIMIZE) { + dwStyle &= ~WS_MAXIMIZE; + SetWindowLong(getMainHandle(), GWL_STYLE, dwStyle); + } + int x = max(0, (GetSystemMetrics(SM_CXSCREEN) - DEFAULT_PLAYER_WIDTH) / 2); + int y = max(0, (GetSystemMetrics(SM_CYSCREEN) - DEFAULT_PLAYER_HEIGHT) / 2); + SetWindowPos(getMainHandle(), 0, x, y, + DEFAULT_PLAYER_WIDTH, DEFAULT_PLAYER_HEIGHT, + SWP_NOZORDER | SWP_FRAMECHANGED); + buffer->setSize(32, 32); + calculateScrollBars(); + + // Update the cached sizing information and repaint the frame window + GetWindowRect(getFrameHandle(), &r); + window_size = Rect(r.left, r.top, r.right, r.bottom); + GetClientRect(getFrameHandle(), &r); + client_size = Rect(r.left, r.top, r.right, r.bottom); + InvalidateRect(getFrameHandle(), 0, TRUE); + UpdateWindow(getFrameHandle()); +} + +void RfbPlayer::openSessionFile(char *_fileName) { + fileName = strDup(_fileName); + + // Close the previous reading thread + if (rfbReader) { + delete rfbReader->join(); + rfbReader = 0; + } + blankBuffer(); + newSession(fileName); + setSpeed(options.playbackSpeed); + rfbReader = new rfbSessionReader(this); + rfbReader->start(); + tb.setTimePos(0); + enableTBandMenuItems(); +} + +void RfbPlayer::setPaused(bool paused) { + if (paused) { + is->pausePlayback(); + tb.checkButton(ID_PAUSE, true); + tb.checkButton(ID_PLAY, false); + tb.checkButton(ID_STOP, false); + } else { + if (is) is->resumePlayback(); + tb.checkButton(ID_PLAY, true); + tb.checkButton(ID_STOP, false); + tb.checkButton(ID_PAUSE, false); + } + tb.enableButton(ID_PAUSE, true); + EnableMenuItem(hMenu, ID_STOP, MF_ENABLED | MF_BYCOMMAND); +} + +void RfbPlayer::stopPlayback() { + stopped = true; + setPos(0); + if (is) { + is->pausePlayback(); + is->interruptFrameDelay(); + } + tb.checkButton(ID_STOP, true); + tb.checkButton(ID_PLAY, false); + tb.checkButton(ID_PAUSE, false); + tb.enableButton(ID_PAUSE, false); + tb.setTimePos(0); + EnableMenuItem(hMenu, ID_STOP, MF_GRAYED | MF_BYCOMMAND); +} + +void RfbPlayer::setSpeed(double speed) { + if (speed > 0) { + char speedStr[20] = "\0"; + double newSpeed = min(speed, MAX_SPEED); + is->setSpeed(newSpeed); + options.playbackSpeed = newSpeed; + SendMessage(tb.getSpeedUpDownHwnd(), UDM_SETPOS, + 0, MAKELONG((short)(newSpeed / 0.5), 0)); + sprintf(speedStr, "%.2f", newSpeed); + SetWindowText(tb.getSpeedEditHwnd(), speedStr); + } +} + +double RfbPlayer::getSpeed() { + return is->getSpeed(); +} + +void RfbPlayer::setPos(long pos) { + is->setTimeOffset(max(pos, imageDataStartTime)); +} + +long RfbPlayer::getSeekOffset() { + return is->getSeekOffset(); +} + +bool RfbPlayer::isSeeking() { + if (is) return is->isSeeking(); + else return false; +} + +bool RfbPlayer::isSeekMode() { + return seekMode; +} + +bool RfbPlayer::isPaused() { + return is->isPaused(); +} + +long RfbPlayer::getTimeOffset() { + return max(is->getTimeOffset(), is->getSeekOffset()); +} + +void RfbPlayer::skipHandshaking() { + int skipBytes = 12 + 4 + 24 + strlen(cp.name()); + is->skip(skipBytes); + state_ = RFBSTATE_NORMAL; +} + +void programInfo() { + win32::FileVersionInfo inf; + _tprintf(_T("%s - %s, Version %s\n"), + inf.getVerString(_T("ProductName")), + inf.getVerString(_T("FileDescription")), + inf.getVerString(_T("FileVersion"))); + printf("%s\n", buildTime); + _tprintf(_T("%s\n\n"), inf.getVerString(_T("LegalCopyright"))); +} + +void programUsage() { + InfoDialog usageDialog(usage_msg); + usageDialog.showDialog(); +} + +char *fileName = 0; + +// playerOptions is the player options with default parameters values, +// it is used only for run the player with command-line parameters +PlayerOptions playerOptions; +bool print_usage = false; +bool print_upf_list = false; + +bool processParams(int argc, char* argv[]) { + playerOptions.commandLineParam = true; + for (int i = 1; i < argc; i++) { + if ((strcasecmp(argv[i], "-help") == 0) || + (strcasecmp(argv[i], "--help") == 0) || + (strcasecmp(argv[i], "/help") == 0) || + (strcasecmp(argv[i], "-h") == 0) || + (strcasecmp(argv[i], "/h") == 0) || + (strcasecmp(argv[i], "/?") == 0) || + (strcasecmp(argv[i], "-?") == 0)) { + print_usage = true; + return true; + } + + if ((strcasecmp(argv[i], "-pf") == 0) || + (strcasecmp(argv[i], "/pf") == 0) && (i < argc-1)) { + char *pf = argv[++i]; + char rgb_order[4] = "\0"; + int order = RGB_ORDER; + int r = -1, g = -1, b = -1; + bool big_endian = false; + if (strlen(pf) < 6) return false; + while (strlen(pf)) { + if ((pf[0] == 'r') || (pf[0] == 'R')) { + if (r >=0 ) return false; + r = atoi(++pf); + strcat(rgb_order, "r"); + continue; + } + if ((pf[0] == 'g') || (pf[0] == 'G')) { + if (g >=0 ) return false; + g = atoi(++pf); + strcat(rgb_order, "g"); + continue; + } + if (((pf[0] == 'b') || (pf[0] == 'B')) && + (pf[1] != 'e') && (pf[1] != 'E')) { + if (b >=0 ) return false; + b = atoi(++pf); + strcat(rgb_order, "b"); + continue; + } + if ((pf[0] == 'l') || (pf[0] == 'L') || + (pf[0] == 'b') || (pf[0] == 'B')) { + if (strcasecmp(pf, "le") == 0) break; + if (strcasecmp(pf, "be") == 0) { big_endian = true; break;} + return false; + } + pf++; + } + if ((r < 0) || (g < 0) || (b < 0) || (r + g + b > 32)) return false; + if (strcasecmp(rgb_order, "rgb") == 0) { order = RGB_ORDER; } + else if (strcasecmp(rgb_order, "rbg") == 0) { order = RBG_ORDER; } + else if (strcasecmp(rgb_order, "grb") == 0) { order = GRB_ORDER; } + else if (strcasecmp(rgb_order, "gbr") == 0) { order = GBR_ORDER; } + else if (strcasecmp(rgb_order, "bgr") == 0) { order = BGR_ORDER; } + else if (strcasecmp(rgb_order, "brg") == 0) { order = BRG_ORDER; } + else return false; + playerOptions.autoDetectPF = false; + playerOptions.setPF(order, r, g, b, big_endian); + continue; + } + + if ((strcasecmp(argv[i], "-upf") == 0) || + (strcasecmp(argv[i], "/upf") == 0) && (i < argc-1)) { + if ((i == argc - 1) || (argv[++i][0] == '-')) { + print_upf_list = true; + return true; + } + PixelFormatList userPfList; + userPfList.readUserDefinedPF(HKEY_CURRENT_USER, UPF_REGISTRY_PATH); + int index = userPfList.getIndexByPFName(argv[i]); + if (index > 0) { + playerOptions.autoDetectPF = false; + playerOptions.setPF(&userPfList[index]->PF); + } else { + return false; + } + continue; + } + + if ((strcasecmp(argv[i], "-speed") == 0) || + (strcasecmp(argv[i], "/speed") == 0) && (i < argc-1)) { + double playbackSpeed = atof(argv[++i]); + if (playbackSpeed <= 0) { + return false; + } + playerOptions.playbackSpeed = playbackSpeed; + continue; + } + + if ((strcasecmp(argv[i], "-pos") == 0) || + (strcasecmp(argv[i], "/pos") == 0) && (i < argc-1)) { + long initTime = atol(argv[++i]); + if (initTime <= 0) + return false; + playerOptions.initTime = initTime; + continue; + } + + if ((strcasecmp(argv[i], "-autoplay") == 0) || + (strcasecmp(argv[i], "/autoplay") == 0) && (i < argc-1)) { + playerOptions.autoPlay = true; + continue; + } + + if ((strcasecmp(argv[i], "-loop") == 0) || + (strcasecmp(argv[i], "/loop") == 0) && (i < argc-1)) { + playerOptions.loopPlayback = true; + continue; + } + + if (i != argc - 1) + return false; + } + + fileName = strDup(argv[argc-1]); + if (fileName[0] == '-') return false; + else return true; +} + +// +// -=- WinMain +// + +int WINAPI WinMain(HINSTANCE inst, HINSTANCE prevInst, char* cmdLine, int cmdShow) { + + // - Process the command-line + + int argc = __argc; + char** argv = __argv; + if ((argc > 1) && (!processParams(argc, argv))) { + MessageBox(0, wrong_cmd_msg, "RfbPlayer", MB_OK | MB_ICONWARNING); + return 0; + } + + if (print_usage) { + programUsage(); + return 0; + } + // Show the user defined pixel formats if required + if (print_upf_list) { + int list_size = 256; + char *upf_list = new char[list_size]; + PixelFormatList userPfList; + userPfList.readUserDefinedPF(HKEY_CURRENT_USER, UPF_REGISTRY_PATH); + strcpy(upf_list, "The list of the user defined pixel formats:\r\n"); + for (int i = userPfList.getDefaultPFCount(); i < userPfList.count(); i++) { + if ((list_size - strlen(upf_list) - 1) < + (strlen(userPfList[i]->format_name) + 2)) { + char *tmpStr = new char[list_size = + list_size + strlen(userPfList[i]->format_name) + 2]; + strcpy(tmpStr, upf_list); + delete [] upf_list; + upf_list = new char[list_size]; + strcpy(upf_list, tmpStr); + delete [] tmpStr; + } + strcat(upf_list, userPfList[i]->format_name); + strcat(upf_list, "\r\n"); + } + InfoDialog upfInfoDialog(upf_list); + upfInfoDialog.showDialog(); + delete [] upf_list; + return 0; + } + + // Create the player + RfbPlayer *player = NULL; + try { + player = new RfbPlayer(fileName, &playerOptions); + } catch (rdr::Exception e) { + MessageBox(NULL, e.str(), "RFB Player", MB_OK | MB_ICONERROR); + delete player; + return 0; + } + + // Run the player + HACCEL hAccel = LoadAccelerators(inst, MAKEINTRESOURCE(IDR_ACCELERATOR)); + MSG msg; + while (GetMessage(&msg, NULL, 0, 0) > 0) { + if(!TranslateAccelerator(player->getMainHandle(), hAccel, &msg)) { + TranslateMessage(&msg); + DispatchMessage(&msg); + } + } + + // Destroy the player + try{ + if (player) delete player; + } catch (rdr::Exception e) { + MessageBox(NULL, e.str(), "RFB Player", MB_OK | MB_ICONERROR); + } + + return 0; +}; diff --git a/win/rfbplayer/rfbplayer.dsp b/win/rfbplayer/rfbplayer.dsp new file mode 100644 index 00000000..90685369 --- /dev/null +++ b/win/rfbplayer/rfbplayer.dsp @@ -0,0 +1,207 @@ +# Microsoft Developer Studio Project File - Name="rfbplayer" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Application" 0x0101 + +CFG=rfbplayer - Win32 Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "rfbplayer.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "rfbplayer.mak" CFG="rfbplayer - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "rfbplayer - Win32 Release" (based on "Win32 (x86) Application") +!MESSAGE "rfbplayer - Win32 Debug" (based on "Win32 (x86) Application") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +CPP=cl.exe +MTL=midl.exe +RSC=rc.exe + +!IF "$(CFG)" == "rfbplayer - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "..\Release" +# PROP Intermediate_Dir "..\Release\rfbplayer" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /YX /FD /c +# ADD CPP /nologo /MT /W3 /GX /O2 /I ".." /I "../../common" /FI"rdr/msvcwarning.h" /D "NDEBUG" /D "_WINDOWS" /D "WIN32" /D "_MBCS" /YX /FD /c +# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32 +# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32 +# ADD BASE RSC /l 0x419 /d "NDEBUG" +# ADD RSC /l 0x409 /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /machine:I386 +# ADD LINK32 user32.lib gdi32.lib Advapi32.lib comctl32.lib shell32.lib comdlg32.lib version.lib /nologo /subsystem:windows /machine:I386 +# Begin Special Build Tool +SOURCE="$(InputPath)" +PreLink_Cmds=cl /c /nologo /Fo..\Release\ /Fd..\Release\rfbplayer /MTd buildTime.cxx +# End Special Build Tool + +!ELSEIF "$(CFG)" == "rfbplayer - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Debug" +# PROP BASE Intermediate_Dir "Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "..\Debug" +# PROP Intermediate_Dir "..\Debug\rfbplayer" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /YX /FD /GZ /c +# ADD CPP /nologo /MTd /W3 /Gm /GX /Zi /Od /I ".." /I "../../common" /FI"rdr/msvcwarning.h" /D "_DEBUG" /D "_WINDOWS" /D "WIN32" /D "_MBCS" /FR /YX /FD /GZ /c +# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /win32 +# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32 +# ADD BASE RSC /l 0x419 /d "_DEBUG" +# ADD RSC /l 0x409 /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /debug /machine:I386 /pdbtype:sept +# ADD LINK32 user32.lib gdi32.lib advapi32.lib ws2_32.lib version.lib comctl32.lib shell32.lib comdlg32.lib /nologo /subsystem:windows /debug /machine:I386 /pdbtype:sept +# Begin Special Build Tool +SOURCE="$(InputPath)" +PreLink_Cmds=cl /c /nologo /Fo..\Debug\ /Fd..\Debug\rfbplayer /MTd buildTime.cxx +# End Special Build Tool + +!ENDIF + +# Begin Target + +# Name "rfbplayer - Win32 Release" +# Name "rfbplayer - Win32 Debug" +# Begin Group "Source Files" + +# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" +# Begin Source File + +SOURCE=.\buildTime.cxx +# End Source File +# Begin Source File + +SOURCE=.\FbsInputStream.cxx +# End Source File +# Begin Source File + +SOURCE=.\PixelFormatList.cxx +# End Source File +# Begin Source File + +SOURCE=.\PlayerOptions.cxx +# End Source File +# Begin Source File + +SOURCE=.\PlayerToolBar.cxx +# End Source File +# Begin Source File + +SOURCE=.\rfbplayer.cxx +# End Source File +# Begin Source File + +SOURCE=.\rfbplayer.rc +# End Source File +# Begin Source File + +SOURCE=.\RfbProto.cxx +# End Source File +# End Group +# Begin Group "Header Files" + +# PROP Default_Filter "h;hpp;hxx;hm;inl" +# Begin Source File + +SOURCE=.\ChoosePixelFormatDialog.h +# End Source File +# Begin Source File + +SOURCE=.\EditPixelFormatDialog.h +# End Source File +# Begin Source File + +SOURCE=.\FbsInputStream.h +# End Source File +# Begin Source File + +SOURCE=.\GotoPosDialog.h +# End Source File +# Begin Source File + +SOURCE=.\InfoDialog.h +# End Source File +# Begin Source File + +SOURCE=.\OptionsDialog.h +# End Source File +# Begin Source File + +SOURCE=.\PixelFormatList.h +# End Source File +# Begin Source File + +SOURCE=.\PlayerOptions.h +# End Source File +# Begin Source File + +SOURCE=.\PlayerToolBar.h +# End Source File +# Begin Source File + +SOURCE=.\rfbplayer.h +# End Source File +# Begin Source File + +SOURCE=.\RfbProto.h +# End Source File +# Begin Source File + +SOURCE=.\rfbSessionReader.h +# End Source File +# Begin Source File + +SOURCE=.\SessionInfoDialog.h +# End Source File +# Begin Source File + +SOURCE=.\UserPixelFormatsDialog.h +# End Source File +# End Group +# Begin Group "Resource Files" + +# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe" +# Begin Source File + +SOURCE=.\rfbplayer.ico +# End Source File +# Begin Source File + +SOURCE=.\toolbar.bmp +# End Source File +# End Group +# End Target +# End Project diff --git a/win/rfbplayer/rfbplayer.h b/win/rfbplayer/rfbplayer.h new file mode 100644 index 00000000..c5c5da8d --- /dev/null +++ b/win/rfbplayer/rfbplayer.h @@ -0,0 +1,195 @@ +/* Copyright (C) 2004 TightVNC Team. All Rights Reserved. + * + * This 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 software 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 software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +// -=- RfbPlayer.h + +#include + +#include + +#include +#include +#include +#include +#include + +using namespace rfb; +using namespace rfb::win32; + +class PlayerToolBar; + +class RfbPlayer : public RfbProto { + public: + RfbPlayer(char *fileName, PlayerOptions *options); + ~RfbPlayer(); + + // -=- Window Message handling + + LRESULT processMainMessage(HWND wnd, UINT msg, WPARAM wParam, LPARAM lParam); + LRESULT processFrameMessage(HWND wnd, UINT msg, WPARAM wParam, LPARAM lParam); + + // -=- Window interface + + HWND getMainHandle() const {return mainHwnd;} + HWND getFrameHandle() const {return frameHwnd;} + void disableTBandMenuItems(); + void enableTBandMenuItems(); + void setFrameSize(int width, int height); + void setVisible(bool visible); + void setTitle(const char *title); + void calculateScrollBars(); + void close(const char* reason=0); + void init(); + + // -=- Coordinate conversions + + inline Point bufferToClient(const Point& p) { + Point pos = p; + if (client_size.width() > buffer->width()) + pos.x += (client_size.width() - buffer->width()) / 2; + else if (client_size.width() < buffer->width()) + pos.x -= scrolloffset.x; + if (client_size.height() > buffer->height()) + pos.y += (client_size.height() - buffer->height()) / 2; + else if (client_size.height() < buffer->height()) + pos.y -= scrolloffset.y; + return pos; + } + inline Rect bufferToClient(const Rect& r) { + return Rect(bufferToClient(r.tl), bufferToClient(r.br)); + } + + inline Point clientToBuffer(const Point& p) { + Point pos = p; + if (client_size.width() > buffer->width()) + pos.x -= (client_size.width() - buffer->width()) / 2; + else if (client_size.width() < buffer->width()) + pos.x += scrolloffset.x; + if (client_size.height() > buffer->height()) + pos.y -= (client_size.height() - buffer->height()) / 2; + else if (client_size.height() < buffer->height()) + pos.y += scrolloffset.y; + return pos; + } + inline Rect clientToBuffer(const Rect& r) { + return Rect(clientToBuffer(r.tl), clientToBuffer(r.br)); + } + + bool setViewportOffset(const Point& tl); + + // -=- RfbProto interface overrides + + virtual void processMsg(); + virtual void serverInit(); + virtual void frameBufferUpdateEnd(); + virtual void setColourMapEntries(int first, int count, U16* rgbs); + virtual void serverCutText(const char* str, int len); + virtual void bell(); + + virtual void beginRect(const Rect& r, unsigned int encoding); + virtual void endRect(const Rect& r, unsigned int encoding); + virtual void fillRect(const Rect& r, Pixel pix); + virtual void imageRect(const Rect& r, void* pixels); + virtual void copyRect(const Rect& r, int srcX, int srcY); + + // -=- Player functions + + // calculateSessionTime() calculates the full session time in sec + long calculateSessionTime(char *fileName); + + // closeSessionFile() closes the session file and blanks the frame buffer + void closeSessionFile(); + + // openSessionFile() opens the new session file and starts play it + void openSessionFile(char *fileName); + + // skipHandshaking() - is implemented to skip the initial handshaking when + // perform backward seeking OR replaying. + void skipHandshaking(); + + void blankBuffer(); + void rewind(); + void setPaused(bool paused); + void stopPlayback(); + long getTimeOffset(); + bool isSeekMode(); + bool isSeeking(); + bool isPaused(); + long getSeekOffset(); + void setPos(long pos); + void setSpeed(double speed); + double getSpeed(); + + protected: + bool seekMode; + bool stopped; + long lastPos; + long sliderStepMs; + char fullSessionTime[20]; + int time_pos_m; + int time_pos_s; + char *fileName; + + // rfbReader is a class which used to reading the rfb data from the file + rfbSessionReader *rfbReader; + + // Returns true if part of the supplied rect is visible, false otherwise + bool invalidateBufferRect(const Rect& crect); + + bool waitWhilePaused(); + + // rewindFlag is a flag wich disable the update of the frame buffer window + // while the rewind is performing. + bool rewindFlag; + + // Local window state + HWND mainHwnd; + HWND frameHwnd; + HMENU hMenu; + Rect window_size; + Rect client_size; + Point scrolloffset; + Point maxscrolloffset; + char *cutText; + win32::DIBSectionBuffer* buffer; + PlayerToolBar tb; + + // The player's parameters + PlayerOptions options; + PixelFormatList supportedPF; + long imageDataStartTime; + long sessionTimeMs; + int currentEncoding; +}; + +// -=- sessionTerminateThread class + +// It is a special thread class, wich is allow the rfbSessionReader class +// terminate itself. + +class sessionTerminateThread : public rfb::Thread { +public: + sessionTerminateThread(RfbPlayer *_player) : player(_player) { + setDeleteAfterRun(); + } + virtual void run() { + player->closeSessionFile(); + } +protected: + RfbPlayer *player; +}; diff --git a/win/rfbplayer/rfbplayer.ico b/win/rfbplayer/rfbplayer.ico new file mode 100644 index 0000000000000000000000000000000000000000..c9136bfe01709805156468013b4b3bdbf790b805 GIT binary patch literal 766 zcmc(cO$x#=5QV4W0bIE*S-JBH9xd0%%B%DM0k7e*bXO3@Z!(5PuvAyRbSCqDLIM@( zSbcvG!+j@mWIs_ob)h=h(?ufQXGBb#7=5ENQc;Fsz|iuEs!bHjBx@TW^CQNZf8?BR zs-xWlj5>Fr;YG;VbECb8(XxiZ5#Bg@%PrTOH`?4+=)#?P;B`lpd{61h5YAqj0Kyud hF;iOnnc1r5zve+FJ5%#h0K8+3lQw$;8^8DO_7naQgMk15 literal 0 HcmV?d00001 diff --git a/win/rfbplayer/rfbplayer.rc b/win/rfbplayer/rfbplayer.rc new file mode 100644 index 00000000..169d8502 --- /dev/null +++ b/win/rfbplayer/rfbplayer.rc @@ -0,0 +1,466 @@ +//Microsoft Developer Studio generated resource script. +// +#include "resource.h" + +#define APSTUDIO_READONLY_SYMBOLS +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 2 resource. +// +#include "afxres.h" + +///////////////////////////////////////////////////////////////////////////// +#undef APSTUDIO_READONLY_SYMBOLS + +///////////////////////////////////////////////////////////////////////////// +// English (U.S.) resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) +#ifdef _WIN32 +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US +#pragma code_page(1252) +#endif //_WIN32 + +#ifdef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// TEXTINCLUDE +// + +1 TEXTINCLUDE DISCARDABLE +BEGIN + "resource.h\0" +END + +2 TEXTINCLUDE DISCARDABLE +BEGIN + "#include ""afxres.h""\r\n" + "\0" +END + +3 TEXTINCLUDE DISCARDABLE +BEGIN + "\r\n" + "\0" +END + +#endif // APSTUDIO_INVOKED + +#endif // English (U.S.) resources +///////////////////////////////////////////////////////////////////////////// + + +///////////////////////////////////////////////////////////////////////////// +// English (U.K.) resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENG) +#ifdef _WIN32 +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_UK +#pragma code_page(1252) +#endif //_WIN32 + +///////////////////////////////////////////////////////////////////////////// +// +// Icon +// + +// Icon with lowest ID value placed first to ensure application icon +// remains consistent on all systems. +IDI_ICON ICON DISCARDABLE "rfbplayer.ico" + +#ifndef _MAC +///////////////////////////////////////////////////////////////////////////// +// +// Version +// + +VS_VERSION_INFO VERSIONINFO + FILEVERSION 1,0,0,1 + PRODUCTVERSION 1,0,0,1 + FILEFLAGSMASK 0x3fL +#ifdef _DEBUG + FILEFLAGS 0x1L +#else + FILEFLAGS 0x0L +#endif + FILEOS 0x40004L + FILETYPE 0x1L + FILESUBTYPE 0x0L +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "080904b0" + BEGIN + VALUE "Comments", "\0" + VALUE "CompanyName", "TightVNC Team\0" + VALUE "FileDescription", "RFB Session Player for Win32\0" + VALUE "FileVersion", "1, 0, 0, 1\0" + VALUE "InternalName", "rfbplayer\0" + VALUE "LegalCopyright", "Copyright (C) 2004-2005 TightVNC Team.\0" + VALUE "LegalTrademarks", "\0" + VALUE "OriginalFilename", "rfbplayer.exe\0" + VALUE "PrivateBuild", "\0" + VALUE "ProductName", "Rfb Session Player 1.0\0" + VALUE "ProductVersion", "1, 0, 0, 1\0" + VALUE "SpecialBuild", "\0" + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x809, 1200 + END +END + +#endif // !_MAC + + +///////////////////////////////////////////////////////////////////////////// +// +// Menu +// + +IDR_MENU MENU DISCARDABLE +BEGIN + POPUP "File" + BEGIN + MENUITEM "Open File...\tCtrl+O", ID_OPENFILE + MENUITEM "Close File...\tCtrl+Q", ID_CLOSEFILE + MENUITEM SEPARATOR + MENUITEM "Info...\tCtrl+I", ID_SESSION_INFO + MENUITEM SEPARATOR + MENUITEM "Exit\tAlt+X", ID_EXIT + END + POPUP "Play" + BEGIN + MENUITEM "Play/Pause\tSpace", ID_PLAYPAUSE + MENUITEM "Stop\tC", ID_STOP + MENUITEM "Go To...\tCtrl+G", ID_GOTO + MENUITEM SEPARATOR + MENUITEM "Loop\tCtrl+L", ID_LOOP + MENUITEM SEPARATOR + MENUITEM "Options...\tO", ID_OPTIONS + END + POPUP "Help" + BEGIN + MENUITEM "Home Page", ID_HOMEPAGE + MENUITEM "Command Line Switches", ID_HELP_COMMANDLINESWITCHES + MENUITEM SEPARATOR + MENUITEM "About RfbPlayer...", ID_ABOUT + END +END + + +///////////////////////////////////////////////////////////////////////////// +// +// Accelerator +// + +IDR_ACCELERATOR ACCELERATORS DISCARDABLE +BEGIN + "C", ID_STOP, VIRTKEY, NOINVERT + "C", ID_COPYTOCLIPBOARD, VIRTKEY, CONTROL, NOINVERT + "C", ID_FRAMEEXTRACT, VIRTKEY, ALT, NOINVERT + "G", ID_GOTO, VIRTKEY, CONTROL, NOINVERT + "I", ID_SESSION_INFO, VIRTKEY, CONTROL, NOINVERT + "L", ID_LOOP, VIRTKEY, CONTROL, NOINVERT + "O", ID_OPTIONS, VIRTKEY, NOINVERT + "O", ID_OPENFILE, VIRTKEY, CONTROL, NOINVERT + "P", ID_OPTIONS, VIRTKEY, CONTROL, NOINVERT + "Q", ID_CLOSEFILE, VIRTKEY, CONTROL, NOINVERT + VK_F5, ID_ZOOM_50, VIRTKEY, NOINVERT + VK_F6, ID_ZOOM_100, VIRTKEY, NOINVERT + VK_F7, ID_ZOOM_200, VIRTKEY, NOINVERT + VK_RETURN, ID_RETURN, VIRTKEY, NOINVERT + VK_RETURN, ID_FULLSCREEN, VIRTKEY, ALT, NOINVERT + VK_SPACE, ID_PLAYPAUSE, VIRTKEY, NOINVERT + "X", ID_EXIT, VIRTKEY, ALT, NOINVERT +END + + +///////////////////////////////////////////////////////////////////////////// +// +// Bitmap +// + +IDB_TOOLBAR BITMAP DISCARDABLE "toolbar.bmp" + +///////////////////////////////////////////////////////////////////////////// +// +// Dialog +// + +IDD_GOTO DIALOG DISCARDABLE 0, 0, 153, 54 +STYLE DS_MODALFRAME | DS_CENTER | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "RfbPlayer : Go to position" +FONT 8, "MS Sans Serif" +BEGIN + EDITTEXT IDC_GOTO_EDIT,40,9,106,14,ES_AUTOHSCROLL + DEFPUSHBUTTON "OK",IDOK,40,33,50,14 + PUSHBUTTON "Cancel",IDCANCEL,95,33,51,14 + LTEXT "Pos (ms):",IDC_STATIC,7,9,33,15,SS_CENTERIMAGE +END + +IDD_PIXELFORMAT DIALOG DISCARDABLE 0, 0, 144, 78 +STYLE DS_MODALFRAME | DS_CENTER | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "RfbPlayer : Pixel Format" +FONT 8, "MS Sans Serif" +BEGIN + COMBOBOX IDC_PIXELFORMAT,7,20,130,98,CBS_DROPDOWNLIST | + WS_VSCROLL | WS_TABSTOP + DEFPUSHBUTTON "OK",IDOK,20,57,50,14 + PUSHBUTTON "Cancel",IDCANCEL,75,57,50,14 + LTEXT "Choose the pixel format:",IDC_STATIC,7,7,130,13 + CONTROL "Big endian flag",IDC_BIG_ENDIAN,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,7,40,63,12 +END + +IDD_OPTIONS DIALOG DISCARDABLE 0, 0, 187, 180 +STYLE DS_MODALFRAME | DS_CENTER | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "Options" +FONT 8, "MS Sans Serif" +BEGIN + COMBOBOX IDC_PIXELFORMAT,15,30,157,75,CBS_DROPDOWNLIST | + WS_VSCROLL | WS_TABSTOP + CONTROL "Big endian flag",IDC_BIG_ENDIAN,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,15,52,60,10 + CONTROL "Ask the pixel format before playing",IDC_ASK_PF,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,15,67,157,10 + CONTROL "Accept the bells",IDC_ACCEPT_BELL,"Button", + BS_AUTOCHECKBOX | BS_TOP | WS_TABSTOP,15,105,157,15 + CONTROL "Accept the cut text",IDC_ACCEPT_CUT_TEXT,"Button", + BS_AUTOCHECKBOX | BS_TOP | WS_TABSTOP,15,120,157,15 + CONTROL "Start play the session when it is opened",IDC_AUTOPLAY, + "Button",BS_AUTOCHECKBOX | BS_TOP | WS_TABSTOP,15,135, + 157,9 + DEFPUSHBUTTON "OK",IDOK,20,161,50,13 + PUSHBUTTON "Cancel",IDCANCEL,75,161,50,13 + PUSHBUTTON "Default",IDC_DEFAULT,130,161,50,13 + PUSHBUTTON "Edit User PF",IDC_EDIT_UPF,110,52,62,14 + GROUPBOX "Pixel format",IDC_STATIC,7,6,173,79 + LTEXT "Forces the pixel format for the rfb session:", + IDC_STATIC,15,17,157,13 + GROUPBOX "Other",IDC_STATIC,7,90,173,65 +END + +IDD_ABOUT DIALOG DISCARDABLE 0, 0, 251, 95 +STYLE DS_MODALFRAME | DS_CENTER | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "About Rfb Session Player for Windows" +FONT 8, "MS Sans Serif" +BEGIN + DEFPUSHBUTTON "OK",IDOK,195,70,47,15 + ICON IDI_ICON,IDC_STATIC,7,10,20,20 + LTEXT ">appname<",IDC_DESCRIPTION,40,10,125,15 + LTEXT ">version<",IDC_VERSION,165,10,77,15 + LTEXT ">buildtime<",IDC_BUILDTIME,40,25,202,15 + LTEXT ">copyright<",IDC_COPYRIGHT,40,40,202,15 + LTEXT "See http://www.tightvnc.com for more information on TightVNC.", + IDC_STATIC,40,55,202,15 +END + +IDD_USERPF_LIST DIALOG DISCARDABLE 0, 0, 207, 162 +STYLE DS_MODALFRAME | DS_CENTER | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "Add / Remove the user pixel formats" +FONT 8, "MS Sans Serif" +BEGIN + LISTBOX IDC_PF_LIST,7,7,136,148,LBS_NOINTEGRALHEIGHT | + WS_VSCROLL | WS_TABSTOP + PUSHBUTTON "Add",IDC_ADD_BUTTON,150,7,50,14 + PUSHBUTTON "Remove",IDC_REMOVE_BUTTON,150,26,50,14 + PUSHBUTTON "Edit",IDC_EDIT_BUTTON,150,45,50,14 + PUSHBUTTON "Close",IDOK,150,64,50,14 +END + +IDD_UPF_EDIT DIALOG DISCARDABLE 0, 0, 204, 126 +STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "Edit the user pixel format" +FONT 8, "MS Sans Serif" +BEGIN + EDITTEXT IDC_NAME_EDIT,68,7,129,13,ES_AUTOHSCROLL + COMBOBOX IDC_BPP_COMBO,68,23,39,45,CBS_DROPDOWNLIST | WS_VSCROLL | + WS_TABSTOP + EDITTEXT IDC_DEPTH_EDIT,157,22,40,13,ES_AUTOHSCROLL | ES_NUMBER + COMBOBOX IDC_BIGENDIAN_COMBO,68,38,39,45,CBS_DROPDOWNLIST | + WS_VSCROLL | WS_TABSTOP + EDITTEXT IDC_REDMAX_EDIT,68,54,39,13,ES_AUTOHSCROLL | ES_NUMBER + EDITTEXT IDC_GREENMAX_EDIT,68,70,39,13,ES_AUTOHSCROLL | ES_NUMBER + EDITTEXT IDC_BLUEMAX_EDIT,68,86,39,13,ES_AUTOHSCROLL | ES_NUMBER + EDITTEXT IDC_REDSHIFT_EDIT,157,54,39,13,ES_AUTOHSCROLL | + ES_NUMBER + EDITTEXT IDC_GREENSHIFT_EDIT,157,70,40,13,ES_AUTOHSCROLL | + ES_NUMBER + EDITTEXT IDC_BLUESHIFT_EDIT,157,86,40,13,ES_AUTOHSCROLL | + ES_NUMBER + PUSHBUTTON "OK",IDOK,93,105,50,14 + PUSHBUTTON "Cancel",IDCANCEL,147,105,50,14 + LTEXT "Pixel format name:",IDC_STATIC,7,7,57,13,SS_CENTERIMAGE + LTEXT "Bit per pixel:",IDC_STATIC,7,23,38,12,SS_CENTERIMAGE + LTEXT "Big endian flag :",IDC_STATIC,7,38,53,13,SS_CENTERIMAGE + LTEXT "Red max :",IDC_STATIC,7,54,33,13,SS_CENTERIMAGE + LTEXT "Green max :",IDC_STATIC,7,70,38,13,SS_CENTERIMAGE + LTEXT "Blue max :",IDC_STATIC,7,86,38,13,SS_CENTERIMAGE + LTEXT "Depth:",IDC_STATIC,112,23,21,12,SS_CENTERIMAGE + LTEXT "Red shifts :",IDC_STATIC,112,54,36,13,SS_CENTERIMAGE + LTEXT "Green shifts :",IDC_STATIC,112,70,43,13,SS_CENTERIMAGE + LTEXT "Blue shifts :",IDC_STATIC,112,86,43,13,SS_CENTERIMAGE +END + +IDD_INFO DIALOG DISCARDABLE 0, 0, 295, 207 +STYLE DS_MODALFRAME | DS_CENTER | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "Information" +FONT 8, "MS Sans Serif" +BEGIN + DEFPUSHBUTTON "&OK",IDOK,122,186,50,14 + EDITTEXT IDC_INFO_EDIT,7,7,281,172,ES_MULTILINE | ES_AUTOVSCROLL | + ES_AUTOHSCROLL | ES_READONLY | ES_WANTRETURN | + WS_VSCROLL | WS_HSCROLL +END + +IDD_SESSION_INFO DIALOG DISCARDABLE 0, 0, 239, 106 +STYLE DS_MODALFRAME | DS_CENTER | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "RFB Session Info" +FONT 8, "MS Sans Serif" +BEGIN + DEFPUSHBUTTON "OK",IDOK,182,85,50,14 + LTEXT "Desktop Name:",IDC_STATIC,7,10,73,15 + LTEXT "Desktop Size:",IDC_STATIC,7,25,73,15 + LTEXT "Pixel Format:",IDC_STATIC,7,40,73,15 + LTEXT "Current Encoding:",IDC_STATIC,7,55,73,15 + LTEXT "RFB Protocol Version:",IDC_STATIC,7,70,73,15 + LTEXT "",IDC_DESKTOP_NAME,80,10,152,15 + LTEXT "",IDC_DESKTOP_SIZE,80,25,152,15 + LTEXT "",IDC_CURRENT_ENCODING,80,55,152,15 + LTEXT "",IDC_VERSION,80,70,152,15 + LTEXT "",IDC_PIXEL_FORMAT,80,40,152,15 +END + + +///////////////////////////////////////////////////////////////////////////// +// +// DESIGNINFO +// + +#ifdef APSTUDIO_INVOKED +GUIDELINES DESIGNINFO DISCARDABLE +BEGIN + IDD_GOTO, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 146 + VERTGUIDE, 40 + VERTGUIDE, 90 + VERTGUIDE, 95 + TOPMARGIN, 9 + BOTTOMMARGIN, 47 + HORZGUIDE, 9 + HORZGUIDE, 24 + HORZGUIDE, 35 + END + + IDD_PIXELFORMAT, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 137 + VERTGUIDE, 20 + VERTGUIDE, 70 + VERTGUIDE, 75 + VERTGUIDE, 125 + TOPMARGIN, 7 + BOTTOMMARGIN, 71 + HORZGUIDE, 7 + HORZGUIDE, 20 + HORZGUIDE, 35 + HORZGUIDE, 40 + HORZGUIDE, 49 + HORZGUIDE, 57 + END + + IDD_OPTIONS, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 180 + VERTGUIDE, 15 + VERTGUIDE, 20 + VERTGUIDE, 70 + VERTGUIDE, 75 + VERTGUIDE, 125 + VERTGUIDE, 130 + VERTGUIDE, 172 + TOPMARGIN, 6 + BOTTOMMARGIN, 174 + HORZGUIDE, 17 + HORZGUIDE, 30 + HORZGUIDE, 42 + HORZGUIDE, 52 + HORZGUIDE, 67 + HORZGUIDE, 85 + HORZGUIDE, 90 + HORZGUIDE, 105 + HORZGUIDE, 120 + HORZGUIDE, 135 + HORZGUIDE, 144 + HORZGUIDE, 150 + HORZGUIDE, 155 + HORZGUIDE, 161 + END + + IDD_USERPF_LIST, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 200 + TOPMARGIN, 7 + BOTTOMMARGIN, 155 + HORZGUIDE, 21 + HORZGUIDE, 26 + HORZGUIDE, 40 + HORZGUIDE, 45 + HORZGUIDE, 59 + HORZGUIDE, 64 + END + + IDD_UPF_EDIT, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 197 + TOPMARGIN, 7 + BOTTOMMARGIN, 119 + END + + IDD_INFO, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 288 + TOPMARGIN, 7 + BOTTOMMARGIN, 200 + END + + IDD_SESSION_INFO, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 232 + TOPMARGIN, 7 + BOTTOMMARGIN, 99 + HORZGUIDE, 10 + HORZGUIDE, 25 + HORZGUIDE, 40 + HORZGUIDE, 55 + HORZGUIDE, 70 + HORZGUIDE, 85 + END +END +#endif // APSTUDIO_INVOKED + +#endif // English (U.K.) resources +///////////////////////////////////////////////////////////////////////////// + + + +#ifndef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 3 resource. +// + + +///////////////////////////////////////////////////////////////////////////// +#endif // not APSTUDIO_INVOKED + diff --git a/win/rfbplayer/toolbar.bmp b/win/rfbplayer/toolbar.bmp new file mode 100644 index 0000000000000000000000000000000000000000..9347a73879737c845d45453340fa3c9be6d195d6 GIT binary patch literal 630 zcmbtQOAZ4e2z_mu%(lnqF`Djs7gyufqj@v?9^%9yCT&s_1Rlue_1>>A+9@C8C;3jk z;6_Puyc@GD053}E4<%L9TKNiScF~$NGLKvo`Cr=BA0!})Gu8n~e#1uqwxn*D1iB{N z10p{Dw{Ru~oHjkG;Q?pd3aNF&RXF0_`7il4;yFJ}_4@__e(dMl{9}JH-}g6Q%`(xk I|H-#fKPmsSWB>pF literal 0 HcmV?d00001 diff --git a/win/vnc.dsw b/win/vnc.dsw new file mode 100644 index 00000000..686cc692 --- /dev/null +++ b/win/vnc.dsw @@ -0,0 +1,248 @@ +Microsoft Developer Studio Workspace File, Format Version 6.00 +# WARNING: DO NOT EDIT OR DELETE THIS WORKSPACE FILE! + +############################################################################### + +Project: "Xregion"=..\common\Xregion\Xregion.dsp - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ +}}} + +############################################################################### + +Project: "logmessages"=.\logmessages\logmessages.dsp - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ +}}} + +############################################################################### + +Project: "network"=..\common\network\network.dsp - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ +}}} + +############################################################################### + +Project: "rdr"=..\common\rdr\rdr.dsp - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ +}}} + +############################################################################### + +Project: "rfb"=..\common\rfb\rfb.dsp - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ + Begin Project Dependency + Project_Dep_Name rdr + End Project Dependency + Begin Project Dependency + Project_Dep_Name Xregion + End Project Dependency +}}} + +############################################################################### + +Project: "rfb_win32"=.\rfb_win32\rfb_win32.dsp - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ + Begin Project Dependency + Project_Dep_Name rfb + End Project Dependency +}}} + +############################################################################### + +Project: "rfbplayer"=.\rfbplayer\rfbplayer.dsp - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ + Begin Project Dependency + Project_Dep_Name rdr + End Project Dependency + Begin Project Dependency + Project_Dep_Name rfb + End Project Dependency + Begin Project Dependency + Project_Dep_Name rfb_win32 + End Project Dependency + Begin Project Dependency + Project_Dep_Name Xregion + End Project Dependency + Begin Project Dependency + Project_Dep_Name zlib + End Project Dependency + Begin Project Dependency + Project_Dep_Name jpeg + End Project Dependency +}}} + +############################################################################### + +Project: "vncconfig"=.\vncconfig\vncconfig.dsp - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ + Begin Project Dependency + Project_Dep_Name rfb + End Project Dependency + Begin Project Dependency + Project_Dep_Name rfb_win32 + End Project Dependency + Begin Project Dependency + Project_Dep_Name network + End Project Dependency + Begin Project Dependency + Project_Dep_Name Xregion + End Project Dependency +}}} + +############################################################################### + +Project: "vncviewer"=.\vncviewer\vncviewer.dsp - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ + Begin Project Dependency + Project_Dep_Name rfb + End Project Dependency + Begin Project Dependency + Project_Dep_Name zlib + End Project Dependency + Begin Project Dependency + Project_Dep_Name jpeg + End Project Dependency + Begin Project Dependency + Project_Dep_Name rfb_win32 + End Project Dependency + Begin Project Dependency + Project_Dep_Name network + End Project Dependency + Begin Project Dependency + Project_Dep_Name rdr + End Project Dependency + Begin Project Dependency + Project_Dep_Name Xregion + End Project Dependency +}}} + +############################################################################### + +Project: "winvnc"=.\winvnc\winvnc.dsp - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ + Begin Project Dependency + Project_Dep_Name network + End Project Dependency + Begin Project Dependency + Project_Dep_Name rfb_win32 + End Project Dependency + Begin Project Dependency + Project_Dep_Name zlib + End Project Dependency + Begin Project Dependency + Project_Dep_Name jpeg + End Project Dependency + Begin Project Dependency + Project_Dep_Name Xregion + End Project Dependency +}}} + +############################################################################### + +Project: "wm_hooks"=.\wm_hooks\wm_hooks.dsp - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ +}}} + +############################################################################### + +Project: "zlib"=..\common\zlib\zlib.dsp - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ +}}} + +############################################################################### + +Project: "jpeg"=..\common\jpeg\jpeg.dsp - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ +}}} + +############################################################################### + +Global: + +Package=<5> +{{{ +}}} + +Package=<3> +{{{ +}}} + +############################################################################### + diff --git a/win/vncconfig/Authentication.h b/win/vncconfig/Authentication.h new file mode 100644 index 00000000..f4b38f8c --- /dev/null +++ b/win/vncconfig/Authentication.h @@ -0,0 +1,142 @@ +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * + * This 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 software 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 software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ +#ifndef WINVNCCONF_AUTHENTICATION +#define WINVNCCONF_AUTHENTICATION + +#include +#include +#include +#include +#include +#include +#include +#include + +static rfb::BoolParameter queryOnlyIfLoggedOn("QueryOnlyIfLoggedOn", + "Only prompt for a local user to accept incoming connections if there is a user logged on", false); + +namespace rfb { + + namespace win32 { + + class AuthenticationPage : public PropSheetPage { + public: + AuthenticationPage(const RegKey& rk) + : PropSheetPage(GetModuleHandle(0), MAKEINTRESOURCE(IDD_AUTHENTICATION)), regKey(rk) {} + void initDialog() { + CharArray sec_types_str(SSecurityFactoryStandard::sec_types.getData()); + std::list sec_types = parseSecTypes(sec_types_str.buf); + + useNone = useVNC = false; + std::list::iterator i; + for (i=sec_types.begin(); i!=sec_types.end(); i++) { + if ((*i) == secTypeNone) useNone = true; + else if ((*i) == secTypeVncAuth) useVNC = true; + } + + HWND security = GetDlgItem(handle, IDC_ENCRYPTION); + SendMessage(security, CB_ADDSTRING, 0, (LPARAM)_T("Always Off")); + SendMessage(security, CB_SETCURSEL, 0, 0); + enableItem(IDC_AUTH_NT, false); enableItem(IDC_AUTH_NT_CONF, false); + enableItem(IDC_ENCRYPTION, false); enableItem(IDC_AUTH_RA2_CONF, false); + + setItemChecked(IDC_AUTH_NONE, useNone); + setItemChecked(IDC_AUTH_VNC, useVNC); + setItemChecked(IDC_QUERY_CONNECT, rfb::Server::queryConnect); + setItemChecked(IDC_QUERY_LOGGED_ON, queryOnlyIfLoggedOn); + onCommand(IDC_AUTH_NONE, 0); + } + bool onCommand(int id, int cmd) { + switch (id) { + case IDC_AUTH_VNC_PASSWD: + { + PasswordDialog passwdDlg(regKey, registryInsecure); + passwdDlg.showDialog(handle); + } + return true; + case IDC_AUTH_NONE: + case IDC_AUTH_VNC: + enableItem(IDC_AUTH_VNC_PASSWD, isItemChecked(IDC_AUTH_VNC)); + case IDC_QUERY_CONNECT: + case IDC_QUERY_LOGGED_ON: + setChanged((useNone != isItemChecked(IDC_AUTH_NONE)) || + (useVNC != isItemChecked(IDC_AUTH_VNC)) || + (rfb::Server::queryConnect != isItemChecked(IDC_QUERY_CONNECT)) || + (queryOnlyIfLoggedOn != isItemChecked(IDC_QUERY_LOGGED_ON))); + enableItem(IDC_QUERY_LOGGED_ON, enableQueryOnlyIfLoggedOn()); + return false; + }; + return false; + } + bool onOk() { + bool useVncChanged = useVNC != isItemChecked(IDC_AUTH_VNC); + useVNC = isItemChecked(IDC_AUTH_VNC); + useNone = isItemChecked(IDC_AUTH_NONE); + if (useVNC) { + verifyVncPassword(regKey); + regKey.setString(_T("SecurityTypes"), _T("VncAuth")); + } else { + if (haveVncPassword() && useVncChanged && + MsgBox(0, _T("The VNC authentication method is disabled, but a password is still stored for it.\n") + _T("Do you want to remove the VNC authentication password from the registry?"), + MB_ICONWARNING | MB_YESNO) == IDYES) { + regKey.setBinary(_T("Password"), 0, 0); + } + regKey.setString(_T("SecurityTypes"), _T("None")); + } + regKey.setString(_T("ReverseSecurityTypes"), _T("None")); + regKey.setBool(_T("QueryConnect"), isItemChecked(IDC_QUERY_CONNECT)); + regKey.setBool(_T("QueryOnlyIfLoggedOn"), isItemChecked(IDC_QUERY_LOGGED_ON)); + return true; + } + void setWarnPasswdInsecure(bool warn) { + registryInsecure = warn; + } + bool enableQueryOnlyIfLoggedOn() { + return isItemChecked(IDC_QUERY_CONNECT) && osVersion.isPlatformNT && (osVersion.dwMajorVersion >= 5); + } + + + static bool haveVncPassword() { + PlainPasswd password(SSecurityFactoryStandard::vncAuthPasswd.getVncAuthPasswd()); + return password.buf && strlen(password.buf) != 0; + } + + static void verifyVncPassword(const RegKey& regKey) { + if (!haveVncPassword()) { + MsgBox(0, _T("The VNC authentication method is enabled, but no password is specified.\n") + _T("The password dialog will now be shown."), MB_ICONINFORMATION | MB_OK); + PasswordDialog passwd(regKey, registryInsecure); + passwd.showDialog(); + } + } + + protected: + RegKey regKey; + static bool registryInsecure; + bool useNone; + bool useVNC; + }; + + }; + + bool AuthenticationPage::registryInsecure = false; + +}; + +#endif diff --git a/win/vncconfig/Connections.h b/win/vncconfig/Connections.h new file mode 100644 index 00000000..7512cc65 --- /dev/null +++ b/win/vncconfig/Connections.h @@ -0,0 +1,298 @@ +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * + * This 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 software 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 software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ +#ifndef WINVNCCONF_CONNECTIONS +#define WINVNCCONF_CONNECTIONS + +#include + +#include +#include +#include +#include +#include +#include + +static rfb::IntParameter http_port("HTTPPortNumber", + "TCP/IP port on which the server will serve the Java applet VNC Viewer ", 5800); +static rfb::IntParameter port_number("PortNumber", + "TCP/IP port on which the server will accept connections", 5900); +static rfb::StringParameter hosts("Hosts", + "Filter describing which hosts are allowed access to this server", "+"); +static rfb::BoolParameter localHost("LocalHost", + "Only accept connections from via the local loop-back network interface", false); + +namespace rfb { + + namespace win32 { + + class ConnHostDialog : public Dialog { + public: + ConnHostDialog() : Dialog(GetModuleHandle(0)) {} + bool showDialog(const TCHAR* pat) { + pattern.replaceBuf(tstrDup(pat)); + return Dialog::showDialog(MAKEINTRESOURCE(IDD_CONN_HOST)); + } + void initDialog() { + if (_tcslen(pattern.buf) == 0) + pattern.replaceBuf(tstrDup("+")); + + if (pattern.buf[0] == _T('+')) + setItemChecked(IDC_ALLOW, true); + else if (pattern.buf[0] == _T('?')) + setItemChecked(IDC_QUERY, true); + else + setItemChecked(IDC_DENY, true); + + setItemString(IDC_HOST_PATTERN, &pattern.buf[1]); + pattern.replaceBuf(0); + } + bool onOk() { + TCharArray host = getItemString(IDC_HOST_PATTERN); + TCharArray newPat(_tcslen(host.buf)+2); + if (isItemChecked(IDC_ALLOW)) + newPat.buf[0] = _T('+'); + else if (isItemChecked(IDC_QUERY)) + newPat.buf[0] = _T('?'); + else + newPat.buf[0] = _T('-'); + newPat.buf[1] = 0; + _tcscat(newPat.buf, host.buf); + + network::TcpFilter::Pattern pat(network::TcpFilter::parsePattern(CStr(newPat.buf))); + pattern.replaceBuf(TCharArray(network::TcpFilter::patternToStr(pat)).takeBuf()); + return true; + } + const TCHAR* getPattern() {return pattern.buf;} + protected: + TCharArray pattern; + }; + + class ConnectionsPage : public PropSheetPage { + public: + ConnectionsPage(const RegKey& rk) + : PropSheetPage(GetModuleHandle(0), MAKEINTRESOURCE(IDD_CONNECTIONS)), regKey(rk) {} + void initDialog() { + vlog.debug("set IDC_PORT %d", (int)port_number); + setItemInt(IDC_PORT, port_number ? port_number : 5900); + setItemChecked(IDC_RFB_ENABLE, port_number != 0); + setItemInt(IDC_IDLE_TIMEOUT, rfb::Server::idleTimeout); + vlog.debug("set IDC_HTTP_PORT %d", (int)http_port); + setItemInt(IDC_HTTP_PORT, http_port ? http_port : 5800); + setItemChecked(IDC_HTTP_ENABLE, http_port != 0); + enableItem(IDC_HTTP_PORT, http_port != 0); + setItemChecked(IDC_LOCALHOST, localHost); + + HWND listBox = GetDlgItem(handle, IDC_HOSTS); + while (SendMessage(listBox, LB_GETCOUNT, 0, 0)) + SendMessage(listBox, LB_DELETESTRING, 0, 0); + + CharArray tmp; + tmp.buf = hosts.getData(); + while (tmp.buf) { + CharArray first; + strSplit(tmp.buf, ',', &first.buf, &tmp.buf); + if (strlen(first.buf)) + SendMessage(listBox, LB_ADDSTRING, 0, (LPARAM)(const TCHAR*)TStr(first.buf)); + } + + onCommand(IDC_RFB_ENABLE, EN_CHANGE); + } + bool onCommand(int id, int cmd) { + switch (id) { + case IDC_HOSTS: + { + DWORD selected = SendMessage(GetDlgItem(handle, IDC_HOSTS), LB_GETCURSEL, 0, 0); + int count = SendMessage(GetDlgItem(handle, IDC_HOSTS), LB_GETCOUNT, 0, 0); + bool enable = selected != LB_ERR; + enableItem(IDC_HOST_REMOVE, enable); + enableItem(IDC_HOST_UP, enable && (selected > 0)); + enableItem(IDC_HOST_DOWN, enable && (selected < count-1)); + enableItem(IDC_HOST_EDIT, enable); + setChanged(isChanged()); + } + return true; + + case IDC_PORT: + if (cmd == EN_CHANGE) { + try { + setItemInt(IDC_HTTP_PORT, rfbPortToHTTP(getItemInt(IDC_PORT))); + } catch (...) { + } + } + case IDC_HTTP_PORT: + case IDC_IDLE_TIMEOUT: + if (cmd == EN_CHANGE) + setChanged(isChanged()); + return false; + + case IDC_HTTP_ENABLE: + case IDC_RFB_ENABLE: + case IDC_LOCALHOST: + { + // HTTP port + enableItem(IDC_HTTP_PORT, isItemChecked(IDC_HTTP_ENABLE) && isItemChecked(IDC_RFB_ENABLE)); + enableItem(IDC_HTTP_ENABLE, isItemChecked(IDC_RFB_ENABLE)); + + // RFB port + enableItem(IDC_PORT, isItemChecked(IDC_RFB_ENABLE)); + + // Hosts + enableItem(IDC_LOCALHOST, isItemChecked(IDC_RFB_ENABLE)); + + bool enableHosts = !isItemChecked(IDC_LOCALHOST) && isItemChecked(IDC_RFB_ENABLE); + enableItem(IDC_HOSTS, enableHosts); + enableItem(IDC_HOST_ADD, enableHosts); + if (!enableHosts) { + enableItem(IDC_HOST_REMOVE, enableHosts); + enableItem(IDC_HOST_UP, enableHosts); + enableItem(IDC_HOST_DOWN, enableHosts); + enableItem(IDC_HOST_EDIT, enableHosts); + } else { + onCommand(IDC_HOSTS, EN_CHANGE); + } + setChanged(isChanged()); + return false; + } + + case IDC_HOST_ADD: + if (hostDialog.showDialog(_T(""))) + { + const TCHAR* pattern = hostDialog.getPattern(); + if (pattern) + SendMessage(GetDlgItem(handle, IDC_HOSTS), LB_ADDSTRING, 0, (LPARAM)pattern); + } + return true; + + case IDC_HOST_EDIT: + { + HWND listBox = GetDlgItem(handle, IDC_HOSTS); + int item = SendMessage(listBox, LB_GETCURSEL, 0, 0); + TCharArray pattern(SendMessage(listBox, LB_GETTEXTLEN, item, 0)+1); + SendMessage(listBox, LB_GETTEXT, item, (LPARAM)pattern.buf); + + if (hostDialog.showDialog(pattern.buf)) { + const TCHAR* newPat = hostDialog.getPattern(); + if (newPat) { + item = SendMessage(listBox, LB_FINDSTRINGEXACT, item, (LPARAM)pattern.buf); + if (item != LB_ERR) { + SendMessage(listBox, LB_DELETESTRING, item, 0); + SendMessage(listBox, LB_INSERTSTRING, item, (LPARAM)newPat); + SendMessage(listBox, LB_SETCURSEL, item, 0); + onCommand(IDC_HOSTS, EN_CHANGE); + } + } + } + } + return true; + + case IDC_HOST_UP: + { + HWND listBox = GetDlgItem(handle, IDC_HOSTS); + int item = SendMessage(listBox, LB_GETCURSEL, 0, 0); + TCharArray pattern(SendMessage(listBox, LB_GETTEXTLEN, item, 0)+1); + SendMessage(listBox, LB_GETTEXT, item, (LPARAM)pattern.buf); + SendMessage(listBox, LB_DELETESTRING, item, 0); + SendMessage(listBox, LB_INSERTSTRING, item-1, (LPARAM)pattern.buf); + SendMessage(listBox, LB_SETCURSEL, item-1, 0); + onCommand(IDC_HOSTS, EN_CHANGE); + } + return true; + + case IDC_HOST_DOWN: + { + HWND listBox = GetDlgItem(handle, IDC_HOSTS); + int item = SendMessage(listBox, LB_GETCURSEL, 0, 0); + TCharArray pattern(SendMessage(listBox, LB_GETTEXTLEN, item, 0)+1); + SendMessage(listBox, LB_GETTEXT, item, (LPARAM)pattern.buf); + SendMessage(listBox, LB_DELETESTRING, item, 0); + SendMessage(listBox, LB_INSERTSTRING, item+1, (LPARAM)pattern.buf); + SendMessage(listBox, LB_SETCURSEL, item+1, 0); + onCommand(IDC_HOSTS, EN_CHANGE); + } + return true; + + case IDC_HOST_REMOVE: + { + HWND listBox = GetDlgItem(handle, IDC_HOSTS); + int item = SendMessage(listBox, LB_GETCURSEL, 0, 0); + SendMessage(listBox, LB_DELETESTRING, item, 0); + onCommand(IDC_HOSTS, EN_CHANGE); + } + + } + return false; + } + bool onOk() { + regKey.setInt(_T("PortNumber"), isItemChecked(IDC_RFB_ENABLE) ? getItemInt(IDC_PORT) : 0); + regKey.setInt(_T("IdleTimeout"), getItemInt(IDC_IDLE_TIMEOUT)); + regKey.setInt(_T("HTTPPortNumber"), isItemChecked(IDC_HTTP_ENABLE) && isItemChecked(IDC_RFB_ENABLE) + ? getItemInt(IDC_HTTP_PORT) : 0); + regKey.setInt(_T("LocalHost"), isItemChecked(IDC_LOCALHOST)); + regKey.setString(_T("Hosts"), TCharArray(getHosts()).buf); + return true; + } + bool isChanged() { + try { + CharArray new_hosts = getHosts(); + CharArray old_hosts = hosts.getData(); + return (strcmp(new_hosts.buf, old_hosts.buf) != 0) || + (localHost != isItemChecked(IDC_LOCALHOST)) || + (port_number != getItemInt(IDC_PORT)) || + (http_port != getItemInt(IDC_HTTP_PORT)) || + ((http_port!=0) != (isItemChecked(IDC_HTTP_ENABLE)!=0)) || + (rfb::Server::idleTimeout != getItemInt(IDC_IDLE_TIMEOUT)); + } catch (rdr::Exception) { + return false; + } + } + char* getHosts() { + int bufLen = 1, i; + HWND listBox = GetDlgItem(handle, IDC_HOSTS); + for (i=0; i +#include +#include +#include + +namespace rfb { + + namespace win32 { + + class DesktopPage : public PropSheetPage { + public: + DesktopPage(const RegKey& rk) + : PropSheetPage(GetModuleHandle(0), MAKEINTRESOURCE(IDD_DESKTOP)), regKey(rk) {} + void initDialog() { + CharArray action = rfb::win32::SDisplay::disconnectAction.getData(); + bool disconnectLock = stricmp(action.buf, "Lock") == 0; + bool disconnectLogoff = stricmp(action.buf, "Logoff") == 0; + typedef BOOL (WINAPI *_LockWorkStation_proto)(); + DynamicFn<_LockWorkStation_proto> _LockWorkStation(_T("user32.dll"), "LockWorkStation"); + if (!_LockWorkStation.isValid()) { + enableItem(IDC_DISCONNECT_LOCK, false); + if (disconnectLock) { + disconnectLogoff = true; + disconnectLock = false; + } + } + setItemChecked(IDC_DISCONNECT_LOGOFF, disconnectLogoff); + setItemChecked(IDC_DISCONNECT_LOCK, disconnectLock); + setItemChecked(IDC_DISCONNECT_NONE, !disconnectLock && !disconnectLogoff); + setItemChecked(IDC_REMOVE_WALLPAPER, rfb::win32::SDisplay::removeWallpaper); + setItemChecked(IDC_REMOVE_PATTERN, rfb::win32::SDisplay::removePattern); + setItemChecked(IDC_DISABLE_EFFECTS, rfb::win32::SDisplay::disableEffects); + } + bool onCommand(int id, int cmd) { + switch (id) { + case IDC_DISCONNECT_LOGOFF: + case IDC_DISCONNECT_LOCK: + case IDC_DISCONNECT_NONE: + case IDC_REMOVE_WALLPAPER: + case IDC_REMOVE_PATTERN: + case IDC_DISABLE_EFFECTS: + CharArray action = rfb::win32::SDisplay::disconnectAction.getData(); + bool disconnectLock = stricmp(action.buf, "Lock") == 0; + bool disconnectLogoff = stricmp(action.buf, "Logoff") == 0; + setChanged((disconnectLogoff != isItemChecked(IDC_DISCONNECT_LOGOFF)) || + (disconnectLock != isItemChecked(IDC_DISCONNECT_LOCK)) || + (isItemChecked(IDC_REMOVE_WALLPAPER) != rfb::win32::SDisplay::removeWallpaper) || + (isItemChecked(IDC_REMOVE_PATTERN) != rfb::win32::SDisplay::removePattern) || + (isItemChecked(IDC_DISABLE_EFFECTS) != rfb::win32::SDisplay::disableEffects)); + break; + } + return false; + } + bool onOk() { + const TCHAR* action = _T("None"); + if (isItemChecked(IDC_DISCONNECT_LOGOFF)) + action = _T("Logoff"); + else if (isItemChecked(IDC_DISCONNECT_LOCK)) + action = _T("Lock"); + regKey.setString(_T("DisconnectAction"), action); + regKey.setBool(_T("RemoveWallpaper"), isItemChecked(IDC_REMOVE_WALLPAPER)); + regKey.setBool(_T("RemovePattern"), isItemChecked(IDC_REMOVE_PATTERN)); + regKey.setBool(_T("DisableEffects"), isItemChecked(IDC_DISABLE_EFFECTS)); + return true; + } + protected: + RegKey regKey; + }; + + }; + +}; + +#endif diff --git a/win/vncconfig/Hooking.h b/win/vncconfig/Hooking.h new file mode 100644 index 00000000..9be82f3a --- /dev/null +++ b/win/vncconfig/Hooking.h @@ -0,0 +1,88 @@ +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * + * This 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 software 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 software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ +#ifndef WINVNCCONF_HOOKING +#define WINVNCCONF_HOOKING + +#include +#include +#include +#include +#include + +namespace rfb { + + namespace win32 { + + class HookingPage : public PropSheetPage { + public: + HookingPage(const RegKey& rk) + : PropSheetPage(GetModuleHandle(0), MAKEINTRESOURCE(IDD_HOOKING)), regKey(rk) {} + void initDialog() { + setItemChecked(IDC_USEPOLLING, rfb::win32::SDisplay::updateMethod == 0); + setItemChecked(IDC_USEHOOKS, (rfb::win32::SDisplay::updateMethod == 1) && + rfb::win32::SDisplay::areHooksAvailable()); + enableItem(IDC_USEHOOKS, rfb::win32::SDisplay::areHooksAvailable()); + setItemChecked(IDC_USEDRIVER, (rfb::win32::SDisplay::updateMethod == 2) && + rfb::win32::SDisplay::isDriverAvailable()); + enableItem(IDC_USEDRIVER, rfb::win32::SDisplay::isDriverAvailable()); + setItemChecked(IDC_POLLCONSOLES, rfb::win32::WMPoller::poll_console_windows); + setItemChecked(IDC_CAPTUREBLT, osVersion.isPlatformNT && + rfb::win32::DeviceFrameBuffer::useCaptureBlt); + enableItem(IDC_CAPTUREBLT, osVersion.isPlatformNT); + onCommand(IDC_USEHOOKS, 0); + } + bool onCommand(int id, int cmd) { + switch (id) { + case IDC_USEPOLLING: + case IDC_USEHOOKS: + case IDC_USEDRIVER: + case IDC_POLLCONSOLES: + case IDC_CAPTUREBLT: + setChanged(((rfb::win32::SDisplay::updateMethod == 0) != isItemChecked(IDC_USEPOLLING)) || + ((rfb::win32::SDisplay::updateMethod == 1) != isItemChecked(IDC_USEHOOKS)) || + ((rfb::win32::SDisplay::updateMethod == 2) != isItemChecked(IDC_USEDRIVER)) || + (rfb::win32::WMPoller::poll_console_windows != isItemChecked(IDC_POLLCONSOLES)) || + (rfb::win32::DeviceFrameBuffer::useCaptureBlt != isItemChecked(IDC_CAPTUREBLT))); + enableItem(IDC_POLLCONSOLES, isItemChecked(IDC_USEHOOKS)); + break; + } + return false; + } + bool onOk() { + if (isItemChecked(IDC_USEPOLLING)) + regKey.setInt(_T("UpdateMethod"), 0); + if (isItemChecked(IDC_USEHOOKS)) + regKey.setInt(_T("UpdateMethod"), 1); + if (isItemChecked(IDC_USEDRIVER)) + regKey.setInt(_T("UpdateMethod"), 2); + regKey.setBool(_T("PollConsoleWindows"), isItemChecked(IDC_POLLCONSOLES)); + regKey.setBool(_T("UseCaptureBlt"), isItemChecked(IDC_CAPTUREBLT)); + + // *** LEGACY compatibility *** + regKey.setBool(_T("UseHooks"), isItemChecked(IDC_USEHOOKS)); + return true; + } + protected: + RegKey regKey; + }; + + }; + +}; + +#endif diff --git a/win/vncconfig/Inputs.h b/win/vncconfig/Inputs.h new file mode 100644 index 00000000..1e0b4bac --- /dev/null +++ b/win/vncconfig/Inputs.h @@ -0,0 +1,84 @@ +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * + * This 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 software 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 software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ +#ifndef WINVNCCONF_INPUTS +#define WINVNCCONF_INPUTS + +#ifndef SPI_GETBLOCKSENDINPUTRESETS +#define SPI_GETBLOCKSENDINPUTRESETS 0x1026 +#define SPI_SETBLOCKSENDINPUTRESETS 0x1027 +#endif + +#include +#include +#include +#include + +namespace rfb { + namespace win32 { + + class InputsPage : public PropSheetPage { + public: + InputsPage(const RegKey& rk) + : PropSheetPage(GetModuleHandle(0), MAKEINTRESOURCE(IDD_INPUTS)), + regKey(rk), enableAffectSSaver(true) {} + void initDialog() { + setItemChecked(IDC_ACCEPT_KEYS, rfb::Server::acceptKeyEvents); + setItemChecked(IDC_ACCEPT_PTR, rfb::Server::acceptPointerEvents); + setItemChecked(IDC_ACCEPT_CUTTEXT, rfb::Server::acceptCutText); + setItemChecked(IDC_SEND_CUTTEXT, rfb::Server::sendCutText); + setItemChecked(IDC_DISABLE_LOCAL_INPUTS, SDisplay::disableLocalInputs); + enableItem(IDC_DISABLE_LOCAL_INPUTS, !osVersion.isPlatformWindows); + BOOL blocked = FALSE; + if (SystemParametersInfo(SPI_GETBLOCKSENDINPUTRESETS, 0, &blocked, 0)) + setItemChecked(IDC_AFFECT_SCREENSAVER, !blocked); + else + enableAffectSSaver = false; + enableItem(IDC_AFFECT_SCREENSAVER, enableAffectSSaver); + } + bool onCommand(int id, int cmd) { + BOOL inputResetsBlocked; + SystemParametersInfo(SPI_GETBLOCKSENDINPUTRESETS, 0, &inputResetsBlocked, 0); + setChanged((rfb::Server::acceptKeyEvents != isItemChecked(IDC_ACCEPT_KEYS)) || + (rfb::Server::acceptPointerEvents != isItemChecked(IDC_ACCEPT_PTR)) || + (rfb::Server::acceptCutText != isItemChecked(IDC_ACCEPT_CUTTEXT)) || + (rfb::Server::sendCutText != isItemChecked(IDC_SEND_CUTTEXT)) || + (SDisplay::disableLocalInputs != isItemChecked(IDC_DISABLE_LOCAL_INPUTS)) || + (enableAffectSSaver && (!inputResetsBlocked != isItemChecked(IDC_AFFECT_SCREENSAVER)))); + return false; + } + bool onOk() { + regKey.setBool(_T("AcceptKeyEvents"), isItemChecked(IDC_ACCEPT_KEYS)); + regKey.setBool(_T("AcceptPointerEvents"), isItemChecked(IDC_ACCEPT_PTR)); + regKey.setBool(_T("AcceptCutText"), isItemChecked(IDC_ACCEPT_CUTTEXT)); + regKey.setBool(_T("SendCutText"), isItemChecked(IDC_SEND_CUTTEXT)); + regKey.setBool(_T("DisableLocalInputs"), isItemChecked(IDC_DISABLE_LOCAL_INPUTS)); + if (enableAffectSSaver) { + BOOL blocked = !isItemChecked(IDC_AFFECT_SCREENSAVER); + SystemParametersInfo(SPI_SETBLOCKSENDINPUTRESETS, blocked, 0, SPIF_UPDATEINIFILE | SPIF_SENDCHANGE); + } + return true; + } + protected: + RegKey regKey; + bool enableAffectSSaver; + }; + + }; +}; + +#endif diff --git a/win/vncconfig/Legacy.cxx b/win/vncconfig/Legacy.cxx new file mode 100644 index 00000000..ae5d7166 --- /dev/null +++ b/win/vncconfig/Legacy.cxx @@ -0,0 +1,248 @@ +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * + * This 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 software 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 software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +#include + +#include +#include +#include + +using namespace rfb; +using namespace win32; + +static LogWriter vlog("Legacy"); + + +void LegacyPage::LoadPrefs() + { + // VNC 3.3.3R3 Preferences Algorithm, as described by vncProperties.cpp + // - Load user-specific settings, based on logged-on user name, + // from HKLM/Software/ORL/WinVNC3/. If they don't exist, + // try again with username "Default". + // - Load system-wide settings from HKLM/Software/ORL/WinVNC3. + // - If AllowProperties is non-zero then load the user's own + // settings from HKCU/Software/ORL/WinVNC3. + + // Get the name of the current user + TCharArray username; + try { + UserName name; + username.buf = name.takeBuf(); + } catch (rdr::SystemException& e) { + if (e.err != ERROR_NOT_LOGGED_ON) + throw; + } + + // Open and read the WinVNC3 registry key + allowProperties = true; + RegKey winvnc3; + try { + winvnc3.openKey(HKEY_LOCAL_MACHINE, _T("Software\\ORL\\WinVNC3")); + int debugMode = winvnc3.getInt(_T("DebugMode"), 0); + const char* debugTarget = 0; + if (debugMode & 2) debugTarget = "file"; + if (debugMode & 4) debugTarget = "stderr"; + if (debugTarget) { + char logSetting[32]; + sprintf(logSetting, "*:%s:%d", debugTarget, winvnc3.getInt(_T("DebugLevel"), 0)); + regKey.setString(_T("Log"), TStr(logSetting)); + } + + TCharArray authHosts; + authHosts.buf = winvnc3.getString(_T("AuthHosts"), 0); + if (authHosts.buf) { + CharArray newHosts; + newHosts.buf = strDup(""); + + // Reformat AuthHosts to Hosts. Wish I'd left the format the same. :( :( :( + try { + CharArray tmp = strDup(authHosts.buf); + while (tmp.buf) { + + // Split the AuthHosts string into patterns to match + CharArray first; + rfb::strSplit(tmp.buf, ':', &first.buf, &tmp.buf); + if (strlen(first.buf)) { + int bits = 0; + CharArray pattern(1+4*4+4); + pattern.buf[0] = first.buf[0]; + pattern.buf[1] = 0; + + // Split the pattern into IP address parts and process + rfb::CharArray address; + address.buf = rfb::strDup(&first.buf[1]); + while (address.buf) { + rfb::CharArray part; + rfb::strSplit(address.buf, '.', &part.buf, &address.buf); + if (bits) + strcat(pattern.buf, "."); + if (strlen(part.buf) > 3) + throw rdr::Exception("Invalid IP address part"); + if (strlen(part.buf) > 0) { + strcat(pattern.buf, part.buf); + bits += 8; + } + } + + // Pad out the address specification if required + int addrBits = bits; + while (addrBits < 32) { + if (addrBits) strcat(pattern.buf, "."); + strcat(pattern.buf, "0"); + addrBits += 8; + } + + // Append the number of bits to match + char buf[4]; + sprintf(buf, "/%d", bits); + strcat(pattern.buf, buf); + + // Append this pattern to the Hosts value + int length = strlen(newHosts.buf) + strlen(pattern.buf) + 2; + CharArray tmpHosts(length); + strcpy(tmpHosts.buf, pattern.buf); + if (strlen(newHosts.buf)) { + strcat(tmpHosts.buf, ","); + strcat(tmpHosts.buf, newHosts.buf); + } + delete [] newHosts.buf; + newHosts.buf = tmpHosts.takeBuf(); + } + } + + // Finally, save the Hosts value + regKey.setString(_T("Hosts"), TStr(newHosts.buf)); + } catch (rdr::Exception) { + MsgBox(0, _T("Unable to convert AuthHosts setting to Hosts format."), + MB_ICONWARNING | MB_OK); + } + } else { + regKey.setString(_T("Hosts"), _T("+")); + } + + regKey.setBool(_T("LocalHost"), winvnc3.getBool(_T("LoopbackOnly"), false)); + // *** check AllowLoopback? + + if (winvnc3.getBool(_T("AuthRequired"), true)) + regKey.setString(_T("SecurityTypes"), _T("VncAuth")); + else + regKey.setString(_T("SecurityTypes"), _T("None")); + + int connectPriority = winvnc3.getInt(_T("ConnectPriority"), 0); + regKey.setBool(_T("DisconnectClients"), connectPriority == 0); + regKey.setBool(_T("AlwaysShared"), connectPriority == 1); + regKey.setBool(_T("NeverShared"), connectPriority == 2); + + } catch(rdr::Exception) { + } + + // Open the local, default-user settings + allowProperties = true; + try { + RegKey userKey; + userKey.openKey(winvnc3, _T("Default")); + vlog.info("loading Default prefs"); + LoadUserPrefs(userKey); + } catch(rdr::Exception& e) { + vlog.error("error reading Default settings:%s", e.str()); + } + + // Open the local, user-specific settings + if (userSettings && username.buf) { + try { + RegKey userKey; + userKey.openKey(winvnc3, username.buf); + vlog.info("loading local User prefs"); + LoadUserPrefs(userKey); + } catch(rdr::Exception& e) { + vlog.error("error reading local User settings:%s", e.str()); + } + + // Open the user's own settings + if (allowProperties) { + try { + RegKey userKey; + userKey.openKey(HKEY_CURRENT_USER, _T("Software\\ORL\\WinVNC3")); + vlog.info("loading global User prefs"); + LoadUserPrefs(userKey); + } catch(rdr::Exception& e) { + vlog.error("error reading global User settings:%s", e.str()); + } + } + } + + // Disable the Options menu item if appropriate + regKey.setBool(_T("DisableOptions"), !allowProperties); + } + + void LegacyPage::LoadUserPrefs(const RegKey& key) + { + if (key.getBool(_T("HTTPConnect"), true)) + regKey.setInt(_T("HTTPPortNumber"), key.getInt(_T("PortNumber"), 5900)-100); + else + regKey.setInt(_T("HTTPPortNumber"), 0); + regKey.setInt(_T("PortNumber"), key.getBool(_T("SocketConnect")) ? key.getInt(_T("PortNumber"), 5900) : 0); + if (key.getBool(_T("AutoPortSelect"), false)) { + MsgBox(0, _T("The AutoPortSelect setting is not supported by this release.") + _T("The port number will default to 5900."), + MB_ICONWARNING | MB_OK); + regKey.setInt(_T("PortNumber"), 5900); + } + regKey.setInt(_T("IdleTimeout"), key.getInt(_T("IdleTimeout"), 0)); + + regKey.setBool(_T("RemoveWallpaper"), key.getBool(_T("RemoveWallpaper"))); + regKey.setBool(_T("RemovePattern"), key.getBool(_T("RemoveWallpaper"))); + regKey.setBool(_T("DisableEffects"), key.getBool(_T("RemoveWallpaper"))); + + if (key.getInt(_T("QuerySetting"), 2) != 2) { + regKey.setBool(_T("QueryConnect"), key.getInt(_T("QuerySetting")) > 2); + MsgBox(0, _T("The QuerySetting option has been replaced by QueryConnect.") + _T("Please see the documentation for details of the QueryConnect option."), + MB_ICONWARNING | MB_OK); + } + regKey.setInt(_T("QueryTimeout"), key.getInt(_T("QueryTimeout"), 10)); + + ObfuscatedPasswd passwd; + key.getBinary(_T("Password"), (void**)&passwd.buf, &passwd.length, 0, 0); + regKey.setBinary(_T("Password"), passwd.buf, passwd.length); + + bool enableInputs = key.getBool(_T("InputsEnabled"), true); + regKey.setBool(_T("AcceptKeyEvents"), enableInputs); + regKey.setBool(_T("AcceptPointerEvents"), enableInputs); + regKey.setBool(_T("AcceptCutText"), enableInputs); + regKey.setBool(_T("SendCutText"), enableInputs); + + switch (key.getInt(_T("LockSetting"), 0)) { + case 0: regKey.setString(_T("DisconnectAction"), _T("None")); break; + case 1: regKey.setString(_T("DisconnectAction"), _T("Lock")); break; + case 2: regKey.setString(_T("DisconnectAction"), _T("Logoff")); break; + }; + + regKey.setBool(_T("DisableLocalInputs"), key.getBool(_T("LocalInputsDisabled"), false)); + + // *** ignore polling preferences + // PollUnderCursor, PollForeground, OnlyPollConsole, OnlyPollOnEvent + regKey.setBool(_T("UseHooks"), !key.getBool(_T("PollFullScreen"), false)); + + if (key.isValue(_T("AllowShutdown"))) + MsgBox(0, _T("The AllowShutdown option is not supported by this release."), MB_ICONWARNING | MB_OK); + if (key.isValue(_T("AllowEditClients"))) + MsgBox(0, _T("The AllowEditClients option is not supported by this release."), MB_ICONWARNING | MB_OK); + + allowProperties = key.getBool(_T("AllowProperties"), allowProperties); + } diff --git a/win/vncconfig/Legacy.h b/win/vncconfig/Legacy.h new file mode 100644 index 00000000..02059a64 --- /dev/null +++ b/win/vncconfig/Legacy.h @@ -0,0 +1,85 @@ +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * + * This 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 software 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 software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +#ifndef WINVNCCONF_LEGACY +#define WINVNCCONF_LEGACY + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace rfb { + + namespace win32 { + + class LegacyPage : public PropSheetPage { + public: + LegacyPage(const RegKey& rk, bool userSettings_) + : PropSheetPage(GetModuleHandle(0), MAKEINTRESOURCE(IDD_LEGACY)), regKey(rk), userSettings(userSettings_) {} + void initDialog() { + setItemChecked(IDC_PROTOCOL_3_3, rfb::Server::protocol3_3); + } + bool onCommand(int id, int cmd) { + switch (id) { + case IDC_LEGACY_IMPORT: + { + DWORD result = MsgBox(0, + _T("Importing your legacy VNC 3.3 settings will overwrite your existing settings.\n") + _T("Are you sure you wish to continue?"), + MB_ICONWARNING | MB_YESNO); + if (result == IDYES) { + LoadPrefs(); + MsgBox(0, _T("Imported VNC 3.3 settings successfully."), + MB_ICONINFORMATION | MB_OK); + + // Sleep to allow RegConfig thread to reload settings + Sleep(1000); + propSheet->reInitPages(); + } + } + return true; + case IDC_PROTOCOL_3_3: + setChanged(isItemChecked(IDC_PROTOCOL_3_3) != rfb::Server::protocol3_3); + return false; + }; + return false; + } + bool onOk() { + regKey.setBool(_T("Protocol3.3"), isItemChecked(IDC_PROTOCOL_3_3)); + return true; + } + + void LoadPrefs(); + void LoadUserPrefs(const RegKey& key); + + protected: + bool allowProperties; + RegKey regKey; + bool userSettings; + }; + + }; + +}; + +#endif diff --git a/win/vncconfig/PasswordDialog.cxx b/win/vncconfig/PasswordDialog.cxx new file mode 100644 index 00000000..d26d86f6 --- /dev/null +++ b/win/vncconfig/PasswordDialog.cxx @@ -0,0 +1,52 @@ +/* Copyright (C) 2004-2005 RealVNC Ltd. All Rights Reserved. + * + * This 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 software 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 software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +#include +#include +#include +#include + +using namespace rfb; +using namespace win32; + +PasswordDialog::PasswordDialog(const RegKey& rk, bool registryInsecure_) + : Dialog(GetModuleHandle(0)), regKey(rk), registryInsecure(registryInsecure_) { +} + +bool PasswordDialog::showDialog(HWND owner) { + return Dialog::showDialog(MAKEINTRESOURCE(IDD_AUTH_VNC_PASSWD), owner); +} + +bool PasswordDialog::onOk() { + TPlainPasswd password1(getItemString(IDC_PASSWORD1)); + TPlainPasswd password2(getItemString(IDC_PASSWORD2)); + if (_tcscmp(password1.buf, password2.buf) != 0) { + MsgBox(0, _T("The supplied passwords do not match"), + MB_ICONEXCLAMATION | MB_OK); + return false; + } + if (registryInsecure && + (MsgBox(0, _T("Please note that your password cannot be stored securely on this system. ") + _T("Are you sure you wish to continue?"), + MB_YESNO | MB_ICONWARNING) == IDNO)) + return false; + PlainPasswd password(strDup(password1.buf)); + ObfuscatedPasswd obfPwd(password); + regKey.setBinary(_T("Password"), obfPwd.buf, obfPwd.length); + return true; +} diff --git a/win/vncconfig/PasswordDialog.h b/win/vncconfig/PasswordDialog.h new file mode 100644 index 00000000..dd23f8e3 --- /dev/null +++ b/win/vncconfig/PasswordDialog.h @@ -0,0 +1,40 @@ +/* Copyright (C) 2004-2005 RealVNC Ltd. All Rights Reserved. + * + * This 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 software 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 software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ +#ifndef WINVNCCONF_PASSWORD_DIALOG +#define WINVNCCONF_PASSWORD_DIALOG + +#include +#include + +namespace rfb { + namespace win32 { + + class PasswordDialog : public Dialog { + public: + PasswordDialog(const RegKey& rk, bool registryInsecure_); + bool showDialog(HWND owner=0); + bool onOk(); + protected: + const RegKey& regKey; + bool registryInsecure; + }; + + }; +}; + +#endif \ No newline at end of file diff --git a/win/vncconfig/Sharing.h b/win/vncconfig/Sharing.h new file mode 100644 index 00000000..872ae133 --- /dev/null +++ b/win/vncconfig/Sharing.h @@ -0,0 +1,59 @@ +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * + * This 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 software 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 software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ +#ifndef WINVNCCONF_SHARING +#define WINVNCCONF_SHARING + +#include +#include +#include + +namespace rfb { + + namespace win32 { + + class SharingPage : public PropSheetPage { + public: + SharingPage(const RegKey& rk) + : PropSheetPage(GetModuleHandle(0), MAKEINTRESOURCE(IDD_SHARING)), regKey(rk) {} + void initDialog() { + setItemChecked(IDC_DISCONNECT_CLIENTS, rfb::Server::disconnectClients); + setItemChecked(IDC_SHARE_NEVER, rfb::Server::neverShared); + setItemChecked(IDC_SHARE_ALWAYS, rfb::Server::alwaysShared); + setItemChecked(IDC_SHARE_CLIENT, !(rfb::Server::neverShared || rfb::Server::alwaysShared)); + } + bool onCommand(int id, int cmd) { + setChanged((isItemChecked(IDC_DISCONNECT_CLIENTS) != rfb::Server::disconnectClients) || + (isItemChecked(IDC_SHARE_NEVER) != rfb::Server::neverShared) || + (isItemChecked(IDC_SHARE_ALWAYS) != rfb::Server::alwaysShared)); + return true; + } + bool onOk() { + regKey.setBool(_T("DisconnectClients"), isItemChecked(IDC_DISCONNECT_CLIENTS)); + regKey.setBool(_T("AlwaysShared"), isItemChecked(IDC_SHARE_ALWAYS)); + regKey.setBool(_T("NeverShared"), isItemChecked(IDC_SHARE_NEVER)); + return true; + } + protected: + RegKey regKey; + }; + + }; + +}; + +#endif diff --git a/win/vncconfig/resource.h b/win/vncconfig/resource.h new file mode 100644 index 00000000..ca1fbf5a --- /dev/null +++ b/win/vncconfig/resource.h @@ -0,0 +1,102 @@ +//{{NO_DEPENDENCIES}} +// Microsoft Developer Studio generated include file. +// Used by vncconfig.rc +// +#define IDR_MANIFEST 1 +#define IDI_ICON 101 +#define IDD_DIALOG1 102 +#define IDD_DIALOG2 103 +#define IDD_SECURITY 104 +#define IDD_AUTHENTICATION 104 +#define IDD_CONNECTIONS 105 +#define IDD_HOOKING 106 +#define IDD_VNC_PASSWD 107 +#define IDD_AUTH_VNC_PASSWD 107 +#define IDD_LEGACY 108 +#define IDD_CONN_HOST 109 +#define IDD_SHARING 110 +#define IDD_INPUTS 111 +#define IDR_TRAY 112 +#define IDD_ABOUT 113 +#define IDI_CONNECTED 115 +#define IDD_DESKTOP 116 +#define IDC_EDIT1 1000 +#define IDC_PORT 1000 +#define IDC_PASSWORD1 1000 +#define IDC_HOST_PATTERN 1000 +#define IDC_AUTH_NONE 1002 +#define IDC_AUTH_VNC 1003 +#define IDC_AUTH_VNC_PASSWD 1009 +#define IDC_USEHOOKS 1011 +#define IDC_POLLCONSOLES 1012 +#define IDC_COMPAREFB 1013 +#define IDC_IDLE_TIMEOUT 1015 +#define IDC_HOSTS 1016 +#define IDC_HOST_ADD 1017 +#define IDC_HOST_REMOVE 1018 +#define IDC_HOST_UP 1019 +#define IDC_BUTTON4 1020 +#define IDC_HOST_DOWN 1020 +#define IDC_AUTH_INPUTONLY_PASSWD 1020 +#define IDC_HOST_EDIT 1021 +#define IDC_PASSWORD2 1022 +#define IDC_LEGACY_IMPORT 1023 +#define IDC_ALLOW 1024 +#define IDC_DENY 1025 +#define IDC_SHARE_ALWAYS 1030 +#define IDC_SHARE_NEVER 1031 +#define IDC_SHARE_CLIENT 1032 +#define IDC_DISCONNECT_CLIENTS 1033 +#define IDC_ACCEPT_KEYS 1034 +#define IDC_ACCEPT_PTR 1035 +#define IDC_ACCEPT_CUTTEXT 1036 +#define IDC_SEND_CUTTEXT 1037 +#define IDC_PROTOCOL_3_3 1038 +#define IDC_DESCRIPTION 1039 +#define IDC_BUILDTIME 1040 +#define IDC_VERSION 1041 +#define IDC_COPYRIGHT 1042 +#define IDC_HTTP_ENABLE 1043 +#define IDC_HTTP_PORT 1044 +#define IDC_BL_THRESHOLD 1046 +#define IDC_BL_TIMEOUT 1047 +#define IDC_AFFECT_SCREENSAVER 1048 +#define IDC_LOCALHOST 1049 +#define IDC_DISABLE_LOCAL_INPUTS 1050 +#define IDC_AUTH_NT 1051 +#define IDC_AUTH_NT_CONF 1052 +#define IDC_AUTH_RA2_CONF 1053 +#define IDC_QUERY_CONNECT 1055 +#define IDC_DISCONNECT_NONE 1056 +#define IDC_DISCONNECT_LOCK 1057 +#define IDC_DISCONNECT_LOGOFF 1058 +#define IDC_REMOVE_WALLPAPER 1059 +#define IDC_REMOVE_PATTERN 1060 +#define IDC_DISABLE_EFFECTS 1061 +#define IDC_CAPTUREBLT 1062 +#define IDC_ENCRYPTION 1063 +#define IDC_QUERY 1064 +#define IDC_USEPOLLING 1066 +#define IDC_USEDRIVER 1068 +#define IDC_QUERY_LOGGED_ON 1069 +#define IDC_AUTH_ADMIN_PASSWD 1076 +#define IDC_AUTH_VIEWONLY_PASSWD 1077 +#define IDC_AUTH_ADMIN_ENABLE 1078 +#define IDC_AUTH_VIEWONLY_ENABLE 1079 +#define IDC_AUTH_INPUTONLY_ENABLE 1080 +#define IDC_AUTH_VNC_EXT 1081 +#define IDC_RFB_ENABLE 1082 +#define ID_OPTIONS 40001 +#define ID_CLOSE 40002 +#define ID_ABOUT 40003 + +// Next default values for new objects +// +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS +#define _APS_NEXT_RESOURCE_VALUE 117 +#define _APS_NEXT_COMMAND_VALUE 40004 +#define _APS_NEXT_CONTROL_VALUE 1083 +#define _APS_NEXT_SYMED_VALUE 101 +#endif +#endif diff --git a/win/vncconfig/vncconfig.cxx b/win/vncconfig/vncconfig.cxx new file mode 100644 index 00000000..6c9e1c5a --- /dev/null +++ b/win/vncconfig/vncconfig.cxx @@ -0,0 +1,191 @@ +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * + * This 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 software 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 software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +#include +#include +#include +#ifdef WIN32 +#define strcasecmp _stricmp +#endif + +#include "resource.h" +#include +#include +#include +#include +#include +#include + +using namespace rfb; +using namespace rfb::win32; + +static LogWriter vlog("main"); + + +#include +#include +#include +#include +#include +#include +#include + + +TStr rfb::win32::AppName("VNC Config"); + + +#ifdef _DEBUG +BoolParameter captureDialogs("CaptureDialogs", "", false); +#endif + +HKEY configKey = HKEY_CURRENT_USER; + + +void +processParams(int argc, char* argv[]) { + for (int i=1; i=, or - + if (Configuration::setParam(argv[i], true)) + continue; + // Try to process -