summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--LICENCE.TXT340
-rw-r--r--Makefile.in5
-rw-r--r--README284
-rw-r--r--README.hpux39
-rw-r--r--README.txt126
-rw-r--r--README_BINARY.txt120
-rw-r--r--Xregion/Makefile.in15
-rw-r--r--Xregion/Region.c1687
-rw-r--r--Xregion/Xregion.dsp132
-rw-r--r--Xregion/Xregion.h220
-rw-r--r--Xregion/region.h190
-rw-r--r--boilerplate.mk35
-rwxr-xr-xconfigure2299
-rw-r--r--configure.in99
-rw-r--r--depend.mk76
-rw-r--r--hpux.patch255
-rw-r--r--java/index.vnc13
-rw-r--r--java/logo150x150.gifbin0 -> 3584 bytes
-rw-r--r--java/vncviewer.jarbin0 -> 81356 bytes
-rw-r--r--logmessages/logmessages.dsp202
-rw-r--r--logmessages/messages.h47
-rw-r--r--logmessages/messages.mc7
-rw-r--r--logmessages/messages.rc2
-rw-r--r--network/Makefile.in17
-rw-r--r--network/Socket.h129
-rw-r--r--network/TcpSocket.cxx458
-rw-r--r--network/TcpSocket.h100
-rw-r--r--network/msvcwarning.h18
-rw-r--r--network/network.dsp129
-rw-r--r--rdr/Exception.cxx64
-rw-r--r--rdr/Exception.h59
-rw-r--r--rdr/FdInStream.cxx281
-rw-r--r--rdr/FdInStream.h77
-rw-r--r--rdr/FdOutStream.cxx174
-rw-r--r--rdr/FdOutStream.h56
-rw-r--r--rdr/FixedMemOutStream.h52
-rw-r--r--rdr/HexInStream.cxx117
-rw-r--r--rdr/HexInStream.h50
-rw-r--r--rdr/HexOutStream.cxx110
-rw-r--r--rdr/HexOutStream.h51
-rw-r--r--rdr/InStream.cxx35
-rw-r--r--rdr/InStream.h153
-rw-r--r--rdr/Makefile.in19
-rw-r--r--rdr/MemInStream.h63
-rw-r--r--rdr/MemOutStream.h82
-rw-r--r--rdr/NullOutStream.cxx60
-rw-r--r--rdr/NullOutStream.h42
-rw-r--r--rdr/OutStream.h152
-rw-r--r--rdr/RandomStream.cxx118
-rw-r--r--rdr/RandomStream.h63
-rw-r--r--rdr/SubstitutingInStream.h102
-rw-r--r--rdr/ZlibInStream.cxx125
-rw-r--r--rdr/ZlibInStream.h59
-rw-r--r--rdr/ZlibOutStream.cxx140
-rw-r--r--rdr/ZlibOutStream.h57
-rw-r--r--rdr/msvcwarning.h18
-rw-r--r--rdr/rdr.dsp231
-rw-r--r--rdr/types.h56
-rw-r--r--rfb/Blacklist.cxx86
-rw-r--r--rfb/Blacklist.h91
-rw-r--r--rfb/CConnection.cxx291
-rw-r--r--rfb/CConnection.h178
-rw-r--r--rfb/CMsgHandler.cxx99
-rw-r--r--rfb/CMsgHandler.h62
-rw-r--r--rfb/CMsgReader.cxx167
-rw-r--r--rfb/CMsgReader.h75
-rw-r--r--rfb/CMsgReaderV3.cxx97
-rw-r--r--rfb/CMsgReaderV3.h35
-rw-r--r--rfb/CMsgWriter.cxx130
-rw-r--r--rfb/CMsgWriter.h64
-rw-r--r--rfb/CMsgWriterV3.cxx49
-rw-r--r--rfb/CMsgWriterV3.h35
-rw-r--r--rfb/CSecurity.h48
-rw-r--r--rfb/CSecurityNone.h38
-rw-r--r--rfb/CSecurityVncAuth.cxx63
-rw-r--r--rfb/CSecurityVncAuth.h40
-rw-r--r--rfb/ColourCube.h96
-rw-r--r--rfb/ColourMap.h34
-rw-r--r--rfb/ComparingUpdateTracker.cxx151
-rw-r--r--rfb/ComparingUpdateTracker.h47
-rw-r--r--rfb/Configuration.cxx410
-rw-r--r--rfb/Configuration.h167
-rw-r--r--rfb/ConnParams.cxx104
-rw-r--r--rfb/ConnParams.h86
-rw-r--r--rfb/Cursor.cxx178
-rw-r--r--rfb/Cursor.h56
-rw-r--r--rfb/Decoder.cxx68
-rw-r--r--rfb/Decoder.h52
-rw-r--r--rfb/Encoder.cxx75
-rw-r--r--rfb/Encoder.h57
-rw-r--r--rfb/Exception.h37
-rw-r--r--rfb/HTTPServer.cxx386
-rw-r--r--rfb/HTTPServer.h112
-rw-r--r--rfb/HextileDecoder.cxx59
-rw-r--r--rfb/HextileDecoder.h35
-rw-r--r--rfb/HextileEncoder.cxx61
-rw-r--r--rfb/HextileEncoder.h35
-rw-r--r--rfb/Hostname.h53
-rw-r--r--rfb/ImageGetter.h30
-rw-r--r--rfb/LogWriter.cxx137
-rw-r--r--rfb/LogWriter.h106
-rw-r--r--rfb/Logger.cxx107
-rw-r--r--rfb/Logger.h70
-rw-r--r--rfb/Logger_file.cxx123
-rw-r--r--rfb/Logger_file.h51
-rw-r--r--rfb/Logger_stdio.cxx32
-rw-r--r--rfb/Logger_stdio.h39
-rw-r--r--rfb/Makefile.in66
-rw-r--r--rfb/Pixel.h26
-rw-r--r--rfb/PixelBuffer.cxx309
-rw-r--r--rfb/PixelBuffer.h172
-rw-r--r--rfb/PixelFormat.cxx238
-rw-r--r--rfb/PixelFormat.h58
-rw-r--r--rfb/RREDecoder.cxx58
-rw-r--r--rfb/RREDecoder.h35
-rw-r--r--rfb/RREEncoder.cxx75
-rw-r--r--rfb/RREEncoder.h37
-rw-r--r--rfb/RawDecoder.cxx55
-rw-r--r--rfb/RawDecoder.h35
-rw-r--r--rfb/RawEncoder.cxx59
-rw-r--r--rfb/RawEncoder.h35
-rw-r--r--rfb/Rect.h112
-rw-r--r--rfb/Region.cxx248
-rw-r--r--rfb/Region.h84
-rw-r--r--rfb/SConnection.cxx330
-rw-r--r--rfb/SConnection.h207
-rw-r--r--rfb/SDesktop.h123
-rw-r--r--rfb/SMsgHandler.cxx64
-rw-r--r--rfb/SMsgHandler.h62
-rw-r--r--rfb/SMsgReader.cxx105
-rw-r--r--rfb/SMsgReader.h57
-rw-r--r--rfb/SMsgReaderV3.cxx57
-rw-r--r--rfb/SMsgReaderV3.h32
-rw-r--r--rfb/SMsgWriter.cxx180
-rw-r--r--rfb/SMsgWriter.h156
-rw-r--r--rfb/SMsgWriterV3.cxx173
-rw-r--r--rfb/SMsgWriterV3.h56
-rw-r--r--rfb/SSecurity.h77
-rw-r--r--rfb/SSecurityFactoryStandard.cxx101
-rw-r--r--rfb/SSecurityFactoryStandard.h75
-rw-r--r--rfb/SSecurityNone.h38
-rw-r--r--rfb/SSecurityVncAuth.cxx83
-rw-r--r--rfb/SSecurityVncAuth.h54
-rw-r--r--rfb/ServerCore.cxx95
-rw-r--r--rfb/ServerCore.h56
-rw-r--r--rfb/Threading.h31
-rw-r--r--rfb/TransImageGetter.cxx278
-rw-r--r--rfb/TransImageGetter.h104
-rw-r--r--rfb/TrueColourMap.h42
-rw-r--r--rfb/UpdateTracker.cxx172
-rw-r--r--rfb/UpdateTracker.h107
-rw-r--r--rfb/UserPasswdGetter.h31
-rw-r--r--rfb/VNCSConnectionST.cxx629
-rw-r--r--rfb/VNCSConnectionST.h168
-rw-r--r--rfb/VNCServer.h86
-rw-r--r--rfb/VNCServerST.cxx430
-rw-r--r--rfb/VNCServerST.h233
-rw-r--r--rfb/ZRLEDecoder.cxx91
-rw-r--r--rfb/ZRLEDecoder.h37
-rw-r--r--rfb/ZRLEEncoder.cxx122
-rw-r--r--rfb/ZRLEEncoder.h53
-rw-r--r--rfb/d3des.c434
-rw-r--r--rfb/d3des.h51
-rw-r--r--rfb/encodings.cxx46
-rw-r--r--rfb/encodings.h38
-rw-r--r--rfb/hextileConstants.h27
-rw-r--r--rfb/hextileDecode.h126
-rw-r--r--rfb/hextileEncode.h247
-rw-r--r--rfb/keysymdef.h1595
-rw-r--r--rfb/msgTypes.h39
-rw-r--r--rfb/msvcwarning.h20
-rw-r--r--rfb/rfb.dsp625
-rw-r--r--rfb/rreDecode.h64
-rw-r--r--rfb/rreEncode.h164
-rw-r--r--rfb/secTypes.cxx64
-rw-r--r--rfb/secTypes.h51
-rw-r--r--rfb/transInitTempl.h254
-rw-r--r--rfb/transTempl.h151
-rw-r--r--rfb/util.cxx78
-rw-r--r--rfb/util.h90
-rw-r--r--rfb/vncAuth.cxx61
-rw-r--r--rfb/vncAuth.h31
-rw-r--r--rfb/win32/Threading_win32.cxx155
-rw-r--r--rfb/win32/Threading_win32.h149
-rw-r--r--rfb/win32/msvcwarning.h19
-rw-r--r--rfb/win32/util_win32.h111
-rw-r--r--rfb/zrleDecode.h251
-rw-r--r--rfb/zrleEncode.h328
-rw-r--r--rfb_win32/AboutDialog.cxx49
-rw-r--r--rfb_win32/AboutDialog.h55
-rw-r--r--rfb_win32/CKeyboard.cxx259
-rw-r--r--rfb_win32/CKeyboard.h53
-rw-r--r--rfb_win32/CPointer.cxx188
-rw-r--r--rfb_win32/CPointer.h76
-rw-r--r--rfb_win32/CleanDesktop.cxx255
-rw-r--r--rfb_win32/CleanDesktop.h57
-rw-r--r--rfb_win32/Clipboard.cxx199
-rw-r--r--rfb_win32/Clipboard.h66
-rw-r--r--rfb_win32/CurrentUser.cxx93
-rw-r--r--rfb_win32/CurrentUser.h78
-rw-r--r--rfb_win32/DIBSectionBuffer.cxx221
-rw-r--r--rfb_win32/DIBSectionBuffer.h87
-rw-r--r--rfb_win32/DeviceFrameBuffer.cxx298
-rw-r--r--rfb_win32/DeviceFrameBuffer.h121
-rw-r--r--rfb_win32/Dialog.cxx353
-rw-r--r--rfb_win32/Dialog.h159
-rw-r--r--rfb_win32/IntervalTimer.h70
-rw-r--r--rfb_win32/LaunchProcess.cxx92
-rw-r--r--rfb_win32/LaunchProcess.h59
-rw-r--r--rfb_win32/MsgWindow.cxx116
-rw-r--r--rfb_win32/MsgWindow.h55
-rw-r--r--rfb_win32/OSVersion.cxx47
-rw-r--r--rfb_win32/OSVersion.h54
-rw-r--r--rfb_win32/RegConfig.cxx151
-rw-r--r--rfb_win32/RegConfig.h56
-rw-r--r--rfb_win32/Registry.cxx272
-rw-r--r--rfb_win32/Registry.h111
-rw-r--r--rfb_win32/SDisplay.cxx612
-rw-r--r--rfb_win32/SDisplay.h149
-rw-r--r--rfb_win32/SInput.cxx459
-rw-r--r--rfb_win32/SInput.h70
-rw-r--r--rfb_win32/Security.h198
-rw-r--r--rfb_win32/Service.cxx638
-rw-r--r--rfb_win32/Service.h123
-rw-r--r--rfb_win32/SocketManager.cxx246
-rw-r--r--rfb_win32/SocketManager.h107
-rw-r--r--rfb_win32/TCharArray.cxx85
-rw-r--r--rfb_win32/TCharArray.h119
-rw-r--r--rfb_win32/TrayIcon.h90
-rw-r--r--rfb_win32/WMCursor.cxx98
-rw-r--r--rfb_win32/WMCursor.h65
-rw-r--r--rfb_win32/WMHooks.cxx324
-rw-r--r--rfb_win32/WMHooks.h85
-rw-r--r--rfb_win32/WMNotifier.cxx57
-rw-r--r--rfb_win32/WMNotifier.h68
-rw-r--r--rfb_win32/WMPoller.cxx101
-rw-r--r--rfb_win32/WMPoller.h67
-rw-r--r--rfb_win32/WMShatter.cxx57
-rw-r--r--rfb_win32/WMShatter.h53
-rw-r--r--rfb_win32/WMWindowCopyRect.cxx83
-rw-r--r--rfb_win32/WMWindowCopyRect.h56
-rw-r--r--rfb_win32/Win32Util.cxx447
-rw-r--r--rfb_win32/Win32Util.h217
-rw-r--r--rfb_win32/keymap.h149
-rw-r--r--rfb_win32/msvcwarning.h20
-rw-r--r--rfb_win32/rfb_win32.dsp341
-rw-r--r--tx/Makefile.in18
-rw-r--r--tx/TXButton.h124
-rw-r--r--tx/TXCheckbox.h142
-rw-r--r--tx/TXDialog.h90
-rw-r--r--tx/TXEntry.h188
-rw-r--r--tx/TXImage.cxx340
-rw-r--r--tx/TXImage.h89
-rw-r--r--tx/TXLabel.h126
-rw-r--r--tx/TXMenu.cxx186
-rw-r--r--tx/TXMenu.h67
-rw-r--r--tx/TXMsgBox.h111
-rw-r--r--tx/TXScrollbar.cxx119
-rw-r--r--tx/TXScrollbar.h82
-rw-r--r--tx/TXViewport.cxx157
-rw-r--r--tx/TXViewport.h77
-rw-r--r--tx/TXWindow.cxx486
-rw-r--r--tx/TXWindow.h210
-rw-r--r--tx/Timer.cxx105
-rw-r--r--tx/Timer.h51
-rw-r--r--vnc.dsw206
-rw-r--r--vncconfig/Authentication.h137
-rw-r--r--vncconfig/Connections.h278
-rw-r--r--vncconfig/Desktop.h93
-rw-r--r--vncconfig/Hooking.h65
-rw-r--r--vncconfig/Inputs.h84
-rw-r--r--vncconfig/Legacy.cxx248
-rw-r--r--vncconfig/Legacy.h86
-rw-r--r--vncconfig/Sharing.h59
-rw-r--r--vncconfig/resource.h86
-rw-r--r--vncconfig/vncconfig.cxx193
-rw-r--r--vncconfig/vncconfig.dsp188
-rw-r--r--vncconfig/vncconfig.exe.manifest22
-rw-r--r--vncconfig/vncconfig.icobin0 -> 1078 bytes
-rw-r--r--vncconfig/vncconfig.rc479
-rw-r--r--vncconfig_unix/Makefile.in23
-rw-r--r--vncconfig_unix/buildtime.c18
-rw-r--r--vncconfig_unix/vncExt.c316
-rw-r--r--vncconfig_unix/vncExt.h279
-rw-r--r--vncconfig_unix/vncconfig.cxx313
-rw-r--r--vncconfig_unix/vncconfig.man115
-rwxr-xr-xvncinstall98
-rw-r--r--vncmkdepend/Makefile4
-rw-r--r--vncmkdepend/README39
-rw-r--r--vncmkdepend/cppsetup.c242
-rw-r--r--vncmkdepend/def.h140
-rw-r--r--vncmkdepend/ifparser.c445
-rw-r--r--vncmkdepend/ifparser.h76
-rw-r--r--vncmkdepend/include.c312
-rw-r--r--vncmkdepend/main.c593
-rw-r--r--vncmkdepend/parse.c568
-rw-r--r--vncmkdepend/pr.c130
-rw-r--r--vncpasswd/Makefile.in18
-rw-r--r--vncpasswd/vncpasswd.cxx119
-rw-r--r--vncpasswd/vncpasswd.man42
-rwxr-xr-xvncserver570
-rw-r--r--vncserver.man120
-rw-r--r--vncviewer/CViewManager.cxx252
-rw-r--r--vncviewer/CViewManager.h64
-rw-r--r--vncviewer/CViewOptions.cxx364
-rw-r--r--vncviewer/CViewOptions.h87
-rw-r--r--vncviewer/ConnectingDialog.h95
-rw-r--r--vncviewer/ConnectionDialog.cxx70
-rw-r--r--vncviewer/ConnectionDialog.h52
-rw-r--r--vncviewer/InfoDialog.cxx65
-rw-r--r--vncviewer/InfoDialog.h48
-rw-r--r--vncviewer/MRU.h140
-rw-r--r--vncviewer/OptionsDialog.cxx309
-rw-r--r--vncviewer/OptionsDialog.h49
-rw-r--r--vncviewer/UserPasswdDialog.cxx84
-rw-r--r--vncviewer/UserPasswdDialog.h54
-rw-r--r--vncviewer/buildTime.cxx1
-rw-r--r--vncviewer/cursor1.curbin0 -> 326 bytes
-rw-r--r--vncviewer/cview.cxx1468
-rw-r--r--vncviewer/cview.h296
-rw-r--r--vncviewer/msvcwarning.h19
-rw-r--r--vncviewer/resource.h80
-rw-r--r--vncviewer/vncviewer.cxx370
-rw-r--r--vncviewer/vncviewer.dsp229
-rw-r--r--vncviewer/vncviewer.exe.manifest22
-rw-r--r--vncviewer/vncviewer.icobin0 -> 8478 bytes
-rw-r--r--vncviewer/vncviewer.rc500
-rw-r--r--vncviewer_unix/AboutDialog.h37
-rw-r--r--vncviewer_unix/CConn.cxx669
-rw-r--r--vncviewer_unix/CConn.h129
-rw-r--r--vncviewer_unix/DesktopWindow.cxx573
-rw-r--r--vncviewer_unix/DesktopWindow.h128
-rw-r--r--vncviewer_unix/InfoDialog.h60
-rw-r--r--vncviewer_unix/Makefile.in23
-rw-r--r--vncviewer_unix/OptionsDialog.h168
-rw-r--r--vncviewer_unix/PasswdDialog.h72
-rw-r--r--vncviewer_unix/ServerDialog.h91
-rw-r--r--vncviewer_unix/buildtime.c18
-rw-r--r--vncviewer_unix/parameters.h44
-rw-r--r--vncviewer_unix/vncviewer.cxx244
-rw-r--r--vncviewer_unix/vncviewer.man189
-rw-r--r--winvnc/AddNewClientDialog.h56
-rw-r--r--winvnc/JavaViewer.cxx95
-rw-r--r--winvnc/JavaViewer.h54
-rw-r--r--winvnc/QueryConnectDialog.cxx100
-rw-r--r--winvnc/QueryConnectDialog.h60
-rw-r--r--winvnc/STrayIcon.cxx234
-rw-r--r--winvnc/STrayIcon.h58
-rw-r--r--winvnc/VNCServerService.cxx52
-rw-r--r--winvnc/VNCServerService.h44
-rw-r--r--winvnc/VNCServerWin32.cxx341
-rw-r--r--winvnc/VNCServerWin32.h99
-rw-r--r--winvnc/buildTime.cxx1
-rw-r--r--winvnc/connected.icobin0 -> 1078 bytes
-rw-r--r--winvnc/java/index.vnc13
-rw-r--r--winvnc/java/logo150x150.gifbin0 -> 3584 bytes
-rw-r--r--winvnc/java/vncviewer.jarbin0 -> 81356 bytes
-rw-r--r--winvnc/msvcwarning.h19
-rw-r--r--winvnc/resource.h37
-rw-r--r--winvnc/winvnc.cxx249
-rw-r--r--winvnc/winvnc.dsp228
-rw-r--r--winvnc/winvnc.icobin0 -> 3310 bytes
-rw-r--r--winvnc/winvnc.rc254
-rw-r--r--winvnc/winvnc4.exe.manifest22
-rw-r--r--wm_hooks/msvcwarning.h19
-rw-r--r--wm_hooks/resource.h15
-rw-r--r--wm_hooks/wm_hooks.cxx462
-rw-r--r--wm_hooks/wm_hooks.def5
-rw-r--r--wm_hooks/wm_hooks.dsp149
-rw-r--r--wm_hooks/wm_hooks.h103
-rw-r--r--wm_hooks/wm_hooks.rc109
-rw-r--r--x0vncserver/Image.cxx149
-rw-r--r--x0vncserver/Image.h48
-rw-r--r--x0vncserver/Makefile.in22
-rw-r--r--x0vncserver/buildtime.c18
-rw-r--r--x0vncserver/x0vncserver.cxx277
-rw-r--r--x0vncserver/x0vncserver.man32
-rw-r--r--xc.patch191
-rw-r--r--xc/config/cf/host.def1
-rw-r--r--xc/config/cf/vnc.def32
-rw-r--r--xc/programs/Xserver/Xvnc.man258
-rw-r--r--xc/programs/Xserver/vnc/Imakefile44
-rw-r--r--xc/programs/Xserver/vnc/RegionHelper.h77
-rw-r--r--xc/programs/Xserver/vnc/XserverDesktop.cc1079
-rw-r--r--xc/programs/Xserver/vnc/XserverDesktop.h102
-rw-r--r--xc/programs/Xserver/vnc/Xvnc/Imakefile74
-rw-r--r--xc/programs/Xserver/vnc/Xvnc/buildtime.c18
-rw-r--r--xc/programs/Xserver/vnc/Xvnc/xvnc.cc1221
-rw-r--r--xc/programs/Xserver/vnc/module/Imakefile54
-rw-r--r--xc/programs/Xserver/vnc/vncExtInit.cc714
-rw-r--r--xc/programs/Xserver/vnc/vncExtInit.h30
-rw-r--r--xc/programs/Xserver/vnc/vncHooks.cc1475
-rw-r--r--xc/programs/Xserver/vnc/vncHooks.h26
-rw-r--r--xc/programs/Xserver/vnc/xf86vncModule.cc96
-rw-r--r--zlib/ChangeLog481
-rw-r--r--zlib/FAQ100
-rw-r--r--zlib/INDEX86
-rw-r--r--zlib/Make_vms.com115
-rw-r--r--zlib/Makefile.in175
-rw-r--r--zlib/Makefile.riscos151
-rw-r--r--zlib/README147
-rw-r--r--zlib/adler32.c48
-rw-r--r--zlib/algorithm.txt213
-rw-r--r--zlib/compress.c68
-rwxr-xr-xzlib/configure212
-rw-r--r--zlib/crc32.c162
-rw-r--r--zlib/deflate.c1350
-rw-r--r--zlib/deflate.h318
-rw-r--r--zlib/descrip.mms48
-rw-r--r--zlib/example.c556
-rw-r--r--zlib/gzio.c875
-rw-r--r--zlib/infblock.c403
-rw-r--r--zlib/infblock.h39
-rw-r--r--zlib/infcodes.c251
-rw-r--r--zlib/infcodes.h27
-rw-r--r--zlib/inffast.c183
-rw-r--r--zlib/inffast.h17
-rw-r--r--zlib/inffixed.h151
-rw-r--r--zlib/inflate.c366
-rw-r--r--zlib/inftrees.c454
-rw-r--r--zlib/inftrees.h58
-rw-r--r--zlib/infutil.c87
-rw-r--r--zlib/infutil.h98
-rw-r--r--zlib/maketree.c85
-rw-r--r--zlib/minigzip.c320
-rw-r--r--zlib/trees.c1214
-rw-r--r--zlib/trees.h128
-rw-r--r--zlib/uncompr.c58
-rw-r--r--zlib/zconf.h279
-rw-r--r--zlib/zlib.3107
-rw-r--r--zlib/zlib.dsp217
-rw-r--r--zlib/zlib.h893
-rw-r--r--zlib/zlib.html971
-rw-r--r--zlib/zutil.c225
-rw-r--r--zlib/zutil.h220
435 files changed, 72719 insertions, 0 deletions
diff --git a/LICENCE.TXT b/LICENCE.TXT
new file mode 100644
index 00000000..ae3b5319
--- /dev/null
+++ b/LICENCE.TXT
@@ -0,0 +1,340 @@
+ GNU GENERAL PUBLIC LICENSE
+ Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+ 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users. This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it. (Some other Free Software Foundation software is covered by
+the GNU Library General Public License instead.) You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+ To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have. You must make sure that they, too, receive or can get the
+source code. And you must show them these terms so they know their
+rights.
+
+ We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+ Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software. If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+ Finally, any free program is threatened constantly by software
+patents. We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary. To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ GNU GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License. The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language. (Hereinafter, translation is included without limitation in
+the term "modification".) Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+ 1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+ 2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+ a) You must cause the modified files to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ b) You must cause any work that you distribute or publish, that in
+ whole or in part contains or is derived from the Program or any
+ part thereof, to be licensed as a whole at no charge to all third
+ parties under the terms of this License.
+
+ c) If the modified program normally reads commands interactively
+ when run, you must cause it, when started running for such
+ interactive use in the most ordinary way, to print or display an
+ announcement including an appropriate copyright notice and a
+ notice that there is no warranty (or else, saying that you provide
+ a warranty) and that users may redistribute the program under
+ these conditions, and telling the user how to view a copy of this
+ License. (Exception: if the Program itself is interactive but
+ does not normally print such an announcement, your work based on
+ the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+ a) Accompany it with the complete corresponding machine-readable
+ source code, which must be distributed under the terms of Sections
+ 1 and 2 above on a medium customarily used for software interchange; or,
+
+ b) Accompany it with a written offer, valid for at least three
+ years, to give any third party, for a charge no more than your
+ cost of physically performing source distribution, a complete
+ machine-readable copy of the corresponding source code, to be
+ distributed under the terms of Sections 1 and 2 above on a medium
+ customarily used for software interchange; or,
+
+ c) Accompany it with the information you received as to the offer
+ to distribute corresponding source code. (This alternative is
+ allowed only for noncommercial distribution and only if you
+ received the program in object code or executable form with such
+ an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it. For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable. However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+ 4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License. Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+ 5. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Program or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+ 6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+ 7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all. For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+ 8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded. In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+ 9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation. If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+ 10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission. For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this. Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+ NO WARRANTY
+
+ 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+ 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+
+ Appendix: How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+ <one line to give the program's name and a brief idea of what it does.>
+ Copyright (C) 19yy <name of author>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+ USA.
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+ Gnomovision version 69, Copyright (C) 19yy name of author
+ Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+ This is free software, and you are welcome to redistribute it
+ under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary. Here is a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+ `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+ <signature of Ty Coon>, 1 April 1989
+ Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs. If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library. If this is what you want to do, use the GNU Library General
+Public License instead of this License.
diff --git a/Makefile.in b/Makefile.in
new file mode 100644
index 00000000..9341f4ad
--- /dev/null
+++ b/Makefile.in
@@ -0,0 +1,5 @@
+
+SUBDIRS = @ZLIB_DIR@ rdr network Xregion rfb tx x0vncserver vncviewer \
+ vncconfig vncpasswd
+
+# followed by boilerplate.mk
diff --git a/README b/README
new file mode 100644
index 00000000..4c0e5fb1
--- /dev/null
+++ b/README
@@ -0,0 +1,284 @@
+
+VNC 4.0 Source Distribution for Unix platforms
+==============================================
+
+Copyright (C) 2002-2004 RealVNC Ltd. All Rights Reserved.
+
+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. VNC 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.
+
+
+There are six programs here:
+
+ vncviewer - this is the VNC viewer, or client, program for X.
+
+ Xvnc - this is the X VNC server - it is both a VNC server and an X server
+ with a "virtual" framebuffer. You normally use the vncserver script
+ to start Xvnc.
+
+ vncserver - this is a wrapper script which makes starting an X VNC server
+ (i.e. desktop) more convenient. It is written in Perl, so to use
+ the script you need that.
+
+ vncpasswd - this program allows you to change the password used to access
+ your X VNC desktops. The vncserver script uses this program when
+ you first start a VNC server.
+
+ vncconfig - this program is used to configure and control a running instance
+ of Xvnc.
+
+ x0vncserver - this is an inefficient VNC server which continuously polls any
+ X display, allowing it to be controlled via VNC. It is
+ intended mainly as a demonstration of a simple VNC server.
+
+In addition to these standalone programs, this distribution can also be used to
+turn the native X server for a platform into a VNC server. For XFree86 version
+4 servers, this is done using a module loaded at run-time. For other X servers
+it requires replacing the native X server binary.
+
+To build this distribution you need a C++ compiler as well as a C compiler.
+You also need a reasonably recent version of the X window system installed.
+This comes as standard with most unix machines. If you don't have it
+installed, see http://www.xfree86.org or http://www.x.org
+
+To build everything but Xvnc, do:
+
+ % ./configure
+ % make
+
+This should build first some libraries - zlib, rdr, network, Xregion, rfb and
+tx - then vncviewer, vncconfig and vncpasswd. If you already have zlib
+installed on your system you can run "./configure --with-installed-zlib" if you
+prefer (this is strongly advised on FreeBSD, since we've been told there are
+problems otherwise).
+
+Building Xvnc
+=============
+
+Building Xvnc and the VNC support for native X servers is much more complex.
+If you don't need to build it, skip to the section below on installing.
+
+Xvnc differs from the other programs in that it is built inside the X source
+tree. Unlike previous versions of Xvnc, we do not provide an X source tree
+with this distribution. We have designed the distribution to be as independent
+as possible of the X tree used.
+
+We have successfully used XFree86 version 4.3.0, 4.2.0 and 3.3.6 (available
+from http://www.xfree86.org). You could also try the original X.org tree
+available from http://www.x.org but this does not build as easily because of
+lack of support for C++, no support for building server only, and other issues.
+Note that the X tree is enormous and notoriously difficult to deal with -
+building it is not for the faint-hearted!
+
+Once you have a copy of the X source tree, make sure it is unpacked at the top
+level of this distribution, so that the xc directory of the X source tree
+matches the xc of this distribution, for example:
+
+ % tar xzf X420src-1.tgz
+
+Then you must apply a patch to some files in the X source tree:
+
+ % patch -Np0 <xc.patch
+
+If this works, you should be able to build the entire X tree, including Xvnc:
+
+ % cd xc
+ % make World
+
+This will take a long time, and will quite probably fail for one reason or
+another! If you are having trouble, we suggest you try to build the X tree in
+isolation first before attempting it with the VNC additions.
+
+If successful, in the xc/programs/Xserver directory you should find an Xvnc
+binary, plus the native X server binary(ies) for your platform with VNC support
+compiled in. If you are building from an XFree86 version 4 tree on a supported
+platform, you should also find a vnc.so module in
+xc/programs/Xserver/vnc/modules.
+
+Exactly which X extensions and features are built into Xvnc and the native X
+server binary is determined by the settings in xc/config/cf. The file vnc.def
+contains the settings we use to build our binary distributions. You may need
+to edit this and the other files as appropriate.
+
+Installing
+==========
+
+Different unix platforms have different conventions for where software should
+be installed. To copy the programs to some directory which is in your PATH
+environment variable, such as /usr/local/bin, there is a script called
+vncinstall which you can use:
+
+ % cd ..
+ % ./vncinstall /usr/local/bin
+
+This will also attempt to install the manual pages in an appropriate directory.
+You can specify an alternative directory as a second argument to vncinstall:
+
+ % ./vncinstall /usr/local/bin /usr/local/man
+
+It will also try to install the vnc.so XFree86 version 4 module if appropriate.
+This will be copied to the /usr/X11R6/lib/modules/extensions directory and can
+be enabled like any other module by adding a Load "vnc" line to the Module
+section of XF86Config. The parameters listed in the Xvnc manual page can be
+set as options in XF86Config e.g. Option "passwordFile" "/root/.vnc/passwd".
+Note that for some reason options cannot be set in the Module section of
+XF86Config - try the Screen section.
+
+If you want to use the Java VNC viewer, you should copy the files from
+the java directory to some suitable installation directory such as
+/usr/local/vnc/classes:
+
+ % mkdir -p /usr/local/vnc/classes
+ % cp java/* /usr/local/vnc/classes
+
+We recommend that you use the vncserver script to run Xvnc for you. You can
+edit the script as appropriate for your site. Things you may need to change
+include:
+
+ * The location of Perl - if Perl is not installed in /usr/bin you'll need
+ to edit the "#!/usr/bin/perl" first line of vncserver.
+
+ * Xvnc's font path and color database. If you have an installation of
+ X which is not in the standard place you may need to add arguments to the
+ Xvnc command line to set these. These should be appended to the $cmd
+ variable at the comment "# Add font path and color database...".
+
+ * $vncJavaFiles - this specifies the location of the files for
+ the VNC viewer Java applet. The default is /usr/local/vnc/classes.
+
+
+ACKNOWLEDGEMENTS
+================
+
+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 software from the X Window System. This is:
+
+ Copyright 1987, 1988, 1998 The Open Group
+
+ Permission to use, copy, modify, distribute, and sell this software and its
+ documentation for any purpose is hereby granted without fee, provided that
+ the above copyright notice appear in all copies and that both that
+ copyright notice and this permission notice appear in supporting
+ documentation.
+
+ The above copyright notice and this permission notice shall be included in
+ all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+ Except as contained in this notice, the name of The Open Group shall not be
+ used in advertising or otherwise to promote the sale, use or other dealings
+ in this Software without prior written authorization from The Open Group.
+
+
+ Copyright 1987, 1988 by Digital Equipment Corporation, Maynard, Massachusetts.
+
+ All Rights Reserved
+
+ Permission to use, copy, modify, and distribute this software and its
+ documentation for any purpose and without fee is hereby granted,
+ provided that the above copyright notice appear in all copies and that
+ both that copyright notice and this permission notice appear in
+ supporting documentation, and that the name of Digital not be
+ used in advertising or publicity pertaining to distribution of the
+ software without specific, written prior permission.
+
+ DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
+ ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
+ DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
+ ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
+ WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
+ ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
+ SOFTWARE.
+
+
+This distribution contains zlib compression software. 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
+
+
+This distribution contains Java DES software by Dave Zimmerman
+<dzimm@widget.com> and Jef Poskanzer <jef@acme.com>. 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 <jef@acme.com>. 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/README.hpux b/README.hpux
new file mode 100644
index 00000000..ecbe9667
--- /dev/null
+++ b/README.hpux
@@ -0,0 +1,39 @@
+I have managed to build Xvnc on HPUX but only with some ugly hacking of
+the X tree. The X tree I used as the basis for the build is the XFree86 4.2.0
+tree. The XFree86 4.3.0 tree is unsuitable as it seems to have had some HPUX
+stuff removed from it. I built using the aCC C++ compiler.
+
+Set the following environment variables:
+
+ % CXX=/opt/aCC/bin/aCC
+ % CFLAGS="+DAportable"
+ % CXXFLAGS="+DAportable -AA +W749 +W740"
+ % BOOTSTRAPCFLAGS=-Dhpux
+ % export CXX CFLAGS CXXFLAGS BOOTSTRAPCFLAGS
+
+Build the main part of the VNC distribution as normal:
+
+ % ./configure
+ % make
+
+Unpack the X tree and apply the patches in xc.patch:
+
+ % gunzip -c X420src-1.tgz | tar xf -
+ % patch -Np0 <xc.patch
+
+Then additionally apply the patches in hpux.patch:
+
+ % patch -Np0 <hpux.patch
+
+Finally try building the X tree:
+
+ % cd xc
+ % make World
+
+If it all goes to plan you will be left with Xvnc in xc/programs/Xserver. You
+will probably have to modify the vncserver script to set up a sensible font
+path, since many of the font directories on HPUX are different from the
+defaults compiled into Xvnc.
+
+If anyone can find a neater way of building a VNC-compatible X tree on HPUX
+please let us know (see http://www.realvnc.com for contact details).
diff --git a/README.txt b/README.txt
new file mode 100644
index 00000000..0811395d
--- /dev/null
+++ b/README.txt
@@ -0,0 +1,126 @@
+
+VNC 4 Source Distribution for Windows platforms
+=============================================
+
+VNC 4 is Copyright RealVNC Ltd. 2002-2004. This software is
+distributed under the GNU General Public Licence as published by the
+Free Software Foundation. See the accompanying licence.txt file for
+the conditions under which the software is made available.
+
+VNC 4 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
+<dzimm@widget.com> and Jef Poskanzer <jef@acme.com>. 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 <jef@acme.com>. 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/README_BINARY.txt b/README_BINARY.txt
new file mode 100644
index 00000000..8f89fe93
--- /dev/null
+++ b/README_BINARY.txt
@@ -0,0 +1,120 @@
+
+VNC 4 Binary Distribution for Windows platforms
+=============================================
+
+VNC 4 is Copyright RealVNC Ltd. 2002-2004. This software is
+distributed under the GNU General Public Licence as published by the
+Free Software Foundation. VNC also contains code from other sources,
+as outlined in the Acknowledgements section below.
+
+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
+<dzimm@widget.com> and Jef Poskanzer <jef@acme.com>. 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 <jef@acme.com>. 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/Xregion/Makefile.in b/Xregion/Makefile.in
new file mode 100644
index 00000000..878a29b6
--- /dev/null
+++ b/Xregion/Makefile.in
@@ -0,0 +1,15 @@
+
+SRCS = Region.c
+
+OBJS = $(SRCS:.c=.o)
+
+library = libXregion.a
+
+all:: $(library)
+
+$(library): $(OBJS)
+ rm -f $(library)
+ $(AR) $(library) $(OBJS)
+ $(RANLIB) $(library)
+
+# followed by boilerplate.mk
diff --git a/Xregion/Region.c b/Xregion/Region.c
new file mode 100644
index 00000000..bf2d123b
--- /dev/null
+++ b/Xregion/Region.c
@@ -0,0 +1,1687 @@
+/* $Xorg: Region.c,v 1.6 2001/02/09 02:03:35 xorgcvs Exp $ */
+/************************************************************************
+
+Copyright 1987, 1988, 1998 The Open Group
+
+Permission to use, copy, modify, distribute, and sell this software and its
+documentation for any purpose is hereby granted without fee, provided that
+the above copyright notice appear in all copies and that both that
+copyright notice and this permission notice appear in supporting
+documentation.
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+Except as contained in this notice, the name of The Open Group shall not be
+used in advertising or otherwise to promote the sale, use or other dealings
+in this Software without prior written authorization from The Open Group.
+
+
+Copyright 1987, 1988 by Digital Equipment Corporation, Maynard, Massachusetts.
+
+ All Rights Reserved
+
+Permission to use, copy, modify, and distribute this software and its
+documentation for any purpose and without fee is hereby granted,
+provided that the above copyright notice appear in all copies and that
+both that copyright notice and this permission notice appear in
+supporting documentation, and that the name of Digital not be
+used in advertising or publicity pertaining to distribution of the
+software without specific, written prior permission.
+
+DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
+ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
+DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
+ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
+WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
+ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
+SOFTWARE.
+
+************************************************************************/
+/* $XFree86: xc/lib/X11/Region.c,v 1.8 2001/12/14 19:54:05 dawes Exp $ */
+/*
+ * The functions in this file implement the Region abstraction, similar to one
+ * used in the X11 sample server. A Region is simply an area, as the name
+ * implies, and is implemented as a "y-x-banded" array of rectangles. To
+ * explain: Each Region is made up of a certain number of rectangles sorted
+ * by y coordinate first, and then by x coordinate.
+ *
+ * Furthermore, the rectangles are banded such that every rectangle with a
+ * given upper-left y coordinate (y1) will have the same lower-right y
+ * coordinate (y2) and vice versa. If a rectangle has scanlines in a band, it
+ * will span the entire vertical distance of the band. This means that some
+ * areas that could be merged into a taller rectangle will be represented as
+ * several shorter rectangles to account for shorter rectangles to its left
+ * or right but within its "vertical scope".
+ *
+ * An added constraint on the rectangles is that they must cover as much
+ * horizontal area as possible. E.g. no two rectangles in a band are allowed
+ * to touch.
+ *
+ * Whenever possible, bands will be merged together to cover a greater vertical
+ * distance (and thus reduce the number of rectangles). Two bands can be merged
+ * only if the bottom of one touches the top of the other and they have
+ * rectangles in the same places (of the same width, of course). This maintains
+ * the y-x-banding that's so nice to have...
+ */
+
+#include "Xregion.h"
+//#include "Xlibint.h"
+//#include "Xutil.h"
+#include "region.h"
+//#include "poly.h"
+
+#ifdef DEBUG
+#include <stdio.h>
+#define assert(expr) {if (!(expr)) fprintf(stderr,\
+"Assertion failed file %s, line %d: expr\n", __FILE__, __LINE__); }
+#else
+#define assert(expr)
+#endif
+
+typedef void (*voidProcp)();
+
+static void miRegionOp();
+/* Create a new empty region */
+Region
+XCreateRegion()
+{
+ Region temp;
+
+ if (! (temp = ( Region )Xmalloc( (unsigned) sizeof( REGION ))))
+ return (Region) NULL;
+ if (! (temp->rects = ( BOX * )Xmalloc( (unsigned) sizeof( BOX )))) {
+ Xfree((char *) temp);
+ return (Region) NULL;
+ }
+ temp->numRects = 0;
+ temp->extents.x1 = 0;
+ temp->extents.y1 = 0;
+ temp->extents.x2 = 0;
+ temp->extents.y2 = 0;
+ temp->size = 1;
+ return( temp );
+}
+
+int
+XClipBox( r, rect )
+ Region r;
+ XRectangle *rect;
+{
+ rect->x = r->extents.x1;
+ rect->y = r->extents.y1;
+ rect->width = r->extents.x2 - r->extents.x1;
+ rect->height = r->extents.y2 - r->extents.y1;
+ return 1;
+}
+
+int
+XUnionRectWithRegion(rect, source, dest)
+ register XRectangle *rect;
+ Region source, dest;
+{
+ REGION region;
+
+ if (!rect->width || !rect->height)
+ return 0;
+ region.rects = &region.extents;
+ region.numRects = 1;
+ region.extents.x1 = rect->x;
+ region.extents.y1 = rect->y;
+ region.extents.x2 = rect->x + rect->width;
+ region.extents.y2 = rect->y + rect->height;
+ region.size = 1;
+
+ return XUnionRegion(&region, source, dest);
+}
+
+/*-
+ *-----------------------------------------------------------------------
+ * miSetExtents --
+ * Reset the extents of a region to what they should be. Called by
+ * miSubtract and miIntersect b/c they can't figure it out along the
+ * way or do so easily, as miUnion can.
+ *
+ * Results:
+ * None.
+ *
+ * Side Effects:
+ * The region's 'extents' structure is overwritten.
+ *
+ *-----------------------------------------------------------------------
+ */
+static void
+miSetExtents (pReg)
+ Region pReg;
+{
+ register BoxPtr pBox,
+ pBoxEnd,
+ pExtents;
+
+ if (pReg->numRects == 0)
+ {
+ pReg->extents.x1 = 0;
+ pReg->extents.y1 = 0;
+ pReg->extents.x2 = 0;
+ pReg->extents.y2 = 0;
+ return;
+ }
+
+ pExtents = &pReg->extents;
+ pBox = pReg->rects;
+ pBoxEnd = &pBox[pReg->numRects - 1];
+
+ /*
+ * Since pBox is the first rectangle in the region, it must have the
+ * smallest y1 and since pBoxEnd is the last rectangle in the region,
+ * it must have the largest y2, because of banding. Initialize x1 and
+ * x2 from pBox and pBoxEnd, resp., as good things to initialize them
+ * to...
+ */
+ pExtents->x1 = pBox->x1;
+ pExtents->y1 = pBox->y1;
+ pExtents->x2 = pBoxEnd->x2;
+ pExtents->y2 = pBoxEnd->y2;
+
+ assert(pExtents->y1 < pExtents->y2);
+ while (pBox <= pBoxEnd)
+ {
+ if (pBox->x1 < pExtents->x1)
+ {
+ pExtents->x1 = pBox->x1;
+ }
+ if (pBox->x2 > pExtents->x2)
+ {
+ pExtents->x2 = pBox->x2;
+ }
+ pBox++;
+ }
+ assert(pExtents->x1 < pExtents->x2);
+}
+
+extern void _XSetClipRectangles();
+
+#if 0
+int
+XSetRegion( dpy, gc, r )
+ Display *dpy;
+ GC gc;
+ register Region r;
+{
+ register int i;
+ register XRectangle *xr, *pr;
+ register BOX *pb;
+ unsigned long total;
+
+ LockDisplay (dpy);
+ total = r->numRects * sizeof (XRectangle);
+ if ((xr = (XRectangle *) _XAllocTemp(dpy, total))) {
+ for (pr = xr, pb = r->rects, i = r->numRects; --i >= 0; pr++, pb++) {
+ pr->x = pb->x1;
+ pr->y = pb->y1;
+ pr->width = pb->x2 - pb->x1;
+ pr->height = pb->y2 - pb->y1;
+ }
+ }
+ if (xr || !r->numRects)
+ _XSetClipRectangles(dpy, gc, 0, 0, xr, r->numRects, YXBanded);
+ if (xr)
+ _XFreeTemp(dpy, (char *)xr, total);
+ UnlockDisplay(dpy);
+ SyncHandle();
+ return 1;
+}
+#endif
+
+int
+XDestroyRegion( r )
+ Region r;
+{
+ Xfree( (char *) r->rects );
+ Xfree( (char *) r );
+ return 1;
+}
+
+
+/* TranslateRegion(pRegion, x, y)
+ translates in place
+ added by raymond
+*/
+
+int
+XOffsetRegion(pRegion, x, y)
+ register Region pRegion;
+ register int x;
+ register int y;
+{
+ register int nbox;
+ register BOX *pbox;
+
+ pbox = pRegion->rects;
+ nbox = pRegion->numRects;
+
+ while(nbox--)
+ {
+ pbox->x1 += x;
+ pbox->x2 += x;
+ pbox->y1 += y;
+ pbox->y2 += y;
+ pbox++;
+ }
+ pRegion->extents.x1 += x;
+ pRegion->extents.x2 += x;
+ pRegion->extents.y1 += y;
+ pRegion->extents.y2 += y;
+ return 1;
+}
+
+/*
+ Utility procedure Compress:
+ Replace r by the region r', where
+ p in r' iff (Quantifer m <= dx) (p + m in r), and
+ Quantifier is Exists if grow is TRUE, For all if grow is FALSE, and
+ (x,y) + m = (x+m,y) if xdir is TRUE; (x,y+m) if xdir is FALSE.
+
+ Thus, if xdir is TRUE and grow is FALSE, r is replaced by the region
+ of all points p such that p and the next dx points on the same
+ horizontal scan line are all in r. We do this using by noting
+ that p is the head of a run of length 2^i + k iff p is the head
+ of a run of length 2^i and p+2^i is the head of a run of length
+ k. Thus, the loop invariant: s contains the region corresponding
+ to the runs of length shift. r contains the region corresponding
+ to the runs of length 1 + dxo & (shift-1), where dxo is the original
+ value of dx. dx = dxo & ~(shift-1). As parameters, s and t are
+ scratch regions, so that we don't have to allocate them on every
+ call.
+*/
+
+#define ZOpRegion(a,b,c) if (grow) XUnionRegion(a,b,c); \
+ else XIntersectRegion(a,b,c)
+#define ZShiftRegion(a,b) if (xdir) XOffsetRegion(a,b,0); \
+ else XOffsetRegion(a,0,b)
+#define ZCopyRegion(a,b) XUnionRegion(a,a,b)
+
+static void
+Compress(r, s, t, dx, xdir, grow)
+ Region r, s, t;
+ register unsigned dx;
+ register int xdir, grow;
+{
+ register unsigned shift = 1;
+
+ ZCopyRegion(r, s);
+ while (dx) {
+ if (dx & shift) {
+ ZShiftRegion(r, -(int)shift);
+ ZOpRegion(r, s, r);
+ dx -= shift;
+ if (!dx) break;
+ }
+ ZCopyRegion(s, t);
+ ZShiftRegion(s, -(int)shift);
+ ZOpRegion(s, t, s);
+ shift <<= 1;
+ }
+}
+
+#undef ZOpRegion
+#undef ZShiftRegion
+#undef ZCopyRegion
+
+int
+XShrinkRegion(r, dx, dy)
+ Region r;
+ int dx, dy;
+{
+ Region s, t;
+ int grow;
+
+ if (!dx && !dy) return 0;
+ if ((! (s = XCreateRegion())) || (! (t = XCreateRegion()))) return 0;
+ if ((grow = (dx < 0))) dx = -dx;
+ if (dx) Compress(r, s, t, (unsigned) 2*dx, TRUE, grow);
+ if ((grow = (dy < 0))) dy = -dy;
+ if (dy) Compress(r, s, t, (unsigned) 2*dy, FALSE, grow);
+ XOffsetRegion(r, dx, dy);
+ XDestroyRegion(s);
+ XDestroyRegion(t);
+ return 0;
+}
+
+#ifdef notdef
+/***********************************************************
+ * Bop down the array of rects until we have passed
+ * scanline y. numRects is the size of the array.
+ ***********************************************************/
+
+static BOX
+*IndexRects(rects, numRects, y)
+ register BOX *rects;
+ register int numRects;
+ register int y;
+{
+ while ((numRects--) && (rects->y2 <= y))
+ rects++;
+ return(rects);
+}
+#endif
+
+/*======================================================================
+ * Region Intersection
+ *====================================================================*/
+/*-
+ *-----------------------------------------------------------------------
+ * miIntersectO --
+ * Handle an overlapping band for miIntersect.
+ *
+ * Results:
+ * None.
+ *
+ * Side Effects:
+ * Rectangles may be added to the region.
+ *
+ *-----------------------------------------------------------------------
+ */
+/* static void*/
+static int
+miIntersectO (pReg, r1, r1End, r2, r2End, y1, y2)
+ register Region pReg;
+ register BoxPtr r1;
+ BoxPtr r1End;
+ register BoxPtr r2;
+ BoxPtr r2End;
+ short y1;
+ short y2;
+{
+ register short x1;
+ register short x2;
+ register BoxPtr pNextRect;
+
+ pNextRect = &pReg->rects[pReg->numRects];
+
+ while ((r1 != r1End) && (r2 != r2End))
+ {
+ x1 = max(r1->x1,r2->x1);
+ x2 = min(r1->x2,r2->x2);
+
+ /*
+ * If there's any overlap between the two rectangles, add that
+ * overlap to the new region.
+ * There's no need to check for subsumption because the only way
+ * such a need could arise is if some region has two rectangles
+ * right next to each other. Since that should never happen...
+ */
+ if (x1 < x2)
+ {
+ assert(y1<y2);
+
+ MEMCHECK(pReg, pNextRect, pReg->rects);
+ pNextRect->x1 = x1;
+ pNextRect->y1 = y1;
+ pNextRect->x2 = x2;
+ pNextRect->y2 = y2;
+ pReg->numRects += 1;
+ pNextRect++;
+ assert(pReg->numRects <= pReg->size);
+ }
+
+ /*
+ * Need to advance the pointers. Shift the one that extends
+ * to the right the least, since the other still has a chance to
+ * overlap with that region's next rectangle, if you see what I mean.
+ */
+ if (r1->x2 < r2->x2)
+ {
+ r1++;
+ }
+ else if (r2->x2 < r1->x2)
+ {
+ r2++;
+ }
+ else
+ {
+ r1++;
+ r2++;
+ }
+ }
+ return 0; /* lint */
+}
+
+int
+XIntersectRegion(reg1, reg2, newReg)
+ Region reg1;
+ Region reg2; /* source regions */
+ register Region newReg; /* destination Region */
+{
+ /* check for trivial reject */
+ if ( (!(reg1->numRects)) || (!(reg2->numRects)) ||
+ (!EXTENTCHECK(&reg1->extents, &reg2->extents)))
+ newReg->numRects = 0;
+ else
+ miRegionOp (newReg, reg1, reg2,
+ (voidProcp) miIntersectO, (voidProcp) NULL, (voidProcp) NULL);
+
+ /*
+ * Can't alter newReg's extents before we call miRegionOp because
+ * it might be one of the source regions and miRegionOp depends
+ * on the extents of those regions being the same. Besides, this
+ * way there's no checking against rectangles that will be nuked
+ * due to coalescing, so we have to examine fewer rectangles.
+ */
+ miSetExtents(newReg);
+ return 1;
+}
+
+static void
+miRegionCopy(dstrgn, rgn)
+ register Region dstrgn;
+ register Region rgn;
+
+{
+ if (dstrgn != rgn) /* don't want to copy to itself */
+ {
+ if (dstrgn->size < rgn->numRects)
+ {
+ if (dstrgn->rects)
+ {
+ BOX *prevRects = dstrgn->rects;
+
+ if (! (dstrgn->rects = (BOX *)
+ Xrealloc((char *) dstrgn->rects,
+ (unsigned) rgn->numRects * (sizeof(BOX))))) {
+ Xfree(prevRects);
+ return;
+ }
+ }
+ dstrgn->size = rgn->numRects;
+ }
+ dstrgn->numRects = rgn->numRects;
+ dstrgn->extents.x1 = rgn->extents.x1;
+ dstrgn->extents.y1 = rgn->extents.y1;
+ dstrgn->extents.x2 = rgn->extents.x2;
+ dstrgn->extents.y2 = rgn->extents.y2;
+
+ memcpy((char *) dstrgn->rects, (char *) rgn->rects,
+ (int) (rgn->numRects * sizeof(BOX)));
+ }
+}
+
+#ifdef notdef
+
+/*
+ * combinRegs(newReg, reg1, reg2)
+ * if one region is above or below the other.
+*/
+
+static void
+combineRegs(newReg, reg1, reg2)
+ register Region newReg;
+ Region reg1;
+ Region reg2;
+{
+ register Region tempReg;
+ register BOX *rects;
+ register BOX *rects1;
+ register BOX *rects2;
+ register int total;
+
+ rects1 = reg1->rects;
+ rects2 = reg2->rects;
+
+ total = reg1->numRects + reg2->numRects;
+ if (! (tempReg = XCreateRegion()))
+ return;
+ tempReg->size = total;
+ /* region 1 is below region 2 */
+ if (reg1->extents.y1 > reg2->extents.y1)
+ {
+ miRegionCopy(tempReg, reg2);
+ rects = &tempReg->rects[tempReg->numRects];
+ total -= tempReg->numRects;
+ while (total--)
+ *rects++ = *rects1++;
+ }
+ else
+ {
+ miRegionCopy(tempReg, reg1);
+ rects = &tempReg->rects[tempReg->numRects];
+ total -= tempReg->numRects;
+ while (total--)
+ *rects++ = *rects2++;
+ }
+ tempReg->extents = reg1->extents;
+ tempReg->numRects = reg1->numRects + reg2->numRects;
+ EXTENTS(&reg2->extents, tempReg);
+ miRegionCopy(newReg, tempReg);
+ Xfree((char *)tempReg);
+}
+
+/*
+ * QuickCheck checks to see if it does not have to go through all the
+ * the ugly code for the region call. It returns 1 if it did all
+ * the work for Union, otherwise 0 - still work to be done.
+*/
+
+static int
+QuickCheck(newReg, reg1, reg2)
+ Region newReg, reg1, reg2;
+{
+
+ /* if unioning with itself or no rects to union with */
+ if ( (reg1 == reg2) || (!(reg1->numRects)) )
+ {
+ miRegionCopy(newReg, reg2);
+ return TRUE;
+ }
+
+ /* if nothing to union */
+ if (!(reg2->numRects))
+ {
+ miRegionCopy(newReg, reg1);
+ return TRUE;
+ }
+
+ /* could put an extent check to see if add above or below */
+
+ if ((reg1->extents.y1 >= reg2->extents.y2) ||
+ (reg2->extents.y1 >= reg1->extents.y2) )
+ {
+ combineRegs(newReg, reg1, reg2);
+ return TRUE;
+ }
+ return FALSE;
+}
+
+/* TopRects(rects, reg1, reg2)
+ * N.B. We now assume that reg1 and reg2 intersect. Therefore we are
+ * NOT checking in the two while loops for stepping off the end of the
+ * region.
+ */
+
+static int
+TopRects(newReg, rects, reg1, reg2, FirstRect)
+ register Region newReg;
+ register BOX *rects;
+ register Region reg1;
+ register Region reg2;
+ BOX *FirstRect;
+{
+ register BOX *tempRects;
+
+ /* need to add some rects from region 1 */
+ if (reg1->extents.y1 < reg2->extents.y1)
+ {
+ tempRects = reg1->rects;
+ while(tempRects->y1 < reg2->extents.y1)
+ {
+ MEMCHECK(newReg, rects, FirstRect);
+ ADDRECTNOX(newReg,rects, tempRects->x1, tempRects->y1,
+ tempRects->x2, MIN(tempRects->y2, reg2->extents.y1));
+ tempRects++;
+ }
+ }
+ /* need to add some rects from region 2 */
+ if (reg2->extents.y1 < reg1->extents.y1)
+ {
+ tempRects = reg2->rects;
+ while (tempRects->y1 < reg1->extents.y1)
+ {
+ MEMCHECK(newReg, rects, FirstRect);
+ ADDRECTNOX(newReg, rects, tempRects->x1,tempRects->y1,
+ tempRects->x2, MIN(tempRects->y2, reg1->extents.y1));
+ tempRects++;
+ }
+ }
+ return 1;
+}
+#endif
+
+/*======================================================================
+ * Generic Region Operator
+ *====================================================================*/
+
+/*-
+ *-----------------------------------------------------------------------
+ * miCoalesce --
+ * Attempt to merge the boxes in the current band with those in the
+ * previous one. Used only by miRegionOp.
+ *
+ * Results:
+ * The new index for the previous band.
+ *
+ * Side Effects:
+ * If coalescing takes place:
+ * - rectangles in the previous band will have their y2 fields
+ * altered.
+ * - pReg->numRects will be decreased.
+ *
+ *-----------------------------------------------------------------------
+ */
+/* static int*/
+static int
+miCoalesce (pReg, prevStart, curStart)
+ register Region pReg; /* Region to coalesce */
+ int prevStart; /* Index of start of previous band */
+ int curStart; /* Index of start of current band */
+{
+ register BoxPtr pPrevBox; /* Current box in previous band */
+ register BoxPtr pCurBox; /* Current box in current band */
+ register BoxPtr pRegEnd; /* End of region */
+ int curNumRects; /* Number of rectangles in current
+ * band */
+ int prevNumRects; /* Number of rectangles in previous
+ * band */
+ int bandY1; /* Y1 coordinate for current band */
+
+ pRegEnd = &pReg->rects[pReg->numRects];
+
+ pPrevBox = &pReg->rects[prevStart];
+ prevNumRects = curStart - prevStart;
+
+ /*
+ * Figure out how many rectangles are in the current band. Have to do
+ * this because multiple bands could have been added in miRegionOp
+ * at the end when one region has been exhausted.
+ */
+ pCurBox = &pReg->rects[curStart];
+ bandY1 = pCurBox->y1;
+ for (curNumRects = 0;
+ (pCurBox != pRegEnd) && (pCurBox->y1 == bandY1);
+ curNumRects++)
+ {
+ pCurBox++;
+ }
+
+ if (pCurBox != pRegEnd)
+ {
+ /*
+ * If more than one band was added, we have to find the start
+ * of the last band added so the next coalescing job can start
+ * at the right place... (given when multiple bands are added,
+ * this may be pointless -- see above).
+ */
+ pRegEnd--;
+ while (pRegEnd[-1].y1 == pRegEnd->y1)
+ {
+ pRegEnd--;
+ }
+ curStart = pRegEnd - pReg->rects;
+ pRegEnd = pReg->rects + pReg->numRects;
+ }
+
+ if ((curNumRects == prevNumRects) && (curNumRects != 0)) {
+ pCurBox -= curNumRects;
+ /*
+ * The bands may only be coalesced if the bottom of the previous
+ * matches the top scanline of the current.
+ */
+ if (pPrevBox->y2 == pCurBox->y1)
+ {
+ /*
+ * Make sure the bands have boxes in the same places. This
+ * assumes that boxes have been added in such a way that they
+ * cover the most area possible. I.e. two boxes in a band must
+ * have some horizontal space between them.
+ */
+ do
+ {
+ if ((pPrevBox->x1 != pCurBox->x1) ||
+ (pPrevBox->x2 != pCurBox->x2))
+ {
+ /*
+ * The bands don't line up so they can't be coalesced.
+ */
+ return (curStart);
+ }
+ pPrevBox++;
+ pCurBox++;
+ prevNumRects -= 1;
+ } while (prevNumRects != 0);
+
+ pReg->numRects -= curNumRects;
+ pCurBox -= curNumRects;
+ pPrevBox -= curNumRects;
+
+ /*
+ * The bands may be merged, so set the bottom y of each box
+ * in the previous band to that of the corresponding box in
+ * the current band.
+ */
+ do
+ {
+ pPrevBox->y2 = pCurBox->y2;
+ pPrevBox++;
+ pCurBox++;
+ curNumRects -= 1;
+ } while (curNumRects != 0);
+
+ /*
+ * If only one band was added to the region, we have to backup
+ * curStart to the start of the previous band.
+ *
+ * If more than one band was added to the region, copy the
+ * other bands down. The assumption here is that the other bands
+ * came from the same region as the current one and no further
+ * coalescing can be done on them since it's all been done
+ * already... curStart is already in the right place.
+ */
+ if (pCurBox == pRegEnd)
+ {
+ curStart = prevStart;
+ }
+ else
+ {
+ do
+ {
+ *pPrevBox++ = *pCurBox++;
+ } while (pCurBox != pRegEnd);
+ }
+
+ }
+ }
+ return (curStart);
+}
+
+/*-
+ *-----------------------------------------------------------------------
+ * miRegionOp --
+ * Apply an operation to two regions. Called by miUnion, miInverse,
+ * miSubtract, miIntersect...
+ *
+ * Results:
+ * None.
+ *
+ * Side Effects:
+ * The new region is overwritten.
+ *
+ * Notes:
+ * The idea behind this function is to view the two regions as sets.
+ * Together they cover a rectangle of area that this function divides
+ * into horizontal bands where points are covered only by one region
+ * or by both. For the first case, the nonOverlapFunc is called with
+ * each the band and the band's upper and lower extents. For the
+ * second, the overlapFunc is called to process the entire band. It
+ * is responsible for clipping the rectangles in the band, though
+ * this function provides the boundaries.
+ * At the end of each band, the new region is coalesced, if possible,
+ * to reduce the number of rectangles in the region.
+ *
+ *-----------------------------------------------------------------------
+ */
+/* static void*/
+static void
+miRegionOp(newReg, reg1, reg2, overlapFunc, nonOverlap1Func, nonOverlap2Func)
+ register Region newReg; /* Place to store result */
+ Region reg1; /* First region in operation */
+ Region reg2; /* 2d region in operation */
+ void (*overlapFunc)(); /* Function to call for over-
+ * lapping bands */
+ void (*nonOverlap1Func)(); /* Function to call for non-
+ * overlapping bands in region
+ * 1 */
+ void (*nonOverlap2Func)(); /* Function to call for non-
+ * overlapping bands in region
+ * 2 */
+{
+ register BoxPtr r1; /* Pointer into first region */
+ register BoxPtr r2; /* Pointer into 2d region */
+ BoxPtr r1End; /* End of 1st region */
+ BoxPtr r2End; /* End of 2d region */
+ register short ybot; /* Bottom of intersection */
+ register short ytop; /* Top of intersection */
+ BoxPtr oldRects; /* Old rects for newReg */
+ int prevBand; /* Index of start of
+ * previous band in newReg */
+ int curBand; /* Index of start of current
+ * band in newReg */
+ register BoxPtr r1BandEnd; /* End of current band in r1 */
+ register BoxPtr r2BandEnd; /* End of current band in r2 */
+ short top; /* Top of non-overlapping
+ * band */
+ short bot; /* Bottom of non-overlapping
+ * band */
+
+ /*
+ * Initialization:
+ * set r1, r2, r1End and r2End appropriately, preserve the important
+ * parts of the destination region until the end in case it's one of
+ * the two source regions, then mark the "new" region empty, allocating
+ * another array of rectangles for it to use.
+ */
+ r1 = reg1->rects;
+ r2 = reg2->rects;
+ r1End = r1 + reg1->numRects;
+ r2End = r2 + reg2->numRects;
+
+ oldRects = newReg->rects;
+
+ EMPTY_REGION(newReg);
+
+ /*
+ * Allocate a reasonable number of rectangles for the new region. The idea
+ * is to allocate enough so the individual functions don't need to
+ * reallocate and copy the array, which is time consuming, yet we don't
+ * have to worry about using too much memory. I hope to be able to
+ * nuke the Xrealloc() at the end of this function eventually.
+ */
+ newReg->size = max(reg1->numRects,reg2->numRects) * 2;
+
+ if (! (newReg->rects = (BoxPtr)
+ Xmalloc ((unsigned) (sizeof(BoxRec) * newReg->size)))) {
+ newReg->size = 0;
+ return;
+ }
+
+ /*
+ * Initialize ybot and ytop.
+ * In the upcoming loop, ybot and ytop serve different functions depending
+ * on whether the band being handled is an overlapping or non-overlapping
+ * band.
+ * In the case of a non-overlapping band (only one of the regions
+ * has points in the band), ybot is the bottom of the most recent
+ * intersection and thus clips the top of the rectangles in that band.
+ * ytop is the top of the next intersection between the two regions and
+ * serves to clip the bottom of the rectangles in the current band.
+ * For an overlapping band (where the two regions intersect), ytop clips
+ * the top of the rectangles of both regions and ybot clips the bottoms.
+ */
+ if (reg1->extents.y1 < reg2->extents.y1)
+ ybot = reg1->extents.y1;
+ else
+ ybot = reg2->extents.y1;
+
+ /*
+ * prevBand serves to mark the start of the previous band so rectangles
+ * can be coalesced into larger rectangles. qv. miCoalesce, above.
+ * In the beginning, there is no previous band, so prevBand == curBand
+ * (curBand is set later on, of course, but the first band will always
+ * start at index 0). prevBand and curBand must be indices because of
+ * the possible expansion, and resultant moving, of the new region's
+ * array of rectangles.
+ */
+ prevBand = 0;
+
+ do
+ {
+ curBand = newReg->numRects;
+
+ /*
+ * This algorithm proceeds one source-band (as opposed to a
+ * destination band, which is determined by where the two regions
+ * intersect) at a time. r1BandEnd and r2BandEnd serve to mark the
+ * rectangle after the last one in the current band for their
+ * respective regions.
+ */
+ r1BandEnd = r1;
+ while ((r1BandEnd != r1End) && (r1BandEnd->y1 == r1->y1))
+ {
+ r1BandEnd++;
+ }
+
+ r2BandEnd = r2;
+ while ((r2BandEnd != r2End) && (r2BandEnd->y1 == r2->y1))
+ {
+ r2BandEnd++;
+ }
+
+ /*
+ * First handle the band that doesn't intersect, if any.
+ *
+ * Note that attention is restricted to one band in the
+ * non-intersecting region at once, so if a region has n
+ * bands between the current position and the next place it overlaps
+ * the other, this entire loop will be passed through n times.
+ */
+ if (r1->y1 < r2->y1)
+ {
+ top = max(r1->y1,ybot);
+ bot = min(r1->y2,r2->y1);
+
+ if ((top != bot) && (nonOverlap1Func != (void (*)())NULL))
+ {
+ (* nonOverlap1Func) (newReg, r1, r1BandEnd, top, bot);
+ }
+
+ ytop = r2->y1;
+ }
+ else if (r2->y1 < r1->y1)
+ {
+ top = max(r2->y1,ybot);
+ bot = min(r2->y2,r1->y1);
+
+ if ((top != bot) && (nonOverlap2Func != (void (*)())NULL))
+ {
+ (* nonOverlap2Func) (newReg, r2, r2BandEnd, top, bot);
+ }
+
+ ytop = r1->y1;
+ }
+ else
+ {
+ ytop = r1->y1;
+ }
+
+ /*
+ * If any rectangles got added to the region, try and coalesce them
+ * with rectangles from the previous band. Note we could just do
+ * this test in miCoalesce, but some machines incur a not
+ * inconsiderable cost for function calls, so...
+ */
+ if (newReg->numRects != curBand)
+ {
+ prevBand = miCoalesce (newReg, prevBand, curBand);
+ }
+
+ /*
+ * Now see if we've hit an intersecting band. The two bands only
+ * intersect if ybot > ytop
+ */
+ ybot = min(r1->y2, r2->y2);
+ curBand = newReg->numRects;
+ if (ybot > ytop)
+ {
+ (* overlapFunc) (newReg, r1, r1BandEnd, r2, r2BandEnd, ytop, ybot);
+
+ }
+
+ if (newReg->numRects != curBand)
+ {
+ prevBand = miCoalesce (newReg, prevBand, curBand);
+ }
+
+ /*
+ * If we've finished with a band (y2 == ybot) we skip forward
+ * in the region to the next band.
+ */
+ if (r1->y2 == ybot)
+ {
+ r1 = r1BandEnd;
+ }
+ if (r2->y2 == ybot)
+ {
+ r2 = r2BandEnd;
+ }
+ } while ((r1 != r1End) && (r2 != r2End));
+
+ /*
+ * Deal with whichever region still has rectangles left.
+ */
+ curBand = newReg->numRects;
+ if (r1 != r1End)
+ {
+ if (nonOverlap1Func != (void (*)())NULL)
+ {
+ do
+ {
+ r1BandEnd = r1;
+ while ((r1BandEnd < r1End) && (r1BandEnd->y1 == r1->y1))
+ {
+ r1BandEnd++;
+ }
+ (* nonOverlap1Func) (newReg, r1, r1BandEnd,
+ max(r1->y1,ybot), r1->y2);
+ r1 = r1BandEnd;
+ } while (r1 != r1End);
+ }
+ }
+ else if ((r2 != r2End) && (nonOverlap2Func != (void (*)())NULL))
+ {
+ do
+ {
+ r2BandEnd = r2;
+ while ((r2BandEnd < r2End) && (r2BandEnd->y1 == r2->y1))
+ {
+ r2BandEnd++;
+ }
+ (* nonOverlap2Func) (newReg, r2, r2BandEnd,
+ max(r2->y1,ybot), r2->y2);
+ r2 = r2BandEnd;
+ } while (r2 != r2End);
+ }
+
+ if (newReg->numRects != curBand)
+ {
+ (void) miCoalesce (newReg, prevBand, curBand);
+ }
+
+ /*
+ * A bit of cleanup. To keep regions from growing without bound,
+ * we shrink the array of rectangles to match the new number of
+ * rectangles in the region. This never goes to 0, however...
+ *
+ * Only do this stuff if the number of rectangles allocated is more than
+ * twice the number of rectangles in the region (a simple optimization...).
+ */
+ if (newReg->numRects < (newReg->size >> 1))
+ {
+ if (REGION_NOT_EMPTY(newReg))
+ {
+ BoxPtr prev_rects = newReg->rects;
+ newReg->size = newReg->numRects;
+ newReg->rects = (BoxPtr) Xrealloc ((char *) newReg->rects,
+ (unsigned) (sizeof(BoxRec) * newReg->size));
+ if (! newReg->rects)
+ newReg->rects = prev_rects;
+ }
+ else
+ {
+ /*
+ * No point in doing the extra work involved in an Xrealloc if
+ * the region is empty
+ */
+ newReg->size = 1;
+ Xfree((char *) newReg->rects);
+ newReg->rects = (BoxPtr) Xmalloc(sizeof(BoxRec));
+ }
+ }
+ Xfree ((char *) oldRects);
+ return;
+}
+
+
+/*======================================================================
+ * Region Union
+ *====================================================================*/
+
+/*-
+ *-----------------------------------------------------------------------
+ * miUnionNonO --
+ * Handle a non-overlapping band for the union operation. Just
+ * Adds the rectangles into the region. Doesn't have to check for
+ * subsumption or anything.
+ *
+ * Results:
+ * None.
+ *
+ * Side Effects:
+ * pReg->numRects is incremented and the final rectangles overwritten
+ * with the rectangles we're passed.
+ *
+ *-----------------------------------------------------------------------
+ */
+/* static void*/
+static int
+miUnionNonO (pReg, r, rEnd, y1, y2)
+ register Region pReg;
+ register BoxPtr r;
+ BoxPtr rEnd;
+ register short y1;
+ register short y2;
+{
+ register BoxPtr pNextRect;
+
+ pNextRect = &pReg->rects[pReg->numRects];
+
+ assert(y1 < y2);
+
+ while (r != rEnd)
+ {
+ assert(r->x1 < r->x2);
+ MEMCHECK(pReg, pNextRect, pReg->rects);
+ pNextRect->x1 = r->x1;
+ pNextRect->y1 = y1;
+ pNextRect->x2 = r->x2;
+ pNextRect->y2 = y2;
+ pReg->numRects += 1;
+ pNextRect++;
+
+ assert(pReg->numRects<=pReg->size);
+ r++;
+ }
+ return 0; /* lint */
+}
+
+
+/*-
+ *-----------------------------------------------------------------------
+ * miUnionO --
+ * Handle an overlapping band for the union operation. Picks the
+ * left-most rectangle each time and merges it into the region.
+ *
+ * Results:
+ * None.
+ *
+ * Side Effects:
+ * Rectangles are overwritten in pReg->rects and pReg->numRects will
+ * be changed.
+ *
+ *-----------------------------------------------------------------------
+ */
+
+/* static void*/
+static int
+miUnionO (pReg, r1, r1End, r2, r2End, y1, y2)
+ register Region pReg;
+ register BoxPtr r1;
+ BoxPtr r1End;
+ register BoxPtr r2;
+ BoxPtr r2End;
+ register short y1;
+ register short y2;
+{
+ register BoxPtr pNextRect;
+
+ pNextRect = &pReg->rects[pReg->numRects];
+
+#define MERGERECT(r) \
+ if ((pReg->numRects != 0) && \
+ (pNextRect[-1].y1 == y1) && \
+ (pNextRect[-1].y2 == y2) && \
+ (pNextRect[-1].x2 >= r->x1)) \
+ { \
+ if (pNextRect[-1].x2 < r->x2) \
+ { \
+ pNextRect[-1].x2 = r->x2; \
+ assert(pNextRect[-1].x1<pNextRect[-1].x2); \
+ } \
+ } \
+ else \
+ { \
+ MEMCHECK(pReg, pNextRect, pReg->rects); \
+ pNextRect->y1 = y1; \
+ pNextRect->y2 = y2; \
+ pNextRect->x1 = r->x1; \
+ pNextRect->x2 = r->x2; \
+ pReg->numRects += 1; \
+ pNextRect += 1; \
+ } \
+ assert(pReg->numRects<=pReg->size);\
+ r++;
+
+ assert (y1<y2);
+ while ((r1 != r1End) && (r2 != r2End))
+ {
+ if (r1->x1 < r2->x1)
+ {
+ MERGERECT(r1);
+ }
+ else
+ {
+ MERGERECT(r2);
+ }
+ }
+
+ if (r1 != r1End)
+ {
+ do
+ {
+ MERGERECT(r1);
+ } while (r1 != r1End);
+ }
+ else while (r2 != r2End)
+ {
+ MERGERECT(r2);
+ }
+ return 0; /* lint */
+}
+
+int
+XUnionRegion(reg1, reg2, newReg)
+ Region reg1;
+ Region reg2; /* source regions */
+ Region newReg; /* destination Region */
+{
+ /* checks all the simple cases */
+
+ /*
+ * Region 1 and 2 are the same or region 1 is empty
+ */
+ if ( (reg1 == reg2) || (!(reg1->numRects)) )
+ {
+ if (newReg != reg2)
+ miRegionCopy(newReg, reg2);
+ return 1;
+ }
+
+ /*
+ * if nothing to union (region 2 empty)
+ */
+ if (!(reg2->numRects))
+ {
+ if (newReg != reg1)
+ miRegionCopy(newReg, reg1);
+ return 1;
+ }
+
+ /*
+ * Region 1 completely subsumes region 2
+ */
+ if ((reg1->numRects == 1) &&
+ (reg1->extents.x1 <= reg2->extents.x1) &&
+ (reg1->extents.y1 <= reg2->extents.y1) &&
+ (reg1->extents.x2 >= reg2->extents.x2) &&
+ (reg1->extents.y2 >= reg2->extents.y2))
+ {
+ if (newReg != reg1)
+ miRegionCopy(newReg, reg1);
+ return 1;
+ }
+
+ /*
+ * Region 2 completely subsumes region 1
+ */
+ if ((reg2->numRects == 1) &&
+ (reg2->extents.x1 <= reg1->extents.x1) &&
+ (reg2->extents.y1 <= reg1->extents.y1) &&
+ (reg2->extents.x2 >= reg1->extents.x2) &&
+ (reg2->extents.y2 >= reg1->extents.y2))
+ {
+ if (newReg != reg2)
+ miRegionCopy(newReg, reg2);
+ return 1;
+ }
+
+ miRegionOp (newReg, reg1, reg2, (voidProcp) miUnionO,
+ (voidProcp) miUnionNonO, (voidProcp) miUnionNonO);
+
+ newReg->extents.x1 = min(reg1->extents.x1, reg2->extents.x1);
+ newReg->extents.y1 = min(reg1->extents.y1, reg2->extents.y1);
+ newReg->extents.x2 = max(reg1->extents.x2, reg2->extents.x2);
+ newReg->extents.y2 = max(reg1->extents.y2, reg2->extents.y2);
+
+ return 1;
+}
+
+
+/*======================================================================
+ * Region Subtraction
+ *====================================================================*/
+
+/*-
+ *-----------------------------------------------------------------------
+ * miSubtractNonO --
+ * Deal with non-overlapping band for subtraction. Any parts from
+ * region 2 we discard. Anything from region 1 we add to the region.
+ *
+ * Results:
+ * None.
+ *
+ * Side Effects:
+ * pReg may be affected.
+ *
+ *-----------------------------------------------------------------------
+ */
+/* static void*/
+static int
+miSubtractNonO1 (pReg, r, rEnd, y1, y2)
+ register Region pReg;
+ register BoxPtr r;
+ BoxPtr rEnd;
+ register short y1;
+ register short y2;
+{
+ register BoxPtr pNextRect;
+
+ pNextRect = &pReg->rects[pReg->numRects];
+
+ assert(y1<y2);
+
+ while (r != rEnd)
+ {
+ assert(r->x1<r->x2);
+ MEMCHECK(pReg, pNextRect, pReg->rects);
+ pNextRect->x1 = r->x1;
+ pNextRect->y1 = y1;
+ pNextRect->x2 = r->x2;
+ pNextRect->y2 = y2;
+ pReg->numRects += 1;
+ pNextRect++;
+
+ assert(pReg->numRects <= pReg->size);
+
+ r++;
+ }
+ return 0; /* lint */
+}
+
+/*-
+ *-----------------------------------------------------------------------
+ * miSubtractO --
+ * Overlapping band subtraction. x1 is the left-most point not yet
+ * checked.
+ *
+ * Results:
+ * None.
+ *
+ * Side Effects:
+ * pReg may have rectangles added to it.
+ *
+ *-----------------------------------------------------------------------
+ */
+/* static void*/
+static int
+miSubtractO (pReg, r1, r1End, r2, r2End, y1, y2)
+ register Region pReg;
+ register BoxPtr r1;
+ BoxPtr r1End;
+ register BoxPtr r2;
+ BoxPtr r2End;
+ register short y1;
+ register short y2;
+{
+ register BoxPtr pNextRect;
+ register int x1;
+
+ x1 = r1->x1;
+
+ assert(y1<y2);
+ pNextRect = &pReg->rects[pReg->numRects];
+
+ while ((r1 != r1End) && (r2 != r2End))
+ {
+ if (r2->x2 <= x1)
+ {
+ /*
+ * Subtrahend missed the boat: go to next subtrahend.
+ */
+ r2++;
+ }
+ else if (r2->x1 <= x1)
+ {
+ /*
+ * Subtrahend preceeds minuend: nuke left edge of minuend.
+ */
+ x1 = r2->x2;
+ if (x1 >= r1->x2)
+ {
+ /*
+ * Minuend completely covered: advance to next minuend and
+ * reset left fence to edge of new minuend.
+ */
+ r1++;
+ if (r1 != r1End)
+ x1 = r1->x1;
+ }
+ else
+ {
+ /*
+ * Subtrahend now used up since it doesn't extend beyond
+ * minuend
+ */
+ r2++;
+ }
+ }
+ else if (r2->x1 < r1->x2)
+ {
+ /*
+ * Left part of subtrahend covers part of minuend: add uncovered
+ * part of minuend to region and skip to next subtrahend.
+ */
+ assert(x1<r2->x1);
+ MEMCHECK(pReg, pNextRect, pReg->rects);
+ pNextRect->x1 = x1;
+ pNextRect->y1 = y1;
+ pNextRect->x2 = r2->x1;
+ pNextRect->y2 = y2;
+ pReg->numRects += 1;
+ pNextRect++;
+
+ assert(pReg->numRects<=pReg->size);
+
+ x1 = r2->x2;
+ if (x1 >= r1->x2)
+ {
+ /*
+ * Minuend used up: advance to new...
+ */
+ r1++;
+ if (r1 != r1End)
+ x1 = r1->x1;
+ }
+ else
+ {
+ /*
+ * Subtrahend used up
+ */
+ r2++;
+ }
+ }
+ else
+ {
+ /*
+ * Minuend used up: add any remaining piece before advancing.
+ */
+ if (r1->x2 > x1)
+ {
+ MEMCHECK(pReg, pNextRect, pReg->rects);
+ pNextRect->x1 = x1;
+ pNextRect->y1 = y1;
+ pNextRect->x2 = r1->x2;
+ pNextRect->y2 = y2;
+ pReg->numRects += 1;
+ pNextRect++;
+ assert(pReg->numRects<=pReg->size);
+ }
+ r1++;
+ if (r1 != r1End)
+ x1 = r1->x1;
+ }
+ }
+
+ /*
+ * Add remaining minuend rectangles to region.
+ */
+ while (r1 != r1End)
+ {
+ assert(x1<r1->x2);
+ MEMCHECK(pReg, pNextRect, pReg->rects);
+ pNextRect->x1 = x1;
+ pNextRect->y1 = y1;
+ pNextRect->x2 = r1->x2;
+ pNextRect->y2 = y2;
+ pReg->numRects += 1;
+ pNextRect++;
+
+ assert(pReg->numRects<=pReg->size);
+
+ r1++;
+ if (r1 != r1End)
+ {
+ x1 = r1->x1;
+ }
+ }
+ return 0; /* lint */
+}
+
+/*-
+ *-----------------------------------------------------------------------
+ * miSubtract --
+ * Subtract regS from regM and leave the result in regD.
+ * S stands for subtrahend, M for minuend and D for difference.
+ *
+ * Results:
+ * TRUE.
+ *
+ * Side Effects:
+ * regD is overwritten.
+ *
+ *-----------------------------------------------------------------------
+ */
+
+int
+XSubtractRegion(regM, regS, regD)
+ Region regM;
+ Region regS;
+ register Region regD;
+{
+ /* check for trivial reject */
+ if ( (!(regM->numRects)) || (!(regS->numRects)) ||
+ (!EXTENTCHECK(&regM->extents, &regS->extents)) )
+ {
+ miRegionCopy(regD, regM);
+ return 1;
+ }
+
+ miRegionOp (regD, regM, regS, (voidProcp) miSubtractO,
+ (voidProcp) miSubtractNonO1, (voidProcp) NULL);
+
+ /*
+ * Can't alter newReg's extents before we call miRegionOp because
+ * it might be one of the source regions and miRegionOp depends
+ * on the extents of those regions being the unaltered. Besides, this
+ * way there's no checking against rectangles that will be nuked
+ * due to coalescing, so we have to examine fewer rectangles.
+ */
+ miSetExtents (regD);
+ return 1;
+}
+
+int
+XXorRegion( sra, srb, dr )
+ Region sra, srb, dr;
+{
+ Region tra, trb;
+
+ if ((! (tra = XCreateRegion())) || (! (trb = XCreateRegion())))
+ return 0;
+ (void) XSubtractRegion(sra,srb,tra);
+ (void) XSubtractRegion(srb,sra,trb);
+ (void) XUnionRegion(tra,trb,dr);
+ XDestroyRegion(tra);
+ XDestroyRegion(trb);
+ return 0;
+}
+
+/*
+ * Check to see if the region is empty. Assumes a region is passed
+ * as a parameter
+ */
+int
+XEmptyRegion( r )
+ Region r;
+{
+ if( r->numRects == 0 ) return TRUE;
+ else return FALSE;
+}
+
+/*
+ * Check to see if two regions are equal
+ */
+int
+XEqualRegion( r1, r2 )
+ Region r1, r2;
+{
+ int i;
+
+ if( r1->numRects != r2->numRects ) return FALSE;
+ else if( r1->numRects == 0 ) return TRUE;
+ else if ( r1->extents.x1 != r2->extents.x1 ) return FALSE;
+ else if ( r1->extents.x2 != r2->extents.x2 ) return FALSE;
+ else if ( r1->extents.y1 != r2->extents.y1 ) return FALSE;
+ else if ( r1->extents.y2 != r2->extents.y2 ) return FALSE;
+ else for( i=0; i < r1->numRects; i++ ) {
+ if ( r1->rects[i].x1 != r2->rects[i].x1 ) return FALSE;
+ else if ( r1->rects[i].x2 != r2->rects[i].x2 ) return FALSE;
+ else if ( r1->rects[i].y1 != r2->rects[i].y1 ) return FALSE;
+ else if ( r1->rects[i].y2 != r2->rects[i].y2 ) return FALSE;
+ }
+ return TRUE;
+}
+
+int
+XPointInRegion( pRegion, x, y )
+ Region pRegion;
+ int x, y;
+{
+ int i;
+
+ if (pRegion->numRects == 0)
+ return FALSE;
+ if (!INBOX(pRegion->extents, x, y))
+ return FALSE;
+ for (i=0; i<pRegion->numRects; i++)
+ {
+ if (INBOX (pRegion->rects[i], x, y))
+ return TRUE;
+ }
+ return FALSE;
+}
+
+int
+XRectInRegion(region, rx, ry, rwidth, rheight)
+ register Region region;
+ int rx, ry;
+ unsigned int rwidth, rheight;
+{
+ register BoxPtr pbox;
+ register BoxPtr pboxEnd;
+ Box rect;
+ register BoxPtr prect = &rect;
+ int partIn, partOut;
+
+ prect->x1 = rx;
+ prect->y1 = ry;
+ prect->x2 = rwidth + rx;
+ prect->y2 = rheight + ry;
+
+ /* this is (just) a useful optimization */
+ if ((region->numRects == 0) || !EXTENTCHECK(&region->extents, prect))
+ return(RectangleOut);
+
+ partOut = FALSE;
+ partIn = FALSE;
+
+ /* can stop when both partOut and partIn are TRUE, or we reach prect->y2 */
+ for (pbox = region->rects, pboxEnd = pbox + region->numRects;
+ pbox < pboxEnd;
+ pbox++)
+ {
+
+ if (pbox->y2 <= ry)
+ continue; /* getting up to speed or skipping remainder of band */
+
+ if (pbox->y1 > ry)
+ {
+ partOut = TRUE; /* missed part of rectangle above */
+ if (partIn || (pbox->y1 >= prect->y2))
+ break;
+ ry = pbox->y1; /* x guaranteed to be == prect->x1 */
+ }
+
+ if (pbox->x2 <= rx)
+ continue; /* not far enough over yet */
+
+ if (pbox->x1 > rx)
+ {
+ partOut = TRUE; /* missed part of rectangle to left */
+ if (partIn)
+ break;
+ }
+
+ if (pbox->x1 < prect->x2)
+ {
+ partIn = TRUE; /* definitely overlap */
+ if (partOut)
+ break;
+ }
+
+ if (pbox->x2 >= prect->x2)
+ {
+ ry = pbox->y2; /* finished with this band */
+ if (ry >= prect->y2)
+ break;
+ rx = prect->x1; /* reset x out to left again */
+ } else
+ {
+ /*
+ * Because boxes in a band are maximal width, if the first box
+ * to overlap the rectangle doesn't completely cover it in that
+ * band, the rectangle must be partially out, since some of it
+ * will be uncovered in that band. partIn will have been set true
+ * by now...
+ */
+ break;
+ }
+
+ }
+
+ return(partIn ? ((ry < prect->y2) ? RectanglePart : RectangleIn) :
+ RectangleOut);
+}
diff --git a/Xregion/Xregion.dsp b/Xregion/Xregion.dsp
new file mode 100644
index 00000000..87d04bff
--- /dev/null
+++ b/Xregion/Xregion.dsp
@@ -0,0 +1,132 @@
+# Microsoft Developer Studio Project File - Name="Xregion" - Package Owner=<4>
+# Microsoft Developer Studio Generated Build File, Format Version 6.00
+# ** DO NOT EDIT **
+
+# TARGTYPE "Win32 (x86) Static Library" 0x0104
+
+CFG=Xregion - 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 "Xregion.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 "Xregion.mak" CFG="Xregion - Win32 Debug Unicode"
+!MESSAGE
+!MESSAGE Possible choices for configuration are:
+!MESSAGE
+!MESSAGE "Xregion - Win32 Release" (based on "Win32 (x86) Static Library")
+!MESSAGE "Xregion - Win32 Debug" (based on "Win32 (x86) Static Library")
+!MESSAGE "Xregion - 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)" == "Xregion - 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"
+# 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 ".." /D "NDEBUG" /D "_LIB" /D "WIN32" /D "_MBCS" /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)" == "Xregion - 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"
+# 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 ".." /D "_DEBUG" /D "_LIB" /D "WIN32" /D "_MBCS" /FD /GZ /c
+# SUBTRACT CPP /YX
+# 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)" == "Xregion - Win32 Debug Unicode"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 1
+# PROP BASE Output_Dir "Xregion___Win32_Debug_Unicode"
+# PROP BASE Intermediate_Dir "Xregion___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"
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /I ".." /D "_DEBUG" /D "_LIB" /D "WIN32" /D "_MBCS" /FD /GZ /c
+# SUBTRACT BASE CPP /YX
+# ADD CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /I ".." /D "_LIB" /D "_DEBUG" /D "WIN32" /D "_UNICODE" /D "UNICODE" /FD /GZ /c
+# SUBTRACT CPP /YX
+# 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 "Xregion - Win32 Release"
+# Name "Xregion - Win32 Debug"
+# Name "Xregion - Win32 Debug Unicode"
+# Begin Group "Source Files"
+
+# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat"
+# Begin Source File
+
+SOURCE=.\Region.c
+# End Source File
+# End Group
+# Begin Group "Header Files"
+
+# PROP Default_Filter "h;hpp;hxx;hm;inl"
+# Begin Source File
+
+SOURCE=.\region.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\Xregion.h
+# End Source File
+# End Group
+# End Target
+# End Project
diff --git a/Xregion/Xregion.h b/Xregion/Xregion.h
new file mode 100644
index 00000000..7fa44d96
--- /dev/null
+++ b/Xregion/Xregion.h
@@ -0,0 +1,220 @@
+/* $Xorg: Xutil.h,v 1.8 2001/02/09 02:03:39 xorgcvs Exp $ */
+
+/***********************************************************
+
+Copyright 1987, 1998 The Open Group
+
+Permission to use, copy, modify, distribute, and sell this software and its
+documentation for any purpose is hereby granted without fee, provided that
+the above copyright notice appear in all copies and that both that
+copyright notice and this permission notice appear in supporting
+documentation.
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+Except as contained in this notice, the name of The Open Group shall not be
+used in advertising or otherwise to promote the sale, use or other dealings
+in this Software without prior written authorization from The Open Group.
+
+
+Copyright 1987 by Digital Equipment Corporation, Maynard, Massachusetts.
+
+ All Rights Reserved
+
+Permission to use, copy, modify, and distribute this software and its
+documentation for any purpose and without fee is hereby granted,
+provided that the above copyright notice appear in all copies and that
+both that copyright notice and this permission notice appear in
+supporting documentation, and that the name of Digital not be
+used in advertising or publicity pertaining to distribution of the
+software without specific, written prior permission.
+
+DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
+ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
+DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
+ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
+WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
+ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
+SOFTWARE.
+
+******************************************************************/
+/* $XFree86: xc/lib/X11/Xutil.h,v 3.4 2001/12/14 19:54:10 dawes Exp $ */
+
+#ifndef _XREGION_H_
+#define _XREGION_H_
+
+// - Faked defines to fool the X11 region code
+
+#include <stdlib.h>
+#include <string.h>
+
+#define Bool int
+#define Xmalloc malloc
+#define Xfree free
+#define Xrealloc realloc
+
+#ifndef max
+#define max(a,b) (((a) > (b)) ? (a) : (b))
+#endif
+
+#ifndef min
+#define min(a,b) (((a) < (b)) ? (a) : (b))
+#endif
+
+#define NeedFunctionPrototypes 1
+
+// - Cribbed from Xlib.h
+
+typedef struct {
+ short x, y;
+} XPoint;
+
+typedef struct {
+ short x, y;
+ unsigned short width, height;
+} XRectangle;
+
+/*
+ * opaque reference to Region data type
+ */
+typedef struct _XRegion *Region;
+
+/* Return values from XRectInRegion() */
+
+#define RectangleOut 0
+#define RectangleIn 1
+#define RectanglePart 2
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+extern int XClipBox(
+#if NeedFunctionPrototypes
+ Region /* r */,
+ XRectangle* /* rect_return */
+#endif
+);
+
+extern Region XCreateRegion(
+#if NeedFunctionPrototypes
+ void
+#endif
+);
+
+extern const char *XDefaultString (void);
+
+extern int XDestroyRegion(
+#if NeedFunctionPrototypes
+ Region /* r */
+#endif
+);
+
+extern int XEmptyRegion(
+#if NeedFunctionPrototypes
+ Region /* r */
+#endif
+);
+
+extern int XEqualRegion(
+#if NeedFunctionPrototypes
+ Region /* r1 */,
+ Region /* r2 */
+#endif
+);
+
+extern int XIntersectRegion(
+#if NeedFunctionPrototypes
+ Region /* sra */,
+ Region /* srb */,
+ Region /* dr_return */
+#endif
+);
+
+extern int XOffsetRegion(
+#if NeedFunctionPrototypes
+ Region /* r */,
+ int /* dx */,
+ int /* dy */
+#endif
+);
+
+extern Bool XPointInRegion(
+#if NeedFunctionPrototypes
+ Region /* r */,
+ int /* x */,
+ int /* y */
+#endif
+);
+
+extern Region XPolygonRegion(
+#if NeedFunctionPrototypes
+ XPoint* /* points */,
+ int /* n */,
+ int /* fill_rule */
+#endif
+);
+
+extern int XRectInRegion(
+#if NeedFunctionPrototypes
+ Region /* r */,
+ int /* x */,
+ int /* y */,
+ unsigned int /* width */,
+ unsigned int /* height */
+#endif
+);
+
+extern int XShrinkRegion(
+#if NeedFunctionPrototypes
+ Region /* r */,
+ int /* dx */,
+ int /* dy */
+#endif
+);
+
+extern int XSubtractRegion(
+#if NeedFunctionPrototypes
+ Region /* sra */,
+ Region /* srb */,
+ Region /* dr_return */
+#endif
+);
+
+extern int XUnionRectWithRegion(
+#if NeedFunctionPrototypes
+ XRectangle* /* rectangle */,
+ Region /* src_region */,
+ Region /* dest_region_return */
+#endif
+);
+
+extern int XUnionRegion(
+#if NeedFunctionPrototypes
+ Region /* sra */,
+ Region /* srb */,
+ Region /* dr_return */
+#endif
+);
+
+extern int XXorRegion(
+#if NeedFunctionPrototypes
+ Region /* sra */,
+ Region /* srb */,
+ Region /* dr_return */
+#endif
+);
+
+#ifdef __cplusplus
+};
+#endif
+
+#endif /* _XUTIL_H_ */
diff --git a/Xregion/region.h b/Xregion/region.h
new file mode 100644
index 00000000..2ddf12ca
--- /dev/null
+++ b/Xregion/region.h
@@ -0,0 +1,190 @@
+/* $Xorg: region.h,v 1.4 2001/02/09 02:03:40 xorgcvs Exp $ */
+/************************************************************************
+
+Copyright 1987, 1998 The Open Group
+
+Permission to use, copy, modify, distribute, and sell this software and its
+documentation for any purpose is hereby granted without fee, provided that
+the above copyright notice appear in all copies and that both that
+copyright notice and this permission notice appear in supporting
+documentation.
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+Except as contained in this notice, the name of The Open Group shall not be
+used in advertising or otherwise to promote the sale, use or other dealings
+in this Software without prior written authorization from The Open Group.
+
+
+Copyright 1987 by Digital Equipment Corporation, Maynard, Massachusetts.
+
+ All Rights Reserved
+
+Permission to use, copy, modify, and distribute this software and its
+documentation for any purpose and without fee is hereby granted,
+provided that the above copyright notice appear in all copies and that
+both that copyright notice and this permission notice appear in
+supporting documentation, and that the name of Digital not be
+used in advertising or publicity pertaining to distribution of the
+software without specific, written prior permission.
+
+DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
+ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
+DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
+ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
+WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
+ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
+SOFTWARE.
+
+************************************************************************/
+
+#ifndef _XREGION_H
+#define _XREGION_H
+
+typedef struct {
+ short x1, x2, y1, y2;
+} Box, BOX, BoxRec, *BoxPtr;
+
+typedef struct {
+ short x, y, width, height;
+}RECTANGLE, RectangleRec, *RectanglePtr;
+
+#define TRUE 1
+#define FALSE 0
+#define MAXSHORT 32767
+#define MINSHORT -MAXSHORT
+#ifndef MAX
+#define MAX(a,b) (((a) > (b)) ? (a) : (b))
+#endif
+#ifndef MIN
+#define MIN(a,b) (((a) < (b)) ? (a) : (b))
+#endif
+
+
+/*
+ * clip region
+ */
+
+typedef struct _XRegion {
+ long size;
+ long numRects;
+ BOX *rects;
+ BOX extents;
+} REGION;
+
+/* Xutil.h contains the declaration:
+ * typedef struct _XRegion *Region;
+ */
+
+/* 1 if two BOXs overlap.
+ * 0 if two BOXs do not overlap.
+ * Remember, x2 and y2 are not in the region
+ */
+#define EXTENTCHECK(r1, r2) \
+ ((r1)->x2 > (r2)->x1 && \
+ (r1)->x1 < (r2)->x2 && \
+ (r1)->y2 > (r2)->y1 && \
+ (r1)->y1 < (r2)->y2)
+
+/*
+ * update region extents
+ */
+#define EXTENTS(r,idRect){\
+ if((r)->x1 < (idRect)->extents.x1)\
+ (idRect)->extents.x1 = (r)->x1;\
+ if((r)->y1 < (idRect)->extents.y1)\
+ (idRect)->extents.y1 = (r)->y1;\
+ if((r)->x2 > (idRect)->extents.x2)\
+ (idRect)->extents.x2 = (r)->x2;\
+ if((r)->y2 > (idRect)->extents.y2)\
+ (idRect)->extents.y2 = (r)->y2;\
+ }
+
+/*
+ * Check to see if there is enough memory in the present region.
+ */
+#define MEMCHECK(reg, rect, firstrect){\
+ if ((reg)->numRects >= ((reg)->size - 1)){\
+ (firstrect) = (BOX *) Xrealloc \
+ ((char *)(firstrect), (unsigned) (2 * (sizeof(BOX)) * ((reg)->size)));\
+ if ((firstrect) == 0)\
+ return(0);\
+ (reg)->size *= 2;\
+ (rect) = &(firstrect)[(reg)->numRects];\
+ }\
+ }
+
+/* this routine checks to see if the previous rectangle is the same
+ * or subsumes the new rectangle to add.
+ */
+
+#define CHECK_PREVIOUS(Reg, R, Rx1, Ry1, Rx2, Ry2)\
+ (!(((Reg)->numRects > 0)&&\
+ ((R-1)->y1 == (Ry1)) &&\
+ ((R-1)->y2 == (Ry2)) &&\
+ ((R-1)->x1 <= (Rx1)) &&\
+ ((R-1)->x2 >= (Rx2))))
+
+/* add a rectangle to the given Region */
+#define ADDRECT(reg, r, rx1, ry1, rx2, ry2){\
+ if (((rx1) < (rx2)) && ((ry1) < (ry2)) &&\
+ CHECK_PREVIOUS((reg), (r), (rx1), (ry1), (rx2), (ry2))){\
+ (r)->x1 = (rx1);\
+ (r)->y1 = (ry1);\
+ (r)->x2 = (rx2);\
+ (r)->y2 = (ry2);\
+ EXTENTS((r), (reg));\
+ (reg)->numRects++;\
+ (r)++;\
+ }\
+ }
+
+
+
+/* add a rectangle to the given Region */
+#define ADDRECTNOX(reg, r, rx1, ry1, rx2, ry2){\
+ if ((rx1 < rx2) && (ry1 < ry2) &&\
+ CHECK_PREVIOUS((reg), (r), (rx1), (ry1), (rx2), (ry2))){\
+ (r)->x1 = (rx1);\
+ (r)->y1 = (ry1);\
+ (r)->x2 = (rx2);\
+ (r)->y2 = (ry2);\
+ (reg)->numRects++;\
+ (r)++;\
+ }\
+ }
+
+#define EMPTY_REGION(pReg) pReg->numRects = 0
+
+#define REGION_NOT_EMPTY(pReg) pReg->numRects
+
+#define INBOX(r, x, y) \
+ ( ( ((r).x2 > x)) && \
+ ( ((r).x1 <= x)) && \
+ ( ((r).y2 > y)) && \
+ ( ((r).y1 <= y)) )
+
+/*
+ * number of points to buffer before sending them off
+ * to scanlines() : Must be an even number
+ */
+#define NUMPTSTOBUFFER 200
+
+/*
+ * used to allocate buffers for points and link
+ * the buffers together
+ */
+typedef struct _POINTBLOCK {
+ XPoint pts[NUMPTSTOBUFFER];
+ struct _POINTBLOCK *next;
+} POINTBLOCK;
+
+#endif
diff --git a/boilerplate.mk b/boilerplate.mk
new file mode 100644
index 00000000..979731c5
--- /dev/null
+++ b/boilerplate.mk
@@ -0,0 +1,35 @@
+
+all::
+ @subdirs="$(SUBDIRS)"; for d in $$subdirs; do (cd $$d; $(MAKE) $@) || exit 1; done
+
+clean::
+ @subdirs="$(SUBDIRS)"; for d in $$subdirs; do (cd $$d; $(MAKE) $@) || exit 1; done
+
+clean::
+ rm -f $(program) $(library) *.o
+
+SHELL = @SHELL@
+top_srcdir = @top_srcdir@
+@SET_MAKE@
+CC = @CC@
+CFLAGS = @CFLAGS@ $(DIR_CFLAGS)
+CCLD = $(CC)
+CXX = @CXX@
+CXXFLAGS = @CXXFLAGS@
+CXXLD = $(CXX)
+CPPFLAGS = @CPPFLAGS@
+DEFS = @DEFS@
+ALL_CPPFLAGS = $(CPPFLAGS) $(DEFS) $(DIR_CPPFLAGS)
+LIBS = @LIBS@
+LDFLAGS = @LDFLAGS@
+RANLIB = @RANLIB@
+AR = ar cq
+
+.SUFFIXES:
+.SUFFIXES: .cxx .c .o
+
+.c.o:
+ $(CC) $(ALL_CPPFLAGS) $(CFLAGS) -c $<
+
+.cxx.o:
+ $(CXX) $(ALL_CPPFLAGS) $(CXXFLAGS) -c $<
diff --git a/configure b/configure
new file mode 100755
index 00000000..40951d21
--- /dev/null
+++ b/configure
@@ -0,0 +1,2299 @@
+#! /bin/sh
+
+# Guess values for system-dependent variables and create Makefiles.
+# Generated automatically using autoconf version 2.13
+# Copyright (C) 1992, 93, 94, 95, 96 Free Software Foundation, Inc.
+#
+# This configure script is free software; the Free Software Foundation
+# gives unlimited permission to copy, distribute and modify it.
+
+# Defaults:
+ac_help=
+ac_default_prefix=/usr/local
+# Any additions from configure.in:
+ac_help="$ac_help
+ --with-x use the X Window System"
+ac_help="$ac_help
+ --with-installed-zlib use the version of zlib which is installed on the
+ system instead of the one distributed with VNC"
+
+# Initialize some variables set by options.
+# The variables have the same names as the options, with
+# dashes changed to underlines.
+build=NONE
+cache_file=./config.cache
+exec_prefix=NONE
+host=NONE
+no_create=
+nonopt=NONE
+no_recursion=
+prefix=NONE
+program_prefix=NONE
+program_suffix=NONE
+program_transform_name=s,x,x,
+silent=
+site=
+srcdir=
+target=NONE
+verbose=
+x_includes=NONE
+x_libraries=NONE
+bindir='${exec_prefix}/bin'
+sbindir='${exec_prefix}/sbin'
+libexecdir='${exec_prefix}/libexec'
+datadir='${prefix}/share'
+sysconfdir='${prefix}/etc'
+sharedstatedir='${prefix}/com'
+localstatedir='${prefix}/var'
+libdir='${exec_prefix}/lib'
+includedir='${prefix}/include'
+oldincludedir='/usr/include'
+infodir='${prefix}/info'
+mandir='${prefix}/man'
+
+# Initialize some other variables.
+subdirs=
+MFLAGS= MAKEFLAGS=
+SHELL=${CONFIG_SHELL-/bin/sh}
+# Maximum number of lines to put in a shell here document.
+ac_max_here_lines=12
+
+ac_prev=
+for ac_option
+do
+
+ # If the previous option needs an argument, assign it.
+ if test -n "$ac_prev"; then
+ eval "$ac_prev=\$ac_option"
+ ac_prev=
+ continue
+ fi
+
+ case "$ac_option" in
+ -*=*) ac_optarg=`echo "$ac_option" | sed 's/[-_a-zA-Z0-9]*=//'` ;;
+ *) ac_optarg= ;;
+ esac
+
+ # Accept the important Cygnus configure options, so we can diagnose typos.
+
+ case "$ac_option" in
+
+ -bindir | --bindir | --bindi | --bind | --bin | --bi)
+ ac_prev=bindir ;;
+ -bindir=* | --bindir=* | --bindi=* | --bind=* | --bin=* | --bi=*)
+ bindir="$ac_optarg" ;;
+
+ -build | --build | --buil | --bui | --bu)
+ ac_prev=build ;;
+ -build=* | --build=* | --buil=* | --bui=* | --bu=*)
+ build="$ac_optarg" ;;
+
+ -cache-file | --cache-file | --cache-fil | --cache-fi \
+ | --cache-f | --cache- | --cache | --cach | --cac | --ca | --c)
+ ac_prev=cache_file ;;
+ -cache-file=* | --cache-file=* | --cache-fil=* | --cache-fi=* \
+ | --cache-f=* | --cache-=* | --cache=* | --cach=* | --cac=* | --ca=* | --c=*)
+ cache_file="$ac_optarg" ;;
+
+ -datadir | --datadir | --datadi | --datad | --data | --dat | --da)
+ ac_prev=datadir ;;
+ -datadir=* | --datadir=* | --datadi=* | --datad=* | --data=* | --dat=* \
+ | --da=*)
+ datadir="$ac_optarg" ;;
+
+ -disable-* | --disable-*)
+ ac_feature=`echo $ac_option|sed -e 's/-*disable-//'`
+ # Reject names that are not valid shell variable names.
+ if test -n "`echo $ac_feature| sed 's/[-a-zA-Z0-9_]//g'`"; then
+ { echo "configure: error: $ac_feature: invalid feature name" 1>&2; exit 1; }
+ fi
+ ac_feature=`echo $ac_feature| sed 's/-/_/g'`
+ eval "enable_${ac_feature}=no" ;;
+
+ -enable-* | --enable-*)
+ ac_feature=`echo $ac_option|sed -e 's/-*enable-//' -e 's/=.*//'`
+ # Reject names that are not valid shell variable names.
+ if test -n "`echo $ac_feature| sed 's/[-_a-zA-Z0-9]//g'`"; then
+ { echo "configure: error: $ac_feature: invalid feature name" 1>&2; exit 1; }
+ fi
+ ac_feature=`echo $ac_feature| sed 's/-/_/g'`
+ case "$ac_option" in
+ *=*) ;;
+ *) ac_optarg=yes ;;
+ esac
+ eval "enable_${ac_feature}='$ac_optarg'" ;;
+
+ -exec-prefix | --exec_prefix | --exec-prefix | --exec-prefi \
+ | --exec-pref | --exec-pre | --exec-pr | --exec-p | --exec- \
+ | --exec | --exe | --ex)
+ ac_prev=exec_prefix ;;
+ -exec-prefix=* | --exec_prefix=* | --exec-prefix=* | --exec-prefi=* \
+ | --exec-pref=* | --exec-pre=* | --exec-pr=* | --exec-p=* | --exec-=* \
+ | --exec=* | --exe=* | --ex=*)
+ exec_prefix="$ac_optarg" ;;
+
+ -gas | --gas | --ga | --g)
+ # Obsolete; use --with-gas.
+ with_gas=yes ;;
+
+ -help | --help | --hel | --he)
+ # Omit some internal or obsolete options to make the list less imposing.
+ # This message is too long to be a string in the A/UX 3.1 sh.
+ cat << EOF
+Usage: configure [options] [host]
+Options: [defaults in brackets after descriptions]
+Configuration:
+ --cache-file=FILE cache test results in FILE
+ --help print this message
+ --no-create do not create output files
+ --quiet, --silent do not print \`checking...' messages
+ --version print the version of autoconf that created configure
+Directory and file names:
+ --prefix=PREFIX install architecture-independent files in PREFIX
+ [$ac_default_prefix]
+ --exec-prefix=EPREFIX install architecture-dependent files in EPREFIX
+ [same as prefix]
+ --bindir=DIR user executables in DIR [EPREFIX/bin]
+ --sbindir=DIR system admin executables in DIR [EPREFIX/sbin]
+ --libexecdir=DIR program executables in DIR [EPREFIX/libexec]
+ --datadir=DIR read-only architecture-independent data in DIR
+ [PREFIX/share]
+ --sysconfdir=DIR read-only single-machine data in DIR [PREFIX/etc]
+ --sharedstatedir=DIR modifiable architecture-independent data in DIR
+ [PREFIX/com]
+ --localstatedir=DIR modifiable single-machine data in DIR [PREFIX/var]
+ --libdir=DIR object code libraries in DIR [EPREFIX/lib]
+ --includedir=DIR C header files in DIR [PREFIX/include]
+ --oldincludedir=DIR C header files for non-gcc in DIR [/usr/include]
+ --infodir=DIR info documentation in DIR [PREFIX/info]
+ --mandir=DIR man documentation in DIR [PREFIX/man]
+ --srcdir=DIR find the sources in DIR [configure dir or ..]
+ --program-prefix=PREFIX prepend PREFIX to installed program names
+ --program-suffix=SUFFIX append SUFFIX to installed program names
+ --program-transform-name=PROGRAM
+ run sed PROGRAM on installed program names
+EOF
+ cat << EOF
+Host type:
+ --build=BUILD configure for building on BUILD [BUILD=HOST]
+ --host=HOST configure for HOST [guessed]
+ --target=TARGET configure for TARGET [TARGET=HOST]
+Features and packages:
+ --disable-FEATURE do not include FEATURE (same as --enable-FEATURE=no)
+ --enable-FEATURE[=ARG] include FEATURE [ARG=yes]
+ --with-PACKAGE[=ARG] use PACKAGE [ARG=yes]
+ --without-PACKAGE do not use PACKAGE (same as --with-PACKAGE=no)
+ --x-includes=DIR X include files are in DIR
+ --x-libraries=DIR X library files are in DIR
+EOF
+ if test -n "$ac_help"; then
+ echo "--enable and --with options recognized:$ac_help"
+ fi
+ exit 0 ;;
+
+ -host | --host | --hos | --ho)
+ ac_prev=host ;;
+ -host=* | --host=* | --hos=* | --ho=*)
+ host="$ac_optarg" ;;
+
+ -includedir | --includedir | --includedi | --included | --include \
+ | --includ | --inclu | --incl | --inc)
+ ac_prev=includedir ;;
+ -includedir=* | --includedir=* | --includedi=* | --included=* | --include=* \
+ | --includ=* | --inclu=* | --incl=* | --inc=*)
+ includedir="$ac_optarg" ;;
+
+ -infodir | --infodir | --infodi | --infod | --info | --inf)
+ ac_prev=infodir ;;
+ -infodir=* | --infodir=* | --infodi=* | --infod=* | --info=* | --inf=*)
+ infodir="$ac_optarg" ;;
+
+ -libdir | --libdir | --libdi | --libd)
+ ac_prev=libdir ;;
+ -libdir=* | --libdir=* | --libdi=* | --libd=*)
+ libdir="$ac_optarg" ;;
+
+ -libexecdir | --libexecdir | --libexecdi | --libexecd | --libexec \
+ | --libexe | --libex | --libe)
+ ac_prev=libexecdir ;;
+ -libexecdir=* | --libexecdir=* | --libexecdi=* | --libexecd=* | --libexec=* \
+ | --libexe=* | --libex=* | --libe=*)
+ libexecdir="$ac_optarg" ;;
+
+ -localstatedir | --localstatedir | --localstatedi | --localstated \
+ | --localstate | --localstat | --localsta | --localst \
+ | --locals | --local | --loca | --loc | --lo)
+ ac_prev=localstatedir ;;
+ -localstatedir=* | --localstatedir=* | --localstatedi=* | --localstated=* \
+ | --localstate=* | --localstat=* | --localsta=* | --localst=* \
+ | --locals=* | --local=* | --loca=* | --loc=* | --lo=*)
+ localstatedir="$ac_optarg" ;;
+
+ -mandir | --mandir | --mandi | --mand | --man | --ma | --m)
+ ac_prev=mandir ;;
+ -mandir=* | --mandir=* | --mandi=* | --mand=* | --man=* | --ma=* | --m=*)
+ mandir="$ac_optarg" ;;
+
+ -nfp | --nfp | --nf)
+ # Obsolete; use --without-fp.
+ with_fp=no ;;
+
+ -no-create | --no-create | --no-creat | --no-crea | --no-cre \
+ | --no-cr | --no-c)
+ no_create=yes ;;
+
+ -no-recursion | --no-recursion | --no-recursio | --no-recursi \
+ | --no-recurs | --no-recur | --no-recu | --no-rec | --no-re | --no-r)
+ no_recursion=yes ;;
+
+ -oldincludedir | --oldincludedir | --oldincludedi | --oldincluded \
+ | --oldinclude | --oldinclud | --oldinclu | --oldincl | --oldinc \
+ | --oldin | --oldi | --old | --ol | --o)
+ ac_prev=oldincludedir ;;
+ -oldincludedir=* | --oldincludedir=* | --oldincludedi=* | --oldincluded=* \
+ | --oldinclude=* | --oldinclud=* | --oldinclu=* | --oldincl=* | --oldinc=* \
+ | --oldin=* | --oldi=* | --old=* | --ol=* | --o=*)
+ oldincludedir="$ac_optarg" ;;
+
+ -prefix | --prefix | --prefi | --pref | --pre | --pr | --p)
+ ac_prev=prefix ;;
+ -prefix=* | --prefix=* | --prefi=* | --pref=* | --pre=* | --pr=* | --p=*)
+ prefix="$ac_optarg" ;;
+
+ -program-prefix | --program-prefix | --program-prefi | --program-pref \
+ | --program-pre | --program-pr | --program-p)
+ ac_prev=program_prefix ;;
+ -program-prefix=* | --program-prefix=* | --program-prefi=* \
+ | --program-pref=* | --program-pre=* | --program-pr=* | --program-p=*)
+ program_prefix="$ac_optarg" ;;
+
+ -program-suffix | --program-suffix | --program-suffi | --program-suff \
+ | --program-suf | --program-su | --program-s)
+ ac_prev=program_suffix ;;
+ -program-suffix=* | --program-suffix=* | --program-suffi=* \
+ | --program-suff=* | --program-suf=* | --program-su=* | --program-s=*)
+ program_suffix="$ac_optarg" ;;
+
+ -program-transform-name | --program-transform-name \
+ | --program-transform-nam | --program-transform-na \
+ | --program-transform-n | --program-transform- \
+ | --program-transform | --program-transfor \
+ | --program-transfo | --program-transf \
+ | --program-trans | --program-tran \
+ | --progr-tra | --program-tr | --program-t)
+ ac_prev=program_transform_name ;;
+ -program-transform-name=* | --program-transform-name=* \
+ | --program-transform-nam=* | --program-transform-na=* \
+ | --program-transform-n=* | --program-transform-=* \
+ | --program-transform=* | --program-transfor=* \
+ | --program-transfo=* | --program-transf=* \
+ | --program-trans=* | --program-tran=* \
+ | --progr-tra=* | --program-tr=* | --program-t=*)
+ program_transform_name="$ac_optarg" ;;
+
+ -q | -quiet | --quiet | --quie | --qui | --qu | --q \
+ | -silent | --silent | --silen | --sile | --sil)
+ silent=yes ;;
+
+ -sbindir | --sbindir | --sbindi | --sbind | --sbin | --sbi | --sb)
+ ac_prev=sbindir ;;
+ -sbindir=* | --sbindir=* | --sbindi=* | --sbind=* | --sbin=* \
+ | --sbi=* | --sb=*)
+ sbindir="$ac_optarg" ;;
+
+ -sharedstatedir | --sharedstatedir | --sharedstatedi \
+ | --sharedstated | --sharedstate | --sharedstat | --sharedsta \
+ | --sharedst | --shareds | --shared | --share | --shar \
+ | --sha | --sh)
+ ac_prev=sharedstatedir ;;
+ -sharedstatedir=* | --sharedstatedir=* | --sharedstatedi=* \
+ | --sharedstated=* | --sharedstate=* | --sharedstat=* | --sharedsta=* \
+ | --sharedst=* | --shareds=* | --shared=* | --share=* | --shar=* \
+ | --sha=* | --sh=*)
+ sharedstatedir="$ac_optarg" ;;
+
+ -site | --site | --sit)
+ ac_prev=site ;;
+ -site=* | --site=* | --sit=*)
+ site="$ac_optarg" ;;
+
+ -srcdir | --srcdir | --srcdi | --srcd | --src | --sr)
+ ac_prev=srcdir ;;
+ -srcdir=* | --srcdir=* | --srcdi=* | --srcd=* | --src=* | --sr=*)
+ srcdir="$ac_optarg" ;;
+
+ -sysconfdir | --sysconfdir | --sysconfdi | --sysconfd | --sysconf \
+ | --syscon | --sysco | --sysc | --sys | --sy)
+ ac_prev=sysconfdir ;;
+ -sysconfdir=* | --sysconfdir=* | --sysconfdi=* | --sysconfd=* | --sysconf=* \
+ | --syscon=* | --sysco=* | --sysc=* | --sys=* | --sy=*)
+ sysconfdir="$ac_optarg" ;;
+
+ -target | --target | --targe | --targ | --tar | --ta | --t)
+ ac_prev=target ;;
+ -target=* | --target=* | --targe=* | --targ=* | --tar=* | --ta=* | --t=*)
+ target="$ac_optarg" ;;
+
+ -v | -verbose | --verbose | --verbos | --verbo | --verb)
+ verbose=yes ;;
+
+ -version | --version | --versio | --versi | --vers)
+ echo "configure generated by autoconf version 2.13"
+ exit 0 ;;
+
+ -with-* | --with-*)
+ ac_package=`echo $ac_option|sed -e 's/-*with-//' -e 's/=.*//'`
+ # Reject names that are not valid shell variable names.
+ if test -n "`echo $ac_package| sed 's/[-_a-zA-Z0-9]//g'`"; then
+ { echo "configure: error: $ac_package: invalid package name" 1>&2; exit 1; }
+ fi
+ ac_package=`echo $ac_package| sed 's/-/_/g'`
+ case "$ac_option" in
+ *=*) ;;
+ *) ac_optarg=yes ;;
+ esac
+ eval "with_${ac_package}='$ac_optarg'" ;;
+
+ -without-* | --without-*)
+ ac_package=`echo $ac_option|sed -e 's/-*without-//'`
+ # Reject names that are not valid shell variable names.
+ if test -n "`echo $ac_package| sed 's/[-a-zA-Z0-9_]//g'`"; then
+ { echo "configure: error: $ac_package: invalid package name" 1>&2; exit 1; }
+ fi
+ ac_package=`echo $ac_package| sed 's/-/_/g'`
+ eval "with_${ac_package}=no" ;;
+
+ --x)
+ # Obsolete; use --with-x.
+ with_x=yes ;;
+
+ -x-includes | --x-includes | --x-include | --x-includ | --x-inclu \
+ | --x-incl | --x-inc | --x-in | --x-i)
+ ac_prev=x_includes ;;
+ -x-includes=* | --x-includes=* | --x-include=* | --x-includ=* | --x-inclu=* \
+ | --x-incl=* | --x-inc=* | --x-in=* | --x-i=*)
+ x_includes="$ac_optarg" ;;
+
+ -x-libraries | --x-libraries | --x-librarie | --x-librari \
+ | --x-librar | --x-libra | --x-libr | --x-lib | --x-li | --x-l)
+ ac_prev=x_libraries ;;
+ -x-libraries=* | --x-libraries=* | --x-librarie=* | --x-librari=* \
+ | --x-librar=* | --x-libra=* | --x-libr=* | --x-lib=* | --x-li=* | --x-l=*)
+ x_libraries="$ac_optarg" ;;
+
+ -*) { echo "configure: error: $ac_option: invalid option; use --help to show usage" 1>&2; exit 1; }
+ ;;
+
+ *)
+ if test -n "`echo $ac_option| sed 's/[-a-z0-9.]//g'`"; then
+ echo "configure: warning: $ac_option: invalid host type" 1>&2
+ fi
+ if test "x$nonopt" != xNONE; then
+ { echo "configure: error: can only configure for one host and one target at a time" 1>&2; exit 1; }
+ fi
+ nonopt="$ac_option"
+ ;;
+
+ esac
+done
+
+if test -n "$ac_prev"; then
+ { echo "configure: error: missing argument to --`echo $ac_prev | sed 's/_/-/g'`" 1>&2; exit 1; }
+fi
+
+trap 'rm -fr conftest* confdefs* core core.* *.core $ac_clean_files; exit 1' 1 2 15
+
+# File descriptor usage:
+# 0 standard input
+# 1 file creation
+# 2 errors and warnings
+# 3 some systems may open it to /dev/tty
+# 4 used on the Kubota Titan
+# 6 checking for... messages and results
+# 5 compiler messages saved in config.log
+if test "$silent" = yes; then
+ exec 6>/dev/null
+else
+ exec 6>&1
+fi
+exec 5>./config.log
+
+echo "\
+This file contains any messages produced by compilers while
+running configure, to aid debugging if configure makes a mistake.
+" 1>&5
+
+# Strip out --no-create and --no-recursion so they do not pile up.
+# Also quote any args containing shell metacharacters.
+ac_configure_args=
+for ac_arg
+do
+ case "$ac_arg" in
+ -no-create | --no-create | --no-creat | --no-crea | --no-cre \
+ | --no-cr | --no-c) ;;
+ -no-recursion | --no-recursion | --no-recursio | --no-recursi \
+ | --no-recurs | --no-recur | --no-recu | --no-rec | --no-re | --no-r) ;;
+ *" "*|*" "*|*[\[\]\~\#\$\^\&\*\(\)\{\}\\\|\;\<\>\?]*)
+ ac_configure_args="$ac_configure_args '$ac_arg'" ;;
+ *) ac_configure_args="$ac_configure_args $ac_arg" ;;
+ esac
+done
+
+# NLS nuisances.
+# Only set these to C if already set. These must not be set unconditionally
+# because not all systems understand e.g. LANG=C (notably SCO).
+# Fixing LC_MESSAGES prevents Solaris sh from translating var values in `set'!
+# Non-C LC_CTYPE values break the ctype check.
+if test "${LANG+set}" = set; then LANG=C; export LANG; fi
+if test "${LC_ALL+set}" = set; then LC_ALL=C; export LC_ALL; fi
+if test "${LC_MESSAGES+set}" = set; then LC_MESSAGES=C; export LC_MESSAGES; fi
+if test "${LC_CTYPE+set}" = set; then LC_CTYPE=C; export LC_CTYPE; fi
+
+# confdefs.h avoids OS command line length limits that DEFS can exceed.
+rm -rf conftest* confdefs.h
+# AIX cpp loses on an empty file, so make sure it contains at least a newline.
+echo > confdefs.h
+
+# A filename unique to this package, relative to the directory that
+# configure is in, which we can look for to find out if srcdir is correct.
+ac_unique_file=rdr/InStream.h
+
+# Find the source files, if location was not specified.
+if test -z "$srcdir"; then
+ ac_srcdir_defaulted=yes
+ # Try the directory containing this script, then its parent.
+ ac_prog=$0
+ ac_confdir=`echo $ac_prog|sed 's%/[^/][^/]*$%%'`
+ test "x$ac_confdir" = "x$ac_prog" && ac_confdir=.
+ srcdir=$ac_confdir
+ if test ! -r $srcdir/$ac_unique_file; then
+ srcdir=..
+ fi
+else
+ ac_srcdir_defaulted=no
+fi
+if test ! -r $srcdir/$ac_unique_file; then
+ if test "$ac_srcdir_defaulted" = yes; then
+ { echo "configure: error: can not find sources in $ac_confdir or .." 1>&2; exit 1; }
+ else
+ { echo "configure: error: can not find sources in $srcdir" 1>&2; exit 1; }
+ fi
+fi
+srcdir=`echo "${srcdir}" | sed 's%\([^/]\)/*$%\1%'`
+
+# Prefer explicitly selected file to automatically selected ones.
+if test -z "$CONFIG_SITE"; then
+ if test "x$prefix" != xNONE; then
+ CONFIG_SITE="$prefix/share/config.site $prefix/etc/config.site"
+ else
+ CONFIG_SITE="$ac_default_prefix/share/config.site $ac_default_prefix/etc/config.site"
+ fi
+fi
+for ac_site_file in $CONFIG_SITE; do
+ if test -r "$ac_site_file"; then
+ echo "loading site script $ac_site_file"
+ . "$ac_site_file"
+ fi
+done
+
+if test -r "$cache_file"; then
+ echo "loading cache $cache_file"
+ . $cache_file
+else
+ echo "creating cache $cache_file"
+ > $cache_file
+fi
+
+ac_ext=c
+# CFLAGS is not in ac_cpp because -g, -O, etc. are not valid cpp options.
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='${CC-cc} -c $CFLAGS $CPPFLAGS conftest.$ac_ext 1>&5'
+ac_link='${CC-cc} -o conftest${ac_exeext} $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS 1>&5'
+cross_compiling=$ac_cv_prog_cc_cross
+
+ac_exeext=
+ac_objext=o
+if (echo "testing\c"; echo 1,2,3) | grep c >/dev/null; then
+ # Stardent Vistra SVR4 grep lacks -e, says ghazi@caip.rutgers.edu.
+ if (echo -n testing; echo 1,2,3) | sed s/-n/xn/ | grep xn >/dev/null; then
+ ac_n= ac_c='
+' ac_t=' '
+ else
+ ac_n=-n ac_c= ac_t=
+ fi
+else
+ ac_n= ac_c='\c' ac_t=
+fi
+
+
+
+ac_cv_prog_cc_g=no
+ac_cv_prog_cxx_g=no
+
+# Extract the first word of "gcc", so it can be a program name with args.
+set dummy gcc; ac_word=$2
+echo $ac_n "checking for $ac_word""... $ac_c" 1>&6
+echo "configure:537: checking for $ac_word" >&5
+if eval "test \"`echo '$''{'ac_cv_prog_CC'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ if test -n "$CC"; then
+ ac_cv_prog_CC="$CC" # Let the user override the test.
+else
+ IFS="${IFS= }"; ac_save_ifs="$IFS"; IFS=":"
+ ac_dummy="$PATH"
+ for ac_dir in $ac_dummy; do
+ test -z "$ac_dir" && ac_dir=.
+ if test -f $ac_dir/$ac_word; then
+ ac_cv_prog_CC="gcc"
+ break
+ fi
+ done
+ IFS="$ac_save_ifs"
+fi
+fi
+CC="$ac_cv_prog_CC"
+if test -n "$CC"; then
+ echo "$ac_t""$CC" 1>&6
+else
+ echo "$ac_t""no" 1>&6
+fi
+
+if test -z "$CC"; then
+ # Extract the first word of "cc", so it can be a program name with args.
+set dummy cc; ac_word=$2
+echo $ac_n "checking for $ac_word""... $ac_c" 1>&6
+echo "configure:567: checking for $ac_word" >&5
+if eval "test \"`echo '$''{'ac_cv_prog_CC'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ if test -n "$CC"; then
+ ac_cv_prog_CC="$CC" # Let the user override the test.
+else
+ IFS="${IFS= }"; ac_save_ifs="$IFS"; IFS=":"
+ ac_prog_rejected=no
+ ac_dummy="$PATH"
+ for ac_dir in $ac_dummy; do
+ test -z "$ac_dir" && ac_dir=.
+ if test -f $ac_dir/$ac_word; then
+ if test "$ac_dir/$ac_word" = "/usr/ucb/cc"; then
+ ac_prog_rejected=yes
+ continue
+ fi
+ ac_cv_prog_CC="cc"
+ break
+ fi
+ done
+ IFS="$ac_save_ifs"
+if test $ac_prog_rejected = yes; then
+ # We found a bogon in the path, so make sure we never use it.
+ set dummy $ac_cv_prog_CC
+ shift
+ if test $# -gt 0; then
+ # We chose a different compiler from the bogus one.
+ # However, it has the same basename, so the bogon will be chosen
+ # first if we set CC to just the basename; use the full file name.
+ shift
+ set dummy "$ac_dir/$ac_word" "$@"
+ shift
+ ac_cv_prog_CC="$@"
+ fi
+fi
+fi
+fi
+CC="$ac_cv_prog_CC"
+if test -n "$CC"; then
+ echo "$ac_t""$CC" 1>&6
+else
+ echo "$ac_t""no" 1>&6
+fi
+
+ if test -z "$CC"; then
+ case "`uname -s`" in
+ *win32* | *WIN32*)
+ # Extract the first word of "cl", so it can be a program name with args.
+set dummy cl; ac_word=$2
+echo $ac_n "checking for $ac_word""... $ac_c" 1>&6
+echo "configure:618: checking for $ac_word" >&5
+if eval "test \"`echo '$''{'ac_cv_prog_CC'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ if test -n "$CC"; then
+ ac_cv_prog_CC="$CC" # Let the user override the test.
+else
+ IFS="${IFS= }"; ac_save_ifs="$IFS"; IFS=":"
+ ac_dummy="$PATH"
+ for ac_dir in $ac_dummy; do
+ test -z "$ac_dir" && ac_dir=.
+ if test -f $ac_dir/$ac_word; then
+ ac_cv_prog_CC="cl"
+ break
+ fi
+ done
+ IFS="$ac_save_ifs"
+fi
+fi
+CC="$ac_cv_prog_CC"
+if test -n "$CC"; then
+ echo "$ac_t""$CC" 1>&6
+else
+ echo "$ac_t""no" 1>&6
+fi
+ ;;
+ esac
+ fi
+ test -z "$CC" && { echo "configure: error: no acceptable cc found in \$PATH" 1>&2; exit 1; }
+fi
+
+echo $ac_n "checking whether the C compiler ($CC $CFLAGS $LDFLAGS) works""... $ac_c" 1>&6
+echo "configure:650: checking whether the C compiler ($CC $CFLAGS $LDFLAGS) works" >&5
+
+ac_ext=c
+# CFLAGS is not in ac_cpp because -g, -O, etc. are not valid cpp options.
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='${CC-cc} -c $CFLAGS $CPPFLAGS conftest.$ac_ext 1>&5'
+ac_link='${CC-cc} -o conftest${ac_exeext} $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS 1>&5'
+cross_compiling=$ac_cv_prog_cc_cross
+
+cat > conftest.$ac_ext << EOF
+
+#line 661 "configure"
+#include "confdefs.h"
+
+main(){return(0);}
+EOF
+if { (eval echo configure:666: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
+ ac_cv_prog_cc_works=yes
+ # If we can't run a trivial program, we are probably using a cross compiler.
+ if (./conftest; exit) 2>/dev/null; then
+ ac_cv_prog_cc_cross=no
+ else
+ ac_cv_prog_cc_cross=yes
+ fi
+else
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+ ac_cv_prog_cc_works=no
+fi
+rm -fr conftest*
+ac_ext=c
+# CFLAGS is not in ac_cpp because -g, -O, etc. are not valid cpp options.
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='${CC-cc} -c $CFLAGS $CPPFLAGS conftest.$ac_ext 1>&5'
+ac_link='${CC-cc} -o conftest${ac_exeext} $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS 1>&5'
+cross_compiling=$ac_cv_prog_cc_cross
+
+echo "$ac_t""$ac_cv_prog_cc_works" 1>&6
+if test $ac_cv_prog_cc_works = no; then
+ { echo "configure: error: installation or configuration problem: C compiler cannot create executables." 1>&2; exit 1; }
+fi
+echo $ac_n "checking whether the C compiler ($CC $CFLAGS $LDFLAGS) is a cross-compiler""... $ac_c" 1>&6
+echo "configure:692: checking whether the C compiler ($CC $CFLAGS $LDFLAGS) is a cross-compiler" >&5
+echo "$ac_t""$ac_cv_prog_cc_cross" 1>&6
+cross_compiling=$ac_cv_prog_cc_cross
+
+echo $ac_n "checking whether we are using GNU C""... $ac_c" 1>&6
+echo "configure:697: checking whether we are using GNU C" >&5
+if eval "test \"`echo '$''{'ac_cv_prog_gcc'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ cat > conftest.c <<EOF
+#ifdef __GNUC__
+ yes;
+#endif
+EOF
+if { ac_try='${CC-cc} -E conftest.c'; { (eval echo configure:706: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; }; } | egrep yes >/dev/null 2>&1; then
+ ac_cv_prog_gcc=yes
+else
+ ac_cv_prog_gcc=no
+fi
+fi
+
+echo "$ac_t""$ac_cv_prog_gcc" 1>&6
+
+if test $ac_cv_prog_gcc = yes; then
+ GCC=yes
+else
+ GCC=
+fi
+
+ac_test_CFLAGS="${CFLAGS+set}"
+ac_save_CFLAGS="$CFLAGS"
+CFLAGS=
+echo $ac_n "checking whether ${CC-cc} accepts -g""... $ac_c" 1>&6
+echo "configure:725: checking whether ${CC-cc} accepts -g" >&5
+if eval "test \"`echo '$''{'ac_cv_prog_cc_g'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ echo 'void f(){}' > conftest.c
+if test -z "`${CC-cc} -g -c conftest.c 2>&1`"; then
+ ac_cv_prog_cc_g=yes
+else
+ ac_cv_prog_cc_g=no
+fi
+rm -f conftest*
+
+fi
+
+echo "$ac_t""$ac_cv_prog_cc_g" 1>&6
+if test "$ac_test_CFLAGS" = set; then
+ CFLAGS="$ac_save_CFLAGS"
+elif test $ac_cv_prog_cc_g = yes; then
+ if test "$GCC" = yes; then
+ CFLAGS="-g -O2"
+ else
+ CFLAGS="-g"
+ fi
+else
+ if test "$GCC" = yes; then
+ CFLAGS="-O2"
+ else
+ CFLAGS=
+ fi
+fi
+
+for ac_prog in $CCC c++ g++ gcc CC cxx cc++ cl
+do
+# Extract the first word of "$ac_prog", so it can be a program name with args.
+set dummy $ac_prog; ac_word=$2
+echo $ac_n "checking for $ac_word""... $ac_c" 1>&6
+echo "configure:761: checking for $ac_word" >&5
+if eval "test \"`echo '$''{'ac_cv_prog_CXX'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ if test -n "$CXX"; then
+ ac_cv_prog_CXX="$CXX" # Let the user override the test.
+else
+ IFS="${IFS= }"; ac_save_ifs="$IFS"; IFS=":"
+ ac_dummy="$PATH"
+ for ac_dir in $ac_dummy; do
+ test -z "$ac_dir" && ac_dir=.
+ if test -f $ac_dir/$ac_word; then
+ ac_cv_prog_CXX="$ac_prog"
+ break
+ fi
+ done
+ IFS="$ac_save_ifs"
+fi
+fi
+CXX="$ac_cv_prog_CXX"
+if test -n "$CXX"; then
+ echo "$ac_t""$CXX" 1>&6
+else
+ echo "$ac_t""no" 1>&6
+fi
+
+test -n "$CXX" && break
+done
+test -n "$CXX" || CXX="gcc"
+
+
+echo $ac_n "checking whether the C++ compiler ($CXX $CXXFLAGS $LDFLAGS) works""... $ac_c" 1>&6
+echo "configure:793: checking whether the C++ compiler ($CXX $CXXFLAGS $LDFLAGS) works" >&5
+
+ac_ext=C
+# CXXFLAGS is not in ac_cpp because -g, -O, etc. are not valid cpp options.
+ac_cpp='$CXXCPP $CPPFLAGS'
+ac_compile='${CXX-g++} -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext 1>&5'
+ac_link='${CXX-g++} -o conftest${ac_exeext} $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS 1>&5'
+cross_compiling=$ac_cv_prog_cxx_cross
+
+cat > conftest.$ac_ext << EOF
+
+#line 804 "configure"
+#include "confdefs.h"
+
+int main(){return(0);}
+EOF
+if { (eval echo configure:809: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
+ ac_cv_prog_cxx_works=yes
+ # If we can't run a trivial program, we are probably using a cross compiler.
+ if (./conftest; exit) 2>/dev/null; then
+ ac_cv_prog_cxx_cross=no
+ else
+ ac_cv_prog_cxx_cross=yes
+ fi
+else
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+ ac_cv_prog_cxx_works=no
+fi
+rm -fr conftest*
+ac_ext=c
+# CFLAGS is not in ac_cpp because -g, -O, etc. are not valid cpp options.
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='${CC-cc} -c $CFLAGS $CPPFLAGS conftest.$ac_ext 1>&5'
+ac_link='${CC-cc} -o conftest${ac_exeext} $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS 1>&5'
+cross_compiling=$ac_cv_prog_cc_cross
+
+echo "$ac_t""$ac_cv_prog_cxx_works" 1>&6
+if test $ac_cv_prog_cxx_works = no; then
+ { echo "configure: error: installation or configuration problem: C++ compiler cannot create executables." 1>&2; exit 1; }
+fi
+echo $ac_n "checking whether the C++ compiler ($CXX $CXXFLAGS $LDFLAGS) is a cross-compiler""... $ac_c" 1>&6
+echo "configure:835: checking whether the C++ compiler ($CXX $CXXFLAGS $LDFLAGS) is a cross-compiler" >&5
+echo "$ac_t""$ac_cv_prog_cxx_cross" 1>&6
+cross_compiling=$ac_cv_prog_cxx_cross
+
+echo $ac_n "checking whether we are using GNU C++""... $ac_c" 1>&6
+echo "configure:840: checking whether we are using GNU C++" >&5
+if eval "test \"`echo '$''{'ac_cv_prog_gxx'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ cat > conftest.C <<EOF
+#ifdef __GNUC__
+ yes;
+#endif
+EOF
+if { ac_try='${CXX-g++} -E conftest.C'; { (eval echo configure:849: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; }; } | egrep yes >/dev/null 2>&1; then
+ ac_cv_prog_gxx=yes
+else
+ ac_cv_prog_gxx=no
+fi
+fi
+
+echo "$ac_t""$ac_cv_prog_gxx" 1>&6
+
+if test $ac_cv_prog_gxx = yes; then
+ GXX=yes
+else
+ GXX=
+fi
+
+ac_test_CXXFLAGS="${CXXFLAGS+set}"
+ac_save_CXXFLAGS="$CXXFLAGS"
+CXXFLAGS=
+echo $ac_n "checking whether ${CXX-g++} accepts -g""... $ac_c" 1>&6
+echo "configure:868: checking whether ${CXX-g++} accepts -g" >&5
+if eval "test \"`echo '$''{'ac_cv_prog_cxx_g'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ echo 'void f(){}' > conftest.cc
+if test -z "`${CXX-g++} -g -c conftest.cc 2>&1`"; then
+ ac_cv_prog_cxx_g=yes
+else
+ ac_cv_prog_cxx_g=no
+fi
+rm -f conftest*
+
+fi
+
+echo "$ac_t""$ac_cv_prog_cxx_g" 1>&6
+if test "$ac_test_CXXFLAGS" = set; then
+ CXXFLAGS="$ac_save_CXXFLAGS"
+elif test $ac_cv_prog_cxx_g = yes; then
+ if test "$GXX" = yes; then
+ CXXFLAGS="-g -O2"
+ else
+ CXXFLAGS="-g"
+ fi
+else
+ if test "$GXX" = yes; then
+ CXXFLAGS="-O2"
+ else
+ CXXFLAGS=
+ fi
+fi
+
+# Extract the first word of "ranlib", so it can be a program name with args.
+set dummy ranlib; ac_word=$2
+echo $ac_n "checking for $ac_word""... $ac_c" 1>&6
+echo "configure:902: checking for $ac_word" >&5
+if eval "test \"`echo '$''{'ac_cv_prog_RANLIB'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ if test -n "$RANLIB"; then
+ ac_cv_prog_RANLIB="$RANLIB" # Let the user override the test.
+else
+ IFS="${IFS= }"; ac_save_ifs="$IFS"; IFS=":"
+ ac_dummy="$PATH"
+ for ac_dir in $ac_dummy; do
+ test -z "$ac_dir" && ac_dir=.
+ if test -f $ac_dir/$ac_word; then
+ ac_cv_prog_RANLIB="ranlib"
+ break
+ fi
+ done
+ IFS="$ac_save_ifs"
+ test -z "$ac_cv_prog_RANLIB" && ac_cv_prog_RANLIB=":"
+fi
+fi
+RANLIB="$ac_cv_prog_RANLIB"
+if test -n "$RANLIB"; then
+ echo "$ac_t""$RANLIB" 1>&6
+else
+ echo "$ac_t""no" 1>&6
+fi
+
+echo $ac_n "checking whether ${MAKE-make} sets \${MAKE}""... $ac_c" 1>&6
+echo "configure:930: checking whether ${MAKE-make} sets \${MAKE}" >&5
+set dummy ${MAKE-make}; ac_make=`echo "$2" | sed 'y%./+-%__p_%'`
+if eval "test \"`echo '$''{'ac_cv_prog_make_${ac_make}_set'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ cat > conftestmake <<\EOF
+all:
+ @echo 'ac_maketemp="${MAKE}"'
+EOF
+# GNU make sometimes prints "make[1]: Entering...", which would confuse us.
+eval `${MAKE-make} -f conftestmake 2>/dev/null | grep temp=`
+if test -n "$ac_maketemp"; then
+ eval ac_cv_prog_make_${ac_make}_set=yes
+else
+ eval ac_cv_prog_make_${ac_make}_set=no
+fi
+rm -f conftestmake
+fi
+if eval "test \"`echo '$ac_cv_prog_make_'${ac_make}_set`\" = yes"; then
+ echo "$ac_t""yes" 1>&6
+ SET_MAKE=
+else
+ echo "$ac_t""no" 1>&6
+ SET_MAKE="MAKE=${MAKE-make}"
+fi
+
+ac_ext=C
+# CXXFLAGS is not in ac_cpp because -g, -O, etc. are not valid cpp options.
+ac_cpp='$CXXCPP $CPPFLAGS'
+ac_compile='${CXX-g++} -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext 1>&5'
+ac_link='${CXX-g++} -o conftest${ac_exeext} $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS 1>&5'
+cross_compiling=$ac_cv_prog_cxx_cross
+
+
+case "`(uname -sr) 2>/dev/null`" in
+"SunOS 5"*)
+ SOLARIS=yes
+ USE_MITSHM=yes
+ ;;
+"Linux"*)
+ LINUX=yes
+ USE_MITSHM=yes
+ ;;
+esac
+
+if test "$USE_MITSHM" = yes; then
+ MITSHM_CPPFLAGS="-DMITSHM"
+fi
+
+
+if test "$GCC" = yes; then
+ CFLAGS="$CFLAGS -Wall"
+ if test "$SOLARIS" = yes; then
+ CFLAGS="$CFLAGS -Wno-unknown-pragmas -Wno-implicit-int"
+ fi
+fi
+if test "$GXX" = yes; then
+ CXXFLAGS="$CXXFLAGS -Wall"
+ if test "$SOLARIS" = yes; then
+ CXXFLAGS="$CXXFLAGS -Wno-unknown-pragmas -Wno-implicit-int -fpermissive"
+ fi
+fi
+
+echo $ac_n "checking how to run the C++ preprocessor""... $ac_c" 1>&6
+echo "configure:994: checking how to run the C++ preprocessor" >&5
+if test -z "$CXXCPP"; then
+if eval "test \"`echo '$''{'ac_cv_prog_CXXCPP'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ ac_ext=C
+# CXXFLAGS is not in ac_cpp because -g, -O, etc. are not valid cpp options.
+ac_cpp='$CXXCPP $CPPFLAGS'
+ac_compile='${CXX-g++} -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext 1>&5'
+ac_link='${CXX-g++} -o conftest${ac_exeext} $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS 1>&5'
+cross_compiling=$ac_cv_prog_cxx_cross
+ CXXCPP="${CXX-g++} -E"
+ cat > conftest.$ac_ext <<EOF
+#line 1007 "configure"
+#include "confdefs.h"
+#include <stdlib.h>
+EOF
+ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out"
+{ (eval echo configure:1012: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; }
+ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"`
+if test -z "$ac_err"; then
+ :
+else
+ echo "$ac_err" >&5
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+ rm -rf conftest*
+ CXXCPP=/lib/cpp
+fi
+rm -f conftest*
+ ac_cv_prog_CXXCPP="$CXXCPP"
+ac_ext=C
+# CXXFLAGS is not in ac_cpp because -g, -O, etc. are not valid cpp options.
+ac_cpp='$CXXCPP $CPPFLAGS'
+ac_compile='${CXX-g++} -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext 1>&5'
+ac_link='${CXX-g++} -o conftest${ac_exeext} $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS 1>&5'
+cross_compiling=$ac_cv_prog_cxx_cross
+fi
+fi
+CXXCPP="$ac_cv_prog_CXXCPP"
+echo "$ac_t""$CXXCPP" 1>&6
+
+# If we find X, set shell vars x_includes and x_libraries to the
+# paths, otherwise set no_x=yes.
+# Uses ac_ vars as temps to allow command line to override cache and checks.
+# --without-x overrides everything else, but does not touch the cache.
+echo $ac_n "checking for X""... $ac_c" 1>&6
+echo "configure:1041: checking for X" >&5
+
+# Check whether --with-x or --without-x was given.
+if test "${with_x+set}" = set; then
+ withval="$with_x"
+ :
+fi
+
+# $have_x is `yes', `no', `disabled', or empty when we do not yet know.
+if test "x$with_x" = xno; then
+ # The user explicitly disabled X.
+ have_x=disabled
+else
+ if test "x$x_includes" != xNONE && test "x$x_libraries" != xNONE; then
+ # Both variables are already set.
+ have_x=yes
+ else
+if eval "test \"`echo '$''{'ac_cv_have_x'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ # One or both of the vars are not set, and there is no cached value.
+ac_x_includes=NO ac_x_libraries=NO
+rm -fr conftestdir
+if mkdir conftestdir; then
+ cd conftestdir
+ # Make sure to not put "make" in the Imakefile rules, since we grep it out.
+ cat > Imakefile <<'EOF'
+acfindx:
+ @echo 'ac_im_incroot="${INCROOT}"; ac_im_usrlibdir="${USRLIBDIR}"; ac_im_libdir="${LIBDIR}"'
+EOF
+ if (xmkmf) >/dev/null 2>/dev/null && test -f Makefile; then
+ # GNU make sometimes prints "make[1]: Entering...", which would confuse us.
+ eval `${MAKE-make} acfindx 2>/dev/null | grep -v make`
+ # Open Windows xmkmf reportedly sets LIBDIR instead of USRLIBDIR.
+ for ac_extension in a so sl; do
+ if test ! -f $ac_im_usrlibdir/libX11.$ac_extension &&
+ test -f $ac_im_libdir/libX11.$ac_extension; then
+ ac_im_usrlibdir=$ac_im_libdir; break
+ fi
+ done
+ # Screen out bogus values from the imake configuration. They are
+ # bogus both because they are the default anyway, and because
+ # using them would break gcc on systems where it needs fixed includes.
+ case "$ac_im_incroot" in
+ /usr/include) ;;
+ *) test -f "$ac_im_incroot/X11/Xos.h" && ac_x_includes="$ac_im_incroot" ;;
+ esac
+ case "$ac_im_usrlibdir" in
+ /usr/lib | /lib) ;;
+ *) test -d "$ac_im_usrlibdir" && ac_x_libraries="$ac_im_usrlibdir" ;;
+ esac
+ fi
+ cd ..
+ rm -fr conftestdir
+fi
+
+if test "$ac_x_includes" = NO; then
+ # Guess where to find include files, by looking for this one X11 .h file.
+ test -z "$x_direct_test_include" && x_direct_test_include=X11/Intrinsic.h
+
+ # First, try using that file with no special directory specified.
+cat > conftest.$ac_ext <<EOF
+#line 1103 "configure"
+#include "confdefs.h"
+#include <$x_direct_test_include>
+EOF
+ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out"
+{ (eval echo configure:1108: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; }
+ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"`
+if test -z "$ac_err"; then
+ rm -rf conftest*
+ # We can compile using X headers with no special include directory.
+ac_x_includes=
+else
+ echo "$ac_err" >&5
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+ rm -rf conftest*
+ # Look for the header file in a standard set of common directories.
+# Check X11 before X11Rn because it is often a symlink to the current release.
+ for ac_dir in \
+ /usr/X11/include \
+ /usr/X11R6/include \
+ /usr/X11R5/include \
+ /usr/X11R4/include \
+ \
+ /usr/include/X11 \
+ /usr/include/X11R6 \
+ /usr/include/X11R5 \
+ /usr/include/X11R4 \
+ \
+ /usr/local/X11/include \
+ /usr/local/X11R6/include \
+ /usr/local/X11R5/include \
+ /usr/local/X11R4/include \
+ \
+ /usr/local/include/X11 \
+ /usr/local/include/X11R6 \
+ /usr/local/include/X11R5 \
+ /usr/local/include/X11R4 \
+ \
+ /usr/X386/include \
+ /usr/x386/include \
+ /usr/XFree86/include/X11 \
+ \
+ /usr/include \
+ /usr/local/include \
+ /usr/unsupported/include \
+ /usr/athena/include \
+ /usr/local/x11r5/include \
+ /usr/lpp/Xamples/include \
+ \
+ /usr/openwin/include \
+ /usr/openwin/share/include \
+ ; \
+ do
+ if test -r "$ac_dir/$x_direct_test_include"; then
+ ac_x_includes=$ac_dir
+ break
+ fi
+ done
+fi
+rm -f conftest*
+fi # $ac_x_includes = NO
+
+if test "$ac_x_libraries" = NO; then
+ # Check for the libraries.
+
+ test -z "$x_direct_test_library" && x_direct_test_library=Xt
+ test -z "$x_direct_test_function" && x_direct_test_function=XtMalloc
+
+ # See if we find them without any special options.
+ # Don't add to $LIBS permanently.
+ ac_save_LIBS="$LIBS"
+ LIBS="-l$x_direct_test_library $LIBS"
+cat > conftest.$ac_ext <<EOF
+#line 1177 "configure"
+#include "confdefs.h"
+
+int main() {
+${x_direct_test_function}()
+; return 0; }
+EOF
+if { (eval echo configure:1184: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
+ rm -rf conftest*
+ LIBS="$ac_save_LIBS"
+# We can link X programs with no special library path.
+ac_x_libraries=
+else
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+ rm -rf conftest*
+ LIBS="$ac_save_LIBS"
+# First see if replacing the include by lib works.
+# Check X11 before X11Rn because it is often a symlink to the current release.
+for ac_dir in `echo "$ac_x_includes" | sed s/include/lib/` \
+ /usr/X11/lib \
+ /usr/X11R6/lib \
+ /usr/X11R5/lib \
+ /usr/X11R4/lib \
+ \
+ /usr/lib/X11 \
+ /usr/lib/X11R6 \
+ /usr/lib/X11R5 \
+ /usr/lib/X11R4 \
+ \
+ /usr/local/X11/lib \
+ /usr/local/X11R6/lib \
+ /usr/local/X11R5/lib \
+ /usr/local/X11R4/lib \
+ \
+ /usr/local/lib/X11 \
+ /usr/local/lib/X11R6 \
+ /usr/local/lib/X11R5 \
+ /usr/local/lib/X11R4 \
+ \
+ /usr/X386/lib \
+ /usr/x386/lib \
+ /usr/XFree86/lib/X11 \
+ \
+ /usr/lib \
+ /usr/local/lib \
+ /usr/unsupported/lib \
+ /usr/athena/lib \
+ /usr/local/x11r5/lib \
+ /usr/lpp/Xamples/lib \
+ /lib/usr/lib/X11 \
+ \
+ /usr/openwin/lib \
+ /usr/openwin/share/lib \
+ ; \
+do
+ for ac_extension in a so sl; do
+ if test -r $ac_dir/lib${x_direct_test_library}.$ac_extension; then
+ ac_x_libraries=$ac_dir
+ break 2
+ fi
+ done
+done
+fi
+rm -f conftest*
+fi # $ac_x_libraries = NO
+
+if test "$ac_x_includes" = NO || test "$ac_x_libraries" = NO; then
+ # Didn't find X anywhere. Cache the known absence of X.
+ ac_cv_have_x="have_x=no"
+else
+ # Record where we found X for the cache.
+ ac_cv_have_x="have_x=yes \
+ ac_x_includes=$ac_x_includes ac_x_libraries=$ac_x_libraries"
+fi
+fi
+ fi
+ eval "$ac_cv_have_x"
+fi # $with_x != no
+
+if test "$have_x" != yes; then
+ echo "$ac_t""$have_x" 1>&6
+ no_x=yes
+else
+ # If each of the values was on the command line, it overrides each guess.
+ test "x$x_includes" = xNONE && x_includes=$ac_x_includes
+ test "x$x_libraries" = xNONE && x_libraries=$ac_x_libraries
+ # Update the cache value to reflect the command line values.
+ ac_cv_have_x="have_x=yes \
+ ac_x_includes=$x_includes ac_x_libraries=$x_libraries"
+ echo "$ac_t""libraries $x_libraries, headers $x_includes" 1>&6
+fi
+
+if test "$no_x" = yes; then
+ # Not all programs may use this symbol, but it does not hurt to define it.
+ cat >> confdefs.h <<\EOF
+#define X_DISPLAY_MISSING 1
+EOF
+
+ X_CFLAGS= X_PRE_LIBS= X_LIBS= X_EXTRA_LIBS=
+else
+ if test -n "$x_includes"; then
+ X_CFLAGS="$X_CFLAGS -I$x_includes"
+ fi
+
+ # It would also be nice to do this for all -L options, not just this one.
+ if test -n "$x_libraries"; then
+ X_LIBS="$X_LIBS -L$x_libraries"
+ # For Solaris; some versions of Sun CC require a space after -R and
+ # others require no space. Words are not sufficient . . . .
+ case "`(uname -sr) 2>/dev/null`" in
+ "SunOS 5"*)
+ echo $ac_n "checking whether -R must be followed by a space""... $ac_c" 1>&6
+echo "configure:1290: checking whether -R must be followed by a space" >&5
+ ac_xsave_LIBS="$LIBS"; LIBS="$LIBS -R$x_libraries"
+ cat > conftest.$ac_ext <<EOF
+#line 1293 "configure"
+#include "confdefs.h"
+
+int main() {
+
+; return 0; }
+EOF
+if { (eval echo configure:1300: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
+ rm -rf conftest*
+ ac_R_nospace=yes
+else
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+ rm -rf conftest*
+ ac_R_nospace=no
+fi
+rm -f conftest*
+ if test $ac_R_nospace = yes; then
+ echo "$ac_t""no" 1>&6
+ X_LIBS="$X_LIBS -R$x_libraries"
+ else
+ LIBS="$ac_xsave_LIBS -R $x_libraries"
+ cat > conftest.$ac_ext <<EOF
+#line 1316 "configure"
+#include "confdefs.h"
+
+int main() {
+
+; return 0; }
+EOF
+if { (eval echo configure:1323: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
+ rm -rf conftest*
+ ac_R_space=yes
+else
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+ rm -rf conftest*
+ ac_R_space=no
+fi
+rm -f conftest*
+ if test $ac_R_space = yes; then
+ echo "$ac_t""yes" 1>&6
+ X_LIBS="$X_LIBS -R $x_libraries"
+ else
+ echo "$ac_t""neither works" 1>&6
+ fi
+ fi
+ LIBS="$ac_xsave_LIBS"
+ esac
+ fi
+
+ # Check for system-dependent libraries X programs must link with.
+ # Do this before checking for the system-independent R6 libraries
+ # (-lICE), since we may need -lsocket or whatever for X linking.
+
+ if test "$ISC" = yes; then
+ X_EXTRA_LIBS="$X_EXTRA_LIBS -lnsl_s -linet"
+ else
+ # Martyn.Johnson@cl.cam.ac.uk says this is needed for Ultrix, if the X
+ # libraries were built with DECnet support. And karl@cs.umb.edu says
+ # the Alpha needs dnet_stub (dnet does not exist).
+ echo $ac_n "checking for dnet_ntoa in -ldnet""... $ac_c" 1>&6
+echo "configure:1355: checking for dnet_ntoa in -ldnet" >&5
+ac_lib_var=`echo dnet'_'dnet_ntoa | sed 'y%./+-%__p_%'`
+if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ ac_save_LIBS="$LIBS"
+LIBS="-ldnet $LIBS"
+cat > conftest.$ac_ext <<EOF
+#line 1363 "configure"
+#include "confdefs.h"
+/* Override any gcc2 internal prototype to avoid an error. */
+#ifdef __cplusplus
+extern "C"
+#endif
+/* We use char because int might match the return type of a gcc2
+ builtin and then its argument prototype would still apply. */
+char dnet_ntoa();
+
+int main() {
+dnet_ntoa()
+; return 0; }
+EOF
+if { (eval echo configure:1377: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
+ rm -rf conftest*
+ eval "ac_cv_lib_$ac_lib_var=yes"
+else
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+ rm -rf conftest*
+ eval "ac_cv_lib_$ac_lib_var=no"
+fi
+rm -f conftest*
+LIBS="$ac_save_LIBS"
+
+fi
+if eval "test \"`echo '$ac_cv_lib_'$ac_lib_var`\" = yes"; then
+ echo "$ac_t""yes" 1>&6
+ X_EXTRA_LIBS="$X_EXTRA_LIBS -ldnet"
+else
+ echo "$ac_t""no" 1>&6
+fi
+
+ if test $ac_cv_lib_dnet_dnet_ntoa = no; then
+ echo $ac_n "checking for dnet_ntoa in -ldnet_stub""... $ac_c" 1>&6
+echo "configure:1399: checking for dnet_ntoa in -ldnet_stub" >&5
+ac_lib_var=`echo dnet_stub'_'dnet_ntoa | sed 'y%./+-%__p_%'`
+if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ ac_save_LIBS="$LIBS"
+LIBS="-ldnet_stub $LIBS"
+cat > conftest.$ac_ext <<EOF
+#line 1407 "configure"
+#include "confdefs.h"
+/* Override any gcc2 internal prototype to avoid an error. */
+#ifdef __cplusplus
+extern "C"
+#endif
+/* We use char because int might match the return type of a gcc2
+ builtin and then its argument prototype would still apply. */
+char dnet_ntoa();
+
+int main() {
+dnet_ntoa()
+; return 0; }
+EOF
+if { (eval echo configure:1421: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
+ rm -rf conftest*
+ eval "ac_cv_lib_$ac_lib_var=yes"
+else
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+ rm -rf conftest*
+ eval "ac_cv_lib_$ac_lib_var=no"
+fi
+rm -f conftest*
+LIBS="$ac_save_LIBS"
+
+fi
+if eval "test \"`echo '$ac_cv_lib_'$ac_lib_var`\" = yes"; then
+ echo "$ac_t""yes" 1>&6
+ X_EXTRA_LIBS="$X_EXTRA_LIBS -ldnet_stub"
+else
+ echo "$ac_t""no" 1>&6
+fi
+
+ fi
+
+ # msh@cis.ufl.edu says -lnsl (and -lsocket) are needed for his 386/AT,
+ # to get the SysV transport functions.
+ # chad@anasazi.com says the Pyramis MIS-ES running DC/OSx (SVR4)
+ # needs -lnsl.
+ # The nsl library prevents programs from opening the X display
+ # on Irix 5.2, according to dickey@clark.net.
+ echo $ac_n "checking for gethostbyname""... $ac_c" 1>&6
+echo "configure:1450: checking for gethostbyname" >&5
+if eval "test \"`echo '$''{'ac_cv_func_gethostbyname'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ cat > conftest.$ac_ext <<EOF
+#line 1455 "configure"
+#include "confdefs.h"
+/* System header to define __stub macros and hopefully few prototypes,
+ which can conflict with char gethostbyname(); below. */
+#include <assert.h>
+/* Override any gcc2 internal prototype to avoid an error. */
+#ifdef __cplusplus
+extern "C"
+#endif
+/* We use char because int might match the return type of a gcc2
+ builtin and then its argument prototype would still apply. */
+char gethostbyname();
+
+int main() {
+
+/* The GNU C library defines this for functions which it implements
+ to always fail with ENOSYS. Some functions are actually named
+ something starting with __ and the normal name is an alias. */
+#if defined (__stub_gethostbyname) || defined (__stub___gethostbyname)
+choke me
+#else
+gethostbyname();
+#endif
+
+; return 0; }
+EOF
+if { (eval echo configure:1481: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
+ rm -rf conftest*
+ eval "ac_cv_func_gethostbyname=yes"
+else
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+ rm -rf conftest*
+ eval "ac_cv_func_gethostbyname=no"
+fi
+rm -f conftest*
+fi
+
+if eval "test \"`echo '$ac_cv_func_'gethostbyname`\" = yes"; then
+ echo "$ac_t""yes" 1>&6
+ :
+else
+ echo "$ac_t""no" 1>&6
+fi
+
+ if test $ac_cv_func_gethostbyname = no; then
+ echo $ac_n "checking for gethostbyname in -lnsl""... $ac_c" 1>&6
+echo "configure:1502: checking for gethostbyname in -lnsl" >&5
+ac_lib_var=`echo nsl'_'gethostbyname | sed 'y%./+-%__p_%'`
+if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ ac_save_LIBS="$LIBS"
+LIBS="-lnsl $LIBS"
+cat > conftest.$ac_ext <<EOF
+#line 1510 "configure"
+#include "confdefs.h"
+/* Override any gcc2 internal prototype to avoid an error. */
+#ifdef __cplusplus
+extern "C"
+#endif
+/* We use char because int might match the return type of a gcc2
+ builtin and then its argument prototype would still apply. */
+char gethostbyname();
+
+int main() {
+gethostbyname()
+; return 0; }
+EOF
+if { (eval echo configure:1524: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
+ rm -rf conftest*
+ eval "ac_cv_lib_$ac_lib_var=yes"
+else
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+ rm -rf conftest*
+ eval "ac_cv_lib_$ac_lib_var=no"
+fi
+rm -f conftest*
+LIBS="$ac_save_LIBS"
+
+fi
+if eval "test \"`echo '$ac_cv_lib_'$ac_lib_var`\" = yes"; then
+ echo "$ac_t""yes" 1>&6
+ X_EXTRA_LIBS="$X_EXTRA_LIBS -lnsl"
+else
+ echo "$ac_t""no" 1>&6
+fi
+
+ fi
+
+ # lieder@skyler.mavd.honeywell.com says without -lsocket,
+ # socket/setsockopt and other routines are undefined under SCO ODT
+ # 2.0. But -lsocket is broken on IRIX 5.2 (and is not necessary
+ # on later versions), says simon@lia.di.epfl.ch: it contains
+ # gethostby* variants that don't use the nameserver (or something).
+ # -lsocket must be given before -lnsl if both are needed.
+ # We assume that if connect needs -lnsl, so does gethostbyname.
+ echo $ac_n "checking for connect""... $ac_c" 1>&6
+echo "configure:1554: checking for connect" >&5
+if eval "test \"`echo '$''{'ac_cv_func_connect'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ cat > conftest.$ac_ext <<EOF
+#line 1559 "configure"
+#include "confdefs.h"
+/* System header to define __stub macros and hopefully few prototypes,
+ which can conflict with char connect(); below. */
+#include <assert.h>
+/* Override any gcc2 internal prototype to avoid an error. */
+#ifdef __cplusplus
+extern "C"
+#endif
+/* We use char because int might match the return type of a gcc2
+ builtin and then its argument prototype would still apply. */
+char connect();
+
+int main() {
+
+/* The GNU C library defines this for functions which it implements
+ to always fail with ENOSYS. Some functions are actually named
+ something starting with __ and the normal name is an alias. */
+#if defined (__stub_connect) || defined (__stub___connect)
+choke me
+#else
+connect();
+#endif
+
+; return 0; }
+EOF
+if { (eval echo configure:1585: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
+ rm -rf conftest*
+ eval "ac_cv_func_connect=yes"
+else
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+ rm -rf conftest*
+ eval "ac_cv_func_connect=no"
+fi
+rm -f conftest*
+fi
+
+if eval "test \"`echo '$ac_cv_func_'connect`\" = yes"; then
+ echo "$ac_t""yes" 1>&6
+ :
+else
+ echo "$ac_t""no" 1>&6
+fi
+
+ if test $ac_cv_func_connect = no; then
+ echo $ac_n "checking for connect in -lsocket""... $ac_c" 1>&6
+echo "configure:1606: checking for connect in -lsocket" >&5
+ac_lib_var=`echo socket'_'connect | sed 'y%./+-%__p_%'`
+if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ ac_save_LIBS="$LIBS"
+LIBS="-lsocket $X_EXTRA_LIBS $LIBS"
+cat > conftest.$ac_ext <<EOF
+#line 1614 "configure"
+#include "confdefs.h"
+/* Override any gcc2 internal prototype to avoid an error. */
+#ifdef __cplusplus
+extern "C"
+#endif
+/* We use char because int might match the return type of a gcc2
+ builtin and then its argument prototype would still apply. */
+char connect();
+
+int main() {
+connect()
+; return 0; }
+EOF
+if { (eval echo configure:1628: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
+ rm -rf conftest*
+ eval "ac_cv_lib_$ac_lib_var=yes"
+else
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+ rm -rf conftest*
+ eval "ac_cv_lib_$ac_lib_var=no"
+fi
+rm -f conftest*
+LIBS="$ac_save_LIBS"
+
+fi
+if eval "test \"`echo '$ac_cv_lib_'$ac_lib_var`\" = yes"; then
+ echo "$ac_t""yes" 1>&6
+ X_EXTRA_LIBS="-lsocket $X_EXTRA_LIBS"
+else
+ echo "$ac_t""no" 1>&6
+fi
+
+ fi
+
+ # gomez@mi.uni-erlangen.de says -lposix is necessary on A/UX.
+ echo $ac_n "checking for remove""... $ac_c" 1>&6
+echo "configure:1652: checking for remove" >&5
+if eval "test \"`echo '$''{'ac_cv_func_remove'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ cat > conftest.$ac_ext <<EOF
+#line 1657 "configure"
+#include "confdefs.h"
+/* System header to define __stub macros and hopefully few prototypes,
+ which can conflict with char remove(); below. */
+#include <assert.h>
+/* Override any gcc2 internal prototype to avoid an error. */
+#ifdef __cplusplus
+extern "C"
+#endif
+/* We use char because int might match the return type of a gcc2
+ builtin and then its argument prototype would still apply. */
+char remove();
+
+int main() {
+
+/* The GNU C library defines this for functions which it implements
+ to always fail with ENOSYS. Some functions are actually named
+ something starting with __ and the normal name is an alias. */
+#if defined (__stub_remove) || defined (__stub___remove)
+choke me
+#else
+remove();
+#endif
+
+; return 0; }
+EOF
+if { (eval echo configure:1683: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
+ rm -rf conftest*
+ eval "ac_cv_func_remove=yes"
+else
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+ rm -rf conftest*
+ eval "ac_cv_func_remove=no"
+fi
+rm -f conftest*
+fi
+
+if eval "test \"`echo '$ac_cv_func_'remove`\" = yes"; then
+ echo "$ac_t""yes" 1>&6
+ :
+else
+ echo "$ac_t""no" 1>&6
+fi
+
+ if test $ac_cv_func_remove = no; then
+ echo $ac_n "checking for remove in -lposix""... $ac_c" 1>&6
+echo "configure:1704: checking for remove in -lposix" >&5
+ac_lib_var=`echo posix'_'remove | sed 'y%./+-%__p_%'`
+if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ ac_save_LIBS="$LIBS"
+LIBS="-lposix $LIBS"
+cat > conftest.$ac_ext <<EOF
+#line 1712 "configure"
+#include "confdefs.h"
+/* Override any gcc2 internal prototype to avoid an error. */
+#ifdef __cplusplus
+extern "C"
+#endif
+/* We use char because int might match the return type of a gcc2
+ builtin and then its argument prototype would still apply. */
+char remove();
+
+int main() {
+remove()
+; return 0; }
+EOF
+if { (eval echo configure:1726: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
+ rm -rf conftest*
+ eval "ac_cv_lib_$ac_lib_var=yes"
+else
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+ rm -rf conftest*
+ eval "ac_cv_lib_$ac_lib_var=no"
+fi
+rm -f conftest*
+LIBS="$ac_save_LIBS"
+
+fi
+if eval "test \"`echo '$ac_cv_lib_'$ac_lib_var`\" = yes"; then
+ echo "$ac_t""yes" 1>&6
+ X_EXTRA_LIBS="$X_EXTRA_LIBS -lposix"
+else
+ echo "$ac_t""no" 1>&6
+fi
+
+ fi
+
+ # BSDI BSD/OS 2.1 needs -lipc for XOpenDisplay.
+ echo $ac_n "checking for shmat""... $ac_c" 1>&6
+echo "configure:1750: checking for shmat" >&5
+if eval "test \"`echo '$''{'ac_cv_func_shmat'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ cat > conftest.$ac_ext <<EOF
+#line 1755 "configure"
+#include "confdefs.h"
+/* System header to define __stub macros and hopefully few prototypes,
+ which can conflict with char shmat(); below. */
+#include <assert.h>
+/* Override any gcc2 internal prototype to avoid an error. */
+#ifdef __cplusplus
+extern "C"
+#endif
+/* We use char because int might match the return type of a gcc2
+ builtin and then its argument prototype would still apply. */
+char shmat();
+
+int main() {
+
+/* The GNU C library defines this for functions which it implements
+ to always fail with ENOSYS. Some functions are actually named
+ something starting with __ and the normal name is an alias. */
+#if defined (__stub_shmat) || defined (__stub___shmat)
+choke me
+#else
+shmat();
+#endif
+
+; return 0; }
+EOF
+if { (eval echo configure:1781: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
+ rm -rf conftest*
+ eval "ac_cv_func_shmat=yes"
+else
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+ rm -rf conftest*
+ eval "ac_cv_func_shmat=no"
+fi
+rm -f conftest*
+fi
+
+if eval "test \"`echo '$ac_cv_func_'shmat`\" = yes"; then
+ echo "$ac_t""yes" 1>&6
+ :
+else
+ echo "$ac_t""no" 1>&6
+fi
+
+ if test $ac_cv_func_shmat = no; then
+ echo $ac_n "checking for shmat in -lipc""... $ac_c" 1>&6
+echo "configure:1802: checking for shmat in -lipc" >&5
+ac_lib_var=`echo ipc'_'shmat | sed 'y%./+-%__p_%'`
+if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ ac_save_LIBS="$LIBS"
+LIBS="-lipc $LIBS"
+cat > conftest.$ac_ext <<EOF
+#line 1810 "configure"
+#include "confdefs.h"
+/* Override any gcc2 internal prototype to avoid an error. */
+#ifdef __cplusplus
+extern "C"
+#endif
+/* We use char because int might match the return type of a gcc2
+ builtin and then its argument prototype would still apply. */
+char shmat();
+
+int main() {
+shmat()
+; return 0; }
+EOF
+if { (eval echo configure:1824: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
+ rm -rf conftest*
+ eval "ac_cv_lib_$ac_lib_var=yes"
+else
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+ rm -rf conftest*
+ eval "ac_cv_lib_$ac_lib_var=no"
+fi
+rm -f conftest*
+LIBS="$ac_save_LIBS"
+
+fi
+if eval "test \"`echo '$ac_cv_lib_'$ac_lib_var`\" = yes"; then
+ echo "$ac_t""yes" 1>&6
+ X_EXTRA_LIBS="$X_EXTRA_LIBS -lipc"
+else
+ echo "$ac_t""no" 1>&6
+fi
+
+ fi
+ fi
+
+ # Check for libraries that X11R6 Xt/Xaw programs need.
+ ac_save_LDFLAGS="$LDFLAGS"
+ test -n "$x_libraries" && LDFLAGS="$LDFLAGS -L$x_libraries"
+ # SM needs ICE to (dynamically) link under SunOS 4.x (so we have to
+ # check for ICE first), but we must link in the order -lSM -lICE or
+ # we get undefined symbols. So assume we have SM if we have ICE.
+ # These have to be linked with before -lX11, unlike the other
+ # libraries we check for below, so use a different variable.
+ # --interran@uluru.Stanford.EDU, kb@cs.umb.edu.
+ echo $ac_n "checking for IceConnectionNumber in -lICE""... $ac_c" 1>&6
+echo "configure:1857: checking for IceConnectionNumber in -lICE" >&5
+ac_lib_var=`echo ICE'_'IceConnectionNumber | sed 'y%./+-%__p_%'`
+if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ ac_save_LIBS="$LIBS"
+LIBS="-lICE $X_EXTRA_LIBS $LIBS"
+cat > conftest.$ac_ext <<EOF
+#line 1865 "configure"
+#include "confdefs.h"
+/* Override any gcc2 internal prototype to avoid an error. */
+#ifdef __cplusplus
+extern "C"
+#endif
+/* We use char because int might match the return type of a gcc2
+ builtin and then its argument prototype would still apply. */
+char IceConnectionNumber();
+
+int main() {
+IceConnectionNumber()
+; return 0; }
+EOF
+if { (eval echo configure:1879: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
+ rm -rf conftest*
+ eval "ac_cv_lib_$ac_lib_var=yes"
+else
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+ rm -rf conftest*
+ eval "ac_cv_lib_$ac_lib_var=no"
+fi
+rm -f conftest*
+LIBS="$ac_save_LIBS"
+
+fi
+if eval "test \"`echo '$ac_cv_lib_'$ac_lib_var`\" = yes"; then
+ echo "$ac_t""yes" 1>&6
+ X_PRE_LIBS="$X_PRE_LIBS -lSM -lICE"
+else
+ echo "$ac_t""no" 1>&6
+fi
+
+ LDFLAGS="$ac_save_LDFLAGS"
+
+fi
+
+
+# Check whether --with-installed-zlib or --without-installed-zlib was given.
+if test "${with_installed_zlib+set}" = set; then
+ withval="$with_installed_zlib"
+ :
+fi
+
+
+if test "$with_installed_zlib" = yes; then
+ echo "using installed zlib"
+ ZLIB_LIB=-lz
+else
+ ZLIB_DIR=zlib
+ ZLIB_INCLUDE='-I$(top_srcdir)/zlib'
+ ZLIB_LIB='$(top_srcdir)/zlib/libz.a'
+ echo "configuring zlib..."
+ (cd zlib; ./configure)
+ echo "...done configuring zlib"
+fi
+
+
+
+
+
+echo $ac_n "checking for vsnprintf""... $ac_c" 1>&6
+echo "configure:1928: checking for vsnprintf" >&5
+if eval "test \"`echo '$''{'ac_cv_func_vsnprintf'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ cat > conftest.$ac_ext <<EOF
+#line 1933 "configure"
+#include "confdefs.h"
+/* System header to define __stub macros and hopefully few prototypes,
+ which can conflict with char vsnprintf(); below. */
+#include <assert.h>
+/* Override any gcc2 internal prototype to avoid an error. */
+#ifdef __cplusplus
+extern "C"
+#endif
+/* We use char because int might match the return type of a gcc2
+ builtin and then its argument prototype would still apply. */
+char vsnprintf();
+
+int main() {
+
+/* The GNU C library defines this for functions which it implements
+ to always fail with ENOSYS. Some functions are actually named
+ something starting with __ and the normal name is an alias. */
+#if defined (__stub_vsnprintf) || defined (__stub___vsnprintf)
+choke me
+#else
+vsnprintf();
+#endif
+
+; return 0; }
+EOF
+if { (eval echo configure:1959: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
+ rm -rf conftest*
+ eval "ac_cv_func_vsnprintf=yes"
+else
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+ rm -rf conftest*
+ eval "ac_cv_func_vsnprintf=no"
+fi
+rm -f conftest*
+fi
+
+if eval "test \"`echo '$ac_cv_func_'vsnprintf`\" = yes"; then
+ echo "$ac_t""yes" 1>&6
+ VSNPRINTF_DEFINE='-DHAVE_VSNPRINTF'
+else
+ echo "$ac_t""no" 1>&6
+VSNPRINTF_DEFINE=
+fi
+
+
+
+echo $ac_n "checking for socklen_t""... $ac_c" 1>&6
+echo "configure:1982: checking for socklen_t" >&5
+cat > conftest.$ac_ext <<EOF
+#line 1984 "configure"
+#include "confdefs.h"
+#include <sys/types.h>
+ #include <sys/socket.h>
+int main() {
+socklen_t x;
+accept(0, 0, &x);
+; return 0; }
+EOF
+if { (eval echo configure:1993: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then
+ rm -rf conftest*
+ echo "$ac_t""yes" 1>&6
+SOCKLEN_T_DEFINE='-DVNC_SOCKLEN_T=socklen_t'
+else
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+ rm -rf conftest*
+ echo "$ac_t""using int" 1>&6
+SOCKLEN_T_DEFINE='-DVNC_SOCKLEN_T=int'
+fi
+rm -f conftest*
+
+
+BOILERPLATE=boilerplate.mk
+
+if (sh -c "make --version" 2>/dev/null | grep GNU 2>&1 >/dev/null); then
+ if sh -c "vncmkdepend" >/dev/null 2>&1; then
+ BOILERPLATE="$BOILERPLATE:depend.mk"
+ fi
+fi
+
+trap '' 1 2 15
+cat > confcache <<\EOF
+# This file is a shell script that caches the results of configure
+# tests run on this system so they can be shared between configure
+# scripts and configure runs. It is not useful on other systems.
+# If it contains results you don't want to keep, you may remove or edit it.
+#
+# By default, configure uses ./config.cache as the cache file,
+# creating it if it does not exist already. You can give configure
+# the --cache-file=FILE option to use a different cache file; that is
+# what configure does when it calls configure scripts in
+# subdirectories, so they share the cache.
+# Giving --cache-file=/dev/null disables caching, for debugging configure.
+# config.status only pays attention to the cache file if you give it the
+# --recheck option to rerun configure.
+#
+EOF
+# The following way of writing the cache mishandles newlines in values,
+# but we know of no workaround that is simple, portable, and efficient.
+# So, don't put newlines in cache variables' values.
+# Ultrix sh set writes to stderr and can't be redirected directly,
+# and sets the high bit in the cache file unless we assign to the vars.
+(set) 2>&1 |
+ case `(ac_space=' '; set | grep ac_space) 2>&1` in
+ *ac_space=\ *)
+ # `set' does not quote correctly, so add quotes (double-quote substitution
+ # turns \\\\ into \\, and sed turns \\ into \).
+ sed -n \
+ -e "s/'/'\\\\''/g" \
+ -e "s/^\\([a-zA-Z0-9_]*_cv_[a-zA-Z0-9_]*\\)=\\(.*\\)/\\1=\${\\1='\\2'}/p"
+ ;;
+ *)
+ # `set' quotes correctly as required by POSIX, so do not add quotes.
+ sed -n -e 's/^\([a-zA-Z0-9_]*_cv_[a-zA-Z0-9_]*\)=\(.*\)/\1=${\1=\2}/p'
+ ;;
+ esac >> confcache
+if cmp -s $cache_file confcache; then
+ :
+else
+ if test -w $cache_file; then
+ echo "updating cache $cache_file"
+ cat confcache > $cache_file
+ else
+ echo "not updating unwritable cache $cache_file"
+ fi
+fi
+rm -f confcache
+
+trap 'rm -fr conftest* confdefs* core core.* *.core $ac_clean_files; exit 1' 1 2 15
+
+test "x$prefix" = xNONE && prefix=$ac_default_prefix
+# Let make expand exec_prefix.
+test "x$exec_prefix" = xNONE && exec_prefix='${prefix}'
+
+# Any assignment to VPATH causes Sun make to only execute
+# the first set of double-colon rules, so remove it if not needed.
+# If there is a colon in the path, we need to keep it.
+if test "x$srcdir" = x.; then
+ ac_vpsub='/^[ ]*VPATH[ ]*=[^:]*$/d'
+fi
+
+trap 'rm -f $CONFIG_STATUS conftest*; exit 1' 1 2 15
+
+# Transform confdefs.h into DEFS.
+# Protect against shell expansion while executing Makefile rules.
+# Protect against Makefile macro expansion.
+cat > conftest.defs <<\EOF
+s%#define \([A-Za-z_][A-Za-z0-9_]*\) *\(.*\)%-D\1=\2%g
+s%[ `~#$^&*(){}\\|;'"<>?]%\\&%g
+s%\[%\\&%g
+s%\]%\\&%g
+s%\$%$$%g
+EOF
+DEFS=`sed -f conftest.defs confdefs.h | tr '\012' ' '`
+rm -f conftest.defs
+
+
+# Without the "./", some shells look in PATH for config.status.
+: ${CONFIG_STATUS=./config.status}
+
+echo creating $CONFIG_STATUS
+rm -f $CONFIG_STATUS
+cat > $CONFIG_STATUS <<EOF
+#! /bin/sh
+# Generated automatically by configure.
+# Run this file to recreate the current configuration.
+# This directory was configured as follows,
+# on host `(hostname || uname -n) 2>/dev/null | sed 1q`:
+#
+# $0 $ac_configure_args
+#
+# Compiler output produced by configure, useful for debugging
+# configure, is in ./config.log if it exists.
+
+ac_cs_usage="Usage: $CONFIG_STATUS [--recheck] [--version] [--help]"
+for ac_option
+do
+ case "\$ac_option" in
+ -recheck | --recheck | --rechec | --reche | --rech | --rec | --re | --r)
+ echo "running \${CONFIG_SHELL-/bin/sh} $0 $ac_configure_args --no-create --no-recursion"
+ exec \${CONFIG_SHELL-/bin/sh} $0 $ac_configure_args --no-create --no-recursion ;;
+ -version | --version | --versio | --versi | --vers | --ver | --ve | --v)
+ echo "$CONFIG_STATUS generated by autoconf version 2.13"
+ exit 0 ;;
+ -help | --help | --hel | --he | --h)
+ echo "\$ac_cs_usage"; exit 0 ;;
+ *) echo "\$ac_cs_usage"; exit 1 ;;
+ esac
+done
+
+ac_given_srcdir=$srcdir
+
+trap 'rm -fr `echo "Makefile:Makefile.in:$BOILERPLATE \
+ rdr/Makefile:rdr/Makefile.in:$BOILERPLATE \
+ network/Makefile:network/Makefile.in:$BOILERPLATE \
+ Xregion/Makefile:Xregion/Makefile.in:$BOILERPLATE \
+ rfb/Makefile:rfb/Makefile.in:$BOILERPLATE \
+ tx/Makefile:tx/Makefile.in:$BOILERPLATE \
+ x0vncserver/Makefile:x0vncserver/Makefile.in:$BOILERPLATE \
+ vncviewer/Makefile:vncviewer/Makefile.in:$BOILERPLATE \
+ vncconfig/Makefile:vncconfig/Makefile.in:$BOILERPLATE \
+ vncpasswd/Makefile:vncpasswd/Makefile.in:$BOILERPLATE \
+" | sed "s/:[^ ]*//g"` conftest*; exit 1' 1 2 15
+EOF
+cat >> $CONFIG_STATUS <<EOF
+
+# Protect against being on the right side of a sed subst in config.status.
+sed 's/%@/@@/; s/@%/@@/; s/%g\$/@g/; /@g\$/s/[\\\\&%]/\\\\&/g;
+ s/@@/%@/; s/@@/@%/; s/@g\$/%g/' > conftest.subs <<\\CEOF
+$ac_vpsub
+$extrasub
+s%@SHELL@%$SHELL%g
+s%@CFLAGS@%$CFLAGS%g
+s%@CPPFLAGS@%$CPPFLAGS%g
+s%@CXXFLAGS@%$CXXFLAGS%g
+s%@FFLAGS@%$FFLAGS%g
+s%@DEFS@%$DEFS%g
+s%@LDFLAGS@%$LDFLAGS%g
+s%@LIBS@%$LIBS%g
+s%@exec_prefix@%$exec_prefix%g
+s%@prefix@%$prefix%g
+s%@program_transform_name@%$program_transform_name%g
+s%@bindir@%$bindir%g
+s%@sbindir@%$sbindir%g
+s%@libexecdir@%$libexecdir%g
+s%@datadir@%$datadir%g
+s%@sysconfdir@%$sysconfdir%g
+s%@sharedstatedir@%$sharedstatedir%g
+s%@localstatedir@%$localstatedir%g
+s%@libdir@%$libdir%g
+s%@includedir@%$includedir%g
+s%@oldincludedir@%$oldincludedir%g
+s%@infodir@%$infodir%g
+s%@mandir@%$mandir%g
+s%@CC@%$CC%g
+s%@CXX@%$CXX%g
+s%@RANLIB@%$RANLIB%g
+s%@SET_MAKE@%$SET_MAKE%g
+s%@MITSHM_CPPFLAGS@%$MITSHM_CPPFLAGS%g
+s%@CXXCPP@%$CXXCPP%g
+s%@X_CFLAGS@%$X_CFLAGS%g
+s%@X_PRE_LIBS@%$X_PRE_LIBS%g
+s%@X_LIBS@%$X_LIBS%g
+s%@X_EXTRA_LIBS@%$X_EXTRA_LIBS%g
+s%@ZLIB_DIR@%$ZLIB_DIR%g
+s%@ZLIB_INCLUDE@%$ZLIB_INCLUDE%g
+s%@ZLIB_LIB@%$ZLIB_LIB%g
+s%@VSNPRINTF_DEFINE@%$VSNPRINTF_DEFINE%g
+s%@SOCKLEN_T_DEFINE@%$SOCKLEN_T_DEFINE%g
+
+CEOF
+EOF
+
+cat >> $CONFIG_STATUS <<\EOF
+
+# Split the substitutions into bite-sized pieces for seds with
+# small command number limits, like on Digital OSF/1 and HP-UX.
+ac_max_sed_cmds=90 # Maximum number of lines to put in a sed script.
+ac_file=1 # Number of current file.
+ac_beg=1 # First line for current file.
+ac_end=$ac_max_sed_cmds # Line after last line for current file.
+ac_more_lines=:
+ac_sed_cmds=""
+while $ac_more_lines; do
+ if test $ac_beg -gt 1; then
+ sed "1,${ac_beg}d; ${ac_end}q" conftest.subs > conftest.s$ac_file
+ else
+ sed "${ac_end}q" conftest.subs > conftest.s$ac_file
+ fi
+ if test ! -s conftest.s$ac_file; then
+ ac_more_lines=false
+ rm -f conftest.s$ac_file
+ else
+ if test -z "$ac_sed_cmds"; then
+ ac_sed_cmds="sed -f conftest.s$ac_file"
+ else
+ ac_sed_cmds="$ac_sed_cmds | sed -f conftest.s$ac_file"
+ fi
+ ac_file=`expr $ac_file + 1`
+ ac_beg=$ac_end
+ ac_end=`expr $ac_end + $ac_max_sed_cmds`
+ fi
+done
+if test -z "$ac_sed_cmds"; then
+ ac_sed_cmds=cat
+fi
+EOF
+
+cat >> $CONFIG_STATUS <<EOF
+
+CONFIG_FILES=\${CONFIG_FILES-"Makefile:Makefile.in:$BOILERPLATE \
+ rdr/Makefile:rdr/Makefile.in:$BOILERPLATE \
+ network/Makefile:network/Makefile.in:$BOILERPLATE \
+ Xregion/Makefile:Xregion/Makefile.in:$BOILERPLATE \
+ rfb/Makefile:rfb/Makefile.in:$BOILERPLATE \
+ tx/Makefile:tx/Makefile.in:$BOILERPLATE \
+ x0vncserver/Makefile:x0vncserver/Makefile.in:$BOILERPLATE \
+ vncviewer/Makefile:vncviewer/Makefile.in:$BOILERPLATE \
+ vncconfig/Makefile:vncconfig/Makefile.in:$BOILERPLATE \
+ vncpasswd/Makefile:vncpasswd/Makefile.in:$BOILERPLATE \
+"}
+EOF
+cat >> $CONFIG_STATUS <<\EOF
+for ac_file in .. $CONFIG_FILES; do if test "x$ac_file" != x..; then
+ # Support "outfile[:infile[:infile...]]", defaulting infile="outfile.in".
+ case "$ac_file" in
+ *:*) ac_file_in=`echo "$ac_file"|sed 's%[^:]*:%%'`
+ ac_file=`echo "$ac_file"|sed 's%:.*%%'` ;;
+ *) ac_file_in="${ac_file}.in" ;;
+ esac
+
+ # Adjust a relative srcdir, top_srcdir, and INSTALL for subdirectories.
+
+ # Remove last slash and all that follows it. Not all systems have dirname.
+ ac_dir=`echo $ac_file|sed 's%/[^/][^/]*$%%'`
+ if test "$ac_dir" != "$ac_file" && test "$ac_dir" != .; then
+ # The file is in a subdirectory.
+ test ! -d "$ac_dir" && mkdir "$ac_dir"
+ ac_dir_suffix="/`echo $ac_dir|sed 's%^\./%%'`"
+ # A "../" for each directory in $ac_dir_suffix.
+ ac_dots=`echo $ac_dir_suffix|sed 's%/[^/]*%../%g'`
+ else
+ ac_dir_suffix= ac_dots=
+ fi
+
+ case "$ac_given_srcdir" in
+ .) srcdir=.
+ if test -z "$ac_dots"; then top_srcdir=.
+ else top_srcdir=`echo $ac_dots|sed 's%/$%%'`; fi ;;
+ /*) srcdir="$ac_given_srcdir$ac_dir_suffix"; top_srcdir="$ac_given_srcdir" ;;
+ *) # Relative path.
+ srcdir="$ac_dots$ac_given_srcdir$ac_dir_suffix"
+ top_srcdir="$ac_dots$ac_given_srcdir" ;;
+ esac
+
+
+ echo creating "$ac_file"
+ rm -f "$ac_file"
+ configure_input="Generated automatically from `echo $ac_file_in|sed 's%.*/%%'` by configure."
+ case "$ac_file" in
+ *Makefile*) ac_comsub="1i\\
+# $configure_input" ;;
+ *) ac_comsub= ;;
+ esac
+
+ ac_file_inputs=`echo $ac_file_in|sed -e "s%^%$ac_given_srcdir/%" -e "s%:% $ac_given_srcdir/%g"`
+ sed -e "$ac_comsub
+s%@configure_input@%$configure_input%g
+s%@srcdir@%$srcdir%g
+s%@top_srcdir@%$top_srcdir%g
+" $ac_file_inputs | (eval "$ac_sed_cmds") > $ac_file
+fi; done
+rm -f conftest.s*
+
+EOF
+cat >> $CONFIG_STATUS <<EOF
+
+EOF
+cat >> $CONFIG_STATUS <<\EOF
+
+exit 0
+EOF
+chmod +x $CONFIG_STATUS
+rm -fr confdefs* $ac_clean_files
+test "$no_create" = yes || ${CONFIG_SHELL-/bin/sh} $CONFIG_STATUS || exit 1
+
diff --git a/configure.in b/configure.in
new file mode 100644
index 00000000..bb49f83b
--- /dev/null
+++ b/configure.in
@@ -0,0 +1,99 @@
+dnl Process this file with autoconf to produce a configure script.
+AC_INIT(rdr/InStream.h)
+
+dnl dirty hack to prevent use of -g in CFLAGS and CXXFLAGS
+ac_cv_prog_cc_g=no
+ac_cv_prog_cxx_g=no
+
+dnl Checks for programs.
+AC_PROG_CC
+AC_PROG_CXX
+AC_PROG_RANLIB
+AC_PROG_MAKE_SET
+AC_LANG_CPLUSPLUS
+
+case "`(uname -sr) 2>/dev/null`" in
+"SunOS 5"*)
+ SOLARIS=yes
+ USE_MITSHM=yes
+ ;;
+"Linux"*)
+ LINUX=yes
+ USE_MITSHM=yes
+ ;;
+esac
+
+if test "$USE_MITSHM" = yes; then
+ MITSHM_CPPFLAGS="-DMITSHM"
+fi
+AC_SUBST(MITSHM_CPPFLAGS)
+
+if test "$GCC" = yes; then
+ CFLAGS="$CFLAGS -Wall"
+ if test "$SOLARIS" = yes; then
+ CFLAGS="$CFLAGS -Wno-unknown-pragmas -Wno-implicit-int"
+ fi
+fi
+if test "$GXX" = yes; then
+ CXXFLAGS="$CXXFLAGS -Wall"
+ if test "$SOLARIS" = yes; then
+ CXXFLAGS="$CXXFLAGS -Wno-unknown-pragmas -Wno-implicit-int -fpermissive"
+ fi
+fi
+
+AC_PATH_XTRA
+
+AC_ARG_WITH(installed-zlib,
+[ --with-installed-zlib use the version of zlib which is installed on the
+ system instead of the one distributed with VNC])
+
+if test "$with_installed_zlib" = yes; then
+ echo "using installed zlib"
+ ZLIB_LIB=-lz
+else
+ ZLIB_DIR=zlib
+ ZLIB_INCLUDE='-I$(top_srcdir)/zlib'
+ ZLIB_LIB='$(top_srcdir)/zlib/libz.a'
+ echo "configuring zlib..."
+ (cd zlib; ./configure)
+ echo "...done configuring zlib"
+fi
+
+AC_SUBST(ZLIB_DIR)
+AC_SUBST(ZLIB_INCLUDE)
+AC_SUBST(ZLIB_LIB)
+
+AC_CHECK_FUNC(vsnprintf,VSNPRINTF_DEFINE='-DHAVE_VSNPRINTF',VSNPRINTF_DEFINE=)
+AC_SUBST(VSNPRINTF_DEFINE)
+
+AC_MSG_CHECKING(for socklen_t)
+AC_TRY_COMPILE(
+[#include <sys/types.h>
+ #include <sys/socket.h>],
+[socklen_t x;
+accept(0, 0, &x);],
+AC_MSG_RESULT(yes)
+SOCKLEN_T_DEFINE='-DVNC_SOCKLEN_T=socklen_t',
+AC_MSG_RESULT(using int)
+SOCKLEN_T_DEFINE='-DVNC_SOCKLEN_T=int')
+AC_SUBST(SOCKLEN_T_DEFINE)
+
+BOILERPLATE=boilerplate.mk
+
+if (sh -c "make --version" 2>/dev/null | grep GNU 2>&1 >/dev/null); then
+ if sh -c "vncmkdepend" >/dev/null 2>&1; then
+ BOILERPLATE="$BOILERPLATE:depend.mk"
+ fi
+fi
+
+AC_OUTPUT(Makefile:Makefile.in:$BOILERPLATE \
+ rdr/Makefile:rdr/Makefile.in:$BOILERPLATE \
+ network/Makefile:network/Makefile.in:$BOILERPLATE \
+ Xregion/Makefile:Xregion/Makefile.in:$BOILERPLATE \
+ rfb/Makefile:rfb/Makefile.in:$BOILERPLATE \
+ tx/Makefile:tx/Makefile.in:$BOILERPLATE \
+ x0vncserver/Makefile:x0vncserver/Makefile.in:$BOILERPLATE \
+ vncviewer/Makefile:vncviewer/Makefile.in:$BOILERPLATE \
+ vncconfig/Makefile:vncconfig/Makefile.in:$BOILERPLATE \
+ vncpasswd/Makefile:vncpasswd/Makefile.in:$BOILERPLATE \
+)
diff --git a/depend.mk b/depend.mk
new file mode 100644
index 00000000..51d4cd63
--- /dev/null
+++ b/depend.mk
@@ -0,0 +1,76 @@
+#
+# C / C++ header dependency stuff
+#
+# Needs GNU make and vncmkdepend, a hacked version of makedepend
+
+.SUFFIXES: .d
+
+CMAKEDEPEND = vncmkdepend
+CXXMAKEDEPEND = vncmkdepend
+
+#
+# The recommended method of doing dependency analysis in the GNU make manual
+# turns out to be painfully slow. This method is similar but it's
+# substantially faster and retains the desirable property that the user doesn't
+# need to manually invoke a "make depend" step.
+#
+# As with the method described in the manual, we generate a separate dependency
+# (.d) file for each source file. The .d file records the header files that
+# each C or C++ source file includes. Any source file recorded in SRCS or
+# CXXSRCS will cause us to try and include the corresponding .d file and GNU
+# make then treats each .d file as a target to be remade.
+#
+# Unlike the manual's method, the rule we provide for making the .d file is
+# actually a fake. All it does is record in a temporary file that the .d file
+# needs to be remade. But as well as all the .d files, we also try to include
+# a file called "depend.phony". This file never exists, but it causes GNU make
+# to try and make the target "depend.phony". The rule for depend.phony then
+# looks at the temporary files generated by the .d rules and then invokes the
+# "omkdepend" program on all of the source files in one go.
+#
+
+#
+# We use simple assignment here to remove any of the depend.tmp files
+# at the time make parses this bit.
+#
+
+dummyvariable := $(shell $(RM) cdepend.tmp cxxdepend.tmp)
+
+#
+# Now the "fake" rules for generating .d files.
+#
+
+%.d: %.c
+ @echo "$<" >> cdepend.tmp
+
+%.d: %.cxx
+ @echo "$<" >> cxxdepend.tmp
+
+#
+# The depend.phony rule which actually runs omkdepend.
+#
+
+depend.phony:
+ @if [ -f cdepend.tmp ]; then \
+ echo $(CMAKEDEPEND) $(ALL_CPPFLAGS) `cat cdepend.tmp`; \
+ $(CMAKEDEPEND) $(ALL_CPPFLAGS) `cat cdepend.tmp`; \
+ rm -f cdepend.tmp; \
+ fi; \
+ if [ -f cxxdepend.tmp ]; then \
+ echo $(CXXMAKEDEPEND) $(ALL_CPPFLAGS) `cat cxxdepend.tmp`; \
+ $(CXXMAKEDEPEND) $(ALL_CPPFLAGS) `cat cxxdepend.tmp`; \
+ rm -f cxxdepend.tmp; \
+ fi
+
+#
+# Now include the .d files and the "depend.phony" file which never exists.
+# For some reason GNU make evaluates the targets in reverse order, so we need
+# to include depend.phony first. The "-" tells make not to complain that it
+# can't find the file.
+#
+
+-include depend.phony
+
+ifdef SRCS
+-include $(patsubst %.c,%.d,$(patsubst %.cxx,%.d,$(SRCS)))
+endif
diff --git a/hpux.patch b/hpux.patch
new file mode 100644
index 00000000..389c6751
--- /dev/null
+++ b/hpux.patch
@@ -0,0 +1,255 @@
+*** xc.orig/config/cf/X11.tmpl Tue Jan 15 22:55:26 2002
+--- xc/config/cf/X11.tmpl Sun Sep 7 19:52:01 2003
+***************
+*** 3120,3126 ****
+ $(RM) index.raw file.nPS file.PS file.txt
+ #endif
+
+! #ifndef MakeSimpleDoc(file,srcs)
+ #define MakeSimpleDoc(file,srcs) MakeDepSimpleDoc(file,srcs,srcs)
+ #endif
+
+--- 3120,3126 ----
+ $(RM) index.raw file.nPS file.PS file.txt
+ #endif
+
+! #ifndef MakeSimpleDoc
+ #define MakeSimpleDoc(file,srcs) MakeDepSimpleDoc(file,srcs,srcs)
+ #endif
+
+*** xc.orig/config/cf/hp.cf Wed Jan 17 16:22:31 2001
+--- xc/config/cf/hp.cf Mon Sep 8 19:54:52 2003
+***************
+*** 131,137 ****
+ #endif
+ #define HPFastScrolling YES
+ #ifndef BuildServer
+! # define BuildServer __hp9000s700
+ #endif
+ #if OSMajorVersion < 10
+ #define NeedBerklib (BuildServer|BuildFontServer)
+--- 131,137 ----
+ #endif
+ #define HPFastScrolling YES
+ #ifndef BuildServer
+! # define BuildServer YES
+ #endif
+ #if OSMajorVersion < 10
+ #define NeedBerklib (BuildServer|BuildFontServer)
+***************
+*** 139,145 ****
+ #define XawI18nDefines -DHAS_WCHAR_H -DHAS_ISW_FUNCS
+
+ #if OSMajorVersion < 6 || (OSMajorVersion == 6 && OSMinorVersion < 2)
+! # define ConnectionFlags -DTCPCONN /* no unix sockets */
+ #endif
+
+ #if OSMajorVersion > 8
+--- 139,145 ----
+ #define XawI18nDefines -DHAS_WCHAR_H -DHAS_ISW_FUNCS
+
+ #if OSMajorVersion < 6 || (OSMajorVersion == 6 && OSMinorVersion < 2)
+! /*# define ConnectionFlags -DTCPCONN*/ /* no unix sockets */
+ #endif
+
+ #if OSMajorVersion > 8
+*** xc/config/cf/site.def.orig Tue Sep 9 17:42:56 2003
+--- xc/config/cf/site.def Tue Sep 9 17:43:07 2003
+***************
+*** 84,90 ****
+ #ifdef AfterVendorCF
+
+ #ifndef ProjectRoot
+! #define ProjectRoot /usr/X11R6
+ #endif
+
+ /*
+--- 84,90 ----
+ #ifdef AfterVendorCF
+
+ #ifndef ProjectRoot
+! /*#define ProjectRoot /usr/X11R6*/
+ #endif
+
+ /*
+*** xc.orig/config/imake/imake.c Fri Dec 14 19:53:18 2001
+--- xc/config/imake/imake.c Mon Sep 8 19:35:33 2003
+***************
+*** 288,293 ****
+--- 288,294 ----
+ #define TRUE 1
+ #define FALSE 0
+
++ #define FIXUP_CPP_WHITESPACE
+ #ifdef FIXUP_CPP_WHITESPACE
+ int InRule = FALSE;
+ # ifdef INLINE_SYNTAX
+***************
+*** 389,394 ****
+--- 390,401 ----
+ FILE *tmpfd = NULL;
+ char makeMacro[ BUFSIZ ];
+ char makefileMacro[ BUFSIZ ];
++
++ #ifdef FIXUP_CPP_WHITESPACE
++ fprintf(stderr,"\nFIXUP_CPP_WHITESPACE is defined!!\n\n");
++ #else
++ #error "FIXUP_CPP_WHITESPACE is not defined"
++ #endif
+
+ program = argv[0];
+ init();
+*** xc.orig/config/imake/imakemdep.h Fri Dec 14 19:53:19 2001
+--- xc/config/imake/imakemdep.h Tue Sep 9 16:38:18 2003
+***************
+*** 48,54 ****
+ #ifdef hp9000s800
+ #define imake_ccflags "-DSYSV"
+ #else
+! #define imake_ccflags "-Wc,-Nd4000,-Ns3000 -DSYSV"
+ #endif
+ #endif
+
+--- 48,54 ----
+ #ifdef hp9000s800
+ #define imake_ccflags "-DSYSV"
+ #else
+! #define imake_ccflags "-DSYSV"
+ #endif
+ #endif
+
+***************
+*** 211,217 ****
+ * all colons). One way to tell if you need this is to see whether or not
+ * your Makefiles have no tabs in them and lots of @@ strings.
+ */
+! #if defined(sun) || defined(SYSV) || defined(SVR4) || defined(hcx) || defined(WIN32) || defined(sco) || (defined(AMOEBA) && defined(CROSS_COMPILE)) || defined(__QNX__) || defined(__sgi)
+ #define FIXUP_CPP_WHITESPACE
+ #endif
+ #ifdef WIN32
+--- 211,217 ----
+ * all colons). One way to tell if you need this is to see whether or not
+ * your Makefiles have no tabs in them and lots of @@ strings.
+ */
+! #if defined(sun) || defined(SYSV) || defined(SVR4) || defined(hcx) || defined(WIN32) || defined(sco) || (defined(AMOEBA) && defined(CROSS_COMPILE)) || defined(__QNX__) || defined(__sgi) || defined(hpux)
+ #define FIXUP_CPP_WHITESPACE
+ #endif
+ #ifdef WIN32
+*** xc.orig/include/Xfuncs.h Fri Dec 14 19:53:25 2001
+--- xc/include/Xfuncs.h Sun Sep 7 20:10:35 2003
+***************
+*** 42,48 ****
+ #else
+ #if defined(SYSV)
+ #include <memory.h>
+! void bcopy();
+ #define bzero(b,len) memset(b, 0, len)
+ #define bcmp(b1,b2,len) memcmp(b1, b2, len)
+ #elif defined(__EMX__)
+--- 42,48 ----
+ #else
+ #if defined(SYSV)
+ #include <memory.h>
+! /*void bcopy();*/
+ #define bzero(b,len) memset(b, 0, len)
+ #define bcmp(b1,b2,len) memcmp(b1, b2, len)
+ #elif defined(__EMX__)
+*** xc.orig/include/extensions/security.h Fri Dec 14 19:53:29 2001
+--- xc/include/extensions/security.h Fri Aug 1 17:43:44 2003
+***************
+*** 110,115 ****
+--- 110,116 ----
+
+ #include "input.h" /* for DeviceIntPtr */
+ #include "property.h" /* for PropertyPtr */
++ #include "resource.h"
+
+ /* resource type to pass in LookupIDByType for authorizations */
+ extern RESTYPE SecurityAuthorizationResType;
+*** xc.orig/lib/font/Type1/fontfcn.c Fri Nov 23 19:21:31 2001
+--- xc/lib/font/Type1/fontfcn.c Sun Sep 7 19:29:27 2003
+***************
+*** 47,52 ****
+--- 47,53 ----
+ */
+ /* $XFree86: xc/lib/font/Type1/fontfcn.c,v 1.11 2001/11/23 19:21:31 dawes Exp $ */
+
++ #include <stdlib.h>
+ #ifndef FONTMODULE
+ #include <stdio.h>
+ #include <string.h>
+*** xc.orig/lib/font/Type1/objects.h Mon Aug 27 20:49:52 2001
+--- xc/lib/font/Type1/objects.h Sun Sep 7 19:29:37 2003
+***************
+*** 50,56 ****
+ #include <Xdefs.h>
+ #include <Xfuncproto.h>
+ #ifndef FONTMODULE
+! #include <stdlib.h>
+ #endif
+ /*SHARED*/
+
+--- 50,56 ----
+ #include <Xdefs.h>
+ #include <Xfuncproto.h>
+ #ifndef FONTMODULE
+! /*#include <stdlib.h>*/
+ #endif
+ /*SHARED*/
+
+*** xc.orig/lib/xtrans/Xtransutil.c Tue Sep 9 17:40:14 2003
+--- xc/lib/xtrans/Xtransutil.c Tue Sep 9 17:40:20 2003
+***************
+*** 503,514 ****
+ if (updateOwner && !updatedOwner) {
+ PRMSG(1, "mkdir: Owner of %s should be set to root\n",
+ path, 0, 0);
+- sleep(5);
+ }
+ if (updateMode && !updatedMode) {
+ PRMSG(1, "mkdir: Mode of %s should be set to %04o\n",
+ path, mode, 0);
+- sleep(5);
+ }
+ return 0;
+ }
+--- 503,512 ----
+*** xc.orig/programs/Xserver/vnc/Xvnc/xvnc.cc 12 Aug 2003 11:00:14 -0000
+--- xc/programs/Xserver/vnc/Xvnc/xvnc.cc 9 Sep 2003 16:15:53 -0000
+***************
+*** 1221,1223 ****
+--- 1221,1229 ----
+ miRegisterPointerDevice(screenInfo.screens[0], p);
+ (void)mieqInit ((DevicePtr)k, (DevicePtr)p);
+ }
++
++ extern "C" {
++ void XTestGenerateEvent() {}
++ void XTestGetPointerPos() {}
++ void XTestJumpPointer() {}
++ }
+*** xc.orig/config/cf/vnc.def 7 Jul 2003 09:51:22
+--- xc/config/cf/vnc.def 9 Sep 2003 15:54:23
+***************
+*** 9,14 ****
+--- 9,20 ----
+ #define XnestServer NO
+ #define XprtServer NO
+
++ #define BuildXKB NO
++ #define HasCplusplus YES
++ #define CplusplusCmd /opt/aCC/bin/aCC
++ #define CplusplusOptions -AA +W749 +W740
++ #define ProjectRoot /usr
++
+ #ifdef SunArchitecture
+ #define ProjectRoot /usr/openwin
+ #define HasGcc2 YES
+***************
+*** 29,32 ****
+--- 34,38 ----
+
+ #define ServerTarget(server,subdirs,objects,libs,syslibs) @@\
+ CCLINK = $(CXXENVSETUP) $(CXX) @@\
++ CCOPTIONS = -AA @@\
+ ServerTargetWithFlags(server,subdirs,objects,libs,syslibs,$(_NOOP_))
diff --git a/java/index.vnc b/java/index.vnc
new file mode 100644
index 00000000..aecb6131
--- /dev/null
+++ b/java/index.vnc
@@ -0,0 +1,13 @@
+<HTML>
+<HEAD>
+<TITLE>
+VNC viewer for Java
+</TITLE>
+</HEAD>
+<BODY>
+<APPLET CODE=vncviewer/VNCViewer.class ARCHIVE=vncviewer.jar
+ WIDTH=400 HEIGHT=250>
+<PARAM name="port" value="$PORT">
+</APPLET>
+</BODY>
+</HTML>
diff --git a/java/logo150x150.gif b/java/logo150x150.gif
new file mode 100644
index 00000000..f1699ba5
--- /dev/null
+++ b/java/logo150x150.gif
Binary files differ
diff --git a/java/vncviewer.jar b/java/vncviewer.jar
new file mode 100644
index 00000000..3c92b5bb
--- /dev/null
+++ b/java/vncviewer.jar
Binary files differ
diff --git a/logmessages/logmessages.dsp b/logmessages/logmessages.dsp
new file mode 100644
index 00000000..b2c86155
--- /dev/null
+++ b/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"
+# 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"
+# 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"
+# 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\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
+
+!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/logmessages/messages.h b/logmessages/messages.h
new file mode 100644
index 00000000..bfb8c56d
--- /dev/null
+++ b/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/logmessages/messages.mc b/logmessages/messages.mc
new file mode 100644
index 00000000..0bc8329e
--- /dev/null
+++ b/logmessages/messages.mc
@@ -0,0 +1,7 @@
+MessageId=0x1
+Severity=Success
+SymbolicName=VNC4LogMessage
+Language=English
+%1: %2
+
+
diff --git a/logmessages/messages.rc b/logmessages/messages.rc
new file mode 100644
index 00000000..0885a897
--- /dev/null
+++ b/logmessages/messages.rc
@@ -0,0 +1,2 @@
+LANGUAGE 0x9,0x1
+1 11 MSG00001.bin
diff --git a/network/Makefile.in b/network/Makefile.in
new file mode 100644
index 00000000..8aed303a
--- /dev/null
+++ b/network/Makefile.in
@@ -0,0 +1,17 @@
+
+SRCS = TcpSocket.cxx
+
+OBJS = $(SRCS:.cxx=.o)
+
+DIR_CPPFLAGS = -I$(top_srcdir) @SOCKLEN_T_DEFINE@
+
+library = libnetwork.a
+
+all:: $(library)
+
+$(library): $(OBJS)
+ rm -f $(library)
+ $(AR) $(library) $(OBJS)
+ $(RANLIB) $(library)
+
+# followed by boilerplate.mk
diff --git a/network/Socket.h b/network/Socket.h
new file mode 100644
index 00000000..a08afe53
--- /dev/null
+++ b/network/Socket.h
@@ -0,0 +1,129 @@
+/* Copyright (C) 2002-2004 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.
+ */
+
+// -=- Socket.h - abstract base-class for any kind of network stream/socket
+
+#ifndef __NETWORK_SOCKET_H__
+#define __NETWORK_SOCKET_H__
+
+#include <rdr/FdInStream.h>
+#include <rdr/FdOutStream.h>
+#include <rdr/Exception.h>
+
+namespace network {
+
+ class Socket {
+ public:
+ Socket(int fd)
+ : instream(new rdr::FdInStream(fd)),
+ outstream(new rdr::FdOutStream(fd)),
+ own_streams(true) {}
+ virtual ~Socket() {
+ if (own_streams) {
+ delete instream;
+ delete outstream;
+ }
+ }
+ rdr::FdInStream &inStream() {return *instream;}
+ rdr::FdOutStream &outStream() {return *outstream;}
+ int getFd() {return outstream->getFd();}
+ virtual void shutdown() = 0;
+
+ // information about this end of the socket
+ virtual char* getMyAddress() = 0; // a string e.g. "192.168.0.1"
+ virtual int getMyPort() = 0;
+ virtual char* getMyEndpoint() = 0; // <address>::<port>
+
+ // information about the remote end of the socket
+ virtual char* getPeerAddress() = 0; // a string e.g. "192.168.0.1"
+ virtual int getPeerPort() = 0;
+ virtual char* getPeerEndpoint() = 0; // <address>::<port>
+
+ // Is the remote end on the same machine?
+ virtual bool sameMachine() = 0;
+
+ protected:
+ Socket() : instream(0), outstream(0), own_streams(false) {}
+ Socket(rdr::FdInStream* i, rdr::FdOutStream* o, bool own)
+ : instream(i), outstream(o), own_streams(own) {}
+ rdr::FdInStream* instream;
+ rdr::FdOutStream* outstream;
+ bool own_streams;
+ };
+
+ class ConnectionFilter {
+ public:
+ virtual bool verifyConnection(Socket* s) = 0;
+ virtual bool queryUserAcceptConnection(Socket*) {return false;}
+ };
+
+ class SocketListener {
+ public:
+ SocketListener() : fd(0), filter(0) {}
+ virtual ~SocketListener() {}
+
+ // shutdown() stops the socket from accepting further connections
+ virtual void shutdown() = 0;
+
+ // accept() returns a new Socket object if there is a connection
+ // attempt in progress AND if the connection passes the filter
+ // if one is installed. Otherwise, returns 0.
+ virtual Socket* accept() = 0;
+
+ void setFilter(ConnectionFilter* f) {filter = f;}
+ int getFd() {return fd;}
+ protected:
+ int fd;
+ ConnectionFilter* filter;
+ };
+
+ struct SocketException : public rdr::SystemException {
+ SocketException(const char* text, int err_) : rdr::SystemException(text, err_) {}
+ };
+
+ class SocketServer {
+ public:
+ virtual ~SocketServer() {}
+
+ // addClient() tells the server to manage the socket.
+ // If the server can't manage the socket, it must shutdown() it.
+ virtual void addClient(network::Socket* sock) = 0;
+
+ // processSocketEvent() tells the server there is a socket read event.
+ // If there is an error, or the socket has been closed/shutdown then
+ // the server MUST delete the socket AND return false.
+ virtual bool processSocketEvent(network::Socket* sock) = 0;
+
+ // checkTimeouts() allows the server to check socket timeouts, etc. The
+ // return value is the number of milliseconds to wait before
+ // checkTimeouts() should be called again. If this number is zero then
+ // there is no timeout and checkTimeouts() should be called the next time
+ // an event occurs.
+ virtual int checkTimeouts() = 0;
+
+ // soonestTimeout() is a function to help work out the soonest of several
+ // timeouts.
+ static void soonestTimeout(int* timeout, int newTimeout) {
+ if (newTimeout && (!*timeout || newTimeout < *timeout))
+ *timeout = newTimeout;
+ }
+ };
+
+}
+
+#endif // __NETWORK_SOCKET_H__
diff --git a/network/TcpSocket.cxx b/network/TcpSocket.cxx
new file mode 100644
index 00000000..b536e673
--- /dev/null
+++ b/network/TcpSocket.cxx
@@ -0,0 +1,458 @@
+/* Copyright (C) 2002-2004 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.
+ */
+
+#ifdef WIN32
+//#include <io.h>
+#include <winsock2.h>
+#define errorNumber WSAGetLastError()
+#define snprintf _snprintf
+#else
+#define errorNumber errno
+#define closesocket close
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <arpa/inet.h>
+#include <netinet/in.h>
+#include <netinet/tcp.h>
+#include <netdb.h>
+#include <unistd.h>
+#include <errno.h>
+#include <string.h>
+#include <signal.h>
+#include <fcntl.h>
+#endif
+
+#include <network/TcpSocket.h>
+#include <rfb/util.h>
+#include <rfb/LogWriter.h>
+
+#ifndef VNC_SOCKLEN_T
+#define VNC_SOCKLEN_T int
+#endif
+
+#ifndef INADDR_NONE
+#define INADDR_NONE ((unsigned long)-1)
+#endif
+
+using namespace network;
+using namespace rdr;
+
+static rfb::LogWriter vlog("TcpSocket");
+
+
+void
+TcpSocket::initTcpSockets() {
+#ifdef WIN32
+ WORD requiredVersion = MAKEWORD(2,0);
+ WSADATA initResult;
+
+ if (WSAStartup(requiredVersion, &initResult) != 0)
+ throw SocketException("unable to initialise Winsock2", errorNumber);
+#else
+ signal(SIGPIPE, SIG_IGN);
+#endif
+}
+
+// -=- TcpSocket
+
+TcpSocket::TcpSocket(int sock, bool close)
+ : Socket(new FdInStream(sock), new FdOutStream(sock), true), closeFd(close)
+{
+}
+
+TcpSocket::TcpSocket(const char *host, int port)
+ : closeFd(true)
+{
+ int sock;
+
+ // - Create a socket
+ if ((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0)
+ throw SocketException("unable to create socket", errorNumber);
+
+#ifndef WIN32
+ // - By default, close the socket on exec()
+ fcntl(sock, F_SETFD, FD_CLOEXEC);
+#endif
+
+ // - Connect it to something
+
+ // Try processing the host as an IP address
+ struct sockaddr_in addr;
+ memset(&addr, 0, sizeof(addr));
+ addr.sin_family = AF_INET;
+ addr.sin_addr.s_addr = inet_addr(host);
+ addr.sin_port = htons(port);
+ if ((int)addr.sin_addr.s_addr == -1) {
+ // Host was not an IP address - try resolving as DNS name
+ struct hostent *hostinfo;
+ hostinfo = gethostbyname(host);
+ if (hostinfo && hostinfo->h_addr) {
+ addr.sin_addr.s_addr = ((struct in_addr *)hostinfo->h_addr)->s_addr;
+ } else {
+ int e = errorNumber;
+ closesocket(sock);
+ throw SocketException("unable to resolve host by name", e);
+ }
+ }
+
+ // Attempt to connect to the remote host
+ if (connect(sock, (struct sockaddr *)&addr, sizeof(addr)) != 0) {
+ int e = errorNumber;
+ closesocket(sock);
+ throw SocketException("unable to connect to host", e);
+ }
+
+ int one = 1;
+ if (setsockopt(sock, IPPROTO_TCP, TCP_NODELAY,
+ (char *)&one, sizeof(one)) < 0) {
+ int e = errorNumber;
+ closesocket(sock);
+ throw SocketException("unable to setsockopt TCP_NODELAY", e);
+ }
+
+ // Create the input and output streams
+ instream = new FdInStream(sock);
+ outstream = new FdOutStream(sock);
+ own_streams = true;
+}
+
+TcpSocket::~TcpSocket() {
+ if (closeFd)
+ closesocket(getFd());
+}
+
+char* TcpSocket::getMyAddress() {
+ struct sockaddr_in info;
+ struct in_addr addr;
+ VNC_SOCKLEN_T info_size = sizeof(info);
+
+ getsockname(getFd(), (struct sockaddr *)&info, &info_size);
+ memcpy(&addr, &info.sin_addr, sizeof(addr));
+
+ char* name = inet_ntoa(addr);
+ if (name) {
+ return rfb::strDup(name);
+ } else {
+ return rfb::strDup("");
+ }
+}
+
+int TcpSocket::getMyPort() {
+ return getSockPort(getFd());
+}
+
+char* TcpSocket::getMyEndpoint() {
+ rfb::CharArray address; address.buf = getMyAddress();
+ int port = getMyPort();
+
+ int buflen = strlen(address.buf) + 32;
+ char* buffer = new char[buflen];
+ sprintf(buffer, "%s::%d", address.buf, port);
+ return buffer;
+}
+
+char* TcpSocket::getPeerAddress() {
+ struct sockaddr_in info;
+ struct in_addr addr;
+ VNC_SOCKLEN_T info_size = sizeof(info);
+
+ getpeername(getFd(), (struct sockaddr *)&info, &info_size);
+ memcpy(&addr, &info.sin_addr, sizeof(addr));
+
+ char* name = inet_ntoa(addr);
+ if (name) {
+ return rfb::strDup(name);
+ } else {
+ return rfb::strDup("");
+ }
+}
+
+int TcpSocket::getPeerPort() {
+ struct sockaddr_in info;
+ VNC_SOCKLEN_T info_size = sizeof(info);
+
+ getpeername(getFd(), (struct sockaddr *)&info, &info_size);
+ return ntohs(info.sin_port);
+}
+
+char* TcpSocket::getPeerEndpoint() {
+ rfb::CharArray address; address.buf = getPeerAddress();
+ int port = getPeerPort();
+
+ int buflen = strlen(address.buf) + 32;
+ char* buffer = new char[buflen];
+ sprintf(buffer, "%s::%d", address.buf, port);
+ return buffer;
+}
+
+bool TcpSocket::sameMachine() {
+ struct sockaddr_in peeraddr, myaddr;
+ VNC_SOCKLEN_T addrlen = sizeof(struct sockaddr_in);
+
+ getpeername(getFd(), (struct sockaddr *)&peeraddr, &addrlen);
+ getsockname(getFd(), (struct sockaddr *)&myaddr, &addrlen);
+
+ return (peeraddr.sin_addr.s_addr == myaddr.sin_addr.s_addr);
+}
+
+void TcpSocket::shutdown()
+{
+ ::shutdown(getFd(), 2);
+}
+
+bool TcpSocket::isSocket(int sock)
+{
+ struct sockaddr_in info;
+ VNC_SOCKLEN_T info_size = sizeof(info);
+ return getsockname(sock, (struct sockaddr *)&info, &info_size) >= 0;
+}
+
+bool TcpSocket::isConnected(int sock)
+{
+ struct sockaddr_in info;
+ VNC_SOCKLEN_T info_size = sizeof(info);
+ return getpeername(sock, (struct sockaddr *)&info, &info_size) >= 0;
+}
+
+int TcpSocket::getSockPort(int sock)
+{
+ struct sockaddr_in info;
+ VNC_SOCKLEN_T info_size = sizeof(info);
+ if (getsockname(sock, (struct sockaddr *)&info, &info_size) < 0)
+ return 0;
+ return ntohs(info.sin_port);
+}
+
+
+TcpListener::TcpListener(int port, bool localhostOnly, int sock, bool close_)
+ : closeFd(close_)
+{
+ if (sock != -1) {
+ fd = sock;
+ return;
+ }
+
+ if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
+ throw SocketException("unable to create listening socket", errorNumber);
+
+#ifndef WIN32
+ // - By default, close the socket on exec()
+ fcntl(sock, F_SETFD, FD_CLOEXEC);
+
+ int one = 1;
+ if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR,
+ (const char *)&one, sizeof(one)) < 0) {
+ int e = errorNumber;
+ closesocket(fd);
+ throw SocketException("unable to create listening socket", e);
+ }
+#endif
+
+ // - Bind it to the desired port
+ struct sockaddr_in addr;
+ memset(&addr, 0, sizeof(addr));
+ addr.sin_family = AF_INET;
+ addr.sin_port = htons(port);
+ if (localhostOnly)
+ addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
+ else
+ addr.sin_addr.s_addr = htonl(INADDR_ANY);
+ if (bind(fd, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
+ int e = errorNumber;
+ closesocket(fd);
+ throw SocketException("unable to bind listening socket", e);
+ }
+
+ // - Set it to be a listening socket
+ if (listen(fd, 5) < 0) {
+ int e = errorNumber;
+ closesocket(fd);
+ throw SocketException("unable to set socket to listening mode", e);
+ }
+}
+
+TcpListener::~TcpListener() {
+ if (closeFd) closesocket(fd);
+}
+
+void TcpListener::shutdown()
+{
+#ifdef WIN32
+ closesocket(getFd());
+#else
+ ::shutdown(getFd(), 2);
+#endif
+}
+
+
+Socket*
+TcpListener::accept() {
+ int new_sock = -1;
+
+ // Accept an incoming connection
+ if ((new_sock = ::accept(fd, 0, 0)) < 0)
+ throw SocketException("unable to accept new connection", errorNumber);
+
+#ifndef WIN32
+ // - By default, close the socket on exec()
+ fcntl(new_sock, F_SETFD, FD_CLOEXEC);
+#endif
+
+ // Disable Nagle's algorithm
+ int one = 1;
+ if (setsockopt(new_sock, IPPROTO_TCP, TCP_NODELAY,
+ (char *)&one, sizeof(one)) < 0) {
+ int e = errorNumber;
+ closesocket(new_sock);
+ throw SocketException("unable to setsockopt TCP_NODELAY", e);
+ }
+
+ // Create the socket object & check connection is allowed
+ TcpSocket* s = new TcpSocket(new_sock);
+ if (filter && !filter->verifyConnection(s)) {
+ delete s;
+ return 0;
+ }
+ return s;
+}
+
+void TcpListener::getMyAddresses(std::list<char*>* result) {
+ const hostent* addrs = gethostbyname(0);
+ if (addrs == 0)
+ throw rdr::SystemException("gethostbyname", errorNumber);
+ if (addrs->h_addrtype != AF_INET)
+ throw rdr::Exception("getMyAddresses: bad family");
+ for (int i=0; addrs->h_addr_list[i] != 0; i++) {
+ const char* addrC = inet_ntoa(*((struct in_addr*)addrs->h_addr_list[i]));
+ char* addr = new char[strlen(addrC)+1];
+ strcpy(addr, addrC);
+ result->push_back(addr);
+ }
+}
+
+int TcpListener::getMyPort() {
+ return TcpSocket::getSockPort(getFd());
+}
+
+
+TcpFilter::TcpFilter(const char* spec) {
+ rfb::CharArray tmp;
+ tmp.buf = rfb::strDup(spec);
+ while (tmp.buf) {
+ rfb::CharArray first;
+ rfb::strSplit(tmp.buf, ',', &first.buf, &tmp.buf);
+ if (strlen(first.buf))
+ filter.push_back(parsePattern(first.buf));
+ }
+}
+
+TcpFilter::~TcpFilter() {
+}
+
+
+static bool
+patternMatchIP(const TcpFilter::Pattern& pattern, const char* value) {
+ unsigned long address = inet_addr(value);
+ if (address == INADDR_NONE) return false;
+ return ((pattern.address & pattern.mask) == (address & pattern.mask));
+}
+
+bool
+TcpFilter::verifyConnection(Socket* s) {
+ rfb::CharArray name;
+
+ name.buf = s->getPeerAddress();
+ std::list<TcpFilter::Pattern>::iterator i;
+ for (i=filter.begin(); i!=filter.end(); i++) {
+ if (patternMatchIP(*i, name.buf)) {
+ switch ((*i).action) {
+ case Accept:
+ vlog.debug("ACCEPT %s", name.buf);
+ return true;
+ case Query:
+ vlog.debug("QUERY %s", name.buf);
+ return queryUserAcceptConnection(s);
+ case Reject:
+ vlog.debug("REJECT %s", name.buf);
+ return false;
+ }
+ }
+ }
+
+ vlog.debug("[REJECT] %s", name.buf);
+ return false;
+}
+
+
+TcpFilter::Pattern TcpFilter::parsePattern(const char* p) {
+ TcpFilter::Pattern pattern;
+
+ bool expandMask = false;
+ rfb::CharArray addr, mask;
+
+ if (rfb::strSplit(&p[1], '/', &addr.buf, &mask.buf)) {
+ if (rfb::strContains(mask.buf, '.')) {
+ pattern.mask = inet_addr(mask.buf);
+ } else {
+ pattern.mask = atoi(mask.buf);
+ expandMask = true;
+ }
+ } else {
+ pattern.mask = 32;
+ expandMask = true;
+ }
+ if (expandMask) {
+ unsigned long expanded = 0;
+ // *** check endianness!
+ for (int i=0; i<(int)pattern.mask; i++)
+ expanded |= 1<<(31-i);
+ pattern.mask = htonl(expanded);
+ }
+
+ pattern.address = inet_addr(addr.buf) & pattern.mask;
+ if ((pattern.address == INADDR_NONE) ||
+ (pattern.address == 0)) pattern.mask = 0;
+
+ switch(p[0]) {
+ case '+': pattern.action = TcpFilter::Accept; break;
+ case '-': pattern.action = TcpFilter::Reject; break;
+ case '?': pattern.action = TcpFilter::Query; break;
+ };
+
+ return pattern;
+}
+
+char* TcpFilter::patternToStr(const TcpFilter::Pattern& p) {
+ in_addr tmp;
+ rfb::CharArray addr, mask;
+ tmp.s_addr = p.address;
+ addr.buf = rfb::strDup(inet_ntoa(tmp));
+ tmp.s_addr = p.mask;
+ mask.buf = rfb::strDup(inet_ntoa(tmp));
+ char* result = new char[strlen(addr.buf)+1+strlen(mask.buf)+1+1];
+ switch (p.action) {
+ case Accept: result[0] = '+'; break;
+ case Reject: result[0] = '-'; break;
+ case Query: result[0] = '?'; break;
+ };
+ result[1] = 0;
+ strcat(result, addr.buf);
+ strcat(result, "/");
+ strcat(result, mask.buf);
+ return result;
+}
diff --git a/network/TcpSocket.h b/network/TcpSocket.h
new file mode 100644
index 00000000..95333402
--- /dev/null
+++ b/network/TcpSocket.h
@@ -0,0 +1,100 @@
+/* Copyright (C) 2002-2004 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.
+ */
+
+// -=- TcpSocket.h - base-class for TCP stream sockets.
+// This header also defines the TcpListener class, used
+// to listen for incoming socket connections over TCP
+//
+// NB: Any file descriptors created by the TcpSocket or
+// TcpListener classes are close-on-exec if the OS supports
+// it. TcpSockets initialised with a caller-supplied fd
+// are NOT set to close-on-exec.
+
+#ifndef __NETWORK_TCP_SOCKET_H__
+#define __NETWORK_TCP_SOCKET_H__
+
+#include <network/Socket.h>
+
+#include <list>
+
+namespace network {
+
+ class TcpSocket : public Socket {
+ public:
+ TcpSocket(int sock, bool close=true);
+ TcpSocket(const char *name, int port);
+ virtual ~TcpSocket();
+
+ virtual char* getMyAddress();
+ virtual int getMyPort();
+ virtual char* getMyEndpoint();
+
+ virtual char* getPeerAddress();
+ virtual int getPeerPort();
+ virtual char* getPeerEndpoint();
+ virtual bool sameMachine();
+
+ virtual void shutdown();
+
+ static void initTcpSockets();
+
+ static bool isSocket(int sock);
+ static bool isConnected(int sock);
+ static int getSockPort(int sock);
+ private:
+ bool closeFd;
+ };
+
+ class TcpListener : public SocketListener {
+ public:
+ TcpListener(int port, bool localhostOnly=false, int sock=-1,
+ bool close=true);
+ virtual ~TcpListener();
+
+ virtual void shutdown();
+ virtual Socket* accept();
+
+ void getMyAddresses(std::list<char*>* addrs);
+ int getMyPort();
+
+ private:
+ bool closeFd;
+ };
+
+ class TcpFilter : public ConnectionFilter {
+ public:
+ TcpFilter(const char* filter);
+ virtual ~TcpFilter();
+
+ virtual bool verifyConnection(Socket* s);
+
+ typedef enum {Accept, Reject, Query} Action;
+ struct Pattern {
+ Action action;
+ unsigned long address;
+ unsigned long mask;
+ };
+ static Pattern parsePattern(const char* s);
+ static char* patternToStr(const Pattern& p);
+ protected:
+ std::list<Pattern> filter;
+ };
+
+}
+
+#endif // __NETWORK_TCP_SOCKET_H__
diff --git a/network/msvcwarning.h b/network/msvcwarning.h
new file mode 100644
index 00000000..e93f2bbc
--- /dev/null
+++ b/network/msvcwarning.h
@@ -0,0 +1,18 @@
+/* Copyright (C) 2002-2003 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.
+ */
+#pragma warning( disable : 4800 ) // forcing bool 'true' or 'false'
diff --git a/network/network.dsp b/network/network.dsp
new file mode 100644
index 00000000..4290700c
--- /dev/null
+++ b/network/network.dsp
@@ -0,0 +1,129 @@
+# Microsoft Developer Studio Project File - Name="network" - Package Owner=<4>
+# Microsoft Developer Studio Generated Build File, Format Version 6.00
+# ** DO NOT EDIT **
+
+# TARGTYPE "Win32 (x86) Static Library" 0x0104
+
+CFG=network - 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 "network.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 "network.mak" CFG="network - Win32 Debug Unicode"
+!MESSAGE
+!MESSAGE Possible choices for configuration are:
+!MESSAGE
+!MESSAGE "network - Win32 Release" (based on "Win32 (x86) Static Library")
+!MESSAGE "network - Win32 Debug" (based on "Win32 (x86) Static Library")
+!MESSAGE "network - 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)" == "network - 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"
+# 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 /O1 /I ".." /FI"msvcwarning.h" /D "NDEBUG" /D "_LIB" /D "WIN32" /D "_MBCS" /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)" == "network - 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"
+# 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 ".." /FI"msvcwarning.h" /D "_DEBUG" /D "_LIB" /D "WIN32" /D "_MBCS" /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)" == "network - Win32 Debug Unicode"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 1
+# PROP BASE Output_Dir "network___Win32_Debug_Unicode"
+# PROP BASE Intermediate_Dir "network___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"
+# 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 ".." /FI"msvcwarning.h" /D "_LIB" /D "_DEBUG" /D "WIN32" /D "_UNICODE" /D "UNICODE" /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 "network - Win32 Release"
+# Name "network - Win32 Debug"
+# Name "network - Win32 Debug Unicode"
+# Begin Group "Source Files"
+
+# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat"
+# Begin Source File
+
+SOURCE=.\TcpSocket.cxx
+# End Source File
+# End Group
+# Begin Group "Header Files"
+
+# PROP Default_Filter "h;hpp;hxx;hm;inl"
+# Begin Source File
+
+SOURCE=.\Socket.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\TcpSocket.h
+# End Source File
+# End Group
+# End Target
+# End Project
diff --git a/rdr/Exception.cxx b/rdr/Exception.cxx
new file mode 100644
index 00000000..5f7799f1
--- /dev/null
+++ b/rdr/Exception.cxx
@@ -0,0 +1,64 @@
+/* Copyright (C) 2002-2003 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 <rdr/Exception.h>
+#ifdef _WIN32
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+#include <winsock2.h>
+#endif
+
+using namespace rdr;
+
+SystemException::SystemException(const char* s, int err_)
+ : Exception(s, "rdr::SystemException"), err(err_)
+{
+ strncat(str_, ": ", len-1-strlen(str_));
+#ifdef _WIN32
+ // Windows error messages are crap, so use unix ones for common errors.
+ const char* msg = 0;
+ switch (err) {
+ case WSAECONNREFUSED: msg = "Connection refused"; break;
+ case WSAETIMEDOUT: msg = "Connection timed out"; break;
+ case WSAECONNRESET: msg = "Connection reset by peer"; break;
+ case WSAECONNABORTED: msg = "Connection aborted"; break;
+ }
+ if (msg) {
+ strncat(str_, msg, len-1-strlen(str_));
+ } else {
+#ifdef UNICODE
+ WCHAR* tmsg = new WCHAR[len-strlen(str_)];
+ FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
+ 0, err, 0, tmsg, len-1-strlen(str_), 0);
+ WideCharToMultiByte(CP_ACP, 0, tmsg, wcslen(tmsg)+1,
+ str_+strlen(str_), len-strlen(str_), 0, 0);
+ delete [] tmsg;
+#else
+ FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
+ 0, err, 0, str_+strlen(str_), len-1-strlen(str_), 0);
+#endif
+ }
+
+#else
+ strncat(str_, strerror(err), len-1-strlen(str_));
+#endif
+ strncat(str_, " (", len-1-strlen(str_));
+ char buf[20];
+ sprintf(buf,"%d",err);
+ strncat(str_, buf, len-1-strlen(str_));
+ strncat(str_, ")", len-1-strlen(str_));
+}
diff --git a/rdr/Exception.h b/rdr/Exception.h
new file mode 100644
index 00000000..98b3f0e9
--- /dev/null
+++ b/rdr/Exception.h
@@ -0,0 +1,59 @@
+/* Copyright (C) 2002-2003 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 __RDR_EXCEPTION_H__
+#define __RDR_EXCEPTION_H__
+
+#include <stdio.h>
+#include <string.h>
+
+namespace rdr {
+
+ struct Exception {
+ enum { len = 256 };
+ char str_[len];
+ char type_[len];
+ Exception(const char* s=0, const char* e="rdr::Exception") {
+ str_[0] = 0;
+ if (s)
+ strncat(str_, s, len-1);
+ else
+ strcat(str_, "Exception");
+ type_[0] = 0;
+ strncat(type_, e, len-1);
+ }
+ virtual const char* str() const { return str_; }
+ virtual const char* type() const { return type_; }
+ };
+
+ struct SystemException : public Exception {
+ int err;
+ SystemException(const char* s, int err_);
+ };
+
+ struct TimedOut : public Exception {
+ TimedOut(const char* s="Timed out") : Exception(s,"rdr::TimedOut") {}
+ };
+
+ struct EndOfStream : public Exception {
+ EndOfStream(const char* s="End of stream")
+ : Exception(s,"rdr::EndOfStream") {}
+ };
+}
+
+#endif
diff --git a/rdr/FdInStream.cxx b/rdr/FdInStream.cxx
new file mode 100644
index 00000000..397847a4
--- /dev/null
+++ b/rdr/FdInStream.cxx
@@ -0,0 +1,281 @@
+/* Copyright (C) 2002-2003 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 <stdio.h>
+#include <string.h>
+#ifdef _WIN32
+#include <winsock2.h>
+#ifndef _WIN32_WCE
+#include <sys/timeb.h>
+#endif
+#define read(s,b,l) recv(s,(char*)b,l,0)
+#define close closesocket
+#undef errno
+#define errno WSAGetLastError()
+#undef EINTR
+#define EINTR WSAEINTR
+#else
+#include <sys/types.h>
+#include <errno.h>
+#include <unistd.h>
+#include <sys/time.h>
+#endif
+
+// XXX should use autoconf HAVE_SYS_SELECT_H
+#ifdef _AIX
+#include <sys/select.h>
+#endif
+
+#include <rdr/FdInStream.h>
+#include <rdr/Exception.h>
+
+using namespace rdr;
+
+enum { DEFAULT_BUF_SIZE = 8192,
+ MIN_BULK_SIZE = 1024 };
+
+FdInStream::FdInStream(int fd_, int timeoutms_, int bufSize_,
+ bool closeWhenDone_)
+ : fd(fd_), closeWhenDone(closeWhenDone_),
+ timeoutms(timeoutms_), blockCallback(0),
+ timing(false), timeWaitedIn100us(5), timedKbits(0),
+ bufSize(bufSize_ ? bufSize_ : DEFAULT_BUF_SIZE), offset(0)
+{
+ ptr = end = start = new U8[bufSize];
+}
+
+FdInStream::FdInStream(int fd_, FdInStreamBlockCallback* blockCallback_,
+ int bufSize_)
+ : fd(fd_), timeoutms(0), blockCallback(blockCallback_),
+ timing(false), timeWaitedIn100us(5), timedKbits(0),
+ bufSize(bufSize_ ? bufSize_ : DEFAULT_BUF_SIZE), offset(0)
+{
+ ptr = end = start = new U8[bufSize];
+}
+
+FdInStream::~FdInStream()
+{
+ delete [] start;
+ if (closeWhenDone) close(fd);
+}
+
+
+void FdInStream::setTimeout(int timeoutms_) {
+ timeoutms = timeoutms_;
+}
+
+void FdInStream::setBlockCallback(FdInStreamBlockCallback* blockCallback_)
+{
+ blockCallback = blockCallback_;
+ timeoutms = 0;
+}
+
+int FdInStream::pos()
+{
+ return offset + ptr - start;
+}
+
+void FdInStream::readBytes(void* data, int length)
+{
+ if (length < MIN_BULK_SIZE) {
+ InStream::readBytes(data, length);
+ return;
+ }
+
+ U8* dataPtr = (U8*)data;
+
+ int n = end - ptr;
+ if (n > length) n = length;
+
+ memcpy(dataPtr, ptr, n);
+ dataPtr += n;
+ length -= n;
+ ptr += n;
+
+ while (length > 0) {
+ n = readWithTimeoutOrCallback(dataPtr, length);
+ dataPtr += n;
+ length -= n;
+ offset += n;
+ }
+}
+
+
+int FdInStream::overrun(int itemSize, int nItems, bool wait)
+{
+ if (itemSize > bufSize)
+ throw Exception("FdInStream overrun: max itemSize exceeded");
+
+ if (end - ptr != 0)
+ memmove(start, ptr, end - ptr);
+
+ offset += ptr - start;
+ end -= ptr - start;
+ ptr = start;
+
+ while (end < start + itemSize) {
+ int n = readWithTimeoutOrCallback((U8*)end, start + bufSize - end, wait);
+ if (n == 0) return 0;
+ end += n;
+ }
+
+ if (itemSize * nItems > end - ptr)
+ nItems = (end - ptr) / itemSize;
+
+ return nItems;
+}
+
+#ifdef _WIN32
+static void gettimeofday(struct timeval* tv, void*)
+{
+ LARGE_INTEGER counts, countsPerSec;
+ static double usecPerCount = 0.0;
+
+ if (QueryPerformanceCounter(&counts)) {
+ if (usecPerCount == 0.0) {
+ QueryPerformanceFrequency(&countsPerSec);
+ usecPerCount = 1000000.0 / countsPerSec.QuadPart;
+ }
+
+ LONGLONG usecs = (LONGLONG)(counts.QuadPart * usecPerCount);
+ tv->tv_usec = (long)(usecs % 1000000);
+ tv->tv_sec = (long)(usecs / 1000000);
+
+ } else {
+#ifndef _WIN32_WCE
+ struct timeb tb;
+ ftime(&tb);
+ tv->tv_sec = tb.time;
+ tv->tv_usec = tb.millitm * 1000;
+#else
+ throw SystemException("QueryPerformanceCounter", GetLastError());
+#endif
+ }
+}
+#endif
+
+//
+// readWithTimeoutOrCallback() reads up to the given length in bytes from the
+// file descriptor into a buffer. If the wait argument is false, then zero is
+// returned if no bytes can be read without blocking. Otherwise if a
+// blockCallback is set, it will be called (repeatedly) instead of blocking.
+// If alternatively there is a timeout set and that timeout expires, it throws
+// a TimedOut exception. Otherwise it returns the number of bytes read. It
+// never attempts to read() unless select() indicates that the fd is readable -
+// this means it can be used on an fd which has been set non-blocking. It also
+// has to cope with the annoying possibility of both select() and read()
+// returning EINTR.
+//
+
+int FdInStream::readWithTimeoutOrCallback(void* buf, int len, bool wait)
+{
+ struct timeval before, after;
+ if (timing)
+ gettimeofday(&before, 0);
+
+ int n;
+ while (true) {
+ do {
+ fd_set fds;
+ struct timeval tv;
+ struct timeval* tvp = &tv;
+
+ if (!wait) {
+ tv.tv_sec = tv.tv_usec = 0;
+ } else if (timeoutms != -1) {
+ tv.tv_sec = timeoutms / 1000;
+ tv.tv_usec = (timeoutms % 1000) * 1000;
+ } else {
+ tvp = 0;
+ }
+
+ FD_ZERO(&fds);
+ FD_SET(fd, &fds);
+ n = select(fd+1, &fds, 0, 0, tvp);
+ } while (n < 0 && errno == EINTR);
+
+ if (n > 0) break;
+ if (n < 0) throw SystemException("select",errno);
+ if (!wait) return 0;
+ if (!blockCallback) throw TimedOut();
+
+ blockCallback->blockCallback();
+ }
+
+ do {
+ n = ::read(fd, buf, len);
+ } while (n < 0 && errno == EINTR);
+
+ if (n < 0) throw SystemException("read",errno);
+ if (n == 0) throw EndOfStream();
+
+ if (timing) {
+ gettimeofday(&after, 0);
+// fprintf(stderr,"%d.%06d\n",(after.tv_sec - before.tv_sec),
+// (after.tv_usec - before.tv_usec));
+ int newTimeWaited = ((after.tv_sec - before.tv_sec) * 10000 +
+ (after.tv_usec - before.tv_usec) / 100);
+ int newKbits = n * 8 / 1000;
+
+// if (newTimeWaited == 0) {
+// fprintf(stderr,"new kbps infinite t %d k %d\n",
+// newTimeWaited, newKbits);
+// } else {
+// fprintf(stderr,"new kbps %d t %d k %d\n",
+// newKbits * 10000 / newTimeWaited, newTimeWaited, newKbits);
+// }
+
+ // limit rate to between 10kbit/s and 40Mbit/s
+
+ if (newTimeWaited > newKbits*1000) newTimeWaited = newKbits*1000;
+ if (newTimeWaited < newKbits/4) newTimeWaited = newKbits/4;
+
+ timeWaitedIn100us += newTimeWaited;
+ timedKbits += newKbits;
+ }
+
+ return n;
+}
+
+void FdInStream::startTiming()
+{
+ timing = true;
+
+ // Carry over up to 1s worth of previous rate for smoothing.
+
+ if (timeWaitedIn100us > 10000) {
+ timedKbits = timedKbits * 10000 / timeWaitedIn100us;
+ timeWaitedIn100us = 10000;
+ }
+}
+
+void FdInStream::stopTiming()
+{
+ timing = false;
+ if (timeWaitedIn100us < timedKbits/2)
+ timeWaitedIn100us = timedKbits/2; // upper limit 20Mbit/s
+}
+
+unsigned int FdInStream::kbitsPerSecond()
+{
+ // The following calculation will overflow 32-bit arithmetic if we have
+ // received more than about 50Mbytes (400Mbits) since we started timing, so
+ // it should be OK for a single RFB update.
+
+ return timedKbits * 10000 / timeWaitedIn100us;
+}
diff --git a/rdr/FdInStream.h b/rdr/FdInStream.h
new file mode 100644
index 00000000..d038b1b3
--- /dev/null
+++ b/rdr/FdInStream.h
@@ -0,0 +1,77 @@
+/* Copyright (C) 2002-2003 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.
+ */
+
+//
+// FdInStream streams from a file descriptor.
+//
+
+#ifndef __RDR_FDINSTREAM_H__
+#define __RDR_FDINSTREAM_H__
+
+#include <rdr/InStream.h>
+
+namespace rdr {
+
+ class FdInStreamBlockCallback {
+ public:
+ virtual void blockCallback() = 0;
+ };
+
+ class FdInStream : public InStream {
+
+ public:
+
+ FdInStream(int fd, int timeoutms=-1, int bufSize=0,
+ bool closeWhenDone_=false);
+ FdInStream(int fd, FdInStreamBlockCallback* blockCallback, int bufSize=0);
+ virtual ~FdInStream();
+
+ void setTimeout(int timeoutms);
+ void setBlockCallback(FdInStreamBlockCallback* blockCallback);
+ int getFd() { return fd; }
+ int pos();
+ void readBytes(void* data, int length);
+
+ void startTiming();
+ void stopTiming();
+ unsigned int kbitsPerSecond();
+ unsigned int timeWaited() { return timeWaitedIn100us; }
+
+ protected:
+ int overrun(int itemSize, int nItems, bool wait);
+
+ private:
+ int readWithTimeoutOrCallback(void* buf, int len, bool wait=true);
+
+ int fd;
+ bool closeWhenDone;
+ int timeoutms;
+ FdInStreamBlockCallback* blockCallback;
+
+ bool timing;
+ unsigned int timeWaitedIn100us;
+ unsigned int timedKbits;
+
+ int bufSize;
+ int offset;
+ U8* start;
+ };
+
+} // end of namespace rdr
+
+#endif
diff --git a/rdr/FdOutStream.cxx b/rdr/FdOutStream.cxx
new file mode 100644
index 00000000..6795fc89
--- /dev/null
+++ b/rdr/FdOutStream.cxx
@@ -0,0 +1,174 @@
+/* Copyright (C) 2002-2003 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 <stdio.h>
+#include <string.h>
+#ifdef _WIN32
+#include <winsock2.h>
+#define write(s,b,l) send(s,(const char*)b,l,0)
+#define EWOULDBLOCK WSAEWOULDBLOCK
+#undef errno
+#define errno WSAGetLastError()
+#undef EINTR
+#define EINTR WSAEINTR
+#else
+#include <sys/types.h>
+#include <errno.h>
+#include <unistd.h>
+#include <sys/time.h>
+#endif
+
+#include <rdr/FdOutStream.h>
+#include <rdr/Exception.h>
+
+
+using namespace rdr;
+
+enum { DEFAULT_BUF_SIZE = 16384,
+ MIN_BULK_SIZE = 1024 };
+
+FdOutStream::FdOutStream(int fd_, int timeoutms_, int bufSize_)
+ : fd(fd_), timeoutms(timeoutms_),
+ bufSize(bufSize_ ? bufSize_ : DEFAULT_BUF_SIZE), offset(0)
+{
+ ptr = start = new U8[bufSize];
+ end = start + bufSize;
+}
+
+FdOutStream::~FdOutStream()
+{
+ try {
+ flush();
+ } catch (Exception&) {
+ }
+ delete [] start;
+}
+
+void FdOutStream::setTimeout(int timeoutms_) {
+ timeoutms = timeoutms_;
+}
+
+void FdOutStream::writeBytes(const void* data, int length)
+{
+ if (length < MIN_BULK_SIZE) {
+ OutStream::writeBytes(data, length);
+ return;
+ }
+
+ const U8* dataPtr = (const U8*)data;
+
+ flush();
+
+ while (length > 0) {
+ int n = writeWithTimeout(dataPtr, length);
+ length -= n;
+ dataPtr += n;
+ offset += n;
+ }
+}
+
+int FdOutStream::length()
+{
+ return offset + ptr - start;
+}
+
+void FdOutStream::flush()
+{
+ U8* sentUpTo = start;
+ while (sentUpTo < ptr) {
+ int n = writeWithTimeout((const void*) sentUpTo, ptr - sentUpTo);
+ sentUpTo += n;
+ offset += n;
+ }
+
+ ptr = start;
+}
+
+
+int FdOutStream::overrun(int itemSize, int nItems)
+{
+ if (itemSize > bufSize)
+ throw Exception("FdOutStream overrun: max itemSize exceeded");
+
+ flush();
+
+ if (itemSize * nItems > end - ptr)
+ nItems = (end - ptr) / itemSize;
+
+ return nItems;
+}
+
+//
+// writeWithTimeout() writes up to the given length in bytes from the given
+// buffer to the file descriptor. If there is a timeout set and that timeout
+// expires, it throws a TimedOut exception. Otherwise it returns the number of
+// bytes written. It never attempts to write() unless select() indicates that
+// the fd is writable - this means it can be used on an fd which has been set
+// non-blocking. It also has to cope with the annoying possibility of both
+// select() and write() returning EINTR.
+//
+
+int FdOutStream::writeWithTimeout(const void* data, int length)
+{
+ int n;
+
+ do {
+
+ do {
+ fd_set fds;
+ struct timeval tv;
+ struct timeval* tvp = &tv;
+
+ if (timeoutms != -1) {
+ tv.tv_sec = timeoutms / 1000;
+ tv.tv_usec = (timeoutms % 1000) * 1000;
+ } else {
+ tvp = 0;
+ }
+
+ FD_ZERO(&fds);
+ FD_SET(fd, &fds);
+#ifdef _WIN32_WCE
+ // NB: This fixes a broken Winsock2 select() behaviour. select()
+ // never returns for non-blocking sockets, unless they're already
+ // ready to be written to...
+ u_long zero = 0; ioctlsocket(fd, FIONBIO, &zero);
+#endif
+ n = select(fd+1, 0, &fds, 0, tvp);
+#ifdef _WIN32_WCE
+ u_long one = 0; ioctlsocket(fd, FIONBIO, &one);
+#endif
+ } while (n < 0 && errno == EINTR);
+
+ if (n < 0) throw SystemException("select",errno);
+
+ if (n == 0) throw TimedOut();
+
+ do {
+ n = ::write(fd, data, length);
+ } while (n < 0 && (errno == EINTR));
+
+ // NB: This outer loop simply fixes a broken Winsock2 EWOULDBLOCK
+ // condition, found only under Win98 (first edition), with slow
+ // network connections. Should in fact never ever happen...
+ } while (n < 0 && (errno == EWOULDBLOCK));
+
+ if (n < 0) throw SystemException("write",errno);
+
+ return n;
+}
diff --git a/rdr/FdOutStream.h b/rdr/FdOutStream.h
new file mode 100644
index 00000000..9c46db94
--- /dev/null
+++ b/rdr/FdOutStream.h
@@ -0,0 +1,56 @@
+/* Copyright (C) 2002-2003 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.
+ */
+
+//
+// FdOutStream streams to a file descriptor.
+//
+
+#ifndef __RDR_FDOUTSTREAM_H__
+#define __RDR_FDOUTSTREAM_H__
+
+#include <rdr/OutStream.h>
+
+namespace rdr {
+
+ class FdOutStream : public OutStream {
+
+ public:
+
+ FdOutStream(int fd, int timeoutms=-1, int bufSize=0);
+ virtual ~FdOutStream();
+
+ void setTimeout(int timeoutms);
+ int getFd() { return fd; }
+
+ void flush();
+ int length();
+ void writeBytes(const void* data, int length);
+
+ private:
+ int overrun(int itemSize, int nItems);
+ int writeWithTimeout(const void* data, int length);
+ int fd;
+ int timeoutms;
+ int bufSize;
+ int offset;
+ U8* start;
+ };
+
+}
+
+#endif
diff --git a/rdr/FixedMemOutStream.h b/rdr/FixedMemOutStream.h
new file mode 100644
index 00000000..f149e600
--- /dev/null
+++ b/rdr/FixedMemOutStream.h
@@ -0,0 +1,52 @@
+/* Copyright (C) 2002-2003 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.
+ */
+
+//
+// A FixedMemOutStream writes to a buffer of a fixed length.
+//
+
+#ifndef __RDR_FIXEDMEMOUTSTREAM_H__
+#define __RDR_FIXEDMEMOUTSTREAM_H__
+
+#include <rdr/OutStream.h>
+#include <rdr/Exception.h>
+
+namespace rdr {
+
+ class FixedMemOutStream : public OutStream {
+
+ public:
+
+ FixedMemOutStream(void* buf, int len) {
+ ptr = start = (U8*)buf;
+ end = start + len;
+ }
+
+ int length() { return ptr - start; }
+ void reposition(int pos) { ptr = start + pos; }
+ const void* data() { return (const void*)start; }
+
+ private:
+
+ int overrun(int itemSize, int nItems) { throw EndOfStream(); }
+ U8* start;
+ };
+
+}
+
+#endif
diff --git a/rdr/HexInStream.cxx b/rdr/HexInStream.cxx
new file mode 100644
index 00000000..a454ca8e
--- /dev/null
+++ b/rdr/HexInStream.cxx
@@ -0,0 +1,117 @@
+/* Copyright (C) 2002-2004 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 <rdr/HexInStream.h>
+#include <rdr/Exception.h>
+
+#include <stdlib.h>
+#include <ctype.h>
+
+using namespace rdr;
+
+const int DEFAULT_BUF_LEN = 16384;
+
+static inline int min(int a, int b) {return a<b ? a : b;}
+
+HexInStream::HexInStream(InStream& is, int bufSize_)
+: bufSize(bufSize_ ? bufSize_ : DEFAULT_BUF_LEN), offset(0), in_stream(is)
+{
+ ptr = end = start = new U8[bufSize];
+}
+
+HexInStream::~HexInStream() {
+ delete [] start;
+}
+
+
+bool HexInStream::readHexAndShift(char c, int* v) {
+ c=tolower(c);
+ if ((c >= '0') && (c <= '9'))
+ *v = (*v << 4) + (c - '0');
+ else if ((c >= 'a') && (c <= 'f'))
+ *v = (*v << 4) + (c - 'a' + 10);
+ else
+ return false;
+ return true;
+}
+
+bool HexInStream::hexStrToBin(const char* s, char** data, int* length) {
+ int l=strlen(s);
+ if ((l % 2) == 0) {
+ delete [] *data;
+ *data = 0; *length = 0;
+ if (l == 0)
+ return true;
+ *data = new char[l/2];
+ *length = l/2;
+ for(int i=0;i<l;i+=2) {
+ int byte = 0;
+ if (!readHexAndShift(s[i], &byte) ||
+ !readHexAndShift(s[i+1], &byte))
+ goto decodeError;
+ (*data)[i/2] = byte;
+ }
+ return true;
+ }
+decodeError:
+ delete [] *data;
+ *data = 0;
+ *length = 0;
+ return false;
+}
+
+
+int HexInStream::pos() {
+ return offset + ptr - start;
+}
+
+int HexInStream::overrun(int itemSize, int nItems, bool wait) {
+ if (itemSize > bufSize)
+ throw Exception("HexInStream overrun: max itemSize exceeded");
+
+ if (end - ptr != 0)
+ memmove(start, ptr, end - ptr);
+
+ end -= ptr - start;
+ offset += ptr - start;
+ ptr = start;
+
+ while (end < ptr + itemSize) {
+ int n = in_stream.check(2, 1, wait);
+ if (n == 0) return 0;
+ const U8* iptr = in_stream.getptr();
+ const U8* eptr = in_stream.getend();
+ int length = min((eptr - iptr)/2, start + bufSize - end);
+
+ U8* optr = (U8*) end;
+ for (int i=0; i<length; i++) {
+ int v = 0;
+ readHexAndShift(iptr[i*2], &v);
+ readHexAndShift(iptr[i*2+1], &v);
+ optr[i] = v;
+ }
+
+ in_stream.setptr(iptr + length*2);
+ end += length;
+ }
+
+ if (itemSize * nItems > end - ptr)
+ nItems = (end - ptr) / itemSize;
+
+ return nItems;
+}
diff --git a/rdr/HexInStream.h b/rdr/HexInStream.h
new file mode 100644
index 00000000..fbfc2738
--- /dev/null
+++ b/rdr/HexInStream.h
@@ -0,0 +1,50 @@
+/* Copyright (C) 2002-2003 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 __RDR_HEX_INSTREAM_H__
+#define __RDR_HEX_INSTREAM_H__
+
+#include <rdr/InStream.h>
+
+namespace rdr {
+
+ class HexInStream : public InStream {
+ public:
+
+ HexInStream(InStream& is, int bufSize=0);
+ virtual ~HexInStream();
+
+ int pos();
+
+ static bool readHexAndShift(char c, int* v);
+ static bool hexStrToBin(const char* s, char** data, int* length);
+
+ protected:
+ int overrun(int itemSize, int nItems, bool wait);
+
+ private:
+ int bufSize;
+ U8* start;
+ int offset;
+
+ InStream& in_stream;
+ };
+
+} // end of namespace rdr
+
+#endif
diff --git a/rdr/HexOutStream.cxx b/rdr/HexOutStream.cxx
new file mode 100644
index 00000000..f82d9f55
--- /dev/null
+++ b/rdr/HexOutStream.cxx
@@ -0,0 +1,110 @@
+/* Copyright (C) 2002-2004 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 <rdr/HexOutStream.h>
+#include <rdr/Exception.h>
+
+using namespace rdr;
+
+const int DEFAULT_BUF_LEN = 16384;
+
+static inline int min(int a, int b) {return a<b ? a : b;}
+
+HexOutStream::HexOutStream(OutStream& os, int buflen)
+: out_stream(os), offset(0), bufSize(buflen ? buflen : DEFAULT_BUF_LEN)
+{
+ if (bufSize % 2)
+ bufSize--;
+ ptr = start = new U8[bufSize];
+ end = start + bufSize;
+}
+
+HexOutStream::~HexOutStream() {
+ delete [] start;
+}
+
+
+char HexOutStream::intToHex(int i) {
+ if ((i>=0) && (i<=9))
+ return '0'+i;
+ else if ((i>=10) && (i<=15))
+ return 'a'+(i-10);
+ else
+ throw rdr::Exception("intToHex failed");
+}
+
+char* HexOutStream::binToHexStr(const char* data, int length) {
+ char* buffer = new char[length*2+1];
+ for (int i=0; i<length; i++) {
+ buffer[i*2] = intToHex((data[i] >> 4) & 15);
+ buffer[i*2+1] = intToHex((data[i] & 15));
+ if (!buffer[i*2] || !buffer[i*2+1]) {
+ delete [] buffer;
+ return 0;
+ }
+ }
+ buffer[length*2] = 0;
+ return buffer;
+}
+
+
+void
+HexOutStream::writeBuffer() {
+ U8* pos = start;
+ while (pos != ptr) {
+ out_stream.check(2);
+ U8* optr = out_stream.getptr();
+ U8* oend = out_stream.getend();
+ int length = min(ptr-pos, (oend-optr)/2);
+
+ for (int i=0; i<length; i++) {
+ optr[i*2] = intToHex((pos[i] >> 4) & 0xf);
+ optr[i*2+1] = intToHex(pos[i] & 0xf);
+ }
+
+ out_stream.setptr(optr + length*2);
+ pos += length;
+ }
+ offset += ptr - start;
+ ptr = start;
+}
+
+int HexOutStream::length()
+{
+ return offset + ptr - start;
+}
+
+void
+HexOutStream::flush() {
+ writeBuffer();
+ out_stream.flush();
+}
+
+int
+HexOutStream::overrun(int itemSize, int nItems) {
+ if (itemSize > bufSize)
+ throw Exception("HexOutStream overrun: max itemSize exceeded");
+
+ writeBuffer();
+
+ if (itemSize * nItems > end - ptr)
+ nItems = (end - ptr) / itemSize;
+
+ return nItems;
+}
+
diff --git a/rdr/HexOutStream.h b/rdr/HexOutStream.h
new file mode 100644
index 00000000..691a16bc
--- /dev/null
+++ b/rdr/HexOutStream.h
@@ -0,0 +1,51 @@
+/* Copyright (C) 2002-2004 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 __RDR_HEX_OUTSTREAM_H__
+#define __RDR_HEX_OUTSTREAM_H__
+
+#include <rdr/OutStream.h>
+
+namespace rdr {
+
+ class HexOutStream : public OutStream {
+ public:
+
+ HexOutStream(OutStream& os, int buflen=0);
+ virtual ~HexOutStream();
+
+ void flush();
+ int length();
+
+ static char intToHex(int i);
+ static char* binToHexStr(const char* data, int length);
+
+ private:
+ void writeBuffer();
+ int overrun(int itemSize, int nItems);
+
+ OutStream& out_stream;
+
+ U8* start;
+ int offset;
+ int bufSize;
+ };
+
+}
+
+#endif
diff --git a/rdr/InStream.cxx b/rdr/InStream.cxx
new file mode 100644
index 00000000..13a6fa19
--- /dev/null
+++ b/rdr/InStream.cxx
@@ -0,0 +1,35 @@
+/* Copyright (C) 2002-2003 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 <rdr/InStream.h>
+#include <rdr/Exception.h>
+
+using namespace rdr;
+
+U32 InStream::maxStringLength = 65535;
+
+char* InStream::readString()
+{
+ U32 len = readU32();
+ if (len > maxStringLength)
+ throw Exception("InStream max string length exceeded");
+ char* str = new char[len+1];
+ readBytes(str, len);
+ str[len] = 0;
+ return str;
+}
diff --git a/rdr/InStream.h b/rdr/InStream.h
new file mode 100644
index 00000000..2048daa6
--- /dev/null
+++ b/rdr/InStream.h
@@ -0,0 +1,153 @@
+/* Copyright (C) 2002-2003 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.
+ */
+
+//
+// rdr::InStream marshalls data from a buffer stored in RDR (RFB Data
+// Representation).
+//
+
+#ifndef __RDR_INSTREAM_H__
+#define __RDR_INSTREAM_H__
+
+#include <rdr/types.h>
+#include <string.h> // for memcpy
+
+namespace rdr {
+
+ class InStream {
+
+ public:
+
+ virtual ~InStream() {}
+
+ // check() ensures there is buffer data for at least one item of size
+ // itemSize bytes. Returns the number of items in the buffer (up to a
+ // maximum of nItems). If wait is false, then instead of blocking to wait
+ // for the bytes, zero is returned if the bytes are not immediately
+ // available.
+
+ inline int check(int itemSize, int nItems=1, bool wait=true)
+ {
+ if (ptr + itemSize * nItems > end) {
+ if (ptr + itemSize > end)
+ return overrun(itemSize, nItems, wait);
+
+ nItems = (end - ptr) / itemSize;
+ }
+ return nItems;
+ }
+
+ // checkNoWait() tries to make sure that the given number of bytes can
+ // be read without blocking. It returns true if this is the case, false
+ // otherwise. The length must be "small" (less than the buffer size).
+
+ inline bool checkNoWait(int length) { return check(length, 1, false)!=0; }
+
+ // readU/SN() methods read unsigned and signed N-bit integers.
+
+ inline U8 readU8() { check(1); return *ptr++; }
+ inline U16 readU16() { check(2); int b0 = *ptr++; int b1 = *ptr++;
+ return b0 << 8 | b1; }
+ inline U32 readU32() { check(4); int b0 = *ptr++; int b1 = *ptr++;
+ int b2 = *ptr++; int b3 = *ptr++;
+ return b0 << 24 | b1 << 16 | b2 << 8 | b3; }
+
+ inline S8 readS8() { return (S8) readU8(); }
+ inline S16 readS16() { return (S16)readU16(); }
+ inline S32 readS32() { return (S32)readU32(); }
+
+ // readString() reads a string - a U32 length followed by the data.
+ // Returns a null-terminated string - the caller should delete[] it
+ // afterwards.
+
+ char* readString();
+
+ // maxStringLength protects against allocating a huge buffer. Set it
+ // higher if you need longer strings.
+
+ static U32 maxStringLength;
+
+ inline void skip(int bytes) {
+ while (bytes > 0) {
+ int n = check(1, bytes);
+ ptr += n;
+ bytes -= n;
+ }
+ }
+
+ // readBytes() reads an exact number of bytes.
+
+ virtual void readBytes(void* data, int length) {
+ U8* dataPtr = (U8*)data;
+ U8* dataEnd = dataPtr + length;
+ while (dataPtr < dataEnd) {
+ int n = check(1, dataEnd - dataPtr);
+ memcpy(dataPtr, ptr, n);
+ ptr += n;
+ dataPtr += n;
+ }
+ }
+
+ // readOpaqueN() reads a quantity without byte-swapping.
+
+ inline U8 readOpaque8() { return readU8(); }
+ inline U16 readOpaque16() { check(2); U16 r; ((U8*)&r)[0] = *ptr++;
+ ((U8*)&r)[1] = *ptr++; return r; }
+ inline U32 readOpaque32() { check(4); U32 r; ((U8*)&r)[0] = *ptr++;
+ ((U8*)&r)[1] = *ptr++; ((U8*)&r)[2] = *ptr++;
+ ((U8*)&r)[3] = *ptr++; return r; }
+ inline U32 readOpaque24A() { check(3); U32 r=0; ((U8*)&r)[0] = *ptr++;
+ ((U8*)&r)[1] = *ptr++; ((U8*)&r)[2] = *ptr++;
+ return r; }
+ inline U32 readOpaque24B() { check(3); U32 r=0; ((U8*)&r)[1] = *ptr++;
+ ((U8*)&r)[2] = *ptr++; ((U8*)&r)[3] = *ptr++;
+ return r; }
+
+ // pos() returns the position in the stream.
+
+ virtual int pos() = 0;
+
+ // getptr(), getend() and setptr() are "dirty" methods which allow you to
+ // manipulate the buffer directly. This is useful for a stream which is a
+ // wrapper around an underlying stream.
+
+ inline const U8* getptr() const { return ptr; }
+ inline const U8* getend() const { return end; }
+ inline void setptr(const U8* p) { ptr = p; }
+
+ private:
+
+ // overrun() is implemented by a derived class to cope with buffer overrun.
+ // 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). If wait is false, then
+ // instead of blocking to wait for the bytes, zero is returned if the bytes
+ // are not immediately available.
+
+ virtual int overrun(int itemSize, int nItems, bool wait=true) = 0;
+
+ protected:
+
+ InStream() {}
+ const U8* ptr;
+ const U8* end;
+ };
+
+}
+
+#endif
diff --git a/rdr/Makefile.in b/rdr/Makefile.in
new file mode 100644
index 00000000..9edf2842
--- /dev/null
+++ b/rdr/Makefile.in
@@ -0,0 +1,19 @@
+
+SRCS = Exception.cxx FdInStream.cxx FdOutStream.cxx InStream.cxx \
+ NullOutStream.cxx RandomStream.cxx ZlibInStream.cxx ZlibOutStream.cxx \
+ HexInStream.cxx HexOutStream.cxx
+
+OBJS = $(SRCS:.cxx=.o)
+
+DIR_CPPFLAGS = -I$(top_srcdir) @ZLIB_INCLUDE@
+
+library = librdr.a
+
+all:: $(library)
+
+$(library): $(OBJS)
+ rm -f $(library)
+ $(AR) $(library) $(OBJS)
+ $(RANLIB) $(library)
+
+# followed by boilerplate.mk
diff --git a/rdr/MemInStream.h b/rdr/MemInStream.h
new file mode 100644
index 00000000..2b05e3df
--- /dev/null
+++ b/rdr/MemInStream.h
@@ -0,0 +1,63 @@
+/* Copyright (C) 2002-2003 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.
+ */
+
+//
+// rdr::MemInStream is an InStream which streams from a given memory buffer.
+// If the deleteWhenDone parameter is true then the buffer will be delete[]d in
+// the destructor. Note that it is delete[]d as a U8* - strictly speaking this
+// means it ought to be new[]ed as a U8* as well, but on most platforms this
+// doesn't matter.
+//
+
+#ifndef __RDR_MEMINSTREAM_H__
+#define __RDR_MEMINSTREAM_H__
+
+#include <rdr/InStream.h>
+#include <rdr/Exception.h>
+
+namespace rdr {
+
+ class MemInStream : public InStream {
+
+ public:
+
+ MemInStream(const void* data, int len, bool deleteWhenDone_=false)
+ : start((const U8*)data), deleteWhenDone(deleteWhenDone_)
+ {
+ ptr = start;
+ end = start + len;
+ }
+
+ virtual ~MemInStream() {
+ if (deleteWhenDone)
+ delete [] (U8*)start;
+ }
+
+ int pos() { return ptr - start; }
+ void reposition(int pos) { ptr = start + pos; }
+
+ private:
+
+ int overrun(int itemSize, int nItems, bool wait) { throw EndOfStream(); }
+ const U8* start;
+ bool deleteWhenDone;
+ };
+
+}
+
+#endif
diff --git a/rdr/MemOutStream.h b/rdr/MemOutStream.h
new file mode 100644
index 00000000..3456f5c7
--- /dev/null
+++ b/rdr/MemOutStream.h
@@ -0,0 +1,82 @@
+/* Copyright (C) 2002-2003 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.
+ */
+
+//
+// A MemOutStream grows as needed when data is written to it.
+//
+
+#ifndef __RDR_MEMOUTSTREAM_H__
+#define __RDR_MEMOUTSTREAM_H__
+
+#include <rdr/OutStream.h>
+
+namespace rdr {
+
+ class MemOutStream : public OutStream {
+
+ public:
+
+ MemOutStream(int len=1024) {
+ start = ptr = new U8[len];
+ end = start + len;
+ }
+
+ virtual ~MemOutStream() {
+ delete [] start;
+ }
+
+ void writeBytes(const void* data, int length) {
+ check(length);
+ memcpy(ptr, data, length);
+ ptr += length;
+ }
+
+ int length() { return ptr - start; }
+ void clear() { ptr = start; };
+ void reposition(int pos) { ptr = start + pos; }
+
+ // data() returns a pointer to the buffer.
+
+ const void* data() { return (const void*)start; }
+
+ private:
+
+ // overrun() either doubles the buffer or adds enough space for nItems of
+ // size itemSize bytes.
+
+ int overrun(int itemSize, int nItems) {
+ int len = ptr - start + itemSize * nItems;
+ if (len < (end - start) * 2)
+ len = (end - start) * 2;
+
+ U8* newStart = new U8[len];
+ memcpy(newStart, start, ptr - start);
+ ptr = newStart + (ptr - start);
+ delete [] start;
+ start = newStart;
+ end = newStart + len;
+
+ return nItems;
+ }
+
+ U8* start;
+ };
+
+}
+
+#endif
diff --git a/rdr/NullOutStream.cxx b/rdr/NullOutStream.cxx
new file mode 100644
index 00000000..e940f2af
--- /dev/null
+++ b/rdr/NullOutStream.cxx
@@ -0,0 +1,60 @@
+/* Copyright (C) 2002-2003 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 <rdr/NullOutStream.h>
+#include <rdr/Exception.h>
+
+using namespace rdr;
+
+static const int bufferSize = 1024;
+
+NullOutStream::NullOutStream()
+ : offset(0)
+{
+ start = ptr = new U8[bufferSize];
+ end = start + bufferSize;
+}
+
+NullOutStream::~NullOutStream()
+{
+ delete [] start;
+}
+
+int NullOutStream::length()
+{
+ return offset + ptr - start;
+}
+
+void NullOutStream::writeBytes(const void* data, int length)
+{
+ offset += length;
+}
+
+int NullOutStream::overrun(int itemSize, int nItems)
+{
+ if (itemSize > bufferSize)
+ throw Exception("NullOutStream overrun: max itemSize exceeded");
+
+ offset += ptr - start;
+ ptr = start;
+
+ if (itemSize * nItems > end - ptr)
+ nItems = (end - ptr) / itemSize;
+
+ return nItems;
+}
diff --git a/rdr/NullOutStream.h b/rdr/NullOutStream.h
new file mode 100644
index 00000000..84a56e5f
--- /dev/null
+++ b/rdr/NullOutStream.h
@@ -0,0 +1,42 @@
+/* Copyright (C) 2002-2003 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 __RDR_NULLOUTSTREAM_H__
+#define __RDR_NULLOUTSTREAM_H__
+
+#include <rdr/OutStream.h>
+
+namespace rdr {
+
+ class NullOutStream : public OutStream {
+
+ public:
+ NullOutStream();
+ virtual ~NullOutStream();
+ int length();
+ void writeBytes(const void* data, int length);
+
+ private:
+ int overrun(int itemSize, int nItems);
+ int offset;
+ U8* start;
+ };
+
+}
+
+#endif
diff --git a/rdr/OutStream.h b/rdr/OutStream.h
new file mode 100644
index 00000000..a064bcc9
--- /dev/null
+++ b/rdr/OutStream.h
@@ -0,0 +1,152 @@
+/* Copyright (C) 2002-2003 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.
+ */
+
+//
+// rdr::OutStream marshalls data into a buffer stored in RDR (RFB Data
+// Representation).
+//
+
+#ifndef __RDR_OUTSTREAM_H__
+#define __RDR_OUTSTREAM_H__
+
+#include <rdr/types.h>
+#include <string.h> // for memcpy
+
+namespace rdr {
+
+ class OutStream {
+
+ protected:
+
+ OutStream() {}
+
+ public:
+
+ virtual ~OutStream() {}
+
+ // check() ensures there is buffer space for at least one item of size
+ // itemSize bytes. Returns the number of items which fit (up to a maximum
+ // of nItems).
+
+ inline int check(int itemSize, int nItems=1)
+ {
+ if (ptr + itemSize * nItems > end) {
+ if (ptr + itemSize > end)
+ return overrun(itemSize, nItems);
+
+ nItems = (end - ptr) / itemSize;
+ }
+ return nItems;
+ }
+
+ // writeU/SN() methods write unsigned and signed N-bit integers.
+
+ inline void writeU8( U8 u) { check(1); *ptr++ = u; }
+ inline void writeU16(U16 u) { check(2); *ptr++ = u >> 8; *ptr++ = (U8)u; }
+ inline void writeU32(U32 u) { check(4); *ptr++ = u >> 24; *ptr++ = u >> 16;
+ *ptr++ = u >> 8; *ptr++ = u; }
+
+ inline void writeS8( S8 s) { writeU8((U8)s); }
+ inline void writeS16(S16 s) { writeU16((U16)s); }
+ inline void writeS32(S32 s) { writeU32((U32)s); }
+
+ // writeString() writes a string - a U32 length followed by the data. The
+ // given string should be null-terminated (but the terminating null is not
+ // written to the stream).
+
+ inline void writeString(const char* str) {
+ U32 len = strlen(str);
+ writeU32(len);
+ writeBytes(str, len);
+ }
+
+ inline void pad(int bytes) {
+ while (bytes-- > 0) writeU8(0);
+ }
+
+ inline void skip(int bytes) {
+ while (bytes > 0) {
+ int n = check(1, bytes);
+ ptr += n;
+ bytes -= n;
+ }
+ }
+
+ // writeBytes() writes an exact number of bytes.
+
+ virtual void writeBytes(const void* data, int length) {
+ const U8* dataPtr = (const U8*)data;
+ const U8* dataEnd = dataPtr + length;
+ while (dataPtr < dataEnd) {
+ int n = check(1, dataEnd - dataPtr);
+ memcpy(ptr, dataPtr, n);
+ ptr += n;
+ dataPtr += n;
+ }
+ }
+
+ // writeOpaqueN() writes a quantity without byte-swapping.
+
+ inline void writeOpaque8( U8 u) { writeU8(u); }
+ inline void writeOpaque16(U16 u) { check(2); *ptr++ = ((U8*)&u)[0];
+ *ptr++ = ((U8*)&u)[1]; }
+ inline void writeOpaque32(U32 u) { check(4); *ptr++ = ((U8*)&u)[0];
+ *ptr++ = ((U8*)&u)[1];
+ *ptr++ = ((U8*)&u)[2];
+ *ptr++ = ((U8*)&u)[3]; }
+ inline void writeOpaque24A(U32 u) { check(3); *ptr++ = ((U8*)&u)[0];
+ *ptr++ = ((U8*)&u)[1];
+ *ptr++ = ((U8*)&u)[2]; }
+ inline void writeOpaque24B(U32 u) { check(3); *ptr++ = ((U8*)&u)[1];
+ *ptr++ = ((U8*)&u)[2];
+ *ptr++ = ((U8*)&u)[3]; }
+
+ // length() returns the length of the stream.
+
+ virtual int length() = 0;
+
+ // flush() requests that the stream be flushed.
+
+ virtual void flush() {}
+
+ // getptr(), getend() and setptr() are "dirty" methods which allow you to
+ // manipulate the buffer directly. This is useful for a stream which is a
+ // wrapper around an underlying stream.
+
+ inline U8* getptr() { return ptr; }
+ inline U8* getend() { return end; }
+ inline void setptr(U8* p) { ptr = p; }
+
+ private:
+
+ // overrun() is implemented by a derived class to cope with buffer overrun.
+ // It ensures there are at least itemSize bytes of buffer space. Returns
+ // the number of items which fit (up to a maximum of nItems). itemSize is
+ // supposed to be "small" (a few bytes).
+
+ virtual int overrun(int itemSize, int nItems) = 0;
+
+ protected:
+
+ U8* ptr;
+ U8* end;
+ };
+
+}
+
+#endif
diff --git a/rdr/RandomStream.cxx b/rdr/RandomStream.cxx
new file mode 100644
index 00000000..7f62e091
--- /dev/null
+++ b/rdr/RandomStream.cxx
@@ -0,0 +1,118 @@
+/* Copyright (C) 2002-2003 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 <rdr/RandomStream.h>
+#include <rdr/Exception.h>
+#include <time.h>
+#include <stdlib.h>
+#ifndef WIN32
+#include <unistd.h>
+#include <errno.h>
+#else
+#define getpid() GetCurrentProcessId()
+#endif
+
+using namespace rdr;
+
+const int DEFAULT_BUF_LEN = 256;
+
+unsigned int RandomStream::seed;
+
+RandomStream::RandomStream()
+ : offset(0)
+{
+ ptr = end = start = new U8[DEFAULT_BUF_LEN];
+
+#ifdef WIN32
+ provider = 0;
+ if (!CryptAcquireContext(&provider, 0, 0, PROV_RSA_FULL, 0)) {
+ if (GetLastError() == NTE_BAD_KEYSET) {
+ if (!CryptAcquireContext(&provider, 0, 0, PROV_RSA_FULL, CRYPT_NEWKEYSET)) {
+ fprintf(stderr, "RandomStream: unable to create keyset\n");
+ provider = 0;
+ }
+ } else {
+ fprintf(stderr, "RandomStream: unable to acquire context\n");
+ provider = 0;
+ }
+ }
+ if (!provider) {
+#else
+ fp = fopen("/dev/urandom", "r");
+ if (!fp)
+ fp = fopen("/dev/random", "r");
+ if (!fp) {
+#endif
+ fprintf(stderr,"RandomStream: warning: no OS supplied random source - using rand()\n");
+ seed += (unsigned int) time(0) + getpid() + getpid() * 987654 + rand();
+ srand(seed);
+ }
+}
+
+RandomStream::~RandomStream() {
+ delete [] start;
+
+#ifdef WIN32
+ if (provider) {
+ CryptReleaseContext(provider, 0);
+ }
+#else
+ if (fp) fclose(fp);
+#endif
+}
+
+int RandomStream::pos() {
+ return offset + ptr - start;
+}
+
+int RandomStream::overrun(int itemSize, int nItems, bool wait) {
+ if (itemSize > DEFAULT_BUF_LEN)
+ throw Exception("RandomStream overrun: max itemSize exceeded");
+
+ if (end - ptr != 0)
+ memmove(start, ptr, end - ptr);
+
+ end -= ptr - start;
+ offset += ptr - start;
+ ptr = start;
+
+ int length = start + DEFAULT_BUF_LEN - end;
+
+#ifdef WIN32
+ if (provider) {
+ if (!CryptGenRandom(provider, length, (U8*)end))
+ throw rdr::SystemException("unable to CryptGenRandom", GetLastError());
+ end += length;
+#else
+ if (fp) {
+ int n = fread((U8*)end, length, 1, fp);
+ if (n != 1)
+ throw rdr::SystemException("reading /dev/urandom or /dev/random failed",
+ errno);
+ end += length;
+#endif
+ } else {
+ for (int i=0; i<length; i++)
+ *(U8*)end++ = (int) (256.0*rand()/(RAND_MAX+1.0));
+ }
+
+ if (itemSize * nItems > end - ptr)
+ nItems = (end - ptr) / itemSize;
+
+ return nItems;
+}
diff --git a/rdr/RandomStream.h b/rdr/RandomStream.h
new file mode 100644
index 00000000..c4aaaa6a
--- /dev/null
+++ b/rdr/RandomStream.h
@@ -0,0 +1,63 @@
+/* Copyright (C) 2002-2003 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 __RDR_RANDOMSTREAM_H__
+#define __RDR_RANDOMSTREAM_H__
+
+#include <stdio.h>
+#include <rdr/InStream.h>
+
+#ifdef WIN32
+#ifndef _WIN32_WINNT
+#define _WIN32_WINNT 0x0400
+#endif
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+#include <wincrypt.h>
+#endif
+
+namespace rdr {
+
+ class RandomStream : public InStream {
+
+ public:
+
+ RandomStream();
+ virtual ~RandomStream();
+
+ int pos();
+
+ protected:
+ int overrun(int itemSize, int nItems, bool wait);
+
+ private:
+ U8* start;
+ int offset;
+
+ static unsigned int seed;
+#ifdef WIN32
+ HCRYPTPROV provider;
+#else
+ FILE* fp;
+#endif
+
+ };
+
+} // end of namespace rdr
+
+#endif
diff --git a/rdr/SubstitutingInStream.h b/rdr/SubstitutingInStream.h
new file mode 100644
index 00000000..3a0559b7
--- /dev/null
+++ b/rdr/SubstitutingInStream.h
@@ -0,0 +1,102 @@
+/* Copyright (C) 2002-2004 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 __RDR_SUBSTITUTINGINSTREAM_H__
+#define __RDR_SUBSTITUTINGINSTREAM_H__
+
+#include <rdr/InStream.h>
+#include <rdr/Exception.h>
+
+namespace rdr {
+
+ class Substitutor {
+ public:
+ virtual char* substitute(const char* varName) = 0;
+ };
+
+ class SubstitutingInStream : public InStream {
+ public:
+ SubstitutingInStream(InStream* underlying_, Substitutor* s,
+ int maxVarNameLen_)
+ : underlying(underlying_), dollar(0), substitutor(s), subst(0),
+ maxVarNameLen(maxVarNameLen_)
+ {
+ ptr = end = underlying->getptr();
+ varName = new char[maxVarNameLen+1];
+ }
+ ~SubstitutingInStream() {
+ delete underlying;
+ delete [] varName;
+ delete [] subst;
+ }
+
+ int pos() { return underlying->pos(); }
+
+ virtual int overrun(int itemSize, int nItems, bool wait=true) {
+ if (itemSize != 1)
+ throw new rdr::Exception("SubstitutingInStream: itemSize must be 1");
+
+ if (subst) {
+ delete [] subst;
+ subst = 0;
+ } else {
+ underlying->setptr(ptr);
+ }
+
+ underlying->check(1);
+ ptr = underlying->getptr();
+ end = underlying->getend();
+ dollar = (const U8*)memchr(ptr, '$', end-ptr);
+ if (dollar) {
+ if (dollar == ptr) {
+ try {
+ int i = 0;
+ while (i < maxVarNameLen) {
+ varName[i++] = underlying->readS8();
+ varName[i] = 0;
+ subst = substitutor->substitute(varName);
+ if (subst) {
+ ptr = (U8*)subst;
+ end = (U8*)subst + strlen(subst);
+ break;
+ }
+ }
+ } catch (EndOfStream&) {
+ }
+
+ if (!subst)
+ dollar = (const U8*)memchr(ptr+1, '$', end-ptr-1);
+ }
+ if (!subst && dollar) end = dollar;
+ }
+
+ if (itemSize * nItems > end - ptr)
+ nItems = (end - ptr) / itemSize;
+
+ return nItems;
+ }
+
+ InStream* underlying;
+ const U8* dollar;
+ Substitutor* substitutor;
+ char* varName;
+ char* subst;
+ int maxVarNameLen;
+ };
+}
+#endif
diff --git a/rdr/ZlibInStream.cxx b/rdr/ZlibInStream.cxx
new file mode 100644
index 00000000..52e4dd35
--- /dev/null
+++ b/rdr/ZlibInStream.cxx
@@ -0,0 +1,125 @@
+/* Copyright (C) 2002-2003 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 <rdr/ZlibInStream.h>
+#include <rdr/Exception.h>
+#include <zlib.h>
+
+using namespace rdr;
+
+enum { DEFAULT_BUF_SIZE = 16384 };
+
+ZlibInStream::ZlibInStream(int bufSize_)
+ : underlying(0), bufSize(bufSize_ ? bufSize_ : DEFAULT_BUF_SIZE), offset(0),
+ bytesIn(0)
+{
+ zs = new z_stream;
+ zs->zalloc = Z_NULL;
+ zs->zfree = Z_NULL;
+ zs->opaque = Z_NULL;
+ zs->next_in = Z_NULL;
+ zs->avail_in = 0;
+ if (inflateInit(zs) != Z_OK) {
+ delete zs;
+ throw Exception("ZlibInStream: inflateInit failed");
+ }
+ ptr = end = start = new U8[bufSize];
+}
+
+ZlibInStream::~ZlibInStream()
+{
+ delete [] start;
+ inflateEnd(zs);
+ delete zs;
+}
+
+void ZlibInStream::setUnderlying(InStream* is, int bytesIn_)
+{
+ underlying = is;
+ bytesIn = bytesIn_;
+ ptr = end = start;
+}
+
+int ZlibInStream::pos()
+{
+ return offset + ptr - start;
+}
+
+void ZlibInStream::reset()
+{
+ ptr = end = start;
+ if (!underlying) return;
+
+ while (bytesIn > 0) {
+ decompress(true);
+ end = start; // throw away any data
+ }
+ underlying = 0;
+}
+
+int ZlibInStream::overrun(int itemSize, int nItems, bool wait)
+{
+ if (itemSize > bufSize)
+ throw Exception("ZlibInStream overrun: max itemSize exceeded");
+ if (!underlying)
+ throw Exception("ZlibInStream overrun: no underlying stream");
+
+ if (end - ptr != 0)
+ memmove(start, ptr, end - ptr);
+
+ offset += ptr - start;
+ end -= ptr - start;
+ ptr = start;
+
+ while (end - ptr < itemSize) {
+ if (!decompress(wait))
+ return 0;
+ }
+
+ if (itemSize * nItems > end - ptr)
+ nItems = (end - ptr) / itemSize;
+
+ return nItems;
+}
+
+// decompress() calls the decompressor once. Note that this won't necessarily
+// generate any output data - it may just consume some input data. Returns
+// false if wait is false and we would block on the underlying stream.
+
+bool ZlibInStream::decompress(bool wait)
+{
+ zs->next_out = (U8*)end;
+ zs->avail_out = start + bufSize - end;
+
+ int n = underlying->check(1, 1, wait);
+ if (n == 0) return false;
+ zs->next_in = (U8*)underlying->getptr();
+ zs->avail_in = underlying->getend() - underlying->getptr();
+ if ((int)zs->avail_in > bytesIn)
+ zs->avail_in = bytesIn;
+
+ int rc = inflate(zs, Z_SYNC_FLUSH);
+ if (rc != Z_OK) {
+ throw Exception("ZlibInStream: inflate failed");
+ }
+
+ bytesIn -= zs->next_in - underlying->getptr();
+ end = zs->next_out;
+ underlying->setptr(zs->next_in);
+ return true;
+}
diff --git a/rdr/ZlibInStream.h b/rdr/ZlibInStream.h
new file mode 100644
index 00000000..81eb161c
--- /dev/null
+++ b/rdr/ZlibInStream.h
@@ -0,0 +1,59 @@
+/* Copyright (C) 2002-2003 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.
+ */
+
+//
+// ZlibInStream streams from a compressed data stream ("underlying"),
+// decompressing with zlib on the fly.
+//
+
+#ifndef __RDR_ZLIBINSTREAM_H__
+#define __RDR_ZLIBINSTREAM_H__
+
+#include <rdr/InStream.h>
+
+struct z_stream_s;
+
+namespace rdr {
+
+ class ZlibInStream : public InStream {
+
+ public:
+
+ ZlibInStream(int bufSize=0);
+ virtual ~ZlibInStream();
+
+ void setUnderlying(InStream* is, int bytesIn);
+ void reset();
+ int pos();
+
+ private:
+
+ int overrun(int itemSize, int nItems, bool wait);
+ bool decompress(bool wait);
+
+ InStream* underlying;
+ int bufSize;
+ int offset;
+ z_stream_s* zs;
+ int bytesIn;
+ U8* start;
+ };
+
+} // end of namespace rdr
+
+#endif
diff --git a/rdr/ZlibOutStream.cxx b/rdr/ZlibOutStream.cxx
new file mode 100644
index 00000000..6aadde13
--- /dev/null
+++ b/rdr/ZlibOutStream.cxx
@@ -0,0 +1,140 @@
+/* Copyright (C) 2002-2004 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 <rdr/ZlibOutStream.h>
+#include <rdr/Exception.h>
+#include <zlib.h>
+
+using namespace rdr;
+
+enum { DEFAULT_BUF_SIZE = 16384 };
+
+ZlibOutStream::ZlibOutStream(OutStream* os, int bufSize_, int compressLevel)
+ : underlying(os), bufSize(bufSize_ ? bufSize_ : DEFAULT_BUF_SIZE), offset(0)
+{
+ zs = new z_stream;
+ zs->zalloc = Z_NULL;
+ zs->zfree = Z_NULL;
+ zs->opaque = Z_NULL;
+ if (deflateInit(zs, compressLevel) != Z_OK) {
+ delete zs;
+ throw Exception("ZlibOutStream: deflateInit failed");
+ }
+ ptr = start = new U8[bufSize];
+ end = start + bufSize;
+}
+
+ZlibOutStream::~ZlibOutStream()
+{
+ try {
+ flush();
+ } catch (Exception&) {
+ }
+ delete [] start;
+ deflateEnd(zs);
+ delete zs;
+}
+
+void ZlibOutStream::setUnderlying(OutStream* os)
+{
+ underlying = os;
+}
+
+int ZlibOutStream::length()
+{
+ return offset + ptr - start;
+}
+
+void ZlibOutStream::flush()
+{
+ zs->next_in = start;
+ zs->avail_in = ptr - start;
+
+// fprintf(stderr,"zos flush: avail_in %d\n",zs->avail_in);
+
+ while (zs->avail_in != 0) {
+
+ do {
+ underlying->check(1);
+ zs->next_out = underlying->getptr();
+ zs->avail_out = underlying->getend() - underlying->getptr();
+
+// fprintf(stderr,"zos flush: calling deflate, avail_in %d, avail_out %d\n",
+// zs->avail_in,zs->avail_out);
+ int rc = deflate(zs, Z_SYNC_FLUSH);
+ if (rc != Z_OK) throw Exception("ZlibOutStream: deflate failed");
+
+// fprintf(stderr,"zos flush: after deflate: %d bytes\n",
+// zs->next_out-underlying->getptr());
+
+ underlying->setptr(zs->next_out);
+ } while (zs->avail_out == 0);
+ }
+
+ offset += ptr - start;
+ ptr = start;
+}
+
+int ZlibOutStream::overrun(int itemSize, int nItems)
+{
+// fprintf(stderr,"ZlibOutStream overrun\n");
+
+ if (itemSize > bufSize)
+ throw Exception("ZlibOutStream overrun: max itemSize exceeded");
+
+ while (end - ptr < itemSize) {
+ zs->next_in = start;
+ zs->avail_in = ptr - start;
+
+ do {
+ underlying->check(1);
+ zs->next_out = underlying->getptr();
+ zs->avail_out = underlying->getend() - underlying->getptr();
+
+// fprintf(stderr,"zos overrun: calling deflate, avail_in %d, avail_out %d\n",
+// zs->avail_in,zs->avail_out);
+
+ int rc = deflate(zs, 0);
+ if (rc != Z_OK) throw Exception("ZlibOutStream: deflate failed");
+
+// fprintf(stderr,"zos overrun: after deflate: %d bytes\n",
+// zs->next_out-underlying->getptr());
+
+ underlying->setptr(zs->next_out);
+ } while (zs->avail_out == 0);
+
+ // output buffer not full
+
+ if (zs->avail_in == 0) {
+ offset += ptr - start;
+ ptr = start;
+ } else {
+ // but didn't consume all the data? try shifting what's left to the
+ // start of the buffer.
+ fprintf(stderr,"z out buf not full, but in data not consumed\n");
+ memmove(start, zs->next_in, ptr - zs->next_in);
+ offset += zs->next_in - start;
+ ptr -= zs->next_in - start;
+ }
+ }
+
+ if (itemSize * nItems > end - ptr)
+ nItems = (end - ptr) / itemSize;
+
+ return nItems;
+}
diff --git a/rdr/ZlibOutStream.h b/rdr/ZlibOutStream.h
new file mode 100644
index 00000000..e51db73b
--- /dev/null
+++ b/rdr/ZlibOutStream.h
@@ -0,0 +1,57 @@
+/* Copyright (C) 2002-2004 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.
+ */
+
+//
+// ZlibOutStream streams to a compressed data stream (underlying), compressing
+// with zlib on the fly.
+//
+
+#ifndef __RDR_ZLIBOUTSTREAM_H__
+#define __RDR_ZLIBOUTSTREAM_H__
+
+#include <rdr/OutStream.h>
+
+struct z_stream_s;
+
+namespace rdr {
+
+ class ZlibOutStream : public OutStream {
+
+ public:
+
+ ZlibOutStream(OutStream* os=0, int bufSize=0, int compressionLevel=-1);
+ virtual ~ZlibOutStream();
+
+ void setUnderlying(OutStream* os);
+ void flush();
+ int length();
+
+ private:
+
+ int overrun(int itemSize, int nItems);
+
+ OutStream* underlying;
+ int bufSize;
+ int offset;
+ z_stream_s* zs;
+ U8* start;
+ };
+
+} // end of namespace rdr
+
+#endif
diff --git a/rdr/msvcwarning.h b/rdr/msvcwarning.h
new file mode 100644
index 00000000..e93f2bbc
--- /dev/null
+++ b/rdr/msvcwarning.h
@@ -0,0 +1,18 @@
+/* Copyright (C) 2002-2003 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.
+ */
+#pragma warning( disable : 4800 ) // forcing bool 'true' or 'false'
diff --git a/rdr/rdr.dsp b/rdr/rdr.dsp
new file mode 100644
index 00000000..260f8f01
--- /dev/null
+++ b/rdr/rdr.dsp
@@ -0,0 +1,231 @@
+# Microsoft Developer Studio Project File - Name="rdr" - Package Owner=<4>
+# Microsoft Developer Studio Generated Build File, Format Version 6.00
+# ** DO NOT EDIT **
+
+# TARGTYPE "Win32 (x86) Static Library" 0x0104
+
+CFG=rdr - 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 "rdr.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 "rdr.mak" CFG="rdr - Win32 Debug Unicode"
+!MESSAGE
+!MESSAGE Possible choices for configuration are:
+!MESSAGE
+!MESSAGE "rdr - Win32 Release" (based on "Win32 (x86) Static Library")
+!MESSAGE "rdr - Win32 Debug" (based on "Win32 (x86) Static Library")
+!MESSAGE "rdr - 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)" == "rdr - 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"
+# 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 /O1 /I ".." /FI"msvcwarning.h" /D "NDEBUG" /D "_LIB" /D "WIN32" /D "_MBCS" /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)" == "rdr - 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"
+# 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 ".." /FI"msvcwarning.h" /D "_DEBUG" /D "_LIB" /D "WIN32" /D "_MBCS" /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)" == "rdr - Win32 Debug Unicode"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 1
+# PROP BASE Output_Dir "rdr___Win32_Debug_Unicode"
+# PROP BASE Intermediate_Dir "rdr___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"
+# 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 ".." /FI"msvcwarning.h" /D "_LIB" /D "_DEBUG" /D "WIN32" /D "_UNICODE" /D "UNICODE" /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 "rdr - Win32 Release"
+# Name "rdr - Win32 Debug"
+# Name "rdr - Win32 Debug Unicode"
+# Begin Group "Header Files"
+
+# PROP Default_Filter "h;hpp;hxx;hm;inl"
+# Begin Source File
+
+SOURCE=.\Exception.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\FdInStream.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\FdOutStream.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\FixedMemOutStream.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\HexInStream.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\HexOutStream.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\InStream.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\MemInStream.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\MemOutStream.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\msvcwarning.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\NullOutStream.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\OutStream.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\RandomStream.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\SubstitutingInStream.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\types.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\ZlibInStream.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\ZlibOutStream.h
+# End Source File
+# End Group
+# Begin Group "Source Files"
+
+# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat"
+# Begin Source File
+
+SOURCE=.\Exception.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\FdInStream.cxx
+# ADD CPP /I "../zlib"
+# End Source File
+# Begin Source File
+
+SOURCE=.\FdOutStream.cxx
+# ADD CPP /I "../zlib"
+# End Source File
+# Begin Source File
+
+SOURCE=.\HexInStream.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\HexOutStream.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\InStream.cxx
+# ADD CPP /I "../zlib"
+# End Source File
+# Begin Source File
+
+SOURCE=.\NullOutStream.cxx
+# ADD CPP /I "../zlib"
+# End Source File
+# Begin Source File
+
+SOURCE=.\RandomStream.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\ZlibInStream.cxx
+# ADD CPP /I "../zlib"
+# End Source File
+# Begin Source File
+
+SOURCE=.\ZlibOutStream.cxx
+# ADD CPP /I "../zlib"
+# End Source File
+# End Group
+# End Target
+# End Project
diff --git a/rdr/types.h b/rdr/types.h
new file mode 100644
index 00000000..3798c975
--- /dev/null
+++ b/rdr/types.h
@@ -0,0 +1,56 @@
+/* Copyright (C) 2002-2004 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 __RDR_TYPES_H__
+#define __RDR_TYPES_H__
+
+namespace rdr {
+
+ typedef unsigned char U8;
+ typedef unsigned short U16;
+ typedef unsigned int U32;
+ typedef signed char S8;
+ typedef signed short S16;
+ typedef signed int S32;
+
+ class U8Array {
+ public:
+ U8Array() : buf(0) {}
+ U8Array(U8* a) : buf(a) {} // note: assumes ownership
+ U8Array(int len) : buf(new U8[len]) {}
+ ~U8Array() { delete [] buf; }
+
+ // Get the buffer pointer & clear it (i.e. caller takes ownership)
+ U8* takeBuf() { U8* tmp = buf; buf = 0; return tmp; }
+
+ U8* buf;
+ };
+
+ class U16Array {
+ public:
+ U16Array() : buf(0) {}
+ U16Array(U16* a) : buf(a) {} // note: assumes ownership
+ U16Array(int len) : buf(new U16[len]) {}
+ ~U16Array() { delete [] buf; }
+ U16* takeBuf() { U16* tmp = buf; buf = 0; return tmp; }
+ U16* buf;
+ };
+
+} // end of namespace rdr
+
+#endif
diff --git a/rfb/Blacklist.cxx b/rfb/Blacklist.cxx
new file mode 100644
index 00000000..4c4f95b2
--- /dev/null
+++ b/rfb/Blacklist.cxx
@@ -0,0 +1,86 @@
+/* Copyright (C) 2002-2003 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 <rfb/Blacklist.h>
+#include <rfb/Configuration.h>
+
+using namespace rfb;
+
+IntParameter Blacklist::threshold("BlacklistThreshold",
+ "The number of unauthenticated connection attempts allowed from any "
+ "individual host before that host is black-listed",
+ 5);
+IntParameter Blacklist::initialTimeout("BlacklistTimeout",
+ "The initial timeout applied when a host is first black-listed. "
+ "The host cannot re-attempt a connection until the timeout expires.",
+ 10);
+
+
+Blacklist::Blacklist() {
+}
+
+Blacklist::~Blacklist() {
+ // Free the map keys
+ BlacklistMap::iterator i;
+ for (i=blm.begin(); i!=blm.end(); i++) {
+ strFree((char*)(*i).first);
+ }
+}
+
+bool Blacklist::isBlackmarked(const char* name) {
+ BlacklistMap::iterator i = blm.find(name);
+ if (i == blm.end()) {
+ // Entry is not already black-marked.
+ // Create the entry unmarked, unblocked,
+ // with suitable defaults set.
+ BlacklistInfo bi;
+ bi.marks = 1;
+ bi.blockUntil = 0;
+ bi.blockTimeout = initialTimeout;
+ blm[strDup(name)] = bi;
+ i = blm.find(name);
+ }
+
+ // Entry exists - has it reached the threshold yet?
+ if ((*i).second.marks >= threshold) {
+ // Yes - entry is blocked - has the timeout expired?
+ time_t now = time(0);
+ if (now >= (*i).second.blockUntil) {
+ // Timeout has expired. Reset timeout and allow
+ // a re-try.
+ (*i).second.blockUntil = now + (*i).second.blockTimeout;
+ (*i).second.blockTimeout = (*i).second.blockTimeout * 2;
+ return false;
+ }
+ // Blocked and timeout still in effect - reject!
+ return true;
+ }
+
+ // We haven't reached the threshold yet.
+ // Increment the black-mark counter but allow
+ // the entry to pass.
+ (*i).second.marks++;
+ return false;
+}
+
+void Blacklist::clearBlackmark(const char* name) {
+ BlacklistMap::iterator i = blm.find(name);
+ if (i != blm.end()) {
+ strFree((char*)(*i).first);
+ blm.erase(i);
+ }
+}
diff --git a/rfb/Blacklist.h b/rfb/Blacklist.h
new file mode 100644
index 00000000..4df7ec80
--- /dev/null
+++ b/rfb/Blacklist.h
@@ -0,0 +1,91 @@
+/* Copyright (C) 2002-2003 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.
+ */
+
+//
+// Blacklist.h - Handling of black-listed entities.
+// Just keeps a table mapping strings to timing information, including
+// how many times the entry has been black-listed and when to next
+// put it on probation (e.g. allow a connection in from the host, and
+// re-blacklist it if that fails).
+//
+
+#ifndef __RFB_BLACKLIST_H__
+#define __RFB_BLACKLIST_H__
+
+#include <string.h>
+#include <time.h>
+#include <map>
+
+#include <rfb/Configuration.h>
+#include <rfb/util.h>
+
+namespace rfb {
+
+ //
+ // -=- Blacklist handler
+ //
+ // Parameters include a threshold after which to blacklist the named
+ // host, and a timeout after which to re-consider them.
+ //
+ // Threshold means that isBlackmarked can be called that number of times
+ // before it will return true.
+ //
+ // Timeout means that after that many seconds, the next call to isBlackmarked
+ // will return false. At the same time, the timeout is doubled, so that the
+ // next calls will fail, until the timeout expires again or clearBlackmark is
+ // called.
+ //
+ // When clearBlackMark is called, the corresponding entry is completely
+ // removed, causing the next isBlackmarked call to return false.
+
+ // KNOWN BUG: Client can keep making rejected requests, thus increasing
+ // their timeout. If client does this for 30 years, timeout may wrap round
+ // to a very small value again.
+
+ // THIS CLASS IS NOT THREAD-SAFE!
+
+ class Blacklist {
+ public:
+ Blacklist();
+ ~Blacklist();
+
+ bool isBlackmarked(const char* name);
+ void clearBlackmark(const char* name);
+
+ static IntParameter threshold;
+ static IntParameter initialTimeout;
+
+ protected:
+ struct ltStr {
+ bool operator()(const char* s1, const char* s2) const {
+ return strcmp(s1, s2) < 0;
+ };
+ };
+ struct BlacklistInfo {
+ int marks;
+ time_t blockUntil;
+ unsigned int blockTimeout;
+ };
+ typedef std::map<const char*,BlacklistInfo,ltStr> BlacklistMap;
+ BlacklistMap blm;
+ };
+
+}
+
+#endif
+
diff --git a/rfb/CConnection.cxx b/rfb/CConnection.cxx
new file mode 100644
index 00000000..c6a3eed8
--- /dev/null
+++ b/rfb/CConnection.cxx
@@ -0,0 +1,291 @@
+/* Copyright (C) 2002-2004 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 <stdio.h>
+#include <string.h>
+#include <rfb/Exception.h>
+#include <rfb/CMsgReaderV3.h>
+#include <rfb/CMsgWriterV3.h>
+#include <rfb/CSecurity.h>
+#include <rfb/secTypes.h>
+#include <rfb/CConnection.h>
+#include <rfb/util.h>
+
+#include <rfb/LogWriter.h>
+
+using namespace rfb;
+
+static LogWriter vlog("CConnection");
+
+CConnection::CConnection()
+ : is(0), os(0), reader_(0), writer_(0),
+ shared(false), security(0), nSecTypes(0), clientSecTypeOrder(false),
+ state_(RFBSTATE_UNINITIALISED), useProtocol3_3(false)
+{
+}
+
+CConnection::~CConnection()
+{
+ if (security) security->destroy();
+ deleteReaderAndWriter();
+}
+
+void CConnection::setServerName(const char* serverName_) {
+ serverName.buf = strDup(serverName_);
+}
+
+void CConnection::deleteReaderAndWriter()
+{
+ delete reader_;
+ reader_ = 0;
+ delete writer_;
+ writer_ = 0;
+}
+
+void CConnection::setStreams(rdr::InStream* is_, rdr::OutStream* os_)
+{
+ is = is_;
+ os = os_;
+}
+
+void CConnection::addSecType(rdr::U8 secType)
+{
+ if (nSecTypes == maxSecTypes)
+ throw Exception("too many security types");
+ secTypes[nSecTypes++] = secType;
+}
+
+void CConnection::setClientSecTypeOrder(bool clientOrder) {
+ clientSecTypeOrder = clientOrder;
+}
+
+void CConnection::initialiseProtocol()
+{
+ state_ = RFBSTATE_PROTOCOL_VERSION;
+}
+
+void CConnection::processMsg()
+{
+ switch (state_) {
+
+ case RFBSTATE_PROTOCOL_VERSION: processVersionMsg(); break;
+ case RFBSTATE_SECURITY_TYPES: processSecurityTypesMsg(); break;
+ case RFBSTATE_SECURITY: processSecurityMsg(); break;
+ case RFBSTATE_SECURITY_RESULT: processSecurityResultMsg(); break;
+ case RFBSTATE_INITIALISATION: processInitMsg(); break;
+ case RFBSTATE_NORMAL: reader_->readMsg(); break;
+ case RFBSTATE_UNINITIALISED:
+ throw Exception("CConnection::processMsg: not initialised yet?");
+ default:
+ throw Exception("CConnection::processMsg: invalid state");
+ }
+}
+
+void CConnection::processVersionMsg()
+{
+ vlog.debug("reading protocol version");
+ bool done;
+ if (!cp.readVersion(is, &done)) {
+ state_ = RFBSTATE_INVALID;
+ throw Exception("reading version failed: not an RFB server?");
+ }
+ if (!done) return;
+
+ vlog.info("Server supports RFB protocol version %d.%d",
+ cp.majorVersion, cp.minorVersion);
+
+ // The only official RFB protocol versions are currently 3.3, 3.7 and 3.8
+ if (cp.beforeVersion(3,3)) {
+ char msg[256];
+ sprintf(msg,"Server gave unsupported RFB protocol version %d.%d",
+ cp.majorVersion, cp.minorVersion);
+ vlog.error(msg);
+ state_ = RFBSTATE_INVALID;
+ throw Exception(msg);
+ } else if (useProtocol3_3 || cp.beforeVersion(3,7)) {
+ cp.setVersion(3,3);
+ } else if (cp.afterVersion(3,8)) {
+ cp.setVersion(3,8);
+ }
+
+ cp.writeVersion(os);
+ state_ = RFBSTATE_SECURITY_TYPES;
+
+ vlog.info("Using RFB protocol version %d.%d",
+ cp.majorVersion, cp.minorVersion);
+}
+
+
+void CConnection::processSecurityTypesMsg()
+{
+ vlog.debug("processing security types message");
+
+ int secType = secTypeInvalid;
+
+ if (cp.isVersion(3,3)) {
+
+ // legacy 3.3 server may only offer "vnc authentication" or "none"
+
+ secType = is->readU32();
+ if (secType == secTypeInvalid) {
+ throwConnFailedException();
+
+ } else if (secType == secTypeNone || secType == secTypeVncAuth) {
+ int j;
+ for (j = 0; j < nSecTypes; j++)
+ if (secTypes[j] == secType) break;
+ if (j == nSecTypes)
+ secType = secTypeInvalid;
+ } else {
+ vlog.error("Unknown 3.3 security type %d", secType);
+ throw Exception("Unknown 3.3 security type");
+ }
+
+ } else {
+
+ // >=3.7 server will offer us a list
+
+ int nServerSecTypes = is->readU8();
+ if (nServerSecTypes == 0)
+ throwConnFailedException();
+
+ int secTypePos = nSecTypes;
+ for (int i = 0; i < nServerSecTypes; i++) {
+ rdr::U8 serverSecType = is->readU8();
+ vlog.debug("Server offers security type %s(%d)",
+ secTypeName(serverSecType),serverSecType);
+
+ // If we haven't already chosen a secType, try this one
+ // If we are using the client's preference for types,
+ // we keep trying types, to find the one that matches and
+ // which appears first in the client's list of supported types.
+ if (secType == secTypeInvalid || clientSecTypeOrder) {
+ for (int j = 0; j < nSecTypes; j++) {
+ if (secTypes[j] == serverSecType && j < secTypePos) {
+ secType = secTypes[j];
+ secTypePos = j;
+ break;
+ }
+ }
+ // NB: Continue reading the remaining server secTypes, but ignore them
+ }
+ }
+
+ // Inform the server of our decision
+ if (secType != secTypeInvalid) {
+ os->writeU8(secType);
+ os->flush();
+ vlog.debug("Choosing security type %s(%d)",secTypeName(secType),secType);
+ }
+ }
+
+ if (secType == secTypeInvalid) {
+ state_ = RFBSTATE_INVALID;
+ vlog.error("No matching security types");
+ throw Exception("No matching security types");
+ }
+
+ state_ = RFBSTATE_SECURITY;
+ security = getCSecurity(secType);
+ processSecurityMsg();
+}
+
+void CConnection::processSecurityMsg()
+{
+ vlog.debug("processing security message");
+ bool done;
+ if (!security->processMsg(this, &done))
+ throwAuthFailureException();
+ if (done) {
+ state_ = RFBSTATE_SECURITY_RESULT;
+ processSecurityResultMsg();
+ }
+}
+
+void CConnection::processSecurityResultMsg()
+{
+ vlog.debug("processing security result message");
+ int result;
+ if (cp.beforeVersion(3,8) && security->getType() == secTypeNone) {
+ result = secResultOK;
+ } else {
+ if (!is->checkNoWait(1)) return;
+ result = is->readU32();
+ }
+ switch (result) {
+ case secResultOK:
+ securityCompleted();
+ break;
+ case secResultFailed:
+ vlog.debug("auth failed");
+ throwAuthFailureException();
+ case secResultTooMany:
+ vlog.debug("auth failed - too many tries");
+ throwAuthFailureException();
+ default:
+ vlog.error("unknown security result");
+ throwAuthFailureException();
+ };
+}
+
+void CConnection::processInitMsg()
+{
+ vlog.debug("reading server initialisation");
+ reader_->readServerInit();
+}
+
+void CConnection::throwAuthFailureException()
+{
+ CharArray reason;
+ vlog.debug("state=%d, ver=%d.%d", state(), cp.majorVersion, cp.minorVersion);
+ if (state()==RFBSTATE_SECURITY_RESULT && !cp.beforeVersion(3,8)) {
+ reason.buf = is->readString();
+ } else {
+ reason.buf = strDup("Authentication failure");
+ }
+ state_ = RFBSTATE_INVALID;
+ vlog.error(reason.buf);
+ throw AuthFailureException(reason.buf);
+}
+
+void CConnection::throwConnFailedException()
+{
+ state_ = RFBSTATE_INVALID;
+ CharArray reason;
+ reason.buf = is->readString();
+ throw ConnFailedException(reason.buf);
+}
+
+void CConnection::securityCompleted()
+{
+ state_ = RFBSTATE_INITIALISATION;
+ reader_ = new CMsgReaderV3(this, is);
+ writer_ = new CMsgWriterV3(&cp, os);
+ vlog.debug("Authentication success!");
+ authSuccess();
+ writer_->writeClientInit(shared);
+}
+
+void CConnection::authSuccess()
+{
+}
+
+void CConnection::serverInit()
+{
+ state_ = RFBSTATE_NORMAL;
+ vlog.debug("initialisation done");
+}
diff --git a/rfb/CConnection.h b/rfb/CConnection.h
new file mode 100644
index 00000000..480fca30
--- /dev/null
+++ b/rfb/CConnection.h
@@ -0,0 +1,178 @@
+/* Copyright (C) 2002-2004 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.
+ */
+//
+// CConnection - class on the client side representing a connection to a
+// server. A derived class should override methods appropriately.
+//
+
+#ifndef __RFB_CCONNECTION_H__
+#define __RFB_CCONNECTION_H__
+
+#include <rdr/InStream.h>
+#include <rdr/OutStream.h>
+#include <rfb/CMsgHandler.h>
+#include <rfb/util.h>
+
+namespace rfb {
+
+ class CMsgReader;
+ class CMsgWriter;
+ class CSecurity;
+ class IdentityVerifier;
+
+ class CConnection : public CMsgHandler {
+ public:
+
+ CConnection();
+ virtual ~CConnection();
+
+ // ***
+ void setServerName(const char* serverName_);
+
+ // Methods to initialise the connection
+
+ // setStreams() sets the streams to be used for the connection. These must
+ // be set before initialiseProtocol() and processMsg() are called. The
+ // CSecurity object may call setStreams() again to provide alternative
+ // streams over which the RFB protocol is sent (i.e. encrypting/decrypting
+ // streams). Ownership of the streams remains with the caller
+ // (i.e. SConnection will not delete them).
+ void setStreams(rdr::InStream* is, rdr::OutStream* os);
+
+ // addSecType() should be called once for each security type which the
+ // client supports. The order in which they're added is such that the
+ // first one is most preferred.
+ void addSecType(rdr::U8 secType);
+
+ // setClientSecTypeOrder() determines whether the client should obey
+ // the server's security type preference, by picking the first server security
+ // type that the client supports, or whether it should pick the first type
+ // that the server supports, from the client-supported list of types.
+ void setClientSecTypeOrder(bool clientOrder);
+
+ // setShared sets the value of the shared flag which will be sent to the
+ // server upon initialisation.
+ void setShared(bool s) { shared = s; }
+
+ // setProtocol3_3 configures whether or not the CConnection should
+ // only ever support protocol version 3.3
+ void setProtocol3_3(bool s) {useProtocol3_3 = s;}
+
+ // initialiseProtocol() should be called once the streams and security
+ // types are set. Subsequently, processMsg() should be called whenever
+ // there is data to read on the InStream.
+ void initialiseProtocol();
+
+ // processMsg() should be called whenever there is either:
+ // - data available on the underlying network stream
+ // In this case, processMsg may return without processing an RFB message,
+ // if the available data does not result in an RFB message being ready
+ // to handle. e.g. if data is encrypted.
+ // NB: This makes it safe to call processMsg() in response to select()
+ // - data available on the CConnection's current InStream
+ // In this case, processMsg should always process the available RFB
+ // message before returning.
+ // NB: In either case, you must have called initialiseProtocol() first.
+ void processMsg();
+
+
+ // Methods to be overridden in a derived class
+
+ // getCSecurity() gets the CSecurity object for the given type. The type
+ // is guaranteed to be one of the secTypes passed in to addSecType(). The
+ // CSecurity object's destroy() method will be called by the CConnection
+ // from its destructor.
+ virtual CSecurity* getCSecurity(int secType)=0;
+
+ // getCurrentCSecurity() gets the CSecurity instance used for this connection.
+ const CSecurity* getCurrentCSecurity() const {return security;}
+
+ // getIdVerifier() returns the identity verifier associated with the connection.
+ // Ownership of the IdentityVerifier is retained by the CConnection instance.
+ virtual IdentityVerifier* getIdentityVerifier() {return 0;}
+
+ // authSuccess() is called when authentication has succeeded.
+ virtual void authSuccess();
+
+ // serverInit() is called when the ServerInit message is received. The
+ // derived class must call on to CConnection::serverInit().
+ virtual void serverInit();
+
+
+ // Other methods
+
+ // deleteReaderAndWriter() deletes the reader and writer associated with
+ // this connection. This may be useful if you want to delete the streams
+ // before deleting the SConnection to make sure that no attempt by the
+ // SConnection is made to read or write.
+ // XXX Do we really need this at all???
+ void deleteReaderAndWriter();
+
+ CMsgReader* reader() { return reader_; }
+ CMsgWriter* writer() { return writer_; }
+
+ rdr::InStream* getInStream() { return is; }
+ rdr::OutStream* getOutStream() { return os; }
+
+ char* getServerName() {return strDup(serverName.buf);}
+
+ enum stateEnum {
+ RFBSTATE_UNINITIALISED,
+ RFBSTATE_PROTOCOL_VERSION,
+ RFBSTATE_SECURITY_TYPES,
+ RFBSTATE_SECURITY,
+ RFBSTATE_SECURITY_RESULT,
+ RFBSTATE_INITIALISATION,
+ RFBSTATE_NORMAL,
+ RFBSTATE_INVALID
+ };
+
+ stateEnum state() { return state_; }
+
+ protected:
+ void setState(stateEnum s) { state_ = s; }
+
+ private:
+ void processVersionMsg();
+ void processSecurityTypesMsg();
+ void processSecurityMsg();
+ void processSecurityResultMsg();
+ void processInitMsg();
+ void throwAuthFailureException();
+ void throwConnFailedException();
+ void securityCompleted();
+
+ rdr::InStream* is;
+ rdr::OutStream* os;
+ CMsgReader* reader_;
+ CMsgWriter* writer_;
+ bool deleteStreamsWhenDone;
+ bool shared;
+ CSecurity* security;
+ enum { maxSecTypes = 8 };
+ int nSecTypes;
+ rdr::U8 secTypes[maxSecTypes];
+ bool clientSecTypeOrder;
+ stateEnum state_;
+
+ CharArray serverName;
+
+ bool useProtocol3_3;
+ };
+}
+#endif
diff --git a/rfb/CMsgHandler.cxx b/rfb/CMsgHandler.cxx
new file mode 100644
index 00000000..10109169
--- /dev/null
+++ b/rfb/CMsgHandler.cxx
@@ -0,0 +1,99 @@
+/* Copyright (C) 2002-2003 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 <rfb/Exception.h>
+#include <rfb/CMsgHandler.h>
+
+using namespace rfb;
+
+CMsgHandler::CMsgHandler()
+{
+}
+
+CMsgHandler::~CMsgHandler()
+{
+}
+
+void CMsgHandler::setDesktopSize(int width, int height)
+{
+ cp.width = width;
+ cp.height = height;
+}
+
+void CMsgHandler::setCursor(const Point& hotspot, const Point& size, void* data, void* mask)
+{
+}
+
+void CMsgHandler::setPixelFormat(const PixelFormat& pf)
+{
+ cp.setPF(pf);
+}
+
+void CMsgHandler::setName(const char* name)
+{
+ cp.setName(name);
+}
+
+void CMsgHandler::serverInit()
+{
+ throw Exception("CMsgHandler::serverInit called");
+}
+
+void CMsgHandler::framebufferUpdateStart()
+{
+}
+
+void CMsgHandler::framebufferUpdateEnd()
+{
+}
+
+void CMsgHandler::beginRect(const Rect& r, unsigned int encoding)
+{
+}
+
+void CMsgHandler::endRect(const Rect& r, unsigned int encoding)
+{
+}
+
+
+void CMsgHandler::setColourMapEntries(int firstColour, int nColours,
+ rdr::U16* rgbs)
+{
+ throw Exception("CMsgHandler::setColourMapEntries called");
+}
+
+void CMsgHandler::bell()
+{
+}
+
+void CMsgHandler::serverCutText(const char* str, int len)
+{
+}
+
+void CMsgHandler::fillRect(const Rect& r, Pixel pix)
+{
+}
+
+void CMsgHandler::imageRect(const Rect& r, void* pixels)
+{
+}
+
+void CMsgHandler::copyRect(const Rect& r, int srcX, int srcY)
+{
+}
+
+
diff --git a/rfb/CMsgHandler.h b/rfb/CMsgHandler.h
new file mode 100644
index 00000000..7a69f145
--- /dev/null
+++ b/rfb/CMsgHandler.h
@@ -0,0 +1,62 @@
+/* Copyright (C) 2002-2003 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.
+ */
+//
+// CMsgHandler - class to handle incoming messages on the client side.
+//
+
+#ifndef __RFB_CMSGHANDLER_H__
+#define __RFB_CMSGHANDLER_H__
+
+#include <rdr/types.h>
+#include <rfb/Pixel.h>
+#include <rfb/ConnParams.h>
+#include <rfb/Rect.h>
+
+namespace rdr { class InStream; }
+
+namespace rfb {
+
+ class CMsgHandler {
+ public:
+ CMsgHandler();
+ virtual ~CMsgHandler();
+
+ virtual void setDesktopSize(int w, int h);
+ virtual void setCursor(const Point& hotspot, const Point& size, void* data, void* mask);
+ virtual void setPixelFormat(const PixelFormat& pf);
+ virtual void setName(const char* name);
+ virtual void serverInit();
+
+ virtual void framebufferUpdateStart();
+ virtual void framebufferUpdateEnd();
+ virtual void beginRect(const Rect& r, unsigned int encoding);
+ virtual void endRect(const Rect& r, unsigned int encoding);
+
+ virtual void setColourMapEntries(int firstColour, int nColours,
+ rdr::U16* rgbs);
+ virtual void bell();
+ virtual void serverCutText(const char* str, int len);
+
+ 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);
+
+ ConnParams cp;
+ };
+}
+#endif
diff --git a/rfb/CMsgReader.cxx b/rfb/CMsgReader.cxx
new file mode 100644
index 00000000..46973eb2
--- /dev/null
+++ b/rfb/CMsgReader.cxx
@@ -0,0 +1,167 @@
+/* Copyright (C) 2002-2004 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 <stdio.h>
+#include <rdr/InStream.h>
+#include <rfb/Exception.h>
+#include <rfb/util.h>
+#include <rfb/CMsgHandler.h>
+#include <rfb/CMsgReader.h>
+
+using namespace rfb;
+
+CMsgReader::CMsgReader(CMsgHandler* handler_, rdr::InStream* is_)
+ : imageBufIdealSize(0), handler(handler_), is(is_),
+ imageBuf(0), imageBufSize(0)
+{
+ for (unsigned int i = 0; i <= encodingMax; i++) {
+ decoders[i] = 0;
+ }
+}
+
+CMsgReader::~CMsgReader()
+{
+ for (unsigned int i = 0; i <= encodingMax; i++) {
+ delete decoders[i];
+ }
+ delete [] imageBuf;
+}
+
+void CMsgReader::endMsg()
+{
+}
+
+void CMsgReader::readSetColourMapEntries()
+{
+ is->skip(1);
+ int firstColour = is->readU16();
+ int nColours = is->readU16();
+ rdr::U16Array rgbs(nColours * 3);
+ for (int i = 0; i < nColours * 3; i++)
+ rgbs.buf[i] = is->readU16();
+ endMsg();
+ handler->setColourMapEntries(firstColour, nColours, rgbs.buf);
+}
+
+void CMsgReader::readBell()
+{
+ endMsg();
+ handler->bell();
+}
+
+void CMsgReader::readServerCutText()
+{
+ is->skip(3);
+ int len = is->readU32();
+ if (len > 256*1024) {
+ is->skip(len);
+ fprintf(stderr,"cut text too long (%d bytes) - ignoring\n",len);
+ return;
+ }
+ CharArray ca(len+1);
+ ca.buf[len] = 0;
+ is->readBytes(ca.buf, len);
+ endMsg();
+ handler->serverCutText(ca.buf, len);
+}
+
+void CMsgReader::readFramebufferUpdateStart()
+{
+ endMsg();
+ handler->framebufferUpdateStart();
+}
+
+void CMsgReader::readFramebufferUpdateEnd()
+{
+ endMsg();
+ handler->framebufferUpdateEnd();
+}
+
+void CMsgReader::readRect(const Rect& r, unsigned int encoding)
+{
+ if ((r.br.x > handler->cp.width) || (r.br.y > handler->cp.height)) {
+ fprintf(stderr, "Rect too big: %dx%d at %d,%d exceeds %dx%d\n",
+ r.width(), r.height(), r.tl.x, r.tl.y,
+ handler->cp.width, handler->cp.height);
+ throw Exception("Rect too big");
+ }
+
+ if (r.is_empty())
+ fprintf(stderr, "Warning: zero size rect\n");
+
+ handler->beginRect(r, encoding);
+
+ if (encoding == encodingCopyRect) {
+ readCopyRect(r);
+ } else {
+ if (!decoders[encoding]) {
+ decoders[encoding] = Decoder::createDecoder(encoding, this);
+ if (!decoders[encoding]) {
+ fprintf(stderr, "Unknown rect encoding %d\n", encoding);
+ throw Exception("Unknown rect encoding");
+ }
+ }
+ decoders[encoding]->readRect(r, handler);
+ }
+
+ handler->endRect(r, encoding);
+}
+
+void CMsgReader::readCopyRect(const Rect& r)
+{
+ int srcX = is->readU16();
+ int srcY = is->readU16();
+ handler->copyRect(r, srcX, srcY);
+}
+
+void CMsgReader::readSetCursor(const Point& hotspot, const Point& size)
+{
+ int data_len = size.x * size.y * (handler->cp.pf().bpp/8);
+ int mask_len = ((size.x+7)/8) * size.y;
+ rdr::U8Array data(data_len);
+ rdr::U8Array mask(mask_len);
+
+ is->readBytes(data.buf, data_len);
+ is->readBytes(mask.buf, mask_len);
+
+ handler->setCursor(hotspot, size, data.buf, mask.buf);
+}
+
+rdr::U8* CMsgReader::getImageBuf(int required, int requested, int* nPixels)
+{
+ int requiredBytes = required * (handler->cp.pf().bpp / 8);
+ int requestedBytes = requested * (handler->cp.pf().bpp / 8);
+ int size = requestedBytes;
+ if (size > imageBufIdealSize) size = imageBufIdealSize;
+
+ if (size < requiredBytes)
+ size = requiredBytes;
+
+ if (imageBufSize < size) {
+ imageBufSize = size;
+ delete [] imageBuf;
+ imageBuf = new rdr::U8[imageBufSize];
+ }
+ if (nPixels)
+ *nPixels = imageBufSize / (handler->cp.pf().bpp / 8);
+ return imageBuf;
+}
+
+int CMsgReader::bpp()
+{
+ return handler->cp.pf().bpp;
+}
diff --git a/rfb/CMsgReader.h b/rfb/CMsgReader.h
new file mode 100644
index 00000000..8b4638c7
--- /dev/null
+++ b/rfb/CMsgReader.h
@@ -0,0 +1,75 @@
+/* Copyright (C) 2002-2003 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.
+ */
+//
+// CMsgReader - class for reading RFB messages on the server side
+// (i.e. messages from client to server).
+//
+
+#ifndef __RFB_CMSGREADER_H__
+#define __RFB_CMSGREADER_H__
+
+#include <rdr/types.h>
+#include <rfb/encodings.h>
+#include <rfb/Decoder.h>
+
+namespace rdr { class InStream; }
+
+namespace rfb {
+ class CMsgHandler;
+ struct Rect;
+
+ class CMsgReader {
+ public:
+ virtual ~CMsgReader();
+
+ virtual void readServerInit()=0;
+
+ // readMsg() reads a message, calling the handler as appropriate.
+ virtual void readMsg()=0;
+
+ rdr::InStream* getInStream() { return is; }
+ rdr::U8* getImageBuf(int required, int requested=0, int* nPixels=0);
+ int bpp();
+
+ int imageBufIdealSize;
+
+ protected:
+ virtual void readSetColourMapEntries();
+ virtual void readBell();
+ virtual void readServerCutText();
+
+ virtual void endMsg();
+
+ virtual void readFramebufferUpdateStart();
+ virtual void readFramebufferUpdateEnd();
+ virtual void readRect(const Rect& r, unsigned int encoding);
+
+ virtual void readCopyRect(const Rect& r);
+
+ virtual void readSetCursor(const Point& hotspot, const Point& size);
+
+ CMsgReader(CMsgHandler* handler, rdr::InStream* is);
+
+ CMsgHandler* handler;
+ rdr::InStream* is;
+ Decoder* decoders[encodingMax+1];
+ rdr::U8* imageBuf;
+ int imageBufSize;
+ };
+}
+#endif
diff --git a/rfb/CMsgReaderV3.cxx b/rfb/CMsgReaderV3.cxx
new file mode 100644
index 00000000..1f974fd5
--- /dev/null
+++ b/rfb/CMsgReaderV3.cxx
@@ -0,0 +1,97 @@
+/* Copyright (C) 2002-2003 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 <rfb/PixelFormat.h>
+#include <rfb/msgTypes.h>
+#include <rfb/Exception.h>
+#include <rdr/InStream.h>
+#include <rfb/CMsgReaderV3.h>
+#include <rfb/CMsgHandler.h>
+
+using namespace rfb;
+
+CMsgReaderV3::CMsgReaderV3(CMsgHandler* handler, rdr::InStream* is)
+ : CMsgReader(handler, is), nUpdateRectsLeft(0)
+{
+}
+
+CMsgReaderV3::~CMsgReaderV3()
+{
+}
+
+void CMsgReaderV3::readServerInit()
+{
+ int width = is->readU16();
+ int height = is->readU16();
+ handler->setDesktopSize(width, height);
+ PixelFormat pf;
+ pf.read(is);
+ handler->setPixelFormat(pf);
+ char* name = is->readString();
+ handler->setName(name);
+ delete [] name;
+ endMsg();
+ handler->serverInit();
+}
+
+void CMsgReaderV3::readMsg()
+{
+ if (nUpdateRectsLeft == 0) {
+
+ int type = is->readU8();
+ switch (type) {
+ case msgTypeFramebufferUpdate: readFramebufferUpdate(); break;
+ case msgTypeSetColourMapEntries: readSetColourMapEntries(); break;
+ case msgTypeBell: readBell(); break;
+ case msgTypeServerCutText: readServerCutText(); break;
+ default:
+ fprintf(stderr, "unknown message type %d\n", type);
+ throw Exception("unknown message type");
+ }
+
+ } else {
+
+ int x = is->readU16();
+ int y = is->readU16();
+ int w = is->readU16();
+ int h = is->readU16();
+ unsigned int encoding = is->readU32();
+
+ switch (encoding) {
+ case pseudoEncodingDesktopSize:
+ handler->setDesktopSize(w, h);
+ break;
+ case pseudoEncodingCursor:
+ readSetCursor(Point(x, y), Point(w, h));
+ break;
+ default:
+ readRect(Rect(x, y, x+w, y+h), encoding);
+ break;
+ };
+
+ nUpdateRectsLeft--;
+ if (nUpdateRectsLeft == 0) handler->framebufferUpdateEnd();
+ }
+}
+
+void CMsgReaderV3::readFramebufferUpdate()
+{
+ is->skip(1);
+ nUpdateRectsLeft = is->readU16();
+ endMsg();
+ handler->framebufferUpdateStart();
+}
diff --git a/rfb/CMsgReaderV3.h b/rfb/CMsgReaderV3.h
new file mode 100644
index 00000000..93c8c6a5
--- /dev/null
+++ b/rfb/CMsgReaderV3.h
@@ -0,0 +1,35 @@
+/* Copyright (C) 2002-2003 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_CMSGREADERV3_H__
+#define __RFB_CMSGREADERV3_H__
+
+#include <rfb/CMsgReader.h>
+
+namespace rfb {
+ class CMsgReaderV3 : public CMsgReader {
+ public:
+ CMsgReaderV3(CMsgHandler* handler, rdr::InStream* is);
+ virtual ~CMsgReaderV3();
+ virtual void readServerInit();
+ virtual void readMsg();
+ private:
+ void readFramebufferUpdate();
+ int nUpdateRectsLeft;
+ };
+}
+#endif
diff --git a/rfb/CMsgWriter.cxx b/rfb/CMsgWriter.cxx
new file mode 100644
index 00000000..a7e4fb93
--- /dev/null
+++ b/rfb/CMsgWriter.cxx
@@ -0,0 +1,130 @@
+/* Copyright (C) 2002-2003 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 <stdio.h>
+#include <rdr/OutStream.h>
+#include <rfb/msgTypes.h>
+#include <rfb/PixelFormat.h>
+#include <rfb/Rect.h>
+#include <rfb/ConnParams.h>
+#include <rfb/Decoder.h>
+#include <rfb/CMsgWriter.h>
+
+using namespace rfb;
+
+CMsgWriter::CMsgWriter(ConnParams* cp_, rdr::OutStream* os_)
+ : cp(cp_), os(os_)
+{
+}
+
+CMsgWriter::~CMsgWriter()
+{
+}
+
+void CMsgWriter::writeSetPixelFormat(const PixelFormat& pf)
+{
+ startMsg(msgTypeSetPixelFormat);
+ os->pad(3);
+ pf.write(os);
+ endMsg();
+}
+
+void CMsgWriter::writeSetEncodings(int nEncodings, rdr::U32* encodings)
+{
+ startMsg(msgTypeSetEncodings);
+ os->skip(1);
+ os->writeU16(nEncodings);
+ for (int i = 0; i < nEncodings; i++)
+ os->writeU32(encodings[i]);
+ endMsg();
+}
+
+// Ask for encodings based on which decoders are supported. Assumes higher
+// encoding numbers are more desirable.
+
+void CMsgWriter::writeSetEncodings(int preferredEncoding, bool useCopyRect)
+{
+ int nEncodings = 0;
+ rdr::U32 encodings[encodingMax+2];
+ if (cp->supportsLocalCursor)
+ encodings[nEncodings++] = pseudoEncodingCursor;
+ if (cp->supportsDesktopResize)
+ encodings[nEncodings++] = pseudoEncodingDesktopSize;
+ if (Decoder::supported(preferredEncoding)) {
+ encodings[nEncodings++] = preferredEncoding;
+ }
+ if (useCopyRect) {
+ encodings[nEncodings++] = encodingCopyRect;
+ }
+ for (int i = encodingMax; i >= 0; i--) {
+ if (i != preferredEncoding && Decoder::supported(i)) {
+ encodings[nEncodings++] = i;
+ }
+ }
+ writeSetEncodings(nEncodings, encodings);
+}
+
+void CMsgWriter::writeFramebufferUpdateRequest(const Rect& r, bool incremental)
+{
+ startMsg(msgTypeFramebufferUpdateRequest);
+ os->writeU8(incremental);
+ os->writeU16(r.tl.x);
+ os->writeU16(r.tl.y);
+ os->writeU16(r.width());
+ os->writeU16(r.height());
+ endMsg();
+}
+
+
+void CMsgWriter::writeKeyEvent(rdr::U32 key, bool down)
+{
+ startMsg(msgTypeKeyEvent);
+ os->writeU8(down);
+ os->pad(2);
+ os->writeU32(key);
+ endMsg();
+}
+
+
+void CMsgWriter::writePointerEvent(int x, int y, int buttonMask)
+{
+ if (x < 0) x = 0;
+ if (y < 0) y = 0;
+ if (x >= cp->width) x = cp->width - 1;
+ if (y >= cp->height) y = cp->height - 1;
+
+ startMsg(msgTypePointerEvent);
+ os->writeU8(buttonMask);
+ os->writeU16(x);
+ os->writeU16(y);
+ endMsg();
+}
+
+
+void CMsgWriter::writeClientCutText(const char* str, int len)
+{
+ startMsg(msgTypeClientCutText);
+ os->pad(3);
+ os->writeU32(len);
+ os->writeBytes(str, len);
+ endMsg();
+}
+
+void CMsgWriter::setOutStream(rdr::OutStream* os_)
+{
+ os = os_;
+}
diff --git a/rfb/CMsgWriter.h b/rfb/CMsgWriter.h
new file mode 100644
index 00000000..8d6e373c
--- /dev/null
+++ b/rfb/CMsgWriter.h
@@ -0,0 +1,64 @@
+/* Copyright (C) 2002-2003 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.
+ */
+//
+// CMsgWriter - class for writing RFB messages on the server side.
+//
+
+#ifndef __RFB_CMSGWRITER_H__
+#define __RFB_CMSGWRITER_H__
+
+#include <rdr/types.h>
+
+namespace rdr { class OutStream; }
+
+namespace rfb {
+
+ class PixelFormat;
+ class ConnParams;
+ struct Rect;
+
+ class CMsgWriter {
+ public:
+ virtual ~CMsgWriter();
+
+ virtual void writeClientInit(bool shared)=0;
+
+ virtual void writeSetPixelFormat(const PixelFormat& pf);
+ virtual void writeSetEncodings(int nEncodings, rdr::U32* encodings);
+ virtual void writeSetEncodings(int preferredEncoding, bool useCopyRect);
+ virtual void writeFramebufferUpdateRequest(const Rect& r,bool incremental);
+ virtual void writeKeyEvent(rdr::U32 key, bool down);
+ virtual void writePointerEvent(int x, int y, int buttonMask);
+ virtual void writeClientCutText(const char* str, int len);
+
+ virtual void startMsg(int type)=0;
+ virtual void endMsg()=0;
+
+ virtual void setOutStream(rdr::OutStream* os);
+
+ ConnParams* getConnParams() { return cp; }
+ rdr::OutStream* getOutStream() { return os; }
+
+ protected:
+ CMsgWriter(ConnParams* cp, rdr::OutStream* os);
+
+ ConnParams* cp;
+ rdr::OutStream* os;
+ };
+}
+#endif
diff --git a/rfb/CMsgWriterV3.cxx b/rfb/CMsgWriterV3.cxx
new file mode 100644
index 00000000..a6ad237c
--- /dev/null
+++ b/rfb/CMsgWriterV3.cxx
@@ -0,0 +1,49 @@
+/* Copyright (C) 2002-2004 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 <rdr/OutStream.h>
+#include <rfb/msgTypes.h>
+#include <rfb/Exception.h>
+#include <rfb/ConnParams.h>
+#include <rfb/CMsgWriterV3.h>
+
+using namespace rfb;
+
+CMsgWriterV3::CMsgWriterV3(ConnParams* cp, rdr::OutStream* os)
+ : CMsgWriter(cp, os)
+{
+}
+
+CMsgWriterV3::~CMsgWriterV3()
+{
+}
+
+void CMsgWriterV3::writeClientInit(bool shared)
+{
+ os->writeU8(shared);
+ endMsg();
+}
+
+void CMsgWriterV3::startMsg(int type)
+{
+ os->writeU8(type);
+}
+
+void CMsgWriterV3::endMsg()
+{
+ os->flush();
+}
diff --git a/rfb/CMsgWriterV3.h b/rfb/CMsgWriterV3.h
new file mode 100644
index 00000000..0cf6157a
--- /dev/null
+++ b/rfb/CMsgWriterV3.h
@@ -0,0 +1,35 @@
+/* Copyright (C) 2002-2003 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_CMSGWRITERV3_H__
+#define __RFB_CMSGWRITERV3_H__
+
+#include <rfb/CMsgWriter.h>
+
+namespace rfb {
+ class CMsgWriterV3 : public CMsgWriter {
+ public:
+ CMsgWriterV3(ConnParams* cp, rdr::OutStream* os);
+ virtual ~CMsgWriterV3();
+
+ virtual void writeClientInit(bool shared);
+ virtual void startMsg(int type);
+ virtual void endMsg();
+
+ };
+}
+#endif
diff --git a/rfb/CSecurity.h b/rfb/CSecurity.h
new file mode 100644
index 00000000..639d550e
--- /dev/null
+++ b/rfb/CSecurity.h
@@ -0,0 +1,48 @@
+/* Copyright (C) 2002-2004 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.
+ */
+//
+// CSecurity - class on the client side for handling security handshaking. A
+// derived class for a particular security type overrides the processMsg()
+// method. processMsg() is called first when the security type has been
+// decided on, and will keep being called whenever there is data to read from
+// the server until either it returns false, indicating authentication/security
+// failure, or it returns with done set to true, to indicate success.
+//
+// Note that the first time processMsg() is called, there is no guarantee that
+// there is any data to read from the CConnection's InStream, but subsequent
+// calls guarantee there is at least one byte which can be read without
+// blocking.
+//
+// description is a string describing the level of encryption applied to the
+// session, or null if no encryption will be used.
+
+#ifndef __RFB_CSECURITY_H__
+#define __RFB_CSECURITY_H__
+
+namespace rfb {
+ class CConnection;
+ class CSecurity {
+ public:
+ virtual ~CSecurity() {}
+ virtual bool processMsg(CConnection* cc, bool* done)=0;
+ virtual void destroy() { delete this; }
+ virtual int getType() const = 0;
+ virtual const char* description() const = 0;
+ };
+}
+#endif
diff --git a/rfb/CSecurityNone.h b/rfb/CSecurityNone.h
new file mode 100644
index 00000000..23b36cee
--- /dev/null
+++ b/rfb/CSecurityNone.h
@@ -0,0 +1,38 @@
+/* Copyright (C) 2002-2004 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.
+ */
+//
+// CSecurityNone.h
+//
+
+#ifndef __CSECURITYNONE_H__
+#define __CSECURITYNONE_H__
+
+#include <rfb/CSecurity.h>
+
+namespace rfb {
+
+ class CSecurityNone : public CSecurity {
+ public:
+ virtual bool processMsg(CConnection* cc, bool* done) {
+ *done = true; return true;
+ }
+ virtual int getType() const {return secTypeNone;}
+ virtual const char* description() const {return "No Encryption";}
+ };
+}
+#endif
diff --git a/rfb/CSecurityVncAuth.cxx b/rfb/CSecurityVncAuth.cxx
new file mode 100644
index 00000000..3d6c87c5
--- /dev/null
+++ b/rfb/CSecurityVncAuth.cxx
@@ -0,0 +1,63 @@
+/* Copyright (C) 2002-2003 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.
+ */
+//
+// CSecurityVncAuth
+//
+
+#include <string.h>
+#include <stdio.h>
+#include <rfb/CConnection.h>
+#include <rfb/UserPasswdGetter.h>
+#include <rfb/vncAuth.h>
+#include <rfb/CSecurityVncAuth.h>
+#include <rfb/LogWriter.h>
+#include <rfb/util.h>
+
+using namespace rfb;
+
+static LogWriter vlog("VncAuth");
+
+CSecurityVncAuth::CSecurityVncAuth(UserPasswdGetter* upg_)
+ : upg(upg_)
+{
+}
+
+CSecurityVncAuth::~CSecurityVncAuth()
+{
+}
+
+bool CSecurityVncAuth::processMsg(CConnection* cc, bool* done)
+{
+ *done = false;
+ rdr::InStream* is = cc->getInStream();
+ rdr::OutStream* os = cc->getOutStream();
+
+ rdr::U8 challenge[vncAuthChallengeSize];
+ is->readBytes(challenge, vncAuthChallengeSize);
+ CharArray passwd;
+ if (!upg->getUserPasswd(0, &passwd.buf)) {
+ vlog.error("Getting password failed");
+ return false;
+ }
+ vncAuthEncryptChallenge(challenge, passwd.buf);
+ memset(passwd.buf, 0, strlen(passwd.buf));
+ os->writeBytes(challenge, vncAuthChallengeSize);
+ os->flush();
+ *done = true;
+ return true;
+}
diff --git a/rfb/CSecurityVncAuth.h b/rfb/CSecurityVncAuth.h
new file mode 100644
index 00000000..bfa40b3b
--- /dev/null
+++ b/rfb/CSecurityVncAuth.h
@@ -0,0 +1,40 @@
+/* Copyright (C) 2002-2004 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_CSECURITYVNCAUTH_H__
+#define __RFB_CSECURITYVNCAUTH_H__
+
+#include <rfb/CSecurity.h>
+#include <rfb/secTypes.h>
+#include <rfb/vncAuth.h>
+
+namespace rfb {
+
+ class UserPasswdGetter;
+
+ class CSecurityVncAuth : public CSecurity {
+ public:
+ CSecurityVncAuth(UserPasswdGetter* pg);
+ virtual ~CSecurityVncAuth();
+ virtual bool processMsg(CConnection* cc, bool* done);
+ virtual int getType() const {return secTypeVncAuth;};
+ virtual const char* description() const {return "No Encryption";}
+ private:
+ UserPasswdGetter* upg;
+ };
+}
+#endif
diff --git a/rfb/ColourCube.h b/rfb/ColourCube.h
new file mode 100644
index 00000000..904256c4
--- /dev/null
+++ b/rfb/ColourCube.h
@@ -0,0 +1,96 @@
+/* Copyright (C) 2002-2003 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.
+ */
+//
+// ColourCube - structure to represent a colour cube. The colour cube consists
+// of its dimensions (nRed x nGreen x nBlue) and a table mapping an (r,g,b)
+// triple to a pixel value.
+//
+// A colour cube is used in two cases. The first is internally in a viewer
+// when it cannot use a trueColour format, nor can it have exclusive access to
+// a writable colour map. This is most notably the case for an X viewer
+// wishing to use a PseudoColor X server's default colormap.
+//
+// The second use is on the server side when a client has asked for a colour
+// map and the server is trueColour. Instead of setting an uneven trueColour
+// format like bgr233, it can set the client's colour map up with a 6x6x6
+// colour cube. For this use the colour cube table has a null mapping, which
+// makes it easy to perform the reverse lookup operation from pixel value to
+// r,g,b values.
+
+#ifndef __RFB_COLOURCUBE_H__
+#define __RFB_COLOURCUBE_H__
+
+#include <rfb/Pixel.h>
+#include <rfb/ColourMap.h>
+
+namespace rfb {
+
+ class ColourCube : public ColourMap {
+ public:
+ ColourCube(int nr, int ng, int nb, Pixel* table_=0)
+ : nRed(nr), nGreen(ng), nBlue(nb), table(table_), deleteTable(false)
+ {
+ if (!table) {
+ table = new Pixel[size()];
+ deleteTable = true;
+ // set a null mapping by default
+ for (int i = 0; i < size(); i++)
+ table[i] = i;
+ }
+ }
+
+ ColourCube() : deleteTable(false) {}
+
+ virtual ~ColourCube() {
+ if (deleteTable) delete [] table;
+ }
+
+ void set(int r, int g, int b, Pixel p) {
+ table[(r * nGreen + g) * nBlue + b] = p;
+ }
+
+ Pixel lookup(int r, int g, int b) const {
+ return table[(r * nGreen + g) * nBlue + b];
+ }
+
+ int size() const { return nRed*nGreen*nBlue; }
+ int redMult() const { return nGreen*nBlue; }
+ int greenMult() const { return nBlue; }
+ int blueMult() const { return 1; }
+
+ // ColourMap lookup() method. Note that this only works when the table has
+ // the default null mapping.
+ virtual void lookup(int i, int* r, int* g, int* b) {
+ if (i >= size()) return;
+ *b = i % nBlue;
+ i /= nBlue;
+ *g = i % nGreen;
+ *r = i / nGreen;
+ *r = (*r * 65535 + (nRed-1) / 2) / (nRed-1);
+ *g = (*g * 65535 + (nGreen-1) / 2) / (nGreen-1);
+ *b = (*b * 65535 + (nBlue-1) / 2) / (nBlue-1);
+ }
+
+ int nRed;
+ int nGreen;
+ int nBlue;
+ Pixel* table;
+ bool deleteTable;
+ };
+}
+#endif
diff --git a/rfb/ColourMap.h b/rfb/ColourMap.h
new file mode 100644
index 00000000..ee7783dc
--- /dev/null
+++ b/rfb/ColourMap.h
@@ -0,0 +1,34 @@
+/* Copyright (C) 2002-2004 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_COLOURMAP_H__
+#define __RFB_COLOURMAP_H__
+namespace rfb {
+ struct Colour {
+ Colour() : r(0), g(0), b(0) {}
+ Colour(int r_, int g_, int b_) : r(r_), g(g_), b(b_) {}
+ int r, g, b;
+ bool operator==(const Colour& c) const {return c.r == r && c.g == g && c.b == b;}
+ bool operator!=(const Colour& c) const {return !(c == *this);}
+ };
+
+ class ColourMap {
+ public:
+ virtual void lookup(int index, int* r, int* g, int* b)=0;
+ };
+}
+#endif
diff --git a/rfb/ComparingUpdateTracker.cxx b/rfb/ComparingUpdateTracker.cxx
new file mode 100644
index 00000000..0c44d85a
--- /dev/null
+++ b/rfb/ComparingUpdateTracker.cxx
@@ -0,0 +1,151 @@
+/* Copyright (C) 2002-2004 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 <stdio.h>
+#include <vector>
+#include <rdr/types.h>
+#include <rfb/Exception.h>
+#include <rfb/ComparingUpdateTracker.h>
+
+using namespace rfb;
+
+ComparingUpdateTracker::ComparingUpdateTracker(PixelBuffer* buffer)
+ : SimpleUpdateTracker(true), fb(buffer),
+ oldFb(fb->getPF(), 0, 0), firstCompare(true)
+{
+ changed.assign_union(fb->getRect());
+}
+
+ComparingUpdateTracker::~ComparingUpdateTracker()
+{
+}
+
+
+void ComparingUpdateTracker::flush_update(UpdateInfo* info,
+ const Region& cliprgn, int maxArea)
+{
+ throw rfb::Exception("flush_update(UpdateInfo*) not implemented");
+}
+
+void ComparingUpdateTracker::flush_update(UpdateTracker &ut,
+ const Region &cliprgn)
+{
+ throw rfb::Exception("flush_update(UpdateTracker&) not implemented");
+}
+
+
+#define BLOCK_SIZE 16
+
+void ComparingUpdateTracker::compare()
+{
+ std::vector<Rect> rects;
+ std::vector<Rect>::iterator i;
+
+ if (firstCompare) {
+ // NB: We leave the change region untouched on this iteration,
+ // since in effect the entire framebuffer has changed.
+ oldFb.setSize(fb->width(), fb->height());
+ for (int y=0; y<fb->height(); y+=BLOCK_SIZE) {
+ Rect pos(0, y, fb->width(), min(fb->height(), y+BLOCK_SIZE));
+ int srcStride;
+ const rdr::U8* srcData = fb->getPixelsR(pos, &srcStride);
+ oldFb.imageRect(pos, srcData, srcStride);
+ }
+ firstCompare = false;
+ } else {
+ copied.get_rects(&rects, copy_delta.x<=0, copy_delta.y<=0);
+ for (i = rects.begin(); i != rects.end(); i++)
+ oldFb.copyRect(*i, copy_delta);
+
+ Region to_check = changed.union_(copied);
+ to_check.get_rects(&rects);
+
+ Region newChanged;
+ for (i = rects.begin(); i != rects.end(); i++)
+ compareRect(*i, &newChanged);
+
+ copied.assign_subtract(newChanged);
+ changed = newChanged;
+ }
+}
+
+void ComparingUpdateTracker::compareRect(const Rect& r, Region* newChanged)
+{
+ if (!r.enclosed_by(fb->getRect())) {
+ fprintf(stderr,"ComparingUpdateTracker: rect outside fb (%d,%d-%d,%d)\n", r.tl.x, r.tl.y, r.br.x, r.br.y);
+ return;
+ }
+
+ int bytesPerPixel = fb->getPF().bpp/8;
+ int oldStride;
+ rdr::U8* oldData = oldFb.getPixelsRW(r, &oldStride);
+ int oldStrideBytes = oldStride * bytesPerPixel;
+
+ std::vector<Rect> changedBlocks;
+
+ for (int blockTop = r.tl.y; blockTop < r.br.y; blockTop += BLOCK_SIZE)
+ {
+ // Get a strip of the source buffer
+ Rect pos(r.tl.x, blockTop, r.br.x, min(r.br.y, blockTop+BLOCK_SIZE));
+ int fbStride;
+ const rdr::U8* newBlockPtr = fb->getPixelsR(pos, &fbStride);
+ int newStrideBytes = fbStride * bytesPerPixel;
+
+ rdr::U8* oldBlockPtr = oldData;
+ int blockBottom = min(blockTop+BLOCK_SIZE, r.br.y);
+
+ for (int blockLeft = r.tl.x; blockLeft < r.br.x; blockLeft += BLOCK_SIZE)
+ {
+ const rdr::U8* newPtr = newBlockPtr;
+ rdr::U8* oldPtr = oldBlockPtr;
+
+ int blockRight = min(blockLeft+BLOCK_SIZE, r.br.x);
+ int blockWidthInBytes = (blockRight-blockLeft) * bytesPerPixel;
+
+ for (int y = blockTop; y < blockBottom; y++)
+ {
+ if (memcmp(oldPtr, newPtr, blockWidthInBytes) != 0)
+ {
+ // A block has changed - copy the remainder to the oldFb
+ changedBlocks.push_back(Rect(blockLeft, blockTop,
+ blockRight, blockBottom));
+ for (int y2 = y; y2 < blockBottom; y2++)
+ {
+ memcpy(oldPtr, newPtr, blockWidthInBytes);
+ newPtr += newStrideBytes;
+ oldPtr += oldStrideBytes;
+ }
+ break;
+ }
+
+ newPtr += newStrideBytes;
+ oldPtr += oldStrideBytes;
+ }
+
+ oldBlockPtr += blockWidthInBytes;
+ newBlockPtr += blockWidthInBytes;
+ }
+
+ oldData += oldStrideBytes * BLOCK_SIZE;
+ }
+
+ if (!changedBlocks.empty()) {
+ Region temp;
+ temp.setOrderedRects(changedBlocks);
+ newChanged->assign_union(temp);
+ }
+}
diff --git a/rfb/ComparingUpdateTracker.h b/rfb/ComparingUpdateTracker.h
new file mode 100644
index 00000000..bec93d1f
--- /dev/null
+++ b/rfb/ComparingUpdateTracker.h
@@ -0,0 +1,47 @@
+/* Copyright (C) 2002-2004 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_COMPARINGUPDATETRACKER_H__
+#define __RFB_COMPARINGUPDATETRACKER_H__
+
+#include <rfb/UpdateTracker.h>
+
+namespace rfb {
+
+ class ComparingUpdateTracker : public SimpleUpdateTracker {
+ public:
+ ComparingUpdateTracker(PixelBuffer* buffer);
+ ~ComparingUpdateTracker();
+
+ // compare() does the comparison and reduces its changed and copied regions
+ // as appropriate.
+
+ virtual void compare();
+
+ virtual void flush_update(UpdateInfo* info, const Region& cliprgn,
+ int maxArea);
+ virtual void flush_update(UpdateTracker &info, const Region &cliprgn);
+ private:
+ void compareRect(const Rect& r, Region* newchanged);
+ PixelBuffer* fb;
+ ManagedPixelBuffer oldFb;
+ bool firstCompare;
+ };
+
+}
+#endif
diff --git a/rfb/Configuration.cxx b/rfb/Configuration.cxx
new file mode 100644
index 00000000..048fb77d
--- /dev/null
+++ b/rfb/Configuration.cxx
@@ -0,0 +1,410 @@
+/* Copyright (C) 2002-2004 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.
+ */
+
+// -=- Configuration.cxx
+
+#include <stdlib.h>
+#include <ctype.h>
+#include <string.h>
+#include <assert.h>
+#ifdef WIN32
+#define strcasecmp _stricmp
+#define strncasecmp _strnicmp
+#endif
+
+#include <rfb/util.h>
+#include <rfb/Configuration.h>
+#include <rfb/LogWriter.h>
+#include <rfb/Exception.h>
+
+#ifdef WIN32
+
+// Under Win32, we use these routines from several threads,
+// so we must provide suitable locking.
+#include <rfb/Threading.h>
+
+static rfb::Mutex configLock;
+
+#endif
+
+#include <rdr/HexOutStream.h>
+#include <rdr/HexInStream.h>
+
+using namespace rfb;
+
+static LogWriter vlog("Config");
+
+
+// -=- Configuration
+
+VoidParameter* Configuration::head = 0;
+
+bool Configuration::setParam(const char* n, const char* v, bool immutable) {
+ return setParam(n, strlen(n), v, immutable);
+}
+
+bool Configuration::setParam(const char* name, int len,
+ const char* val, bool immutable)
+{
+ VoidParameter* current = head;
+ while (current) {
+ if ((int)strlen(current->getName()) == len &&
+ strncasecmp(current->getName(), name, len) == 0)
+ {
+ bool b = current->setParam(val);
+ if (b && immutable)
+ current->setImmutable();
+ return b;
+ }
+ current = current->_next;
+ }
+ return false;
+}
+
+bool Configuration::setParam(const char* config, bool immutable) {
+ bool hyphen = false;
+ if (config[0] == '-') {
+ hyphen = true;
+ config++;
+ if (config[0] == '-') config++; // allow gnu-style --<option>
+ }
+ const char* equal = strchr(config, '=');
+ if (equal) {
+ return setParam(config, equal-config, equal+1, immutable);
+ } else if (hyphen) {
+ VoidParameter* current = head;
+ while (current) {
+ if (strcasecmp(current->getName(), config) == 0) {
+ bool b = current->setParam();
+ if (b && immutable)
+ current->setImmutable();
+ return b;
+ }
+ current = current->_next;
+ }
+ }
+ return false;
+}
+
+VoidParameter* Configuration::getParam(const char* param)
+{
+ VoidParameter* current = head;
+ while (current) {
+ if (strcasecmp(current->getName(), param) == 0)
+ return current;
+ current = current->_next;
+ }
+ return 0;
+}
+
+void Configuration::listParams(int width, int nameWidth) {
+ VoidParameter* current = head;
+ while (current) {
+ char* def_str = current->getDefaultStr();
+ const char* desc = current->getDescription();
+ fprintf(stderr," %-*s -", nameWidth, current->getName());
+ int column = strlen(current->getName());
+ if (column < nameWidth) column = nameWidth;
+ column += 4;
+ while (true) {
+ const char* s = strchr(desc, ' ');
+ int wordLen;
+ if (s) wordLen = s-desc;
+ else wordLen = strlen(desc);
+
+ if (column + wordLen + 1 > width) {
+ fprintf(stderr,"\n%*s",nameWidth+4,"");
+ column = nameWidth+4;
+ }
+ fprintf(stderr," %.*s",wordLen,desc);
+ column += wordLen + 1;
+ desc += wordLen + 1;
+ if (!s) break;
+ }
+
+ if (def_str) {
+ if (column + (int)strlen(def_str) + 11 > width)
+ fprintf(stderr,"\n%*s",nameWidth+4,"");
+ fprintf(stderr," (default=%s)\n",def_str);
+ strFree(def_str);
+ } else {
+ fprintf(stderr,"\n");
+ }
+ current = current->_next;
+ }
+}
+
+// -=- VoidParameter
+
+VoidParameter::VoidParameter(const char* name_, const char* desc_)
+ : immutable(false), name(name_), description(desc_) {
+ _next = Configuration::head;
+ Configuration::head = this;
+}
+
+VoidParameter::~VoidParameter() {
+}
+
+const char*
+VoidParameter::getName() const {
+ return name;
+}
+
+const char*
+VoidParameter::getDescription() const {
+ return description;
+}
+
+bool VoidParameter::setParam() {
+ return false;
+}
+
+bool VoidParameter::isBool() const {
+ return false;
+}
+
+void
+VoidParameter::setImmutable() {
+ vlog.debug("set immutable %s", getName());
+ immutable = true;
+}
+
+// -=- AliasParameter
+
+AliasParameter::AliasParameter(const char* name_, const char* desc_,
+ VoidParameter* param_)
+ : VoidParameter(name_, desc_), param(param_) {
+}
+
+bool
+AliasParameter::setParam(const char* v) {
+ return param->setParam(v);
+}
+
+bool AliasParameter::setParam() {
+ return param->setParam();
+}
+
+char*
+AliasParameter::getDefaultStr() const {
+ return 0;
+}
+
+char* AliasParameter::getValueStr() const {
+ return param->getValueStr();
+}
+
+bool AliasParameter::isBool() const {
+ return param->isBool();
+}
+
+// -=- BoolParameter
+
+BoolParameter::BoolParameter(const char* name_, const char* desc_, bool v)
+: VoidParameter(name_, desc_), value(v), def_value(v) {
+}
+
+bool
+BoolParameter::setParam(const char* v) {
+ if (immutable) return true;
+
+ if (*v == 0 || strcasecmp(v, "1") == 0 || strcasecmp(v, "on") == 0
+ || strcasecmp(v, "true") == 0 || strcasecmp(v, "yes") == 0)
+ value = 1;
+ else if (strcasecmp(v, "0") == 0 || strcasecmp(v, "off") == 0
+ || strcasecmp(v, "false") == 0 || strcasecmp(v, "no") == 0)
+ value = 0;
+ else {
+ vlog.error("Bool parameter %s: invalid value '%s'", getName(), v);
+ return false;
+ }
+
+ vlog.debug("set %s(Bool) to %s(%d)", getName(), v, value);
+ return true;
+}
+
+bool BoolParameter::setParam() {
+ setParam(true);
+ return true;
+}
+
+void BoolParameter::setParam(bool b) {
+ if (immutable) return;
+ value = b;
+ vlog.debug("set %s(Bool) to %d", getName(), value);
+}
+
+char*
+BoolParameter::getDefaultStr() const {
+ char* result = new char[8];
+ sprintf(result, "%d", (int)def_value);
+ return result;
+}
+
+char* BoolParameter::getValueStr() const {
+ char* result = new char[8];
+ sprintf(result, "%d", (int)value);
+ return result;
+}
+
+bool BoolParameter::isBool() const {
+ return true;
+}
+
+BoolParameter::operator bool() const {
+ return value;
+}
+
+// -=- IntParameter
+
+IntParameter::IntParameter(const char* name_, const char* desc_, int v)
+: VoidParameter(name_, desc_), value(v), def_value(v) {
+}
+
+bool
+IntParameter::setParam(const char* v) {
+ if (immutable) return true;
+ vlog.debug("set %s(Int) to %s", getName(), v);
+ value = atoi(v);
+ return true;
+}
+
+bool
+IntParameter::setParam(int v) {
+ if (immutable) return true;
+ vlog.debug("set %s(Int) to %d", getName(), v);
+ value = v;
+ return true;
+}
+
+char*
+IntParameter::getDefaultStr() const {
+ char* result = new char[16];
+ sprintf(result, "%d", def_value);
+ return result;
+}
+
+char* IntParameter::getValueStr() const {
+ char* result = new char[16];
+ sprintf(result, "%d", value);
+ return result;
+}
+
+IntParameter::operator int() const {
+ return value;
+}
+
+// -=- StringParameter
+
+StringParameter::StringParameter(const char* name_, const char* desc_,
+ const char* v)
+ : VoidParameter(name_, desc_), value(strDup(v)), def_value(v)
+{
+ if (!v) {
+ fprintf(stderr,"Default value <null> for %s not allowed\n",name_);
+ throw rfb::Exception("Default value <null> not allowed");
+ }
+}
+
+StringParameter::~StringParameter() {
+ strFree(value);
+}
+
+bool StringParameter::setParam(const char* v) {
+#ifdef WIN32
+ Lock l(configLock);
+#endif
+ if (immutable) return true;
+ if (!v)
+ throw rfb::Exception("setParam(<null>) not allowed");
+ vlog.debug("set %s(String) to %s", getName(), v);
+ strFree(value);
+ value = strDup(v);
+ return value != 0;
+}
+
+char* StringParameter::getDefaultStr() const {
+ return strDup(def_value);
+}
+
+char* StringParameter::getValueStr() const {
+#ifdef WIN32
+ Lock l(configLock);
+#endif
+ return strDup(value);
+}
+
+// -=- BinaryParameter
+
+BinaryParameter::BinaryParameter(const char* name_, const char* desc_, const void* v, int l)
+: VoidParameter(name_, desc_), value(0), length(0), def_value((char*)v), def_length(l) {
+ if (l) {
+ value = new char[l];
+ length = l;
+ memcpy(value, v, l);
+ }
+}
+BinaryParameter::~BinaryParameter() {
+ if (value)
+ delete [] value;
+}
+
+bool BinaryParameter::setParam(const char* v) {
+#ifdef WIN32
+ Lock l(configLock);
+#endif
+ if (immutable) return true;
+ vlog.debug("set %s(Binary) to %s", getName(), v);
+ return rdr::HexInStream::hexStrToBin(v, &value, &length);
+}
+
+void BinaryParameter::setParam(const void* v, int len) {
+#ifdef WIN32
+ Lock l(configLock);
+#endif
+ if (immutable) return;
+ vlog.debug("set %s(Binary)", getName());
+ delete [] value; value = 0;
+ if (len) {
+ value = new char[len];
+ length = len;
+ memcpy(value, v, len);
+ }
+}
+
+char* BinaryParameter::getDefaultStr() const {
+ return rdr::HexOutStream::binToHexStr(def_value, def_length);
+}
+
+char* BinaryParameter::getValueStr() const {
+#ifdef WIN32
+ Lock l(configLock);
+#endif
+ return rdr::HexOutStream::binToHexStr(value, length);
+}
+
+void BinaryParameter::getData(void** data_, int* length_) const {
+#ifdef WIN32
+ Lock l(configLock);
+#endif
+ if (length_) *length_ = length;
+ if (data_) {
+ *data_ = new char[length];
+ memcpy(*data_, value, length);
+ }
+}
diff --git a/rfb/Configuration.h b/rfb/Configuration.h
new file mode 100644
index 00000000..1b37ac94
--- /dev/null
+++ b/rfb/Configuration.h
@@ -0,0 +1,167 @@
+/* Copyright (C) 2002-2004 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.
+ */
+
+// -=- Configuration.h
+//
+// This header defines a set of classes used to represent configuration
+// parameters of different types. Instances of the different parameter
+// types are associated with instances of the Configuration class, and
+// are each given a unique name. The Configuration class provides a
+// generic API through which parameters may be located by name and their
+// value set, thus removing the need to write platform-specific code.
+// Simply defining a new parameter and associating it with a Configuration
+// will allow it to be configured by the user.
+//
+// The Configuration class is used to allow multiple distinct configurations
+// to co-exist at the same time. A process serving several desktops, for
+// instance, can create a Configuration instance for each, to allow them
+// to be configured independently, from the command-line, registry, etc.
+
+#ifndef __RFB_CONFIGURATION_H__
+#define __RFB_CONFIGURATION_H__
+
+namespace rfb {
+ class VoidParameter;
+
+ // -=- Configuration
+ // Class used to access parameters.
+
+ class Configuration {
+ public:
+ // - Set named parameter to value
+ static bool setParam(const char* param, const char* value, bool immutable=false);
+
+ // - Set parameter to value (separated by "=")
+ static bool setParam(const char* config, bool immutable=false);
+
+ // - Set named parameter to value, with name truncated at len
+ static bool setParam(const char* name, int len,
+ const char* val, bool immutable);
+
+ // - Get named parameter
+ static VoidParameter* getParam(const char* param);
+
+ static void listParams(int width=79, int nameWidth=10);
+
+ static VoidParameter* head;
+ };
+
+ // -=- VoidParameter
+ // Configuration parameter base-class.
+
+ class VoidParameter {
+ public:
+ VoidParameter(const char* name_, const char* desc_);
+ virtual ~VoidParameter();
+ const char* getName() const;
+ const char* getDescription() const;
+
+ virtual bool setParam(const char* value) = 0;
+ virtual bool setParam();
+ virtual char* getDefaultStr() const = 0;
+ virtual char* getValueStr() const = 0;
+ virtual bool isBool() const;
+
+ virtual void setImmutable();
+
+ VoidParameter* _next;
+ protected:
+ bool immutable;
+ const char* name;
+ const char* description;
+ };
+
+ class AliasParameter : public VoidParameter {
+ public:
+ AliasParameter(const char* name_, const char* desc_,VoidParameter* param_);
+ virtual bool setParam(const char* value);
+ virtual bool setParam();
+ virtual char* getDefaultStr() const;
+ virtual char* getValueStr() const;
+ virtual bool isBool() const;
+ private:
+ VoidParameter* param;
+ };
+
+ class BoolParameter : public VoidParameter {
+ public:
+ BoolParameter(const char* name_, const char* desc_, bool v);
+ virtual bool setParam(const char* value);
+ virtual bool setParam();
+ virtual void setParam(bool b);
+ virtual char* getDefaultStr() const;
+ virtual char* getValueStr() const;
+ virtual bool isBool() const;
+ operator bool() const;
+ protected:
+ bool value;
+ bool def_value;
+ };
+
+ class IntParameter : public VoidParameter {
+ public:
+ IntParameter(const char* name_, const char* desc_, int v);
+ virtual bool setParam(const char* value);
+ virtual bool setParam(int v);
+ virtual char* getDefaultStr() const;
+ virtual char* getValueStr() const;
+ operator int() const;
+ protected:
+ int value;
+ int def_value;
+ };
+
+ class StringParameter : public VoidParameter {
+ public:
+ // StringParameter contains a null-terminated string, which CANNOT
+ // be Null, and so neither can the default value!
+ StringParameter(const char* name_, const char* desc_, const char* v);
+ virtual ~StringParameter();
+ virtual bool setParam(const char* value);
+ virtual char* getDefaultStr() const;
+ virtual char* getValueStr() const;
+
+ // getData() returns a copy of the data - it must be delete[]d by the
+ // caller.
+ char* getData() const { return getValueStr(); }
+ protected:
+ char* value;
+ const char* def_value;
+ };
+
+ class BinaryParameter : public VoidParameter {
+ public:
+ BinaryParameter(const char* name_, const char* desc_, const void* v, int l);
+ virtual ~BinaryParameter();
+ virtual bool setParam(const char* value);
+ virtual void setParam(const void* v, int l);
+ virtual char* getDefaultStr() const;
+ virtual char* getValueStr() const;
+
+ void getData(void** data, int* length) const;
+
+ protected:
+ char* value;
+ int length;
+ char* def_value;
+ int def_length;
+ };
+
+};
+
+#endif // __RFB_CONFIGURATION_H__
diff --git a/rfb/ConnParams.cxx b/rfb/ConnParams.cxx
new file mode 100644
index 00000000..95529405
--- /dev/null
+++ b/rfb/ConnParams.cxx
@@ -0,0 +1,104 @@
+/* Copyright (C) 2002-2003 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 <rdr/InStream.h>
+#include <rdr/OutStream.h>
+#include <rfb/Exception.h>
+#include <rfb/encodings.h>
+#include <rfb/Encoder.h>
+#include <rfb/ConnParams.h>
+#include <rfb/util.h>
+
+using namespace rfb;
+
+ConnParams::ConnParams()
+ : majorVersion(0), minorVersion(0), width(0), height(0), useCopyRect(false),
+ supportsLocalCursor(false), supportsDesktopResize(false),
+ name_(0), nEncodings_(0), encodings_(0),
+ currentEncoding_(encodingRaw), verStrPos(0)
+{
+ setName("");
+}
+
+ConnParams::~ConnParams()
+{
+ delete [] name_;
+ delete [] encodings_;
+}
+
+bool ConnParams::readVersion(rdr::InStream* is, bool* done)
+{
+ if (verStrPos >= 12) return false;
+ while (is->checkNoWait(1) && verStrPos < 12) {
+ verStr[verStrPos++] = is->readU8();
+ }
+
+ if (verStrPos < 12) {
+ *done = false;
+ return true;
+ }
+ *done = true;
+ verStr[12] = 0;
+ return (sscanf(verStr, "RFB %03d.%03d\n", &majorVersion,&minorVersion) == 2);
+}
+
+void ConnParams::writeVersion(rdr::OutStream* os)
+{
+ char str[13];
+ sprintf(str, "RFB %03d.%03d\n", majorVersion, minorVersion);
+ os->writeBytes(str, 12);
+ os->flush();
+}
+
+void ConnParams::setPF(const PixelFormat& pf)
+{
+ pf_ = pf;
+
+ if (pf.bpp != 8 && pf.bpp != 16 && pf.bpp != 32)
+ throw Exception("setPF: not 8, 16 or 32 bpp?");
+}
+
+void ConnParams::setName(const char* name)
+{
+ delete [] name_;
+ name_ = strDup(name);
+}
+
+void ConnParams::setEncodings(int nEncodings, const rdr::U32* encodings)
+{
+ if (nEncodings > nEncodings_) {
+ delete [] encodings_;
+ encodings_ = new rdr::U32[nEncodings];
+ }
+ nEncodings_ = nEncodings;
+ useCopyRect = false;
+ supportsLocalCursor = false;
+ currentEncoding_ = encodingRaw;
+
+ for (int i = nEncodings-1; i >= 0; i--) {
+ encodings_[i] = encodings[i];
+
+ if (encodings[i] == encodingCopyRect)
+ useCopyRect = true;
+ else if (encodings[i] == pseudoEncodingCursor)
+ supportsLocalCursor = true;
+ else if (encodings[i] == pseudoEncodingDesktopSize)
+ supportsDesktopResize = true;
+ else if (encodings[i] <= encodingMax && Encoder::supported(encodings[i]))
+ currentEncoding_ = encodings[i];
+ }
+}
diff --git a/rfb/ConnParams.h b/rfb/ConnParams.h
new file mode 100644
index 00000000..fb96a7a6
--- /dev/null
+++ b/rfb/ConnParams.h
@@ -0,0 +1,86 @@
+/* Copyright (C) 2002-2003 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.
+ */
+//
+// ConnParams - structure containing the connection parameters.
+//
+
+#ifndef __RFB_CONNPARAMS_H__
+#define __RFB_CONNPARAMS_H__
+
+#include <rdr/types.h>
+#include <rfb/PixelFormat.h>
+
+namespace rdr { class InStream; }
+
+namespace rfb {
+
+ class ConnParams {
+ public:
+ ConnParams();
+ ~ConnParams();
+
+ bool readVersion(rdr::InStream* is, bool* done);
+ void writeVersion(rdr::OutStream* os);
+
+ int majorVersion;
+ int minorVersion;
+
+ void setVersion(int major, int minor) {
+ majorVersion = major; minorVersion = minor;
+ }
+ bool isVersion(int major, int minor) {
+ return majorVersion == major && minorVersion == minor;
+ }
+ bool beforeVersion(int major, int minor) {
+ return (majorVersion < major ||
+ (majorVersion == major && minorVersion < minor));
+ }
+ bool afterVersion(int major, int minor) {
+ return !beforeVersion(major,minor+1);
+ }
+
+ int width;
+ int height;
+
+ const PixelFormat& pf() { return pf_; }
+ void setPF(const PixelFormat& pf);
+
+ const char* name() { return name_; }
+ void setName(const char* name);
+
+ rdr::U32 currentEncoding() { return currentEncoding_; }
+ int nEncodings() { return nEncodings_; }
+ const rdr::U32* encodings() { return encodings_; }
+ void setEncodings(int nEncodings, const rdr::U32* encodings);
+ bool useCopyRect;
+
+ bool supportsLocalCursor;
+ bool supportsDesktopResize;
+
+ private:
+
+ PixelFormat pf_;
+ char* name_;
+ int nEncodings_;
+ rdr::U32* encodings_;
+ int currentEncoding_;
+ char verStr[13];
+ int verStrPos;
+ };
+}
+#endif
diff --git a/rfb/Cursor.cxx b/rfb/Cursor.cxx
new file mode 100644
index 00000000..b50d9255
--- /dev/null
+++ b/rfb/Cursor.cxx
@@ -0,0 +1,178 @@
+/* Copyright (C) 2002-2004 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 <string.h>
+#include <rfb/Cursor.h>
+#include <rfb/LogWriter.h>
+
+using namespace rfb;
+
+static LogWriter vlog("Cursor");
+
+void Cursor::setSize(int w, int h) {
+ int oldMaskLen = maskLen();
+ ManagedPixelBuffer::setSize(w, h);
+ if (maskLen() > oldMaskLen) {
+ delete [] mask.buf;
+ mask.buf = new rdr::U8[maskLen()];
+ }
+}
+
+void Cursor::drawOutline(const Pixel& c)
+{
+ Cursor outlined;
+
+ // Create a mirror of the existing cursor
+ outlined.setPF(getPF());
+ outlined.setSize(width(), height());
+ outlined.hotspot = hotspot;
+
+ // Clear the mirror's background to the outline colour
+ outlined.fillRect(getRect(), c);
+
+ // Blit the existing cursor, using its mask
+ outlined.maskRect(getRect(), data, mask.buf);
+
+ // Now just adjust the mask to add the outline. The outline pixels
+ // will already be the right colour. :)
+ int maskBytesPerRow = (width() + 7) / 8;
+ for (int y = 0; y < height(); y++) {
+ for (int byte=0; byte<maskBytesPerRow; byte++) {
+ rdr::U8 m8 = mask.buf[y*maskBytesPerRow + byte];
+
+ // Handle above & below outline
+ if (y > 0) m8 |= mask.buf[(y-1)*maskBytesPerRow + byte];
+ if (y < height()-1) m8 |= mask.buf[(y+1)*maskBytesPerRow + byte];
+
+ // Left outline
+ m8 |= mask.buf[y*maskBytesPerRow + byte] << 1;
+ if (byte < maskBytesPerRow-1)
+ m8 |= (mask.buf[y*maskBytesPerRow + byte + 1] >> 7) & 1;
+
+ // Right outline
+ m8 |= mask.buf[y*maskBytesPerRow + byte] >> 1;
+ if (byte > 0)
+ m8 |= (mask.buf[y*maskBytesPerRow + byte - 1] << 7) & 128;
+
+ outlined.mask.buf[y*maskBytesPerRow + byte] = m8;
+ }
+ }
+
+ // Replace the existing cursor & mask with the new one
+ delete [] data;
+ delete [] mask.buf;
+ data = outlined.data; outlined.data = 0;
+ mask.buf = outlined.mask.buf; outlined.mask.buf = 0;
+}
+
+rdr::U8* Cursor::getBitmap(Pixel* pix0, Pixel* pix1)
+{
+ bool gotPix0 = false;
+ bool gotPix1 = false;
+ rdr::U8Array source(maskLen());
+ memset(source.buf, 0, maskLen());
+
+ int maskBytesPerRow = (width() + 7) / 8;
+ for (int y = 0; y < height(); y++) {
+ for (int x = 0; x < width(); x++) {
+ int byte = y * maskBytesPerRow + x / 8;
+ int bit = 7 - x % 8;
+ if (mask.buf[byte] & (1 << bit)) {
+ Pixel pix=0;
+ switch (getPF().bpp) {
+ case 8: pix = ((rdr::U8*) data)[y * width() + x]; break;
+ case 16: pix = ((rdr::U16*)data)[y * width() + x]; break;
+ case 32: pix = ((rdr::U32*)data)[y * width() + x]; break;
+ }
+ if (!gotPix0 || pix == *pix0) {
+ gotPix0 = true;
+ *pix0 = pix;
+ } else if (!gotPix1 || pix == *pix1) {
+ gotPix1 = true;
+ *pix1 = pix;
+ source.buf[byte] |= (1 << bit);
+ } else {
+ // not a bitmap
+ return 0;
+ }
+ }
+ }
+ }
+ return source.takeBuf();
+}
+
+// crop() determines the "busy" rectangle for the cursor - the minimum bounding
+// rectangle containing actual pixels. This isn't the most efficient algorithm
+// but it's short. For sanity, we make sure that the busy rectangle always
+// includes the hotspot (the hotspot is unsigned on the wire so otherwise it
+// would cause problems if it was above or left of the actual pixels)
+
+void Cursor::crop()
+{
+ Rect busy = getRect().intersect(Rect(hotspot.x, hotspot.y,
+ hotspot.x+1, hotspot.y+1));
+ int maskBytesPerRow = (width() + 7) / 8;
+ int x, y;
+ for (y = 0; y < height(); y++) {
+ for (x = 0; x < width(); x++) {
+ int byte = y * maskBytesPerRow + x / 8;
+ int bit = 7 - x % 8;
+ if (mask.buf[byte] & (1 << bit)) {
+ if (x < busy.tl.x) busy.tl.x = x;
+ if (x+1 > busy.br.x) busy.br.x = x+1;
+ if (y < busy.tl.y) busy.tl.y = y;
+ if (y+1 > busy.br.y) busy.br.y = y+1;
+ }
+ }
+ }
+
+ if (width() == busy.width() && height() == busy.height()) return;
+
+ vlog.debug("cropping %dx%d to %dx%d", width(), height(),
+ busy.width(), busy.height());
+
+ // Copy the pixel data
+ int newDataLen = busy.area() * (getPF().bpp/8);
+ rdr::U8* newData = new rdr::U8[newDataLen];
+ getImage(newData, busy);
+
+ // Copy the mask
+ int newMaskBytesPerRow = (busy.width()+7)/8;
+ int newMaskLen = newMaskBytesPerRow * busy.height();
+ rdr::U8* newMask = new rdr::U8[newMaskLen];
+ memset(newMask, 0, newMaskLen);
+ for (y = 0; y < busy.height(); y++) {
+ int newByte, newBit;
+ for (x = 0; x < busy.width(); x++) {
+ int oldByte = (y+busy.tl.y) * maskBytesPerRow + (x+busy.tl.x) / 8;
+ int oldBit = 7 - (x+busy.tl.x) % 8;
+ newByte = y * newMaskBytesPerRow + x / 8;
+ newBit = 7 - x % 8;
+ if (mask.buf[oldByte] & (1 << oldBit))
+ newMask[newByte] |= (1 << newBit);
+ }
+ }
+
+ // Set the size and data to the new, cropped cursor.
+ setSize(busy.width(), busy.height());
+ hotspot = hotspot.subtract(busy.tl);
+ delete [] data;
+ delete [] mask.buf;
+ datasize = newDataLen;
+ data = newData;
+ mask.buf = newMask;
+}
diff --git a/rfb/Cursor.h b/rfb/Cursor.h
new file mode 100644
index 00000000..0f187750
--- /dev/null
+++ b/rfb/Cursor.h
@@ -0,0 +1,56 @@
+/* Copyright (C) 2002-2003 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.
+ */
+//
+// Cursor - structure containing information describing
+// the current cursor shape
+//
+
+#ifndef __RFB_CURSOR_H__
+#define __RFB_CURSOR_H__
+
+#include <rfb/PixelBuffer.h>
+
+namespace rfb {
+
+ class Cursor : public ManagedPixelBuffer {
+ public:
+ Cursor() {}
+ rdr::U8Array mask;
+ Point hotspot;
+
+ int maskLen() { return (width() + 7) / 8 * height(); }
+
+ // setSize() resizes the cursor. The contents of the data and mask are
+ // undefined after this call.
+ virtual void setSize(int w, int h);
+
+ // drawOutline() adds an outline to the cursor in the given colour.
+ void drawOutline(const Pixel& c);
+
+ // getBitmap() tests whether the cursor is monochrome, and if so returns a
+ // bitmap together with background and foreground colours. The size and
+ // layout of the bitmap are the same as the mask.
+ rdr::U8* getBitmap(Pixel* pix0, Pixel* pix1);
+
+ // crop() crops the cursor down to the smallest possible size, based on the
+ // mask.
+ void crop();
+ };
+
+}
+#endif
diff --git a/rfb/Decoder.cxx b/rfb/Decoder.cxx
new file mode 100644
index 00000000..816cb6e4
--- /dev/null
+++ b/rfb/Decoder.cxx
@@ -0,0 +1,68 @@
+/* Copyright (C) 2002-2003 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 <stdio.h>
+#include <rfb/Exception.h>
+#include <rfb/Decoder.h>
+#include <rfb/RawDecoder.h>
+#include <rfb/RREDecoder.h>
+#include <rfb/HextileDecoder.h>
+#include <rfb/ZRLEDecoder.h>
+
+using namespace rfb;
+
+Decoder::~Decoder()
+{
+}
+
+DecoderCreateFnType Decoder::createFns[encodingMax+1] = { 0 };
+
+bool Decoder::supported(unsigned int encoding)
+{
+ return encoding <= encodingMax && createFns[encoding];
+}
+
+Decoder* Decoder::createDecoder(unsigned int encoding, CMsgReader* reader)
+{
+ if (encoding <= encodingMax && createFns[encoding])
+ return (*createFns[encoding])(reader);
+ return 0;
+}
+
+void Decoder::registerDecoder(unsigned int encoding,
+ DecoderCreateFnType createFn)
+{
+ if (encoding > encodingMax)
+ throw Exception("Decoder::registerDecoder: encoding out of range");
+
+ if (createFns[encoding])
+ fprintf(stderr,"Replacing existing decoder for encoding %s (%d)\n",
+ encodingName(encoding), encoding);
+ createFns[encoding] = createFn;
+}
+
+int DecoderInit::count = 0;
+
+DecoderInit::DecoderInit()
+{
+ if (count++ != 0) return;
+
+ Decoder::registerDecoder(encodingRaw, RawDecoder::create);
+ Decoder::registerDecoder(encodingRRE, RREDecoder::create);
+ Decoder::registerDecoder(encodingHextile, HextileDecoder::create);
+ Decoder::registerDecoder(encodingZRLE, ZRLEDecoder::create);
+}
diff --git a/rfb/Decoder.h b/rfb/Decoder.h
new file mode 100644
index 00000000..914b26a9
--- /dev/null
+++ b/rfb/Decoder.h
@@ -0,0 +1,52 @@
+/* Copyright (C) 2002-2003 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_DECODER_H__
+#define __RFB_DECODER_H__
+
+#include <rfb/Rect.h>
+#include <rfb/encodings.h>
+
+namespace rfb {
+ class CMsgReader;
+ class CMsgHandler;
+ class Decoder;
+ typedef Decoder* (*DecoderCreateFnType)(CMsgReader*);
+
+ class Decoder {
+ public:
+ virtual ~Decoder();
+ virtual void readRect(const Rect& r, CMsgHandler* handler)=0;
+
+ static bool supported(unsigned int encoding);
+ static Decoder* createDecoder(unsigned int encoding, CMsgReader* reader);
+ static void registerDecoder(unsigned int encoding,
+ DecoderCreateFnType createFn);
+ private:
+ static DecoderCreateFnType createFns[encodingMax+1];
+ };
+
+ class DecoderInit {
+ static int count;
+ public:
+ DecoderInit();
+ };
+
+ static DecoderInit decoderInitObj;
+}
+
+#endif
diff --git a/rfb/Encoder.cxx b/rfb/Encoder.cxx
new file mode 100644
index 00000000..f2d6d3d2
--- /dev/null
+++ b/rfb/Encoder.cxx
@@ -0,0 +1,75 @@
+/* Copyright (C) 2002-2004 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 <stdio.h>
+#include <rfb/Exception.h>
+#include <rfb/Encoder.h>
+#include <rfb/RawEncoder.h>
+#include <rfb/RREEncoder.h>
+#include <rfb/HextileEncoder.h>
+#include <rfb/ZRLEEncoder.h>
+
+using namespace rfb;
+
+Encoder::~Encoder()
+{
+}
+
+EncoderCreateFnType Encoder::createFns[encodingMax+1] = { 0 };
+
+bool Encoder::supported(unsigned int encoding)
+{
+ return encoding <= encodingMax && createFns[encoding];
+}
+
+Encoder* Encoder::createEncoder(unsigned int encoding, SMsgWriter* writer)
+{
+ if (encoding <= encodingMax && createFns[encoding])
+ return (*createFns[encoding])(writer);
+ return 0;
+}
+
+void Encoder::registerEncoder(unsigned int encoding,
+ EncoderCreateFnType createFn)
+{
+ if (encoding > encodingMax)
+ throw Exception("Encoder::registerEncoder: encoding out of range");
+
+ if (createFns[encoding])
+ fprintf(stderr,"Replacing existing encoder for encoding %s (%d)\n",
+ encodingName(encoding), encoding);
+ createFns[encoding] = createFn;
+}
+
+void Encoder::unregisterEncoder(unsigned int encoding)
+{
+ if (encoding > encodingMax)
+ throw Exception("Encoder::unregisterEncoder: encoding out of range");
+ createFns[encoding] = 0;
+}
+
+int EncoderInit::count = 0;
+
+EncoderInit::EncoderInit()
+{
+ if (count++ != 0) return;
+
+ Encoder::registerEncoder(encodingRaw, RawEncoder::create);
+ Encoder::registerEncoder(encodingRRE, RREEncoder::create);
+ Encoder::registerEncoder(encodingHextile, HextileEncoder::create);
+ Encoder::registerEncoder(encodingZRLE, ZRLEEncoder::create);
+}
diff --git a/rfb/Encoder.h b/rfb/Encoder.h
new file mode 100644
index 00000000..7795d90b
--- /dev/null
+++ b/rfb/Encoder.h
@@ -0,0 +1,57 @@
+/* Copyright (C) 2002-2004 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_ENCODER_H__
+#define __RFB_ENCODER_H__
+
+#include <rfb/Rect.h>
+#include <rfb/encodings.h>
+
+namespace rfb {
+ class SMsgWriter;
+ class Encoder;
+ class ImageGetter;
+ typedef Encoder* (*EncoderCreateFnType)(SMsgWriter*);
+
+ class Encoder {
+ public:
+ virtual ~Encoder();
+
+ // writeRect() tries to write the given rectangle. If it is unable to
+ // write the whole rectangle it returns false and sets actual to the actual
+ // rectangle which was updated.
+ virtual bool writeRect(const Rect& r, ImageGetter* ig, Rect* actual)=0;
+
+ static bool supported(unsigned int encoding);
+ static Encoder* createEncoder(unsigned int encoding, SMsgWriter* writer);
+ static void registerEncoder(unsigned int encoding,
+ EncoderCreateFnType createFn);
+ static void unregisterEncoder(unsigned int encoding);
+ private:
+ static EncoderCreateFnType createFns[encodingMax+1];
+ };
+
+ class EncoderInit {
+ static int count;
+ public:
+ EncoderInit();
+ };
+
+ static EncoderInit encoderInitObj;
+}
+
+#endif
diff --git a/rfb/Exception.h b/rfb/Exception.h
new file mode 100644
index 00000000..aa98271b
--- /dev/null
+++ b/rfb/Exception.h
@@ -0,0 +1,37 @@
+/* Copyright (C) 2002-2003 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_EXCEPTION_H__
+#define __RFB_EXCEPTION_H__
+
+#include <rdr/Exception.h>
+
+namespace rfb {
+ struct Exception : public rdr::Exception {
+ Exception(const char* s=0, const char* e="rfb::Exception")
+ : rdr::Exception(s,e) {}
+ };
+ struct AuthFailureException : public Exception {
+ AuthFailureException(const char* s="Authentication failure")
+ : Exception(s,"rfb::AuthFailureException") {}
+ };
+ struct ConnFailedException : public Exception {
+ ConnFailedException(const char* s="Connection failed")
+ : Exception(s,"rfb::ConnFailedException") {}
+ };
+}
+#endif
diff --git a/rfb/HTTPServer.cxx b/rfb/HTTPServer.cxx
new file mode 100644
index 00000000..55e6909e
--- /dev/null
+++ b/rfb/HTTPServer.cxx
@@ -0,0 +1,386 @@
+/* Copyright (C) 2002-2004 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 <rfb/HTTPServer.h>
+#include <rfb/LogWriter.h>
+#include <rfb/util.h>
+#include <rdr/MemOutStream.h>
+#include <time.h>
+
+// *** Shouldn't really link against this - only for ClientWaitTimeMillis
+// and IdleTimeout
+#include <rfb/ServerCore.h>
+
+#ifdef WIN32
+#define strcasecmp _stricmp
+#endif
+
+
+using namespace rfb;
+using namespace rdr;
+
+static LogWriter vlog("HTTPServer");
+
+
+//
+// -=- LineReader
+// Helper class which is repeatedly called until a line has been read
+// (lines end in \n or \r\n).
+// Returns true when line complete, and resets internal state so that
+// next read() call will start reading a new line.
+// Only one buffer is kept - process line before reading next line!
+//
+
+class LineReader : public CharArray {
+public:
+ LineReader(InStream& is_, int l)
+ : CharArray(l), is(is_), pos(0), len(l), bufferOverrun(false) {}
+
+ // Returns true if line complete, false otherwise
+ bool read() {
+ while (is.checkNoWait(1)) {
+ char c = is.readU8();
+
+ if (c == '\n') {
+ if (pos && (buf[pos-1] == '\r'))
+ pos--;
+ bufferOverrun = false;
+ buf[pos++] = 0;
+ pos = 0;
+ return true;
+ }
+
+ if (pos == (len-1)) {
+ bufferOverrun = true;
+ buf[pos] = 0;
+ return true;
+ }
+
+ buf[pos++] = c;
+ }
+
+ return false;
+ }
+ bool didBufferOverrun() const {return bufferOverrun;}
+protected:
+ InStream& is;
+ int pos, len;
+ bool bufferOverrun;
+};
+
+
+//
+// -=- HTTPServer::Session
+// Manages the internal state for an HTTP session.
+// processHTTP returns true when request has completed,
+// indicating that socket & session data can be deleted.
+//
+
+class rfb::HTTPServer::Session {
+public:
+ Session(network::Socket& s, rfb::HTTPServer& srv)
+ : contentType(0), line(s.inStream(), 256), sock(s),
+ server(srv), state(ReadRequestLine), lastActive(time(0)) {
+ }
+ ~Session() {
+ }
+
+ void writeResponse(int result, const char* text);
+ bool writeResponse(int code);
+
+ bool processHTTP();
+
+ network::Socket* getSock() const {return &sock;}
+
+ int checkIdleTimeout();
+protected:
+ CharArray uri;
+ const char* contentType;
+ LineReader line;
+ network::Socket& sock;
+ rfb::HTTPServer& server;
+ enum {ReadRequestLine, ReadHeaders, WriteResponse} state;
+ enum {GetRequest, HeadRequest} request;
+ time_t lastActive;
+};
+
+
+// - Internal helper routines
+
+void
+copyStream(InStream& is, OutStream& os) {
+ try {
+ while (1) {
+ os.writeU8(is.readU8());
+ }
+ } catch (rdr::EndOfStream) {
+ }
+}
+
+void writeLine(OutStream& os, const char* text) {
+ os.writeBytes(text, strlen(text));
+ os.writeBytes("\r\n", 2);
+}
+
+
+// - Write an HTTP-compliant response to the client
+
+void
+HTTPServer::Session::writeResponse(int result, const char* text) {
+ char buffer[1024];
+ if (strlen(text) > 512)
+ throw new rdr::Exception("Internal error - HTTP response text too big");
+ sprintf(buffer, "%s %d %s", "HTTP/1.1", result, text);
+ OutStream& os=sock.outStream();
+ writeLine(os, buffer);
+ writeLine(os, "Server: RealVNC/4.0");
+ writeLine(os, "Connection: close");
+ os.writeBytes("Content-Type: ", 14);
+ if (result == 200) {
+ if (!contentType)
+ contentType = guessContentType(uri.buf, "text/html");
+ os.writeBytes(contentType, strlen(contentType));
+ } else {
+ os.writeBytes("text/html", 9);
+ }
+ os.writeBytes("\r\n", 2);
+ writeLine(os, "");
+ if (result != 200) {
+ writeLine(os, "<!DOCTYPE HTML PUBLIC \"-//IETF//DTD HTML 2.0//EN\">");
+ writeLine(os, "<HTML><HEAD>");
+ sprintf(buffer, "<TITLE>%d %s</TITLE>", result, text);
+ writeLine(os, buffer);
+ writeLine(os, "</HEAD><BODY><H1>");
+ writeLine(os, text);
+ writeLine(os, "</H1></BODY></HTML>");
+ sock.outStream().flush();
+ }
+}
+
+bool
+HTTPServer::Session::writeResponse(int code) {
+ switch (code) {
+ case 200: writeResponse(code, "OK"); break;
+ case 400: writeResponse(code, "Bad Request"); break;
+ case 404: writeResponse(code, "Not Found"); break;
+ case 501: writeResponse(code, "Not Implemented"); break;
+ default: writeResponse(500, "Unknown Error"); break;
+ };
+
+ // This return code is passed straight out of processHTTP().
+ // true indicates that the request has been completely processed.
+ return true;
+}
+
+// - Main HTTP request processing routine
+
+bool
+HTTPServer::Session::processHTTP() {
+ lastActive = time(0);
+
+ while (sock.inStream().checkNoWait(1)) {
+
+ switch (state) {
+
+ // Reading the Request-Line
+ case ReadRequestLine:
+
+ // Either read a line, or run out of incoming data
+ if (!line.read())
+ return false;
+
+ // We have read a line! Skip it if it's blank
+ if (strlen(line.buf) == 0)
+ continue;
+
+ // The line contains a request to process.
+ {
+ char method[16], path[128], version[16];
+ int matched = sscanf(line.buf, "%15s%127s%15s",
+ method, path, version);
+ if (matched != 3)
+ return writeResponse(400);
+
+ // Store the required "method"
+ if (strcmp(method, "GET") == 0)
+ request = GetRequest;
+ else if (strcmp(method, "HEAD") == 0)
+ request = HeadRequest;
+ else
+ return writeResponse(501);
+
+ // Store the URI to the "document"
+ uri.buf = strDup(path);
+ }
+
+ // Move on to reading the request headers
+ state = ReadHeaders;
+ break;
+
+ // Reading the request headers
+ case ReadHeaders:
+
+ // Try to read a line
+ if (!line.read())
+ return false;
+
+ // Skip headers until we hit a blank line
+ if (strlen(line.buf) != 0)
+ continue;
+
+ // Headers ended - write the response!
+ {
+ CharArray address(sock.getPeerAddress());
+ vlog.info("getting %s for %s", uri.buf, address.buf);
+ InStream* data = server.getFile(uri.buf, &contentType);
+ if (!data)
+ return writeResponse(404);
+
+ try {
+ writeResponse(200);
+ if (request == GetRequest)
+ copyStream(*data, sock.outStream());
+ sock.outStream().flush();
+ } catch (rdr::Exception& e) {
+ vlog.error("error writing HTTP document:%s", e.str());
+ }
+ delete data;
+ }
+
+ // The operation is complete!
+ return true;
+
+ default:
+ throw rdr::Exception("invalid HTTPSession state!");
+ };
+
+ }
+
+ // Indicate that we're still processing the HTTP request.
+ return false;
+}
+
+int HTTPServer::Session::checkIdleTimeout() {
+ time_t now = time(0);
+ int timeout = (lastActive + rfb::Server::idleTimeout) - now;
+ if (timeout > 0)
+ return timeout * 1000;
+ sock.shutdown();
+ return 0;
+}
+
+// -=- Constructor / destructor
+
+HTTPServer::HTTPServer() {
+}
+
+HTTPServer::~HTTPServer() {
+ std::list<Session*>::iterator i;
+ for (i=sessions.begin(); i!=sessions.end(); i++) {
+ delete (*i)->getSock();
+ delete *i;
+ }
+}
+
+
+// -=- SocketServer interface implementation
+
+void
+HTTPServer::addClient(network::Socket* sock) {
+ Session* s = new Session(*sock, *this);
+ if (!s) {
+ sock->shutdown();
+ } else {
+ sock->inStream().setTimeout(rfb::Server::clientWaitTimeMillis);
+ sock->outStream().setTimeout(rfb::Server::clientWaitTimeMillis);
+ sessions.push_front(s);
+ }
+}
+
+bool
+HTTPServer::processSocketEvent(network::Socket* sock) {
+ std::list<Session*>::iterator i;
+ for (i=sessions.begin(); i!=sessions.end(); i++) {
+ if ((*i)->getSock() == sock) {
+ try {
+ if ((*i)->processHTTP()) {
+ vlog.info("completed HTTP request");
+ delete *i;
+ sessions.erase(i);
+ break;
+ }
+ return true;
+ } catch (rdr::Exception& e) {
+ vlog.error("untrapped: %s", e.str());
+ delete *i;
+ sessions.erase(i);
+ break;
+ }
+ }
+ }
+ delete sock;
+ return false;
+}
+
+void HTTPServer::getSockets(std::list<network::Socket*>* sockets)
+{
+ sockets->clear();
+ std::list<Session*>::iterator ci;
+ for (ci = sessions.begin(); ci != sessions.end(); ci++) {
+ sockets->push_back((*ci)->getSock());
+ }
+}
+
+int HTTPServer::checkTimeouts() {
+ std::list<Session*>::iterator ci;
+ int timeout = 0;
+ for (ci = sessions.begin(); ci != sessions.end(); ci++) {
+ soonestTimeout(&timeout, (*ci)->checkIdleTimeout());
+ }
+ return timeout;
+}
+
+
+// -=- Default getFile implementation
+
+InStream*
+HTTPServer::getFile(const char* name, const char** contentType) {
+ return 0;
+}
+
+const char*
+HTTPServer::guessContentType(const char* name, const char* defType) {
+ CharArray file, ext;
+ if (!strSplit(name, '.', &file.buf, &ext.buf))
+ return defType;
+ if (strcasecmp(ext.buf, "html") == 0 ||
+ strcasecmp(ext.buf, "htm") == 0) {
+ return "text/html";
+ } else if (strcasecmp(ext.buf, "txt") == 0) {
+ return "text/plain";
+ } else if (strcasecmp(ext.buf, "gif") == 0) {
+ return "image/gif";
+ } else if (strcasecmp(ext.buf, "jpg") == 0) {
+ return "image/jpeg";
+ } else if (strcasecmp(ext.buf, "jar") == 0) {
+ return "application/java-archive";
+ } else if (strcasecmp(ext.buf, "exe") == 0) {
+ return "application/octet-stream";
+ }
+ return defType;
+}
diff --git a/rfb/HTTPServer.h b/rfb/HTTPServer.h
new file mode 100644
index 00000000..9431195c
--- /dev/null
+++ b/rfb/HTTPServer.h
@@ -0,0 +1,112 @@
+/* Copyright (C) 2002-2004 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.
+ */
+
+// -=- HTTPServer.h
+
+// Single-threaded HTTP server implementation.
+// All I/O is handled by the processSocketEvent routine,
+// which is called by the main-loop of the VNC server whenever
+// there is an event on an HTTP socket.
+
+#ifndef __RFB_HTTP_SERVER_H__
+#define __RFB_HTTP_SERVER_H__
+
+#include <list>
+
+#include <rdr/MemInStream.h>
+#include <rfb/UpdateTracker.h>
+#include <rfb/Configuration.h>
+#include <network/Socket.h>
+
+namespace rfb {
+
+ class HTTPServer : public network::SocketServer {
+ public:
+ // -=- Constructors
+
+ // - HTTPServer(files)
+ // Create an HTTP server which will use the getFile method
+ // to satisfy HTTP GET requests.
+ HTTPServer();
+
+ virtual ~HTTPServer();
+
+ // -=- Client management
+
+ // - Run a client connection on the supplied socket
+ // This causes the server to perform HTTP protocol on the
+ // supplied socket.
+ // The socket will be closed if protocol initialisation
+ // fails.
+ virtual void addClient(network::Socket* sock);
+
+ // -=- Event processing methods
+
+ // - Process an input event on a particular Socket
+ // The platform-specific side of the server implementation calls
+ // this method whenever data arrives on one of the active
+ // network sockets.
+ // The method returns true if the Socket is still in use by the
+ // server, or false if it is no longer required and should be
+ // deleted.
+ // NB: If false is returned then the Socket is deleted and must
+ // not be accessed again!
+
+ virtual bool processSocketEvent(network::Socket* sock);
+
+ // - Check for socket timeouts
+ virtual int checkTimeouts();
+
+ // getSockets() gets a list of sockets. This can be used to generate an
+ // fd_set for calling select().
+
+ virtual void getSockets(std::list<network::Socket*>* sockets);
+
+
+ // -=- File interface
+
+ // - getFile is passed the path portion of a URL and returns an
+ // InStream containing the data to return. If the requested
+ // file is available then the contentType should be set to the
+ // type of the file, or left untouched if the file type is to
+ // be determined automatically by HTTPServer.
+ // If the file is not available then null is returned.
+ // Overridden getFile functions should call the default version
+ // if they do not recognise a path name.
+ // NB: The caller assumes ownership of the returned InStream.
+ // NB: The contentType is statically allocated by the getFile impl.
+ // NB: contentType is *guaranteed* to be valid when getFile is called.
+
+ virtual rdr::InStream* getFile(const char* name, const char** contentType);
+
+ // - guessContentType is passed the name of a file and returns the
+ // name of an HTTP content type, based on the file's extension. If
+ // the extension isn't recognised then defType is returned. This can
+ // be used from getFile to easily default to the supplied contentType,
+ // or by passing zero in to determine whether a type is recognised or not.
+
+ static const char* guessContentType(const char* name, const char* defType);
+
+ protected:
+ class Session;
+ std::list<Session*> sessions;
+ };
+}
+
+#endif
+
diff --git a/rfb/HextileDecoder.cxx b/rfb/HextileDecoder.cxx
new file mode 100644
index 00000000..97c7ca74
--- /dev/null
+++ b/rfb/HextileDecoder.cxx
@@ -0,0 +1,59 @@
+/* Copyright (C) 2002-2003 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 <rfb/CMsgReader.h>
+#include <rfb/CMsgHandler.h>
+#include <rfb/HextileDecoder.h>
+
+using namespace rfb;
+
+#define EXTRA_ARGS CMsgHandler* handler
+#define FILL_RECT(r, p) handler->fillRect(r, p)
+#define IMAGE_RECT(r, p) handler->imageRect(r, p)
+#define BPP 8
+#include <rfb/hextileDecode.h>
+#undef BPP
+#define BPP 16
+#include <rfb/hextileDecode.h>
+#undef BPP
+#define BPP 32
+#include <rfb/hextileDecode.h>
+#undef BPP
+
+Decoder* HextileDecoder::create(CMsgReader* reader)
+{
+ return new HextileDecoder(reader);
+}
+
+HextileDecoder::HextileDecoder(CMsgReader* reader_) : reader(reader_)
+{
+}
+
+HextileDecoder::~HextileDecoder()
+{
+}
+
+void HextileDecoder::readRect(const Rect& r, CMsgHandler* handler)
+{
+ rdr::InStream* is = reader->getInStream();
+ rdr::U8* buf = reader->getImageBuf(16 * 16 * 4);
+ switch (reader->bpp()) {
+ case 8: hextileDecode8 (r, is, (rdr::U8*) buf, handler); break;
+ case 16: hextileDecode16(r, is, (rdr::U16*)buf, handler); break;
+ case 32: hextileDecode32(r, is, (rdr::U32*)buf, handler); break;
+ }
+}
diff --git a/rfb/HextileDecoder.h b/rfb/HextileDecoder.h
new file mode 100644
index 00000000..718bd38b
--- /dev/null
+++ b/rfb/HextileDecoder.h
@@ -0,0 +1,35 @@
+/* Copyright (C) 2002-2003 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_HEXTILEDECODER_H__
+#define __RFB_HEXTILEDECODER_H__
+
+#include <rfb/Decoder.h>
+
+namespace rfb {
+
+ class HextileDecoder : public Decoder {
+ public:
+ static Decoder* create(CMsgReader* reader);
+ virtual void readRect(const Rect& r, CMsgHandler* handler);
+ virtual ~HextileDecoder();
+ private:
+ HextileDecoder(CMsgReader* reader);
+ CMsgReader* reader;
+ };
+}
+#endif
diff --git a/rfb/HextileEncoder.cxx b/rfb/HextileEncoder.cxx
new file mode 100644
index 00000000..736bd5f7
--- /dev/null
+++ b/rfb/HextileEncoder.cxx
@@ -0,0 +1,61 @@
+/* Copyright (C) 2002-2004 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 <rfb/ImageGetter.h>
+#include <rfb/encodings.h>
+#include <rfb/SMsgWriter.h>
+#include <rfb/HextileEncoder.h>
+
+using namespace rfb;
+
+#define EXTRA_ARGS ImageGetter* ig
+#define GET_IMAGE_INTO_BUF(r,buf) ig->getImage(buf, r);
+#define BPP 8
+#include <rfb/hextileEncode.h>
+#undef BPP
+#define BPP 16
+#include <rfb/hextileEncode.h>
+#undef BPP
+#define BPP 32
+#include <rfb/hextileEncode.h>
+#undef BPP
+
+Encoder* HextileEncoder::create(SMsgWriter* writer)
+{
+ return new HextileEncoder(writer);
+}
+
+HextileEncoder::HextileEncoder(SMsgWriter* writer_) : writer(writer_)
+{
+}
+
+HextileEncoder::~HextileEncoder()
+{
+}
+
+bool HextileEncoder::writeRect(const Rect& r, ImageGetter* ig, Rect* actual)
+{
+ writer->startRect(r, encodingHextile);
+ rdr::OutStream* os = writer->getOutStream();
+ switch (writer->bpp()) {
+ case 8: hextileEncode8(r, os, ig); break;
+ case 16: hextileEncode16(r, os, ig); break;
+ case 32: hextileEncode32(r, os, ig); break;
+ }
+ writer->endRect();
+ return true;
+}
diff --git a/rfb/HextileEncoder.h b/rfb/HextileEncoder.h
new file mode 100644
index 00000000..f09ead8e
--- /dev/null
+++ b/rfb/HextileEncoder.h
@@ -0,0 +1,35 @@
+/* Copyright (C) 2002-2004 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_HEXTILEENCODER_H__
+#define __RFB_HEXTILEENCODER_H__
+
+#include <rfb/Encoder.h>
+
+namespace rfb {
+
+ class HextileEncoder : public Encoder {
+ public:
+ static Encoder* create(SMsgWriter* writer);
+ virtual bool writeRect(const Rect& r, ImageGetter* ig, Rect* actual);
+ virtual ~HextileEncoder();
+ private:
+ HextileEncoder(SMsgWriter* writer);
+ SMsgWriter* writer;
+ };
+}
+#endif
diff --git a/rfb/Hostname.h b/rfb/Hostname.h
new file mode 100644
index 00000000..bdff474e
--- /dev/null
+++ b/rfb/Hostname.h
@@ -0,0 +1,53 @@
+/* Copyright (C) 2002-2003 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_HOSTNAME_H__
+#define __RFB_HOSTNAME_H__
+
+#include <rfb/util.h>
+
+namespace rfb {
+
+ void getHostAndPort(const char* hi, char** host, int* port, int basePort=5900) {
+ CharArray portBuf;
+ CharArray hostBuf;
+ if (hi[0] == '[') {
+ if (!strSplit(&hi[1], ']', &hostBuf.buf, &portBuf.buf))
+ throw rdr::Exception("unmatched [ in host");
+ } else {
+ portBuf.buf = strDup(hi);
+ }
+ if (strSplit(portBuf.buf, ':', hostBuf.buf ? 0 : &hostBuf.buf, &portBuf.buf)) {
+ if (portBuf.buf[0] == ':') {
+ *port = atoi(&portBuf.buf[1]);
+ } else {
+ *port = atoi(portBuf.buf);
+ if (*port < 100) *port += basePort;
+ }
+ } else {
+ *port = basePort;
+ }
+ if (strlen(hostBuf.buf) == 0)
+ *host = strDup("localhost");
+ else
+ *host = hostBuf.takeBuf();
+ }
+
+};
+
+#endif // __RFB_HOSTNAME_H__
diff --git a/rfb/ImageGetter.h b/rfb/ImageGetter.h
new file mode 100644
index 00000000..b550a12f
--- /dev/null
+++ b/rfb/ImageGetter.h
@@ -0,0 +1,30 @@
+/* Copyright (C) 2002-2003 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_IMAGEGETTER_H__
+#define __RFB_IMAGEGETTER_H__
+
+#include <rfb/Rect.h>
+
+namespace rfb {
+ class ImageGetter {
+ public:
+ virtual void getImage(void* imageBuf,
+ const Rect& r, int stride=0) = 0;
+ };
+}
+#endif
diff --git a/rfb/LogWriter.cxx b/rfb/LogWriter.cxx
new file mode 100644
index 00000000..6f267f12
--- /dev/null
+++ b/rfb/LogWriter.cxx
@@ -0,0 +1,137 @@
+/* Copyright (C) 2002-2004 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.
+ */
+
+// -=- LogWriter.cxx - client-side logging interface
+
+#include <string.h>
+#ifdef WIN32
+#define strcasecmp _stricmp
+#endif
+
+#include <rfb/LogWriter.h>
+#include <rfb/Configuration.h>
+#include <rfb/util.h>
+#include <stdlib.h>
+
+rfb::LogParameter rfb::logParams;
+
+using namespace rfb;
+
+
+LogWriter::LogWriter(const char* name) : m_name(name), m_level(0), m_log(0), m_next(log_writers) {
+ log_writers = this;
+}
+
+LogWriter::~LogWriter() {
+ // *** Should remove this logger here!
+}
+
+void LogWriter::setLog(Logger *logger) {
+ m_log = logger;
+}
+
+void LogWriter::setLevel(int level) {
+ m_level = level;
+}
+
+void
+LogWriter::listLogWriters(int width) {
+ // *** make this respect width...
+ LogWriter* current = log_writers;
+ printf(" ");
+ while (current) {
+ printf("%s", current->m_name);
+ current = current->m_next;
+ if (current) printf(", ");
+ }
+ printf("\n");
+}
+
+LogWriter* LogWriter::log_writers;
+
+LogWriter*
+LogWriter::getLogWriter(const char* name) {
+ LogWriter* current = log_writers;
+ while (current) {
+ if (strcasecmp(name, current->m_name) == 0) return current;
+ current = current->m_next;
+ }
+ return 0;
+}
+
+bool LogWriter::setLogParams(const char* params) {
+ CharArray logwriterName, loggerName, logLevel;
+ if (!strSplit(params, ':', &logwriterName.buf, &loggerName.buf) ||
+ !strSplit(loggerName.buf, ':', &loggerName.buf, &logLevel.buf)) {
+ fprintf(stderr,"failed to parse log params:%s\n",params);
+ return false;
+ }
+ int level = atoi(logLevel.buf);
+ Logger* logger = 0;
+ if (strcmp("", loggerName.buf) != 0) {
+ logger = Logger::getLogger(loggerName.buf);
+ if (!logger) fprintf(stderr,"no logger found! %s\n",loggerName.buf);
+ }
+ if (strcmp("*", logwriterName.buf) == 0) {
+ LogWriter* current = log_writers;
+ while (current) {
+ current->setLog(logger);
+ current->setLevel(level);
+ current = current->m_next;
+ }
+ return true;
+ } else {
+ LogWriter* logwriter = getLogWriter(logwriterName.buf);
+ if (!logwriter) {
+ fprintf(stderr,"no logwriter found! %s\n",logwriterName.buf);
+ } else {
+ logwriter->setLog(logger);
+ logwriter->setLevel(level);
+ return true;
+ }
+ }
+ return false;
+}
+
+
+LogParameter::LogParameter()
+ : StringParameter("Log",
+ "Specifies which log output should be directed to "
+ "which target logger, and the level of output to log. "
+ "Format is <log>:<target>:<level>[, ...].",
+ "") {
+}
+
+bool LogParameter::setParam(const char* v) {
+ if (immutable) return true;
+ LogWriter::setLogParams("*::0");
+ StringParameter::setParam(v);
+ CharArray logParam;
+ CharArray params(getData());
+ while (params.buf) {
+ strSplit(params.buf, ',', &logParam.buf, &params.buf);
+ if (strlen(logParam.buf) && !LogWriter::setLogParams(logParam.buf))
+ return false;
+ }
+ return true;
+}
+
+void LogParameter::setDefault(const char* d) {
+ def_value = d;
+ setParam(def_value);
+}
diff --git a/rfb/LogWriter.h b/rfb/LogWriter.h
new file mode 100644
index 00000000..58e81f28
--- /dev/null
+++ b/rfb/LogWriter.h
@@ -0,0 +1,106 @@
+/* Copyright (C) 2002-2003 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.
+ */
+
+// -=- LogWriter.h - The Log writer class.
+
+#ifndef __RFB_LOG_WRITER_H__
+#define __RFB_LOG_WRITER_H__
+
+#include <stdarg.h>
+#include <rfb/Logger.h>
+#include <rfb/Configuration.h>
+
+// Each log writer instance has a unique textual name,
+// and is attached to a particular Log instance and
+// is assigned a particular log level.
+
+#define DEF_LOGFUNCTION(name, level) \
+ inline void name(const char* fmt, ...) { \
+ if (m_log && (level <= m_level)) { \
+ va_list ap; va_start(ap, fmt); \
+ m_log->write(level, m_name, fmt, ap);\
+ va_end(ap); \
+ } \
+ }
+
+namespace rfb {
+
+ class LogWriter;
+
+ class LogWriter {
+ public:
+ LogWriter(const char* name);
+ ~LogWriter();
+
+ const char *getName() {return m_name;}
+
+ void setLog(Logger *logger);
+ void setLevel(int level);
+
+ inline void write(int level, const char* format, ...) {
+ if (m_log && (level <= m_level)) {
+ va_list ap;
+ va_start(ap, format);
+ m_log->write(level, m_name, format, ap);
+ va_end(ap);
+ }
+ }
+
+ DEF_LOGFUNCTION(error, 0)
+ DEF_LOGFUNCTION(status, 10)
+ DEF_LOGFUNCTION(info, 30)
+ DEF_LOGFUNCTION(debug, 100)
+
+ // -=- DIAGNOSTIC & HELPER ROUTINES
+
+ static void listLogWriters(int width=79);
+
+ // -=- CLASS FIELDS & FUNCTIONS
+
+ static LogWriter* log_writers;
+
+ static LogWriter* getLogWriter(const char* name);
+
+ static bool setLogParams(const char* params);
+
+ private:
+ const char* m_name;
+ int m_level;
+ Logger* m_log;
+ LogWriter* m_next;
+ };
+
+ class LogParameter : public StringParameter {
+ public:
+ LogParameter();
+ virtual bool setParam(const char* v);
+
+ // Call this to set a suitable default value.
+ // Can't use the normal default mechanism for
+ // this because there is no guarantee on C++
+ // constructor ordering - some LogWriters may
+ // not exist when LogParameter gets constructed.
+ // NB: The default value must exist for the
+ // lifetime of the process!
+ void setDefault(const char* v);
+ };
+ extern LogParameter logParams;
+
+};
+
+#endif // __RFB_LOG_WRITER_H__
diff --git a/rfb/Logger.cxx b/rfb/Logger.cxx
new file mode 100644
index 00000000..84b8f559
--- /dev/null
+++ b/rfb/Logger.cxx
@@ -0,0 +1,107 @@
+/* Copyright (C) 2002-2003 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.
+ */
+
+// -=- Logger.cxx - support for the Logger and LogWriter classes
+
+#include <stdarg.h>
+#include <stdio.h>
+#include <string.h>
+#ifdef WIN32
+#define strcasecmp _stricmp
+#define vsnprintf _vsnprintf
+#define HAVE_VSNPRINTF
+#endif
+
+#include <rfb/Logger.h>
+#include <rfb/LogWriter.h>
+#include <rfb/util.h>
+
+using namespace rfb;
+
+#ifndef HAVE_VSNPRINTF
+static int vsnprintf(char *str, size_t n, const char *format, va_list ap)
+{
+ str[0] = 0;
+ FILE* fp = fopen("/dev/null","w");
+ if (!fp) return 0;
+ int len = vfprintf(fp, format, ap);
+ if (len <= 0) return 0;
+ fclose(fp);
+
+ CharArray s(len+1);
+ vsprintf(s.buf, format, ap);
+
+ if (len > (int)n-1) len = n-1;
+ memcpy(str, s.buf, len);
+ str[len] = 0;
+ return len;
+}
+#endif
+
+
+Logger* Logger::loggers = 0;
+
+Logger::Logger(const char* name) : registered(false), m_name(name), m_next(0) {
+}
+
+Logger::~Logger() {
+ // *** Should remove this logger here!
+}
+
+void Logger::write(int level, const char *logname, const char* format,
+ va_list ap)
+{
+ // - Format the supplied data, and pass it to the
+ // actual log_message function
+ // The log level is included as a hint for loggers capable of representing
+ // different log levels in some way.
+ char buf1[4096];
+ vsnprintf(buf1, sizeof(buf1)-1, format, ap);
+ buf1[sizeof(buf1)-1] = 0;
+ write(level, logname, buf1);
+}
+
+void
+Logger::registerLogger() {
+ if (!registered) {
+ registered = true;
+ m_next = loggers;
+ loggers=this;
+ }
+}
+
+Logger*
+Logger::getLogger(const char* name) {
+ Logger* current = loggers;
+ while (current) {
+ if (strcasecmp(name, current->m_name) == 0) return current;
+ current = current->m_next;
+ }
+ return 0;
+}
+
+void
+Logger::listLoggers() {
+ Logger* current = loggers;
+ while (current) {
+ printf(" %s\n", current->m_name);
+ current = current->m_next;
+ }
+}
+
+
diff --git a/rfb/Logger.h b/rfb/Logger.h
new file mode 100644
index 00000000..4233964b
--- /dev/null
+++ b/rfb/Logger.h
@@ -0,0 +1,70 @@
+/* Copyright (C) 2002-2003 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.
+ */
+
+// -=- Logger.h - The Logger class.
+
+#ifndef __RFB_LOGGER_H__
+#define __RFB_LOGGER_H__
+
+#include <stdarg.h>
+#include <stdio.h>
+
+// Each log writer instance has a unique textual name,
+// and is attached to a particular Logger instance and
+// is assigned a particular log level.
+
+namespace rfb {
+
+ class Logger {
+ public:
+
+ // -=- Create / Destroy a logger
+
+ Logger(const char* name);
+ virtual ~Logger();
+
+ // -=- Get the name of a logger
+
+ const char *getName() {return m_name;}
+
+ // -=- Write data to a log
+
+ virtual void write(int level, const char *logname, const char *text) = 0;
+ void write(int level, const char *logname, const char* format, va_list ap);
+
+ // -=- Register a logger
+
+ void registerLogger();
+
+ // -=- CLASS FIELDS & FUNCTIONS
+
+ static Logger* loggers;
+
+ static Logger* getLogger(const char* name);
+
+ static void listLoggers();
+
+ private:
+ bool registered;
+ const char *m_name;
+ Logger *m_next;
+ };
+
+};
+
+#endif // __RFB_LOGGER_H__
diff --git a/rfb/Logger_file.cxx b/rfb/Logger_file.cxx
new file mode 100644
index 00000000..ac249b39
--- /dev/null
+++ b/rfb/Logger_file.cxx
@@ -0,0 +1,123 @@
+/* Copyright (C) 2002-2004 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.
+ */
+
+// -=- Logger_file.cxx - Logger instance for a file
+
+#include <stdlib.h>
+#include <string.h>
+
+#include <rfb/util.h>
+#include <rfb/Logger_file.h>
+#include <rfb/Threading.h>
+
+using namespace rfb;
+
+
+// If threading is available then protect the write() operation
+// from concurrent accesses
+#ifdef __RFB_THREADING_IMPL
+static Mutex logLock;
+#endif
+
+
+Logger_File::Logger_File(const char* loggerName)
+ : Logger(loggerName), indent(13), width(79), m_filename(0), m_file(0),
+ m_lastLogTime(0)
+{
+}
+
+Logger_File::~Logger_File()
+{
+ closeFile();
+}
+
+void Logger_File::write(int level, const char *logname, const char *message)
+{
+#ifdef __RFB_THREADING_IMPL
+ Lock l(logLock);
+#endif
+ if (!m_file) {
+ if (!m_filename) return;
+ m_file = fopen(m_filename, "w+");
+ if (!m_file) return;
+ }
+
+#ifndef _WIN32_WCE
+ time_t current = time(0);
+ if (current != m_lastLogTime) {
+ m_lastLogTime = current;
+ fprintf(m_file, "\n%s", ctime(&m_lastLogTime));
+ }
+#endif
+
+ fprintf(m_file," %s:", logname);
+ int column = strlen(logname) + 2;
+ if (column < indent) {
+ fprintf(m_file,"%*s",indent-column,"");
+ column = indent;
+ }
+ while (true) {
+ const char* s = strchr(message, ' ');
+ int wordLen;
+ if (s) wordLen = s-message;
+ else wordLen = strlen(message);
+
+ if (column + wordLen + 1 > width) {
+ fprintf(m_file,"\n%*s",indent,"");
+ column = indent;
+ }
+ fprintf(m_file," %.*s",wordLen,message);
+ column += wordLen + 1;
+ message += wordLen + 1;
+ if (!s) break;
+ }
+ fprintf(m_file,"\n");
+ fflush(m_file);
+}
+
+void Logger_File::setFilename(const char* filename)
+{
+ closeFile();
+ m_filename = strDup(filename);
+}
+
+void Logger_File::setFile(FILE* file)
+{
+ closeFile();
+ m_file = file;
+}
+
+void Logger_File::closeFile()
+{
+ if (m_filename) {
+ if (m_file) {
+ fclose(m_file);
+ m_file = 0;
+ }
+ strFree(m_filename);
+ m_filename = 0;
+ }
+}
+
+static Logger_File logger("file");
+
+bool rfb::initFileLogger(const char* filename) {
+ logger.setFilename(filename);
+ logger.registerLogger();
+ return true;
+}
diff --git a/rfb/Logger_file.h b/rfb/Logger_file.h
new file mode 100644
index 00000000..30c3f402
--- /dev/null
+++ b/rfb/Logger_file.h
@@ -0,0 +1,51 @@
+/* Copyright (C) 2002-2003 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.
+ */
+
+// -=- Logger_file - log to a file
+
+#ifndef __RFB_LOGGER_FILE_H__
+#define __RFB_LOGGER_FILE_H__
+
+#include <time.h>
+#include <rfb/Logger.h>
+
+namespace rfb {
+
+ class Logger_File : public Logger {
+ public:
+ Logger_File(const char* loggerName);
+ ~Logger_File();
+
+ virtual void write(int level, const char *logname, const char *message);
+ void setFilename(const char* filename);
+ void setFile(FILE* file);
+
+ int indent;
+ int width;
+
+ protected:
+ void closeFile();
+ char* m_filename;
+ FILE* m_file;
+ time_t m_lastLogTime;
+ };
+
+ bool initFileLogger(const char* filename);
+};
+
+#endif
diff --git a/rfb/Logger_stdio.cxx b/rfb/Logger_stdio.cxx
new file mode 100644
index 00000000..ac9556e1
--- /dev/null
+++ b/rfb/Logger_stdio.cxx
@@ -0,0 +1,32 @@
+/* Copyright (C) 2002-2003 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.
+ */
+
+// -=- Logger_stdio.cxx - Logger instances for stderr and stdout
+
+#include <rfb/Logger_stdio.h>
+
+using namespace rfb;
+
+static Logger_StdIO logStdErr("stderr", stderr);
+static Logger_StdIO logStdOut("stdout", stdout);
+
+bool rfb::initStdIOLoggers() {
+ logStdErr.registerLogger();
+ logStdOut.registerLogger();
+ return true;
+}
diff --git a/rfb/Logger_stdio.h b/rfb/Logger_stdio.h
new file mode 100644
index 00000000..68f795fc
--- /dev/null
+++ b/rfb/Logger_stdio.h
@@ -0,0 +1,39 @@
+/* Copyright (C) 2002-2003 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.
+ */
+
+// -=- Logger_stdio - standard output logger instances
+
+#ifndef __RFB_LOGGER_STDIO_H__
+#define __RFB_LOGGER_STDIO_H__
+
+#include <rfb/Logger_file.h>
+
+namespace rfb {
+
+ class Logger_StdIO : public Logger_File {
+ public:
+ Logger_StdIO(const char *name, FILE* file) : Logger_File(name) {
+ setFile(file);
+ }
+ };
+
+ bool initStdIOLoggers();
+
+};
+
+#endif
diff --git a/rfb/Makefile.in b/rfb/Makefile.in
new file mode 100644
index 00000000..50bc04c2
--- /dev/null
+++ b/rfb/Makefile.in
@@ -0,0 +1,66 @@
+
+CXXSRCS = \
+ Blacklist.cxx \
+ CConnection.cxx \
+ CMsgHandler.cxx \
+ CMsgReader.cxx \
+ CMsgReaderV3.cxx \
+ CMsgWriter.cxx \
+ CMsgWriterV3.cxx \
+ CSecurityVncAuth.cxx \
+ ComparingUpdateTracker.cxx \
+ Configuration.cxx \
+ ConnParams.cxx \
+ Cursor.cxx \
+ Decoder.cxx \
+ Encoder.cxx \
+ HTTPServer.cxx \
+ HextileDecoder.cxx \
+ HextileEncoder.cxx \
+ LogWriter.cxx \
+ Logger.cxx \
+ Logger_file.cxx \
+ Logger_stdio.cxx \
+ PixelBuffer.cxx \
+ PixelFormat.cxx \
+ RREEncoder.cxx \
+ RREDecoder.cxx \
+ RawDecoder.cxx \
+ RawEncoder.cxx \
+ Region.cxx \
+ SConnection.cxx \
+ SMsgHandler.cxx \
+ SMsgReader.cxx \
+ SMsgReaderV3.cxx \
+ SMsgWriter.cxx \
+ SMsgWriterV3.cxx \
+ ServerCore.cxx \
+ SSecurityFactoryStandard.cxx \
+ SSecurityVncAuth.cxx \
+ TransImageGetter.cxx \
+ UpdateTracker.cxx \
+ VNCSConnectionST.cxx \
+ VNCServerST.cxx \
+ ZRLEEncoder.cxx \
+ ZRLEDecoder.cxx \
+ encodings.cxx \
+ secTypes.cxx \
+ util.cxx \
+ vncAuth.cxx
+
+SRCS = d3des.c $(CXXSRCS)
+
+OBJS = d3des.o $(CXXSRCS:.cxx=.o)
+
+DIR_CPPFLAGS = -I$(top_srcdir) @VSNPRINTF_DEFINE@
+
+library = librfb.a
+
+all:: $(library)
+
+$(library): $(OBJS)
+ rm -f $(library)
+ $(AR) $(library) $(OBJS)
+ $(RANLIB) $(library)
+
+# followed by boilerplate.mk
diff --git a/rfb/Pixel.h b/rfb/Pixel.h
new file mode 100644
index 00000000..2b1aaf0d
--- /dev/null
+++ b/rfb/Pixel.h
@@ -0,0 +1,26 @@
+/* Copyright (C) 2002-2003 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_PIXEL_H__
+#define __RFB_PIXEL_H__
+
+#include <rdr/types.h>
+
+namespace rfb {
+ typedef rdr::U32 Pixel; // must be big enough to hold any pixel value
+}
+#endif
diff --git a/rfb/PixelBuffer.cxx b/rfb/PixelBuffer.cxx
new file mode 100644
index 00000000..fcad227e
--- /dev/null
+++ b/rfb/PixelBuffer.cxx
@@ -0,0 +1,309 @@
+/* Copyright (C) 2002-2004 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.
+ */
+
+// -=- PixelBuffer.cxx
+//
+// The PixelBuffer class encapsulates the PixelFormat and dimensions
+// of a block of pixel data.
+
+#include <rfb/Exception.h>
+#include <rfb/LogWriter.h>
+#include <rfb/PixelBuffer.h>
+
+using namespace rfb;
+using namespace rdr;
+
+static LogWriter vlog("PixelBuffer");
+
+
+// -=- Generic pixel buffer class
+
+PixelBuffer::PixelBuffer(const PixelFormat& pf, int w, int h, ColourMap* cm)
+ : format(pf), width_(w), height_(h), colourmap(cm) {}
+PixelBuffer::PixelBuffer() : width_(0), height_(0), colourmap(0) {}
+
+PixelBuffer::~PixelBuffer() {}
+
+
+void PixelBuffer::setPF(const PixelFormat &pf) {format = pf;}
+const PixelFormat& PixelBuffer::getPF() const {return format;}
+ColourMap* PixelBuffer::getColourMap() const {return colourmap;}
+
+
+void
+PixelBuffer::getImage(void* imageBuf, const Rect& r, int outStride) {
+ int inStride;
+ const U8* data = getPixelsR(r, &inStride);
+ // We assume that the specified rectangle is pre-clipped to the buffer
+ int bytesPerPixel = format.bpp/8;
+ int inBytesPerRow = inStride * bytesPerPixel;
+ if (!outStride) outStride = r.width();
+ int outBytesPerRow = outStride * bytesPerPixel;
+ int bytesPerMemCpy = r.width() * bytesPerPixel;
+ U8* imageBufPos = (U8*)imageBuf;
+ const U8* end = data + (inBytesPerRow * r.height());
+ while (data < end) {
+ memcpy(imageBufPos, data, bytesPerMemCpy);
+ imageBufPos += outBytesPerRow;
+ data += inBytesPerRow;
+ }
+}
+
+/* ***
+Pixel PixelBuffer::getPixel(const Point& p) {
+ int stride;
+ Rect r = Rect(p.x, p.y, p.x+1, p.y+1);
+ switch(format.bpp) {
+ case 8: return *((rdr::U8*)getDataAt(r, &stride));
+ case 16: return *((rdr::U16*)getDataAt(r, &stride));
+ case 32: return *((rdr::U32*)getDataAt(r, &stride));
+ default: return 0;
+ };
+}
+*/
+
+
+FullFramePixelBuffer::FullFramePixelBuffer(const PixelFormat& pf, int w, int h,
+ rdr::U8* data_, ColourMap* cm)
+ : PixelBuffer(pf, w, h, cm), data(data_)
+{
+}
+
+FullFramePixelBuffer::FullFramePixelBuffer() : data(0) {}
+
+FullFramePixelBuffer::~FullFramePixelBuffer() {}
+
+
+int FullFramePixelBuffer::getStride() const { return width(); }
+
+rdr::U8* FullFramePixelBuffer::getPixelsRW(const Rect& r, int* stride)
+{
+ *stride = getStride();
+ return &data[(r.tl.x + (r.tl.y * *stride)) * format.bpp/8];
+}
+
+
+void FullFramePixelBuffer::fillRect(const Rect& r, Pixel pix) {
+ int stride;
+ U8* data = getPixelsRW(r, &stride);
+ int bytesPerPixel = getPF().bpp/8;
+ int bytesPerRow = bytesPerPixel * stride;
+ int bytesPerFill = bytesPerPixel * r.width();
+
+ U8* end = data + (bytesPerRow * r.height());
+ while (data < end) {
+ switch (bytesPerPixel) {
+ case 1:
+ memset(data, pix, bytesPerFill);
+ break;
+ case 2:
+ {
+ U16* optr = (U16*)data;
+ U16* eol = optr + r.width();
+ while (optr < eol)
+ *optr++ = pix;
+ }
+ break;
+ case 4:
+ {
+ U32* optr = (U32*)data;
+ U32* eol = optr + r.width();
+ while (optr < eol)
+ *optr++ = pix;
+ }
+ break;
+ }
+ data += bytesPerRow;
+ }
+}
+
+void FullFramePixelBuffer::imageRect(const Rect& r, const void* pixels, int srcStride) {
+ int bytesPerPixel = getPF().bpp/8;
+ int destStride;
+ U8* dest = getPixelsRW(r, &destStride);
+ int bytesPerDestRow = bytesPerPixel * destStride;
+ if (!srcStride) srcStride = r.width();
+ int bytesPerSrcRow = bytesPerPixel * srcStride;
+ int bytesPerFill = bytesPerPixel * r.width();
+ const U8* src = (const U8*)pixels;
+ U8* end = dest + (bytesPerDestRow * r.height());
+ while (dest < end) {
+ memcpy(dest, src, bytesPerFill);
+ dest += bytesPerDestRow;
+ src += bytesPerSrcRow;
+ }
+}
+
+void FullFramePixelBuffer::maskRect(const Rect& r, const void* pixels, const void* mask_) {
+ Rect cr = getRect().intersect(r);
+ if (cr.is_empty()) return;
+ int stride;
+ U8* data = getPixelsRW(cr, &stride);
+ U8* mask = (U8*) mask_;
+ int w = cr.width();
+ int h = cr.height();
+ int bpp = getPF().bpp;
+ int pixelStride = r.width();
+ int maskStride = (r.width() + 7) / 8;
+
+ Point offset = Point(cr.tl.x-r.tl.x, cr.tl.y-r.tl.y);
+ mask += offset.y * maskStride;
+ for (int y = 0; y < h; y++) {
+ int cy = offset.y + y;
+ for (int x = 0; x < w; x++) {
+ int cx = offset.x + x;
+ U8* byte = mask + (cx / 8);
+ int bit = 7 - cx % 8;
+ if ((*byte) & (1 << bit)) {
+ switch (bpp) {
+ case 8:
+ ((U8*)data)[y * stride + x] = ((U8*)pixels)[cy * pixelStride + cx];
+ break;
+ case 16:
+ ((U16*)data)[y * stride + x] = ((U16*)pixels)[cy * pixelStride + cx];
+ break;
+ case 32:
+ ((U32*)data)[y * stride + x] = ((U32*)pixels)[cy * pixelStride + cx];
+ break;
+ }
+ }
+ }
+ mask += maskStride;
+ }
+}
+
+void FullFramePixelBuffer::maskRect(const Rect& r, Pixel pixel, const void* mask_) {
+ Rect cr = getRect().intersect(r);
+ if (cr.is_empty()) return;
+ int stride;
+ U8* data = getPixelsRW(cr, &stride);
+ U8* mask = (U8*) mask_;
+ int w = cr.width();
+ int h = cr.height();
+ int bpp = getPF().bpp;
+ int maskStride = (r.width() + 7) / 8;
+
+ Point offset = Point(cr.tl.x-r.tl.x, cr.tl.y-r.tl.y);
+ mask += offset.y * maskStride;
+ for (int y = 0; y < h; y++) {
+ for (int x = 0; x < w; x++) {
+ int cx = offset.x + x;
+ U8* byte = mask + (cx / 8);
+ int bit = 7 - cx % 8;
+ if ((*byte) & (1 << bit)) {
+ switch (bpp) {
+ case 8:
+ ((U8*)data)[y * stride + x] = pixel;
+ break;
+ case 16:
+ ((U16*)data)[y * stride + x] = pixel;
+ break;
+ case 32:
+ ((U32*)data)[y * stride + x] = pixel;
+ break;
+ }
+ }
+ }
+ mask += maskStride;
+ }
+}
+
+void FullFramePixelBuffer::copyRect(const Rect &rect, const Point &move_by_delta) {
+ int stride;
+ U8* data = getPixelsRW(getRect(), &stride);
+ // We assume that the specified rectangle is pre-clipped to the buffer
+ unsigned int bytesPerPixel, bytesPerRow, bytesPerMemCpy;
+ Rect srect = rect.translate(move_by_delta.negate());
+ bytesPerPixel = getPF().bpp/8;
+ bytesPerRow = stride * bytesPerPixel;
+ bytesPerMemCpy = rect.width() * bytesPerPixel;
+ if (move_by_delta.y <= 0) {
+ U8* dest = data + rect.tl.x*bytesPerPixel + rect.tl.y*bytesPerRow;
+ U8* src = data + srect.tl.x*bytesPerPixel + srect.tl.y*bytesPerRow;
+ for (int i=rect.tl.y; i<rect.br.y; i++) {
+ memmove(dest, src, bytesPerMemCpy);
+ dest += bytesPerRow;
+ src += bytesPerRow;
+ }
+ } else {
+ U8* dest = data + rect.tl.x*bytesPerPixel + (rect.br.y-1)*bytesPerRow;
+ U8* src = data + srect.tl.x*bytesPerPixel + (srect.br.y-1)*bytesPerRow;
+ for (int i=rect.tl.y; i<rect.br.y; i++) {
+ memmove(dest, src, bytesPerMemCpy);
+ dest -= bytesPerRow;
+ src -= bytesPerRow;
+ }
+ }
+}
+
+
+// -=- Managed pixel buffer class
+// Automatically allocates enough space for the specified format & area
+
+ManagedPixelBuffer::ManagedPixelBuffer()
+ : datasize(0), own_colourmap(false)
+{
+ checkDataSize();
+};
+
+ManagedPixelBuffer::ManagedPixelBuffer(const PixelFormat& pf, int w, int h)
+ : FullFramePixelBuffer(pf, w, h, 0, 0), datasize(0), own_colourmap(false)
+{
+ checkDataSize();
+};
+
+ManagedPixelBuffer::~ManagedPixelBuffer() {
+ if (data) delete [] data;
+ if (colourmap && own_colourmap) delete colourmap;
+};
+
+
+void
+ManagedPixelBuffer::setPF(const PixelFormat &pf) {
+ format = pf; checkDataSize();
+};
+void
+ManagedPixelBuffer::setSize(int w, int h) {
+ width_ = w; height_ = h; checkDataSize();
+};
+
+
+void
+ManagedPixelBuffer::setColourMap(ColourMap* cm, bool own_cm) {
+ if (colourmap && own_colourmap) delete colourmap;
+ colourmap = cm;
+ own_colourmap = own_cm;
+}
+
+inline void
+ManagedPixelBuffer::checkDataSize() {
+ unsigned long new_datasize = width_ * height_ * (format.bpp/8);
+ if (datasize < new_datasize) {
+ vlog.debug("reallocating managed buffer (%dx%d)", width_, height_);
+ if (data) {
+ delete [] data;
+ datasize = 0; data = 0;
+ }
+ if (new_datasize) {
+ data = new U8[new_datasize];
+ if (!data)
+ throw Exception("rfb::ManagedPixelBuffer unable to allocate buffer");
+ datasize = new_datasize;
+ }
+ }
+};
diff --git a/rfb/PixelBuffer.h b/rfb/PixelBuffer.h
new file mode 100644
index 00000000..2ba105a7
--- /dev/null
+++ b/rfb/PixelBuffer.h
@@ -0,0 +1,172 @@
+/* Copyright (C) 2002-2004 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.
+ */
+
+// -=- PixelBuffer.h
+//
+// The PixelBuffer class encapsulates the PixelFormat and dimensions
+// of a block of pixel data.
+
+#ifndef __RFB_PIXEL_BUFFER_H__
+#define __RFB_PIXEL_BUFFER_H__
+
+#include <rfb/ImageGetter.h>
+#include <rfb/PixelFormat.h>
+#include <rfb/ColourMap.h>
+#include <rfb/Rect.h>
+#include <rfb/Pixel.h>
+
+namespace rfb {
+
+ class Region;
+
+ class PixelBuffer : public ImageGetter {
+ public:
+ PixelBuffer(const PixelFormat& pf, int width, int height, ColourMap* cm);
+ virtual ~PixelBuffer();
+
+ ///////////////////////////////////////////////
+ // Format / Layout
+ //
+
+ // Set/get pixel format & colourmap
+ virtual void setPF(const PixelFormat &pf);
+ virtual const PixelFormat &getPF() const;
+ virtual ColourMap* getColourMap() const;
+
+ // Get width, height and number of pixels
+ int width() const { return width_; }
+ int height() const { return height_; }
+ int area() const { return width_ * height_; }
+
+ // Get rectangle encompassing this buffer
+ // Top-left of rectangle is either at (0,0), or the specified point.
+ Rect getRect() const { return Rect(0, 0, width_, height_); }
+ Rect getRect(const Point& pos) const {
+ return Rect(pos, pos.translate(Point(width_, height_)));
+ }
+
+ ///////////////////////////////////////////////
+ // Access to pixel data
+ //
+
+ // Get a pointer into the buffer
+ // The pointer is to the top-left pixel of the specified Rect.
+ // The buffer stride (in pixels) is returned.
+ virtual const rdr::U8* getPixelsR(const Rect& r, int* stride) = 0;
+
+ // Get pixel data for a given part of the buffer
+ // Data is copied into the supplied buffer, with the specified
+ // stride.
+ virtual void getImage(void* imageBuf, const Rect& r, int stride=0);
+
+ // Get the data at (x,y) as a Pixel.
+ // VERY INEFFICIENT!!!
+ // *** Pixel getPixel(const Point& p);
+
+ ///////////////////////////////////////////////
+ // Framebuffer update methods
+ //
+
+ // Ensure that the specified rectangle of buffer is up to date.
+ // Overridden by derived classes implementing framebuffer access
+ // to copy the required display data into place.
+ virtual void grabRegion(const Region& region) {}
+
+ protected:
+ PixelBuffer();
+ PixelFormat format;
+ int width_, height_;
+ ColourMap* colourmap;
+ };
+
+ // FullFramePixelBuffer
+
+ class FullFramePixelBuffer : public PixelBuffer {
+ public:
+ FullFramePixelBuffer(const PixelFormat& pf, int width, int height,
+ rdr::U8* data_, ColourMap* cm);
+ virtual ~FullFramePixelBuffer();
+
+ // - Get the number of pixels per row in the actual pixel buffer data area
+ // This may in some cases NOT be the same as width().
+ virtual int getStride() const;
+
+ // Get a pointer to specified pixel data
+ virtual rdr::U8* getPixelsRW(const Rect& r, int* stride);
+ virtual const rdr::U8* getPixelsR(const Rect& r, int* stride) {
+ return getPixelsRW(r, stride);
+ }
+
+ ///////////////////////////////////////////////
+ // Basic rendering operations
+ // These operations DO NOT clip to the pixelbuffer area, or trap overruns.
+
+ // Fill a rectangle
+ virtual void fillRect(const Rect &dest, Pixel pix);
+
+ // Copy pixel data to the buffer
+ virtual void imageRect(const Rect &dest, const void* pixels, int stride=0);
+
+ // Copy pixel data from one PixelBuffer location to another
+ virtual void copyRect(const Rect &dest, const Point &move_by_delta);
+
+ // Copy pixel data to the buffer through a mask
+ // pixels is a pointer to the pixel to be copied to r.tl.
+ // maskPos specifies the pixel offset in the mask to start from.
+ // mask_ is a pointer to the mask bits at (0,0).
+ // pStride and mStride are the strides of the pixel and mask buffers.
+ virtual void maskRect(const Rect& r, const void* pixels, const void* mask_);
+
+ // pixel is the Pixel value to be used where mask_ is set
+ virtual void maskRect(const Rect& r, Pixel pixel, const void* mask_);
+
+ // *** Should this be visible?
+ rdr::U8* data;
+
+ protected:
+ FullFramePixelBuffer();
+ };
+
+ // -=- Managed pixel buffer class
+ // Automatically allocates enough space for the specified format & area
+
+ class ManagedPixelBuffer : public FullFramePixelBuffer {
+ public:
+ ManagedPixelBuffer();
+ ManagedPixelBuffer(const PixelFormat& pf, int width, int height);
+ virtual ~ManagedPixelBuffer();
+
+ // Manage the pixel buffer layout
+ virtual void setPF(const PixelFormat &pf);
+ virtual void setSize(int w, int h);
+
+ // Assign a colour map to the buffer
+ virtual void setColourMap(ColourMap* cm, bool own_cm);
+
+ // Return the total number of bytes of pixel data in the buffer
+ int dataLen() const { return width_ * height_ * (format.bpp/8); }
+
+ protected:
+ unsigned long datasize;
+ bool own_colourmap;
+ void checkDataSize();
+ };
+
+};
+
+#endif // __RFB_PIXEL_BUFFER_H__
diff --git a/rfb/PixelFormat.cxx b/rfb/PixelFormat.cxx
new file mode 100644
index 00000000..683b53a0
--- /dev/null
+++ b/rfb/PixelFormat.cxx
@@ -0,0 +1,238 @@
+/* Copyright (C) 2002-2003 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 <stdio.h>
+#include <string.h>
+#include <rdr/InStream.h>
+#include <rdr/OutStream.h>
+#include <rfb/PixelFormat.h>
+
+#ifdef _WIN32
+#define strcasecmp _stricmp
+#endif
+
+using namespace rfb;
+
+PixelFormat::PixelFormat(int b, int d, bool e, bool t,
+ int rm, int gm, int bm, int rs, int gs, int bs)
+ : bpp(b), depth(d), bigEndian(e), trueColour(t),
+ redMax(rm), greenMax(gm), blueMax(bm),
+ redShift(rs), greenShift(gs), blueShift(bs)
+{
+}
+
+PixelFormat::PixelFormat()
+ : bpp(8), depth(8), bigEndian(false), trueColour(true),
+ redMax(7), greenMax(7), blueMax(3),
+ redShift(0), greenShift(3), blueShift(6)
+{
+}
+
+bool PixelFormat::equal(const PixelFormat& other) const
+{
+ return (bpp == other.bpp &&
+ depth == other.depth &&
+ (bigEndian == other.bigEndian || bpp == 8) &&
+ trueColour == other.trueColour &&
+ (!trueColour || (redMax == other.redMax &&
+ greenMax == other.greenMax &&
+ blueMax == other.blueMax &&
+ redShift == other.redShift &&
+ greenShift == other.greenShift &&
+ blueShift == other.blueShift)));
+}
+
+void PixelFormat::read(rdr::InStream* is)
+{
+ bpp = is->readU8();
+ depth = is->readU8();
+ bigEndian = is->readU8();
+ trueColour = is->readU8();
+ redMax = is->readU16();
+ greenMax = is->readU16();
+ blueMax = is->readU16();
+ redShift = is->readU8();
+ greenShift = is->readU8();
+ blueShift = is->readU8();
+ is->skip(3);
+}
+
+void PixelFormat::write(rdr::OutStream* os) const
+{
+ os->writeU8(bpp);
+ os->writeU8(depth);
+ os->writeU8(bigEndian);
+ os->writeU8(trueColour);
+ os->writeU16(redMax);
+ os->writeU16(greenMax);
+ os->writeU16(blueMax);
+ os->writeU8(redShift);
+ os->writeU8(greenShift);
+ os->writeU8(blueShift);
+ os->pad(3);
+}
+
+Pixel PixelFormat::pixelFromRGB(rdr::U16 red, rdr::U16 green, rdr::U16 blue,
+ ColourMap* cm) const
+{
+ if (trueColour) {
+ rdr::U32 r = ((rdr::U32)red * redMax + 32767) / 65535;
+ rdr::U32 g = ((rdr::U32)green * greenMax + 32767) / 65535;
+ rdr::U32 b = ((rdr::U32)blue * blueMax + 32767) / 65535;
+
+ return (r << redShift) | (g << greenShift) | (b << blueShift);
+ } else if (cm) {
+ // Try to find the closest pixel by Cartesian distance
+ int colours = 1 << depth;
+ int diff = 256 * 256 * 4;
+ int col = 0;
+ for (int i=0; i<colours; i++) {
+ int r, g, b;
+ cm->lookup(i, &r, &g, &b);
+ int rd = (r-red) >> 8;
+ int gd = (g-green) >> 8;
+ int bd = (b-blue) >> 8;
+ int d = rd*rd + gd*gd + bd*bd;
+ if (d < diff) {
+ col = i;
+ diff = d;
+ }
+ }
+ return col;
+ }
+ // XXX just return 0 for colour map?
+ return 0;
+}
+
+
+void PixelFormat::rgbFromPixel(Pixel p, ColourMap* cm, Colour* rgb) const
+{
+ if (trueColour) {
+ rgb->r = (((p >> redShift ) & redMax ) * 65535 + redMax /2) / redMax;
+ rgb->g = (((p >> greenShift) & greenMax) * 65535 + greenMax/2) / greenMax;
+ rgb->b = (((p >> blueShift ) & blueMax ) * 65535 + blueMax /2) / blueMax;
+ } else {
+ cm->lookup(p, &rgb->r, &rgb->g, &rgb->b);
+ }
+}
+
+
+void PixelFormat::print(char* str, int len) const
+{
+ // Unfortunately snprintf is not widely available so we build the string up
+ // using strncat - not pretty, but should be safe against buffer overruns.
+
+ char num[20];
+ if (len < 1) return;
+ str[0] = 0;
+ strncat(str, "depth ", len-1-strlen(str));
+ sprintf(num,"%d",depth);
+ strncat(str, num, len-1-strlen(str));
+ strncat(str, " (", len-1-strlen(str));
+ sprintf(num,"%d",bpp);
+ strncat(str, num, len-1-strlen(str));
+ strncat(str, "bpp)", len-1-strlen(str));
+ if (bpp != 8) {
+ if (bigEndian)
+ strncat(str, " big-endian", len-1-strlen(str));
+ else
+ strncat(str, " little-endian", len-1-strlen(str));
+ }
+
+ if (!trueColour) {
+ strncat(str, " colour-map", len-1-strlen(str));
+ return;
+ }
+
+ if (blueShift == 0 && greenShift > blueShift && redShift > greenShift &&
+ blueMax == (1 << greenShift) - 1 &&
+ greenMax == (1 << (redShift-greenShift)) - 1 &&
+ redMax == (1 << (depth-redShift)) - 1)
+ {
+ strncat(str, " rgb", len-1-strlen(str));
+ sprintf(num,"%d",depth-redShift);
+ strncat(str, num, len-1-strlen(str));
+ sprintf(num,"%d",redShift-greenShift);
+ strncat(str, num, len-1-strlen(str));
+ sprintf(num,"%d",greenShift);
+ strncat(str, num, len-1-strlen(str));
+ return;
+ }
+
+ if (redShift == 0 && greenShift > redShift && blueShift > greenShift &&
+ redMax == (1 << greenShift) - 1 &&
+ greenMax == (1 << (blueShift-greenShift)) - 1 &&
+ blueMax == (1 << (depth-blueShift)) - 1)
+ {
+ strncat(str, " bgr", len-1-strlen(str));
+ sprintf(num,"%d",depth-blueShift);
+ strncat(str, num, len-1-strlen(str));
+ sprintf(num,"%d",blueShift-greenShift);
+ strncat(str, num, len-1-strlen(str));
+ sprintf(num,"%d",greenShift);
+ strncat(str, num, len-1-strlen(str));
+ return;
+ }
+
+ strncat(str, " rgb max ", len-1-strlen(str));
+ sprintf(num,"%d,",redMax);
+ strncat(str, num, len-1-strlen(str));
+ sprintf(num,"%d,",greenMax);
+ strncat(str, num, len-1-strlen(str));
+ sprintf(num,"%d",blueMax);
+ strncat(str, num, len-1-strlen(str));
+ strncat(str, " shift ", len-1-strlen(str));
+ sprintf(num,"%d,",redShift);
+ strncat(str, num, len-1-strlen(str));
+ sprintf(num,"%d,",greenShift);
+ strncat(str, num, len-1-strlen(str));
+ sprintf(num,"%d",blueShift);
+ strncat(str, num, len-1-strlen(str));
+}
+
+
+bool PixelFormat::parse(const char* str)
+{
+ char rgbbgr[4];
+ int bits1, bits2, bits3;
+ if (sscanf(str, "%3s%1d%1d%1d", rgbbgr, &bits1, &bits2, &bits3) < 4)
+ return false;
+
+ depth = bits1 + bits2 + bits3;
+ bpp = depth <= 8 ? 8 : ((depth <= 16) ? 16 : 32);
+ trueColour = true;
+ rdr::U32 endianTest = 1;
+ bigEndian = (*(rdr::U8*)&endianTest == 0);
+
+ greenShift = bits3;
+ greenMax = (1 << bits2) - 1;
+
+ if (strcasecmp(rgbbgr, "bgr") == 0) {
+ redShift = 0;
+ redMax = (1 << bits3) - 1;
+ blueShift = bits3 + bits2;
+ blueMax = (1 << bits1) - 1;
+ } else if (strcasecmp(rgbbgr, "rgb") == 0) {
+ blueShift = 0;
+ blueMax = (1 << bits3) - 1;
+ redShift = bits3 + bits2;
+ redMax = (1 << bits1) - 1;
+ } else {
+ return false;
+ }
+ return true;
+}
diff --git a/rfb/PixelFormat.h b/rfb/PixelFormat.h
new file mode 100644
index 00000000..0f2edd26
--- /dev/null
+++ b/rfb/PixelFormat.h
@@ -0,0 +1,58 @@
+/* Copyright (C) 2002-2003 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.
+ */
+//
+// PixelFormat - structure to represent a pixel format. Also has useful
+// methods for reading & writing to streams, etc.
+//
+
+#ifndef __RFB_PIXELFORMAT_H__
+#define __RFB_PIXELFORMAT_H__
+
+#include <rfb/Pixel.h>
+#include <rfb/ColourMap.h>
+
+namespace rdr { class InStream; class OutStream; }
+
+namespace rfb {
+
+ class PixelFormat {
+ public:
+ PixelFormat(int b, int d, bool e, bool t,
+ int rm=0, int gm=0, int bm=0, int rs=0, int gs=0, int bs=0);
+ PixelFormat();
+ bool equal(const PixelFormat& other) const;
+ void read(rdr::InStream* is);
+ void write(rdr::OutStream* os) const;
+ Pixel pixelFromRGB(rdr::U16 red, rdr::U16 green, rdr::U16 blue, ColourMap* cm=0) const;
+ void rgbFromPixel(Pixel pix, ColourMap* cm, Colour* rgb) const;
+ void print(char* str, int len) const;
+ bool parse(const char* str);
+
+ int bpp;
+ int depth;
+ bool bigEndian;
+ bool trueColour;
+ int redMax;
+ int greenMax;
+ int blueMax;
+ int redShift;
+ int greenShift;
+ int blueShift;
+ };
+}
+#endif
diff --git a/rfb/RREDecoder.cxx b/rfb/RREDecoder.cxx
new file mode 100644
index 00000000..c613dbb8
--- /dev/null
+++ b/rfb/RREDecoder.cxx
@@ -0,0 +1,58 @@
+/* Copyright (C) 2002-2003 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 <rfb/CMsgReader.h>
+#include <rfb/CMsgHandler.h>
+#include <rfb/RREDecoder.h>
+
+using namespace rfb;
+
+#define EXTRA_ARGS CMsgHandler* handler
+#define FILL_RECT(r, p) handler->fillRect(r, p)
+#define IMAGE_RECT(r, p) handler->imageRect(r, p)
+#define BPP 8
+#include <rfb/rreDecode.h>
+#undef BPP
+#define BPP 16
+#include <rfb/rreDecode.h>
+#undef BPP
+#define BPP 32
+#include <rfb/rreDecode.h>
+#undef BPP
+
+Decoder* RREDecoder::create(CMsgReader* reader)
+{
+ return new RREDecoder(reader);
+}
+
+RREDecoder::RREDecoder(CMsgReader* reader_) : reader(reader_)
+{
+}
+
+RREDecoder::~RREDecoder()
+{
+}
+
+void RREDecoder::readRect(const Rect& r, CMsgHandler* handler)
+{
+ rdr::InStream* is = reader->getInStream();
+ switch (reader->bpp()) {
+ case 8: rreDecode8 (r, is, handler); break;
+ case 16: rreDecode16(r, is, handler); break;
+ case 32: rreDecode32(r, is, handler); break;
+ }
+}
diff --git a/rfb/RREDecoder.h b/rfb/RREDecoder.h
new file mode 100644
index 00000000..75a5e859
--- /dev/null
+++ b/rfb/RREDecoder.h
@@ -0,0 +1,35 @@
+/* Copyright (C) 2002-2003 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_RREDECODER_H__
+#define __RFB_RREDECODER_H__
+
+#include <rfb/Decoder.h>
+
+namespace rfb {
+
+ class RREDecoder : public Decoder {
+ public:
+ static Decoder* create(CMsgReader* reader);
+ virtual void readRect(const Rect& r, CMsgHandler* handler);
+ virtual ~RREDecoder();
+ private:
+ RREDecoder(CMsgReader* reader);
+ CMsgReader* reader;
+ };
+}
+#endif
diff --git a/rfb/RREEncoder.cxx b/rfb/RREEncoder.cxx
new file mode 100644
index 00000000..612a869a
--- /dev/null
+++ b/rfb/RREEncoder.cxx
@@ -0,0 +1,75 @@
+/* Copyright (C) 2002-2004 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 <rdr/OutStream.h>
+#include <rfb/ImageGetter.h>
+#include <rfb/encodings.h>
+#include <rfb/SMsgWriter.h>
+#include <rfb/RREEncoder.h>
+
+using namespace rfb;
+
+#define BPP 8
+#include <rfb/rreEncode.h>
+#undef BPP
+#define BPP 16
+#include <rfb/rreEncode.h>
+#undef BPP
+#define BPP 32
+#include <rfb/rreEncode.h>
+#undef BPP
+
+Encoder* RREEncoder::create(SMsgWriter* writer)
+{
+ return new RREEncoder(writer);
+}
+
+RREEncoder::RREEncoder(SMsgWriter* writer_) : writer(writer_)
+{
+}
+
+RREEncoder::~RREEncoder()
+{
+}
+
+bool RREEncoder::writeRect(const Rect& r, ImageGetter* ig, Rect* actual)
+{
+ int w = r.width();
+ int h = r.height();
+ rdr::U8* imageBuf = writer->getImageBuf(w*h);
+ ig->getImage(imageBuf, r);
+
+ mos.clear();
+
+ int nSubrects = -1;
+ switch (writer->bpp()) {
+ case 8: nSubrects = rreEncode8(imageBuf, w, h, &mos); break;
+ case 16: nSubrects = rreEncode16(imageBuf, w, h, &mos); break;
+ case 32: nSubrects = rreEncode32(imageBuf, w, h, &mos); break;
+ }
+
+ if (nSubrects < 0) {
+ return writer->writeRect(r, encodingRaw, ig, actual);
+ }
+
+ writer->startRect(r, encodingRRE);
+ rdr::OutStream* os = writer->getOutStream();
+ os->writeU32(nSubrects);
+ os->writeBytes(mos.data(), mos.length());
+ writer->endRect();
+ return true;
+}
diff --git a/rfb/RREEncoder.h b/rfb/RREEncoder.h
new file mode 100644
index 00000000..40b203e3
--- /dev/null
+++ b/rfb/RREEncoder.h
@@ -0,0 +1,37 @@
+/* Copyright (C) 2002-2004 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_RREENCODER_H__
+#define __RFB_RREENCODER_H__
+
+#include <rdr/MemOutStream.h>
+#include <rfb/Encoder.h>
+
+namespace rfb {
+
+ class RREEncoder : public Encoder {
+ public:
+ static Encoder* create(SMsgWriter* writer);
+ virtual bool writeRect(const Rect& r, ImageGetter* ig, Rect* actual);
+ virtual ~RREEncoder();
+ private:
+ RREEncoder(SMsgWriter* writer);
+ SMsgWriter* writer;
+ rdr::MemOutStream mos;
+ };
+}
+#endif
diff --git a/rfb/RawDecoder.cxx b/rfb/RawDecoder.cxx
new file mode 100644
index 00000000..5a5d62b7
--- /dev/null
+++ b/rfb/RawDecoder.cxx
@@ -0,0 +1,55 @@
+/* Copyright (C) 2002-2003 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 <rdr/InStream.h>
+#include <rfb/CMsgReader.h>
+#include <rfb/CMsgHandler.h>
+#include <rfb/RawDecoder.h>
+
+using namespace rfb;
+
+Decoder* RawDecoder::create(CMsgReader* reader)
+{
+ return new RawDecoder(reader);
+}
+
+RawDecoder::RawDecoder(CMsgReader* reader_) : reader(reader_)
+{
+}
+
+RawDecoder::~RawDecoder()
+{
+}
+
+void RawDecoder::readRect(const Rect& r, CMsgHandler* handler)
+{
+ int x = r.tl.x;
+ int y = r.tl.y;
+ int w = r.width();
+ int h = r.height();
+ int nPixels;
+ rdr::U8* imageBuf = reader->getImageBuf(w, w*h, &nPixels);
+ int bytesPerRow = w * (reader->bpp() / 8);
+ while (h > 0) {
+ int nRows = nPixels / w;
+ if (nRows > h) nRows = h;
+ reader->getInStream()->readBytes(imageBuf, nRows * bytesPerRow);
+ handler->imageRect(Rect(x, y, x+w, y+nRows), imageBuf);
+ h -= nRows;
+ y += nRows;
+ }
+}
diff --git a/rfb/RawDecoder.h b/rfb/RawDecoder.h
new file mode 100644
index 00000000..b3dd9b7c
--- /dev/null
+++ b/rfb/RawDecoder.h
@@ -0,0 +1,35 @@
+/* Copyright (C) 2002-2003 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_RAWDECODER_H__
+#define __RFB_RAWDECODER_H__
+
+#include <rfb/Decoder.h>
+
+namespace rfb {
+
+ class RawDecoder : public Decoder {
+ public:
+ static Decoder* create(CMsgReader* reader);
+ virtual void readRect(const Rect& r, CMsgHandler* handler);
+ virtual ~RawDecoder();
+ private:
+ RawDecoder(CMsgReader* reader);
+ CMsgReader* reader;
+ };
+}
+#endif
diff --git a/rfb/RawEncoder.cxx b/rfb/RawEncoder.cxx
new file mode 100644
index 00000000..d758ec6d
--- /dev/null
+++ b/rfb/RawEncoder.cxx
@@ -0,0 +1,59 @@
+/* Copyright (C) 2002-2004 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 <rdr/OutStream.h>
+#include <rfb/ImageGetter.h>
+#include <rfb/encodings.h>
+#include <rfb/SMsgWriter.h>
+#include <rfb/RawEncoder.h>
+
+using namespace rfb;
+
+Encoder* RawEncoder::create(SMsgWriter* writer)
+{
+ return new RawEncoder(writer);
+}
+
+RawEncoder::RawEncoder(SMsgWriter* writer_) : writer(writer_)
+{
+}
+
+RawEncoder::~RawEncoder()
+{
+}
+
+bool RawEncoder::writeRect(const Rect& r, ImageGetter* ig, Rect* actual)
+{
+ int x = r.tl.x;
+ int y = r.tl.y;
+ int w = r.width();
+ int h = r.height();
+ int nPixels;
+ rdr::U8* imageBuf = writer->getImageBuf(w, w*h, &nPixels);
+ int bytesPerRow = w * (writer->bpp() / 8);
+ writer->startRect(r, encodingRaw);
+ while (h > 0) {
+ int nRows = nPixels / w;
+ if (nRows > h) nRows = h;
+ ig->getImage(imageBuf, Rect(x, y, x+w, y+nRows));
+ writer->getOutStream()->writeBytes(imageBuf, nRows * bytesPerRow);
+ h -= nRows;
+ y += nRows;
+ }
+ writer->endRect();
+ return true;
+}
diff --git a/rfb/RawEncoder.h b/rfb/RawEncoder.h
new file mode 100644
index 00000000..c8b6a62d
--- /dev/null
+++ b/rfb/RawEncoder.h
@@ -0,0 +1,35 @@
+/* Copyright (C) 2002-2004 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_RAWENCODER_H__
+#define __RFB_RAWENCODER_H__
+
+#include <rfb/Encoder.h>
+
+namespace rfb {
+
+ class RawEncoder : public Encoder {
+ public:
+ static Encoder* create(SMsgWriter* writer);
+ virtual bool writeRect(const Rect& r, ImageGetter* ig, Rect* actual);
+ virtual ~RawEncoder();
+ private:
+ RawEncoder(SMsgWriter* writer);
+ SMsgWriter* writer;
+ };
+}
+#endif
diff --git a/rfb/Rect.h b/rfb/Rect.h
new file mode 100644
index 00000000..ee43e669
--- /dev/null
+++ b/rfb/Rect.h
@@ -0,0 +1,112 @@
+/* Copyright (C) 2002-2003 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.
+ */
+
+// rfb::Rect and rfb::Point structures
+
+#ifndef __RFB_RECT_INCLUDED__
+#define __RFB_RECT_INCLUDED__
+
+#ifndef max
+#define max(a,b) (((a) > (b)) ? (a) : (b))
+#endif
+
+#ifndef min
+#define min(a,b) (((a) < (b)) ? (a) : (b))
+#endif
+
+namespace rfb {
+
+ // rfb::Point
+ //
+ // Represents a point in 2D space, by X and Y coordinates.
+ // Can also be used to represent a delta, or offset, between
+ // two Points.
+ // Functions are provided to allow Points to be compared for
+ // equality and translated by a supplied offset.
+ // Functions are also provided to negate offset Points.
+
+ struct Point {
+ Point() : x(0), y(0) {}
+ Point(int x_, int y_) : x(x_), y(y_) {}
+ inline Point negate() const {return Point(-x, -y);}
+ inline bool equals(const Point &p) const {return x==p.x && y==p.y;}
+ inline Point translate(const Point &p) const {return Point(x+p.x, y+p.y);}
+ inline Point subtract(const Point &p) const {return Point(x-p.x, y-p.y);}
+ int x, y;
+ };
+
+ // rfb::Rect
+ //
+ // Represents a rectangular region defined by its top-left (tl)
+ // and bottom-right (br) Points.
+ // Rects may be compared for equality, checked to determine whether
+ // or not they are empty, cleared (made empty), or intersected with
+ // one another. The bounding rectangle of two existing Rects
+ // may be calculated, as may the area of a Rect.
+ // Rects may also be translated, in the same way as Points, by
+ // an offset specified in a Point structure.
+
+ struct Rect {
+ Rect() {}
+ Rect(Point tl_, Point br_) : tl(tl_), br(br_) {}
+ Rect(int x1, int y1, int x2, int y2) : tl(x1, y1), br(x2, y2) {}
+ inline void setXYWH(int x, int y, int w, int h) {
+ tl.x = x; tl.y = y; br.x = x+w; br.y = y+h;
+ }
+ inline Rect intersect(const Rect &r) const {
+ Rect result;
+ result.tl.x = max(tl.x, r.tl.x);
+ result.tl.y = max(tl.y, r.tl.y);
+ result.br.x = max(min(br.x, r.br.x), result.tl.x);
+ result.br.y = max(min(br.y, r.br.y), result.tl.y);
+ return result;
+ }
+ inline Rect union_boundary(const Rect &r) const {
+ if (r.is_empty()) return *this;
+ if (is_empty()) return r;
+ Rect result;
+ result.tl.x = min(tl.x, r.tl.x);
+ result.tl.y = min(tl.y, r.tl.y);
+ result.br.x = max(br.x, r.br.x);
+ result.br.y = max(br.y, r.br.y);
+ return result;
+ }
+ inline Rect translate(const Point &p) const {
+ return Rect(tl.translate(p), br.translate(p));
+ }
+ inline bool equals(const Rect &r) const {return r.tl.equals(tl) && r.br.equals(br);}
+ inline bool is_empty() const {return (tl.x >= br.x) || (tl.y >= br.y);}
+ inline void clear() {tl = Point(); br = Point();}
+ inline bool enclosed_by(const Rect &r) const {
+ return (tl.x>=r.tl.x) && (tl.y>=r.tl.y) && (br.x<=r.br.x) && (br.y<=r.br.y);
+ }
+ inline bool overlaps(const Rect &r) const {
+ return tl.x < r.br.x && tl.y < r.br.y && br.x > r.tl.x && br.y > r.tl.y;
+ }
+ inline unsigned int area() const {return is_empty() ? 0 : (br.x-tl.x)*(br.y-tl.y);}
+ inline Point dimensions() const {return Point(width(), height());}
+ inline int width() const {return br.x-tl.x;}
+ inline int height() const {return br.y-tl.y;}
+ inline bool contains(const Point &p) const {
+ return (tl.x<=p.x) && (tl.y<=p.y) && (br.x>p.x) && (br.y>p.y);
+ }
+ Point tl;
+ Point br;
+ };
+}
+#endif // __RFB_RECT_INCLUDED__
diff --git a/rfb/Region.cxx b/rfb/Region.cxx
new file mode 100644
index 00000000..bbcc892c
--- /dev/null
+++ b/rfb/Region.cxx
@@ -0,0 +1,248 @@
+/* Copyright (C) 2002-2004 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.
+ */
+
+// Cross-platform Region class based on the X11 region implementation. Note
+// that for efficiency this code manipulates the Xlib region structure
+// directly. Apart from the layout of the structure, there is one other key
+// assumption made: a Region returned from XCreateRegion must always have its
+// rects member allocated so that there is space for at least one rectangle.
+//
+
+#include <rfb/Region.h>
+#include <Xregion/Xregion.h>
+#include <Xregion/region.h>
+#include <assert.h>
+#include <stdio.h>
+
+// A _RectRegion must never be passed as a return parameter to the Xlib region
+// operations. This is because for efficiency its "rects" member has not been
+// allocated with Xmalloc. It is however safe to pass it as an input
+// parameter.
+
+class _RectRegion {
+public:
+ _RectRegion(const rfb::Rect& r) {
+ region.rects = &region.extents;
+ region.numRects = 1;
+ region.extents.x1 = r.tl.x;
+ region.extents.y1 = r.tl.y;
+ region.extents.x2 = r.br.x;
+ region.extents.y2 = r.br.y;
+ region.size = 1;
+ if (r.is_empty())
+ region.numRects = 0;
+ }
+ REGION region;
+};
+
+
+rfb::Region::Region() {
+ xrgn = XCreateRegion();
+ assert(xrgn);
+}
+
+rfb::Region::Region(const Rect& r) {
+ xrgn = XCreateRegion();
+ assert(xrgn);
+ reset(r);
+}
+
+rfb::Region::Region(const rfb::Region& r) {
+ xrgn = XCreateRegion();
+ assert(xrgn);
+ XUnionRegion(xrgn, r.xrgn, xrgn);
+}
+
+rfb::Region::~Region() {
+ XDestroyRegion(xrgn);
+}
+
+rfb::Region& rfb::Region::operator=(const rfb::Region& r) {
+ clear();
+ XUnionRegion(xrgn, r.xrgn, xrgn);
+ return *this;
+}
+
+void rfb::Region::clear() {
+ xrgn->numRects = 0;
+ xrgn->extents.x1 = 0;
+ xrgn->extents.y1 = 0;
+ xrgn->extents.x2 = 0;
+ xrgn->extents.y2 = 0;
+}
+
+void rfb::Region::reset(const Rect& r) {
+ if (r.is_empty()) {
+ clear();
+ } else {
+ xrgn->numRects = 1;
+ xrgn->rects[0].x1 = xrgn->extents.x1 = r.tl.x;
+ xrgn->rects[0].y1 = xrgn->extents.y1 = r.tl.y;
+ xrgn->rects[0].x2 = xrgn->extents.x2 = r.br.x;
+ xrgn->rects[0].y2 = xrgn->extents.y2 = r.br.y;
+ }
+}
+
+void rfb::Region::translate(const Point& delta) {
+ XOffsetRegion(xrgn, delta.x, delta.y);
+}
+
+void rfb::Region::setOrderedRects(const std::vector<Rect>& rects) {
+ clear();
+ std::vector<Rect>::const_iterator i;
+ for (i=rects.begin(); i != rects.end(); i++) {
+ _RectRegion rr(*i);
+ XUnionRegion(xrgn, &rr.region, xrgn);
+ }
+}
+
+void rfb::Region::setExtentsAndOrderedRects(const ShortRect* extents,
+ int nRects, const ShortRect* rects)
+{
+ if (xrgn->size < nRects)
+ {
+ BOX* prevRects = xrgn->rects;
+ xrgn->rects = (BOX*)Xrealloc((char*)xrgn->rects, nRects * sizeof(BOX));
+ if (!xrgn->rects) {
+ fprintf(stderr,"Xrealloc failed\n");
+ Xfree(prevRects);
+ return;
+ }
+ xrgn->size = nRects;
+ }
+
+ xrgn->numRects = nRects;
+ xrgn->extents.x1 = extents->x1;
+ xrgn->extents.y1 = extents->y1;
+ xrgn->extents.x2 = extents->x2;
+ xrgn->extents.y2 = extents->y2;
+ for (int i = 0; i < nRects; i++) {
+ xrgn->rects[i].x1 = rects[i].x1;
+ xrgn->rects[i].y1 = rects[i].y1;
+ xrgn->rects[i].x2 = rects[i].x2;
+ xrgn->rects[i].y2 = rects[i].y2;
+ }
+}
+
+void rfb::Region::copyFrom(const rfb::Region& r) {
+ XUnionRegion(r.xrgn, r.xrgn, xrgn);
+}
+
+void rfb::Region::assign_intersect(const rfb::Region& r) {
+ XIntersectRegion(xrgn, r.xrgn, xrgn);
+}
+
+void rfb::Region::assign_union(const rfb::Region& r) {
+ XUnionRegion(xrgn, r.xrgn, xrgn);
+}
+
+void rfb::Region::assign_subtract(const rfb::Region& r) {
+ XSubtractRegion(xrgn, r.xrgn, xrgn);
+}
+
+rfb::Region rfb::Region::intersect(const rfb::Region& r) const {
+ rfb::Region ret;
+ XIntersectRegion(xrgn, r.xrgn, ret.xrgn);
+ return ret;
+}
+
+rfb::Region rfb::Region::union_(const rfb::Region& r) const {
+ rfb::Region ret;
+ XUnionRegion(xrgn, r.xrgn, ret.xrgn);
+ return ret;
+}
+
+rfb::Region rfb::Region::subtract(const rfb::Region& r) const {
+ rfb::Region ret;
+ XSubtractRegion(xrgn, r.xrgn, ret.xrgn);
+ return ret;
+}
+
+bool rfb::Region::equals(const rfb::Region& r) const {
+ return XEqualRegion(xrgn, r.xrgn);
+}
+
+int rfb::Region::numRects() const {
+ return xrgn->numRects;
+}
+
+bool rfb::Region::get_rects(std::vector<Rect>* rects,
+ bool left2right, bool topdown, int maxArea) const
+{
+ int nRects = xrgn->numRects;
+ int xInc = left2right ? 1 : -1;
+ int yInc = topdown ? 1 : -1;
+ int i = topdown ? 0 : nRects-1;
+ rects->clear();
+ rects->reserve(nRects);
+
+ while (nRects > 0) {
+ int firstInNextBand = i;
+ int nRectsInBand = 0;
+
+ while (nRects > 0 && xrgn->rects[firstInNextBand].y1 == xrgn->rects[i].y1)
+ {
+ firstInNextBand += yInc;
+ nRects--;
+ nRectsInBand++;
+ }
+
+ if (xInc != yInc)
+ i = firstInNextBand - yInc;
+
+ while (nRectsInBand > 0) {
+ int y = xrgn->rects[i].y1;
+ int h = maxArea / (xrgn->rects[i].x2 - xrgn->rects[i].x1);
+ if (!h) h = xrgn->rects[i].y2 - y;
+ do {
+ if (h > xrgn->rects[i].y2 - y)
+ h = xrgn->rects[i].y2 - y;
+ Rect r(xrgn->rects[i].x1, y, xrgn->rects[i].x2, y+h);
+ rects->push_back(r);
+ y += h;
+ } while (y < xrgn->rects[i].y2);
+ i += xInc;
+ nRectsInBand--;
+ }
+
+ i = firstInNextBand;
+ }
+
+ return !rects->empty();
+}
+
+rfb::Rect rfb::Region::get_bounding_rect() const {
+ return Rect(xrgn->extents.x1, xrgn->extents.y1,
+ xrgn->extents.x2, xrgn->extents.y2);
+}
+
+
+void rfb::Region::debug_print(const char* prefix) const
+{
+ fprintf(stderr,"%s num rects %3ld extents %3d,%3d %3dx%3d\n",
+ prefix, xrgn->numRects, xrgn->extents.x1, xrgn->extents.y1,
+ xrgn->extents.x2-xrgn->extents.x1,
+ xrgn->extents.y2-xrgn->extents.y1);
+
+ for (int i = 0; i < xrgn->numRects; i++) {
+ fprintf(stderr," rect %3d,%3d %3dx%3d\n",
+ xrgn->rects[i].x1, xrgn->rects[i].y1,
+ xrgn->rects[i].x2-xrgn->rects[i].x1,
+ xrgn->rects[i].y2-xrgn->rects[i].y1);
+ }
+}
diff --git a/rfb/Region.h b/rfb/Region.h
new file mode 100644
index 00000000..8fb9889e
--- /dev/null
+++ b/rfb/Region.h
@@ -0,0 +1,84 @@
+/* Copyright (C) 2002-2004 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.
+ */
+
+// Cross-platform Region class based on the X11 region implementation
+
+#ifndef __RFB_REGION_INCLUDED__
+#define __RFB_REGION_INCLUDED__
+
+#include <rfb/Rect.h>
+#include <vector>
+
+struct _XRegion;
+
+namespace rfb {
+
+ struct ShortRect {
+ short x1, y1, x2, y2;
+ };
+
+ class Region {
+ public:
+ // Create an empty region
+ Region();
+ // Create a rectangular region
+ Region(const Rect& r);
+
+ Region(const Region& r);
+ Region &operator=(const Region& src);
+
+ ~Region();
+
+ // the following methods alter the region in place:
+
+ void clear();
+ void reset(const Rect& r);
+ void translate(const rfb::Point& delta);
+ void setOrderedRects(const std::vector<Rect>& rects);
+ void setExtentsAndOrderedRects(const ShortRect* extents, int nRects,
+ const ShortRect* rects);
+ void copyFrom(const Region& r);
+
+ void assign_intersect(const Region& r);
+ void assign_union(const Region& r);
+ void assign_subtract(const Region& r);
+
+ // the following three operations return a new region:
+
+ Region intersect(const Region& r) const;
+ Region union_(const Region& r) const;
+ Region subtract(const Region& r) const;
+
+ bool equals(const Region& b) const;
+ int numRects() const;
+ bool is_empty() const { return numRects() == 0; }
+
+ bool get_rects(std::vector<Rect>* rects, bool left2right=true,
+ bool topdown=true, int maxArea=0) const;
+ Rect get_bounding_rect() const;
+
+ void debug_print(const char *prefix) const;
+
+ protected:
+
+ struct _XRegion* xrgn;
+ };
+
+};
+
+#endif // __RFB_REGION_INCLUDED__
diff --git a/rfb/SConnection.cxx b/rfb/SConnection.cxx
new file mode 100644
index 00000000..e969ed8c
--- /dev/null
+++ b/rfb/SConnection.cxx
@@ -0,0 +1,330 @@
+/* Copyright (C) 2002-2004 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 <stdio.h>
+#include <string.h>
+#include <rfb/Exception.h>
+#include <rfb/secTypes.h>
+#include <rfb/SMsgReaderV3.h>
+#include <rfb/SMsgWriterV3.h>
+#include <rfb/SSecurity.h>
+#include <rfb/SConnection.h>
+#include <rfb/ServerCore.h>
+
+#include <rfb/LogWriter.h>
+
+using namespace rfb;
+
+static LogWriter vlog("SConnection");
+
+// AccessRights values
+const SConnection::AccessRights SConnection::AccessView = 0x0001;
+const SConnection::AccessRights SConnection::AccessKeyEvents = 0x0002;
+const SConnection::AccessRights SConnection::AccessPtrEvents = 0x0004;
+const SConnection::AccessRights SConnection::AccessCutText = 0x0008;
+const SConnection::AccessRights SConnection::AccessDefault = 0x03ff;
+const SConnection::AccessRights SConnection::AccessNoQuery = 0x0400;
+const SConnection::AccessRights SConnection::AccessFull = 0xffff;
+
+
+SConnection::SConnection()
+ : readyForSetColourMapEntries(false),
+ is(0), os(0), reader_(0), writer_(0),
+ nSecTypes(0), security(0), state_(RFBSTATE_UNINITIALISED)
+{
+ defaultMajorVersion = 3;
+ defaultMinorVersion = 8;
+ if (rfb::Server::protocol3_3)
+ defaultMinorVersion = 3;
+
+ cp.setVersion(defaultMajorVersion, defaultMinorVersion);
+}
+
+SConnection::~SConnection()
+{
+ if (security) security->destroy();
+ deleteReaderAndWriter();
+}
+
+void SConnection::deleteReaderAndWriter()
+{
+ delete reader_;
+ reader_ = 0;
+ delete writer_;
+ writer_ = 0;
+}
+
+void SConnection::setStreams(rdr::InStream* is_, rdr::OutStream* os_)
+{
+ is = is_;
+ os = os_;
+}
+
+void SConnection::addSecType(rdr::U8 secType)
+{
+ if (nSecTypes == maxSecTypes)
+ throw Exception("too many security types");
+ secTypes[nSecTypes++] = secType;
+ vlog.debug("Offering security type %s(%d)",
+ secTypeName(secType),secType);
+}
+
+void SConnection::initialiseProtocol()
+{
+ cp.writeVersion(os);
+ state_ = RFBSTATE_PROTOCOL_VERSION;
+}
+
+void SConnection::processMsg()
+{
+ switch (state_) {
+ case RFBSTATE_PROTOCOL_VERSION: processVersionMsg(); break;
+ case RFBSTATE_SECURITY_TYPE: processSecurityTypeMsg(); break;
+ case RFBSTATE_SECURITY: processSecurityMsg(); break;
+ case RFBSTATE_INITIALISATION: processInitMsg(); break;
+ case RFBSTATE_NORMAL: reader_->readMsg(); break;
+ case RFBSTATE_QUERYING:
+ throw Exception("SConnection::processMsg: bogus data from client while "
+ "querying");
+ case RFBSTATE_UNINITIALISED:
+ throw Exception("SConnection::processMsg: not initialised yet?");
+ default:
+ throw Exception("SConnection::processMsg: invalid state");
+ }
+}
+
+void SConnection::processVersionMsg()
+{
+ vlog.debug("reading protocol version");
+ bool done;
+ if (!cp.readVersion(is, &done)) {
+ state_ = RFBSTATE_INVALID;
+ throw Exception("reading version failed: not an RFB client?");
+ }
+ if (!done) return;
+
+ vlog.info("Client needs protocol version %d.%d",
+ cp.majorVersion, cp.minorVersion);
+
+ if (cp.majorVersion != 3) {
+ // unknown protocol version
+ char msg[256];
+ sprintf(msg,"Error: client needs protocol version %d.%d, server has %d.%d",
+ cp.majorVersion, cp.minorVersion,
+ defaultMajorVersion, defaultMinorVersion);
+ throwConnFailedException(msg);
+ }
+
+ if (cp.minorVersion != 3 && cp.minorVersion != 7 && cp.minorVersion != 8) {
+ vlog.error("Client uses unofficial protocol version %d.%d",
+ cp.majorVersion,cp.minorVersion);
+ if (cp.minorVersion >= 8)
+ cp.minorVersion = 8;
+ else if (cp.minorVersion == 7)
+ cp.minorVersion = 7;
+ else
+ cp.minorVersion = 3;
+ vlog.error("Assuming compatibility with version %d.%d",
+ cp.majorVersion,cp.minorVersion);
+ }
+
+ versionReceived();
+
+ if (cp.isVersion(3,3)) {
+
+ // cope with legacy 3.3 client only if "no authentication" or "vnc
+ // authentication" is supported.
+
+ int i;
+ for (i = 0; i < nSecTypes; i++) {
+ if (secTypes[i] == secTypeNone || secTypes[i] == secTypeVncAuth) break;
+ }
+ if (i == nSecTypes) {
+ char msg[256];
+ sprintf(msg,"No supported security type for %d.%d client",
+ cp.majorVersion, cp.minorVersion);
+ throwConnFailedException(msg);
+ }
+
+ os->writeU32(secTypes[i]);
+ if (secTypes[i] == secTypeNone) os->flush();
+ state_ = RFBSTATE_SECURITY;
+ security = getSSecurity(secTypes[i]);
+ processSecurityMsg();
+ return;
+ }
+
+ // list supported security types for >=3.7 clients
+
+ if (nSecTypes == 0)
+ throwConnFailedException("No supported security types");
+
+ os->writeU8(nSecTypes);
+ os->writeBytes(secTypes, nSecTypes);
+ os->flush();
+ state_ = RFBSTATE_SECURITY_TYPE;
+}
+
+
+void SConnection::processSecurityTypeMsg()
+{
+ vlog.debug("processing security type message");
+ int secType = is->readU8();
+ vlog.info("Client requests security type %s(%d)",
+ secTypeName(secType),secType);
+ int i;
+ for (i = 0; i < nSecTypes; i++) {
+ if (secType == secTypes[i]) break;
+ }
+ if (i == nSecTypes) {
+ char msg[256];
+ sprintf(msg,"Security type %s(%d) from client not supported",
+ secTypeName(secType),secType);
+ throwConnFailedException(msg);
+ }
+ state_ = RFBSTATE_SECURITY;
+ security = getSSecurity(secType);
+ processSecurityMsg();
+}
+
+void SConnection::processSecurityMsg()
+{
+ vlog.debug("processing security message");
+ bool done;
+ bool ok = security->processMsg(this, &done);
+ if (done) {
+ state_ = RFBSTATE_QUERYING;
+ if (ok) {
+ queryConnection(security->getUserName());
+ } else {
+ const char* failureMsg = security->failureMessage();
+ if (!failureMsg) failureMsg = "Authentication failure";
+ approveConnection(false, failureMsg);
+ }
+ }
+ if (!ok) {
+ state_ = RFBSTATE_INVALID;
+ authFailure();
+ throw AuthFailureException();
+ }
+}
+
+void SConnection::processInitMsg()
+{
+ vlog.debug("reading client initialisation");
+ reader_->readClientInit();
+}
+
+void SConnection::throwConnFailedException(const char* msg)
+{
+ vlog.info(msg);
+ if (state_ == RFBSTATE_PROTOCOL_VERSION) {
+ if (cp.majorVersion == 3 && cp.minorVersion == 3) {
+ os->writeU32(0);
+ os->writeString(msg);
+ os->flush();
+ } else {
+ os->writeU8(0);
+ os->writeString(msg);
+ os->flush();
+ }
+ }
+ state_ = RFBSTATE_INVALID;
+ throw ConnFailedException(msg);
+}
+
+void SConnection::writeConnFailedFromScratch(const char* msg,
+ rdr::OutStream* os)
+{
+ os->writeBytes("RFB 003.003\n", 12);
+ os->writeU32(0);
+ os->writeString(msg);
+ os->flush();
+}
+
+void SConnection::versionReceived()
+{
+}
+
+void SConnection::authSuccess()
+{
+}
+
+void SConnection::authFailure()
+{
+}
+
+void SConnection::queryConnection(const char* userName)
+{
+ approveConnection(true);
+}
+
+void SConnection::approveConnection(bool accept, const char* reason)
+{
+ if (state_ != RFBSTATE_QUERYING)
+ throw Exception("SConnection::approveConnection: invalid state");
+
+ if (!reason) reason = "Authentication failure";
+
+ if (!cp.beforeVersion(3,8) || security->getType() != secTypeNone) {
+ if (accept) {
+ os->writeU32(secResultOK);
+ } else {
+ os->writeU32(secResultFailed);
+ if (!cp.beforeVersion(3,8)) // 3.8 onwards have failure message
+ os->writeString(reason);
+ }
+ os->flush();
+ }
+
+ if (accept) {
+ state_ = RFBSTATE_INITIALISATION;
+ reader_ = new SMsgReaderV3(this, is);
+ writer_ = new SMsgWriterV3(&cp, os);
+ authSuccess();
+ } else {
+ state_ = RFBSTATE_INVALID;
+ authFailure();
+ throw AuthFailureException(reason);
+ }
+}
+
+void SConnection::setInitialColourMap()
+{
+}
+
+void SConnection::clientInit(bool shared)
+{
+ writer_->writeServerInit();
+ state_ = RFBSTATE_NORMAL;
+}
+
+void SConnection::setPixelFormat(const PixelFormat& pf)
+{
+ SMsgHandler::setPixelFormat(pf);
+ readyForSetColourMapEntries = true;
+}
+
+void SConnection::framebufferUpdateRequest(const Rect& r, bool incremental)
+{
+ if (!readyForSetColourMapEntries) {
+ readyForSetColourMapEntries = true;
+ if (!cp.pf().trueColour) {
+ setInitialColourMap();
+ }
+ }
+}
diff --git a/rfb/SConnection.h b/rfb/SConnection.h
new file mode 100644
index 00000000..19453c62
--- /dev/null
+++ b/rfb/SConnection.h
@@ -0,0 +1,207 @@
+/* Copyright (C) 2002-2004 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.
+ */
+//
+// SConnection - class on the server side representing a connection to a
+// client. A derived class should override methods appropriately.
+//
+
+#ifndef __RFB_SCONNECTION_H__
+#define __RFB_SCONNECTION_H__
+
+#include <rdr/InStream.h>
+#include <rdr/OutStream.h>
+#include <rfb/SMsgHandler.h>
+
+namespace rfb {
+
+ class SMsgReader;
+ class SMsgWriter;
+ class SSecurity;
+
+ class SConnection : public SMsgHandler {
+ public:
+
+ SConnection();
+ virtual ~SConnection();
+
+ // Methods to initialise the connection
+
+ // setStreams() sets the streams to be used for the connection. These must
+ // be set before initialiseProtocol() and processMsg() are called. The
+ // SSecurity object may call setStreams() again to provide alternative
+ // streams over which the RFB protocol is sent (i.e. encrypting/decrypting
+ // streams). Ownership of the streams remains with the caller
+ // (i.e. SConnection will not delete them).
+ void setStreams(rdr::InStream* is, rdr::OutStream* os);
+
+ // addSecType() should be called once for each security type which the
+ // server supports to this client.
+ void addSecType(rdr::U8 secType);
+
+ // initialiseProtocol() should be called once the streams and security
+ // types are set. Subsequently, processMsg() should be called whenever
+ // there is data to read on the InStream.
+ void initialiseProtocol();
+
+ // processMsg() should be called whenever there is data to read on the
+ // InStream. You must have called initialiseProtocol() first.
+ void processMsg();
+
+ // approveConnection() is called to either accept or reject the connection.
+ // If accept is false, the reason string gives the reason for the
+ // rejection. It can either be called directly from queryConnection() or
+ // later, after queryConnection() has returned. It can only be called when
+ // in state RFBSTATE_QUERYING. On rejection, an AuthFailureException is
+ // thrown, so this must be handled appropriately by the caller.
+ void approveConnection(bool accept, const char* reason=0);
+
+
+ // Methods to be overridden in a derived class
+
+ // versionReceived() indicates that the version number has just been read
+ // from the client. The version will already have been "cooked"
+ // to deal with unknown/bogus viewer protocol numbers.
+ virtual void versionReceived();
+
+ // getSSecurity() gets the SSecurity object for the given type. The type
+ // is guaranteed to be one of the secTypes passed in to addSecType(). The
+ // SSecurity object's destroy() method will be called by the SConnection
+ // from its destructor.
+ virtual SSecurity* getSSecurity(int secType)=0;
+
+ // authSuccess() is called when authentication has succeeded.
+ virtual void authSuccess();
+
+ // authFailure() is called when authentication has failed. This method is
+ // not normally overridden since an exception is thrown anyway.
+ virtual void authFailure();
+
+ // queryConnection() is called when authentication has succeeded, but
+ // before informing the client. It can be overridden to query a local user
+ // to accept the incoming connection, for example. The userName argument
+ // is the name of the user making the connection, or null (note that the
+ // storage for userName is owned by the caller). The connection must be
+ // accepted or rejected by calling approveConnection(), either directly
+ // from queryConnection() or some time later.
+ virtual void queryConnection(const char* userName);
+
+ // clientInit() is called when the ClientInit message is received. The
+ // derived class must call on to SConnection::clientInit().
+ virtual void clientInit(bool shared);
+
+ // setPixelFormat() is called when a SetPixelFormat message is received.
+ // The derived class must call on to SConnection::setPixelFormat().
+ virtual void setPixelFormat(const PixelFormat& pf);
+
+ // framebufferUpdateRequest() is called when a FramebufferUpdateRequest
+ // message is received. The derived class must call on to
+ // SConnection::framebufferUpdateRequest().
+ virtual void framebufferUpdateRequest(const Rect& r, bool incremental);
+
+ // setInitialColourMap() is called when the client needs an initial
+ // SetColourMapEntries message. In fact this only happens when the client
+ // accepts the server's default pixel format and it uses a colour map.
+ virtual void setInitialColourMap();
+
+ // setAccessRights() allows a security package to limit the access rights
+ // of a VNCSConnectionST to the server. How the access rights are treated
+ // is up to the derived class.
+
+ typedef rdr::U16 AccessRights;
+ static const AccessRights AccessView; // View display contents
+ static const AccessRights AccessKeyEvents; // Send key events
+ static const AccessRights AccessPtrEvents; // Send pointer events
+ static const AccessRights AccessCutText; // Send/receive clipboard events
+ static const AccessRights AccessDefault; // The default rights, INCLUDING FUTURE ONES
+ static const AccessRights AccessNoQuery; // Connect without local user accepting
+ static const AccessRights AccessFull; // All of the available AND FUTURE rights
+ virtual void setAccessRights(AccessRights ar) = 0;
+
+ // Other methods
+
+ // authenticated() returns true if the client has authenticated
+ // successfully.
+ bool authenticated() { return (state_ == RFBSTATE_INITIALISATION ||
+ state_ == RFBSTATE_NORMAL); }
+
+ // deleteReaderAndWriter() deletes the reader and writer associated with
+ // this connection. This may be useful if you want to delete the streams
+ // before deleting the SConnection to make sure that no attempt by the
+ // SConnection is made to read or write.
+ // XXX Do we really need this at all???
+ void deleteReaderAndWriter();
+
+ // throwConnFailedException() prints a message to the log, sends a conn
+ // failed message to the client (if possible) and throws a
+ // ConnFailedException.
+ void throwConnFailedException(const char* msg);
+
+ // writeConnFailedFromScratch() sends a conn failed message to an OutStream
+ // without the need to negotiate the protocol version first. It actually
+ // does this by assuming that the client will understand version 3.3 of the
+ // protocol.
+ static void writeConnFailedFromScratch(const char* msg,
+ rdr::OutStream* os);
+
+ SMsgReader* reader() { return reader_; }
+ SMsgWriter* writer() { return writer_; }
+
+ rdr::InStream* getInStream() { return is; }
+ rdr::OutStream* getOutStream() { return os; }
+
+ enum stateEnum {
+ RFBSTATE_UNINITIALISED,
+ RFBSTATE_PROTOCOL_VERSION,
+ RFBSTATE_SECURITY_TYPE,
+ RFBSTATE_SECURITY,
+ RFBSTATE_QUERYING,
+ RFBSTATE_INITIALISATION,
+ RFBSTATE_NORMAL,
+ RFBSTATE_CLOSING,
+ RFBSTATE_INVALID
+ };
+
+ stateEnum state() { return state_; }
+
+ // ssecurity() returns a pointer to this connection's SSecurity object, if any
+ const SSecurity* ssecurity() const { return security; }
+
+ protected:
+ void setState(stateEnum s) { state_ = s; }
+
+ bool readyForSetColourMapEntries;
+
+ private:
+ void processVersionMsg();
+ void processSecurityTypeMsg();
+ void processSecurityMsg();
+ void processInitMsg();
+
+ int defaultMajorVersion, defaultMinorVersion;
+ rdr::InStream* is;
+ rdr::OutStream* os;
+ SMsgReader* reader_;
+ SMsgWriter* writer_;
+ enum { maxSecTypes = 8 };
+ int nSecTypes;
+ rdr::U8 secTypes[maxSecTypes];
+ SSecurity* security;
+ stateEnum state_;
+ };
+}
+#endif
diff --git a/rfb/SDesktop.h b/rfb/SDesktop.h
new file mode 100644
index 00000000..eb17a529
--- /dev/null
+++ b/rfb/SDesktop.h
@@ -0,0 +1,123 @@
+/* Copyright (C) 2002-2004 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.
+ */
+
+/////////////////////////////////////////////////////////////////////////////
+
+// SDesktop is an interface implemented by back-ends, on which callbacks are
+// made by the VNCServer as appropriate for pointer and keyboard events, etc.
+// SDesktop objects are always created before the VNCServer - the SDesktop
+// will be passed a pointer to the VNCServer in the start() call. If a more
+// implementation-specific pointer to the VNCServer is required then this
+// can be provided to the SDesktop via an implementation-specific method.
+//
+// An SDesktop usually has an associated PixelBuffer which it tells the
+// VNCServer via the VNCServer's setPixelBuffer() method. It can do this at
+// any time, but the PixelBuffer MUST be valid by the time the call to start()
+// returns. The PixelBuffer may be set to null again if desired when stop() is
+// called. Note that start() and stop() are guaranteed to be called
+// alternately; there should never be two calls to start() without an
+// intervening stop() and vice-versa.
+//
+
+#ifndef __RFB_SDESKTOP_H__
+#define __RFB_SDESKTOP_H__
+
+#include <rfb/PixelBuffer.h>
+#include <rfb/VNCServer.h>
+#include <rfb/Exception.h>
+
+namespace rfb {
+
+ class VNCServer;
+
+ class SDesktop {
+ public:
+ // start() is called by the server when the first client authenticates
+ // successfully, and can be used to begin any expensive tasks which are not
+ // needed when there are no clients. A valid PixelBuffer must have been
+ // set via the VNCServer's setPixelBuffer() method by the time this call
+ // returns.
+
+ virtual void start(VNCServer* vs) {}
+
+ // stop() is called by the server when there are no longer any
+ // authenticated clients, and therefore the desktop can cease any
+ // expensive tasks. No further calls to the VNCServer passed to start()
+ // can be made once stop has returned.
+
+ virtual void stop() {}
+
+ // pointerEvent(), keyEvent() and clientCutText() are called in response to
+ // the relevant RFB protocol messages from clients.
+
+ virtual void pointerEvent(const Point& pos, rdr::U8 buttonmask) {}
+ virtual void keyEvent(rdr::U32 key, bool down) {}
+ virtual void clientCutText(const char* str, int len) {}
+
+ // framebufferUpdateRequest() is called to let the desktop know that at
+ // least one client has become ready for an update. Desktops can check
+ // whether there are clients ready at any time by calling the VNCServer's
+ // clientsReadyForUpdate() method.
+
+ virtual void framebufferUpdateRequest() {}
+
+ // getFbSize() returns the current dimensions of the framebuffer.
+ // This can be called even while the SDesktop is not start()ed.
+
+ virtual Point getFbSize() = 0;
+
+ protected:
+ virtual ~SDesktop() {}
+ };
+
+ // -=- SStaticDesktop
+ // Trivial implementation of the SDesktop interface, which provides
+ // dummy input handlers and event processing routine, and exports
+ // a plain black desktop of the specified format.
+ class SStaticDesktop : public SDesktop {
+ public:
+ SStaticDesktop(const Point& size) : server(0), buffer(0) {
+ PixelFormat pf;
+ buffer = new ManagedPixelBuffer(pf, size.x, size.y);
+ if (buffer) memset(buffer->data, 0, (pf.bpp/8) * (size.x*size.y));
+ }
+ SStaticDesktop(const Point& size, const PixelFormat& pf) : buffer(0) {
+ buffer = new ManagedPixelBuffer(pf, size.x, size.y);
+ if (buffer) memset(buffer->data, 0, (pf.bpp/8) * (size.x*size.y));
+ }
+ virtual ~SStaticDesktop() {
+ if (buffer) delete buffer;
+ }
+
+ virtual void start(VNCServer* vs) {
+ server = vs;
+ server->setPixelBuffer(buffer);
+ }
+ virtual void stop() {
+ server->setPixelBuffer(0);
+ server = 0;
+ }
+
+ protected:
+ VNCServer* server;
+ ManagedPixelBuffer* buffer;
+ };
+
+};
+
+#endif // __RFB_SDESKTOP_H__
diff --git a/rfb/SMsgHandler.cxx b/rfb/SMsgHandler.cxx
new file mode 100644
index 00000000..d6a139c7
--- /dev/null
+++ b/rfb/SMsgHandler.cxx
@@ -0,0 +1,64 @@
+/* Copyright (C) 2002-2003 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 <rfb/Exception.h>
+#include <rfb/SMsgHandler.h>
+
+using namespace rfb;
+
+SMsgHandler::SMsgHandler()
+{
+}
+
+SMsgHandler::~SMsgHandler()
+{
+}
+
+void SMsgHandler::clientInit(bool shared)
+{
+}
+
+void SMsgHandler::setPixelFormat(const PixelFormat& pf)
+{
+ cp.setPF(pf);
+}
+
+void SMsgHandler::setEncodings(int nEncodings, rdr::U32* encodings)
+{
+ cp.setEncodings(nEncodings, encodings);
+ supportsLocalCursor();
+}
+
+void SMsgHandler::framebufferUpdateRequest(const Rect& r, bool incremental)
+{
+}
+
+void SMsgHandler::keyEvent(rdr::U32 key, bool down)
+{
+}
+
+void SMsgHandler::pointerEvent(int x, int y, int buttonMask)
+{
+}
+
+void SMsgHandler::clientCutText(const char* str, int len)
+{
+}
+
+void SMsgHandler::supportsLocalCursor()
+{
+}
diff --git a/rfb/SMsgHandler.h b/rfb/SMsgHandler.h
new file mode 100644
index 00000000..f326ad43
--- /dev/null
+++ b/rfb/SMsgHandler.h
@@ -0,0 +1,62 @@
+/* Copyright (C) 2002-2003 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.
+ */
+//
+// SMsgHandler - class to handle incoming messages on the server side.
+//
+
+#ifndef __RFB_SMSGHANDLER_H__
+#define __RFB_SMSGHANDLER_H__
+
+#include <rdr/types.h>
+#include <rfb/PixelFormat.h>
+#include <rfb/ConnParams.h>
+#include <rfb/Rect.h>
+
+namespace rdr { class InStream; }
+
+namespace rfb {
+
+ class SMsgHandler {
+ public:
+ SMsgHandler();
+ virtual ~SMsgHandler();
+
+ // The following methods are called as corresponding messages are read. A
+ // derived class should override these methods as desired. Note that for
+ // the setPixelFormat() and setEncodings() methods, a derived class must
+ // call on to SMsgHandler's methods.
+
+ virtual void clientInit(bool shared);
+
+ virtual void setPixelFormat(const PixelFormat& pf);
+ virtual void setEncodings(int nEncodings, rdr::U32* encodings);
+ virtual void framebufferUpdateRequest(const Rect& r, bool incremental);
+ virtual void keyEvent(rdr::U32 key, bool down);
+ virtual void pointerEvent(int x, int y, int buttonMask);
+ virtual void clientCutText(const char* str, int len);
+
+ // supportsLocalCursor() is called whenever the status of
+ // cp.supportsLocalCursor has changed. At the moment this happens on a
+ // setEncodings message, but in the future this may be due to a message
+ // specially for this purpose.
+ virtual void supportsLocalCursor();
+
+ ConnParams cp;
+ };
+}
+#endif
diff --git a/rfb/SMsgReader.cxx b/rfb/SMsgReader.cxx
new file mode 100644
index 00000000..2939aa10
--- /dev/null
+++ b/rfb/SMsgReader.cxx
@@ -0,0 +1,105 @@
+/* Copyright (C) 2002-2003 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 <stdio.h>
+#include <rdr/InStream.h>
+#include <rfb/Exception.h>
+#include <rfb/util.h>
+#include <rfb/SMsgHandler.h>
+#include <rfb/SMsgReader.h>
+
+using namespace rfb;
+
+SMsgReader::SMsgReader(SMsgHandler* handler_, rdr::InStream* is_)
+ : handler(handler_), is(is_)
+{
+}
+
+SMsgReader::~SMsgReader()
+{
+}
+
+void SMsgReader::endMsg()
+{
+}
+
+void SMsgReader::readSetPixelFormat()
+{
+ is->skip(3);
+ PixelFormat pf;
+ pf.read(is);
+ endMsg();
+ handler->setPixelFormat(pf);
+}
+
+void SMsgReader::readSetEncodings()
+{
+ is->skip(1);
+ int nEncodings = is->readU16();
+ rdr::U32* encodings = new rdr::U32[nEncodings];
+ for (int i = 0; i < nEncodings; i++)
+ encodings[i] = is->readU32();
+ endMsg();
+ handler->setEncodings(nEncodings, encodings);
+ delete [] encodings;
+}
+
+void SMsgReader::readFramebufferUpdateRequest()
+{
+ bool inc = is->readU8();
+ int x = is->readU16();
+ int y = is->readU16();
+ int w = is->readU16();
+ int h = is->readU16();
+ endMsg();
+ handler->framebufferUpdateRequest(Rect(x, y, x+w, y+h), inc);
+}
+
+void SMsgReader::readKeyEvent()
+{
+ bool down = is->readU8();
+ is->skip(2);
+ rdr::U32 key = is->readU32();
+ endMsg();
+ handler->keyEvent(key, down);
+}
+
+void SMsgReader::readPointerEvent()
+{
+ int mask = is->readU8();
+ int x = is->readU16();
+ int y = is->readU16();
+ endMsg();
+ handler->pointerEvent(x, y, mask);
+}
+
+
+void SMsgReader::readClientCutText()
+{
+ is->skip(3);
+ int len = is->readU32();
+ if (len > 256*1024) {
+ is->skip(len);
+ fprintf(stderr,"cut text too long (%d bytes) - ignoring\n",len);
+ return;
+ }
+ CharArray ca(len+1);
+ ca.buf[len] = 0;
+ is->readBytes(ca.buf, len);
+ endMsg();
+ handler->clientCutText(ca.buf, len);
+}
diff --git a/rfb/SMsgReader.h b/rfb/SMsgReader.h
new file mode 100644
index 00000000..4d269380
--- /dev/null
+++ b/rfb/SMsgReader.h
@@ -0,0 +1,57 @@
+/* Copyright (C) 2002-2003 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.
+ */
+//
+// SMsgReader - class for reading RFB messages on the server side
+// (i.e. messages from client to server).
+//
+
+#ifndef __RFB_SMSGREADER_H__
+#define __RFB_SMSGREADER_H__
+
+namespace rdr { class InStream; }
+
+namespace rfb {
+ class SMsgHandler;
+
+ class SMsgReader {
+ public:
+ virtual ~SMsgReader();
+
+ virtual void readClientInit()=0;
+
+ // readMsg() reads a message, calling the handler as appropriate.
+ virtual void readMsg()=0;
+
+ rdr::InStream* getInStream() { return is; }
+
+ protected:
+ virtual void readSetPixelFormat();
+ virtual void readSetEncodings();
+ virtual void readFramebufferUpdateRequest();
+ virtual void readKeyEvent();
+ virtual void readPointerEvent();
+ virtual void readClientCutText();
+ virtual void endMsg();
+
+ SMsgReader(SMsgHandler* handler, rdr::InStream* is);
+
+ SMsgHandler* handler;
+ rdr::InStream* is;
+ };
+}
+#endif
diff --git a/rfb/SMsgReaderV3.cxx b/rfb/SMsgReaderV3.cxx
new file mode 100644
index 00000000..e5ae744a
--- /dev/null
+++ b/rfb/SMsgReaderV3.cxx
@@ -0,0 +1,57 @@
+/* Copyright (C) 2002-2003 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 <rfb/PixelFormat.h>
+#include <rfb/msgTypes.h>
+#include <rfb/Exception.h>
+#include <rdr/InStream.h>
+#include <rfb/SMsgReaderV3.h>
+#include <rfb/SMsgHandler.h>
+
+using namespace rfb;
+
+SMsgReaderV3::SMsgReaderV3(SMsgHandler* handler, rdr::InStream* is)
+ : SMsgReader(handler, is)
+{
+}
+
+SMsgReaderV3::~SMsgReaderV3()
+{
+}
+
+void SMsgReaderV3::readClientInit()
+{
+ bool shared = is->readU8();
+ endMsg();
+ handler->clientInit(shared);
+}
+
+void SMsgReaderV3::readMsg()
+{
+ int msgType = is->readU8();
+ switch (msgType) {
+ case msgTypeSetPixelFormat: readSetPixelFormat(); break;
+ case msgTypeSetEncodings: readSetEncodings(); break;
+ case msgTypeFramebufferUpdateRequest: readFramebufferUpdateRequest(); break;
+ case msgTypeKeyEvent: readKeyEvent(); break;
+ case msgTypePointerEvent: readPointerEvent(); break;
+ case msgTypeClientCutText: readClientCutText(); break;
+ default:
+ fprintf(stderr, "unknown message type %d\n", msgType);
+ throw Exception("unknown message type");
+ }
+}
diff --git a/rfb/SMsgReaderV3.h b/rfb/SMsgReaderV3.h
new file mode 100644
index 00000000..28cc7a63
--- /dev/null
+++ b/rfb/SMsgReaderV3.h
@@ -0,0 +1,32 @@
+/* Copyright (C) 2002-2003 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_SMSGREADERV3_H__
+#define __RFB_SMSGREADERV3_H__
+
+#include <rfb/SMsgReader.h>
+
+namespace rfb {
+ class SMsgReaderV3 : public SMsgReader {
+ public:
+ SMsgReaderV3(SMsgHandler* handler, rdr::InStream* is);
+ virtual ~SMsgReaderV3();
+ virtual void readClientInit();
+ virtual void readMsg();
+ };
+}
+#endif
diff --git a/rfb/SMsgWriter.cxx b/rfb/SMsgWriter.cxx
new file mode 100644
index 00000000..ac743826
--- /dev/null
+++ b/rfb/SMsgWriter.cxx
@@ -0,0 +1,180 @@
+/* Copyright (C) 2002-2004 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 <stdio.h>
+#include <assert.h>
+#include <rdr/OutStream.h>
+#include <rfb/msgTypes.h>
+#include <rfb/ColourMap.h>
+#include <rfb/ConnParams.h>
+#include <rfb/UpdateTracker.h>
+#include <rfb/SMsgWriter.h>
+#include <rfb/LogWriter.h>
+
+using namespace rfb;
+
+static LogWriter vlog("SMsgWriter");
+
+SMsgWriter::SMsgWriter(ConnParams* cp_, rdr::OutStream* os_)
+ : imageBufIdealSize(0), cp(cp_), os(os_), lenBeforeRect(0),
+ currentEncoding(0), updatesSent(0), rawBytesEquivalent(0),
+ imageBuf(0), imageBufSize(0)
+{
+ for (unsigned int i = 0; i <= encodingMax; i++) {
+ encoders[i] = 0;
+ bytesSent[i] = 0;
+ rectsSent[i] = 0;
+ }
+}
+
+SMsgWriter::~SMsgWriter()
+{
+ vlog.info("framebuffer updates %d",updatesSent);
+ int bytes = 0;
+ for (unsigned int i = 0; i <= encodingMax; i++) {
+ delete encoders[i];
+ if (i != encodingCopyRect)
+ bytes += bytesSent[i];
+ if (rectsSent[i])
+ vlog.info(" %s rects %d, bytes %d",
+ encodingName(i), rectsSent[i], bytesSent[i]);
+ }
+ vlog.info(" raw bytes equivalent %d, compression ratio %f",
+ rawBytesEquivalent, (double)rawBytesEquivalent / bytes);
+ delete [] imageBuf;
+}
+
+void SMsgWriter::writeSetColourMapEntries(int firstColour, int nColours,
+ ColourMap* cm)
+{
+ startMsg(msgTypeSetColourMapEntries);
+ os->pad(1);
+ os->writeU16(firstColour);
+ os->writeU16(nColours);
+ for (int i = firstColour; i < firstColour+nColours; i++) {
+ int r, g, b;
+ cm->lookup(i, &r, &g, &b);
+ os->writeU16(r);
+ os->writeU16(g);
+ os->writeU16(b);
+ }
+ endMsg();
+}
+
+void SMsgWriter::writeBell()
+{
+ startMsg(msgTypeBell);
+ endMsg();
+}
+
+void SMsgWriter::writeServerCutText(const char* str, int len)
+{
+ startMsg(msgTypeServerCutText);
+ os->pad(3);
+ os->writeU32(len);
+ os->writeBytes(str, len);
+ endMsg();
+}
+
+void SMsgWriter::writeFramebufferUpdate(const UpdateInfo& ui, ImageGetter* ig,
+ Region* updatedRegion)
+{
+ writeFramebufferUpdateStart(ui.numRects());
+ writeRects(ui, ig, updatedRegion);
+ writeFramebufferUpdateEnd();
+}
+
+void SMsgWriter::writeRects(const UpdateInfo& ui, ImageGetter* ig,
+ Region* updatedRegion)
+{
+ std::vector<Rect> rects;
+ std::vector<Rect>::const_iterator i;
+ updatedRegion->copyFrom(ui.changed);
+ updatedRegion->assign_union(ui.copied);
+
+ ui.copied.get_rects(&rects, ui.copy_delta.x <= 0, ui.copy_delta.y <= 0);
+ for (i = rects.begin(); i != rects.end(); i++)
+ writeCopyRect(*i, i->tl.x - ui.copy_delta.x, i->tl.y - ui.copy_delta.y);
+
+ ui.changed.get_rects(&rects);
+ for (i = rects.begin(); i != rects.end(); i++) {
+ Rect actual;
+ if (!writeRect(*i, ig, &actual)) {
+ updatedRegion->assign_subtract(*i);
+ updatedRegion->assign_union(actual);
+ }
+ }
+}
+
+
+bool SMsgWriter::needFakeUpdate()
+{
+ return false;
+}
+
+bool SMsgWriter::writeRect(const Rect& r, ImageGetter* ig, Rect* actual)
+{
+ return writeRect(r, cp->currentEncoding(), ig, actual);
+}
+
+bool SMsgWriter::writeRect(const Rect& r, unsigned int encoding,
+ ImageGetter* ig, Rect* actual)
+{
+ if (!encoders[encoding]) {
+ encoders[encoding] = Encoder::createEncoder(encoding, this);
+ assert(encoders[encoding]);
+ }
+ return encoders[encoding]->writeRect(r, ig, actual);
+}
+
+void SMsgWriter::writeCopyRect(const Rect& r, int srcX, int srcY)
+{
+ startRect(r,encodingCopyRect);
+ os->writeU16(srcX);
+ os->writeU16(srcY);
+ endRect();
+}
+
+void SMsgWriter::setOutStream(rdr::OutStream* os_)
+{
+ os = os_;
+}
+
+rdr::U8* SMsgWriter::getImageBuf(int required, int requested, int* nPixels)
+{
+ int requiredBytes = required * (cp->pf().bpp / 8);
+ int requestedBytes = requested * (cp->pf().bpp / 8);
+ int size = requestedBytes;
+ if (size > imageBufIdealSize) size = imageBufIdealSize;
+
+ if (size < requiredBytes)
+ size = requiredBytes;
+
+ if (imageBufSize < size) {
+ imageBufSize = size;
+ delete [] imageBuf;
+ imageBuf = new rdr::U8[imageBufSize];
+ }
+ if (nPixels)
+ *nPixels = imageBufSize / (cp->pf().bpp / 8);
+ return imageBuf;
+}
+
+int SMsgWriter::bpp()
+{
+ return cp->pf().bpp;
+}
diff --git a/rfb/SMsgWriter.h b/rfb/SMsgWriter.h
new file mode 100644
index 00000000..6eba0682
--- /dev/null
+++ b/rfb/SMsgWriter.h
@@ -0,0 +1,156 @@
+/* Copyright (C) 2002-2004 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.
+ */
+//
+// SMsgWriter - class for writing RFB messages on the server side.
+//
+
+#ifndef __RFB_SMSGWRITER_H__
+#define __RFB_SMSGWRITER_H__
+
+#include <rdr/types.h>
+#include <rfb/encodings.h>
+#include <rfb/Encoder.h>
+
+namespace rdr { class OutStream; }
+
+namespace rfb {
+
+ class PixelFormat;
+ class ConnParams;
+ class ImageGetter;
+ class ColourMap;
+ class Region;
+ class UpdateInfo;
+
+ class WriteSetCursorCallback {
+ public:
+ virtual void writeSetCursorCallback() = 0;
+ };
+
+ class SMsgWriter {
+ public:
+ virtual ~SMsgWriter();
+
+ // writeServerInit() must only be called at the appropriate time in the
+ // protocol initialisation.
+ virtual void writeServerInit()=0;
+
+ // Methods to write normal protocol messages
+
+ // writeSetColourMapEntries() writes a setColourMapEntries message, using
+ // the given ColourMap object to lookup the RGB values of the given range
+ // of colours.
+ virtual void writeSetColourMapEntries(int firstColour, int nColours,
+ ColourMap* cm);
+
+ // writeBell() and writeServerCutText() do the obvious thing.
+ virtual void writeBell();
+ virtual void writeServerCutText(const char* str, int len);
+
+ // writeSetDesktopSize() on a V3 writer won't actually write immediately,
+ // but will write the relevant pseudo-rectangle as part of the next update.
+ virtual bool writeSetDesktopSize()=0;
+
+ // Like setDestkopSize, we can't just write out a setCursor message
+ // immediately on a V3 writer. Instead of calling writeSetCursor()
+ // directly, you must call cursorChange(), and then invoke writeSetCursor()
+ // in response to the writeSetCursorCallback() callback. For a V3 writer
+ // this will happen when the next update is sent.
+ virtual void cursorChange(WriteSetCursorCallback* cb)=0;
+ virtual void writeSetCursor(int width, int height, int hotspotX,
+ int hotspotY, void* data, void* mask)=0;
+
+ // needFakeUpdate() returns true when an immediate update is needed in
+ // order to flush out setDesktopSize or setCursor pseudo-rectangles to the
+ // client.
+ virtual bool needFakeUpdate();
+
+ // writeFramebufferUpdate() writes a framebuffer update using the given
+ // UpdateInfo and ImageGetter. On a V3 writer this may have
+ // pseudo-rectangles for setDesktopSize and setCursor added to it, and so
+ // may invoke writeSetCursorCallback().
+ virtual void writeFramebufferUpdate(const UpdateInfo& ui, ImageGetter* ig,
+ Region* updatedRegion);
+
+ // writeRects() accepts an UpdateInfo (changed & copied regions) and an
+ // ImageGetter to fetch pixels from. It then calls writeCopyRect() and
+ // writeRect() as appropriate. writeFramebufferUpdateStart() must be used
+ // before the first writeRects() call and writeFrameBufferUpdateEnd() after
+ // the last one. It returns the actual region sent to the client, which
+ // may be smaller than the update passed in.
+ virtual void writeRects(const UpdateInfo& update, ImageGetter* ig,
+ Region* updatedRegion);
+
+ // To construct a framebuffer update you can call
+ // writeFramebufferUpdateStart(), followed by a number of writeCopyRect()s
+ // and writeRect()s, finishing with writeFramebufferUpdateEnd(). If you
+ // know the exact number of rectangles ahead of time you can specify it to
+ // writeFramebufferUpdateStart() which can be more efficient.
+ virtual void writeFramebufferUpdateStart(int nRects)=0;
+ virtual void writeFramebufferUpdateStart()=0;
+ virtual void writeFramebufferUpdateEnd()=0;
+
+ // writeRect() tries to write the given rectangle. If it is unable to
+ // write the whole rectangle it returns false and sets actual to the actual
+ // rectangle which was updated.
+ virtual bool writeRect(const Rect& r, ImageGetter* ig, Rect* actual);
+ virtual bool writeRect(const Rect& r, unsigned int encoding,
+ ImageGetter* ig, Rect* actual);
+
+ virtual void writeCopyRect(const Rect& r, int srcX, int srcY);
+
+ virtual void startRect(const Rect& r, unsigned int enc)=0;
+ virtual void endRect()=0;
+
+ // setOutStream() changes the OutStream on the fly.
+ virtual void setOutStream(rdr::OutStream* os);
+
+ ConnParams* getConnParams() { return cp; }
+ rdr::OutStream* getOutStream() { return os; }
+ rdr::U8* getImageBuf(int required, int requested=0, int* nPixels=0);
+ int bpp();
+
+ int getUpdatesSent() { return updatesSent; }
+ int getRectsSent(int encoding) { return rectsSent[encoding]; }
+ int getBytesSent(int encoding) { return bytesSent[encoding]; }
+ int getRawBytesEquivalent() { return rawBytesEquivalent; }
+
+ int imageBufIdealSize;
+
+ protected:
+ SMsgWriter(ConnParams* cp, rdr::OutStream* os);
+
+ virtual void startMsg(int type)=0;
+ virtual void endMsg()=0;
+
+ ConnParams* cp;
+ rdr::OutStream* os;
+
+ Encoder* encoders[encodingMax+1];
+ int lenBeforeRect;
+ unsigned int currentEncoding;
+ int updatesSent;
+ int bytesSent[encodingMax+1];
+ int rectsSent[encodingMax+1];
+ int rawBytesEquivalent;
+
+ rdr::U8* imageBuf;
+ int imageBufSize;
+ };
+}
+#endif
diff --git a/rfb/SMsgWriterV3.cxx b/rfb/SMsgWriterV3.cxx
new file mode 100644
index 00000000..20a72801
--- /dev/null
+++ b/rfb/SMsgWriterV3.cxx
@@ -0,0 +1,173 @@
+/* Copyright (C) 2002-2004 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 <rdr/OutStream.h>
+#include <rdr/MemOutStream.h>
+#include <rfb/msgTypes.h>
+#include <rfb/Exception.h>
+#include <rfb/ConnParams.h>
+#include <rfb/SMsgWriterV3.h>
+
+using namespace rfb;
+
+SMsgWriterV3::SMsgWriterV3(ConnParams* cp, rdr::OutStream* os)
+ : SMsgWriter(cp, os), updateOS(0), realOS(os), nRectsInUpdate(0),
+ nRectsInHeader(0), wsccb(0),
+ needSetDesktopSize(false)
+{
+}
+
+SMsgWriterV3::~SMsgWriterV3()
+{
+ delete updateOS;
+}
+
+void SMsgWriterV3::writeServerInit()
+{
+ os->writeU16(cp->width);
+ os->writeU16(cp->height);
+ cp->pf().write(os);
+ os->writeString(cp->name());
+ endMsg();
+}
+
+void SMsgWriterV3::startMsg(int type)
+{
+ if (os != realOS)
+ throw Exception("startMsg called while writing an update?");
+
+ os->writeU8(type);
+}
+
+void SMsgWriterV3::endMsg()
+{
+ os->flush();
+}
+
+bool SMsgWriterV3::writeSetDesktopSize() {
+ if (!cp->supportsDesktopResize) return false;
+ needSetDesktopSize = true;
+ return true;
+}
+
+void SMsgWriterV3::cursorChange(WriteSetCursorCallback* cb)
+{
+ wsccb = cb;
+}
+
+void SMsgWriterV3::writeSetCursor(int width, int height, int hotspotX,
+ int hotspotY, void* data, void* mask)
+{
+ if (!wsccb) return;
+ if (++nRectsInUpdate > nRectsInHeader && nRectsInHeader)
+ throw Exception("SMsgWriterV3::writeSetCursor: nRects out of sync");
+ os->writeS16(hotspotX);
+ os->writeS16(hotspotY);
+ os->writeU16(width);
+ os->writeU16(height);
+ os->writeU32(pseudoEncodingCursor);
+ os->writeBytes(data, width * height * (cp->pf().bpp/8));
+ os->writeBytes(mask, (width+7)/8 * height);
+}
+
+void SMsgWriterV3::writeFramebufferUpdateStart(int nRects)
+{
+ startMsg(msgTypeFramebufferUpdate);
+ os->pad(1);
+ if (wsccb) nRects++;
+ if (needSetDesktopSize) nRects++;
+ os->writeU16(nRects);
+ nRectsInUpdate = 0;
+ nRectsInHeader = nRects;
+ if (wsccb) {
+ wsccb->writeSetCursorCallback();
+ wsccb = 0;
+ }
+}
+
+void SMsgWriterV3::writeFramebufferUpdateStart()
+{
+ nRectsInUpdate = nRectsInHeader = 0;
+ if (!updateOS)
+ updateOS = new rdr::MemOutStream;
+ os = updateOS;
+}
+
+void SMsgWriterV3::writeFramebufferUpdateEnd()
+{
+ if (needSetDesktopSize) {
+ if (++nRectsInUpdate > nRectsInHeader && nRectsInHeader)
+ throw Exception("SMsgWriterV3 setDesktopSize: nRects out of sync");
+ os->writeS16(0);
+ os->writeS16(0);
+ os->writeU16(cp->width);
+ os->writeU16(cp->height);
+ os->writeU32(pseudoEncodingDesktopSize);
+ needSetDesktopSize = false;
+ }
+
+ if (nRectsInUpdate != nRectsInHeader && nRectsInHeader)
+ throw Exception("SMsgWriterV3::writeFramebufferUpdateEnd: "
+ "nRects out of sync");
+ if (os == updateOS) {
+ os = realOS;
+ startMsg(msgTypeFramebufferUpdate);
+ os->pad(1);
+ os->writeU16(nRectsInUpdate);
+ os->writeBytes(updateOS->data(), updateOS->length());
+ updateOS->clear();
+ }
+
+ updatesSent++;
+ endMsg();
+}
+
+bool SMsgWriterV3::needFakeUpdate()
+{
+ return wsccb || needSetDesktopSize;
+}
+
+void SMsgWriterV3::startRect(const Rect& r, unsigned int encoding)
+{
+ if (++nRectsInUpdate > nRectsInHeader && nRectsInHeader)
+ throw Exception("SMsgWriterV3::startRect: nRects out of sync");
+
+ currentEncoding = encoding;
+ lenBeforeRect = os->length();
+ if (encoding != encodingCopyRect)
+ rawBytesEquivalent += 12 + r.width() * r.height() * (bpp()/8);
+
+ os->writeS16(r.tl.x);
+ os->writeS16(r.tl.y);
+ os->writeU16(r.width());
+ os->writeU16(r.height());
+ os->writeU32(encoding);
+}
+
+void SMsgWriterV3::endRect()
+{
+ if (currentEncoding <= encodingMax) {
+ bytesSent[currentEncoding] += os->length() - lenBeforeRect;
+ rectsSent[currentEncoding]++;
+ }
+}
+
+void SMsgWriterV3::setOutStream(rdr::OutStream* os_)
+{
+ SMsgWriter::setOutStream(os_);
+ realOS = os;
+}
diff --git a/rfb/SMsgWriterV3.h b/rfb/SMsgWriterV3.h
new file mode 100644
index 00000000..3881061f
--- /dev/null
+++ b/rfb/SMsgWriterV3.h
@@ -0,0 +1,56 @@
+/* Copyright (C) 2002-2003 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_SMSGWRITERV3_H__
+#define __RFB_SMSGWRITERV3_H__
+
+#include <rfb/SMsgWriter.h>
+
+namespace rdr { class MemOutStream; }
+
+namespace rfb {
+ class SMsgWriterV3 : public SMsgWriter {
+ public:
+ SMsgWriterV3(ConnParams* cp, rdr::OutStream* os);
+ virtual ~SMsgWriterV3();
+
+ virtual void writeServerInit();
+ virtual void startMsg(int type);
+ virtual void endMsg();
+ virtual bool writeSetDesktopSize();
+ virtual void cursorChange(WriteSetCursorCallback* cb);
+ virtual void writeSetCursor(int width, int height, int hotspotX,
+ int hotspotY, void* data, void* mask);
+ virtual void writeFramebufferUpdateStart(int nRects);
+ virtual void writeFramebufferUpdateStart();
+ virtual void writeFramebufferUpdateEnd();
+ virtual bool needFakeUpdate();
+ virtual void startRect(const Rect& r, unsigned int encoding);
+ virtual void endRect();
+
+ virtual void setOutStream(rdr::OutStream* os);
+
+ private:
+ rdr::MemOutStream* updateOS;
+ rdr::OutStream* realOS;
+ int nRectsInUpdate;
+ int nRectsInHeader;
+ WriteSetCursorCallback* wsccb;
+ bool needSetDesktopSize;
+ };
+}
+#endif
diff --git a/rfb/SSecurity.h b/rfb/SSecurity.h
new file mode 100644
index 00000000..2ca53449
--- /dev/null
+++ b/rfb/SSecurity.h
@@ -0,0 +1,77 @@
+/* Copyright (C) 2002-2003 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.
+ */
+//
+// SSecurity - class on the server side for handling security handshaking. A
+// derived class for a particular security type overrides the processMsg()
+// method. processMsg() is called first when the security type has been
+// decided on, and will keep being called whenever there is data to read from
+// the client until either it returns false, indicating authentication/security
+// failure, or it returns with done set to true, to indicate success.
+//
+// processMsg() must never block (or at least must never block until the client
+// has been authenticated) - this is to prevent denial of service attacks.
+// Note that the first time processMsg() is called, there is no guarantee that
+// there is any data to read from the SConnection's InStream, but subsequent
+// calls guarantee there is at least one byte which can be read without
+// blocking.
+//
+// getType() should return the secType value corresponding to the SSecurity
+// implementation.
+//
+// failureMessage_.buf can be set to a string which will be passed to the client
+// if processMsg returns false, to indicate the reason for the failure.
+
+#ifndef __RFB_SSECURITY_H__
+#define __RFB_SSECURITY_H__
+
+#include <rfb/util.h>
+
+namespace rfb {
+
+ class SConnection;
+
+ class SSecurity {
+ public:
+ virtual ~SSecurity() {}
+ virtual bool processMsg(SConnection* sc, bool* done)=0;
+ virtual void destroy() { delete this; }
+ virtual int getType() const = 0;
+
+ // getUserName() gets the name of the user attempting authentication. The
+ // storage is owned by the SSecurity object, so a copy must be taken if
+ // necessary. Null may be returned to indicate that there is no user name
+ // for this security type.
+ virtual const char* getUserName() const = 0;
+
+ virtual const char* failureMessage() {return failureMessage_.buf;}
+ protected:
+ CharArray failureMessage_;
+ };
+
+ // SSecurityFactory creates new SSecurity instances for
+ // particular security types.
+ // The instances must be destroyed by calling destroy()
+ // on them when done.
+ class SSecurityFactory {
+ public:
+ virtual ~SSecurityFactory() {}
+ virtual SSecurity* getSSecurity(int secType, bool noAuth=false)=0;
+ };
+
+}
+#endif
diff --git a/rfb/SSecurityFactoryStandard.cxx b/rfb/SSecurityFactoryStandard.cxx
new file mode 100644
index 00000000..e3a40aa3
--- /dev/null
+++ b/rfb/SSecurityFactoryStandard.cxx
@@ -0,0 +1,101 @@
+/* Copyright (C) 2002-2004 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.
+ */
+//
+// SSecurityFactoryStandard
+//
+
+#include <rfb/secTypes.h>
+#include <rfb/SSecurityNone.h>
+#include <rfb/Configuration.h>
+#include <rfb/LogWriter.h>
+#include <rfb/Exception.h>
+#include <rfb/SSecurityFactoryStandard.h>
+
+using namespace rfb;
+
+static LogWriter vlog("SSecurityFactoryStandard");
+
+VncAuthPasswdParameter* SSecurityFactoryStandard::vncAuthPasswd = 0;
+
+
+SSecurity* SSecurityFactoryStandard::getSSecurity(int secType, bool noAuth) {
+ switch (secType) {
+ case secTypeNone: return new SSecurityNone();
+ case secTypeVncAuth:
+ if (!vncAuthPasswd)
+ throw rdr::Exception("No VncAuthPasswdParameter defined!");
+ return new SSecurityVncAuth(vncAuthPasswd);
+ default:
+ throw Exception("Unsupported secType?");
+ }
+}
+
+VncAuthPasswdParameter::VncAuthPasswdParameter() {
+ if (SSecurityFactoryStandard::vncAuthPasswd)
+ throw rdr::Exception("duplicate VncAuthPasswdParameter!");
+ SSecurityFactoryStandard::vncAuthPasswd = this;
+}
+
+
+VncAuthPasswdConfigParameter::VncAuthPasswdConfigParameter()
+: passwdParam("Password",
+ "Obfuscated binary encoding of the password which clients must supply to "
+ "access the server", 0, 0) {
+}
+
+char* VncAuthPasswdConfigParameter::getVncAuthPasswd() {
+ CharArray obfuscated;
+ int len;
+ passwdParam.getData((void**)&obfuscated.buf, &len);
+ printf("vnc password len=%d\n", len); // ***
+ if (len == 8) {
+ CharArray password(9);
+ memcpy(password.buf, obfuscated.buf, 8);
+ vncAuthUnobfuscatePasswd(password.buf);
+ return password.takeBuf();
+ }
+ return 0;
+}
+
+
+VncAuthPasswdFileParameter::VncAuthPasswdFileParameter()
+ : param("PasswordFile", "Password file for VNC authentication", "") {
+}
+
+char* VncAuthPasswdFileParameter::getVncAuthPasswd() {
+ CharArray fname(param.getData());
+ if (!fname.buf[0]) {
+ vlog.error("passwordFile parameter not set");
+ return 0;
+ }
+ FILE* fp = fopen(fname.buf, "r");
+ if (!fp) {
+ vlog.error("opening password file '%s' failed",fname.buf);
+ return 0;
+ }
+ CharArray passwd(9);
+ int len = fread(passwd.buf, 1, 9, fp);
+ fclose(fp);
+ if (len != 8) {
+ vlog.error("password file '%s' is the wrong length",fname.buf);
+ return 0;
+ }
+ vncAuthUnobfuscatePasswd(passwd.buf);
+ return passwd.takeBuf();
+}
+
diff --git a/rfb/SSecurityFactoryStandard.h b/rfb/SSecurityFactoryStandard.h
new file mode 100644
index 00000000..5fced046
--- /dev/null
+++ b/rfb/SSecurityFactoryStandard.h
@@ -0,0 +1,75 @@
+/* Copyright (C) 2002-2003 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.
+ */
+
+//
+// SSecurityFactoryStandard - implementation of the SSecurityFactory
+// interface.
+//
+// Server implementations must define an instance of a
+// VncAuthPasswdParameter-based class somewhere. Any class based on
+// VncAuthPasswdParameter will automatically register itself as the
+// password parameter to be used by the Standard factory.
+//
+// Two implementations are provided here:
+//
+// VncAuthPasswdConfigParameter - reads the password from the Binary
+// parameter "Password".
+// VncAuthPasswdFileParameter - reads the password from the file named
+// in the String parameter "PasswordFile".
+//
+// This factory supports only the "None" and "VncAuth" security types.
+//
+
+#ifndef __RFB_SSECURITYFACTORY_STANDARD_H__
+#define __RFB_SSECURITYFACTORY_STANDARD_H__
+
+#include <rfb/SSecurityVncAuth.h>
+#include <rfb/Configuration.h>
+#include <rfb/util.h>
+
+namespace rfb {
+
+ class VncAuthPasswdParameter : public VncAuthPasswdGetter {
+ public:
+ VncAuthPasswdParameter();
+ virtual ~VncAuthPasswdParameter() {}
+ };
+
+ class SSecurityFactoryStandard : public SSecurityFactory {
+ public:
+ virtual SSecurity* getSSecurity(int secType, bool noAuth);
+ static VncAuthPasswdParameter* vncAuthPasswd;
+ };
+
+ class VncAuthPasswdConfigParameter : public VncAuthPasswdParameter {
+ public:
+ VncAuthPasswdConfigParameter();
+ virtual char* getVncAuthPasswd();
+ protected:
+ BinaryParameter passwdParam;
+ };
+
+ class VncAuthPasswdFileParameter : public VncAuthPasswdParameter {
+ public:
+ VncAuthPasswdFileParameter();
+ virtual char* getVncAuthPasswd();
+ StringParameter param;
+ };
+
+}
+#endif
diff --git a/rfb/SSecurityNone.h b/rfb/SSecurityNone.h
new file mode 100644
index 00000000..09b2db4f
--- /dev/null
+++ b/rfb/SSecurityNone.h
@@ -0,0 +1,38 @@
+/* Copyright (C) 2002-2003 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.
+ */
+//
+// SSecurityNone.h
+//
+
+#ifndef __SSECURITYNONE_H__
+#define __SSECURITYNONE_H__
+
+#include <rfb/SSecurity.h>
+
+namespace rfb {
+
+ class SSecurityNone : public SSecurity {
+ public:
+ virtual bool processMsg(SConnection* sc, bool* done) {
+ *done = true; return true;
+ }
+ virtual int getType() const {return secTypeNone;}
+ virtual const char* getUserName() const {return 0;}
+ };
+}
+#endif
diff --git a/rfb/SSecurityVncAuth.cxx b/rfb/SSecurityVncAuth.cxx
new file mode 100644
index 00000000..532d1a6a
--- /dev/null
+++ b/rfb/SSecurityVncAuth.cxx
@@ -0,0 +1,83 @@
+/* Copyright (C) 2002-2003 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.
+ */
+//
+// SSecurityVncAuth
+//
+
+#include <rfb/SSecurityVncAuth.h>
+#include <rdr/RandomStream.h>
+#include <rfb/SConnection.h>
+#include <rfb/vncAuth.h>
+#include <rfb/Configuration.h>
+#include <rfb/LogWriter.h>
+#include <rfb/util.h>
+#include <string.h>
+#include <stdio.h>
+
+using namespace rfb;
+
+static LogWriter vlog("VncAuth");
+
+
+SSecurityVncAuth::SSecurityVncAuth(VncAuthPasswdGetter* pg_)
+ : sentChallenge(false), responsePos(0), pg(pg_)
+{
+}
+
+bool SSecurityVncAuth::processMsg(SConnection* sc, bool* done)
+{
+ *done = false;
+ rdr::InStream* is = sc->getInStream();
+ rdr::OutStream* os = sc->getOutStream();
+
+ if (!sentChallenge) {
+ rdr::RandomStream rs;
+ rs.readBytes(challenge, vncAuthChallengeSize);
+ os->writeBytes(challenge, vncAuthChallengeSize);
+ os->flush();
+ sentChallenge = true;
+ return true;
+ }
+
+ if (responsePos >= vncAuthChallengeSize) return false;
+ while (is->checkNoWait(1) && responsePos < vncAuthChallengeSize) {
+ response[responsePos++] = is->readU8();
+ }
+
+ if (responsePos < vncAuthChallengeSize) return true;
+
+ CharArray passwd(pg->getVncAuthPasswd());
+
+ // Beyond this point, there is no more VNCAuth protocol to perform.
+ *done = true;
+
+ if (!passwd.buf) {
+ failureMessage_.buf = strDup("No password configured for VNC Auth");
+ vlog.error(failureMessage_.buf);
+ return false;
+ }
+
+ vncAuthEncryptChallenge(challenge, passwd.buf);
+ memset(passwd.buf, 0, strlen(passwd.buf));
+
+ if (memcmp(challenge, response, vncAuthChallengeSize) != 0) {
+ return false;
+ }
+
+ return true;
+}
diff --git a/rfb/SSecurityVncAuth.h b/rfb/SSecurityVncAuth.h
new file mode 100644
index 00000000..edbd7206
--- /dev/null
+++ b/rfb/SSecurityVncAuth.h
@@ -0,0 +1,54 @@
+/* Copyright (C) 2002-2003 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.
+ */
+// SSecurityVncAuth - legacy VNC authentication protocol.
+// The getPasswd call can be overridden if you wish to store
+// the VncAuth password in an implementation-specific place.
+// Otherwise, the password is read from a BinaryParameter
+// called Password.
+
+#ifndef __RFB_SSECURITYVNCAUTH_H__
+#define __RFB_SSECURITYVNCAUTH_H__
+
+#include <rfb/SSecurity.h>
+#include <rfb/secTypes.h>
+#include <rfb/vncAuth.h>
+
+namespace rfb {
+
+ class VncAuthPasswdGetter {
+ public:
+ // getPasswd() returns a string or null if unsuccessful. The
+ // SSecurityVncAuth object delete[]s the string when done.
+ virtual char* getVncAuthPasswd()=0;
+ };
+
+ class SSecurityVncAuth : public SSecurity {
+ public:
+ SSecurityVncAuth(VncAuthPasswdGetter* pg);
+ virtual bool processMsg(SConnection* sc, bool* done);
+ virtual int getType() const {return secTypeVncAuth;}
+ virtual const char* getUserName() const {return 0;}
+ private:
+ rdr::U8 challenge[vncAuthChallengeSize];
+ rdr::U8 response[vncAuthChallengeSize];
+ bool sentChallenge;
+ int responsePos;
+ VncAuthPasswdGetter* pg;
+ };
+}
+#endif
diff --git a/rfb/ServerCore.cxx b/rfb/ServerCore.cxx
new file mode 100644
index 00000000..7a20c569
--- /dev/null
+++ b/rfb/ServerCore.cxx
@@ -0,0 +1,95 @@
+/* Copyright (C) 2002-2004 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.
+ */
+
+// -=- ServerCore.cxx
+
+// This header will define the Server interface, from which ServerMT and
+// ServerST will be derived.
+
+#include <string.h>
+#include <rfb/util.h>
+#include <rfb/ServerCore.h>
+#include <rfb/vncAuth.h>
+
+rfb::IntParameter rfb::Server::idleTimeout
+("IdleTimeout",
+ "The number of seconds after which an idle VNC connection will be dropped",
+ 3600);
+rfb::IntParameter rfb::Server::clientWaitTimeMillis
+("ClientWaitTimeMillis",
+ "The number of milliseconds to wait for a client which is no longer "
+ "responding",
+ 20000);
+rfb::StringParameter rfb::Server::sec_types
+("SecurityTypes",
+ "Specify which security scheme to use for incoming connections (None, VncAuth)",
+ "VncAuth");
+rfb::StringParameter rfb::Server::rev_sec_types
+("ReverseSecurityTypes",
+ "Specify encryption scheme to use for reverse connections (None)",
+ "None");
+rfb::BoolParameter rfb::Server::compareFB
+("CompareFB",
+ "Perform pixel comparison on framebuffer to reduce unnecessary updates",
+ true);
+rfb::BoolParameter rfb::Server::protocol3_3
+("Protocol3.3",
+ "Always use protocol version 3.3 for backwards compatibility with "
+ "badly-behaved clients",
+ false);
+rfb::BoolParameter rfb::Server::alwaysShared
+("AlwaysShared",
+ "Always treat incoming connections as shared, regardless of the client-"
+ "specified setting",
+ false);
+rfb::BoolParameter rfb::Server::neverShared
+("NeverShared",
+ "Never treat incoming connections as shared, regardless of the client-"
+ "specified setting",
+ false);
+rfb::BoolParameter rfb::Server::disconnectClients
+("DisconnectClients",
+ "Disconnect existing clients if an incoming connection is non-shared. "
+ "If combined with NeverShared then new connections will be refused "
+ "while there is a client active",
+ true);
+rfb::BoolParameter rfb::Server::acceptKeyEvents
+("AcceptKeyEvents",
+ "Accept key press and release events from clients.",
+ true);
+rfb::BoolParameter rfb::Server::acceptPointerEvents
+("AcceptPointerEvents",
+ "Accept pointer press and release events from clients.",
+ true);
+rfb::BoolParameter rfb::Server::acceptCutText
+("AcceptCutText",
+ "Accept clipboard updates from clients.",
+ true);
+rfb::BoolParameter rfb::Server::sendCutText
+("SendCutText",
+ "Send clipboard changes to clients.",
+ true);
+rfb::BoolParameter rfb::Server::queryConnect
+("QueryConnect",
+ "Prompt the local user to accept or reject incoming connections.",
+ false);
+rfb::IntParameter rfb::Server::blacklistLevel
+("BlacklistLevel",
+ "When to test whether particular host should be blacklisted. (0 = Never, "
+ "1 = Test before authentication, 2 = Test on connect)",
+ 1);
diff --git a/rfb/ServerCore.h b/rfb/ServerCore.h
new file mode 100644
index 00000000..74d74432
--- /dev/null
+++ b/rfb/ServerCore.h
@@ -0,0 +1,56 @@
+/* Copyright (C) 2002-2004 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.
+ */
+
+// -=- ServerCore.h
+
+// This header will define the Server interface, from which ServerMT and
+// ServerST will be derived.
+
+#ifndef __RFB_SERVER_CORE_H__
+#define __RFB_SERVER_CORE_H__
+
+#include <rfb/Configuration.h>
+#include <rfb/util.h>
+
+namespace rfb {
+
+ class Server {
+ public:
+
+ static IntParameter idleTimeout;
+ static IntParameter clientWaitTimeMillis;
+ static StringParameter sec_types;
+ static StringParameter rev_sec_types;
+ static BoolParameter compareFB;
+ static BoolParameter protocol3_3;
+ static BoolParameter alwaysShared;
+ static BoolParameter neverShared;
+ static BoolParameter disconnectClients;
+ static BoolParameter acceptKeyEvents;
+ static BoolParameter acceptPointerEvents;
+ static BoolParameter acceptCutText;
+ static BoolParameter sendCutText;
+ static BoolParameter queryConnect;
+ static IntParameter blacklistLevel;
+
+ };
+
+};
+
+#endif // __RFB_SERVER_CORE_H__
+
diff --git a/rfb/Threading.h b/rfb/Threading.h
new file mode 100644
index 00000000..effc4363
--- /dev/null
+++ b/rfb/Threading.h
@@ -0,0 +1,31 @@
+/* Copyright (C) 2002-2003 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.h
+// General purpose threading interface.
+// If the current platform supports threading then __RFB_THREADING_IMPL
+// will be defined after this header has been included.
+
+#ifndef __RFB_THREADING_H__
+#define __RFB_THREADING_H__
+
+#ifdef WIN32
+#include <rfb/win32/Threading_win32.h>
+#endif
+
+#endif // __RFB_THREADING_H__
diff --git a/rfb/TransImageGetter.cxx b/rfb/TransImageGetter.cxx
new file mode 100644
index 00000000..693b18c5
--- /dev/null
+++ b/rfb/TransImageGetter.cxx
@@ -0,0 +1,278 @@
+/* Copyright (C) 2002-2003 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 <stdio.h>
+#include <stdlib.h>
+#include <rfb/PixelFormat.h>
+#include <rfb/Exception.h>
+#include <rfb/ConnParams.h>
+#include <rfb/SMsgWriter.h>
+#include <rfb/ColourMap.h>
+#include <rfb/TrueColourMap.h>
+#include <rfb/PixelBuffer.h>
+#include <rfb/ColourCube.h>
+#include <rfb/TransImageGetter.h>
+
+using namespace rfb;
+
+const PixelFormat bgr233PF(8, 8, false, true, 7, 7, 3, 0, 3, 6);
+
+static void noTransFn(void* table_,
+ const PixelFormat& inPF, void* inPtr, int inStride,
+ const PixelFormat& outPF, void* outPtr, int outStride,
+ int width, int height)
+{
+ rdr::U8* ip = (rdr::U8*)inPtr;
+ rdr::U8* op = (rdr::U8*)outPtr;
+ int inStrideBytes = inStride * (inPF.bpp/8);
+ int outStrideBytes = outStride * (outPF.bpp/8);
+ int widthBytes = width * (outPF.bpp/8);
+
+ while (height > 0) {
+ memcpy(op, ip, widthBytes);
+ ip += inStrideBytes;
+ op += outStrideBytes;
+ height--;
+ }
+}
+
+#define BPPOUT 8
+#include "transInitTempl.h"
+#define BPPIN 8
+#include "transTempl.h"
+#undef BPPIN
+#define BPPIN 16
+#include "transTempl.h"
+#undef BPPIN
+#define BPPIN 32
+#include "transTempl.h"
+#undef BPPIN
+#undef BPPOUT
+
+#define BPPOUT 16
+#include "transInitTempl.h"
+#define BPPIN 8
+#include "transTempl.h"
+#undef BPPIN
+#define BPPIN 16
+#include "transTempl.h"
+#undef BPPIN
+#define BPPIN 32
+#include "transTempl.h"
+#undef BPPIN
+#undef BPPOUT
+
+#define BPPOUT 32
+#include "transInitTempl.h"
+#define BPPIN 8
+#include "transTempl.h"
+#undef BPPIN
+#define BPPIN 16
+#include "transTempl.h"
+#undef BPPIN
+#define BPPIN 32
+#include "transTempl.h"
+#undef BPPIN
+#undef BPPOUT
+
+
+// Translation functions. Note that transSimple* is only used for 8/16bpp and
+// transRGB* is used for 16/32bpp
+
+static transFnType transSimpleFns[][3] = {
+ { transSimple8to8, transSimple8to16, transSimple8to32 },
+ { transSimple16to8, transSimple16to16, transSimple16to32 },
+};
+static transFnType transRGBFns[][3] = {
+ { transRGB16to8, transRGB16to16, transRGB16to32 },
+ { transRGB32to8, transRGB32to16, transRGB32to32 }
+};
+static transFnType transRGBCubeFns[][3] = {
+ { transRGBCube16to8, transRGBCube16to16, transRGBCube16to32 },
+ { transRGBCube32to8, transRGBCube32to16, transRGBCube32to32 }
+};
+
+// Table initialisation functions.
+
+typedef void (*initCMtoTCFnType)(rdr::U8** tablep, const PixelFormat& inPF,
+ ColourMap* cm, const PixelFormat& outPF);
+typedef void (*initTCtoTCFnType)(rdr::U8** tablep, const PixelFormat& inPF,
+ const PixelFormat& outPF);
+typedef void (*initCMtoCubeFnType)(rdr::U8** tablep, const PixelFormat& inPF,
+ ColourMap* cm, ColourCube* cube);
+typedef void (*initTCtoCubeFnType)(rdr::U8** tablep, const PixelFormat& inPF,
+ ColourCube* cube);
+
+
+static initCMtoTCFnType initSimpleCMtoTCFns[] = {
+ initSimpleCMtoTC8, initSimpleCMtoTC16, initSimpleCMtoTC32
+};
+
+static initTCtoTCFnType initSimpleTCtoTCFns[] = {
+ initSimpleTCtoTC8, initSimpleTCtoTC16, initSimpleTCtoTC32
+};
+
+static initCMtoCubeFnType initSimpleCMtoCubeFns[] = {
+ initSimpleCMtoCube8, initSimpleCMtoCube16, initSimpleCMtoCube32
+};
+
+static initTCtoCubeFnType initSimpleTCtoCubeFns[] = {
+ initSimpleTCtoCube8, initSimpleTCtoCube16, initSimpleTCtoCube32
+};
+
+static initTCtoTCFnType initRGBTCtoTCFns[] = {
+ initRGBTCtoTC8, initRGBTCtoTC16, initRGBTCtoTC32
+};
+
+static initTCtoCubeFnType initRGBTCtoCubeFns[] = {
+ initRGBTCtoCube8, initRGBTCtoCube16, initRGBTCtoCube32
+};
+
+
+TransImageGetter::TransImageGetter(bool econ)
+ : economic(econ), pb(0), table(0), transFn(0), cube(0)
+{
+}
+
+TransImageGetter::~TransImageGetter()
+{
+ delete [] table;
+}
+
+void TransImageGetter::init(PixelBuffer* pb_, const PixelFormat& out,
+ SMsgWriter* writer, ColourCube* cube_)
+{
+ pb = pb_;
+ outPF = out;
+ transFn = 0;
+ cube = cube_;
+ const PixelFormat& inPF = pb->getPF();
+
+ if ((inPF.bpp != 8) && (inPF.bpp != 16) && (inPF.bpp != 32))
+ throw Exception("TransImageGetter: bpp in not 8, 16 or 32");
+
+ if ((outPF.bpp != 8) && (outPF.bpp != 16) && (outPF.bpp != 32))
+ throw Exception("TransImageGetter: bpp out not 8, 16 or 32");
+
+ if (!outPF.trueColour) {
+ if (outPF.bpp != 8)
+ throw Exception("TransImageGetter: outPF has colour map but not 8bpp");
+
+ if (!inPF.trueColour) {
+ if (inPF.bpp != 8)
+ throw Exception("TransImageGetter: inPF has colourMap but not 8bpp");
+
+ // CM to CM/Cube
+
+ if (cube) {
+ transFn = transSimpleFns[inPF.bpp/16][outPF.bpp/16];
+ (*initSimpleCMtoCubeFns[outPF.bpp/16]) (&table, inPF,
+ pb->getColourMap(), cube);
+ } else {
+ transFn = noTransFn;
+ setColourMapEntries(0, 256, writer);
+ }
+ return;
+ }
+
+ // TC to CM/Cube
+
+ ColourCube defaultCube(6,6,6);
+ if (!cube) cube = &defaultCube;
+
+ if ((inPF.bpp > 16) || (economic && (inPF.bpp == 16))) {
+ transFn = transRGBCubeFns[inPF.bpp/32][outPF.bpp/16];
+ (*initRGBTCtoCubeFns[outPF.bpp/16]) (&table, inPF, cube);
+ } else {
+ transFn = transSimpleFns[inPF.bpp/16][outPF.bpp/16];
+ (*initSimpleTCtoCubeFns[outPF.bpp/16]) (&table, inPF, cube);
+ }
+
+ if (cube != &defaultCube)
+ return;
+
+ if (writer) writer->writeSetColourMapEntries(0, 216, cube);
+ cube = 0;
+ return;
+ }
+
+ if (inPF.equal(outPF)) {
+ transFn = noTransFn;
+ return;
+ }
+
+ if (!inPF.trueColour) {
+
+ // CM to TC
+
+ if (inPF.bpp != 8)
+ throw Exception("TransImageGetter: inPF has colourMap but not 8bpp");
+ transFn = transSimpleFns[inPF.bpp/16][outPF.bpp/16];
+ (*initSimpleCMtoTCFns[outPF.bpp/16]) (&table, inPF, pb->getColourMap(),
+ outPF);
+ return;
+ }
+
+ // TC to TC
+
+ if ((inPF.bpp > 16) || (economic && (inPF.bpp == 16))) {
+ transFn = transRGBFns[inPF.bpp/32][outPF.bpp/16];
+ (*initRGBTCtoTCFns[outPF.bpp/16]) (&table, inPF, outPF);
+ } else {
+ transFn = transSimpleFns[inPF.bpp/16][outPF.bpp/16];
+ (*initSimpleTCtoTCFns[outPF.bpp/16]) (&table, inPF, outPF);
+ }
+}
+
+void TransImageGetter::setColourMapEntries(int firstCol, int nCols,
+ SMsgWriter* writer)
+{
+ if (nCols == 0)
+ nCols = (1 << pb->getPF().depth) - firstCol;
+ if (pb->getPF().trueColour) return; // shouldn't be called in this case
+
+ if (outPF.trueColour) {
+ (*initSimpleCMtoTCFns[outPF.bpp/16]) (&table, pb->getPF(),
+ pb->getColourMap(), outPF);
+ } else if (cube) {
+ (*initSimpleCMtoCubeFns[outPF.bpp/16]) (&table, pb->getPF(),
+ pb->getColourMap(), cube);
+ } else if (writer && pb->getColourMap()) {
+ writer->writeSetColourMapEntries(firstCol, nCols, pb->getColourMap());
+ }
+}
+
+void TransImageGetter::getImage(void* outPtr, const Rect& r, int outStride)
+{
+ if (!transFn)
+ throw Exception("TransImageGetter: not initialised yet");
+
+ int inStride;
+ const rdr::U8* inPtr = pb->getPixelsR(r.translate(offset.negate()), &inStride);
+
+ if (!outStride) outStride = r.width();
+
+ (*transFn)(table, pb->getPF(), (void*)inPtr, inStride,
+ outPF, outPtr, outStride, r.width(), r.height());
+}
+
+void TransImageGetter::translatePixels(void* inPtr, void* outPtr,
+ int nPixels) const
+{
+ (*transFn)(table, pb->getPF(), inPtr, nPixels,
+ outPF, outPtr, nPixels, nPixels, 1);
+}
diff --git a/rfb/TransImageGetter.h b/rfb/TransImageGetter.h
new file mode 100644
index 00000000..60ab0699
--- /dev/null
+++ b/rfb/TransImageGetter.h
@@ -0,0 +1,104 @@
+/* Copyright (C) 2002-2003 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.
+ */
+//
+// TransImageGetter - class to perform translation between pixel formats,
+// implementing the ImageGetter interface.
+//
+
+#ifndef __RFB_TRANSIMAGEGETTER_H__
+#define __RFB_TRANSIMAGEGETTER_H__
+
+#include <rfb/Rect.h>
+#include <rfb/PixelFormat.h>
+#include <rfb/ImageGetter.h>
+
+namespace rfb {
+ typedef void (*transFnType)(void* table_,
+ const PixelFormat& inPF, void* inPtr,
+ int inStride,
+ const PixelFormat& outPF, void* outPtr,
+ int outStride, int width, int height);
+
+ class SMsgWriter;
+ class ColourMap;
+ class PixelBuffer;
+ class ColourCube;
+
+ class TransImageGetter : public ImageGetter {
+ public:
+
+ TransImageGetter(bool econ=false);
+ virtual ~TransImageGetter();
+
+ // init() is called to initialise the translation tables. The PixelBuffer
+ // argument gives the source data and format details, outPF gives the
+ // client's pixel format. If the client has a colour map, then the writer
+ // argument is used to send a SetColourMapEntries message to the client.
+
+ void init(PixelBuffer* pb, const PixelFormat& outPF, SMsgWriter* writer=0,
+ ColourCube* cube=0);
+
+ // setColourMapEntries() is called when the PixelBuffer has a colour map
+ // which has changed. firstColour and nColours specify which part of the
+ // colour map has changed. If nColours is 0, this means the rest of the
+ // colour map. The PixelBuffer previously passed to init() must have a
+ // valid ColourMap object. If the client also has a colour map, then the
+ // writer argument is used to send a SetColourMapEntries message to the
+ // client. If the client is true colour then instead we update the
+ // internal translation table - in this case the caller should also make
+ // sure that the client receives an update of the relevant parts of the
+ // framebuffer (the simplest thing to do is just update the whole
+ // framebuffer, though it is possible to be smarter than this).
+
+ void setColourMapEntries(int firstColour, int nColours,
+ SMsgWriter* writer=0);
+
+ // getImage() gets the given rectangle of data from the PixelBuffer,
+ // translates it into the client's pixel format and puts it in the buffer
+ // pointed to by the outPtr argument. The optional outStride argument can
+ // be used where padding is required between the output scanlines (the
+ // padding will be outStride-r.width() pixels).
+ void getImage(void* outPtr, const Rect& r, int outStride=0);
+
+ // translatePixels() translates the given number of pixels from inPtr,
+ // putting it into the buffer pointed to by outPtr. The pixels at inPtr
+ // should be in the same format as the PixelBuffer, and the translated
+ // pixels will be in the format previously given by the outPF argument to
+ // init(). Note that this call does not use the PixelBuffer's pixel data.
+ void translatePixels(void* inPtr, void* outPtr, int nPixels) const;
+
+ // setPixelBuffer() changes the pixel buffer to be used. The new pixel
+ // buffer MUST have the same pixel format as the old one - if not you
+ // should call init() instead.
+ void setPixelBuffer(PixelBuffer* pb_) { pb = pb_; }
+
+ // setOffset() sets an offset which is subtracted from the coordinates of
+ // the rectangle given to getImage().
+ void setOffset(const Point& offset_) { offset = offset_; }
+
+ private:
+ bool economic;
+ PixelBuffer* pb;
+ PixelFormat outPF;
+ rdr::U8* table;
+ transFnType transFn;
+ ColourCube* cube;
+ Point offset;
+ };
+}
+#endif
diff --git a/rfb/TrueColourMap.h b/rfb/TrueColourMap.h
new file mode 100644
index 00000000..c0d49071
--- /dev/null
+++ b/rfb/TrueColourMap.h
@@ -0,0 +1,42 @@
+/* Copyright (C) 2002-2003 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_TRUECOLOURMAP_H__
+#define __RFB_TRUECOLOURMAP_H__
+
+#include <rfb/ColourMap.h>
+
+namespace rfb {
+
+ class TrueColourMap : public ColourMap {
+ public:
+ TrueColourMap(const PixelFormat& pf_) : pf(pf_) {}
+
+ virtual void lookup(int i, int* r, int* g, int* b)
+ {
+ *r = (((i >> pf.redShift ) & pf.redMax)
+ * 65535 + pf.redMax/2) / pf.redMax;
+ *g = (((i >> pf.greenShift) & pf.greenMax)
+ * 65535 + pf.greenMax/2) / pf.greenMax;
+ *b = (((i >> pf.blueShift) & pf.blueMax)
+ * 65535 + pf.blueMax/2) / pf.blueMax;
+ }
+ private:
+ PixelFormat pf;
+ };
+}
+#endif
diff --git a/rfb/UpdateTracker.cxx b/rfb/UpdateTracker.cxx
new file mode 100644
index 00000000..cc0fb101
--- /dev/null
+++ b/rfb/UpdateTracker.cxx
@@ -0,0 +1,172 @@
+/* Copyright (C) 2002-2004 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.
+ */
+
+// -=- rfbUpdateTracker.cpp
+//
+// Tracks updated regions and a region-copy event, too
+//
+
+#include <assert.h>
+
+#include <rfb/UpdateTracker.h>
+#include <rfb/LogWriter.h>
+
+using namespace rfb;
+
+static LogWriter vlog("UpdateTracker");
+
+// -=- ClippedUpdateTracker
+
+void ClippedUpdateTracker::add_changed(const Region &region) {
+ child.add_changed(region.intersect(cliprgn));
+}
+
+void ClippedUpdateTracker::add_copied(const Region &dest, const Point &delta) {
+ // Clip the destination to the display area
+ Region clipdest = dest.intersect(cliprgn);
+ if (clipdest.is_empty()) return;
+
+ // Clip the source to the screen
+ Region tmp = clipdest;
+ tmp.translate(delta.negate());
+ tmp.assign_intersect(cliprgn);
+ if (!tmp.is_empty()) {
+ // Translate the source back to a destination region
+ tmp.translate(delta);
+
+ // Pass the copy region to the child tracker
+ child.add_copied(tmp, delta);
+ }
+
+ // And add any bits that we had to remove to the changed region
+ tmp = clipdest.subtract(tmp);
+ if (!tmp.is_empty()) {
+ child.add_changed(tmp);
+ }
+}
+
+// SimpleUpdateTracker
+
+SimpleUpdateTracker::SimpleUpdateTracker(bool use_copyrect) {
+ copy_enabled = use_copyrect;
+}
+
+SimpleUpdateTracker::~SimpleUpdateTracker() {
+}
+
+void SimpleUpdateTracker::enable_copyrect(bool enable) {
+ if (!enable && copy_enabled) {
+ add_changed(copied);
+ copied.clear();
+ }
+ copy_enabled=enable;
+}
+
+void SimpleUpdateTracker::add_changed(const Region &region) {
+ changed.assign_union(region);
+}
+
+void SimpleUpdateTracker::add_copied(const Region &dest, const Point &delta) {
+ // Do we support copyrect?
+ if (!copy_enabled) {
+ add_changed(dest);
+ return;
+ }
+
+ // Is there anything to do?
+ if (dest.is_empty()) return;
+
+ // Calculate whether any of this copy can be treated as a continuation
+ // of an earlier one
+ Region src = dest;
+ src.translate(delta.negate());
+ Region overlap = src.intersect(copied);
+
+ if (overlap.is_empty()) {
+ // There is no overlap
+
+ Rect newbr = dest.get_bounding_rect();
+ Rect oldbr = copied.get_bounding_rect();
+ if (oldbr.area() > newbr.area()) {
+ // Old copyrect is (probably) bigger - use it
+ changed.assign_union(dest);
+ } else {
+ // New copyrect is probably bigger
+ // Use the new one
+ // But be careful not to copy stuff that still needs
+ // to be updated.
+ Region invalid_src = src.intersect(changed);
+ invalid_src.translate(delta);
+ changed.assign_union(invalid_src);
+ changed.assign_union(copied);
+ copied = dest;
+ copy_delta = delta;
+ }
+ return;
+ }
+
+ Region invalid_src = overlap.intersect(changed);
+ invalid_src.translate(delta);
+ changed.assign_union(invalid_src);
+
+ overlap.translate(delta);
+
+ Region nonoverlapped_copied = dest.union_(copied).subtract(overlap);
+ changed.assign_union(nonoverlapped_copied);
+
+ copied = overlap;
+ copy_delta = copy_delta.translate(delta);
+
+ return;
+}
+
+void SimpleUpdateTracker::subtract(const Region& region) {
+ copied.assign_subtract(region);
+ changed.assign_subtract(region);
+}
+
+void SimpleUpdateTracker::get_update(UpdateInfo* info, const Region& clip)
+{
+ copied.assign_subtract(changed);
+ info->changed = changed.intersect(clip);
+ info->copied = copied.intersect(clip);
+ info->copy_delta = copy_delta;
+}
+
+void SimpleUpdateTracker::flush_update(UpdateTracker &info,
+ const Region &cliprgn)
+{
+ Region copied_clipped = copied.intersect(cliprgn);
+ Region changed_clipped = changed.intersect(cliprgn);
+ copied.assign_subtract(copied_clipped);
+ changed.assign_subtract(changed_clipped);
+ if (!copied_clipped.is_empty()) {
+ info.add_copied(copied_clipped, copy_delta);
+ }
+ if (!changed_clipped.is_empty())
+ info.add_changed(changed_clipped);
+}
+
+void SimpleUpdateTracker::get_update(UpdateTracker &to) const {
+ if (!copied.is_empty()) {
+ to.add_copied(copied, copy_delta);
+ }
+ if (!changed.is_empty()) {
+ to.add_changed(changed);
+ }
+}
diff --git a/rfb/UpdateTracker.h b/rfb/UpdateTracker.h
new file mode 100644
index 00000000..5015a250
--- /dev/null
+++ b/rfb/UpdateTracker.h
@@ -0,0 +1,107 @@
+/* Copyright (C) 2002-2004 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_UPDATETRACKER_INCLUDED__
+#define __RFB_UPDATETRACKER_INCLUDED__
+
+#include <rfb/Rect.h>
+#include <rfb/Region.h>
+#include <rfb/PixelBuffer.h>
+
+namespace rfb {
+
+ class UpdateInfo {
+ public:
+ Region changed;
+ Region copied;
+ Point copy_delta;
+ bool is_empty() const {
+ return copied.is_empty() && changed.is_empty();
+ }
+ int numRects() const {
+ return copied.numRects() + changed.numRects();
+ }
+ };
+
+ class UpdateTracker {
+ public:
+ UpdateTracker() {};
+ virtual ~UpdateTracker() {};
+
+ virtual void add_changed(const Region &region) = 0;
+ virtual void add_copied(const Region &dest, const Point &delta) = 0;
+ };
+
+ class ClippedUpdateTracker : public UpdateTracker {
+ public:
+ ClippedUpdateTracker(UpdateTracker &child_) : child(child_) {};
+ ClippedUpdateTracker(UpdateTracker &child_,
+ const Region &cliprgn_) : child(child_), cliprgn(cliprgn_) {};
+ virtual ~ClippedUpdateTracker() {};
+
+ virtual void set_clip_region(const Region cliprgn_) {cliprgn = cliprgn_;};
+
+ virtual void add_changed(const Region &region);
+ virtual void add_copied(const Region &dest, const Point &delta);
+ protected:
+ UpdateTracker &child;
+ Region cliprgn;
+ };
+
+ class SimpleUpdateTracker : public UpdateTracker {
+ public:
+ SimpleUpdateTracker(bool use_copyrect=false);
+ virtual ~SimpleUpdateTracker();
+
+ virtual void enable_copyrect(bool enable);
+
+ virtual void add_changed(const Region &region);
+ virtual void add_copied(const Region &dest, const Point &delta);
+ virtual void subtract(const Region& region);
+
+ // Fill the supplied UpdateInfo structure with update information
+ virtual void get_update(UpdateInfo* info, const Region& cliprgn);
+
+ // Pass the current updates to the supplied tracker
+ virtual void get_update(UpdateTracker &to) const;
+
+ // Also removes the updates that are returned from this update tracker
+ virtual void flush_update(UpdateTracker &to, const Region &cliprgn);
+
+
+ // Get the changed/copied regions
+ const Region& get_changed() const {return changed;}
+ const Region& get_copied() const {return copied;}
+ const Point& get_delta() const {return copy_delta;}
+
+ // Move the entire update region by an offset
+ void translate(const Point& p) {changed.translate(p); copied.translate(p);}
+
+ virtual bool is_empty() const {return changed.is_empty() && copied.is_empty();}
+
+ virtual void clear() {changed.clear(); copied.clear();};
+ protected:
+ Region changed;
+ Region copied;
+ Point copy_delta;
+ bool copy_enabled;
+ };
+
+}
+
+#endif // __RFB_UPDATETRACKER_INCLUDED__
diff --git a/rfb/UserPasswdGetter.h b/rfb/UserPasswdGetter.h
new file mode 100644
index 00000000..c242ed0a
--- /dev/null
+++ b/rfb/UserPasswdGetter.h
@@ -0,0 +1,31 @@
+/* Copyright (C) 2002-2003 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_USERPASSWDGETTER_H__
+#define __RFB_USERPASSWDGETTER_H__
+namespace rfb {
+ class UserPasswdGetter {
+ public:
+ // getUserPasswd gets the username and password. This might
+ // involve a dialog, getpass(), etc. The user buffer pointer
+ // can be null, in which case no user name will be retrieved.
+ // The caller MUST delete [] the result(s) iff the
+ // call succeeds (returns true), and ignore them if failed.
+ virtual bool getUserPasswd(char** user, char** password)=0;
+ };
+}
+#endif
diff --git a/rfb/VNCSConnectionST.cxx b/rfb/VNCSConnectionST.cxx
new file mode 100644
index 00000000..3ae378e0
--- /dev/null
+++ b/rfb/VNCSConnectionST.cxx
@@ -0,0 +1,629 @@
+/* Copyright (C) 2002-2004 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 <rfb/VNCSConnectionST.h>
+#include <rfb/LogWriter.h>
+#include <rfb/secTypes.h>
+#include <rfb/ServerCore.h>
+#include <rfb/ComparingUpdateTracker.h>
+#define XK_MISCELLANY
+#define XK_XKB_KEYS
+#include <rfb/keysymdef.h>
+
+using namespace rfb;
+
+static LogWriter vlog("VNCSConnST");
+
+VNCSConnectionST::VNCSConnectionST(VNCServerST* server_, network::Socket *s,
+ bool reverse)
+ : sock(s), reverseConnection(reverse), server(server_),
+ image_getter(server->useEconomicTranslate),
+ drawRenderedCursor(false), removeRenderedCursor(false),
+ pointerEventTime(0), accessRights(AccessDefault)
+{
+ setStreams(&sock->inStream(), &sock->outStream());
+ peerEndpoint.buf = sock->getPeerEndpoint();
+ VNCServerST::connectionsLog.write(1,"accepted: %s", peerEndpoint.buf);
+
+ setSocketTimeouts();
+ lastEventTime = time(0);
+
+ // Initialise security
+ CharArray sec_types_str;
+ if (reverseConnection)
+ sec_types_str.buf = rfb::Server::rev_sec_types.getData();
+ else
+ sec_types_str.buf = rfb::Server::sec_types.getData();
+ std::list<int> sec_types = parseSecTypes(sec_types_str.buf);
+ std::list<int>::iterator i;
+ for (i=sec_types.begin(); i!=sec_types.end(); i++) {
+ addSecType(*i);
+ }
+
+ server->clients.push_front(this);
+}
+
+
+VNCSConnectionST::~VNCSConnectionST()
+{
+ // If we reach here then VNCServerST is deleting us!
+ VNCServerST::connectionsLog.write(1,"closed: %s (%s)",
+ peerEndpoint.buf, closeReason.buf);
+
+ // Release any keys the client still had pressed
+ std::set<rdr::U32>::iterator i;
+ for (i=pressedKeys.begin(); i!=pressedKeys.end(); i++)
+ server->desktop->keyEvent(*i, false);
+ if (server->pointerClient == this)
+ server->pointerClient = 0;
+
+ // Remove this client from the server
+ server->clients.remove(this);
+}
+
+
+// Methods called from VNCServerST
+
+bool VNCSConnectionST::init()
+{
+ try {
+ initialiseProtocol();
+ } catch (rdr::Exception& e) {
+ close(e.str());
+ return false;
+ }
+ return true;
+}
+
+void VNCSConnectionST::close(const char* reason)
+{
+ // Log the reason for the close
+ if (!closeReason.buf)
+ closeReason.buf = strDup(reason);
+ else
+ vlog.debug("second close: %s (%s)", peerEndpoint.buf, reason);
+
+ // Just shutdown the socket. This will cause processMessages to
+ // eventually fail, causing us and our socket to be deleted.
+ sock->shutdown();
+ setState(RFBSTATE_CLOSING);
+}
+
+
+bool VNCSConnectionST::processMessages()
+{
+ if (state() == RFBSTATE_CLOSING) return false;
+ try {
+ // - Now set appropriate socket timeouts and process data
+ setSocketTimeouts();
+ bool clientsReadyBefore = server->clientsReadyForUpdate();
+
+ while (getInStream()->checkNoWait(1)) {
+ processMsg();
+ }
+
+ if (!clientsReadyBefore && !requested.is_empty())
+ server->desktop->framebufferUpdateRequest();
+
+ return true;
+
+ } catch (rdr::EndOfStream&) {
+ close("Clean disconnection");
+ } catch (rdr::Exception &e) {
+ close(e.str());
+ }
+ return false;
+}
+
+void VNCSConnectionST::writeFramebufferUpdateOrClose()
+{
+ try {
+ writeFramebufferUpdate();
+ } catch(rdr::Exception &e) {
+ close(e.str());
+ }
+}
+
+void VNCSConnectionST::pixelBufferChange()
+{
+ try {
+ if (!authenticated()) return;
+ if (cp.width && cp.height && (server->pb->width() != cp.width ||
+ server->pb->height() != cp.height))
+ {
+ // We need to clip the next update to the new size, but also add any
+ // extra bits if it's bigger. If we wanted to do this exactly, something
+ // like the code below would do it, but at the moment we just update the
+ // entire new size. However, we do need to clip the renderedCursorRect
+ // because that might be added to updates in writeFramebufferUpdate().
+
+ //updates.intersect(server->pb->getRect());
+ //
+ //if (server->pb->width() > cp.width)
+ // updates.add_changed(Rect(cp.width, 0, server->pb->width(),
+ // server->pb->height()));
+ //if (server->pb->height() > cp.height)
+ // updates.add_changed(Rect(0, cp.height, cp.width,
+ // server->pb->height()));
+
+ renderedCursorRect = renderedCursorRect.intersect(server->pb->getRect());
+
+ cp.width = server->pb->width();
+ cp.height = server->pb->height();
+ if (!writer()->writeSetDesktopSize()) {
+ close("Client does not support desktop resize");
+ return;
+ }
+ }
+ // Just update the whole screen at the moment because we're too lazy to
+ // work out what's actually changed.
+ updates.clear();
+ updates.add_changed(server->pb->getRect());
+ vlog.debug("pixel buffer changed - re-initialising image getter");
+ image_getter.init(server->pb, cp.pf(), writer());
+ if (writer()->needFakeUpdate())
+ writeFramebufferUpdate();
+ } catch(rdr::Exception &e) {
+ close(e.str());
+ }
+}
+
+void VNCSConnectionST::setColourMapEntriesOrClose(int firstColour,int nColours)
+{
+ try {
+ setColourMapEntries(firstColour, nColours);
+ } catch(rdr::Exception& e) {
+ close(e.str());
+ }
+}
+
+void VNCSConnectionST::bell()
+{
+ try {
+ if (state() == RFBSTATE_NORMAL) writer()->writeBell();
+ } catch(rdr::Exception& e) {
+ close(e.str());
+ }
+}
+
+void VNCSConnectionST::serverCutText(const char *str, int len)
+{
+ try {
+ if (!(accessRights & AccessCutText)) return;
+ if (!rfb::Server::sendCutText) return;
+ if (state() == RFBSTATE_NORMAL)
+ writer()->writeServerCutText(str, len);
+ } catch(rdr::Exception& e) {
+ close(e.str());
+ }
+}
+
+void VNCSConnectionST::setCursorOrClose()
+{
+ try {
+ setCursor();
+ } catch(rdr::Exception& e) {
+ close(e.str());
+ }
+}
+
+
+int VNCSConnectionST::checkIdleTimeout()
+{
+ int idleTimeout = rfb::Server::idleTimeout;
+ if (idleTimeout == 0) return 0;
+ if (state() != RFBSTATE_NORMAL && idleTimeout < 15)
+ idleTimeout = 15; // minimum of 15 seconds while authenticating
+ time_t now = time(0);
+ if (now < lastEventTime) {
+ // Someone must have set the time backwards. Set lastEventTime so that the
+ // idleTimeout will count from now.
+ vlog.info("Time has gone backwards - resetting idle timeout");
+ lastEventTime = now;
+ }
+ int timeLeft = lastEventTime + idleTimeout - now;
+ if (timeLeft < -60) {
+ // Our callback is over a minute late - someone must have set the time
+ // forwards. Set lastEventTime so that the idleTimeout will count from
+ // now.
+ vlog.info("Time has gone forwards - resetting idle timeout");
+ lastEventTime = now;
+ return idleTimeout;
+ }
+ if (timeLeft <= 0) {
+ close("Idle timeout");
+ return 0;
+ }
+ return timeLeft * 1000;
+}
+
+// renderedCursorChange() is called whenever the server-side rendered cursor
+// changes shape or position. It ensures that the next update will clean up
+// the old rendered cursor and if necessary draw the new rendered cursor.
+
+void VNCSConnectionST::renderedCursorChange()
+{
+ if (state() != RFBSTATE_NORMAL) return;
+ removeRenderedCursor = true;
+ if (needRenderedCursor())
+ drawRenderedCursor = true;
+}
+
+// needRenderedCursor() returns true if this client needs the server-side
+// rendered cursor. This may be because it does not support local cursor or
+// because the current cursor position has not been set by this client.
+// Unfortunately we can't know for sure when the current cursor position has
+// been set by this client. We guess that this is the case when the current
+// cursor position is the same as the last pointer event from this client, or
+// if it is a very short time since this client's last pointer event (up to a
+// second). [ Ideally we should do finer-grained timing here and make the time
+// configurable, but I don't think it's that important. ]
+
+bool VNCSConnectionST::needRenderedCursor()
+{
+ return (state() == RFBSTATE_NORMAL
+ && (!cp.supportsLocalCursor
+ || (!server->cursorPos.equals(pointerEventPos) &&
+ (time(0) - pointerEventTime) > 0)));
+}
+
+
+void VNCSConnectionST::approveConnectionOrClose(bool accept,
+ const char* reason)
+{
+ try {
+ approveConnection(accept, reason);
+ } catch (rdr::Exception& e) {
+ close(e.str());
+ }
+}
+
+
+
+// -=- Callbacks from SConnection
+
+void VNCSConnectionST::versionReceived() {
+ CharArray address(sock->getPeerAddress());
+ if ((rfb::Server::blacklistLevel == 1) &&
+ server->blHosts->isBlackmarked(address.buf)) {
+ server->connectionsLog.error("blacklisted: %s", address.buf);
+ throwConnFailedException("Too many security failures");
+ }
+}
+
+SSecurity* VNCSConnectionST::getSSecurity(int secType) {
+ if (!server->securityFactory)
+ throw rdr::Exception("no SSecurityFactory registered!");
+ return server->securityFactory->getSSecurity(secType, reverseConnection);
+}
+
+void VNCSConnectionST::authSuccess()
+{
+ lastEventTime = time(0);
+
+ // - Authentication succeeded - clear from blacklist
+ CharArray name; name.buf = sock->getPeerAddress();
+ server->blHosts->clearBlackmark(name.buf);
+
+ server->startDesktop();
+
+ // - Set the connection parameters appropriately
+ cp.width = server->pb->width();
+ cp.height = server->pb->height();
+ cp.setName(server->getName());
+
+ // - Set the default pixel format
+ cp.setPF(server->pb->getPF());
+ char buffer[256];
+ cp.pf().print(buffer, 256);
+ vlog.info("Server default pixel format %s", buffer);
+ image_getter.init(server->pb, cp.pf(), 0);
+
+ // - Mark the entire display as "dirty"
+ updates.add_changed(server->pb->getRect());
+}
+
+void VNCSConnectionST::queryConnection(const char* userName)
+{
+ // - Does the client have the right to bypass the query?
+ if (reverseConnection || !rfb::Server::queryConnect ||
+ (accessRights & AccessNoQuery))
+ {
+ approveConnection(true);
+ return;
+ }
+
+ CharArray reason;
+ VNCServerST::queryResult qr = server->queryConnection(sock, userName,
+ &reason.buf);
+ if (qr == VNCServerST::PENDING) return;
+ approveConnection(qr == VNCServerST::ACCEPT, reason.buf);
+}
+
+void VNCSConnectionST::clientInit(bool shared)
+{
+ lastEventTime = time(0);
+ if (rfb::Server::alwaysShared || reverseConnection) shared = true;
+ if (rfb::Server::neverShared) shared = false;
+ if (!shared) {
+ if (rfb::Server::disconnectClients) {
+ // - Close all the other connected clients
+ vlog.debug("non-shared connection - closing clients");
+ server->closeClients("Non-shared connection requested", getSock());
+ } else {
+ // - Refuse this connection if there are existing clients, in addition to this one
+ if (server->authClientCount() > 1) {
+ close("Server is already in use");
+ return;
+ }
+ }
+ }
+ SConnection::clientInit(shared);
+}
+
+void VNCSConnectionST::setPixelFormat(const PixelFormat& pf)
+{
+ SConnection::setPixelFormat(pf);
+ char buffer[256];
+ pf.print(buffer, 256);
+ vlog.info("Client pixel format %s", buffer);
+ image_getter.init(server->pb, pf, writer());
+ setCursor();
+}
+
+void VNCSConnectionST::pointerEvent(int x, int y, int buttonMask)
+{
+ pointerEventTime = lastEventTime = time(0);
+ if (!(accessRights & AccessPtrEvents)) return;
+ if (!rfb::Server::acceptPointerEvents) return;
+ if (!server->pointerClient || server->pointerClient == this) {
+ pointerEventPos = Point(x, y);
+ if (buttonMask)
+ server->pointerClient = this;
+ else
+ server->pointerClient = 0;
+ server->desktop->pointerEvent(pointerEventPos, buttonMask);
+ }
+}
+
+
+class VNCSConnectionSTShiftPresser {
+public:
+ VNCSConnectionSTShiftPresser(SDesktop* desktop_)
+ : desktop(desktop_), pressed(false) {}
+ ~VNCSConnectionSTShiftPresser() {
+ if (pressed) { desktop->keyEvent(XK_Shift_L, false); }
+ }
+ void press() {
+ desktop->keyEvent(XK_Shift_L, true);
+ pressed = true;
+ }
+ SDesktop* desktop;
+ bool pressed;
+};
+
+// keyEvent() - record in the pressedKeys which keys were pressed. Allow
+// multiple down events (for autorepeat), but only allow a single up event.
+void VNCSConnectionST::keyEvent(rdr::U32 key, bool down) {
+ lastEventTime = time(0);
+ if (!(accessRights & AccessKeyEvents)) return;
+ if (!rfb::Server::acceptKeyEvents) return;
+
+ // Turn ISO_Left_Tab into shifted Tab.
+ VNCSConnectionSTShiftPresser shiftPresser(server->desktop);
+ if (key == XK_ISO_Left_Tab) {
+ if (pressedKeys.find(XK_Shift_L) == pressedKeys.end() &&
+ pressedKeys.find(XK_Shift_R) == pressedKeys.end())
+ shiftPresser.press();
+ key = XK_Tab;
+ }
+
+ if (down) {
+ pressedKeys.insert(key);
+ } else {
+ if (!pressedKeys.erase(key)) return;
+ }
+ server->desktop->keyEvent(key, down);
+}
+
+void VNCSConnectionST::clientCutText(const char* str, int len)
+{
+ if (!(accessRights & AccessCutText)) return;
+ if (!rfb::Server::acceptCutText) return;
+ server->desktop->clientCutText(str, len);
+}
+
+void VNCSConnectionST::framebufferUpdateRequest(const Rect& r,bool incremental)
+{
+ if (!(accessRights & AccessView)) return;
+
+ SConnection::framebufferUpdateRequest(r, incremental);
+
+ Region reqRgn(r);
+ requested.assign_union(reqRgn);
+
+ if (!incremental) {
+ // Non-incremental update - treat as if area requested has changed
+ updates.add_changed(reqRgn);
+ server->comparer->add_changed(reqRgn);
+ }
+
+ writeFramebufferUpdate();
+}
+
+void VNCSConnectionST::setInitialColourMap()
+{
+ setColourMapEntries(0, 0);
+}
+
+// supportsLocalCursor() is called whenever the status of
+// cp.supportsLocalCursor has changed. If the client does now support local
+// cursor, we make sure that the old server-side rendered cursor is cleaned up
+// and the cursor is sent to the client.
+
+void VNCSConnectionST::supportsLocalCursor()
+{
+ if (cp.supportsLocalCursor) {
+ removeRenderedCursor = true;
+ drawRenderedCursor = false;
+ setCursor();
+ }
+}
+
+void VNCSConnectionST::writeSetCursorCallback()
+{
+ rdr::U8* transData = writer()->getImageBuf(server->cursor.area());
+ image_getter.translatePixels(server->cursor.data, transData,
+ server->cursor.area());
+
+ writer()->writeSetCursor(server->cursor.width(),
+ server->cursor.height(),
+ server->cursor.hotspot.x,
+ server->cursor.hotspot.y,
+ transData, server->cursor.mask.buf);
+}
+
+
+void VNCSConnectionST::writeFramebufferUpdate()
+{
+ if (state() != RFBSTATE_NORMAL || requested.is_empty()) return;
+
+ server->checkUpdate();
+
+ // If the previous position of the rendered cursor overlaps the source of the
+ // copy, then when the copy happens the corresponding rectangle in the
+ // destination will be wrong, so add it to the changed region.
+
+ if (!updates.get_copied().is_empty() && !renderedCursorRect.is_empty()) {
+ Rect bogusCopiedCursor = (renderedCursorRect.translate(updates.get_delta())
+ .intersect(server->pb->getRect()));
+ if (!updates.get_copied().intersect(bogusCopiedCursor).is_empty()) {
+ updates.add_changed(bogusCopiedCursor);
+ }
+ }
+
+ // If we need to remove the old rendered cursor, just add the rectangle to
+ // the changed region.
+
+ if (removeRenderedCursor) {
+ updates.add_changed(renderedCursorRect);
+ renderedCursorRect.clear();
+ removeRenderedCursor = false;
+ }
+
+ // Return if there is nothing to send the client.
+
+ if (updates.is_empty() && !writer()->needFakeUpdate() && !drawRenderedCursor)
+ return;
+
+ // If the client needs a server-side rendered cursor, work out the cursor
+ // rectangle. If it's empty then don't bother drawing it, but if it overlaps
+ // with the update region, we need to draw the rendered cursor regardless of
+ // whether it has changed.
+
+ if (needRenderedCursor()) {
+ renderedCursorRect
+ = (server->renderedCursor.getRect(server->renderedCursorTL)
+ .intersect(requested.get_bounding_rect()));
+
+ if (renderedCursorRect.is_empty()) {
+ drawRenderedCursor = false;
+ } else if (!updates.get_changed().union_(updates.get_copied())
+ .intersect(renderedCursorRect).is_empty()) {
+ drawRenderedCursor = true;
+ }
+
+ // We could remove the new cursor rect from updates here. It's not clear
+ // whether this is worth it. If we do remove it, then we won't draw over
+ // the same bit of screen twice, but we have the overhead of a more complex
+ // region.
+
+ //if (drawRenderedCursor)
+ // updates.subtract(renderedCursorRect);
+ }
+
+ UpdateInfo update;
+ updates.enable_copyrect(cp.useCopyRect);
+ updates.get_update(&update, requested);
+ if (!update.is_empty() || writer()->needFakeUpdate() || drawRenderedCursor) {
+ int nRects = update.numRects() + (drawRenderedCursor ? 1 : 0);
+ writer()->writeFramebufferUpdateStart(nRects);
+ Region updatedRegion;
+ writer()->writeRects(update, &image_getter, &updatedRegion);
+ updates.subtract(updatedRegion);
+ if (drawRenderedCursor)
+ writeRenderedCursorRect();
+ writer()->writeFramebufferUpdateEnd();
+ requested.clear();
+ }
+}
+
+
+// writeRenderedCursorRect() writes a single rectangle drawing the rendered
+// cursor on the client.
+
+void VNCSConnectionST::writeRenderedCursorRect()
+{
+ image_getter.setPixelBuffer(&server->renderedCursor);
+ image_getter.setOffset(server->renderedCursorTL);
+
+ Rect actual;
+ writer()->writeRect(renderedCursorRect, &image_getter, &actual);
+
+ image_getter.setPixelBuffer(server->pb);
+ image_getter.setOffset(Point(0,0));
+
+ drawRenderedCursor = false;
+}
+
+void VNCSConnectionST::setColourMapEntries(int firstColour, int nColours)
+{
+ if (!readyForSetColourMapEntries) return;
+ if (server->pb->getPF().trueColour) return;
+
+ image_getter.setColourMapEntries(firstColour, nColours, writer());
+
+ if (cp.pf().trueColour) {
+ updates.add_changed(server->pb->getRect());
+ }
+}
+
+
+// setCursor() is called whenever the cursor has changed shape or pixel format.
+// If the client supports local cursor then it will arrange for the cursor to
+// be sent to the client.
+
+void VNCSConnectionST::setCursor()
+{
+ if (state() != RFBSTATE_NORMAL || !cp.supportsLocalCursor) return;
+ writer()->cursorChange(this);
+ if (writer()->needFakeUpdate())
+ writeFramebufferUpdate();
+}
+
+void VNCSConnectionST::setSocketTimeouts()
+{
+ int timeoutms = rfb::Server::clientWaitTimeMillis;
+ if (timeoutms == 0 || timeoutms > rfb::Server::idleTimeout * 1000) {
+ timeoutms = rfb::Server::idleTimeout * 1000;
+ if (timeoutms == 0)
+ timeoutms = -1;
+ }
+ sock->inStream().setTimeout(timeoutms);
+ sock->outStream().setTimeout(timeoutms);
+}
diff --git a/rfb/VNCSConnectionST.h b/rfb/VNCSConnectionST.h
new file mode 100644
index 00000000..ba480e59
--- /dev/null
+++ b/rfb/VNCSConnectionST.h
@@ -0,0 +1,168 @@
+/* Copyright (C) 2002-2004 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.
+ */
+
+//
+// VNCSConnectionST is our derived class of SConnection for VNCServerST - there
+// is one for each connected client. We think of VNCSConnectionST as part of
+// the VNCServerST implementation, so its methods are allowed full access to
+// members of VNCServerST.
+//
+
+#ifndef __RFB_VNCSCONNECTIONST_H__
+#define __RFB_VNCSCONNECTIONST_H__
+
+#include <set>
+#include <rfb/SConnection.h>
+#include <rfb/SMsgWriter.h>
+#include <rfb/TransImageGetter.h>
+#include <rfb/VNCServerST.h>
+
+namespace rfb {
+ class VNCSConnectionST : public SConnection,
+ public WriteSetCursorCallback {
+ public:
+ VNCSConnectionST(VNCServerST* server_, network::Socket* s, bool reverse);
+ virtual ~VNCSConnectionST();
+
+ // Methods called from VNCServerST. None of these methods ever knowingly
+ // throw an exception.
+
+ // Unless otherwise stated, the SConnectionST may not be valid after any of
+ // these methods are called, since they catch exceptions and may have
+ // called close() which deletes the object.
+
+ // init() must be called to initialise the protocol. If it fails it
+ // returns false, and close() will have been called.
+ bool init();
+
+ // close() shuts down the socket to the client and deletes the
+ // SConnectionST object.
+ void close(const char* reason);
+
+ // processMessages() processes incoming messages from the client, invoking
+ // various callbacks as a result. It continues to process messages until
+ // reading might block. Returns true if the client is still valid &
+ // active, or false if it has disconnected or an error has occurred.
+ bool processMessages();
+
+ void writeFramebufferUpdateOrClose();
+ void pixelBufferChange();
+ void setColourMapEntriesOrClose(int firstColour, int nColours);
+ void bell();
+ void serverCutText(const char *str, int len);
+ void setCursorOrClose();
+
+ // checkIdleTimeout() returns the number of milliseconds left until the
+ // idle timeout expires. If it has expired, the connection is closed and
+ // zero is returned. Zero is also returned if there is no idle timeout.
+ int checkIdleTimeout();
+
+ // The following methods never throw exceptions nor do they ever delete the
+ // SConnectionST object.
+
+ // renderedCursorChange() is called whenever the server-side rendered
+ // cursor changes shape or position. It ensures that the next update will
+ // clean up the old rendered cursor and if necessary draw the new rendered
+ // cursor.
+ void renderedCursorChange();
+
+ // needRenderedCursor() returns true if this client needs the server-side
+ // rendered cursor. This may be because it does not support local cursor
+ // or because the current cursor position has not been set by this client.
+ bool needRenderedCursor();
+
+ network::Socket* getSock() { return sock; }
+ bool readyForUpdate() { return !requested.is_empty(); }
+ void add_changed(const Region& region) { updates.add_changed(region); }
+ void add_copied(const Region& dest, const Point& delta) {
+ updates.add_copied(dest, delta);
+ }
+
+ const char* getPeerEndpoint() const {return peerEndpoint.buf;}
+
+ // approveConnectionOrClose() is called some time after
+ // VNCServerST::queryConnection() has returned with PENDING to accept or
+ // reject the connection. The accept argument should be true for
+ // acceptance, or false for rejection, in which case a string reason may
+ // also be given.
+
+ void approveConnectionOrClose(bool accept, const char* reason);
+
+ private:
+ // SConnection callbacks
+
+ // These methods are invoked as callbacks from processMsg(). Note that
+ // none of these methods should call any of the above methods which may
+ // delete the SConnectionST object.
+
+ virtual void versionReceived();
+ virtual SSecurity* getSSecurity(int secType);
+ virtual void authSuccess();
+ virtual void queryConnection(const char* userName);
+ virtual void clientInit(bool shared);
+ virtual void setPixelFormat(const PixelFormat& pf);
+ virtual void pointerEvent(int x, int y, int buttonMask);
+ virtual void keyEvent(rdr::U32 key, bool down);
+ virtual void framebufferUpdateRequest(const Rect& r, bool incremental);
+ virtual void clientCutText(const char* str, int len);
+ virtual void setInitialColourMap();
+ virtual void supportsLocalCursor();
+
+ // setAccessRights() allows a security package to limit the access rights
+ // of a VNCSConnectioST to the server. These access rights are applied
+ // such that the actual rights granted are the minimum of the server's
+ // default access settings and the connection's access settings.
+ virtual void setAccessRights(AccessRights ar) {accessRights=ar;}
+
+ // WriteSetCursorCallback
+ virtual void writeSetCursorCallback();
+
+ // Internal methods
+
+ // writeFramebufferUpdate() attempts to write a framebuffer update to the
+ // client.
+
+ void writeFramebufferUpdate();
+
+ void writeRenderedCursorRect();
+ void setColourMapEntries(int firstColour, int nColours);
+ void setCursor();
+ void setSocketTimeouts();
+
+ network::Socket* sock;
+ CharArray peerEndpoint;
+ bool reverseConnection;
+ VNCServerST* server;
+ SimpleUpdateTracker updates;
+ TransImageGetter image_getter;
+ Region requested;
+ bool drawRenderedCursor, removeRenderedCursor;
+ Rect renderedCursorRect;
+
+ std::set<rdr::U32> pressedKeys;
+
+ time_t lastEventTime;
+ time_t pointerEventTime;
+ Point pointerEventPos;
+
+ AccessRights accessRights;
+
+ CharArray closeReason;
+ };
+}
+#endif
diff --git a/rfb/VNCServer.h b/rfb/VNCServer.h
new file mode 100644
index 00000000..e80044ce
--- /dev/null
+++ b/rfb/VNCServer.h
@@ -0,0 +1,86 @@
+/* Copyright (C) 2002-2004 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.
+ */
+//
+// VNCServer - abstract interface implemented by the RFB library. The back-end
+// code calls the relevant methods as appropriate.
+
+#ifndef __RFB_VNCSERVER_H__
+#define __RFB_VNCSERVER_H__
+
+#include <rfb/UpdateTracker.h>
+#include <rfb/SSecurity.h>
+
+namespace rfb {
+
+ class VNCServer : public UpdateTracker {
+ public:
+
+ // setPixelBuffer() tells the server to use the given pixel buffer. If
+ // this differs in size from the previous pixel buffer, this may result in
+ // protocol messages being sent, or clients being disconnected.
+ virtual void setPixelBuffer(PixelBuffer* pb) = 0;
+
+ // setColourMapEntries() tells the server that some entries in the colour
+ // map have changed. The server will retrieve them via the PixelBuffer's
+ // ColourMap object. This may result in protocol messages being sent.
+ // If nColours is 0, this means the rest of the colour map.
+ virtual void setColourMapEntries(int firstColour=0, int nColours=0) = 0;
+
+ // serverCutText() tells the server that the cut text has changed. This
+ // will normally be sent to all clients.
+ virtual void serverCutText(const char* str, int len) = 0;
+
+ // bell() tells the server that it should make all clients make a bell sound.
+ virtual void bell() = 0;
+
+ // clientsReadyForUpdate() returns true if there is at least one client
+ // waiting for an update, false if no clients are ready.
+ virtual bool clientsReadyForUpdate() = 0;
+
+ // - Close all currently-connected clients, by calling
+ // their close() method with the supplied reason.
+ virtual void closeClients(const char* reason) = 0;
+
+ // tryUpdate() causes the server to attempt to send updates to any waiting
+ // clients.
+ virtual void tryUpdate() = 0;
+
+ // setCursor() tells the server that the cursor has changed. The
+ // cursorData argument contains width*height pixel values in the pixel
+ // buffer's format. The mask argument is a bitmask with a 1-bit meaning
+ // the corresponding pixel in cursorData is valid. The mask consists of
+ // left-to-right, top-to-bottom scanlines, where each scanline is padded to
+ // a whole number of bytes [(width+7)/8]. Within each byte the most
+ // significant bit represents the leftmost pixel, and the bytes are simply
+ // in left-to-right order. The server takes its own copy of the data in
+ // cursorData and mask.
+ virtual void setCursor(int width, int height, int hotspotX, int hotspotY,
+ void* cursorData, void* mask) = 0;
+
+ // setCursorPos() tells the server the current position of the cursor.
+ virtual void setCursorPos(int x, int y) = 0;
+
+ // setSSecurityFactory() tells the server which factory to use when
+ // attempting to authenticate connections.
+ virtual void setSSecurityFactory(SSecurityFactory* f) = 0;
+
+ // setName() tells the server what desktop title to supply to clients
+ virtual void setName(const char* name) = 0;
+ };
+}
+#endif
diff --git a/rfb/VNCServerST.cxx b/rfb/VNCServerST.cxx
new file mode 100644
index 00000000..b3f9e88e
--- /dev/null
+++ b/rfb/VNCServerST.cxx
@@ -0,0 +1,430 @@
+/* Copyright (C) 2002-2004 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.
+ */
+
+// -=- Single-Threaded VNC Server implementation
+
+
+// Note about how sockets get closed:
+//
+// Closing sockets to clients is non-trivial because the code which calls
+// VNCServerST must explicitly know about all the sockets (so that it can block
+// on them appropriately). However, VNCServerST may want to close clients for
+// a number of reasons, and from a variety of entry points. The simplest is
+// when processSocketEvent() is called for a client, and the remote end has
+// closed its socket. A more complex reason is when processSocketEvent() is
+// called for a client which has just sent a ClientInit with the shared flag
+// set to false - in this case we want to close all other clients. Yet another
+// reason for disconnecting clients is when the desktop size has changed as a
+// result of a call to setPixelBuffer().
+//
+// Because we don't want to mess up any data structures which the calling code
+// may maintain regarding sockets with data to process, we can't just delete a
+// socket when we decide to close the connection to a client. Instead, we only
+// go as far as calling shutdown() on the socket. This should ensure that
+// eventually the calling code will get round to calling processSocketEvent()
+// for that socket. Then we can delete the VNCSConnectionST object and its
+// associated network::Socket object, and return false from that call to let
+// the calling code know that the socket has been deleted. This is the only
+// way that these objects get deleted.
+//
+// It is possible that there are platforms where calling shutdown() cannot
+// guarantee that processSocketEvent() will be called - if so then it may be
+// necessary to introduce some kind of "socket closure callback", but we'll
+// only do that if it proves absolutely necessary.
+//
+// One minor complication is that we don't allocate a VNCSConnectionST object
+// for a blacklisted host (since we want to minimise the resources used for
+// dealing with such a connection). So we maintain a separate list of
+// closingSockets for this purpose.
+
+
+#include <rfb/ServerCore.h>
+#include <rfb/VNCServerST.h>
+#include <rfb/VNCSConnectionST.h>
+#include <rfb/ComparingUpdateTracker.h>
+#include <rfb/SSecurityFactoryStandard.h>
+#include <rfb/util.h>
+
+#include <rdr/types.h>
+
+using namespace rfb;
+
+static LogWriter slog("VNCServerST");
+LogWriter VNCServerST::connectionsLog("Connections");
+static SSecurityFactoryStandard defaultSecurityFactory;
+
+//
+// -=- VNCServerST Implementation
+//
+
+// -=- Constructors/Destructor
+
+VNCServerST::VNCServerST(const char* name_, SDesktop* desktop_,
+ SSecurityFactory* sf)
+ : blHosts(&blacklist), desktop(desktop_), desktopStarted(false), pb(0),
+ name(strDup(name_)), pointerClient(0), comparer(0),
+ renderedCursorInvalid(false),
+ securityFactory(sf ? sf : &defaultSecurityFactory),
+ queryConnectionHandler(0), useEconomicTranslate(false)
+{
+ slog.debug("creating single-threaded server %s", name.buf);
+}
+
+VNCServerST::~VNCServerST()
+{
+ slog.debug("shutting down server %s", name.buf);
+
+ // Close any active clients, with appropriate logging & cleanup
+ closeClients("Server shutdown");
+
+ // Delete all the clients, and their sockets, and any closing sockets
+ // NB: Deleting a client implicitly removes it from the clients list
+ while (!clients.empty()) {
+ delete clients.front()->getSock();
+ delete clients.front();
+ }
+ while (!closingSockets.empty()) {
+ delete closingSockets.front();
+ closingSockets.pop_front();
+ }
+
+ // Stop the desktop object if active, *only* after deleting all clients!
+ if (desktopStarted) {
+ desktopStarted = false;
+ desktop->stop();
+ }
+
+ delete comparer;
+}
+
+
+// SocketServer methods
+
+void VNCServerST::addClient(network::Socket* sock)
+{
+ addClient(sock, false);
+}
+
+void VNCServerST::addClient(network::Socket* sock, bool reverse)
+{
+ // - Check the connection isn't black-marked
+ // *** do this in getSecurity instead?
+ CharArray address(sock->getPeerAddress());
+ if ((rfb::Server::blacklistLevel == 2) &&
+ blHosts->isBlackmarked(address.buf)) {
+ connectionsLog.error("blacklisted: %s", address.buf);
+ try {
+ SConnection::writeConnFailedFromScratch("Too many security failures",
+ &sock->outStream());
+ } catch (rdr::Exception&) {
+ }
+ sock->shutdown();
+ closingSockets.push_back(sock);
+ return;
+ }
+
+ VNCSConnectionST* client = new VNCSConnectionST(this, sock, reverse);
+ client->init();
+}
+
+bool VNCServerST::processSocketEvent(network::Socket* sock)
+{
+ // - Find the appropriate VNCSConnectionST and process the event
+ std::list<VNCSConnectionST*>::iterator ci;
+ for (ci = clients.begin(); ci != clients.end(); ci++) {
+ if ((*ci)->getSock() == sock) {
+ if ((*ci)->processMessages())
+ return true;
+ // processMessages failed, so delete the client
+ delete *ci;
+ break;
+ }
+ }
+
+ // - If no client is using the Socket then delete it
+ closingSockets.remove(sock);
+ delete sock;
+
+ // - Check that the desktop object is still required
+ if (authClientCount() == 0 && desktopStarted) {
+ slog.debug("no authenticated clients - stopping desktop");
+ desktopStarted = false;
+ desktop->stop();
+ }
+
+ // - Inform the caller not to continue handling the Socket
+ return false;
+}
+
+int VNCServerST::checkTimeouts()
+{
+ int timeout = 0;
+ std::list<VNCSConnectionST*>::iterator ci, ci_next;
+ for (ci=clients.begin();ci!=clients.end();ci=ci_next) {
+ ci_next = ci; ci_next++;
+ soonestTimeout(&timeout, (*ci)->checkIdleTimeout());
+ }
+ return timeout;
+}
+
+
+// VNCServer methods
+
+void VNCServerST::setPixelBuffer(PixelBuffer* pb_)
+{
+ pb = pb_;
+ delete comparer;
+ comparer = 0;
+
+ if (pb) {
+ comparer = new ComparingUpdateTracker(pb);
+ cursor.setPF(pb->getPF());
+ renderedCursor.setPF(pb->getPF());
+
+ std::list<VNCSConnectionST*>::iterator ci, ci_next;
+ for (ci=clients.begin();ci!=clients.end();ci=ci_next) {
+ ci_next = ci; ci_next++;
+ (*ci)->pixelBufferChange();
+ }
+ } else {
+ if (desktopStarted)
+ throw Exception("setPixelBuffer: null PixelBuffer when desktopStarted?");
+ }
+}
+
+void VNCServerST::setColourMapEntries(int firstColour, int nColours)
+{
+ std::list<VNCSConnectionST*>::iterator ci, ci_next;
+ for (ci = clients.begin(); ci != clients.end(); ci = ci_next) {
+ ci_next = ci; ci_next++;
+ (*ci)->setColourMapEntriesOrClose(firstColour, nColours);
+ }
+}
+
+void VNCServerST::bell()
+{
+ std::list<VNCSConnectionST*>::iterator ci, ci_next;
+ for (ci = clients.begin(); ci != clients.end(); ci = ci_next) {
+ ci_next = ci; ci_next++;
+ (*ci)->bell();
+ }
+}
+
+void VNCServerST::serverCutText(const char* str, int len)
+{
+ std::list<VNCSConnectionST*>::iterator ci, ci_next;
+ for (ci = clients.begin(); ci != clients.end(); ci = ci_next) {
+ ci_next = ci; ci_next++;
+ (*ci)->serverCutText(str, len);
+ }
+}
+
+void VNCServerST::add_changed(const Region& region)
+{
+ comparer->add_changed(region);
+}
+
+void VNCServerST::add_copied(const Region& dest, const Point& delta)
+{
+ comparer->add_copied(dest, delta);
+}
+
+bool VNCServerST::clientsReadyForUpdate()
+{
+ std::list<VNCSConnectionST*>::iterator ci;
+ for (ci = clients.begin(); ci != clients.end(); ci++) {
+ if ((*ci)->readyForUpdate())
+ return true;
+ }
+ return false;
+}
+
+void VNCServerST::tryUpdate()
+{
+ std::list<VNCSConnectionST*>::iterator ci, ci_next;
+ for (ci = clients.begin(); ci != clients.end(); ci = ci_next) {
+ ci_next = ci; ci_next++;
+ (*ci)->writeFramebufferUpdateOrClose();
+ }
+}
+
+void VNCServerST::setCursor(int width, int height, int newHotspotX,
+ int newHotspotY, void* data, void* mask)
+{
+ cursor.hotspot.x = newHotspotX;
+ cursor.hotspot.y = newHotspotY;
+ cursor.setSize(width, height);
+ memcpy(cursor.data, data, cursor.dataLen());
+ memcpy(cursor.mask.buf, mask, cursor.maskLen());
+
+ cursor.crop();
+
+ renderedCursorInvalid = true;
+
+ std::list<VNCSConnectionST*>::iterator ci, ci_next;
+ for (ci = clients.begin(); ci != clients.end(); ci = ci_next) {
+ ci_next = ci; ci_next++;
+ (*ci)->renderedCursorChange();
+ (*ci)->setCursorOrClose();
+ }
+}
+
+void VNCServerST::setCursorPos(int x, int y)
+{
+ if (cursorPos.x != x || cursorPos.y != y) {
+ cursorPos.x = x;
+ cursorPos.y = y;
+ renderedCursorInvalid = true;
+ std::list<VNCSConnectionST*>::iterator ci;
+ for (ci = clients.begin(); ci != clients.end(); ci++)
+ (*ci)->renderedCursorChange();
+ }
+}
+
+// Other public methods
+
+void VNCServerST::approveConnection(network::Socket* sock, bool accept,
+ const char* reason)
+{
+ std::list<VNCSConnectionST*>::iterator ci;
+ for (ci = clients.begin(); ci != clients.end(); ci++) {
+ if ((*ci)->getSock() == sock) {
+ (*ci)->approveConnectionOrClose(accept, reason);
+ return;
+ }
+ }
+}
+
+void VNCServerST::closeClients(const char* reason, network::Socket* except)
+{
+ std::list<VNCSConnectionST*>::iterator i, next_i;
+ for (i=clients.begin(); i!=clients.end(); i=next_i) {
+ next_i = i; next_i++;
+ if ((*i)->getSock() != except)
+ (*i)->close(reason);
+ }
+}
+
+void VNCServerST::getSockets(std::list<network::Socket*>* sockets)
+{
+ sockets->clear();
+ std::list<VNCSConnectionST*>::iterator ci;
+ for (ci = clients.begin(); ci != clients.end(); ci++) {
+ sockets->push_back((*ci)->getSock());
+ }
+ std::list<network::Socket*>::iterator si;
+ for (si = closingSockets.begin(); si != closingSockets.end(); si++) {
+ sockets->push_back(*si);
+ }
+}
+
+SConnection* VNCServerST::getSConnection(network::Socket* sock) {
+ std::list<VNCSConnectionST*>::iterator ci;
+ for (ci = clients.begin(); ci != clients.end(); ci++) {
+ if ((*ci)->getSock() == sock)
+ return *ci;
+ }
+ return 0;
+}
+
+
+// -=- Internal methods
+
+void VNCServerST::startDesktop()
+{
+ if (!desktopStarted) {
+ slog.debug("starting desktop");
+ desktop->start(this);
+ desktopStarted = true;
+ if (!pb)
+ throw Exception("SDesktop::start() did not set a valid PixelBuffer");
+ }
+}
+
+int VNCServerST::authClientCount() {
+ int count = 0;
+ std::list<VNCSConnectionST*>::iterator ci;
+ for (ci = clients.begin(); ci != clients.end(); ci++) {
+ if ((*ci)->authenticated())
+ count++;
+ }
+ return count;
+}
+
+inline bool VNCServerST::needRenderedCursor()
+{
+ std::list<VNCSConnectionST*>::iterator ci;
+ for (ci = clients.begin(); ci != clients.end(); ci++)
+ if ((*ci)->needRenderedCursor()) return true;
+ return false;
+}
+
+// checkUpdate() is called just before sending an update. It checks to see
+// what updates are pending and propagates them to the update tracker for each
+// client. It uses the ComparingUpdateTracker's compare() method to filter out
+// areas of the screen which haven't actually changed. It also checks the
+// state of the (server-side) rendered cursor, if necessary rendering it again
+// with the correct background.
+
+void VNCServerST::checkUpdate()
+{
+ bool renderCursor = needRenderedCursor();
+
+ if (comparer->is_empty() && !(renderCursor && renderedCursorInvalid))
+ return;
+
+ Region toCheck = comparer->get_changed().union_(comparer->get_copied());
+
+ if (renderCursor) {
+ Rect clippedCursorRect
+ = cursor.getRect(cursorTL()).intersect(pb->getRect());
+
+ if (!renderedCursorInvalid && (toCheck.intersect(clippedCursorRect)
+ .is_empty())) {
+ renderCursor = false;
+ } else {
+ renderedCursorTL = clippedCursorRect.tl;
+ renderedCursor.setSize(clippedCursorRect.width(),
+ clippedCursorRect.height());
+ toCheck.assign_union(clippedCursorRect);
+ }
+ }
+
+ pb->grabRegion(toCheck);
+
+ if (rfb::Server::compareFB)
+ comparer->compare();
+
+ if (renderCursor) {
+ pb->getImage(renderedCursor.data,
+ renderedCursor.getRect(renderedCursorTL));
+ renderedCursor.maskRect(cursor.getRect(cursorTL()
+ .subtract(renderedCursorTL)),
+ cursor.data, cursor.mask.buf);
+ renderedCursorInvalid = false;
+ }
+
+ std::list<VNCSConnectionST*>::iterator ci, ci_next;
+ for (ci = clients.begin(); ci != clients.end(); ci = ci_next) {
+ ci_next = ci; ci_next++;
+ (*ci)->add_copied(comparer->get_copied(), comparer->get_delta());
+ (*ci)->add_changed(comparer->get_changed());
+ }
+
+ comparer->clear();
+}
diff --git a/rfb/VNCServerST.h b/rfb/VNCServerST.h
new file mode 100644
index 00000000..6655a0d6
--- /dev/null
+++ b/rfb/VNCServerST.h
@@ -0,0 +1,233 @@
+/* Copyright (C) 2002-2004 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.
+ */
+
+// -=- VNCServerST.h
+
+// Single-threaded VNCServer implementation
+
+#ifndef __RFB_VNCSERVERST_H__
+#define __RFB_VNCSERVERST_H__
+
+#include <list>
+
+#include <rfb/SDesktop.h>
+#include <rfb/VNCServer.h>
+#include <rfb/Configuration.h>
+#include <rfb/LogWriter.h>
+#include <rfb/Blacklist.h>
+#include <rfb/Cursor.h>
+#include <network/Socket.h>
+
+namespace rfb {
+
+ class VNCSConnectionST;
+ class ComparingUpdateTracker;
+ class PixelBuffer;
+
+ class VNCServerST : public VNCServer, public network::SocketServer {
+ public:
+ // -=- Constructors
+
+ // Create a server exporting the supplied desktop.
+ VNCServerST(const char* name_, SDesktop* desktop_,
+ SSecurityFactory* securityFactory_=0);
+ virtual ~VNCServerST();
+
+
+ // Methods overridden from SocketServer
+
+ // - Run a client connection on the supplied socket
+ // This causes the server to allocate the required structures
+ // to handle a client connection, and to initialise the RFB
+ // protocol.
+ // NB: The server assumes ownership of the Socket object.
+
+ virtual void addClient(network::Socket* sock);
+
+ // - Process an input event on a particular Socket
+ // The platform-specific side of the server implementation calls
+ // this method whenever data arrives on one of the active
+ // network sockets.
+ // The method returns true if the Socket is still in use by the
+ // server, or false if it is no longer required and has been
+ // deleted.
+ // NB: If false is returned then the Socket is deleted and must
+ // not be accessed again!
+
+ virtual bool processSocketEvent(network::Socket* sock);
+
+ // - checkTimeouts() returns the number of milliseconds left until the next
+ // idle timeout expires. If any have already expired, the corresponding
+ // connections are closed. Zero is returned if there is no idle timeout.
+
+ virtual int checkTimeouts();
+
+
+ // Methods overridden from VNCServer
+
+ virtual void setPixelBuffer(PixelBuffer* pb);
+ virtual void setColourMapEntries(int firstColour=0, int nColours=0);
+ virtual void serverCutText(const char* str, int len);
+ virtual void add_changed(const Region &region);
+ virtual void add_copied(const Region &dest, const Point &delta);
+ virtual bool clientsReadyForUpdate();
+ virtual void tryUpdate();
+ virtual void setCursor(int width, int height, int hotspotX, int hotspotY,
+ void* cursorData, void* mask);
+ virtual void setCursorPos(int x, int y);
+ virtual void setSSecurityFactory(SSecurityFactory* f) {securityFactory=f;}
+
+ virtual void bell();
+
+ // - Close all currently-connected clients, by calling
+ // their close() method with the supplied reason.
+ virtual void closeClients(const char* reason) {closeClients(reason, 0);}
+
+ // VNCServerST-only methods
+
+ // If a particular VNCSConnectionST* is specified then
+ // that connection will NOT be closed.
+ void closeClients(const char* reason, network::Socket* sock);
+
+ // addClient() with an extra flag to say if this is a reverse connection to
+ // a listening client. Reverse connections are not authenticated and are
+ // always shared (unless the NeverShared parameter is set).
+
+ void addClient(network::Socket* sock, bool reverse);
+
+
+ // getSockets() gets a list of sockets. This can be used to generate an
+ // fd_set for calling select().
+
+ void getSockets(std::list<network::Socket*>* sockets);
+
+ // getSConnection() gets the SConnection for a particular Socket. If
+ // the Socket is not recognised then null is returned.
+
+ SConnection* getSConnection(network::Socket* sock);
+
+ // getDesktopSize() returns the size of the SDesktop exported by this
+ // server.
+ Point getDesktopSize() const {return desktop->getFbSize();}
+
+ // getName() returns the name of this VNC Server. NB: The value returned
+ // is the server's internal buffer which may change after any other methods
+ // are called - take a copy if necessary.
+ const char* getName() const {return name.buf;}
+
+ // setName() specifies the desktop name that the server should provide to
+ // clients
+ void setName(const char* name_) {name.replaceBuf(strDup(name_));}
+
+ // A QueryConnectionHandler, if supplied, is passed details of incoming
+ // connections to approve, reject, or query the user about.
+ //
+ // queryConnection() is called when a connection has been
+ // successfully authenticated. The sock and userName arguments identify
+ // the socket and the name of the authenticated user, if any. It should
+ // return ACCEPT if the connection should be accepted, REJECT if it should
+ // be rejected, or PENDING if a decision cannot yet be reached. If REJECT
+ // is returned, *reason can be set to a string describing the reason - this
+ // will be delete[]ed when it is finished with. If PENDING is returned,
+ // approveConnection() must be called some time later to accept or reject
+ // the connection.
+ enum queryResult { ACCEPT, REJECT, PENDING };
+ struct QueryConnectionHandler {
+ virtual ~QueryConnectionHandler() {}
+ virtual queryResult queryConnection(network::Socket* sock,
+ const char* userName,
+ char** reason) = 0;
+ };
+ void setQueryConnectionHandler(QueryConnectionHandler* qch) {
+ queryConnectionHandler = qch;
+ }
+
+ // queryConnection is called as described above, and either passes the
+ // request on to the registered handler, or accepts the connection if
+ // no handler has been specified.
+ virtual queryResult queryConnection(network::Socket* sock,
+ const char* userName,
+ char** reason) {
+ return queryConnectionHandler
+ ? queryConnectionHandler->queryConnection(sock, userName, reason)
+ : ACCEPT;
+ }
+
+ // approveConnection() is called by the active QueryConnectionHandler,
+ // some time after queryConnection() has returned with PENDING, to accept
+ // or reject the connection. The accept argument should be true for
+ // acceptance, or false for rejection, in which case a string reason may
+ // also be given.
+ void approveConnection(network::Socket* sock, bool accept,
+ const char* reason);
+
+ // setBlacklist() is called to replace the VNCServerST's internal
+ // Blacklist instance with another instance. This allows a single
+ // Blacklist to be shared by multiple VNCServerST instances.
+ void setBlacklist(Blacklist* bl) {blHosts = bl ? bl : &blacklist;}
+
+ // setEconomicTranslate() determines (for new connections) whether pixels
+ // should be translated for <=16bpp clients using a large lookup table (fast)
+ // or separate, smaller R, G and B tables (slower). If set to true, small tables
+ // are used, to save memory.
+ void setEconomicTranslate(bool et) { useEconomicTranslate = et; }
+
+ protected:
+
+ friend class VNCSConnectionST;
+
+ void startDesktop();
+
+ static LogWriter connectionsLog;
+ Blacklist blacklist;
+ Blacklist* blHosts;
+
+ SDesktop* desktop;
+ bool desktopStarted;
+ PixelBuffer* pb;
+
+ CharArray name;
+
+ std::list<VNCSConnectionST*> clients;
+ VNCSConnectionST* pointerClient;
+ std::list<network::Socket*> closingSockets;
+
+ ComparingUpdateTracker* comparer;
+
+ Point cursorPos;
+ Cursor cursor;
+ Point cursorTL() { return cursorPos.subtract(cursor.hotspot); }
+ Point renderedCursorTL;
+ ManagedPixelBuffer renderedCursor;
+ bool renderedCursorInvalid;
+
+ // - Check how many of the clients are authenticated.
+ int authClientCount();
+
+ bool needRenderedCursor();
+ void checkUpdate();
+
+ SSecurityFactory* securityFactory;
+ QueryConnectionHandler* queryConnectionHandler;
+ bool useEconomicTranslate;
+ };
+
+};
+
+#endif
+
diff --git a/rfb/ZRLEDecoder.cxx b/rfb/ZRLEDecoder.cxx
new file mode 100644
index 00000000..2dd4a129
--- /dev/null
+++ b/rfb/ZRLEDecoder.cxx
@@ -0,0 +1,91 @@
+/* Copyright (C) 2002-2003 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 <rfb/CMsgReader.h>
+#include <rfb/CMsgHandler.h>
+#include <rfb/ZRLEDecoder.h>
+
+using namespace rfb;
+
+#define EXTRA_ARGS CMsgHandler* handler
+#define FILL_RECT(r, p) handler->fillRect(r, p)
+#define IMAGE_RECT(r, p) handler->imageRect(r, p)
+#define BPP 8
+#include <rfb/zrleDecode.h>
+#undef BPP
+#define BPP 16
+#include <rfb/zrleDecode.h>
+#undef BPP
+#define BPP 32
+#include <rfb/zrleDecode.h>
+#define CPIXEL 24A
+#include <rfb/zrleDecode.h>
+#undef CPIXEL
+#define CPIXEL 24B
+#include <rfb/zrleDecode.h>
+#undef CPIXEL
+#undef BPP
+
+Decoder* ZRLEDecoder::create(CMsgReader* reader)
+{
+ return new ZRLEDecoder(reader);
+}
+
+ZRLEDecoder::ZRLEDecoder(CMsgReader* reader_) : reader(reader_)
+{
+}
+
+ZRLEDecoder::~ZRLEDecoder()
+{
+}
+
+void ZRLEDecoder::readRect(const Rect& r, CMsgHandler* handler)
+{
+ rdr::InStream* is = reader->getInStream();
+ rdr::U8* buf = reader->getImageBuf(64 * 64 * 4);
+ switch (reader->bpp()) {
+ case 8: zrleDecode8 (r, is, &zis, (rdr::U8*) buf, handler); break;
+ case 16: zrleDecode16(r, is, &zis, (rdr::U16*)buf, handler); break;
+ case 32:
+ {
+ const rfb::PixelFormat& pf = handler->cp.pf();
+ bool fitsInLS3Bytes = ((pf.redMax << pf.redShift) < (1<<24) &&
+ (pf.greenMax << pf.greenShift) < (1<<24) &&
+ (pf.blueMax << pf.blueShift) < (1<<24));
+
+ bool fitsInMS3Bytes = (pf.redShift > 7 &&
+ pf.greenShift > 7 &&
+ pf.blueShift > 7);
+
+ if ((fitsInLS3Bytes && !pf.bigEndian) ||
+ (fitsInMS3Bytes && pf.bigEndian))
+ {
+ zrleDecode24A(r, is, &zis, (rdr::U32*)buf, handler);
+ }
+ else if ((fitsInLS3Bytes && pf.bigEndian) ||
+ (fitsInMS3Bytes && !pf.bigEndian))
+ {
+ zrleDecode24B(r, is, &zis, (rdr::U32*)buf, handler);
+ }
+ else
+ {
+ zrleDecode32(r, is, &zis, (rdr::U32*)buf, handler);
+ }
+ break;
+ }
+ }
+}
diff --git a/rfb/ZRLEDecoder.h b/rfb/ZRLEDecoder.h
new file mode 100644
index 00000000..c8b1febf
--- /dev/null
+++ b/rfb/ZRLEDecoder.h
@@ -0,0 +1,37 @@
+/* Copyright (C) 2002-2003 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_ZRLEDECODER_H__
+#define __RFB_ZRLEDECODER_H__
+
+#include <rdr/ZlibInStream.h>
+#include <rfb/Decoder.h>
+
+namespace rfb {
+
+ class ZRLEDecoder : public Decoder {
+ public:
+ static Decoder* create(CMsgReader* reader);
+ virtual void readRect(const Rect& r, CMsgHandler* handler);
+ virtual ~ZRLEDecoder();
+ private:
+ ZRLEDecoder(CMsgReader* reader);
+ CMsgReader* reader;
+ rdr::ZlibInStream zis;
+ };
+}
+#endif
diff --git a/rfb/ZRLEEncoder.cxx b/rfb/ZRLEEncoder.cxx
new file mode 100644
index 00000000..1f288691
--- /dev/null
+++ b/rfb/ZRLEEncoder.cxx
@@ -0,0 +1,122 @@
+/* Copyright (C) 2002-2004 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 <rdr/OutStream.h>
+#include <rfb/Exception.h>
+#include <rfb/ImageGetter.h>
+#include <rfb/encodings.h>
+#include <rfb/ConnParams.h>
+#include <rfb/SMsgWriter.h>
+#include <rfb/ZRLEEncoder.h>
+#include <rfb/Configuration.h>
+
+using namespace rfb;
+
+rdr::MemOutStream* ZRLEEncoder::sharedMos = 0;
+int ZRLEEncoder::maxLen = 513 * 1024; // enough for width 2048 32-bit pixels
+
+IntParameter zlibLevel("ZlibLevel","Zlib compression level",-1);
+
+#define EXTRA_ARGS ImageGetter* ig
+#define GET_IMAGE_INTO_BUF(r,buf) ig->getImage(buf, r);
+#define BPP 8
+#include <rfb/zrleEncode.h>
+#undef BPP
+#define BPP 16
+#include <rfb/zrleEncode.h>
+#undef BPP
+#define BPP 32
+#include <rfb/zrleEncode.h>
+#define CPIXEL 24A
+#include <rfb/zrleEncode.h>
+#undef CPIXEL
+#define CPIXEL 24B
+#include <rfb/zrleEncode.h>
+#undef CPIXEL
+#undef BPP
+
+Encoder* ZRLEEncoder::create(SMsgWriter* writer)
+{
+ return new ZRLEEncoder(writer);
+}
+
+ZRLEEncoder::ZRLEEncoder(SMsgWriter* writer_)
+ : writer(writer_), zos(0,0,zlibLevel)
+{
+ if (sharedMos)
+ mos = sharedMos;
+ else
+ mos = new rdr::MemOutStream(129*1024);
+}
+
+ZRLEEncoder::~ZRLEEncoder()
+{
+ if (!sharedMos)
+ delete mos;
+}
+
+bool ZRLEEncoder::writeRect(const Rect& r, ImageGetter* ig, Rect* actual)
+{
+ rdr::U8* imageBuf = writer->getImageBuf(64 * 64 * 4 + 4);
+ mos->clear();
+ bool wroteAll = true;
+ *actual = r;
+
+ switch (writer->bpp()) {
+ case 8:
+ wroteAll = zrleEncode8(r, mos, &zos, imageBuf, maxLen, actual, ig);
+ break;
+ case 16:
+ wroteAll = zrleEncode16(r, mos, &zos, imageBuf, maxLen, actual, ig);
+ break;
+ case 32:
+ {
+ const PixelFormat& pf = writer->getConnParams()->pf();
+
+ bool fitsInLS3Bytes = ((pf.redMax << pf.redShift) < (1<<24) &&
+ (pf.greenMax << pf.greenShift) < (1<<24) &&
+ (pf.blueMax << pf.blueShift) < (1<<24));
+
+ bool fitsInMS3Bytes = (pf.redShift > 7 &&
+ pf.greenShift > 7 &&
+ pf.blueShift > 7);
+
+ if ((fitsInLS3Bytes && !pf.bigEndian) ||
+ (fitsInMS3Bytes && pf.bigEndian))
+ {
+ wroteAll = zrleEncode24A(r, mos, &zos, imageBuf, maxLen, actual, ig);
+ }
+ else if ((fitsInLS3Bytes && pf.bigEndian) ||
+ (fitsInMS3Bytes && !pf.bigEndian))
+ {
+ wroteAll = zrleEncode24B(r, mos, &zos, imageBuf, maxLen, actual, ig);
+ }
+ else
+ {
+ wroteAll = zrleEncode32(r, mos, &zos, imageBuf, maxLen, actual, ig);
+ }
+ break;
+ }
+ }
+
+ writer->startRect(*actual, encodingZRLE);
+ rdr::OutStream* os = writer->getOutStream();
+ os->writeU32(mos->length());
+ os->writeBytes(mos->data(), mos->length());
+ writer->endRect();
+ return wroteAll;
+}
diff --git a/rfb/ZRLEEncoder.h b/rfb/ZRLEEncoder.h
new file mode 100644
index 00000000..17222a3c
--- /dev/null
+++ b/rfb/ZRLEEncoder.h
@@ -0,0 +1,53 @@
+/* Copyright (C) 2002-2004 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_ZRLEENCODER_H__
+#define __RFB_ZRLEENCODER_H__
+
+#include <rdr/MemOutStream.h>
+#include <rdr/ZlibOutStream.h>
+#include <rfb/Encoder.h>
+
+namespace rfb {
+
+ class ZRLEEncoder : public Encoder {
+ public:
+ static Encoder* create(SMsgWriter* writer);
+ virtual bool writeRect(const Rect& r, ImageGetter* ig, Rect* actual);
+ virtual ~ZRLEEncoder();
+
+ // setMaxLen() sets the maximum size in bytes of any ZRLE rectangle. This
+ // can be used to stop the MemOutStream from growing too large. The value
+ // must be large enough to allow for at least one row of ZRLE tiles. So
+ // for example for a screen width of 2048 32-bit pixels this is 2K*4*64 =
+ // 512Kbytes plus a bit of overhead.
+ static void setMaxLen(int m) { maxLen = m; }
+
+ // setSharedMos() sets a MemOutStream to be shared amongst all
+ // ZRLEEncoders. Should be called before any ZRLEEncoders are created.
+ static void setSharedMos(rdr::MemOutStream* mos_) { sharedMos = mos_; }
+
+ private:
+ ZRLEEncoder(SMsgWriter* writer);
+ SMsgWriter* writer;
+ rdr::ZlibOutStream zos;
+ rdr::MemOutStream* mos;
+ static rdr::MemOutStream* sharedMos;
+ static int maxLen;
+ };
+}
+#endif
diff --git a/rfb/d3des.c b/rfb/d3des.c
new file mode 100644
index 00000000..9227ddd3
--- /dev/null
+++ b/rfb/d3des.c
@@ -0,0 +1,434 @@
+/*
+ * This is D3DES (V5.09) by Richard Outerbridge with the double and
+ * triple-length support removed for use in VNC. Also the bytebit[] array
+ * has been reversed so that the most significant bit in each byte of the
+ * key is ignored, not the least significant.
+ *
+ * These changes are:
+ * Copyright (C) 1999 AT&T Laboratories Cambridge. All Rights Reserved.
+ *
+ * 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.
+ */
+
+/* D3DES (V5.09) -
+ *
+ * A portable, public domain, version of the Data Encryption Standard.
+ *
+ * Written with Symantec's THINK (Lightspeed) C by Richard Outerbridge.
+ * Thanks to: Dan Hoey for his excellent Initial and Inverse permutation
+ * code; Jim Gillogly & Phil Karn for the DES key schedule code; Dennis
+ * Ferguson, Eric Young and Dana How for comparing notes; and Ray Lau,
+ * for humouring me on.
+ *
+ * Copyright (c) 1988,1989,1990,1991,1992 by Richard Outerbridge.
+ * (GEnie : OUTER; CIS : [71755,204]) Graven Imagery, 1992.
+ */
+
+#include "d3des.h"
+
+static void scrunch(unsigned char *, unsigned long *);
+static void unscrun(unsigned long *, unsigned char *);
+static void desfunc(unsigned long *, unsigned long *);
+static void cookey(unsigned long *);
+
+static unsigned long KnL[32] = { 0L };
+
+static unsigned short bytebit[8] = {
+ 01, 02, 04, 010, 020, 040, 0100, 0200 };
+
+static unsigned long bigbyte[24] = {
+ 0x800000L, 0x400000L, 0x200000L, 0x100000L,
+ 0x80000L, 0x40000L, 0x20000L, 0x10000L,
+ 0x8000L, 0x4000L, 0x2000L, 0x1000L,
+ 0x800L, 0x400L, 0x200L, 0x100L,
+ 0x80L, 0x40L, 0x20L, 0x10L,
+ 0x8L, 0x4L, 0x2L, 0x1L };
+
+/* Use the key schedule specified in the Standard (ANSI X3.92-1981). */
+
+static unsigned char pc1[56] = {
+ 56, 48, 40, 32, 24, 16, 8, 0, 57, 49, 41, 33, 25, 17,
+ 9, 1, 58, 50, 42, 34, 26, 18, 10, 2, 59, 51, 43, 35,
+ 62, 54, 46, 38, 30, 22, 14, 6, 61, 53, 45, 37, 29, 21,
+ 13, 5, 60, 52, 44, 36, 28, 20, 12, 4, 27, 19, 11, 3 };
+
+static unsigned char totrot[16] = {
+ 1,2,4,6,8,10,12,14,15,17,19,21,23,25,27,28 };
+
+static unsigned char pc2[48] = {
+ 13, 16, 10, 23, 0, 4, 2, 27, 14, 5, 20, 9,
+ 22, 18, 11, 3, 25, 7, 15, 6, 26, 19, 12, 1,
+ 40, 51, 30, 36, 46, 54, 29, 39, 50, 44, 32, 47,
+ 43, 48, 38, 55, 33, 52, 45, 41, 49, 35, 28, 31 };
+
+void deskey(key, edf) /* Thanks to James Gillogly & Phil Karn! */
+unsigned char *key;
+int edf;
+{
+ register int i, j, l, m, n;
+ unsigned char pc1m[56], pcr[56];
+ unsigned long kn[32];
+
+ for ( j = 0; j < 56; j++ ) {
+ l = pc1[j];
+ m = l & 07;
+ pc1m[j] = (key[l >> 3] & bytebit[m]) ? 1 : 0;
+ }
+ for( i = 0; i < 16; i++ ) {
+ if( edf == DE1 ) m = (15 - i) << 1;
+ else m = i << 1;
+ n = m + 1;
+ kn[m] = kn[n] = 0L;
+ for( j = 0; j < 28; j++ ) {
+ l = j + totrot[i];
+ if( l < 28 ) pcr[j] = pc1m[l];
+ else pcr[j] = pc1m[l - 28];
+ }
+ for( j = 28; j < 56; j++ ) {
+ l = j + totrot[i];
+ if( l < 56 ) pcr[j] = pc1m[l];
+ else pcr[j] = pc1m[l - 28];
+ }
+ for( j = 0; j < 24; j++ ) {
+ if( pcr[pc2[j]] ) kn[m] |= bigbyte[j];
+ if( pcr[pc2[j+24]] ) kn[n] |= bigbyte[j];
+ }
+ }
+ cookey(kn);
+ return;
+ }
+
+static void cookey(raw1)
+register unsigned long *raw1;
+{
+ register unsigned long *cook, *raw0;
+ unsigned long dough[32];
+ register int i;
+
+ cook = dough;
+ for( i = 0; i < 16; i++, raw1++ ) {
+ raw0 = raw1++;
+ *cook = (*raw0 & 0x00fc0000L) << 6;
+ *cook |= (*raw0 & 0x00000fc0L) << 10;
+ *cook |= (*raw1 & 0x00fc0000L) >> 10;
+ *cook++ |= (*raw1 & 0x00000fc0L) >> 6;
+ *cook = (*raw0 & 0x0003f000L) << 12;
+ *cook |= (*raw0 & 0x0000003fL) << 16;
+ *cook |= (*raw1 & 0x0003f000L) >> 4;
+ *cook++ |= (*raw1 & 0x0000003fL);
+ }
+ usekey(dough);
+ return;
+ }
+
+void cpkey(into)
+register unsigned long *into;
+{
+ register unsigned long *from, *endp;
+
+ from = KnL, endp = &KnL[32];
+ while( from < endp ) *into++ = *from++;
+ return;
+ }
+
+void usekey(from)
+register unsigned long *from;
+{
+ register unsigned long *to, *endp;
+
+ to = KnL, endp = &KnL[32];
+ while( to < endp ) *to++ = *from++;
+ return;
+ }
+
+void des(inblock, outblock)
+unsigned char *inblock, *outblock;
+{
+ unsigned long work[2];
+
+ scrunch(inblock, work);
+ desfunc(work, KnL);
+ unscrun(work, outblock);
+ return;
+ }
+
+static void scrunch(outof, into)
+register unsigned char *outof;
+register unsigned long *into;
+{
+ *into = (*outof++ & 0xffL) << 24;
+ *into |= (*outof++ & 0xffL) << 16;
+ *into |= (*outof++ & 0xffL) << 8;
+ *into++ |= (*outof++ & 0xffL);
+ *into = (*outof++ & 0xffL) << 24;
+ *into |= (*outof++ & 0xffL) << 16;
+ *into |= (*outof++ & 0xffL) << 8;
+ *into |= (*outof & 0xffL);
+ return;
+ }
+
+static void unscrun(outof, into)
+register unsigned long *outof;
+register unsigned char *into;
+{
+ *into++ = (*outof >> 24) & 0xffL;
+ *into++ = (*outof >> 16) & 0xffL;
+ *into++ = (*outof >> 8) & 0xffL;
+ *into++ = *outof++ & 0xffL;
+ *into++ = (*outof >> 24) & 0xffL;
+ *into++ = (*outof >> 16) & 0xffL;
+ *into++ = (*outof >> 8) & 0xffL;
+ *into = *outof & 0xffL;
+ return;
+ }
+
+static unsigned long SP1[64] = {
+ 0x01010400L, 0x00000000L, 0x00010000L, 0x01010404L,
+ 0x01010004L, 0x00010404L, 0x00000004L, 0x00010000L,
+ 0x00000400L, 0x01010400L, 0x01010404L, 0x00000400L,
+ 0x01000404L, 0x01010004L, 0x01000000L, 0x00000004L,
+ 0x00000404L, 0x01000400L, 0x01000400L, 0x00010400L,
+ 0x00010400L, 0x01010000L, 0x01010000L, 0x01000404L,
+ 0x00010004L, 0x01000004L, 0x01000004L, 0x00010004L,
+ 0x00000000L, 0x00000404L, 0x00010404L, 0x01000000L,
+ 0x00010000L, 0x01010404L, 0x00000004L, 0x01010000L,
+ 0x01010400L, 0x01000000L, 0x01000000L, 0x00000400L,
+ 0x01010004L, 0x00010000L, 0x00010400L, 0x01000004L,
+ 0x00000400L, 0x00000004L, 0x01000404L, 0x00010404L,
+ 0x01010404L, 0x00010004L, 0x01010000L, 0x01000404L,
+ 0x01000004L, 0x00000404L, 0x00010404L, 0x01010400L,
+ 0x00000404L, 0x01000400L, 0x01000400L, 0x00000000L,
+ 0x00010004L, 0x00010400L, 0x00000000L, 0x01010004L };
+
+static unsigned long SP2[64] = {
+ 0x80108020L, 0x80008000L, 0x00008000L, 0x00108020L,
+ 0x00100000L, 0x00000020L, 0x80100020L, 0x80008020L,
+ 0x80000020L, 0x80108020L, 0x80108000L, 0x80000000L,
+ 0x80008000L, 0x00100000L, 0x00000020L, 0x80100020L,
+ 0x00108000L, 0x00100020L, 0x80008020L, 0x00000000L,
+ 0x80000000L, 0x00008000L, 0x00108020L, 0x80100000L,
+ 0x00100020L, 0x80000020L, 0x00000000L, 0x00108000L,
+ 0x00008020L, 0x80108000L, 0x80100000L, 0x00008020L,
+ 0x00000000L, 0x00108020L, 0x80100020L, 0x00100000L,
+ 0x80008020L, 0x80100000L, 0x80108000L, 0x00008000L,
+ 0x80100000L, 0x80008000L, 0x00000020L, 0x80108020L,
+ 0x00108020L, 0x00000020L, 0x00008000L, 0x80000000L,
+ 0x00008020L, 0x80108000L, 0x00100000L, 0x80000020L,
+ 0x00100020L, 0x80008020L, 0x80000020L, 0x00100020L,
+ 0x00108000L, 0x00000000L, 0x80008000L, 0x00008020L,
+ 0x80000000L, 0x80100020L, 0x80108020L, 0x00108000L };
+
+static unsigned long SP3[64] = {
+ 0x00000208L, 0x08020200L, 0x00000000L, 0x08020008L,
+ 0x08000200L, 0x00000000L, 0x00020208L, 0x08000200L,
+ 0x00020008L, 0x08000008L, 0x08000008L, 0x00020000L,
+ 0x08020208L, 0x00020008L, 0x08020000L, 0x00000208L,
+ 0x08000000L, 0x00000008L, 0x08020200L, 0x00000200L,
+ 0x00020200L, 0x08020000L, 0x08020008L, 0x00020208L,
+ 0x08000208L, 0x00020200L, 0x00020000L, 0x08000208L,
+ 0x00000008L, 0x08020208L, 0x00000200L, 0x08000000L,
+ 0x08020200L, 0x08000000L, 0x00020008L, 0x00000208L,
+ 0x00020000L, 0x08020200L, 0x08000200L, 0x00000000L,
+ 0x00000200L, 0x00020008L, 0x08020208L, 0x08000200L,
+ 0x08000008L, 0x00000200L, 0x00000000L, 0x08020008L,
+ 0x08000208L, 0x00020000L, 0x08000000L, 0x08020208L,
+ 0x00000008L, 0x00020208L, 0x00020200L, 0x08000008L,
+ 0x08020000L, 0x08000208L, 0x00000208L, 0x08020000L,
+ 0x00020208L, 0x00000008L, 0x08020008L, 0x00020200L };
+
+static unsigned long SP4[64] = {
+ 0x00802001L, 0x00002081L, 0x00002081L, 0x00000080L,
+ 0x00802080L, 0x00800081L, 0x00800001L, 0x00002001L,
+ 0x00000000L, 0x00802000L, 0x00802000L, 0x00802081L,
+ 0x00000081L, 0x00000000L, 0x00800080L, 0x00800001L,
+ 0x00000001L, 0x00002000L, 0x00800000L, 0x00802001L,
+ 0x00000080L, 0x00800000L, 0x00002001L, 0x00002080L,
+ 0x00800081L, 0x00000001L, 0x00002080L, 0x00800080L,
+ 0x00002000L, 0x00802080L, 0x00802081L, 0x00000081L,
+ 0x00800080L, 0x00800001L, 0x00802000L, 0x00802081L,
+ 0x00000081L, 0x00000000L, 0x00000000L, 0x00802000L,
+ 0x00002080L, 0x00800080L, 0x00800081L, 0x00000001L,
+ 0x00802001L, 0x00002081L, 0x00002081L, 0x00000080L,
+ 0x00802081L, 0x00000081L, 0x00000001L, 0x00002000L,
+ 0x00800001L, 0x00002001L, 0x00802080L, 0x00800081L,
+ 0x00002001L, 0x00002080L, 0x00800000L, 0x00802001L,
+ 0x00000080L, 0x00800000L, 0x00002000L, 0x00802080L };
+
+static unsigned long SP5[64] = {
+ 0x00000100L, 0x02080100L, 0x02080000L, 0x42000100L,
+ 0x00080000L, 0x00000100L, 0x40000000L, 0x02080000L,
+ 0x40080100L, 0x00080000L, 0x02000100L, 0x40080100L,
+ 0x42000100L, 0x42080000L, 0x00080100L, 0x40000000L,
+ 0x02000000L, 0x40080000L, 0x40080000L, 0x00000000L,
+ 0x40000100L, 0x42080100L, 0x42080100L, 0x02000100L,
+ 0x42080000L, 0x40000100L, 0x00000000L, 0x42000000L,
+ 0x02080100L, 0x02000000L, 0x42000000L, 0x00080100L,
+ 0x00080000L, 0x42000100L, 0x00000100L, 0x02000000L,
+ 0x40000000L, 0x02080000L, 0x42000100L, 0x40080100L,
+ 0x02000100L, 0x40000000L, 0x42080000L, 0x02080100L,
+ 0x40080100L, 0x00000100L, 0x02000000L, 0x42080000L,
+ 0x42080100L, 0x00080100L, 0x42000000L, 0x42080100L,
+ 0x02080000L, 0x00000000L, 0x40080000L, 0x42000000L,
+ 0x00080100L, 0x02000100L, 0x40000100L, 0x00080000L,
+ 0x00000000L, 0x40080000L, 0x02080100L, 0x40000100L };
+
+static unsigned long SP6[64] = {
+ 0x20000010L, 0x20400000L, 0x00004000L, 0x20404010L,
+ 0x20400000L, 0x00000010L, 0x20404010L, 0x00400000L,
+ 0x20004000L, 0x00404010L, 0x00400000L, 0x20000010L,
+ 0x00400010L, 0x20004000L, 0x20000000L, 0x00004010L,
+ 0x00000000L, 0x00400010L, 0x20004010L, 0x00004000L,
+ 0x00404000L, 0x20004010L, 0x00000010L, 0x20400010L,
+ 0x20400010L, 0x00000000L, 0x00404010L, 0x20404000L,
+ 0x00004010L, 0x00404000L, 0x20404000L, 0x20000000L,
+ 0x20004000L, 0x00000010L, 0x20400010L, 0x00404000L,
+ 0x20404010L, 0x00400000L, 0x00004010L, 0x20000010L,
+ 0x00400000L, 0x20004000L, 0x20000000L, 0x00004010L,
+ 0x20000010L, 0x20404010L, 0x00404000L, 0x20400000L,
+ 0x00404010L, 0x20404000L, 0x00000000L, 0x20400010L,
+ 0x00000010L, 0x00004000L, 0x20400000L, 0x00404010L,
+ 0x00004000L, 0x00400010L, 0x20004010L, 0x00000000L,
+ 0x20404000L, 0x20000000L, 0x00400010L, 0x20004010L };
+
+static unsigned long SP7[64] = {
+ 0x00200000L, 0x04200002L, 0x04000802L, 0x00000000L,
+ 0x00000800L, 0x04000802L, 0x00200802L, 0x04200800L,
+ 0x04200802L, 0x00200000L, 0x00000000L, 0x04000002L,
+ 0x00000002L, 0x04000000L, 0x04200002L, 0x00000802L,
+ 0x04000800L, 0x00200802L, 0x00200002L, 0x04000800L,
+ 0x04000002L, 0x04200000L, 0x04200800L, 0x00200002L,
+ 0x04200000L, 0x00000800L, 0x00000802L, 0x04200802L,
+ 0x00200800L, 0x00000002L, 0x04000000L, 0x00200800L,
+ 0x04000000L, 0x00200800L, 0x00200000L, 0x04000802L,
+ 0x04000802L, 0x04200002L, 0x04200002L, 0x00000002L,
+ 0x00200002L, 0x04000000L, 0x04000800L, 0x00200000L,
+ 0x04200800L, 0x00000802L, 0x00200802L, 0x04200800L,
+ 0x00000802L, 0x04000002L, 0x04200802L, 0x04200000L,
+ 0x00200800L, 0x00000000L, 0x00000002L, 0x04200802L,
+ 0x00000000L, 0x00200802L, 0x04200000L, 0x00000800L,
+ 0x04000002L, 0x04000800L, 0x00000800L, 0x00200002L };
+
+static unsigned long SP8[64] = {
+ 0x10001040L, 0x00001000L, 0x00040000L, 0x10041040L,
+ 0x10000000L, 0x10001040L, 0x00000040L, 0x10000000L,
+ 0x00040040L, 0x10040000L, 0x10041040L, 0x00041000L,
+ 0x10041000L, 0x00041040L, 0x00001000L, 0x00000040L,
+ 0x10040000L, 0x10000040L, 0x10001000L, 0x00001040L,
+ 0x00041000L, 0x00040040L, 0x10040040L, 0x10041000L,
+ 0x00001040L, 0x00000000L, 0x00000000L, 0x10040040L,
+ 0x10000040L, 0x10001000L, 0x00041040L, 0x00040000L,
+ 0x00041040L, 0x00040000L, 0x10041000L, 0x00001000L,
+ 0x00000040L, 0x10040040L, 0x00001000L, 0x00041040L,
+ 0x10001000L, 0x00000040L, 0x10000040L, 0x10040000L,
+ 0x10040040L, 0x10000000L, 0x00040000L, 0x10001040L,
+ 0x00000000L, 0x10041040L, 0x00040040L, 0x10000040L,
+ 0x10040000L, 0x10001000L, 0x10001040L, 0x00000000L,
+ 0x10041040L, 0x00041000L, 0x00041000L, 0x00001040L,
+ 0x00001040L, 0x00040040L, 0x10000000L, 0x10041000L };
+
+static void desfunc(block, keys)
+register unsigned long *block, *keys;
+{
+ register unsigned long fval, work, right, leftt;
+ register int round;
+
+ leftt = block[0];
+ right = block[1];
+ work = ((leftt >> 4) ^ right) & 0x0f0f0f0fL;
+ right ^= work;
+ leftt ^= (work << 4);
+ work = ((leftt >> 16) ^ right) & 0x0000ffffL;
+ right ^= work;
+ leftt ^= (work << 16);
+ work = ((right >> 2) ^ leftt) & 0x33333333L;
+ leftt ^= work;
+ right ^= (work << 2);
+ work = ((right >> 8) ^ leftt) & 0x00ff00ffL;
+ leftt ^= work;
+ right ^= (work << 8);
+ right = ((right << 1) | ((right >> 31) & 1L)) & 0xffffffffL;
+ work = (leftt ^ right) & 0xaaaaaaaaL;
+ leftt ^= work;
+ right ^= work;
+ leftt = ((leftt << 1) | ((leftt >> 31) & 1L)) & 0xffffffffL;
+
+ for( round = 0; round < 8; round++ ) {
+ work = (right << 28) | (right >> 4);
+ work ^= *keys++;
+ fval = SP7[ work & 0x3fL];
+ fval |= SP5[(work >> 8) & 0x3fL];
+ fval |= SP3[(work >> 16) & 0x3fL];
+ fval |= SP1[(work >> 24) & 0x3fL];
+ work = right ^ *keys++;
+ fval |= SP8[ work & 0x3fL];
+ fval |= SP6[(work >> 8) & 0x3fL];
+ fval |= SP4[(work >> 16) & 0x3fL];
+ fval |= SP2[(work >> 24) & 0x3fL];
+ leftt ^= fval;
+ work = (leftt << 28) | (leftt >> 4);
+ work ^= *keys++;
+ fval = SP7[ work & 0x3fL];
+ fval |= SP5[(work >> 8) & 0x3fL];
+ fval |= SP3[(work >> 16) & 0x3fL];
+ fval |= SP1[(work >> 24) & 0x3fL];
+ work = leftt ^ *keys++;
+ fval |= SP8[ work & 0x3fL];
+ fval |= SP6[(work >> 8) & 0x3fL];
+ fval |= SP4[(work >> 16) & 0x3fL];
+ fval |= SP2[(work >> 24) & 0x3fL];
+ right ^= fval;
+ }
+
+ right = (right << 31) | (right >> 1);
+ work = (leftt ^ right) & 0xaaaaaaaaL;
+ leftt ^= work;
+ right ^= work;
+ leftt = (leftt << 31) | (leftt >> 1);
+ work = ((leftt >> 8) ^ right) & 0x00ff00ffL;
+ right ^= work;
+ leftt ^= (work << 8);
+ work = ((leftt >> 2) ^ right) & 0x33333333L;
+ right ^= work;
+ leftt ^= (work << 2);
+ work = ((right >> 16) ^ leftt) & 0x0000ffffL;
+ leftt ^= work;
+ right ^= (work << 16);
+ work = ((right >> 4) ^ leftt) & 0x0f0f0f0fL;
+ leftt ^= work;
+ right ^= (work << 4);
+ *block++ = right;
+ *block = leftt;
+ return;
+ }
+
+/* Validation sets:
+ *
+ * Single-length key, single-length plaintext -
+ * Key : 0123 4567 89ab cdef
+ * Plain : 0123 4567 89ab cde7
+ * Cipher : c957 4425 6a5e d31d
+ *
+ * Double-length key, single-length plaintext -
+ * Key : 0123 4567 89ab cdef fedc ba98 7654 3210
+ * Plain : 0123 4567 89ab cde7
+ * Cipher : 7f1d 0a77 826b 8aff
+ *
+ * Double-length key, double-length plaintext -
+ * Key : 0123 4567 89ab cdef fedc ba98 7654 3210
+ * Plain : 0123 4567 89ab cdef 0123 4567 89ab cdff
+ * Cipher : 27a0 8440 406a df60 278f 47cf 42d6 15d7
+ *
+ * Triple-length key, single-length plaintext -
+ * Key : 0123 4567 89ab cdef fedc ba98 7654 3210 89ab cdef 0123 4567
+ * Plain : 0123 4567 89ab cde7
+ * Cipher : de0b 7c06 ae5e 0ed5
+ *
+ * Triple-length key, double-length plaintext -
+ * Key : 0123 4567 89ab cdef fedc ba98 7654 3210 89ab cdef 0123 4567
+ * Plain : 0123 4567 89ab cdef 0123 4567 89ab cdff
+ * Cipher : ad0d 1b30 ac17 cf07 0ed1 1c63 81e4 4de5
+ *
+ * d3des V5.0a rwo 9208.07 18:44 Graven Imagery
+ **********************************************************************/
diff --git a/rfb/d3des.h b/rfb/d3des.h
new file mode 100644
index 00000000..ea3da44c
--- /dev/null
+++ b/rfb/d3des.h
@@ -0,0 +1,51 @@
+/*
+ * This is D3DES (V5.09) by Richard Outerbridge with the double and
+ * triple-length support removed for use in VNC.
+ *
+ * These changes are:
+ * Copyright (C) 1999 AT&T Laboratories Cambridge. All Rights Reserved.
+ *
+ * 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.
+ */
+
+/* d3des.h -
+ *
+ * Headers and defines for d3des.c
+ * Graven Imagery, 1992.
+ *
+ * Copyright (c) 1988,1989,1990,1991,1992 by Richard Outerbridge
+ * (GEnie : OUTER; CIS : [71755,204])
+ */
+
+#define EN0 0 /* MODE == encrypt */
+#define DE1 1 /* MODE == decrypt */
+
+extern void deskey(unsigned char *, int);
+/* hexkey[8] MODE
+ * Sets the internal key register according to the hexadecimal
+ * key contained in the 8 bytes of hexkey, according to the DES,
+ * for encryption or decryption according to MODE.
+ */
+
+extern void usekey(unsigned long *);
+/* cookedkey[32]
+ * Loads the internal key register with the data in cookedkey.
+ */
+
+extern void cpkey(unsigned long *);
+/* cookedkey[32]
+ * Copies the contents of the internal key register into the storage
+ * located at &cookedkey[0].
+ */
+
+extern void des(unsigned char *, unsigned char *);
+/* from[8] to[8]
+ * Encrypts/Decrypts (according to the key currently loaded in the
+ * internal key register) one block of eight bytes at address 'from'
+ * into the block at address 'to'. They can be the same.
+ */
+
+/* d3des.h V5.09 rwo 9208.04 15:06 Graven Imagery
+ ********************************************************************/
diff --git a/rfb/encodings.cxx b/rfb/encodings.cxx
new file mode 100644
index 00000000..56a64ee7
--- /dev/null
+++ b/rfb/encodings.cxx
@@ -0,0 +1,46 @@
+/* Copyright (C) 2002-2003 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 <string.h>
+#ifdef _WIN32
+#define strcasecmp _stricmp
+#endif
+#include <rfb/encodings.h>
+
+int rfb::encodingNum(const char* name)
+{
+ if (strcasecmp(name, "raw") == 0) return encodingRaw;
+ if (strcasecmp(name, "copyRect") == 0) return encodingCopyRect;
+ if (strcasecmp(name, "RRE") == 0) return encodingRRE;
+ if (strcasecmp(name, "CoRRE") == 0) return encodingCoRRE;
+ if (strcasecmp(name, "hextile") == 0) return encodingHextile;
+ if (strcasecmp(name, "ZRLE") == 0) return encodingZRLE;
+ return -1;
+}
+
+const char* rfb::encodingName(unsigned int num)
+{
+ switch (num) {
+ case encodingRaw: return "raw";
+ case encodingCopyRect: return "copyRect";
+ case encodingRRE: return "RRE";
+ case encodingCoRRE: return "CoRRE";
+ case encodingHextile: return "hextile";
+ case encodingZRLE: return "ZRLE";
+ default: return "[unknown encoding]";
+ }
+}
diff --git a/rfb/encodings.h b/rfb/encodings.h
new file mode 100644
index 00000000..ae104b41
--- /dev/null
+++ b/rfb/encodings.h
@@ -0,0 +1,38 @@
+/* Copyright (C) 2002-2004 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_ENCODINGS_H__
+#define __RFB_ENCODINGS_H__
+
+namespace rfb {
+
+ const unsigned int encodingRaw = 0;
+ const unsigned int encodingCopyRect = 1;
+ const unsigned int encodingRRE = 2;
+ const unsigned int encodingCoRRE = 4;
+ const unsigned int encodingHextile = 5;
+ const unsigned int encodingZRLE = 16;
+
+ const unsigned int encodingMax = 255;
+
+ const unsigned int pseudoEncodingCursor = 0xffffff11;
+ const unsigned int pseudoEncodingDesktopSize = 0xffffff21;
+
+ int encodingNum(const char* name);
+ const char* encodingName(unsigned int num);
+}
+#endif
diff --git a/rfb/hextileConstants.h b/rfb/hextileConstants.h
new file mode 100644
index 00000000..272afbba
--- /dev/null
+++ b/rfb/hextileConstants.h
@@ -0,0 +1,27 @@
+/* Copyright (C) 2002-2003 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_HEXTILECONSTANTS_H__
+#define __RFB_HEXTILECONSTANTS_H__
+namespace rfb {
+ const int hextileRaw = (1 << 0);
+ const int hextileBgSpecified = (1 << 1);
+ const int hextileFgSpecified = (1 << 2);
+ const int hextileAnySubrects = (1 << 3);
+ const int hextileSubrectsColoured = (1 << 4);
+}
+#endif
diff --git a/rfb/hextileDecode.h b/rfb/hextileDecode.h
new file mode 100644
index 00000000..dc685e3a
--- /dev/null
+++ b/rfb/hextileDecode.h
@@ -0,0 +1,126 @@
+/* Copyright (C) 2002-2003 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.
+ */
+//
+// Hextile decoding function.
+//
+// This file is #included after having set the following macros:
+// BPP - 8, 16 or 32
+// EXTRA_ARGS - optional extra arguments
+// FILL_RECT - fill a rectangle with a single colour
+// IMAGE_RECT - draw a rectangle of pixel data from a buffer
+
+#include <rdr/InStream.h>
+#include <rfb/hextileConstants.h>
+
+namespace rfb {
+
+// CONCAT2E concatenates its arguments, expanding them if they are macros
+
+#ifndef CONCAT2E
+#define CONCAT2(a,b) a##b
+#define CONCAT2E(a,b) CONCAT2(a,b)
+#endif
+
+#define PIXEL_T rdr::CONCAT2E(U,BPP)
+#define READ_PIXEL CONCAT2E(readOpaque,BPP)
+#define HEXTILE_DECODE CONCAT2E(hextileDecode,BPP)
+
+void HEXTILE_DECODE (const Rect& r, rdr::InStream* is, PIXEL_T* buf
+#ifdef EXTRA_ARGS
+ , EXTRA_ARGS
+#endif
+ )
+{
+ Rect t;
+ PIXEL_T bg = 0;
+ PIXEL_T fg = 0;
+
+ for (t.tl.y = r.tl.y; t.tl.y < r.br.y; t.tl.y += 16) {
+
+ t.br.y = min(r.br.y, t.tl.y + 16);
+
+ for (t.tl.x = r.tl.x; t.tl.x < r.br.x; t.tl.x += 16) {
+
+ t.br.x = min(r.br.x, t.tl.x + 16);
+
+ int tileType = is->readU8();
+
+ if (tileType & hextileRaw) {
+ is->readBytes(buf, t.area() * (BPP/8));
+ IMAGE_RECT(t, buf);
+ continue;
+ }
+
+ if (tileType & hextileBgSpecified)
+ bg = is->READ_PIXEL();
+
+#ifdef FAVOUR_FILL_RECT
+ FILL_RECT(t, bg);
+#else
+ int len = t.area();
+ PIXEL_T* ptr = (PIXEL_T*)buf;
+ while (len-- > 0) *ptr++ = bg;
+#endif
+
+ if (tileType & hextileFgSpecified)
+ fg = is->READ_PIXEL();
+
+ if (tileType & hextileAnySubrects) {
+ int nSubrects = is->readU8();
+
+ for (int i = 0; i < nSubrects; i++) {
+
+ if (tileType & hextileSubrectsColoured)
+ fg = is->READ_PIXEL();
+
+ int xy = is->readU8();
+ int wh = is->readU8();
+
+#ifdef FAVOUR_FILL_RECT
+ Rect s;
+ s.tl.x = t.tl.x + ((xy >> 4) & 15);
+ s.tl.y = t.tl.y + (xy & 15);
+ s.br.x = s.tl.x + ((wh >> 4) & 15) + 1;
+ s.br.y = s.tl.y + (wh & 15) + 1;
+ FILL_RECT(s, fg);
+#else
+ int x = ((xy >> 4) & 15);
+ int y = (xy & 15);
+ int w = ((wh >> 4) & 15) + 1;
+ int h = (wh & 15) + 1;
+ PIXEL_T* ptr = (PIXEL_T*)buf + y * t.width() + x;
+ int rowAdd = t.width() - w;
+ while (h-- > 0) {
+ int len = w;
+ while (len-- > 0) *ptr++ = fg;
+ ptr += rowAdd;
+ }
+#endif
+ }
+ }
+#ifndef FAVOUR_FILL_RECT
+ IMAGE_RECT(t, buf);
+#endif
+ }
+ }
+}
+
+#undef PIXEL_T
+#undef READ_PIXEL
+#undef HEXTILE_DECODE
+}
diff --git a/rfb/hextileEncode.h b/rfb/hextileEncode.h
new file mode 100644
index 00000000..a55842a0
--- /dev/null
+++ b/rfb/hextileEncode.h
@@ -0,0 +1,247 @@
+/* Copyright (C) 2002-2003 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.
+ */
+//
+// Hextile encoding function.
+//
+// This file is #included after having set the following macros:
+// BPP - 8, 16 or 32
+// EXTRA_ARGS - optional extra arguments
+// GET_IMAGE_INTO_BUF - gets a rectangle of pixel data into a buffer
+
+#include <rdr/OutStream.h>
+#include <rfb/hextileConstants.h>
+
+namespace rfb {
+
+// CONCAT2E concatenates its arguments, expanding them if they are macros
+
+#ifndef CONCAT2E
+#define CONCAT2(a,b) a##b
+#define CONCAT2E(a,b) CONCAT2(a,b)
+#endif
+
+#define PIXEL_T rdr::CONCAT2E(U,BPP)
+#define WRITE_PIXEL CONCAT2E(writeOpaque,BPP)
+#define HEXTILE_ENCODE CONCAT2E(hextileEncode,BPP)
+#define HEXTILE_ENCODE_TILE CONCAT2E(hextileEncodeTile,BPP)
+#define TEST_TILE_TYPE CONCAT2E(hextileTestTileType,BPP)
+
+int TEST_TILE_TYPE (PIXEL_T* data, int w, int h, PIXEL_T* bg, PIXEL_T* fg);
+int HEXTILE_ENCODE_TILE (PIXEL_T* data, int w, int h, int tileType,
+ rdr::U8* encoded, PIXEL_T bg);
+
+void HEXTILE_ENCODE(const Rect& r, rdr::OutStream* os
+#ifdef EXTRA_ARGS
+ , EXTRA_ARGS
+#endif
+ )
+{
+ Rect t;
+ PIXEL_T buf[256];
+ PIXEL_T oldBg = 0, oldFg = 0;
+ bool oldBgValid = false;
+ bool oldFgValid = false;
+ rdr::U8 encoded[256*(BPP/8)];
+
+ for (t.tl.y = r.tl.y; t.tl.y < r.br.y; t.tl.y += 16) {
+
+ t.br.y = min(r.br.y, t.tl.y + 16);
+
+ for (t.tl.x = r.tl.x; t.tl.x < r.br.x; t.tl.x += 16) {
+
+ t.br.x = min(r.br.x, t.tl.x + 16);
+
+ GET_IMAGE_INTO_BUF(t,buf);
+
+ PIXEL_T bg, fg;
+ int tileType = TEST_TILE_TYPE(buf, t.width(), t.height(), &bg, &fg);
+
+ if (!oldBgValid || oldBg != bg) {
+ tileType |= hextileBgSpecified;
+ oldBg = bg;
+ oldBgValid = true;
+ }
+
+ int encodedLen = 0;
+
+ if (tileType & hextileAnySubrects) {
+
+ if (tileType & hextileSubrectsColoured) {
+ oldFgValid = false;
+ } else {
+ if (!oldFgValid || oldFg != fg) {
+ tileType |= hextileFgSpecified;
+ oldFg = fg;
+ oldFgValid = true;
+ }
+ }
+
+ encodedLen = HEXTILE_ENCODE_TILE(buf, t.width(), t.height(), tileType,
+ encoded, bg);
+
+ if (encodedLen < 0) {
+ GET_IMAGE_INTO_BUF(t,buf);
+ os->writeU8(hextileRaw);
+ os->writeBytes(buf, t.width() * t.height() * (BPP/8));
+ oldBgValid = oldFgValid = false;
+ continue;
+ }
+ }
+
+ os->writeU8(tileType);
+ if (tileType & hextileBgSpecified) os->WRITE_PIXEL(bg);
+ if (tileType & hextileFgSpecified) os->WRITE_PIXEL(fg);
+ if (tileType & hextileAnySubrects) os->writeBytes(encoded, encodedLen);
+ }
+ }
+}
+
+
+int HEXTILE_ENCODE_TILE (PIXEL_T* data, int w, int h, int tileType,
+ rdr::U8* encoded, PIXEL_T bg)
+{
+ rdr::U8* nSubrectsPtr = encoded;
+ *nSubrectsPtr = 0;
+ encoded++;
+
+ for (int y = 0; y < h; y++)
+ {
+ int x = 0;
+ while (x < w) {
+ if (*data == bg) {
+ x++;
+ data++;
+ continue;
+ }
+
+ // Find horizontal subrect first
+ PIXEL_T* ptr = data+1;
+ PIXEL_T* eol = data+w-x;
+ while (ptr < eol && *ptr == *data) ptr++;
+ int sw = ptr - data;
+
+ ptr = data + w;
+ int sh = 1;
+ while (sh < h-y) {
+ eol = ptr + sw;
+ while (ptr < eol)
+ if (*ptr++ != *data) goto endOfHorizSubrect;
+ ptr += w - sw;
+ sh++;
+ }
+ endOfHorizSubrect:
+
+ // Find vertical subrect
+ int vh;
+ for (vh = sh; vh < h-y; vh++)
+ if (data[vh*w] != *data) break;
+
+ if (vh != sh) {
+ ptr = data+1;
+ int vw;
+ for (vw = 1; vw < sw; vw++) {
+ for (int i = 0; i < vh; i++)
+ if (ptr[i*w] != *data) goto endOfVertSubrect;
+ ptr++;
+ }
+ endOfVertSubrect:
+
+ // If vertical subrect bigger than horizontal then use that.
+ if (sw*sh < vw*vh) {
+ sw = vw;
+ sh = vh;
+ }
+ }
+
+ (*nSubrectsPtr)++;
+
+ if (tileType & hextileSubrectsColoured) {
+ if (encoded - nSubrectsPtr + (BPP/8) > w*h*(BPP/8)) return -1;
+#if (BPP == 8)
+ *encoded++ = *data;
+#elif (BPP == 16)
+ *encoded++ = ((rdr::U8*)data)[0];
+ *encoded++ = ((rdr::U8*)data)[1];
+#elif (BPP == 32)
+ *encoded++ = ((rdr::U8*)data)[0];
+ *encoded++ = ((rdr::U8*)data)[1];
+ *encoded++ = ((rdr::U8*)data)[2];
+ *encoded++ = ((rdr::U8*)data)[3];
+#endif
+ }
+
+ if (encoded - nSubrectsPtr + 2 > w*h*(BPP/8)) return -1;
+ *encoded++ = (x << 4) | y;
+ *encoded++ = ((sw-1) << 4) | (sh-1);
+
+ ptr = data+w;
+ PIXEL_T* eor = data+w*sh;
+ while (ptr < eor) {
+ eol = ptr + sw;
+ while (ptr < eol) *ptr++ = bg;
+ ptr += w - sw;
+ }
+ x += sw;
+ data += sw;
+ }
+ }
+ return encoded - nSubrectsPtr;
+}
+
+
+int TEST_TILE_TYPE (PIXEL_T* data, int w, int h, PIXEL_T* bg, PIXEL_T* fg)
+{
+ int tileType = 0;
+ PIXEL_T pix1 = *data, pix2 = 0;
+ int count1 = 0, count2 = 0;
+ PIXEL_T* end = data + w*h;
+
+ for (PIXEL_T* ptr = data; ptr < end; ptr++) {
+ if (*ptr == pix1) {
+ count1++;
+ continue;
+ }
+
+ if (count2 == 0) {
+ tileType |= hextileAnySubrects;
+ pix2 = *ptr;
+ }
+
+ if (*data == pix2) {
+ count2++;
+ continue;
+ }
+
+ tileType |= hextileSubrectsColoured;
+ break;
+ }
+
+ if (count1 >= count2) {
+ *bg = pix1; *fg = pix2;
+ } else {
+ *bg = pix2; *fg = pix1;
+ }
+ return tileType;
+}
+
+#undef PIXEL_T
+#undef WRITE_PIXEL
+#undef HEXTILE_ENCODE
+#undef HEXTILE_ENCODE_TILE
+#undef TEST_TILE_TYPE
+}
diff --git a/rfb/keysymdef.h b/rfb/keysymdef.h
new file mode 100644
index 00000000..979ebdd5
--- /dev/null
+++ b/rfb/keysymdef.h
@@ -0,0 +1,1595 @@
+/* $TOG: keysymdef.h /main/28 1998/05/22 16:18:01 kaleb $ */
+
+/***********************************************************
+Copyright 1987, 1994, 1998 The Open Group
+
+All Rights Reserved.
+
+The above copyright notice and this permission notice shall be included
+in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR
+OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+OTHER DEALINGS IN THE SOFTWARE.
+
+Except as contained in this notice, the name of The Open Group shall
+not be used in advertising or otherwise to promote the sale, use or
+other dealings in this Software without prior written authorization
+from The Open Group.
+
+
+Copyright 1987 by Digital Equipment Corporation, Maynard, Massachusetts
+
+ All Rights Reserved
+
+Permission to use, copy, modify, and distribute this software and its
+documentation for any purpose and without fee is hereby granted,
+provided that the above copyright notice appear in all copies and that
+both that copyright notice and this permission notice appear in
+supporting documentation, and that the name of Digital not be
+used in advertising or publicity pertaining to distribution of the
+software without specific, written prior permission.
+
+DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
+ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
+DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
+ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
+WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
+ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
+SOFTWARE.
+
+******************************************************************/
+
+#define XK_VoidSymbol 0xFFFFFF /* void symbol */
+
+#ifdef XK_MISCELLANY
+/*
+ * TTY Functions, cleverly chosen to map to ascii, for convenience of
+ * programming, but could have been arbitrary (at the cost of lookup
+ * tables in client code.
+ */
+
+#define XK_BackSpace 0xFF08 /* back space, back char */
+#define XK_Tab 0xFF09
+#define XK_Linefeed 0xFF0A /* Linefeed, LF */
+#define XK_Clear 0xFF0B
+#define XK_Return 0xFF0D /* Return, enter */
+#define XK_Pause 0xFF13 /* Pause, hold */
+#define XK_Scroll_Lock 0xFF14
+#define XK_Sys_Req 0xFF15
+#define XK_Escape 0xFF1B
+#define XK_Delete 0xFFFF /* Delete, rubout */
+
+
+
+/* International & multi-key character composition */
+
+#define XK_Multi_key 0xFF20 /* Multi-key character compose */
+#define XK_Codeinput 0xFF37
+#define XK_SingleCandidate 0xFF3C
+#define XK_MultipleCandidate 0xFF3D
+#define XK_PreviousCandidate 0xFF3E
+
+/* Japanese keyboard support */
+
+#define XK_Kanji 0xFF21 /* Kanji, Kanji convert */
+#define XK_Muhenkan 0xFF22 /* Cancel Conversion */
+#define XK_Henkan_Mode 0xFF23 /* Start/Stop Conversion */
+#define XK_Henkan 0xFF23 /* Alias for Henkan_Mode */
+#define XK_Romaji 0xFF24 /* to Romaji */
+#define XK_Hiragana 0xFF25 /* to Hiragana */
+#define XK_Katakana 0xFF26 /* to Katakana */
+#define XK_Hiragana_Katakana 0xFF27 /* Hiragana/Katakana toggle */
+#define XK_Zenkaku 0xFF28 /* to Zenkaku */
+#define XK_Hankaku 0xFF29 /* to Hankaku */
+#define XK_Zenkaku_Hankaku 0xFF2A /* Zenkaku/Hankaku toggle */
+#define XK_Touroku 0xFF2B /* Add to Dictionary */
+#define XK_Massyo 0xFF2C /* Delete from Dictionary */
+#define XK_Kana_Lock 0xFF2D /* Kana Lock */
+#define XK_Kana_Shift 0xFF2E /* Kana Shift */
+#define XK_Eisu_Shift 0xFF2F /* Alphanumeric Shift */
+#define XK_Eisu_toggle 0xFF30 /* Alphanumeric toggle */
+#define XK_Kanji_Bangou 0xFF37 /* Codeinput */
+#define XK_Zen_Koho 0xFF3D /* Multiple/All Candidate(s) */
+#define XK_Mae_Koho 0xFF3E /* Previous Candidate */
+
+/* 0xFF31 thru 0xFF3F are under XK_KOREAN */
+
+/* Cursor control & motion */
+
+#define XK_Home 0xFF50
+#define XK_Left 0xFF51 /* Move left, left arrow */
+#define XK_Up 0xFF52 /* Move up, up arrow */
+#define XK_Right 0xFF53 /* Move right, right arrow */
+#define XK_Down 0xFF54 /* Move down, down arrow */
+#define XK_Prior 0xFF55 /* Prior, previous */
+#define XK_Page_Up 0xFF55
+#define XK_Next 0xFF56 /* Next */
+#define XK_Page_Down 0xFF56
+#define XK_End 0xFF57 /* EOL */
+#define XK_Begin 0xFF58 /* BOL */
+
+
+/* Misc Functions */
+
+#define XK_Select 0xFF60 /* Select, mark */
+#define XK_Print 0xFF61
+#define XK_Execute 0xFF62 /* Execute, run, do */
+#define XK_Insert 0xFF63 /* Insert, insert here */
+#define XK_Undo 0xFF65 /* Undo, oops */
+#define XK_Redo 0xFF66 /* redo, again */
+#define XK_Menu 0xFF67
+#define XK_Find 0xFF68 /* Find, search */
+#define XK_Cancel 0xFF69 /* Cancel, stop, abort, exit */
+#define XK_Help 0xFF6A /* Help */
+#define XK_Break 0xFF6B
+#define XK_Mode_switch 0xFF7E /* Character set switch */
+#define XK_script_switch 0xFF7E /* Alias for mode_switch */
+#define XK_Num_Lock 0xFF7F
+
+/* Keypad Functions, keypad numbers cleverly chosen to map to ascii */
+
+#define XK_KP_Space 0xFF80 /* space */
+#define XK_KP_Tab 0xFF89
+#define XK_KP_Enter 0xFF8D /* enter */
+#define XK_KP_F1 0xFF91 /* PF1, KP_A, ... */
+#define XK_KP_F2 0xFF92
+#define XK_KP_F3 0xFF93
+#define XK_KP_F4 0xFF94
+#define XK_KP_Home 0xFF95
+#define XK_KP_Left 0xFF96
+#define XK_KP_Up 0xFF97
+#define XK_KP_Right 0xFF98
+#define XK_KP_Down 0xFF99
+#define XK_KP_Prior 0xFF9A
+#define XK_KP_Page_Up 0xFF9A
+#define XK_KP_Next 0xFF9B
+#define XK_KP_Page_Down 0xFF9B
+#define XK_KP_End 0xFF9C
+#define XK_KP_Begin 0xFF9D
+#define XK_KP_Insert 0xFF9E
+#define XK_KP_Delete 0xFF9F
+#define XK_KP_Equal 0xFFBD /* equals */
+#define XK_KP_Multiply 0xFFAA
+#define XK_KP_Add 0xFFAB
+#define XK_KP_Separator 0xFFAC /* separator, often comma */
+#define XK_KP_Subtract 0xFFAD
+#define XK_KP_Decimal 0xFFAE
+#define XK_KP_Divide 0xFFAF
+
+#define XK_KP_0 0xFFB0
+#define XK_KP_1 0xFFB1
+#define XK_KP_2 0xFFB2
+#define XK_KP_3 0xFFB3
+#define XK_KP_4 0xFFB4
+#define XK_KP_5 0xFFB5
+#define XK_KP_6 0xFFB6
+#define XK_KP_7 0xFFB7
+#define XK_KP_8 0xFFB8
+#define XK_KP_9 0xFFB9
+
+
+
+/*
+ * Auxilliary Functions; note the duplicate definitions for left and right
+ * function keys; Sun keyboards and a few other manufactures have such
+ * function key groups on the left and/or right sides of the keyboard.
+ * We've not found a keyboard with more than 35 function keys total.
+ */
+
+#define XK_F1 0xFFBE
+#define XK_F2 0xFFBF
+#define XK_F3 0xFFC0
+#define XK_F4 0xFFC1
+#define XK_F5 0xFFC2
+#define XK_F6 0xFFC3
+#define XK_F7 0xFFC4
+#define XK_F8 0xFFC5
+#define XK_F9 0xFFC6
+#define XK_F10 0xFFC7
+#define XK_F11 0xFFC8
+#define XK_L1 0xFFC8
+#define XK_F12 0xFFC9
+#define XK_L2 0xFFC9
+#define XK_F13 0xFFCA
+#define XK_L3 0xFFCA
+#define XK_F14 0xFFCB
+#define XK_L4 0xFFCB
+#define XK_F15 0xFFCC
+#define XK_L5 0xFFCC
+#define XK_F16 0xFFCD
+#define XK_L6 0xFFCD
+#define XK_F17 0xFFCE
+#define XK_L7 0xFFCE
+#define XK_F18 0xFFCF
+#define XK_L8 0xFFCF
+#define XK_F19 0xFFD0
+#define XK_L9 0xFFD0
+#define XK_F20 0xFFD1
+#define XK_L10 0xFFD1
+#define XK_F21 0xFFD2
+#define XK_R1 0xFFD2
+#define XK_F22 0xFFD3
+#define XK_R2 0xFFD3
+#define XK_F23 0xFFD4
+#define XK_R3 0xFFD4
+#define XK_F24 0xFFD5
+#define XK_R4 0xFFD5
+#define XK_F25 0xFFD6
+#define XK_R5 0xFFD6
+#define XK_F26 0xFFD7
+#define XK_R6 0xFFD7
+#define XK_F27 0xFFD8
+#define XK_R7 0xFFD8
+#define XK_F28 0xFFD9
+#define XK_R8 0xFFD9
+#define XK_F29 0xFFDA
+#define XK_R9 0xFFDA
+#define XK_F30 0xFFDB
+#define XK_R10 0xFFDB
+#define XK_F31 0xFFDC
+#define XK_R11 0xFFDC
+#define XK_F32 0xFFDD
+#define XK_R12 0xFFDD
+#define XK_F33 0xFFDE
+#define XK_R13 0xFFDE
+#define XK_F34 0xFFDF
+#define XK_R14 0xFFDF
+#define XK_F35 0xFFE0
+#define XK_R15 0xFFE0
+
+/* Modifiers */
+
+#define XK_Shift_L 0xFFE1 /* Left shift */
+#define XK_Shift_R 0xFFE2 /* Right shift */
+#define XK_Control_L 0xFFE3 /* Left control */
+#define XK_Control_R 0xFFE4 /* Right control */
+#define XK_Caps_Lock 0xFFE5 /* Caps lock */
+#define XK_Shift_Lock 0xFFE6 /* Shift lock */
+
+#define XK_Meta_L 0xFFE7 /* Left meta */
+#define XK_Meta_R 0xFFE8 /* Right meta */
+#define XK_Alt_L 0xFFE9 /* Left alt */
+#define XK_Alt_R 0xFFEA /* Right alt */
+#define XK_Super_L 0xFFEB /* Left super */
+#define XK_Super_R 0xFFEC /* Right super */
+#define XK_Hyper_L 0xFFED /* Left hyper */
+#define XK_Hyper_R 0xFFEE /* Right hyper */
+#endif /* XK_MISCELLANY */
+
+/*
+ * ISO 9995 Function and Modifier Keys
+ * Byte 3 = 0xFE
+ */
+
+#ifdef XK_XKB_KEYS
+#define XK_ISO_Lock 0xFE01
+#define XK_ISO_Level2_Latch 0xFE02
+#define XK_ISO_Level3_Shift 0xFE03
+#define XK_ISO_Level3_Latch 0xFE04
+#define XK_ISO_Level3_Lock 0xFE05
+#define XK_ISO_Group_Shift 0xFF7E /* Alias for mode_switch */
+#define XK_ISO_Group_Latch 0xFE06
+#define XK_ISO_Group_Lock 0xFE07
+#define XK_ISO_Next_Group 0xFE08
+#define XK_ISO_Next_Group_Lock 0xFE09
+#define XK_ISO_Prev_Group 0xFE0A
+#define XK_ISO_Prev_Group_Lock 0xFE0B
+#define XK_ISO_First_Group 0xFE0C
+#define XK_ISO_First_Group_Lock 0xFE0D
+#define XK_ISO_Last_Group 0xFE0E
+#define XK_ISO_Last_Group_Lock 0xFE0F
+
+#define XK_ISO_Left_Tab 0xFE20
+#define XK_ISO_Move_Line_Up 0xFE21
+#define XK_ISO_Move_Line_Down 0xFE22
+#define XK_ISO_Partial_Line_Up 0xFE23
+#define XK_ISO_Partial_Line_Down 0xFE24
+#define XK_ISO_Partial_Space_Left 0xFE25
+#define XK_ISO_Partial_Space_Right 0xFE26
+#define XK_ISO_Set_Margin_Left 0xFE27
+#define XK_ISO_Set_Margin_Right 0xFE28
+#define XK_ISO_Release_Margin_Left 0xFE29
+#define XK_ISO_Release_Margin_Right 0xFE2A
+#define XK_ISO_Release_Both_Margins 0xFE2B
+#define XK_ISO_Fast_Cursor_Left 0xFE2C
+#define XK_ISO_Fast_Cursor_Right 0xFE2D
+#define XK_ISO_Fast_Cursor_Up 0xFE2E
+#define XK_ISO_Fast_Cursor_Down 0xFE2F
+#define XK_ISO_Continuous_Underline 0xFE30
+#define XK_ISO_Discontinuous_Underline 0xFE31
+#define XK_ISO_Emphasize 0xFE32
+#define XK_ISO_Center_Object 0xFE33
+#define XK_ISO_Enter 0xFE34
+
+#define XK_dead_grave 0xFE50
+#define XK_dead_acute 0xFE51
+#define XK_dead_circumflex 0xFE52
+#define XK_dead_tilde 0xFE53
+#define XK_dead_macron 0xFE54
+#define XK_dead_breve 0xFE55
+#define XK_dead_abovedot 0xFE56
+#define XK_dead_diaeresis 0xFE57
+#define XK_dead_abovering 0xFE58
+#define XK_dead_doubleacute 0xFE59
+#define XK_dead_caron 0xFE5A
+#define XK_dead_cedilla 0xFE5B
+#define XK_dead_ogonek 0xFE5C
+#define XK_dead_iota 0xFE5D
+#define XK_dead_voiced_sound 0xFE5E
+#define XK_dead_semivoiced_sound 0xFE5F
+#define XK_dead_belowdot 0xFE60
+
+#define XK_First_Virtual_Screen 0xFED0
+#define XK_Prev_Virtual_Screen 0xFED1
+#define XK_Next_Virtual_Screen 0xFED2
+#define XK_Last_Virtual_Screen 0xFED4
+#define XK_Terminate_Server 0xFED5
+
+#define XK_AccessX_Enable 0xFE70
+#define XK_AccessX_Feedback_Enable 0xFE71
+#define XK_RepeatKeys_Enable 0xFE72
+#define XK_SlowKeys_Enable 0xFE73
+#define XK_BounceKeys_Enable 0xFE74
+#define XK_StickyKeys_Enable 0xFE75
+#define XK_MouseKeys_Enable 0xFE76
+#define XK_MouseKeys_Accel_Enable 0xFE77
+#define XK_Overlay1_Enable 0xFE78
+#define XK_Overlay2_Enable 0xFE79
+#define XK_AudibleBell_Enable 0xFE7A
+
+#define XK_Pointer_Left 0xFEE0
+#define XK_Pointer_Right 0xFEE1
+#define XK_Pointer_Up 0xFEE2
+#define XK_Pointer_Down 0xFEE3
+#define XK_Pointer_UpLeft 0xFEE4
+#define XK_Pointer_UpRight 0xFEE5
+#define XK_Pointer_DownLeft 0xFEE6
+#define XK_Pointer_DownRight 0xFEE7
+#define XK_Pointer_Button_Dflt 0xFEE8
+#define XK_Pointer_Button1 0xFEE9
+#define XK_Pointer_Button2 0xFEEA
+#define XK_Pointer_Button3 0xFEEB
+#define XK_Pointer_Button4 0xFEEC
+#define XK_Pointer_Button5 0xFEED
+#define XK_Pointer_DblClick_Dflt 0xFEEE
+#define XK_Pointer_DblClick1 0xFEEF
+#define XK_Pointer_DblClick2 0xFEF0
+#define XK_Pointer_DblClick3 0xFEF1
+#define XK_Pointer_DblClick4 0xFEF2
+#define XK_Pointer_DblClick5 0xFEF3
+#define XK_Pointer_Drag_Dflt 0xFEF4
+#define XK_Pointer_Drag1 0xFEF5
+#define XK_Pointer_Drag2 0xFEF6
+#define XK_Pointer_Drag3 0xFEF7
+#define XK_Pointer_Drag4 0xFEF8
+#define XK_Pointer_Drag5 0xFEFD
+
+#define XK_Pointer_EnableKeys 0xFEF9
+#define XK_Pointer_Accelerate 0xFEFA
+#define XK_Pointer_DfltBtnNext 0xFEFB
+#define XK_Pointer_DfltBtnPrev 0xFEFC
+
+#endif
+
+/*
+ * 3270 Terminal Keys
+ * Byte 3 = 0xFD
+ */
+
+#ifdef XK_3270
+#define XK_3270_Duplicate 0xFD01
+#define XK_3270_FieldMark 0xFD02
+#define XK_3270_Right2 0xFD03
+#define XK_3270_Left2 0xFD04
+#define XK_3270_BackTab 0xFD05
+#define XK_3270_EraseEOF 0xFD06
+#define XK_3270_EraseInput 0xFD07
+#define XK_3270_Reset 0xFD08
+#define XK_3270_Quit 0xFD09
+#define XK_3270_PA1 0xFD0A
+#define XK_3270_PA2 0xFD0B
+#define XK_3270_PA3 0xFD0C
+#define XK_3270_Test 0xFD0D
+#define XK_3270_Attn 0xFD0E
+#define XK_3270_CursorBlink 0xFD0F
+#define XK_3270_AltCursor 0xFD10
+#define XK_3270_KeyClick 0xFD11
+#define XK_3270_Jump 0xFD12
+#define XK_3270_Ident 0xFD13
+#define XK_3270_Rule 0xFD14
+#define XK_3270_Copy 0xFD15
+#define XK_3270_Play 0xFD16
+#define XK_3270_Setup 0xFD17
+#define XK_3270_Record 0xFD18
+#define XK_3270_ChangeScreen 0xFD19
+#define XK_3270_DeleteWord 0xFD1A
+#define XK_3270_ExSelect 0xFD1B
+#define XK_3270_CursorSelect 0xFD1C
+#define XK_3270_PrintScreen 0xFD1D
+#define XK_3270_Enter 0xFD1E
+#endif
+
+/*
+ * Latin 1
+ * Byte 3 = 0
+ */
+#ifdef XK_LATIN1
+#define XK_space 0x020
+#define XK_exclam 0x021
+#define XK_quotedbl 0x022
+#define XK_numbersign 0x023
+#define XK_dollar 0x024
+#define XK_percent 0x025
+#define XK_ampersand 0x026
+#define XK_apostrophe 0x027
+#define XK_quoteright 0x027 /* deprecated */
+#define XK_parenleft 0x028
+#define XK_parenright 0x029
+#define XK_asterisk 0x02a
+#define XK_plus 0x02b
+#define XK_comma 0x02c
+#define XK_minus 0x02d
+#define XK_period 0x02e
+#define XK_slash 0x02f
+#define XK_0 0x030
+#define XK_1 0x031
+#define XK_2 0x032
+#define XK_3 0x033
+#define XK_4 0x034
+#define XK_5 0x035
+#define XK_6 0x036
+#define XK_7 0x037
+#define XK_8 0x038
+#define XK_9 0x039
+#define XK_colon 0x03a
+#define XK_semicolon 0x03b
+#define XK_less 0x03c
+#define XK_equal 0x03d
+#define XK_greater 0x03e
+#define XK_question 0x03f
+#define XK_at 0x040
+#define XK_A 0x041
+#define XK_B 0x042
+#define XK_C 0x043
+#define XK_D 0x044
+#define XK_E 0x045
+#define XK_F 0x046
+#define XK_G 0x047
+#define XK_H 0x048
+#define XK_I 0x049
+#define XK_J 0x04a
+#define XK_K 0x04b
+#define XK_L 0x04c
+#define XK_M 0x04d
+#define XK_N 0x04e
+#define XK_O 0x04f
+#define XK_P 0x050
+#define XK_Q 0x051
+#define XK_R 0x052
+#define XK_S 0x053
+#define XK_T 0x054
+#define XK_U 0x055
+#define XK_V 0x056
+#define XK_W 0x057
+#define XK_X 0x058
+#define XK_Y 0x059
+#define XK_Z 0x05a
+#define XK_bracketleft 0x05b
+#define XK_backslash 0x05c
+#define XK_bracketright 0x05d
+#define XK_asciicircum 0x05e
+#define XK_underscore 0x05f
+#define XK_grave 0x060
+#define XK_quoteleft 0x060 /* deprecated */
+#define XK_a 0x061
+#define XK_b 0x062
+#define XK_c 0x063
+#define XK_d 0x064
+#define XK_e 0x065
+#define XK_f 0x066
+#define XK_g 0x067
+#define XK_h 0x068
+#define XK_i 0x069
+#define XK_j 0x06a
+#define XK_k 0x06b
+#define XK_l 0x06c
+#define XK_m 0x06d
+#define XK_n 0x06e
+#define XK_o 0x06f
+#define XK_p 0x070
+#define XK_q 0x071
+#define XK_r 0x072
+#define XK_s 0x073
+#define XK_t 0x074
+#define XK_u 0x075
+#define XK_v 0x076
+#define XK_w 0x077
+#define XK_x 0x078
+#define XK_y 0x079
+#define XK_z 0x07a
+#define XK_braceleft 0x07b
+#define XK_bar 0x07c
+#define XK_braceright 0x07d
+#define XK_asciitilde 0x07e
+
+#define XK_nobreakspace 0x0a0
+#define XK_exclamdown 0x0a1
+#define XK_cent 0x0a2
+#define XK_sterling 0x0a3
+#define XK_currency 0x0a4
+#define XK_yen 0x0a5
+#define XK_brokenbar 0x0a6
+#define XK_section 0x0a7
+#define XK_diaeresis 0x0a8
+#define XK_copyright 0x0a9
+#define XK_ordfeminine 0x0aa
+#define XK_guillemotleft 0x0ab /* left angle quotation mark */
+#define XK_notsign 0x0ac
+#define XK_hyphen 0x0ad
+#define XK_registered 0x0ae
+#define XK_macron 0x0af
+#define XK_degree 0x0b0
+#define XK_plusminus 0x0b1
+#define XK_twosuperior 0x0b2
+#define XK_threesuperior 0x0b3
+#define XK_acute 0x0b4
+#define XK_mu 0x0b5
+#define XK_paragraph 0x0b6
+#define XK_periodcentered 0x0b7
+#define XK_cedilla 0x0b8
+#define XK_onesuperior 0x0b9
+#define XK_masculine 0x0ba
+#define XK_guillemotright 0x0bb /* right angle quotation mark */
+#define XK_onequarter 0x0bc
+#define XK_onehalf 0x0bd
+#define XK_threequarters 0x0be
+#define XK_questiondown 0x0bf
+#define XK_Agrave 0x0c0
+#define XK_Aacute 0x0c1
+#define XK_Acircumflex 0x0c2
+#define XK_Atilde 0x0c3
+#define XK_Adiaeresis 0x0c4
+#define XK_Aring 0x0c5
+#define XK_AE 0x0c6
+#define XK_Ccedilla 0x0c7
+#define XK_Egrave 0x0c8
+#define XK_Eacute 0x0c9
+#define XK_Ecircumflex 0x0ca
+#define XK_Ediaeresis 0x0cb
+#define XK_Igrave 0x0cc
+#define XK_Iacute 0x0cd
+#define XK_Icircumflex 0x0ce
+#define XK_Idiaeresis 0x0cf
+#define XK_ETH 0x0d0
+#define XK_Eth 0x0d0 /* deprecated */
+#define XK_Ntilde 0x0d1
+#define XK_Ograve 0x0d2
+#define XK_Oacute 0x0d3
+#define XK_Ocircumflex 0x0d4
+#define XK_Otilde 0x0d5
+#define XK_Odiaeresis 0x0d6
+#define XK_multiply 0x0d7
+#define XK_Ooblique 0x0d8
+#define XK_Ugrave 0x0d9
+#define XK_Uacute 0x0da
+#define XK_Ucircumflex 0x0db
+#define XK_Udiaeresis 0x0dc
+#define XK_Yacute 0x0dd
+#define XK_THORN 0x0de
+#define XK_Thorn 0x0de /* deprecated */
+#define XK_ssharp 0x0df
+#define XK_agrave 0x0e0
+#define XK_aacute 0x0e1
+#define XK_acircumflex 0x0e2
+#define XK_atilde 0x0e3
+#define XK_adiaeresis 0x0e4
+#define XK_aring 0x0e5
+#define XK_ae 0x0e6
+#define XK_ccedilla 0x0e7
+#define XK_egrave 0x0e8
+#define XK_eacute 0x0e9
+#define XK_ecircumflex 0x0ea
+#define XK_ediaeresis 0x0eb
+#define XK_igrave 0x0ec
+#define XK_iacute 0x0ed
+#define XK_icircumflex 0x0ee
+#define XK_idiaeresis 0x0ef
+#define XK_eth 0x0f0
+#define XK_ntilde 0x0f1
+#define XK_ograve 0x0f2
+#define XK_oacute 0x0f3
+#define XK_ocircumflex 0x0f4
+#define XK_otilde 0x0f5
+#define XK_odiaeresis 0x0f6
+#define XK_division 0x0f7
+#define XK_oslash 0x0f8
+#define XK_ugrave 0x0f9
+#define XK_uacute 0x0fa
+#define XK_ucircumflex 0x0fb
+#define XK_udiaeresis 0x0fc
+#define XK_yacute 0x0fd
+#define XK_thorn 0x0fe
+#define XK_ydiaeresis 0x0ff
+#endif /* XK_LATIN1 */
+
+/*
+ * Latin 2
+ * Byte 3 = 1
+ */
+
+#ifdef XK_LATIN2
+#define XK_Aogonek 0x1a1
+#define XK_breve 0x1a2
+#define XK_Lstroke 0x1a3
+#define XK_Lcaron 0x1a5
+#define XK_Sacute 0x1a6
+#define XK_Scaron 0x1a9
+#define XK_Scedilla 0x1aa
+#define XK_Tcaron 0x1ab
+#define XK_Zacute 0x1ac
+#define XK_Zcaron 0x1ae
+#define XK_Zabovedot 0x1af
+#define XK_aogonek 0x1b1
+#define XK_ogonek 0x1b2
+#define XK_lstroke 0x1b3
+#define XK_lcaron 0x1b5
+#define XK_sacute 0x1b6
+#define XK_caron 0x1b7
+#define XK_scaron 0x1b9
+#define XK_scedilla 0x1ba
+#define XK_tcaron 0x1bb
+#define XK_zacute 0x1bc
+#define XK_doubleacute 0x1bd
+#define XK_zcaron 0x1be
+#define XK_zabovedot 0x1bf
+#define XK_Racute 0x1c0
+#define XK_Abreve 0x1c3
+#define XK_Lacute 0x1c5
+#define XK_Cacute 0x1c6
+#define XK_Ccaron 0x1c8
+#define XK_Eogonek 0x1ca
+#define XK_Ecaron 0x1cc
+#define XK_Dcaron 0x1cf
+#define XK_Dstroke 0x1d0
+#define XK_Nacute 0x1d1
+#define XK_Ncaron 0x1d2
+#define XK_Odoubleacute 0x1d5
+#define XK_Rcaron 0x1d8
+#define XK_Uring 0x1d9
+#define XK_Udoubleacute 0x1db
+#define XK_Tcedilla 0x1de
+#define XK_racute 0x1e0
+#define XK_abreve 0x1e3
+#define XK_lacute 0x1e5
+#define XK_cacute 0x1e6
+#define XK_ccaron 0x1e8
+#define XK_eogonek 0x1ea
+#define XK_ecaron 0x1ec
+#define XK_dcaron 0x1ef
+#define XK_dstroke 0x1f0
+#define XK_nacute 0x1f1
+#define XK_ncaron 0x1f2
+#define XK_odoubleacute 0x1f5
+#define XK_udoubleacute 0x1fb
+#define XK_rcaron 0x1f8
+#define XK_uring 0x1f9
+#define XK_tcedilla 0x1fe
+#define XK_abovedot 0x1ff
+#endif /* XK_LATIN2 */
+
+/*
+ * Latin 3
+ * Byte 3 = 2
+ */
+
+#ifdef XK_LATIN3
+#define XK_Hstroke 0x2a1
+#define XK_Hcircumflex 0x2a6
+#define XK_Iabovedot 0x2a9
+#define XK_Gbreve 0x2ab
+#define XK_Jcircumflex 0x2ac
+#define XK_hstroke 0x2b1
+#define XK_hcircumflex 0x2b6
+#define XK_idotless 0x2b9
+#define XK_gbreve 0x2bb
+#define XK_jcircumflex 0x2bc
+#define XK_Cabovedot 0x2c5
+#define XK_Ccircumflex 0x2c6
+#define XK_Gabovedot 0x2d5
+#define XK_Gcircumflex 0x2d8
+#define XK_Ubreve 0x2dd
+#define XK_Scircumflex 0x2de
+#define XK_cabovedot 0x2e5
+#define XK_ccircumflex 0x2e6
+#define XK_gabovedot 0x2f5
+#define XK_gcircumflex 0x2f8
+#define XK_ubreve 0x2fd
+#define XK_scircumflex 0x2fe
+#endif /* XK_LATIN3 */
+
+
+/*
+ * Latin 4
+ * Byte 3 = 3
+ */
+
+#ifdef XK_LATIN4
+#define XK_kra 0x3a2
+#define XK_kappa 0x3a2 /* deprecated */
+#define XK_Rcedilla 0x3a3
+#define XK_Itilde 0x3a5
+#define XK_Lcedilla 0x3a6
+#define XK_Emacron 0x3aa
+#define XK_Gcedilla 0x3ab
+#define XK_Tslash 0x3ac
+#define XK_rcedilla 0x3b3
+#define XK_itilde 0x3b5
+#define XK_lcedilla 0x3b6
+#define XK_emacron 0x3ba
+#define XK_gcedilla 0x3bb
+#define XK_tslash 0x3bc
+#define XK_ENG 0x3bd
+#define XK_eng 0x3bf
+#define XK_Amacron 0x3c0
+#define XK_Iogonek 0x3c7
+#define XK_Eabovedot 0x3cc
+#define XK_Imacron 0x3cf
+#define XK_Ncedilla 0x3d1
+#define XK_Omacron 0x3d2
+#define XK_Kcedilla 0x3d3
+#define XK_Uogonek 0x3d9
+#define XK_Utilde 0x3dd
+#define XK_Umacron 0x3de
+#define XK_amacron 0x3e0
+#define XK_iogonek 0x3e7
+#define XK_eabovedot 0x3ec
+#define XK_imacron 0x3ef
+#define XK_ncedilla 0x3f1
+#define XK_omacron 0x3f2
+#define XK_kcedilla 0x3f3
+#define XK_uogonek 0x3f9
+#define XK_utilde 0x3fd
+#define XK_umacron 0x3fe
+#endif /* XK_LATIN4 */
+
+/*
+ * Latin-9 (a.k.a. Latin-0)
+ * Byte 3 = 19
+ */
+
+#ifdef XK_LATIN9
+#define XK_OE 0x13bc
+#define XK_oe 0x13bd
+#define XK_Ydiaeresis 0x13be
+#endif /* XK_LATIN9 */
+
+/*
+ * Katakana
+ * Byte 3 = 4
+ */
+
+#ifdef XK_KATAKANA
+#define XK_overline 0x47e
+#define XK_kana_fullstop 0x4a1
+#define XK_kana_openingbracket 0x4a2
+#define XK_kana_closingbracket 0x4a3
+#define XK_kana_comma 0x4a4
+#define XK_kana_conjunctive 0x4a5
+#define XK_kana_middledot 0x4a5 /* deprecated */
+#define XK_kana_WO 0x4a6
+#define XK_kana_a 0x4a7
+#define XK_kana_i 0x4a8
+#define XK_kana_u 0x4a9
+#define XK_kana_e 0x4aa
+#define XK_kana_o 0x4ab
+#define XK_kana_ya 0x4ac
+#define XK_kana_yu 0x4ad
+#define XK_kana_yo 0x4ae
+#define XK_kana_tsu 0x4af
+#define XK_kana_tu 0x4af /* deprecated */
+#define XK_prolongedsound 0x4b0
+#define XK_kana_A 0x4b1
+#define XK_kana_I 0x4b2
+#define XK_kana_U 0x4b3
+#define XK_kana_E 0x4b4
+#define XK_kana_O 0x4b5
+#define XK_kana_KA 0x4b6
+#define XK_kana_KI 0x4b7
+#define XK_kana_KU 0x4b8
+#define XK_kana_KE 0x4b9
+#define XK_kana_KO 0x4ba
+#define XK_kana_SA 0x4bb
+#define XK_kana_SHI 0x4bc
+#define XK_kana_SU 0x4bd
+#define XK_kana_SE 0x4be
+#define XK_kana_SO 0x4bf
+#define XK_kana_TA 0x4c0
+#define XK_kana_CHI 0x4c1
+#define XK_kana_TI 0x4c1 /* deprecated */
+#define XK_kana_TSU 0x4c2
+#define XK_kana_TU 0x4c2 /* deprecated */
+#define XK_kana_TE 0x4c3
+#define XK_kana_TO 0x4c4
+#define XK_kana_NA 0x4c5
+#define XK_kana_NI 0x4c6
+#define XK_kana_NU 0x4c7
+#define XK_kana_NE 0x4c8
+#define XK_kana_NO 0x4c9
+#define XK_kana_HA 0x4ca
+#define XK_kana_HI 0x4cb
+#define XK_kana_FU 0x4cc
+#define XK_kana_HU 0x4cc /* deprecated */
+#define XK_kana_HE 0x4cd
+#define XK_kana_HO 0x4ce
+#define XK_kana_MA 0x4cf
+#define XK_kana_MI 0x4d0
+#define XK_kana_MU 0x4d1
+#define XK_kana_ME 0x4d2
+#define XK_kana_MO 0x4d3
+#define XK_kana_YA 0x4d4
+#define XK_kana_YU 0x4d5
+#define XK_kana_YO 0x4d6
+#define XK_kana_RA 0x4d7
+#define XK_kana_RI 0x4d8
+#define XK_kana_RU 0x4d9
+#define XK_kana_RE 0x4da
+#define XK_kana_RO 0x4db
+#define XK_kana_WA 0x4dc
+#define XK_kana_N 0x4dd
+#define XK_voicedsound 0x4de
+#define XK_semivoicedsound 0x4df
+#define XK_kana_switch 0xFF7E /* Alias for mode_switch */
+#endif /* XK_KATAKANA */
+
+/*
+ * Arabic
+ * Byte 3 = 5
+ */
+
+#ifdef XK_ARABIC
+#define XK_Arabic_comma 0x5ac
+#define XK_Arabic_semicolon 0x5bb
+#define XK_Arabic_question_mark 0x5bf
+#define XK_Arabic_hamza 0x5c1
+#define XK_Arabic_maddaonalef 0x5c2
+#define XK_Arabic_hamzaonalef 0x5c3
+#define XK_Arabic_hamzaonwaw 0x5c4
+#define XK_Arabic_hamzaunderalef 0x5c5
+#define XK_Arabic_hamzaonyeh 0x5c6
+#define XK_Arabic_alef 0x5c7
+#define XK_Arabic_beh 0x5c8
+#define XK_Arabic_tehmarbuta 0x5c9
+#define XK_Arabic_teh 0x5ca
+#define XK_Arabic_theh 0x5cb
+#define XK_Arabic_jeem 0x5cc
+#define XK_Arabic_hah 0x5cd
+#define XK_Arabic_khah 0x5ce
+#define XK_Arabic_dal 0x5cf
+#define XK_Arabic_thal 0x5d0
+#define XK_Arabic_ra 0x5d1
+#define XK_Arabic_zain 0x5d2
+#define XK_Arabic_seen 0x5d3
+#define XK_Arabic_sheen 0x5d4
+#define XK_Arabic_sad 0x5d5
+#define XK_Arabic_dad 0x5d6
+#define XK_Arabic_tah 0x5d7
+#define XK_Arabic_zah 0x5d8
+#define XK_Arabic_ain 0x5d9
+#define XK_Arabic_ghain 0x5da
+#define XK_Arabic_tatweel 0x5e0
+#define XK_Arabic_feh 0x5e1
+#define XK_Arabic_qaf 0x5e2
+#define XK_Arabic_kaf 0x5e3
+#define XK_Arabic_lam 0x5e4
+#define XK_Arabic_meem 0x5e5
+#define XK_Arabic_noon 0x5e6
+#define XK_Arabic_ha 0x5e7
+#define XK_Arabic_heh 0x5e7 /* deprecated */
+#define XK_Arabic_waw 0x5e8
+#define XK_Arabic_alefmaksura 0x5e9
+#define XK_Arabic_yeh 0x5ea
+#define XK_Arabic_fathatan 0x5eb
+#define XK_Arabic_dammatan 0x5ec
+#define XK_Arabic_kasratan 0x5ed
+#define XK_Arabic_fatha 0x5ee
+#define XK_Arabic_damma 0x5ef
+#define XK_Arabic_kasra 0x5f0
+#define XK_Arabic_shadda 0x5f1
+#define XK_Arabic_sukun 0x5f2
+#define XK_Arabic_switch 0xFF7E /* Alias for mode_switch */
+#endif /* XK_ARABIC */
+
+/*
+ * Cyrillic
+ * Byte 3 = 6
+ */
+#ifdef XK_CYRILLIC
+#define XK_Serbian_dje 0x6a1
+#define XK_Macedonia_gje 0x6a2
+#define XK_Cyrillic_io 0x6a3
+#define XK_Ukrainian_ie 0x6a4
+#define XK_Ukranian_je 0x6a4 /* deprecated */
+#define XK_Macedonia_dse 0x6a5
+#define XK_Ukrainian_i 0x6a6
+#define XK_Ukranian_i 0x6a6 /* deprecated */
+#define XK_Ukrainian_yi 0x6a7
+#define XK_Ukranian_yi 0x6a7 /* deprecated */
+#define XK_Cyrillic_je 0x6a8
+#define XK_Serbian_je 0x6a8 /* deprecated */
+#define XK_Cyrillic_lje 0x6a9
+#define XK_Serbian_lje 0x6a9 /* deprecated */
+#define XK_Cyrillic_nje 0x6aa
+#define XK_Serbian_nje 0x6aa /* deprecated */
+#define XK_Serbian_tshe 0x6ab
+#define XK_Macedonia_kje 0x6ac
+#define XK_Byelorussian_shortu 0x6ae
+#define XK_Cyrillic_dzhe 0x6af
+#define XK_Serbian_dze 0x6af /* deprecated */
+#define XK_numerosign 0x6b0
+#define XK_Serbian_DJE 0x6b1
+#define XK_Macedonia_GJE 0x6b2
+#define XK_Cyrillic_IO 0x6b3
+#define XK_Ukrainian_IE 0x6b4
+#define XK_Ukranian_JE 0x6b4 /* deprecated */
+#define XK_Macedonia_DSE 0x6b5
+#define XK_Ukrainian_I 0x6b6
+#define XK_Ukranian_I 0x6b6 /* deprecated */
+#define XK_Ukrainian_YI 0x6b7
+#define XK_Ukranian_YI 0x6b7 /* deprecated */
+#define XK_Cyrillic_JE 0x6b8
+#define XK_Serbian_JE 0x6b8 /* deprecated */
+#define XK_Cyrillic_LJE 0x6b9
+#define XK_Serbian_LJE 0x6b9 /* deprecated */
+#define XK_Cyrillic_NJE 0x6ba
+#define XK_Serbian_NJE 0x6ba /* deprecated */
+#define XK_Serbian_TSHE 0x6bb
+#define XK_Macedonia_KJE 0x6bc
+#define XK_Byelorussian_SHORTU 0x6be
+#define XK_Cyrillic_DZHE 0x6bf
+#define XK_Serbian_DZE 0x6bf /* deprecated */
+#define XK_Cyrillic_yu 0x6c0
+#define XK_Cyrillic_a 0x6c1
+#define XK_Cyrillic_be 0x6c2
+#define XK_Cyrillic_tse 0x6c3
+#define XK_Cyrillic_de 0x6c4
+#define XK_Cyrillic_ie 0x6c5
+#define XK_Cyrillic_ef 0x6c6
+#define XK_Cyrillic_ghe 0x6c7
+#define XK_Cyrillic_ha 0x6c8
+#define XK_Cyrillic_i 0x6c9
+#define XK_Cyrillic_shorti 0x6ca
+#define XK_Cyrillic_ka 0x6cb
+#define XK_Cyrillic_el 0x6cc
+#define XK_Cyrillic_em 0x6cd
+#define XK_Cyrillic_en 0x6ce
+#define XK_Cyrillic_o 0x6cf
+#define XK_Cyrillic_pe 0x6d0
+#define XK_Cyrillic_ya 0x6d1
+#define XK_Cyrillic_er 0x6d2
+#define XK_Cyrillic_es 0x6d3
+#define XK_Cyrillic_te 0x6d4
+#define XK_Cyrillic_u 0x6d5
+#define XK_Cyrillic_zhe 0x6d6
+#define XK_Cyrillic_ve 0x6d7
+#define XK_Cyrillic_softsign 0x6d8
+#define XK_Cyrillic_yeru 0x6d9
+#define XK_Cyrillic_ze 0x6da
+#define XK_Cyrillic_sha 0x6db
+#define XK_Cyrillic_e 0x6dc
+#define XK_Cyrillic_shcha 0x6dd
+#define XK_Cyrillic_che 0x6de
+#define XK_Cyrillic_hardsign 0x6df
+#define XK_Cyrillic_YU 0x6e0
+#define XK_Cyrillic_A 0x6e1
+#define XK_Cyrillic_BE 0x6e2
+#define XK_Cyrillic_TSE 0x6e3
+#define XK_Cyrillic_DE 0x6e4
+#define XK_Cyrillic_IE 0x6e5
+#define XK_Cyrillic_EF 0x6e6
+#define XK_Cyrillic_GHE 0x6e7
+#define XK_Cyrillic_HA 0x6e8
+#define XK_Cyrillic_I 0x6e9
+#define XK_Cyrillic_SHORTI 0x6ea
+#define XK_Cyrillic_KA 0x6eb
+#define XK_Cyrillic_EL 0x6ec
+#define XK_Cyrillic_EM 0x6ed
+#define XK_Cyrillic_EN 0x6ee
+#define XK_Cyrillic_O 0x6ef
+#define XK_Cyrillic_PE 0x6f0
+#define XK_Cyrillic_YA 0x6f1
+#define XK_Cyrillic_ER 0x6f2
+#define XK_Cyrillic_ES 0x6f3
+#define XK_Cyrillic_TE 0x6f4
+#define XK_Cyrillic_U 0x6f5
+#define XK_Cyrillic_ZHE 0x6f6
+#define XK_Cyrillic_VE 0x6f7
+#define XK_Cyrillic_SOFTSIGN 0x6f8
+#define XK_Cyrillic_YERU 0x6f9
+#define XK_Cyrillic_ZE 0x6fa
+#define XK_Cyrillic_SHA 0x6fb
+#define XK_Cyrillic_E 0x6fc
+#define XK_Cyrillic_SHCHA 0x6fd
+#define XK_Cyrillic_CHE 0x6fe
+#define XK_Cyrillic_HARDSIGN 0x6ff
+#endif /* XK_CYRILLIC */
+
+/*
+ * Greek
+ * Byte 3 = 7
+ */
+
+#ifdef XK_GREEK
+#define XK_Greek_ALPHAaccent 0x7a1
+#define XK_Greek_EPSILONaccent 0x7a2
+#define XK_Greek_ETAaccent 0x7a3
+#define XK_Greek_IOTAaccent 0x7a4
+#define XK_Greek_IOTAdiaeresis 0x7a5
+#define XK_Greek_OMICRONaccent 0x7a7
+#define XK_Greek_UPSILONaccent 0x7a8
+#define XK_Greek_UPSILONdieresis 0x7a9
+#define XK_Greek_OMEGAaccent 0x7ab
+#define XK_Greek_accentdieresis 0x7ae
+#define XK_Greek_horizbar 0x7af
+#define XK_Greek_alphaaccent 0x7b1
+#define XK_Greek_epsilonaccent 0x7b2
+#define XK_Greek_etaaccent 0x7b3
+#define XK_Greek_iotaaccent 0x7b4
+#define XK_Greek_iotadieresis 0x7b5
+#define XK_Greek_iotaaccentdieresis 0x7b6
+#define XK_Greek_omicronaccent 0x7b7
+#define XK_Greek_upsilonaccent 0x7b8
+#define XK_Greek_upsilondieresis 0x7b9
+#define XK_Greek_upsilonaccentdieresis 0x7ba
+#define XK_Greek_omegaaccent 0x7bb
+#define XK_Greek_ALPHA 0x7c1
+#define XK_Greek_BETA 0x7c2
+#define XK_Greek_GAMMA 0x7c3
+#define XK_Greek_DELTA 0x7c4
+#define XK_Greek_EPSILON 0x7c5
+#define XK_Greek_ZETA 0x7c6
+#define XK_Greek_ETA 0x7c7
+#define XK_Greek_THETA 0x7c8
+#define XK_Greek_IOTA 0x7c9
+#define XK_Greek_KAPPA 0x7ca
+#define XK_Greek_LAMDA 0x7cb
+#define XK_Greek_LAMBDA 0x7cb
+#define XK_Greek_MU 0x7cc
+#define XK_Greek_NU 0x7cd
+#define XK_Greek_XI 0x7ce
+#define XK_Greek_OMICRON 0x7cf
+#define XK_Greek_PI 0x7d0
+#define XK_Greek_RHO 0x7d1
+#define XK_Greek_SIGMA 0x7d2
+#define XK_Greek_TAU 0x7d4
+#define XK_Greek_UPSILON 0x7d5
+#define XK_Greek_PHI 0x7d6
+#define XK_Greek_CHI 0x7d7
+#define XK_Greek_PSI 0x7d8
+#define XK_Greek_OMEGA 0x7d9
+#define XK_Greek_alpha 0x7e1
+#define XK_Greek_beta 0x7e2
+#define XK_Greek_gamma 0x7e3
+#define XK_Greek_delta 0x7e4
+#define XK_Greek_epsilon 0x7e5
+#define XK_Greek_zeta 0x7e6
+#define XK_Greek_eta 0x7e7
+#define XK_Greek_theta 0x7e8
+#define XK_Greek_iota 0x7e9
+#define XK_Greek_kappa 0x7ea
+#define XK_Greek_lamda 0x7eb
+#define XK_Greek_lambda 0x7eb
+#define XK_Greek_mu 0x7ec
+#define XK_Greek_nu 0x7ed
+#define XK_Greek_xi 0x7ee
+#define XK_Greek_omicron 0x7ef
+#define XK_Greek_pi 0x7f0
+#define XK_Greek_rho 0x7f1
+#define XK_Greek_sigma 0x7f2
+#define XK_Greek_finalsmallsigma 0x7f3
+#define XK_Greek_tau 0x7f4
+#define XK_Greek_upsilon 0x7f5
+#define XK_Greek_phi 0x7f6
+#define XK_Greek_chi 0x7f7
+#define XK_Greek_psi 0x7f8
+#define XK_Greek_omega 0x7f9
+#define XK_Greek_switch 0xFF7E /* Alias for mode_switch */
+#endif /* XK_GREEK */
+
+/*
+ * Technical
+ * Byte 3 = 8
+ */
+
+#ifdef XK_TECHNICAL
+#define XK_leftradical 0x8a1
+#define XK_topleftradical 0x8a2
+#define XK_horizconnector 0x8a3
+#define XK_topintegral 0x8a4
+#define XK_botintegral 0x8a5
+#define XK_vertconnector 0x8a6
+#define XK_topleftsqbracket 0x8a7
+#define XK_botleftsqbracket 0x8a8
+#define XK_toprightsqbracket 0x8a9
+#define XK_botrightsqbracket 0x8aa
+#define XK_topleftparens 0x8ab
+#define XK_botleftparens 0x8ac
+#define XK_toprightparens 0x8ad
+#define XK_botrightparens 0x8ae
+#define XK_leftmiddlecurlybrace 0x8af
+#define XK_rightmiddlecurlybrace 0x8b0
+#define XK_topleftsummation 0x8b1
+#define XK_botleftsummation 0x8b2
+#define XK_topvertsummationconnector 0x8b3
+#define XK_botvertsummationconnector 0x8b4
+#define XK_toprightsummation 0x8b5
+#define XK_botrightsummation 0x8b6
+#define XK_rightmiddlesummation 0x8b7
+#define XK_lessthanequal 0x8bc
+#define XK_notequal 0x8bd
+#define XK_greaterthanequal 0x8be
+#define XK_integral 0x8bf
+#define XK_therefore 0x8c0
+#define XK_variation 0x8c1
+#define XK_infinity 0x8c2
+#define XK_nabla 0x8c5
+#define XK_approximate 0x8c8
+#define XK_similarequal 0x8c9
+#define XK_ifonlyif 0x8cd
+#define XK_implies 0x8ce
+#define XK_identical 0x8cf
+#define XK_radical 0x8d6
+#define XK_includedin 0x8da
+#define XK_includes 0x8db
+#define XK_intersection 0x8dc
+#define XK_union 0x8dd
+#define XK_logicaland 0x8de
+#define XK_logicalor 0x8df
+#define XK_partialderivative 0x8ef
+#define XK_function 0x8f6
+#define XK_leftarrow 0x8fb
+#define XK_uparrow 0x8fc
+#define XK_rightarrow 0x8fd
+#define XK_downarrow 0x8fe
+#endif /* XK_TECHNICAL */
+
+/*
+ * Special
+ * Byte 3 = 9
+ */
+
+#ifdef XK_SPECIAL
+#define XK_blank 0x9df
+#define XK_soliddiamond 0x9e0
+#define XK_checkerboard 0x9e1
+#define XK_ht 0x9e2
+#define XK_ff 0x9e3
+#define XK_cr 0x9e4
+#define XK_lf 0x9e5
+#define XK_nl 0x9e8
+#define XK_vt 0x9e9
+#define XK_lowrightcorner 0x9ea
+#define XK_uprightcorner 0x9eb
+#define XK_upleftcorner 0x9ec
+#define XK_lowleftcorner 0x9ed
+#define XK_crossinglines 0x9ee
+#define XK_horizlinescan1 0x9ef
+#define XK_horizlinescan3 0x9f0
+#define XK_horizlinescan5 0x9f1
+#define XK_horizlinescan7 0x9f2
+#define XK_horizlinescan9 0x9f3
+#define XK_leftt 0x9f4
+#define XK_rightt 0x9f5
+#define XK_bott 0x9f6
+#define XK_topt 0x9f7
+#define XK_vertbar 0x9f8
+#endif /* XK_SPECIAL */
+
+/*
+ * Publishing
+ * Byte 3 = a
+ */
+
+#ifdef XK_PUBLISHING
+#define XK_emspace 0xaa1
+#define XK_enspace 0xaa2
+#define XK_em3space 0xaa3
+#define XK_em4space 0xaa4
+#define XK_digitspace 0xaa5
+#define XK_punctspace 0xaa6
+#define XK_thinspace 0xaa7
+#define XK_hairspace 0xaa8
+#define XK_emdash 0xaa9
+#define XK_endash 0xaaa
+#define XK_signifblank 0xaac
+#define XK_ellipsis 0xaae
+#define XK_doubbaselinedot 0xaaf
+#define XK_onethird 0xab0
+#define XK_twothirds 0xab1
+#define XK_onefifth 0xab2
+#define XK_twofifths 0xab3
+#define XK_threefifths 0xab4
+#define XK_fourfifths 0xab5
+#define XK_onesixth 0xab6
+#define XK_fivesixths 0xab7
+#define XK_careof 0xab8
+#define XK_figdash 0xabb
+#define XK_leftanglebracket 0xabc
+#define XK_decimalpoint 0xabd
+#define XK_rightanglebracket 0xabe
+#define XK_marker 0xabf
+#define XK_oneeighth 0xac3
+#define XK_threeeighths 0xac4
+#define XK_fiveeighths 0xac5
+#define XK_seveneighths 0xac6
+#define XK_trademark 0xac9
+#define XK_signaturemark 0xaca
+#define XK_trademarkincircle 0xacb
+#define XK_leftopentriangle 0xacc
+#define XK_rightopentriangle 0xacd
+#define XK_emopencircle 0xace
+#define XK_emopenrectangle 0xacf
+#define XK_leftsinglequotemark 0xad0
+#define XK_rightsinglequotemark 0xad1
+#define XK_leftdoublequotemark 0xad2
+#define XK_rightdoublequotemark 0xad3
+#define XK_prescription 0xad4
+#define XK_minutes 0xad6
+#define XK_seconds 0xad7
+#define XK_latincross 0xad9
+#define XK_hexagram 0xada
+#define XK_filledrectbullet 0xadb
+#define XK_filledlefttribullet 0xadc
+#define XK_filledrighttribullet 0xadd
+#define XK_emfilledcircle 0xade
+#define XK_emfilledrect 0xadf
+#define XK_enopencircbullet 0xae0
+#define XK_enopensquarebullet 0xae1
+#define XK_openrectbullet 0xae2
+#define XK_opentribulletup 0xae3
+#define XK_opentribulletdown 0xae4
+#define XK_openstar 0xae5
+#define XK_enfilledcircbullet 0xae6
+#define XK_enfilledsqbullet 0xae7
+#define XK_filledtribulletup 0xae8
+#define XK_filledtribulletdown 0xae9
+#define XK_leftpointer 0xaea
+#define XK_rightpointer 0xaeb
+#define XK_club 0xaec
+#define XK_diamond 0xaed
+#define XK_heart 0xaee
+#define XK_maltesecross 0xaf0
+#define XK_dagger 0xaf1
+#define XK_doubledagger 0xaf2
+#define XK_checkmark 0xaf3
+#define XK_ballotcross 0xaf4
+#define XK_musicalsharp 0xaf5
+#define XK_musicalflat 0xaf6
+#define XK_malesymbol 0xaf7
+#define XK_femalesymbol 0xaf8
+#define XK_telephone 0xaf9
+#define XK_telephonerecorder 0xafa
+#define XK_phonographcopyright 0xafb
+#define XK_caret 0xafc
+#define XK_singlelowquotemark 0xafd
+#define XK_doublelowquotemark 0xafe
+#define XK_cursor 0xaff
+#endif /* XK_PUBLISHING */
+
+/*
+ * APL
+ * Byte 3 = b
+ */
+
+#ifdef XK_APL
+#define XK_leftcaret 0xba3
+#define XK_rightcaret 0xba6
+#define XK_downcaret 0xba8
+#define XK_upcaret 0xba9
+#define XK_overbar 0xbc0
+#define XK_downtack 0xbc2
+#define XK_upshoe 0xbc3
+#define XK_downstile 0xbc4
+#define XK_underbar 0xbc6
+#define XK_jot 0xbca
+#define XK_quad 0xbcc
+#define XK_uptack 0xbce
+#define XK_circle 0xbcf
+#define XK_upstile 0xbd3
+#define XK_downshoe 0xbd6
+#define XK_rightshoe 0xbd8
+#define XK_leftshoe 0xbda
+#define XK_lefttack 0xbdc
+#define XK_righttack 0xbfc
+#endif /* XK_APL */
+
+/*
+ * Hebrew
+ * Byte 3 = c
+ */
+
+#ifdef XK_HEBREW
+#define XK_hebrew_doublelowline 0xcdf
+#define XK_hebrew_aleph 0xce0
+#define XK_hebrew_bet 0xce1
+#define XK_hebrew_beth 0xce1 /* deprecated */
+#define XK_hebrew_gimel 0xce2
+#define XK_hebrew_gimmel 0xce2 /* deprecated */
+#define XK_hebrew_dalet 0xce3
+#define XK_hebrew_daleth 0xce3 /* deprecated */
+#define XK_hebrew_he 0xce4
+#define XK_hebrew_waw 0xce5
+#define XK_hebrew_zain 0xce6
+#define XK_hebrew_zayin 0xce6 /* deprecated */
+#define XK_hebrew_chet 0xce7
+#define XK_hebrew_het 0xce7 /* deprecated */
+#define XK_hebrew_tet 0xce8
+#define XK_hebrew_teth 0xce8 /* deprecated */
+#define XK_hebrew_yod 0xce9
+#define XK_hebrew_finalkaph 0xcea
+#define XK_hebrew_kaph 0xceb
+#define XK_hebrew_lamed 0xcec
+#define XK_hebrew_finalmem 0xced
+#define XK_hebrew_mem 0xcee
+#define XK_hebrew_finalnun 0xcef
+#define XK_hebrew_nun 0xcf0
+#define XK_hebrew_samech 0xcf1
+#define XK_hebrew_samekh 0xcf1 /* deprecated */
+#define XK_hebrew_ayin 0xcf2
+#define XK_hebrew_finalpe 0xcf3
+#define XK_hebrew_pe 0xcf4
+#define XK_hebrew_finalzade 0xcf5
+#define XK_hebrew_finalzadi 0xcf5 /* deprecated */
+#define XK_hebrew_zade 0xcf6
+#define XK_hebrew_zadi 0xcf6 /* deprecated */
+#define XK_hebrew_qoph 0xcf7
+#define XK_hebrew_kuf 0xcf7 /* deprecated */
+#define XK_hebrew_resh 0xcf8
+#define XK_hebrew_shin 0xcf9
+#define XK_hebrew_taw 0xcfa
+#define XK_hebrew_taf 0xcfa /* deprecated */
+#define XK_Hebrew_switch 0xFF7E /* Alias for mode_switch */
+#endif /* XK_HEBREW */
+
+/*
+ * Thai
+ * Byte 3 = d
+ */
+
+#ifdef XK_THAI
+#define XK_Thai_kokai 0xda1
+#define XK_Thai_khokhai 0xda2
+#define XK_Thai_khokhuat 0xda3
+#define XK_Thai_khokhwai 0xda4
+#define XK_Thai_khokhon 0xda5
+#define XK_Thai_khorakhang 0xda6
+#define XK_Thai_ngongu 0xda7
+#define XK_Thai_chochan 0xda8
+#define XK_Thai_choching 0xda9
+#define XK_Thai_chochang 0xdaa
+#define XK_Thai_soso 0xdab
+#define XK_Thai_chochoe 0xdac
+#define XK_Thai_yoying 0xdad
+#define XK_Thai_dochada 0xdae
+#define XK_Thai_topatak 0xdaf
+#define XK_Thai_thothan 0xdb0
+#define XK_Thai_thonangmontho 0xdb1
+#define XK_Thai_thophuthao 0xdb2
+#define XK_Thai_nonen 0xdb3
+#define XK_Thai_dodek 0xdb4
+#define XK_Thai_totao 0xdb5
+#define XK_Thai_thothung 0xdb6
+#define XK_Thai_thothahan 0xdb7
+#define XK_Thai_thothong 0xdb8
+#define XK_Thai_nonu 0xdb9
+#define XK_Thai_bobaimai 0xdba
+#define XK_Thai_popla 0xdbb
+#define XK_Thai_phophung 0xdbc
+#define XK_Thai_fofa 0xdbd
+#define XK_Thai_phophan 0xdbe
+#define XK_Thai_fofan 0xdbf
+#define XK_Thai_phosamphao 0xdc0
+#define XK_Thai_moma 0xdc1
+#define XK_Thai_yoyak 0xdc2
+#define XK_Thai_rorua 0xdc3
+#define XK_Thai_ru 0xdc4
+#define XK_Thai_loling 0xdc5
+#define XK_Thai_lu 0xdc6
+#define XK_Thai_wowaen 0xdc7
+#define XK_Thai_sosala 0xdc8
+#define XK_Thai_sorusi 0xdc9
+#define XK_Thai_sosua 0xdca
+#define XK_Thai_hohip 0xdcb
+#define XK_Thai_lochula 0xdcc
+#define XK_Thai_oang 0xdcd
+#define XK_Thai_honokhuk 0xdce
+#define XK_Thai_paiyannoi 0xdcf
+#define XK_Thai_saraa 0xdd0
+#define XK_Thai_maihanakat 0xdd1
+#define XK_Thai_saraaa 0xdd2
+#define XK_Thai_saraam 0xdd3
+#define XK_Thai_sarai 0xdd4
+#define XK_Thai_saraii 0xdd5
+#define XK_Thai_saraue 0xdd6
+#define XK_Thai_sarauee 0xdd7
+#define XK_Thai_sarau 0xdd8
+#define XK_Thai_sarauu 0xdd9
+#define XK_Thai_phinthu 0xdda
+#define XK_Thai_maihanakat_maitho 0xdde
+#define XK_Thai_baht 0xddf
+#define XK_Thai_sarae 0xde0
+#define XK_Thai_saraae 0xde1
+#define XK_Thai_sarao 0xde2
+#define XK_Thai_saraaimaimuan 0xde3
+#define XK_Thai_saraaimaimalai 0xde4
+#define XK_Thai_lakkhangyao 0xde5
+#define XK_Thai_maiyamok 0xde6
+#define XK_Thai_maitaikhu 0xde7
+#define XK_Thai_maiek 0xde8
+#define XK_Thai_maitho 0xde9
+#define XK_Thai_maitri 0xdea
+#define XK_Thai_maichattawa 0xdeb
+#define XK_Thai_thanthakhat 0xdec
+#define XK_Thai_nikhahit 0xded
+#define XK_Thai_leksun 0xdf0
+#define XK_Thai_leknung 0xdf1
+#define XK_Thai_leksong 0xdf2
+#define XK_Thai_leksam 0xdf3
+#define XK_Thai_leksi 0xdf4
+#define XK_Thai_lekha 0xdf5
+#define XK_Thai_lekhok 0xdf6
+#define XK_Thai_lekchet 0xdf7
+#define XK_Thai_lekpaet 0xdf8
+#define XK_Thai_lekkao 0xdf9
+#endif /* XK_THAI */
+
+/*
+ * Korean
+ * Byte 3 = e
+ */
+
+#ifdef XK_KOREAN
+
+#define XK_Hangul 0xff31 /* Hangul start/stop(toggle) */
+#define XK_Hangul_Start 0xff32 /* Hangul start */
+#define XK_Hangul_End 0xff33 /* Hangul end, English start */
+#define XK_Hangul_Hanja 0xff34 /* Start Hangul->Hanja Conversion */
+#define XK_Hangul_Jamo 0xff35 /* Hangul Jamo mode */
+#define XK_Hangul_Romaja 0xff36 /* Hangul Romaja mode */
+#define XK_Hangul_Codeinput 0xff37 /* Hangul code input mode */
+#define XK_Hangul_Jeonja 0xff38 /* Jeonja mode */
+#define XK_Hangul_Banja 0xff39 /* Banja mode */
+#define XK_Hangul_PreHanja 0xff3a /* Pre Hanja conversion */
+#define XK_Hangul_PostHanja 0xff3b /* Post Hanja conversion */
+#define XK_Hangul_SingleCandidate 0xff3c /* Single candidate */
+#define XK_Hangul_MultipleCandidate 0xff3d /* Multiple candidate */
+#define XK_Hangul_PreviousCandidate 0xff3e /* Previous candidate */
+#define XK_Hangul_Special 0xff3f /* Special symbols */
+#define XK_Hangul_switch 0xFF7E /* Alias for mode_switch */
+
+/* Hangul Consonant Characters */
+#define XK_Hangul_Kiyeog 0xea1
+#define XK_Hangul_SsangKiyeog 0xea2
+#define XK_Hangul_KiyeogSios 0xea3
+#define XK_Hangul_Nieun 0xea4
+#define XK_Hangul_NieunJieuj 0xea5
+#define XK_Hangul_NieunHieuh 0xea6
+#define XK_Hangul_Dikeud 0xea7
+#define XK_Hangul_SsangDikeud 0xea8
+#define XK_Hangul_Rieul 0xea9
+#define XK_Hangul_RieulKiyeog 0xeaa
+#define XK_Hangul_RieulMieum 0xeab
+#define XK_Hangul_RieulPieub 0xeac
+#define XK_Hangul_RieulSios 0xead
+#define XK_Hangul_RieulTieut 0xeae
+#define XK_Hangul_RieulPhieuf 0xeaf
+#define XK_Hangul_RieulHieuh 0xeb0
+#define XK_Hangul_Mieum 0xeb1
+#define XK_Hangul_Pieub 0xeb2
+#define XK_Hangul_SsangPieub 0xeb3
+#define XK_Hangul_PieubSios 0xeb4
+#define XK_Hangul_Sios 0xeb5
+#define XK_Hangul_SsangSios 0xeb6
+#define XK_Hangul_Ieung 0xeb7
+#define XK_Hangul_Jieuj 0xeb8
+#define XK_Hangul_SsangJieuj 0xeb9
+#define XK_Hangul_Cieuc 0xeba
+#define XK_Hangul_Khieuq 0xebb
+#define XK_Hangul_Tieut 0xebc
+#define XK_Hangul_Phieuf 0xebd
+#define XK_Hangul_Hieuh 0xebe
+
+/* Hangul Vowel Characters */
+#define XK_Hangul_A 0xebf
+#define XK_Hangul_AE 0xec0
+#define XK_Hangul_YA 0xec1
+#define XK_Hangul_YAE 0xec2
+#define XK_Hangul_EO 0xec3
+#define XK_Hangul_E 0xec4
+#define XK_Hangul_YEO 0xec5
+#define XK_Hangul_YE 0xec6
+#define XK_Hangul_O 0xec7
+#define XK_Hangul_WA 0xec8
+#define XK_Hangul_WAE 0xec9
+#define XK_Hangul_OE 0xeca
+#define XK_Hangul_YO 0xecb
+#define XK_Hangul_U 0xecc
+#define XK_Hangul_WEO 0xecd
+#define XK_Hangul_WE 0xece
+#define XK_Hangul_WI 0xecf
+#define XK_Hangul_YU 0xed0
+#define XK_Hangul_EU 0xed1
+#define XK_Hangul_YI 0xed2
+#define XK_Hangul_I 0xed3
+
+/* Hangul syllable-final (JongSeong) Characters */
+#define XK_Hangul_J_Kiyeog 0xed4
+#define XK_Hangul_J_SsangKiyeog 0xed5
+#define XK_Hangul_J_KiyeogSios 0xed6
+#define XK_Hangul_J_Nieun 0xed7
+#define XK_Hangul_J_NieunJieuj 0xed8
+#define XK_Hangul_J_NieunHieuh 0xed9
+#define XK_Hangul_J_Dikeud 0xeda
+#define XK_Hangul_J_Rieul 0xedb
+#define XK_Hangul_J_RieulKiyeog 0xedc
+#define XK_Hangul_J_RieulMieum 0xedd
+#define XK_Hangul_J_RieulPieub 0xede
+#define XK_Hangul_J_RieulSios 0xedf
+#define XK_Hangul_J_RieulTieut 0xee0
+#define XK_Hangul_J_RieulPhieuf 0xee1
+#define XK_Hangul_J_RieulHieuh 0xee2
+#define XK_Hangul_J_Mieum 0xee3
+#define XK_Hangul_J_Pieub 0xee4
+#define XK_Hangul_J_PieubSios 0xee5
+#define XK_Hangul_J_Sios 0xee6
+#define XK_Hangul_J_SsangSios 0xee7
+#define XK_Hangul_J_Ieung 0xee8
+#define XK_Hangul_J_Jieuj 0xee9
+#define XK_Hangul_J_Cieuc 0xeea
+#define XK_Hangul_J_Khieuq 0xeeb
+#define XK_Hangul_J_Tieut 0xeec
+#define XK_Hangul_J_Phieuf 0xeed
+#define XK_Hangul_J_Hieuh 0xeee
+
+/* Ancient Hangul Consonant Characters */
+#define XK_Hangul_RieulYeorinHieuh 0xeef
+#define XK_Hangul_SunkyeongeumMieum 0xef0
+#define XK_Hangul_SunkyeongeumPieub 0xef1
+#define XK_Hangul_PanSios 0xef2
+#define XK_Hangul_KkogjiDalrinIeung 0xef3
+#define XK_Hangul_SunkyeongeumPhieuf 0xef4
+#define XK_Hangul_YeorinHieuh 0xef5
+
+/* Ancient Hangul Vowel Characters */
+#define XK_Hangul_AraeA 0xef6
+#define XK_Hangul_AraeAE 0xef7
+
+/* Ancient Hangul syllable-final (JongSeong) Characters */
+#define XK_Hangul_J_PanSios 0xef8
+#define XK_Hangul_J_KkogjiDalrinIeung 0xef9
+#define XK_Hangul_J_YeorinHieuh 0xefa
+
+/* Korean currency symbol */
+#define XK_Korean_Won 0xeff
+
+#endif /* XK_KOREAN */
+
+#ifdef XK_CURRENCY
+#define XK_EcuSign 0x20a0
+#define XK_ColonSign 0x20a1
+#define XK_CruzeiroSign 0x20a2
+#define XK_FFrancSign 0x20a3
+#define XK_LiraSign 0x20a4
+#define XK_MillSign 0x20a5
+#define XK_NairaSign 0x20a6
+#define XK_PesetaSign 0x20a7
+#define XK_RupeeSign 0x20a8
+#define XK_WonSign 0x20a9
+#define XK_NewSheqelSign 0x20aa
+#define XK_DongSign 0x20ab
+#define XK_EuroSign 0x20ac
+#endif
diff --git a/rfb/msgTypes.h b/rfb/msgTypes.h
new file mode 100644
index 00000000..9bcf4d52
--- /dev/null
+++ b/rfb/msgTypes.h
@@ -0,0 +1,39 @@
+/* Copyright (C) 2002-2003 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_MSGTYPES_H__
+#define __RFB_MSGTYPES_H__
+
+namespace rfb {
+ // server to client
+
+ const int msgTypeFramebufferUpdate = 0;
+ const int msgTypeSetColourMapEntries = 1;
+ const int msgTypeBell = 2;
+ const int msgTypeServerCutText = 3;
+
+ // client to server
+
+ const int msgTypeSetPixelFormat = 0;
+ const int msgTypeFixColourMapEntries = 1;
+ const int msgTypeSetEncodings = 2;
+ const int msgTypeFramebufferUpdateRequest = 3;
+ const int msgTypeKeyEvent = 4;
+ const int msgTypePointerEvent = 5;
+ const int msgTypeClientCutText = 6;
+}
+#endif
diff --git a/rfb/msvcwarning.h b/rfb/msvcwarning.h
new file mode 100644
index 00000000..4127ce9d
--- /dev/null
+++ b/rfb/msvcwarning.h
@@ -0,0 +1,20 @@
+/* Copyright (C) 2002-2003 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.
+ */
+#pragma warning( disable : 4244 ) // loss of data e.g. int to char
+#pragma warning( disable : 4800 ) // forcing bool 'true' or 'false'
+#pragma warning( disable : 4786 ) // debug identifier truncated
diff --git a/rfb/rfb.dsp b/rfb/rfb.dsp
new file mode 100644
index 00000000..2521bfdc
--- /dev/null
+++ b/rfb/rfb.dsp
@@ -0,0 +1,625 @@
+# Microsoft Developer Studio Project File - Name="rfb" - 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 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.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.mak" CFG="rfb - Win32 Debug Unicode"
+!MESSAGE
+!MESSAGE Possible choices for configuration are:
+!MESSAGE
+!MESSAGE "rfb - Win32 Release" (based on "Win32 (x86) Static Library")
+!MESSAGE "rfb - Win32 Debug" (based on "Win32 (x86) Static Library")
+!MESSAGE "rfb - 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 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"
+# 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 /O1 /I ".." /FI"msvcwarning.h" /D "NDEBUG" /D "_LIB" /D "WIN32" /D "_MBCS" /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 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"
+# 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 ".." /FI"msvcwarning.h" /D "_DEBUG" /D "_LIB" /D "WIN32" /D "_MBCS" /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 Debug Unicode"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 1
+# PROP BASE Output_Dir "rfb___Win32_Debug_Unicode"
+# PROP BASE Intermediate_Dir "rfb___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"
+# 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 ".." /FI"msvcwarning.h" /D "_LIB" /D "_DEBUG" /D "WIN32" /D "_UNICODE" /D "UNICODE" /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 Release"
+# Name "rfb - Win32 Debug"
+# Name "rfb - Win32 Debug Unicode"
+# Begin Group "Source Files"
+
+# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat"
+# Begin Source File
+
+SOURCE=.\Blacklist.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\CConnection.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\CMsgHandler.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\CMsgReader.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\CMsgReaderV3.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\CMsgWriter.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\CMsgWriterV3.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\ComparingUpdateTracker.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\Configuration.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\ConnParams.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\CSecurityVncAuth.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\Cursor.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\d3des.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\Decoder.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\Encoder.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\encodings.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\HextileDecoder.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\HextileEncoder.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\HTTPServer.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\Logger.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\Logger_file.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\Logger_stdio.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\LogWriter.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\PixelBuffer.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\PixelFormat.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\RawDecoder.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\RawEncoder.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\Region.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\RREDecoder.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\RREEncoder.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\SConnection.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\secTypes.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\ServerCore.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\SMsgHandler.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\SMsgReader.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\SMsgReaderV3.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\SMsgWriter.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\SMsgWriterV3.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\SSecurityFactoryStandard.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\SSecurityVncAuth.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\win32\Threading_win32.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\TransImageGetter.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\UpdateTracker.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\util.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\vncAuth.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\VNCSConnectionST.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\VNCServerST.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\ZRLEDecoder.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\ZRLEEncoder.cxx
+# End Source File
+# End Group
+# Begin Group "Header Files"
+
+# PROP Default_Filter "h;hpp;hxx;hm;inl"
+# Begin Source File
+
+SOURCE=.\Blacklist.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\CConnection.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\CMsgHandler.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\CMsgReader.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\CMsgReaderV3.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\CMsgWriter.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\CMsgWriterV3.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\ColourCube.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\ColourMap.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\ComparingUpdateTracker.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\Configuration.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\ConnParams.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\CSecurity.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\CSecurityNone.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\CSecurityVncAuth.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\Cursor.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\d3des.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\Decoder.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\Encoder.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\encodings.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\Exception.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\hextileConstants.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\hextileDecode.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\HextileDecoder.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\hextileEncode.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\HextileEncoder.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\Hostname.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\HTTPServer.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\ImageGetter.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\keysymdef.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\Logger.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\Logger_file.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\Logger_stdio.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\LogWriter.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\msgTypes.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\msvcwarning.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\Pixel.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\PixelBuffer.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\PixelFormat.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\RawDecoder.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\RawEncoder.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\Rect.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\Region.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\rreDecode.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\RREDecoder.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\rreEncode.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\RREEncoder.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\SConnection.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\SDesktop.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\secTypes.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\ServerCore.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\SMsgHandler.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\SMsgReader.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\SMsgReaderV3.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\SMsgWriter.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\SMsgWriterV3.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\SSecurity.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\SSecurityFactoryStandard.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\SSecurityNone.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\SSecurityVncAuth.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\Threading.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\win32\Threading_win32.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\TransImageGetter.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\transInitTempl.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\transTempl.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\TrueColourMap.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\UpdateTracker.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\UserPasswdGetter.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\util.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\win32\util_win32.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\vncAuth.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\VNCSConnectionST.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\VNCServer.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\VNCServerST.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\zrleDecode.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\ZRLEDecoder.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\zrleEncode.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\ZRLEEncoder.h
+# End Source File
+# End Group
+# End Target
+# End Project
diff --git a/rfb/rreDecode.h b/rfb/rreDecode.h
new file mode 100644
index 00000000..9f69ceeb
--- /dev/null
+++ b/rfb/rreDecode.h
@@ -0,0 +1,64 @@
+/* Copyright (C) 2002-2003 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.
+ */
+//
+// RRE decoding function.
+//
+// This file is #included after having set the following macros:
+// BPP - 8, 16 or 32
+// EXTRA_ARGS - optional extra arguments
+// FILL_RECT - fill a rectangle with a single colour
+
+#include <rdr/InStream.h>
+
+namespace rfb {
+
+// CONCAT2E concatenates its arguments, expanding them if they are macros
+
+#ifndef CONCAT2E
+#define CONCAT2(a,b) a##b
+#define CONCAT2E(a,b) CONCAT2(a,b)
+#endif
+
+#define PIXEL_T rdr::CONCAT2E(U,BPP)
+#define READ_PIXEL CONCAT2E(readOpaque,BPP)
+#define RRE_DECODE CONCAT2E(rreDecode,BPP)
+
+void RRE_DECODE (const Rect& r, rdr::InStream* is
+#ifdef EXTRA_ARGS
+ , EXTRA_ARGS
+#endif
+ )
+{
+ int nSubrects = is->readU32();
+ PIXEL_T bg = is->READ_PIXEL();
+ FILL_RECT(r, bg);
+
+ for (int i = 0; i < nSubrects; i++) {
+ PIXEL_T pix = is->READ_PIXEL();
+ int x = is->readU16();
+ int y = is->readU16();
+ int w = is->readU16();
+ int h = is->readU16();
+ FILL_RECT(Rect(r.tl.x+x, r.tl.y+y, r.tl.x+x+w, r.tl.y+y+h), pix);
+ }
+}
+
+#undef PIXEL_T
+#undef READ_PIXEL
+#undef RRE_DECODE
+}
diff --git a/rfb/rreEncode.h b/rfb/rreEncode.h
new file mode 100644
index 00000000..4877a12d
--- /dev/null
+++ b/rfb/rreEncode.h
@@ -0,0 +1,164 @@
+/* Copyright (C) 2002-2003 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.
+ */
+//
+// RRE encoding function.
+//
+// This file is #included after having set the following macros:
+// BPP - 8, 16 or 32
+//
+// The data argument to RRE_ENCODE contains the pixel data, and it writes the
+// encoded version to the given OutStream. If the encoded version exceeds w*h
+// it aborts and returns -1, otherwise it returns the number of subrectangles.
+//
+
+#include <rdr/OutStream.h>
+
+namespace rfb {
+
+// CONCAT2E concatenates its arguments, expanding them if they are macros
+
+#ifndef CONCAT2E
+#define CONCAT2(a,b) a##b
+#define CONCAT2E(a,b) CONCAT2(a,b)
+#endif
+
+#define PIXEL_T rdr::CONCAT2E(U,BPP)
+#define WRITE_PIXEL CONCAT2E(writeOpaque,BPP)
+#define RRE_ENCODE CONCAT2E(rreEncode,BPP)
+
+int RRE_ENCODE (PIXEL_T* data, int w, int h, rdr::OutStream* os, PIXEL_T bg);
+
+int RRE_ENCODE (void* data, int w, int h, rdr::OutStream* os)
+{
+ // Find the background colour - count occurrences of up to 4 different pixel
+ // values, and choose the one which occurs most often.
+
+ const int nCols = 4;
+ PIXEL_T pix[nCols];
+ int count[nCols] = { 0, };
+ PIXEL_T* ptr = (PIXEL_T*)data;
+ PIXEL_T* end = ptr + w*h;
+
+ while (ptr < end) {
+ int i;
+ for (i = 0; i < nCols; i++) {
+ if (count[i] == 0)
+ pix[i] = *ptr;
+
+ if (pix[i] == *ptr) {
+ count[i]++;
+ break;
+ }
+ }
+
+ if (i == nCols) break;
+ ptr++;
+ }
+
+ int bg = 0;
+ for (int i = 1; i < nCols; i++)
+ if (count[i] > count[bg]) bg = i;
+
+ // Now call the function to do the encoding.
+
+ return RRE_ENCODE ((PIXEL_T*)data, w, h, os, pix[bg]);
+}
+
+int RRE_ENCODE (PIXEL_T* data, int w, int h, rdr::OutStream* os, PIXEL_T bg)
+{
+ int oldLen = os->length();
+ os->WRITE_PIXEL(bg);
+
+ int nSubrects = 0;
+
+ for (int y = 0; y < h; y++)
+ {
+ int x = 0;
+ while (x < w) {
+ if (*data == bg) {
+ x++;
+ data++;
+ continue;
+ }
+
+ // Find horizontal subrect first
+ PIXEL_T* ptr = data+1;
+ PIXEL_T* eol = data+w-x;
+ while (ptr < eol && *ptr == *data) ptr++;
+ int sw = ptr - data;
+
+ ptr = data + w;
+ int sh = 1;
+ while (sh < h-y) {
+ eol = ptr + sw;
+ while (ptr < eol)
+ if (*ptr++ != *data) goto endOfHorizSubrect;
+ ptr += w - sw;
+ sh++;
+ }
+ endOfHorizSubrect:
+
+ // Find vertical subrect
+ int vh;
+ for (vh = sh; vh < h-y; vh++)
+ if (data[vh*w] != *data) break;
+
+ if (vh != sh) {
+ ptr = data+1;
+ int vw;
+ for (vw = 1; vw < sw; vw++) {
+ for (int i = 0; i < vh; i++)
+ if (ptr[i*w] != *data) goto endOfVertSubrect;
+ ptr++;
+ }
+ endOfVertSubrect:
+
+ // If vertical subrect bigger than horizontal then use that.
+ if (sw*sh < vw*vh) {
+ sw = vw;
+ sh = vh;
+ }
+ }
+
+ nSubrects++;
+ os->WRITE_PIXEL(*data);
+ os->writeU16(x);
+ os->writeU16(y);
+ os->writeU16(sw);
+ os->writeU16(sh);
+ if (os->length() > oldLen + w*h) return -1;
+
+ ptr = data+w;
+ PIXEL_T* eor = data+w*sh;
+ while (ptr < eor) {
+ eol = ptr + sw;
+ while (ptr < eol) *ptr++ = bg;
+ ptr += w - sw;
+ }
+ x += sw;
+ data += sw;
+ }
+ }
+
+ return nSubrects;
+}
+
+#undef PIXEL_T
+#undef WRITE_PIXEL
+#undef RRE_ENCODE
+}
diff --git a/rfb/secTypes.cxx b/rfb/secTypes.cxx
new file mode 100644
index 00000000..7c6c25ca
--- /dev/null
+++ b/rfb/secTypes.cxx
@@ -0,0 +1,64 @@
+/* Copyright (C) 2002-2004 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 <string.h>
+#ifdef _WIN32
+#define strcasecmp _stricmp
+#endif
+#include <rfb/secTypes.h>
+#include <rfb/util.h>
+
+int rfb::secTypeNum(const char* name)
+{
+ if (strcasecmp(name, "None") == 0) return secTypeNone;
+ if (strcasecmp(name, "VncAuth") == 0) return secTypeVncAuth;
+ if (strcasecmp(name, "RA2") == 0) return secTypeRA2;
+ if (strcasecmp(name, "RA2ne") == 0) return secTypeRA2ne;
+ return secTypeInvalid;
+}
+
+const char* rfb::secTypeName(int num)
+{
+ switch (num) {
+ case secTypeNone: return "None";
+ case secTypeVncAuth: return "VncAuth";
+ case secTypeRA2: return "RA2";
+ case secTypeRA2ne: return "RA2ne";
+ default: return "[unknown secType]";
+ }
+}
+
+bool rfb::secTypeEncrypts(int num)
+{
+ switch (num) {
+ case secTypeRA2: return true;
+ default: return false;
+ }
+}
+
+std::list<int> rfb::parseSecTypes(const char* types_)
+{
+ std::list<int> result;
+ CharArray types(strDup(types_)), type;
+ while (types.buf) {
+ strSplit(types.buf, ',', &type.buf, &types.buf);
+ int typeNum = secTypeNum(type.buf);
+ if (typeNum != secTypeInvalid)
+ result.push_back(typeNum);
+ }
+ return result;
+}
diff --git a/rfb/secTypes.h b/rfb/secTypes.h
new file mode 100644
index 00000000..f0b326e9
--- /dev/null
+++ b/rfb/secTypes.h
@@ -0,0 +1,51 @@
+/* Copyright (C) 2002-2004 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.
+ */
+//
+// secTypes.h - constants for the various security types.
+//
+
+#ifndef __RFB_SECTYPES_H__
+#define __RFB_SECTYPES_H__
+
+#include <list>
+
+namespace rfb {
+ const int secTypeInvalid = 0;
+ const int secTypeNone = 1;
+ const int secTypeVncAuth = 2;
+
+ const int secTypeRA2 = 5;
+ const int secTypeRA2ne = 6;
+
+ const int secTypeTight = 16;
+ const int secTypeUltra = 17;
+ const int secTypeTLS = 18;
+
+ // result types
+
+ const int secResultOK = 0;
+ const int secResultFailed = 1;
+ const int secResultTooMany = 2; // deprecated
+
+ const char* secTypeName(int num);
+ int secTypeNum(const char* name);
+ bool secTypeEncrypts(int num);
+ std::list<int> parseSecTypes(const char* types);
+}
+
+#endif
diff --git a/rfb/transInitTempl.h b/rfb/transInitTempl.h
new file mode 100644
index 00000000..2658f9f0
--- /dev/null
+++ b/rfb/transInitTempl.h
@@ -0,0 +1,254 @@
+/* Copyright (C) 2002-2003 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.
+ */
+//
+// transInitTempl.h - templates for functions to initialise lookup tables for
+// the translation functions.
+//
+// This file is #included after having set the following macros:
+// BPPOUT - 8, 16 or 32
+
+#if !defined(BPPOUT)
+#error "transInitTempl.h: BPPOUT not defined"
+#endif
+
+namespace rfb {
+
+// CONCAT2E concatenates its arguments, expanding them if they are macros
+
+#ifndef CONCAT2E
+#define CONCAT2(a,b) a##b
+#define CONCAT2E(a,b) CONCAT2(a,b)
+#endif
+
+#ifndef SWAP16
+#define SWAP16(n) ((((n) & 0xff) << 8) | (((n) >> 8) & 0xff))
+#endif
+
+#ifndef SWAP32
+#define SWAP32(n) (((n) >> 24) | (((n) & 0x00ff0000) >> 8) | \
+ (((n) & 0x0000ff00) << 8) | ((n) << 24))
+#endif
+
+#define OUTPIXEL rdr::CONCAT2E(U,BPPOUT)
+#define SWAPOUT CONCAT2E(SWAP,BPPOUT)
+#define initSimpleCMtoTCOUT CONCAT2E(initSimpleCMtoTC,BPPOUT)
+#define initSimpleTCtoTCOUT CONCAT2E(initSimpleTCtoTC,BPPOUT)
+#define initSimpleCMtoCubeOUT CONCAT2E(initSimpleCMtoCube,BPPOUT)
+#define initSimpleTCtoCubeOUT CONCAT2E(initSimpleTCtoCube,BPPOUT)
+#define initRGBTCtoTCOUT CONCAT2E(initRGBTCtoTC,BPPOUT)
+#define initRGBTCtoCubeOUT CONCAT2E(initRGBTCtoCube,BPPOUT)
+#define initOneRGBTableOUT CONCAT2E(initOneRGBTable,BPPOUT)
+#define initOneRGBCubeTableOUT CONCAT2E(initOneRGBCubeTable,BPPOUT)
+
+#ifndef TRANS_INIT_TEMPL_ENDIAN_TEST
+#define TRANS_INIT_TEMPL_ENDIAN_TEST
+ static rdr::U32 endianTest = 1;
+ static bool nativeBigEndian = *(rdr::U8*)(&endianTest) != 1;
+#endif
+
+void initSimpleCMtoTCOUT (rdr::U8** tablep, const PixelFormat& inPF,
+ ColourMap* cm, const PixelFormat& outPF)
+{
+ if (inPF.bpp != 8 && inPF.bigEndian != nativeBigEndian)
+ throw Exception("Internal error: inPF is not native endian");
+
+ int size = 1 << inPF.bpp;
+
+ delete [] *tablep;
+ *tablep = new rdr::U8[size * sizeof(OUTPIXEL)];
+ OUTPIXEL* table = (OUTPIXEL*)*tablep;
+
+ for (int i = 0; i < size; i++) {
+ int r,g,b;
+ cm->lookup(i,&r,&g,&b);
+
+ table[i] = ((((r * outPF.redMax + 32767) / 65535) << outPF.redShift) |
+ (((g * outPF.greenMax + 32767) / 65535) << outPF.greenShift) |
+ (((b * outPF.blueMax + 32767) / 65535) << outPF.blueShift));
+#if (BPPOUT != 8)
+ if (outPF.bigEndian != nativeBigEndian)
+ table[i] = SWAPOUT (table[i]);
+#endif
+ }
+}
+
+void initSimpleTCtoTCOUT (rdr::U8** tablep, const PixelFormat& inPF,
+ const PixelFormat& outPF)
+{
+ if (inPF.bpp != 8 && inPF.bigEndian != nativeBigEndian)
+ throw Exception("Internal error: inPF is not native endian");
+
+ int size = 1 << inPF.bpp;
+
+ delete [] *tablep;
+ *tablep = new rdr::U8[size * sizeof(OUTPIXEL)];
+ OUTPIXEL* table = (OUTPIXEL*)*tablep;
+
+ for (int i = 0; i < size; i++) {
+ int r = (i >> inPF.redShift) & inPF.redMax;
+ int g = (i >> inPF.greenShift) & inPF.greenMax;
+ int b = (i >> inPF.blueShift) & inPF.blueMax;
+
+ r = (r * outPF.redMax + inPF.redMax/2) / inPF.redMax;
+ g = (g * outPF.greenMax + inPF.greenMax/2) / inPF.greenMax;
+ b = (b * outPF.blueMax + inPF.blueMax/2) / inPF.blueMax;
+
+ table[i] = ((r << outPF.redShift) |
+ (g << outPF.greenShift) |
+ (b << outPF.blueShift));
+#if (BPPOUT != 8)
+ if (outPF.bigEndian != nativeBigEndian)
+ table[i] = SWAPOUT (table[i]);
+#endif
+ }
+}
+
+void initSimpleCMtoCubeOUT (rdr::U8** tablep, const PixelFormat& inPF,
+ ColourMap* cm, ColourCube* cube)
+{
+ if (inPF.bpp != 8 && inPF.bigEndian != nativeBigEndian)
+ throw Exception("Internal error: inPF is not native endian");
+
+ int size = 1 << inPF.bpp;
+
+ delete [] *tablep;
+ *tablep = new rdr::U8[size * sizeof(OUTPIXEL)];
+ OUTPIXEL* table = (OUTPIXEL*)*tablep;
+
+ for (int i = 0; i < size; i++) {
+ int r,g,b;
+ cm->lookup(i,&r,&g,&b);
+ r = (r * (cube->nRed-1) + 32767) / 65535;
+ g = (g * (cube->nGreen-1) + 32767) / 65535;
+ b = (b * (cube->nBlue-1) + 32767) / 65535;
+ table[i] = cube->lookup(r, g, b);
+ }
+}
+
+void initSimpleTCtoCubeOUT (rdr::U8** tablep, const PixelFormat& inPF,
+ ColourCube* cube)
+{
+ if (inPF.bpp != 8 && inPF.bigEndian != nativeBigEndian)
+ throw Exception("Internal error: inPF is not native endian");
+
+ int size = 1 << inPF.bpp;
+
+ delete [] *tablep;
+ *tablep = new rdr::U8[size * sizeof(OUTPIXEL)];
+ OUTPIXEL* table = (OUTPIXEL*)*tablep;
+
+ for (int i = 0; i < size; i++) {
+ int r = (i >> inPF.redShift) & inPF.redMax;
+ int g = (i >> inPF.greenShift) & inPF.greenMax;
+ int b = (i >> inPF.blueShift) & inPF.blueMax;
+
+ r = (r * (cube->nRed-1) + inPF.redMax/2) / inPF.redMax;
+ g = (g * (cube->nGreen-1) + inPF.greenMax/2) / inPF.greenMax;
+ b = (b * (cube->nBlue-1) + inPF.blueMax/2) / inPF.blueMax;
+
+ table[i] = cube->lookup(r, g, b);
+ }
+}
+
+void initOneRGBTableOUT (OUTPIXEL* table, int inMax, int outMax,
+ int outShift, bool swap)
+{
+ int size = inMax + 1;
+
+ for (int i = 0; i < size; i++) {
+ table[i] = ((i * outMax + inMax / 2) / inMax) << outShift;
+#if (BPPOUT != 8)
+ if (swap)
+ table[i] = SWAPOUT (table[i]);
+#endif
+ }
+}
+
+void initRGBTCtoTCOUT (rdr::U8** tablep, const PixelFormat& inPF,
+ const PixelFormat& outPF)
+{
+ if (inPF.bpp != 8 && inPF.bigEndian != nativeBigEndian)
+ throw Exception("Internal error: inPF is not native endian");
+
+ int size = inPF.redMax + inPF.greenMax + inPF.blueMax + 3;
+
+ delete [] *tablep;
+ *tablep = new rdr::U8[size * sizeof(OUTPIXEL)];
+
+ OUTPIXEL* redTable = (OUTPIXEL*)*tablep;
+ OUTPIXEL* greenTable = redTable + inPF.redMax + 1;
+ OUTPIXEL* blueTable = greenTable + inPF.greenMax + 1;
+
+ bool swap = (outPF.bigEndian != nativeBigEndian);
+
+ initOneRGBTableOUT (redTable, inPF.redMax, outPF.redMax,
+ outPF.redShift, swap);
+ initOneRGBTableOUT (greenTable, inPF.greenMax, outPF.greenMax,
+ outPF.greenShift, swap);
+ initOneRGBTableOUT (blueTable, inPF.blueMax, outPF.blueMax,
+ outPF.blueShift, swap);
+}
+
+
+void initOneRGBCubeTableOUT (OUTPIXEL* table, int inMax, int outMax,
+ int outMult)
+{
+ int size = inMax + 1;
+
+ for (int i = 0; i < size; i++) {
+ table[i] = ((i * outMax + inMax / 2) / inMax) * outMult;
+ }
+}
+
+void initRGBTCtoCubeOUT (rdr::U8** tablep, const PixelFormat& inPF,
+ ColourCube* cube)
+{
+ if (inPF.bpp != 8 && inPF.bigEndian != nativeBigEndian)
+ throw Exception("Internal error: inPF is not native endian");
+
+ int size = inPF.redMax + inPF.greenMax + inPF.blueMax + 3 + cube->size();
+
+ delete [] *tablep;
+ *tablep = new rdr::U8[size * sizeof(OUTPIXEL)];
+
+ OUTPIXEL* redTable = (OUTPIXEL*)*tablep;
+ OUTPIXEL* greenTable = redTable + inPF.redMax + 1;
+ OUTPIXEL* blueTable = greenTable + inPF.greenMax + 1;
+ OUTPIXEL* cubeTable = blueTable + inPF.blueMax + 1;
+
+ initOneRGBCubeTableOUT (redTable, inPF.redMax, cube->nRed-1,
+ cube->redMult());
+ initOneRGBCubeTableOUT (greenTable, inPF.greenMax, cube->nGreen-1,
+ cube->greenMult());
+ initOneRGBCubeTableOUT (blueTable, inPF.blueMax, cube->nBlue-1,
+ cube->blueMult());
+ for (int i = 0; i < cube->size(); i++) {
+ cubeTable[i] = cube->table[i];
+ }
+}
+
+#undef OUTPIXEL
+#undef initSimpleCMtoTCOUT
+#undef initSimpleTCtoTCOUT
+#undef initSimpleCMtoCubeOUT
+#undef initSimpleTCtoCubeOUT
+#undef initRGBTCtoTCOUT
+#undef initRGBTCtoCubeOUT
+#undef initOneRGBTableOUT
+#undef initOneRGBCubeTableOUT
+}
diff --git a/rfb/transTempl.h b/rfb/transTempl.h
new file mode 100644
index 00000000..907b8396
--- /dev/null
+++ b/rfb/transTempl.h
@@ -0,0 +1,151 @@
+/* Copyright (C) 2002-2003 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.
+ */
+//
+// transTempl.h - templates for translation functions.
+//
+// This file is #included after having set the following macros:
+// BPPIN - 8, 16 or 32
+// BPPOUT - 8, 16 or 32
+
+#if !defined(BPPIN) || !defined(BPPOUT)
+#error "transTempl.h: BPPIN or BPPOUT not defined"
+#endif
+
+// CONCAT2E concatenates its arguments, expanding them if they are macros
+
+#ifndef CONCAT2E
+#define CONCAT2(a,b) a##b
+#define CONCAT2E(a,b) CONCAT2(a,b)
+#endif
+
+#ifndef CONCAT4E
+#define CONCAT4(a,b,c,d) a##b##c##d
+#define CONCAT4E(a,b,c,d) CONCAT4(a,b,c,d)
+#endif
+
+#define INPIXEL rdr::CONCAT2E(U,BPPIN)
+#define OUTPIXEL rdr::CONCAT2E(U,BPPOUT)
+#define transSimpleINtoOUT CONCAT4E(transSimple,BPPIN,to,BPPOUT)
+#define transRGBINtoOUT CONCAT4E(transRGB,BPPIN,to,BPPOUT)
+#define transRGBCubeINtoOUT CONCAT4E(transRGBCube,BPPIN,to,BPPOUT)
+
+#if (BPPIN <= 16)
+
+// transSimpleINtoOUT uses a single table. This can be used for any incoming
+// and outgoing pixel formats, as long as the incoming pixel format is not too
+// large (for 16bpp, the table needs 64K entries).
+
+void transSimpleINtoOUT (void* table_,
+ const PixelFormat& inPF, void* inPtr, int inStride,
+ const PixelFormat& outPF, void* outPtr, int outStride,
+ int width, int height)
+{
+ OUTPIXEL* table = (OUTPIXEL*)table_;
+ INPIXEL* ip = (INPIXEL*)inPtr;
+ OUTPIXEL* op = (OUTPIXEL*)outPtr;
+ int inExtra = inStride - width;
+ int outExtra = outStride - width;
+
+ while (height > 0) {
+ OUTPIXEL* opEndOfRow = op + width;
+ while (op < opEndOfRow)
+ *op++ = table[*ip++];
+ ip += inExtra;
+ op += outExtra;
+ height--;
+ }
+}
+
+#endif
+
+#if (BPPIN >= 16)
+
+// transRGBINtoOUT uses three tables, one each for red, green and blue
+// components and adds the values to produce the result. This can be used
+// where a single table would be too large (e.g. 32bpp). It only works for a
+// trueColour incoming pixel format. Usually the outgoing pixel format is
+// trueColour, but we add rather than ORing the three values so that it is also
+// possible to generate an index into a colour cube. I believe that in most
+// cases adding is just as fast as ORing - if not then we should split this
+// into two different functions for efficiency.
+
+void transRGBINtoOUT (void* table,
+ const PixelFormat& inPF, void* inPtr, int inStride,
+ const PixelFormat& outPF, void* outPtr, int outStride,
+ int width, int height)
+{
+ OUTPIXEL* redTable = (OUTPIXEL*)table;
+ OUTPIXEL* greenTable = redTable + inPF.redMax + 1;
+ OUTPIXEL* blueTable = greenTable + inPF.greenMax + 1;
+ INPIXEL* ip = (INPIXEL*)inPtr;
+ OUTPIXEL* op = (OUTPIXEL*)outPtr;
+ int inExtra = inStride - width;
+ int outExtra = outStride - width;
+
+ while (height > 0) {
+ OUTPIXEL* opEndOfRow = op + width;
+ while (op < opEndOfRow) {
+ *op++ = (redTable [(*ip >> inPF.redShift) & inPF.redMax] +
+ greenTable[(*ip >> inPF.greenShift) & inPF.greenMax] +
+ blueTable [(*ip >> inPF.blueShift) & inPF.blueMax]);
+ ip++;
+ }
+ ip += inExtra;
+ op += outExtra;
+ height--;
+ }
+}
+
+// transRGBCubeINtoOUT is similar to transRGBINtoOUT but also looks up the
+// colour cube index in a fourth table to yield a pixel value.
+
+void transRGBCubeINtoOUT (void* table,
+ const PixelFormat& inPF, void* inPtr, int inStride,
+ const PixelFormat& outPF, void* outPtr,
+ int outStride, int width, int height)
+{
+ OUTPIXEL* redTable = (OUTPIXEL*)table;
+ OUTPIXEL* greenTable = redTable + inPF.redMax + 1;
+ OUTPIXEL* blueTable = greenTable + inPF.greenMax + 1;
+ OUTPIXEL* cubeTable = blueTable + inPF.blueMax + 1;
+ INPIXEL* ip = (INPIXEL*)inPtr;
+ OUTPIXEL* op = (OUTPIXEL*)outPtr;
+ int inExtra = inStride - width;
+ int outExtra = outStride - width;
+
+ while (height > 0) {
+ OUTPIXEL* opEndOfRow = op + width;
+ while (op < opEndOfRow) {
+ *op++ = cubeTable[(redTable [(*ip >> inPF.redShift) & inPF.redMax] +
+ greenTable[(*ip >> inPF.greenShift) & inPF.greenMax] +
+ blueTable [(*ip >> inPF.blueShift) & inPF.blueMax])];
+ ip++;
+ }
+ ip += inExtra;
+ op += outExtra;
+ height--;
+ }
+}
+
+#endif
+
+#undef INPIXEL
+#undef OUTPIXEL
+#undef transSimpleINtoOUT
+#undef transRGBINtoOUT
+#undef transRGBCubeINtoOUT
diff --git a/rfb/util.cxx b/rfb/util.cxx
new file mode 100644
index 00000000..2dbc2df4
--- /dev/null
+++ b/rfb/util.cxx
@@ -0,0 +1,78 @@
+/* Copyright (C) 2002-2003 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 <rfb/util.h>
+
+namespace rfb {
+
+ char* strDup(const char* s) {
+ if (!s) return 0;
+ int l = strlen(s);
+ char* r = new char[l+1];
+ memcpy(r, s, l+1);
+ return r;
+ };
+
+ void strFree(char* s) {
+ delete [] s;
+ }
+
+
+ bool strSplit(const char* src, const char limiter, char** out1, char** out2, bool fromEnd) {
+ CharArray out1old, out2old;
+ if (out1) out1old.buf = *out1;
+ if (out2) out2old.buf = *out2;
+ int len = strlen(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 char[i+1];
+ if (i) memcpy(*out1, src, i);
+ (*out1)[i] = 0;
+ }
+ if (out2) {
+ *out2 = new char[len-i];
+ if (len-i-1) memcpy(*out2, &src[i+1], len-i-1);
+ (*out2)[len-i-1] = 0;
+ }
+ return true;
+ }
+ i+=increment;
+ }
+ if (out1) *out1 = strDup(src);
+ if (out2) *out2 = 0;
+ return false;
+ }
+
+ bool strContains(const char* src, char c) {
+ int l=strlen(src);
+ for (int i=0; i<l; i++)
+ if (src[i] == c) return true;
+ return false;
+ }
+
+ void strCopy(char* dest, const char* src, int destlen) {
+ if (src)
+ strncpy(dest, src, destlen-1);
+ dest[src ? destlen-1 : 0] = 0;
+ }
+
+};
diff --git a/rfb/util.h b/rfb/util.h
new file mode 100644
index 00000000..d792c8d1
--- /dev/null
+++ b/rfb/util.h
@@ -0,0 +1,90 @@
+/* Copyright (C) 2002-2004 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.
+ */
+
+//
+// util.h - miscellaneous useful bits
+//
+
+#ifndef __RFB_UTIL_H__
+#define __RFB_UTIL_H__
+
+#include <string.h>
+
+namespace rfb {
+
+ // -=- Class to handle cleanup of arrays of characters
+ class CharArray {
+ public:
+ CharArray() : buf(0) {}
+ CharArray(char* str) : buf(str) {} // note: assumes ownership
+ CharArray(int len) {
+ buf = new char[len];
+ }
+ ~CharArray() {
+ delete [] buf;
+ }
+ // Get the buffer pointer & clear it (i.e. caller takes ownership)
+ char* takeBuf() {char* tmp = buf; buf = 0; return tmp;}
+ void replaceBuf(char* b) {delete [] buf; buf = b;}
+ char* buf;
+ private:
+ CharArray(const CharArray&);
+ CharArray& operator=(const CharArray&);
+ };
+
+ char* strDup(const char* s);
+ void strFree(char* s);
+
+ // Returns true if split successful. Returns false otherwise.
+ // ALWAYS *copies* first part of string to out1 buffer.
+ // If limiter not found, leaves out2 alone (null) and just copies to out1.
+ // If out1 or out2 non-zero, calls strFree and zeroes them.
+ // If fromEnd is true, splits at end of string rather than beginning.
+ // Either out1 or out2 may be null, in which case the split will not return
+ // that part of the string. Obviously, setting both to 0 is not useful...
+ bool strSplit(const char* src, const char limiter, char** out1, char** out2, bool fromEnd=false);
+
+ // Returns true if src contains c
+ bool strContains(const char* src, char c);
+
+ // Copies src to dest, up to specified length-1, and guarantees termination
+ void strCopy(char* dest, const char* src, int destlen);
+}
+#endif
+
+// Some platforms (e.g. Windows) include max() and min() macros in their
+// standard headers, so we define them only when not already defined. Note
+// also that max() & min() are standard C++ template functions, so some C++
+// headers will undefine them. We place our definitions outside the #ifndef
+// __RFB_UTIL_H__, so that you can always guarantee they will be defined if
+// this file is the last #include before you use them.
+
+#ifndef max
+#define max(a,b) (((a) > (b)) ? (a) : (b))
+#endif
+
+#ifndef min
+#define min(a,b) (((a) < (b)) ? (a) : (b))
+#endif
+
+
+// -=- PLATFORM SPECIFIC UTILITY FUNCTIONS/IMPLEMENTATIONS
+#ifdef WIN32
+#include "win32/util_win32.h"
+#endif
+
diff --git a/rfb/vncAuth.cxx b/rfb/vncAuth.cxx
new file mode 100644
index 00000000..6bd6a620
--- /dev/null
+++ b/rfb/vncAuth.cxx
@@ -0,0 +1,61 @@
+/* Copyright (C) 2002-2003 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.
+ */
+//
+// vncAuth
+//
+// XXX not thread-safe, because d3des isn't - do we need to worry about this?
+//
+
+#include <string.h>
+extern "C" {
+#include <rfb/d3des.h>
+}
+#include <rfb/vncAuth.h>
+
+using namespace rfb;
+
+void rfb::vncAuthEncryptChallenge(rdr::U8* challenge, const char* passwd)
+{
+ unsigned char key[8] = { 0, };
+ int len = strlen(passwd);
+ if (len > 8) len = 8;
+ for (int i = 0; i < len; i++)
+ key[i] = passwd[i];
+
+ deskey(key, EN0);
+
+ for (int j = 0; j < vncAuthChallengeSize; j += 8)
+ des(challenge+j, challenge+j);
+}
+
+static unsigned char obfuscationKey[] = {23,82,107,6,35,78,88,7};
+
+void rfb::vncAuthObfuscatePasswd(char* passwd)
+{
+ for (int i = strlen(passwd); i < 8; i++)
+ passwd[i] = 0;
+ deskey(obfuscationKey, EN0);
+ des((unsigned char*)passwd, (unsigned char*)passwd);
+}
+
+void rfb::vncAuthUnobfuscatePasswd(char* passwd)
+{
+ deskey(obfuscationKey, DE1);
+ des((unsigned char*)passwd, (unsigned char*)passwd);
+ passwd[8] = 0;
+}
diff --git a/rfb/vncAuth.h b/rfb/vncAuth.h
new file mode 100644
index 00000000..18d87ade
--- /dev/null
+++ b/rfb/vncAuth.h
@@ -0,0 +1,31 @@
+/* Copyright (C) 2002-2003 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_VNCAUTH_H__
+#define __RFB_VNCAUTH_H__
+
+#include <rdr/types.h>
+
+namespace rfb {
+
+ const int vncAuthChallengeSize = 16;
+
+ void vncAuthEncryptChallenge(rdr::U8* challenge, const char* passwd);
+ void vncAuthObfuscatePasswd(char* passwd);
+ void vncAuthUnobfuscatePasswd(char* passwd);
+}
+#endif
diff --git a/rfb/win32/Threading_win32.cxx b/rfb/win32/Threading_win32.cxx
new file mode 100644
index 00000000..28cfdb7e
--- /dev/null
+++ b/rfb/win32/Threading_win32.cxx
@@ -0,0 +1,155 @@
+/* Copyright (C) 2002-2003 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.cxx
+// Win32 Threading interface implementation
+
+#include <malloc.h>
+
+#include <rdr/Exception.h>
+#include <rfb/LogWriter.h>
+#include <rfb/win32/Threading_win32.h>
+#include <rfb/util.h>
+
+using namespace rfb;
+
+static LogWriter vlog("Threading");
+
+static DWORD threadStorage = TlsAlloc();
+
+
+inline logAction(Thread* t, const char* action) {
+ vlog.debug("%-16.16s %s(%lx)", action, t->getName(), t);
+}
+
+inline 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_) : sig(0), deleteAfterRun(false) {
+ sig = new Condition(mutex);
+ cond_event = CreateEvent(NULL, TRUE, FALSE, NULL);
+ if (!name_)
+ name_ = "Unnamed";
+ name = strDup(name_);
+ thread = CreateThread(NULL, 0, threadProc, this, CREATE_SUSPENDED, &thread_id);
+ state = ThreadCreated;
+ logAction(this, "created");
+}
+
+Thread::Thread(HANDLE thread_, DWORD thread_id_) : sig(0), deleteAfterRun(false), thread(thread_), thread_id(thread_id_) {
+ sig = new Condition(mutex);
+ cond_event = CreateEvent(NULL, TRUE, FALSE, NULL);
+ name = strDup("Native");
+ state = ThreadNative;
+ logAction(this, "created");
+}
+
+Thread::~Thread() {
+ logAction(this, "destroying");
+ if (!deleteAfterRun) this->join();
+ if (sig)
+ delete sig;
+ if (cond_event)
+ CloseHandle(cond_event);
+ logAction(this, "destroyed");
+ strFree(name);
+}
+
+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 (!thread) {
+ logAction(this, "already joined");
+ } else {
+ logAction(this, "joining");
+ while (state == ThreadStarted) {
+ sig->wait();
+ logAction(this, "checking");
+ }
+ CloseHandle(thread);
+ thread = 0;
+ logAction(this, "joined");
+ }
+ return this;
+}
+
+const char*
+Thread::getName() const {
+ return name;
+}
+
+ThreadState
+Thread::getState() const {
+ return state;
+}
+
+unsigned long
+Thread::getThreadId() const {
+ return thread_id;
+}
+
+Thread*
+Thread::self() {
+ Thread* thread = (Thread*) TlsGetValue(threadStorage);
+ if (!thread) {
+ thread = new Thread(GetCurrentThread(), GetCurrentThreadId());
+ TlsSetValue(threadStorage, thread);
+ }
+ return thread;
+} \ No newline at end of file
diff --git a/rfb/win32/Threading_win32.h b/rfb/win32/Threading_win32.h
new file mode 100644
index 00000000..e95e0f73
--- /dev/null
+++ b/rfb/win32/Threading_win32.h
@@ -0,0 +1,149 @@
+/* Copyright (C) 2002-2003 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
+
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+
+#include <stdio.h>
+
+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, 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);
+
+ HANDLE thread;
+ DWORD thread_id;
+ char* name;
+ ThreadState state;
+ Condition* sig;
+ Mutex mutex;
+
+ HANDLE cond_event;
+ Thread* cond_next;
+
+ bool deleteAfterRun;
+ };
+
+ class Condition {
+ public:
+ Condition(Mutex& m) : mutex(m), waiting(0) {
+ }
+ ~Condition() {
+ }
+ void signal() {
+ Lock l(cond_lock);
+ if (waiting) {
+ SetEvent(waiting->cond_event);
+ waiting = waiting->cond_next;
+ }
+ }
+ // - MUST hold "mutex" to call wait()
+ // WIN32: if processMsg is true then wait will continue
+ // to process messages in the thread's queue.
+ // Avoid using this unless you have to!
+ void wait(bool processMsgs=false) {
+ Thread* self = Thread::self();
+ ResetEvent(self->cond_event);
+ { Lock l(cond_lock);
+ self->cond_next = waiting;
+ waiting = self;
+ }
+ mutex.exit();
+ if (processMsgs) {
+ while (1) {
+ DWORD result = MsgWaitForMultipleObjects(1, &self->cond_event, FALSE, INFINITE, QS_ALLINPUT);
+ if (result == WAIT_OBJECT_0)
+ break;
+ MSG msg;
+ while (PeekMessage(&msg, 0, 0, 0, PM_REMOVE)) {
+ DispatchMessage(&msg);
+ }
+ }
+ } else {
+ WaitForSingleObject(self->cond_event, INFINITE);
+ }
+ mutex.enter();
+ }
+
+ protected:
+ Mutex& mutex;
+ Mutex cond_lock;
+ Thread* waiting;
+ };
+
+};
+
+#endif // __RFB_THREADING_IMPL
diff --git a/rfb/win32/msvcwarning.h b/rfb/win32/msvcwarning.h
new file mode 100644
index 00000000..d0c98c3c
--- /dev/null
+++ b/rfb/win32/msvcwarning.h
@@ -0,0 +1,19 @@
+/* Copyright (C) 2002-2003 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.
+ */
+#pragma warning( disable : 4244 ) // loss of data e.g. int to char
+#pragma warning( disable : 4800 ) // forcing bool 'true' or 'false'
diff --git a/rfb/win32/util_win32.h b/rfb/win32/util_win32.h
new file mode 100644
index 00000000..4bde5ec7
--- /dev/null
+++ b/rfb/win32/util_win32.h
@@ -0,0 +1,111 @@
+/* Copyright (C) 2002-2003 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.
+ */
+
+//
+// util_win32.h - miscellaneous useful bits for Win32 only
+//
+
+#ifndef __RFB_UTIL_WIN32_H__
+#define __RFB_UTIL_WIN32_H__
+
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+// *** #include <iostream.h>
+
+#include <rfb/LogWriter.h>
+#include <rfb/Exception.h>
+
+namespace rfb {
+
+ // WIN32-ONLY PROFILING CODE
+ //
+ // CpuTime and CpuTimer provide a simple way to profile particular
+ // sections of code
+ //
+ // Use one CpuTime object per task to be profiled. CpuTime instances
+ // maintain a cumulative total of time spent in user and kernel space
+ // by threads.
+ // When a CpuTime object is created, a label must be specified to
+ // identify the task being profiled.
+ // When the object is destroyed, it will print debugging information
+ // containing the user and kernel times accumulated.
+ //
+ // Place a CpuTimer object in each section of code which is to be
+ // profiled. When the object is created, it snapshots the current
+ // kernel and user times and stores them. These are used when the
+ // object is destroyed to establish how much time has elapsed in the
+ // intervening period. The accumulated time is then added to the
+ // associated CpuTime object.
+ //
+ // This code works only on platforms providing __int64
+
+ class CpuTime {
+ public:
+ CpuTime(const char *name)
+ : timer_name(strDup(name)),
+ kernel_time(0), user_time(0), max_user_time(0), iterations(0) {}
+ ~CpuTime() {
+ g_log_writer.info("timer %s : %I64ums (krnl), %I64ums (user), %I64uus (user-max) (%I64u its)\n",
+ timer_name, kernel_time/10000, user_time/10000, max_user_time/10,
+ iterations);
+ delete [] timer_name;
+ }
+ static LogWriter g_log_writer;
+ char* timer_name;
+ __int64 kernel_time;
+ __int64 user_time;
+ __int64 iterations;
+ __int64 max_user_time;
+ };
+
+ class CpuTimer {
+ public:
+ inline CpuTimer(CpuTime &ct) : cputime(ct) {
+ FILETIME create_time, end_time;
+ if (!GetThreadTimes(GetCurrentThread(),
+ &create_time, &end_time,
+ (LPFILETIME)&start_kernel_time,
+ (LPFILETIME)&start_user_time)) {
+ throw rdr::SystemException("rfb::CpuTimer failed to initialise", GetLastError());
+ }
+ }
+ inline ~CpuTimer() {
+ FILETIME create_time, end_time;
+ __int64 end_kernel_time, end_user_time;
+ if (!GetThreadTimes(GetCurrentThread(),
+ &create_time, &end_time,
+ (LPFILETIME)&end_kernel_time,
+ (LPFILETIME)&end_user_time)) {
+ throw rdr::SystemException("rfb::CpuTimer destructor failed", GetLastError());
+ }
+ cputime.kernel_time += end_kernel_time - start_kernel_time;
+ cputime.user_time += end_user_time - start_user_time;
+ if (end_user_time - start_user_time > cputime.max_user_time) {
+ cputime.max_user_time = end_user_time - start_user_time;
+ }
+ cputime.iterations++;
+ }
+ private:
+ CpuTime& cputime;
+ __int64 start_kernel_time;
+ __int64 start_user_time;
+ };
+
+};
+
+#endif // __RFB_UTIL_WIN32_H__
diff --git a/rfb/zrleDecode.h b/rfb/zrleDecode.h
new file mode 100644
index 00000000..b5391b11
--- /dev/null
+++ b/rfb/zrleDecode.h
@@ -0,0 +1,251 @@
+/* Copyright (C) 2002-2003 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.
+ */
+
+//
+// ZRLE decoding function.
+//
+// This file is #included after having set the following macros:
+// BPP - 8, 16 or 32
+// EXTRA_ARGS - optional extra arguments
+// FILL_RECT - fill a rectangle with a single colour
+// IMAGE_RECT - draw a rectangle of pixel data from a buffer
+
+#include <rdr/InStream.h>
+#include <rdr/ZlibInStream.h>
+#include <assert.h>
+
+namespace rfb {
+
+// CONCAT2E concatenates its arguments, expanding them if they are macros
+
+#ifndef CONCAT2E
+#define CONCAT2(a,b) a##b
+#define CONCAT2E(a,b) CONCAT2(a,b)
+#endif
+
+#ifdef CPIXEL
+#define PIXEL_T rdr::CONCAT2E(U,BPP)
+#define READ_PIXEL CONCAT2E(readOpaque,CPIXEL)
+#define ZRLE_DECODE CONCAT2E(zrleDecode,CPIXEL)
+#else
+#define PIXEL_T rdr::CONCAT2E(U,BPP)
+#define READ_PIXEL CONCAT2E(readOpaque,BPP)
+#define ZRLE_DECODE CONCAT2E(zrleDecode,BPP)
+#endif
+
+void ZRLE_DECODE (const Rect& r, rdr::InStream* is,
+ rdr::ZlibInStream* zis, PIXEL_T* buf
+#ifdef EXTRA_ARGS
+ , EXTRA_ARGS
+#endif
+ )
+{
+ int length = is->readU32();
+ zis->setUnderlying(is, length);
+ Rect t;
+
+ for (t.tl.y = r.tl.y; t.tl.y < r.br.y; t.tl.y += 64) {
+
+ t.br.y = min(r.br.y, t.tl.y + 64);
+
+ for (t.tl.x = r.tl.x; t.tl.x < r.br.x; t.tl.x += 64) {
+
+ t.br.x = min(r.br.x, t.tl.x + 64);
+
+ int mode = zis->readU8();
+ bool rle = mode & 128;
+ int palSize = mode & 127;
+ PIXEL_T palette[128];
+
+ for (int i = 0; i < palSize; i++) {
+ palette[i] = zis->READ_PIXEL();
+ }
+
+ if (palSize == 1) {
+ PIXEL_T pix = palette[0];
+ FILL_RECT(t,pix);
+ continue;
+ }
+
+ if (!rle) {
+ if (palSize == 0) {
+
+ // raw
+
+#ifdef CPIXEL
+ for (PIXEL_T* ptr = buf; ptr < buf+t.area(); ptr++) {
+ *ptr = zis->READ_PIXEL();
+ }
+#else
+ zis->readBytes(buf, t.area() * (BPP / 8));
+#endif
+
+ } else {
+
+ // packed pixels
+ int bppp = ((palSize > 16) ? 8 :
+ ((palSize > 4) ? 4 : ((palSize > 2) ? 2 : 1)));
+
+ PIXEL_T* ptr = buf;
+
+ for (int i = 0; i < t.height(); i++) {
+ PIXEL_T* eol = ptr + t.width();
+ rdr::U8 byte = 0;
+ rdr::U8 nbits = 0;
+
+ while (ptr < eol) {
+ if (nbits == 0) {
+ byte = zis->readU8();
+ nbits = 8;
+ }
+ nbits -= bppp;
+ rdr::U8 index = (byte >> nbits) & ((1 << bppp) - 1) & 127;
+ *ptr++ = palette[index];
+ }
+ }
+ }
+
+#ifdef FAVOUR_FILL_RECT
+ //fprintf(stderr,"copying data to screen %dx%d at %d,%d\n",
+ //t.width(),t.height(),t.tl.x,t.tl.y);
+ IMAGE_RECT(t,buf);
+#endif
+
+ } else {
+
+ if (palSize == 0) {
+
+ // plain RLE
+
+ PIXEL_T* ptr = buf;
+ PIXEL_T* end = ptr + t.area();
+ while (ptr < end) {
+ PIXEL_T pix = zis->READ_PIXEL();
+ int len = 1;
+ int b;
+ do {
+ b = zis->readU8();
+ len += b;
+ } while (b == 255);
+
+ assert(len <= end - ptr);
+
+#ifdef FAVOUR_FILL_RECT
+ int i = ptr - buf;
+ ptr += len;
+
+ int runX = i % t.width();
+ int runY = i / t.width();
+
+ if (runX + len > t.width()) {
+ if (runX != 0) {
+ FILL_RECT(Rect(t.tl.x+runX, t.tl.y+runY, t.width()-runX, 1),
+ pix);
+ len -= t.width()-runX;
+ runX = 0;
+ runY++;
+ }
+
+ if (len > t.width()) {
+ FILL_RECT(Rect(t.tl.x, t.tl.y+runY, t.width(), len/t.width()),
+ pix);
+ runY += len / t.width();
+ len = len % t.width();
+ }
+ }
+
+ if (len != 0) {
+ FILL_RECT(Rect(t.tl.x+runX, t.tl.y+runY, len, 1), pix);
+ }
+#else
+ while (len-- > 0) *ptr++ = pix;
+#endif
+
+ }
+ } else {
+
+ // palette RLE
+
+ PIXEL_T* ptr = buf;
+ PIXEL_T* end = ptr + t.area();
+ while (ptr < end) {
+ int index = zis->readU8();
+ int len = 1;
+ if (index & 128) {
+ int b;
+ do {
+ b = zis->readU8();
+ len += b;
+ } while (b == 255);
+
+ assert(len <= end - ptr);
+ }
+
+ index &= 127;
+
+ PIXEL_T pix = palette[index];
+
+#ifdef FAVOUR_FILL_RECT
+ int i = ptr - buf;
+ ptr += len;
+
+ int runX = i % t.width();
+ int runY = i / t.width();
+
+ if (runX + len > t.width()) {
+ if (runX != 0) {
+ FILL_RECT(Rect(t.tl.x+runX, t.tl.y+runY, t.width()-runX, 1),
+ pix);
+ len -= t.width()-runX;
+ runX = 0;
+ runY++;
+ }
+
+ if (len > t.width()) {
+ FILL_RECT(Rect(t.tl.x, t.tl.y+runY, t.width(), len/t.width()),
+ pix);
+ runY += len / t.width();
+ len = len % t.width();
+ }
+ }
+
+ if (len != 0) {
+ FILL_RECT(Rect(t.tl.x+runX, t.tl.y+runY, len, 1), pix);
+ }
+#else
+ while (len-- > 0) *ptr++ = pix;
+#endif
+ }
+ }
+ }
+
+#ifndef FAVOUR_FILL_RECT
+ //fprintf(stderr,"copying data to screen %dx%d at %d,%d\n",
+ //t.width(),t.height(),t.tl.x,t.tl.y);
+ IMAGE_RECT(t,buf);
+#endif
+ }
+ }
+
+ zis->reset();
+}
+
+#undef ZRLE_DECODE
+#undef READ_PIXEL
+#undef PIXEL_T
+}
diff --git a/rfb/zrleEncode.h b/rfb/zrleEncode.h
new file mode 100644
index 00000000..a1582f2a
--- /dev/null
+++ b/rfb/zrleEncode.h
@@ -0,0 +1,328 @@
+/* Copyright (C) 2002-2004 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.
+ */
+
+//
+// zrleEncode.h - zrle encoding function.
+//
+// This file is #included after having set the following macros:
+// BPP - 8, 16 or 32
+// EXTRA_ARGS - optional extra arguments
+// GET_IMAGE_INTO_BUF - gets a rectangle of pixel data into a buffer
+//
+// Note that the buf argument to ZRLE_ENCODE needs to be at least one pixel
+// bigger than the largest tile of pixel data, since the ZRLE encoding
+// algorithm writes to the position one past the end of the pixel data.
+//
+
+#include <rdr/OutStream.h>
+#include <rdr/ZlibOutStream.h>
+#include <assert.h>
+
+namespace rfb {
+
+// CONCAT2E concatenates its arguments, expanding them if they are macros
+
+#ifndef CONCAT2E
+#define CONCAT2(a,b) a##b
+#define CONCAT2E(a,b) CONCAT2(a,b)
+#endif
+
+#ifdef CPIXEL
+#define PIXEL_T rdr::CONCAT2E(U,BPP)
+#define WRITE_PIXEL CONCAT2E(writeOpaque,CPIXEL)
+#define ZRLE_ENCODE CONCAT2E(zrleEncode,CPIXEL)
+#define ZRLE_ENCODE_TILE CONCAT2E(zrleEncodeTile,CPIXEL)
+#define BPPOUT 24
+#else
+#define PIXEL_T rdr::CONCAT2E(U,BPP)
+#define WRITE_PIXEL CONCAT2E(writeOpaque,BPP)
+#define ZRLE_ENCODE CONCAT2E(zrleEncode,BPP)
+#define ZRLE_ENCODE_TILE CONCAT2E(zrleEncodeTile,BPP)
+#define BPPOUT BPP
+#endif
+
+#ifndef ZRLE_ONCE
+#define ZRLE_ONCE
+static const int bitsPerPackedPixel[] = {
+ 0, 1, 2, 2, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4
+};
+
+// The PaletteHelper class helps us build up the palette from pixel data by
+// storing a reverse index using a simple hash-table
+
+class PaletteHelper {
+public:
+ enum { MAX_SIZE = 127 };
+
+ PaletteHelper()
+ {
+ memset(index, 255, sizeof(index));
+ size = 0;
+ }
+
+ inline int hash(rdr::U32 pix)
+ {
+ return (pix ^ (pix >> 17)) & 4095;
+ }
+
+ inline void insert(rdr::U32 pix)
+ {
+ if (size < MAX_SIZE) {
+ int i = hash(pix);
+ while (index[i] != 255 && key[i] != pix)
+ i++;
+ if (index[i] != 255) return;
+
+ index[i] = size;
+ key[i] = pix;
+ palette[size] = pix;
+ }
+ size++;
+ }
+
+ inline int lookup(rdr::U32 pix)
+ {
+ assert(size <= MAX_SIZE);
+ int i = hash(pix);
+ while (index[i] != 255 && key[i] != pix)
+ i++;
+ if (index[i] != 255) return index[i];
+ return -1;
+ }
+
+ rdr::U32 palette[MAX_SIZE];
+ rdr::U8 index[4096+MAX_SIZE];
+ rdr::U32 key[4096+MAX_SIZE];
+ int size;
+};
+#endif
+
+void ZRLE_ENCODE_TILE (PIXEL_T* data, int w, int h, rdr::OutStream* os);
+
+bool ZRLE_ENCODE (const Rect& r, rdr::OutStream* os,
+ rdr::ZlibOutStream* zos, void* buf, int maxLen, Rect* actual
+#ifdef EXTRA_ARGS
+ , EXTRA_ARGS
+#endif
+ )
+{
+ zos->setUnderlying(os);
+ // RLE overhead is at worst 1 byte per 64x64 (4Kpixel) block
+ int worstCaseLine = r.width() * 64 * (BPPOUT/8) + 1 + r.width() / 64;
+ // Zlib overhead is at worst 6 bytes plus 5 bytes per 32Kbyte block.
+ worstCaseLine += 11 + 5 * (worstCaseLine >> 15);
+ Rect t;
+
+ for (t.tl.y = r.tl.y; t.tl.y < r.br.y; t.tl.y += 64) {
+
+ t.br.y = min(r.br.y, t.tl.y + 64);
+
+ if (os->length() + worstCaseLine > maxLen) {
+ if (t.tl.y == r.tl.y)
+ throw Exception("ZRLE: not enough space for first line?");
+ actual->tl = r.tl;
+ actual->br.x = r.br.x;
+ actual->br.y = t.tl.y;
+ return false;
+ }
+
+ for (t.tl.x = r.tl.x; t.tl.x < r.br.x; t.tl.x += 64) {
+
+ t.br.x = min(r.br.x, t.tl.x + 64);
+
+ GET_IMAGE_INTO_BUF(t,buf);
+
+ ZRLE_ENCODE_TILE((PIXEL_T*)buf, t.width(), t.height(), zos);
+ }
+
+ zos->flush();
+ }
+ return true;
+}
+
+
+void ZRLE_ENCODE_TILE (PIXEL_T* data, int w, int h, rdr::OutStream* os)
+{
+ // First find the palette and the number of runs
+
+ PaletteHelper ph;
+
+ int runs = 0;
+ int singlePixels = 0;
+
+ PIXEL_T* ptr = data;
+ PIXEL_T* end = ptr + h * w;
+ *end = ~*(end-1); // one past the end is different so the while loop ends
+
+ while (ptr < end) {
+ PIXEL_T pix = *ptr;
+ if (*++ptr != pix) {
+ singlePixels++;
+ } else {
+ while (*++ptr == pix) ;
+ runs++;
+ }
+ ph.insert(pix);
+ }
+
+ //fprintf(stderr,"runs %d, single pixels %d, paletteSize %d\n",
+ // runs, singlePixels, ph.size);
+
+ // Solid tile is a special case
+
+ if (ph.size == 1) {
+ os->writeU8(1);
+ os->WRITE_PIXEL(ph.palette[0]);
+ return;
+ }
+
+ // Try to work out whether to use RLE and/or a palette. We do this by
+ // estimating the number of bytes which will be generated and picking the
+ // method which results in the fewest bytes. Of course this may not result
+ // in the fewest bytes after compression...
+
+ bool useRle = false;
+ bool usePalette = false;
+
+ int estimatedBytes = w * h * (BPPOUT/8); // start assuming raw
+
+ int plainRleBytes = ((BPPOUT/8)+1) * (runs + singlePixels);
+
+ if (plainRleBytes < estimatedBytes) {
+ useRle = true;
+ estimatedBytes = plainRleBytes;
+ }
+
+ if (ph.size < 128) {
+ int paletteRleBytes = (BPPOUT/8) * ph.size + 2 * runs + singlePixels;
+
+ if (paletteRleBytes < estimatedBytes) {
+ useRle = true;
+ usePalette = true;
+ estimatedBytes = paletteRleBytes;
+ }
+
+ if (ph.size < 17) {
+ int packedBytes = ((BPPOUT/8) * ph.size +
+ w * h * bitsPerPackedPixel[ph.size-1] / 8);
+
+ if (packedBytes < estimatedBytes) {
+ useRle = false;
+ usePalette = true;
+ estimatedBytes = packedBytes;
+ }
+ }
+ }
+
+ if (!usePalette) ph.size = 0;
+
+ os->writeU8((useRle ? 128 : 0) | ph.size);
+
+ for (int i = 0; i < ph.size; i++) {
+ os->WRITE_PIXEL(ph.palette[i]);
+ }
+
+ if (useRle) {
+
+ PIXEL_T* ptr = data;
+ PIXEL_T* end = ptr + w * h;
+ PIXEL_T* runStart;
+ PIXEL_T pix;
+ while (ptr < end) {
+ runStart = ptr;
+ pix = *ptr++;
+ while (*ptr == pix && ptr < end)
+ ptr++;
+ int len = ptr - runStart;
+ if (len <= 2 && usePalette) {
+ int index = ph.lookup(pix);
+ if (len == 2)
+ os->writeU8(index);
+ os->writeU8(index);
+ continue;
+ }
+ if (usePalette) {
+ int index = ph.lookup(pix);
+ os->writeU8(index | 128);
+ } else {
+ os->WRITE_PIXEL(pix);
+ }
+ len -= 1;
+ while (len >= 255) {
+ os->writeU8(255);
+ len -= 255;
+ }
+ os->writeU8(len);
+ }
+
+ } else {
+
+ // no RLE
+
+ if (usePalette) {
+
+ // packed pixels
+
+ assert (ph.size < 17);
+
+ int bppp = bitsPerPackedPixel[ph.size-1];
+
+ PIXEL_T* ptr = data;
+
+ for (int i = 0; i < h; i++) {
+ rdr::U8 nbits = 0;
+ rdr::U8 byte = 0;
+
+ PIXEL_T* eol = ptr + w;
+
+ while (ptr < eol) {
+ PIXEL_T pix = *ptr++;
+ rdr::U8 index = ph.lookup(pix);
+ byte = (byte << bppp) | index;
+ nbits += bppp;
+ if (nbits >= 8) {
+ os->writeU8(byte);
+ nbits = 0;
+ }
+ }
+ if (nbits > 0) {
+ byte <<= 8 - nbits;
+ os->writeU8(byte);
+ }
+ }
+ } else {
+
+ // raw
+
+#ifdef CPIXEL
+ for (PIXEL_T* ptr = data; ptr < data+w*h; ptr++) {
+ os->WRITE_PIXEL(*ptr);
+ }
+#else
+ os->writeBytes(data, w*h*(BPP/8));
+#endif
+ }
+ }
+}
+
+#undef PIXEL_T
+#undef WRITE_PIXEL
+#undef ZRLE_ENCODE
+#undef ZRLE_ENCODE_TILE
+#undef BPPOUT
+}
diff --git a/rfb_win32/AboutDialog.cxx b/rfb_win32/AboutDialog.cxx
new file mode 100644
index 00000000..efb15c00
--- /dev/null
+++ b/rfb_win32/AboutDialog.cxx
@@ -0,0 +1,49 @@
+/* Copyright (C) 2002-2003 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 <rfb_win32/AboutDialog.h>
+#include <rfb_win32/Win32Util.h>
+#include <rfb_win32/TCharArray.h>
+#include <rfb/LogWriter.h>
+
+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("FileVersion")));
+ SetWindowText(GetDlgItem(handle, Copyright), verInfo.getVerString(_T("LegalCopyright")));
+ SetWindowText(GetDlgItem(handle, Description), verInfo.getVerString(_T("FileDescription")));
+}
diff --git a/rfb_win32/AboutDialog.h b/rfb_win32/AboutDialog.h
new file mode 100644
index 00000000..cb1713d4
--- /dev/null
+++ b/rfb_win32/AboutDialog.h
@@ -0,0 +1,55 @@
+/* Copyright (C) 2002-2003 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 <rfb_win32/Dialog.h>
+#include <rfb/util.h>
+
+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/rfb_win32/CKeyboard.cxx b/rfb_win32/CKeyboard.cxx
new file mode 100644
index 00000000..ad852a0e
--- /dev/null
+++ b/rfb_win32/CKeyboard.cxx
@@ -0,0 +1,259 @@
+/* Copyright (C) 2002-2003 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 <map>
+
+#define XK_MISCELLANY
+#define XK_LATIN1
+#define XK_CURRENCY
+#include <rfb/keysymdef.h>
+
+#include <rfb_win32/CKeyboard.h>
+#include <rfb/CMsgWriter.h>
+#include <rfb/LogWriter.h>
+#include <rfb_win32/OSVersion.h>
+#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<int,rdr::U32> keysymMap;
+} ckeymapper;
+
+
+class ModifierKeyReleaser {
+public:
+ ModifierKeyReleaser(CMsgWriter* writer_, int vkCode, bool extended)
+ : writer(writer_), extendedVkey(vkCode + (extended ? 256 : 0)),
+ keysym(0)
+ {}
+ void release(std::map<int,rdr::U32>* downKeysym) {
+ if (downKeysym->find(extendedVkey) != downKeysym->end()) {
+ keysym = (*downKeysym)[extendedVkey];
+ vlog.debug("fake release extendedVkey 0x%x, keysym 0x%x",
+ extendedVkey, keysym);
+ writer->writeKeyEvent(keysym, false);
+ }
+ }
+ ~ModifierKeyReleaser() {
+ if (keysym) {
+ vlog.debug("fake press extendedVkey 0x%x, keysym 0x%x",
+ extendedVkey, keysym);
+ writer->writeKeyEvent(keysym, true);
+ }
+ }
+ CMsgWriter* 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(CMsgWriter* 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(CMsgWriter* writer) {
+ std::map<int,rdr::U32>::iterator i, next_i;
+ for (i=downKeysym.begin(); i!=downKeysym.end(); i=next_i) {
+ next_i = i; next_i++;
+ writer->writeKeyEvent((*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(CMsgWriter* writer, int extendedVkey)
+{
+ if (downKeysym.find(extendedVkey) != downKeysym.end()) {
+ vlog.debug("release extendedVkey 0x%x, keysym 0x%x",
+ extendedVkey, downKeysym[extendedVkey]);
+ writer->writeKeyEvent(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(CMsgWriter* 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->writeKeyEvent(downKeysym[extendedVkey], false);
+ }
+ }
+ vlog.debug("press extendedVkey 0x%x, keysym 0x%x",
+ extendedVkey, keysym);
+ writer->writeKeyEvent(keysym, true);
+ downKeysym[extendedVkey] = keysym;
+}
diff --git a/rfb_win32/CKeyboard.h b/rfb_win32/CKeyboard.h
new file mode 100644
index 00000000..10346ffc
--- /dev/null
+++ b/rfb_win32/CKeyboard.h
@@ -0,0 +1,53 @@
+/* Copyright (C) 2002-2003 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 <rdr/types.h>
+#include <map>
+
+namespace rfb {
+
+ class CMsgWriter;
+
+ namespace win32 {
+
+ class CKeyboard {
+ public:
+ void keyEvent(CMsgWriter* writer, rdr::U8 vkey, rdr::U32 flags,
+ bool down);
+ void releaseAllKeys(CMsgWriter* writer);
+ const std::map<int,rdr::U32>& pressedKeys() const {return downKeysym;};
+ bool keyPressed(int k) const {return downKeysym.find(k)!=downKeysym.end();}
+ private:
+ void win32::CKeyboard::releaseKey(CMsgWriter* writer, int extendedVkey);
+ void win32::CKeyboard::pressKey(CMsgWriter* writer, int extendedVkey,
+ rdr::U32 keysym);
+ std::map<int,rdr::U32> downKeysym;
+ };
+
+ }; // win32
+
+}; // rfb
+
+#endif // __RFB_WIN32_CKEYBOARD_H__
diff --git a/rfb_win32/CPointer.cxx b/rfb_win32/CPointer.cxx
new file mode 100644
index 00000000..1cab662a
--- /dev/null
+++ b/rfb_win32/CPointer.cxx
@@ -0,0 +1,188 @@
+/* Copyright (C) 2002-2003 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.
+ */
+
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+
+#include <rfb/LogWriter.h>
+#include <rfb_win32/CPointer.h>
+
+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(CMsgWriter* writer, int x, int y, int buttonMask) {
+ //
+ // - Duplicate Event Filtering
+ //
+
+ bool maskChanged = buttonMask != currButtonMask;
+ bool posChanged = !Point(x, y).equals(currPos);
+ if (!(posChanged || maskChanged))
+ return;
+
+ // Pass on the event to the event-interval handler
+ threePointerEvent(writer, x, y, buttonMask);
+
+ // Save the position and mask
+ currPos = Point(x, y);
+ 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(CMsgWriter* writer, int x, int y, 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 = Point(x, y);
+ 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.x, threePos.y, threeMask);
+ else
+ threeEmulating = ((buttonMask & 5) == 5);
+ }
+
+ } else {
+
+ if (threeTimer.isActive()) {
+ // - We are timing for an emulation event
+
+ if (abs(threePos.x - x) <= 4 || abs(threePos.y - 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.x, threePos.y, 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, x, y, buttonMask);
+}
+
+void CPointer::intervalPointerEvent(CMsgWriter* writer, int x, int y, int buttonMask) {
+ //
+ // - Pointer Event Interval
+ //
+ vlog.write(101, "ptrEvent: %d,%d (%lx)", x, 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->writePointerEvent(intervalPos.x, intervalPos.y, intervalMask);
+ intervalQueued = false;
+ }
+
+ if (!sendNow) {
+ // If we're not sending now then just queue the event
+ intervalQueued = true;
+ intervalPos = Point(x, y);
+ intervalMask = buttonMask;
+ } else {
+ // Start the interval timer if required, and send the event
+ intervalQueued = false;
+ intervalMask = buttonMask;
+ if (pointerEventInterval)
+ intervalTimer.start(pointerEventInterval);
+ writer->writePointerEvent(x, y, buttonMask);
+ }
+}
+
+void CPointer::handleTimer(CMsgWriter* writer, int timerId) {
+ if (timerId == intervalTimer.getId()) {
+ // Pointer interval has expired - send any queued events
+ if (intervalQueued) {
+ writer->writePointerEvent(intervalPos.x, intervalPos.y, 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.x, threePos.y, threeMask);
+ }
+}
diff --git a/rfb_win32/CPointer.h b/rfb_win32/CPointer.h
new file mode 100644
index 00000000..f111de74
--- /dev/null
+++ b/rfb_win32/CPointer.h
@@ -0,0 +1,76 @@
+/* Copyright (C) 2002-2003 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 <rdr/Exception.h>
+#include <rfb/Configuration.h>
+#include <rfb/CMsgWriter.h>
+#include <rfb/Rect.h>
+#include <rfb_win32/IntervalTimer.h>
+
+namespace rfb {
+
+ class CMsgWriter;
+
+ namespace win32 {
+
+ class CPointer {
+ public:
+ CPointer();
+ ~CPointer();
+
+ void pointerEvent(CMsgWriter* writer, int x, int y, int buttonMask);
+ void handleTimer(CMsgWriter* 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(CMsgWriter* writer, int x, int y, int buttonMask);
+ IntervalTimer intervalTimer;
+ bool intervalQueued;
+ Point intervalPos;
+ int intervalMask;
+
+ void threePointerEvent(CMsgWriter* writer, int x, int y, int buttonMask);
+ IntervalTimer threeTimer;
+ Point threePos;
+ int threeMask;
+ bool threeEmulating;
+ };
+
+ }; // win32
+
+}; // rfb
+
+#endif // __RFB_WIN32_CPOINTER_H__
diff --git a/rfb_win32/CleanDesktop.cxx b/rfb_win32/CleanDesktop.cxx
new file mode 100644
index 00000000..9fb83478
--- /dev/null
+++ b/rfb_win32/CleanDesktop.cxx
@@ -0,0 +1,255 @@
+/* Copyright (C) 2002-2004 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
+
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+#include <wininet.h>
+#include <shlobj.h>
+
+#include <rfb_win32/CleanDesktop.h>
+#include <rfb_win32/CurrentUser.h>
+#include <rfb_win32/Registry.h>
+#include <rfb/LogWriter.h>
+#include <rdr/Exception.h>
+
+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();
+ }
+ bool enable(bool enable_) {
+ // - Get the current Active Desktop options
+ COMPONENTSOPT adOptions;
+ memset(&adOptions, 0, sizeof(adOptions));
+ adOptions.dwSize = sizeof(adOptions);
+
+ HRESULT result = handle->GetDesktopItemOptions(&adOptions, 0);
+ if (result != S_OK) {
+ vlog.error("failed to get Active Desktop options: %d", result);
+ return false;
+ }
+
+ // - If Active Desktop is active, disable it
+ if ((adOptions.fActiveDesktop==0) != (enable_==0)) {
+ if (enable_)
+ vlog.debug("enabling Active Desktop");
+ else
+ vlog.debug("disabling Active Desktop");
+
+ adOptions.fActiveDesktop = enable_;
+ result = handle->SetDesktopItemOptions(&adOptions, 0);
+ if (result != S_OK) {
+ vlog.error("failed to disable ActiveDesktop: %d", result);
+ return false;
+ }
+ handle->ApplyChanges(AD_APPLY_REFRESH);
+ return true;
+ }
+ return false;
+ }
+ IActiveDesktop* handle;
+};
+
+
+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), 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() {
+#if (WINVER >= 0x500)
+ try {
+ ImpersonateCurrentUser icu;
+
+ vlog.debug("disable desktop effects");
+
+ SysParamsInfo(SPI_SETFONTSMOOTHING, FALSE, 0, SPIF_SENDCHANGE);
+ 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);
+ }
+ restoreEffects = true;
+
+ } catch (rdr::Exception& e) {
+ vlog.info(e.str());
+ }
+#else
+ vlog.info("disableffects not implemented");
+#endif
+}
+
+void CleanDesktop::enableEffects() {
+ try {
+ if (restoreEffects) {
+#if (WINVER >= 0x500)
+ 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);
+ 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("enableEffects not implemented");
+#endif
+ }
+
+ } catch (rdr::Exception& e) {
+ vlog.info(e.str());
+ }
+}
diff --git a/rfb_win32/CleanDesktop.h b/rfb_win32/CleanDesktop.h
new file mode 100644
index 00000000..73f41534
--- /dev/null
+++ b/rfb_win32/CleanDesktop.h
@@ -0,0 +1,57 @@
+/* Copyright (C) 2002-2003 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 <rfb_win32/TCharArray.h>
+
+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/rfb_win32/Clipboard.cxx b/rfb_win32/Clipboard.cxx
new file mode 100644
index 00000000..96d1e942
--- /dev/null
+++ b/rfb_win32/Clipboard.cxx
@@ -0,0 +1,199 @@
+/* Copyright (C) 2002-2004 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 <rfb_win32/Clipboard.h>
+#include <rfb_win32/WMShatter.h>
+#include <rfb/util.h>
+
+#include <rfb/LogWriter.h>
+
+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<len; i++) {
+ if (text[i] != '\x0d')
+ unix[j++] = text[i];
+ }
+ return unix;
+}
+
+char*
+unix2dos(const char* text) {
+ int len = strlen(text)+1;
+ char* dos = new char[strlen(text)*2+1];
+ int i, j=0;
+ for (i=0; i<len; i++) {
+ if (text[i] == '\x0a')
+ dos[j++] = '\x0d';
+ dos[j++] = text[i];
+ }
+ return dos;
+}
+
+
+//
+// -=- ASCII filter (in-place)
+//
+
+void
+removeNonAsciiChars(char* text) {
+ int len = strlen(text);
+ int i=0, j=0;
+ for (; i<len; i++) {
+ if ((text[i] >= 1) && (text[i] <= 127))
+ 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);
+ removeNonAsciiChars(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);
+ removeNonAsciiChars(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/rfb_win32/Clipboard.h b/rfb_win32/Clipboard.h
new file mode 100644
index 00000000..57297e1e
--- /dev/null
+++ b/rfb_win32/Clipboard.h
@@ -0,0 +1,66 @@
+/* Copyright (C) 2002-2003 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 <rfb/SDesktop.h>
+#include <rfb/Threading.h>
+#include <rfb_win32/MsgWindow.h>
+#include <rfb_win32/DeviceFrameBuffer.h>
+
+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/rfb_win32/CurrentUser.cxx b/rfb_win32/CurrentUser.cxx
new file mode 100644
index 00000000..1a244853
--- /dev/null
+++ b/rfb_win32/CurrentUser.cxx
@@ -0,0 +1,93 @@
+/* Copyright (C) 2002-2004 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 <windows.h>
+#include <lmcons.h>
+#include <rfb_win32/CurrentUser.h>
+#include <rfb_win32/Service.h>
+#include <rfb_win32/OSVersion.h>
+
+using namespace rfb;
+using namespace win32;
+
+
+CurrentUserToken::CurrentUserToken() : isValid_(false) {
+ // - If the OS doesn't support security, ignore it
+ if (!isServiceProcess()) {
+ // - Running in User-Mode - just get our current token
+ if (!OpenProcessToken(GetCurrentProcess(), GENERIC_ALL, &h)) {
+ DWORD err = GetLastError();
+ if (err != ERROR_CALL_NOT_IMPLEMENTED)
+ throw rdr::SystemException("OpenProcessToken failed", GetLastError());
+ }
+ isValid_ = true;
+ } else {
+ // - Under XP/2003 and above, we can just ask the operating system
+ typedef BOOL (WINAPI *WTSQueryUserToken_proto)(ULONG, PHANDLE);
+ DynamicFn<WTSQueryUserToken_proto> _WTSQueryUserToken(_T("wtsapi32.dll"), "WTSQueryUserToken");
+ if (_WTSQueryUserToken.isValid()) {
+ (*_WTSQueryUserToken)(-1, &h);
+ isValid_ = true;
+ } else {
+ // - Under NT/2K we have to resort to a nasty hack...
+ HWND tray = FindWindow(_T("Shell_TrayWnd"), 0);
+ if (!tray)
+ return;
+ DWORD processId = 0;
+ GetWindowThreadProcessId(tray, &processId);
+ if (!processId)
+ return;
+ Handle process = OpenProcess(MAXIMUM_ALLOWED, FALSE, processId);
+ if (!process.h)
+ return;
+ OpenProcessToken(process, MAXIMUM_ALLOWED, &h);
+ isValid_ = true;
+ }
+ }
+}
+
+
+ImpersonateCurrentUser::ImpersonateCurrentUser() {
+ RegCloseKey(HKEY_CURRENT_USER);
+ if (!isServiceProcess())
+ return;
+ if (!token.isValid())
+ throw rdr::Exception("CurrentUserToken is not valid");
+ 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);
+ }
+}
+
+
+UserName::UserName() : TCharArray(UNLEN+1) {
+ DWORD len = UNLEN+1;
+ if (!GetUserName(buf, &len))
+ throw rdr::SystemException("GetUserName failed", GetLastError());
+}
diff --git a/rfb_win32/CurrentUser.h b/rfb_win32/CurrentUser.h
new file mode 100644
index 00000000..469946b6
--- /dev/null
+++ b/rfb_win32/CurrentUser.h
@@ -0,0 +1,78 @@
+/* Copyright (C) 2002-2004 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 <rfb_win32/Service.h>
+#include <rfb_win32/OSVersion.h>
+#include <rfb_win32/Win32Util.h>
+
+namespace rfb {
+
+ namespace win32 {
+
+ // CurrentUserToken
+ // h == 0
+ // if platform is not NT *or* unable to get token
+ // *or* if process is hosting service.
+ // h != 0
+ // if process is hosting service *and*
+ // if platform is NT *and* able to get token.
+ // isValid() == true
+ // if platform is not NT *or* token is valid.
+
+ struct CurrentUserToken : public Handle {
+ CurrentUserToken();
+ bool isValid() const {return isValid_;};
+ private:
+ bool isValid_;
+ };
+
+ // 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.
+
+ struct UserName : public TCharArray {
+ UserName();
+ };
+
+ }
+
+}
+
+#endif
diff --git a/rfb_win32/DIBSectionBuffer.cxx b/rfb_win32/DIBSectionBuffer.cxx
new file mode 100644
index 00000000..21743768
--- /dev/null
+++ b/rfb_win32/DIBSectionBuffer.cxx
@@ -0,0 +1,221 @@
+/* Copyright (C) 2002-2004 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 <rfb/LogWriter.h>
+
+#include <rfb_win32/DIBSectionBuffer.h>
+#include <rfb_win32/Win32Util.h>
+
+
+using namespace rfb;
+using namespace win32;
+
+static LogWriter vlog("DIBSection");
+
+
+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.debug("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;
+
+ vlog.debug("area=%d, bpp=%d", width_ * height_, format.bpp);
+
+ // 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;
+ }
+ } else {
+ // Set the DIBSection's palette
+ refreshPalette();
+ }
+ }
+}
+
+void DIBSectionBuffer::refreshPalette() {
+ if (format.bpp > 8) {
+ vlog.error("refresh palette called for truecolour DIB");
+ return;
+ }
+ vlog.debug("refreshing palette");
+ if (device)
+ copyPaletteToDIB(palette, device, bitmap);
+ else
+ copyPaletteToDIB(palette, WindowDC(window), bitmap);
+}
+
+
diff --git a/rfb_win32/DIBSectionBuffer.h b/rfb_win32/DIBSectionBuffer.h
new file mode 100644
index 00000000..51e2da31
--- /dev/null
+++ b/rfb_win32/DIBSectionBuffer.h
@@ -0,0 +1,87 @@
+/* Copyright (C) 2002-2003 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__
+
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+
+#include <rfb/PixelBuffer.h>
+#include <rfb/Region.h>
+#include <rfb/ColourMap.h>
+#include <rfb/Exception.h>
+
+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/rfb_win32/DeviceFrameBuffer.cxx b/rfb_win32/DeviceFrameBuffer.cxx
new file mode 100644
index 00000000..a4d10211
--- /dev/null
+++ b/rfb_win32/DeviceFrameBuffer.cxx
@@ -0,0 +1,298 @@
+/* Copyright (C) 2002-2004 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.
+
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+#include <assert.h>
+
+#include <vector>
+
+#include <rfb/LogWriter.h>
+#include <rfb/Exception.h>
+#include <rdr/types.h>
+#include <rfb/VNCServer.h>
+#include <rfb_win32/DeviceFrameBuffer.h>
+#include <rfb_win32/Win32Util.h>
+#include <rfb_win32/OSVersion.h>
+
+namespace rfb {
+
+namespace win32 {
+
+static LogWriter vlog("FrameBuffer");
+
+
+// -=- 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
+ RECT cr;
+ if (!GetClipBox(device, &cr))
+ throw rdr::SystemException("GetClipBox", GetLastError());
+ deviceCoords = Rect(cr.left, cr.top, cr.right, cr.bottom);
+ 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 ? 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<Rect> rects;
+ std::vector<Rect>::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 colour 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, 0, 0, 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());
+
+ assert(maskInfo.bmPlanes == 1 && maskInfo.bmBitsPixel == 1);
+
+ // - 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.x, cursor.hotspot.y,
+ cursorBm.data, cursor.mask.buf);
+ } catch (rdr::Exception& e) {
+ vlog.error(e.str());
+ }
+}
+
+
+void
+DeviceFrameBuffer::updateColourMap() {
+ if (!format.trueColour)
+ copyDevicePaletteToDIB(device, this);
+}
+
+}; // namespace win32
+
+}; // namespace rfb
diff --git a/rfb_win32/DeviceFrameBuffer.h b/rfb_win32/DeviceFrameBuffer.h
new file mode 100644
index 00000000..5e97b222
--- /dev/null
+++ b/rfb_win32/DeviceFrameBuffer.h
@@ -0,0 +1,121 @@
+/* Copyright (C) 2002-2003 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__
+
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+
+#include <rfb_win32/DIBSectionBuffer.h>
+#include <rfb/Cursor.h>
+#include <rfb/Region.h>
+#include <rfb/Exception.h>
+
+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 &region);
+
+ // - 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;}
+
+ 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;
+ };
+
+ // -=- createDisplayDeviceFrameBuffer
+ // createDisplayDeviceFrameBuffer must be passed the name of a display device,
+ // and will return a new FrameBuffer object attached to that display
+ // device.
+ // If the device name is not specified then the default display is
+ // returned.
+
+ DeviceFrameBuffer *createDisplayDeviceFrameBuffer(const char *device=0);
+
+ // -=- createDisplayFrameBuffers
+ // Creates a set of framebuffers, one for each available display
+ // device.
+
+ typedef std::vector<DeviceFrameBuffer *> DeviceFrameBuffers;
+ void createDisplayDeviceFrameBuffers(DeviceFrameBuffers *fbs);
+
+ };
+
+};
+
+#endif // __RFB_WIN32_DEVICE_FRAME_BUFFER_H__
diff --git a/rfb_win32/Dialog.cxx b/rfb_win32/Dialog.cxx
new file mode 100644
index 00000000..157cf5fe
--- /dev/null
+++ b/rfb_win32/Dialog.cxx
@@ -0,0 +1,353 @@
+/* Copyright (C) 2002-2003 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 <rfb_win32/Dialog.h>
+#include <rfb_win32/TCharArray.h>
+#include <rfb/LogWriter.h>
+#include <rdr/Exception.h>
+#include <rfb_win32/Win32Util.h>
+#ifdef _DIALOG_CAPTURE
+#include <rfb_win32/DeviceFrameBuffer.h>
+#include <extra/LoadBMP.cxx>
+#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<PropSheetPage*> pages_, HICON icon_)
+: title(tstrDup(title_)), inst(inst_), pages(pages_), alreadyShowing(0), handle(0), icon(icon_) {
+}
+
+PropSheet::~PropSheet() {
+}
+
+
+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<PropSheetPage*>::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_NOCONTEXTHELP)*/;
+ 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);
+
+#if (WINVER >= 0x0500)
+#ifdef _DIALOG_CAPTURE
+ // *** NOT TESTED
+ 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());
+ char filename[256];
+ sprintf(filename, "%s\\capture%d.bmp", tmpdir, i);
+ saveBMP(filename, &fb);
+ i++;
+ }
+ ReleaseDC(handle, dc);
+ } else {
+#endif
+#endif
+ try {
+ if (owner)
+ EnableWindow(owner, FALSE);
+ // Run the PropertySheet
+ MSG msg;
+ while (GetMessage(&msg, 0, 0, 0)) {
+ if (!PropSheet_IsDialogMessage(handle, &msg))
+ DispatchMessage(&msg);
+ if (!PropSheet_GetCurrentPageHwnd(handle))
+ break;
+ }
+ if (owner)
+ EnableWindow(owner, TRUE);
+ } catch (...) {
+ if (owner)
+ EnableWindow(owner, TRUE);
+ throw;
+ }
+#if (WINVER >= 0x0500)
+#ifdef _DIALOG_CAPTURE
+ }
+#endif
+#endif
+
+ plog.info("finished %lx", handle);
+
+ DestroyWindow(handle);
+ handle = 0;
+ alreadyShowing = false;
+
+ // Clear up the pages' GDI objects
+ for (pspi=pages.begin(); pspi!=pages.end(); pspi++)
+ (*pspi)->setPropSheet(0);
+ delete [] hpages; hpages = 0;
+
+ return true;
+ } catch (rdr::Exception) {
+ alreadyShowing = false;
+
+ std::list<PropSheetPage*>::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<PropSheetPage*>::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<PropSheetPage*>::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/rfb_win32/Dialog.h b/rfb_win32/Dialog.h
new file mode 100644
index 00000000..d46133a0
--- /dev/null
+++ b/rfb_win32/Dialog.h
@@ -0,0 +1,159 @@
+/* Copyright (C) 2002-2003 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__
+
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+#include <prsht.h>
+#include <list>
+#include <rfb_win32/TCharArray.h>
+
+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<PropSheetPage*> 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<PropSheetPage*> 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/rfb_win32/IntervalTimer.h b/rfb_win32/IntervalTimer.h
new file mode 100644
index 00000000..645d0ee9
--- /dev/null
+++ b/rfb_win32/IntervalTimer.h
@@ -0,0 +1,70 @@
+/* Copyright (C) 2002-2003 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/rfb_win32/LaunchProcess.cxx b/rfb_win32/LaunchProcess.cxx
new file mode 100644
index 00000000..0a48d60c
--- /dev/null
+++ b/rfb_win32/LaunchProcess.cxx
@@ -0,0 +1,92 @@
+/* Copyright (C) 2002-2004 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 <rfb_win32/LaunchProcess.h>
+#include <rfb_win32/Win32Util.h>
+
+#include <rfb/util.h>
+
+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) {
+ if (procInfo.hProcess && (WaitForSingleObject(procInfo.hProcess, 0) != WAIT_OBJECT_0))
+ return;
+ await();
+
+ // - 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 VNC server
+ // 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);
+#ifdef _DEBUG
+ DWORD flags = CREATE_NEW_CONSOLE;
+#else
+ DWORD flags = CREATE_NO_WINDOW;
+#endif
+ BOOL success;
+ if (userToken)
+ 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::await() {
+ if (!procInfo.hProcess)
+ return;
+ WaitForSingleObject(procInfo.hProcess, INFINITE);
+ CloseHandle(procInfo.hProcess);
+ CloseHandle(procInfo.hThread);
+ memset(&procInfo, 0, sizeof(procInfo));
+}
+
diff --git a/rfb_win32/LaunchProcess.h b/rfb_win32/LaunchProcess.h
new file mode 100644
index 00000000..6fd34e9c
--- /dev/null
+++ b/rfb_win32/LaunchProcess.h
@@ -0,0 +1,59 @@
+/* Copyright (C) 2002-2003 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__
+
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+
+#include <rfb_win32/TCharArray.h>
+
+namespace rfb {
+
+ namespace win32 {
+
+ class LaunchProcess {
+ public:
+ LaunchProcess(const TCHAR* exeName_, const TCHAR* params);
+ ~LaunchProcess();
+
+ // If userToken is 0 then starts as current user, otherwise
+ // starts as the specified user. userToken must be a primary token.
+ void start(HANDLE userToken);
+
+ // Wait for the process to quit, and close the handles to it.
+ void await();
+
+ PROCESS_INFORMATION procInfo;
+ protected:
+ TCharArray exeName;
+ TCharArray params;
+ };
+
+
+ };
+
+};
+
+#endif
diff --git a/rfb_win32/MsgWindow.cxx b/rfb_win32/MsgWindow.cxx
new file mode 100644
index 00000000..519d6ab9
--- /dev/null
+++ b/rfb_win32/MsgWindow.cxx
@@ -0,0 +1,116 @@
+/* Copyright (C) 2002-2004 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 <rfb_win32/MsgWindow.h>
+#include <rfb_win32/WMShatter.h>
+#include <rfb/LogWriter.h>
+#include <rdr/Exception.h>
+#include <malloc.h>
+#include <tchar.h>
+
+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);
+} \ No newline at end of file
diff --git a/rfb_win32/MsgWindow.h b/rfb_win32/MsgWindow.h
new file mode 100644
index 00000000..94baca38
--- /dev/null
+++ b/rfb_win32/MsgWindow.h
@@ -0,0 +1,55 @@
+/* Copyright (C) 2002-2003 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__
+
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+
+#include <rfb_win32/TCharArray.h>
+
+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/rfb_win32/OSVersion.cxx b/rfb_win32/OSVersion.cxx
new file mode 100644
index 00000000..1976098f
--- /dev/null
+++ b/rfb_win32/OSVersion.cxx
@@ -0,0 +1,47 @@
+/* Copyright (C) 2002-2003 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 <rfb_win32/OSVersion.h>
+#include <rdr/Exception.h>
+#include <tchar.h>
+
+using namespace rfb;
+using namespace win32;
+
+
+OSVersionInfo::OSVersionInfo() {
+ // Get OS Version Info
+ ZeroMemory(static_cast<OSVERSIONINFO*>(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/rfb_win32/OSVersion.h b/rfb_win32/OSVersion.h
new file mode 100644
index 00000000..1d529431
--- /dev/null
+++ b/rfb_win32/OSVersion.h
@@ -0,0 +1,54 @@
+/* Copyright (C) 2002-2003 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__
+
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+
+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/rfb_win32/RegConfig.cxx b/rfb_win32/RegConfig.cxx
new file mode 100644
index 00000000..fcb309b5
--- /dev/null
+++ b/rfb_win32/RegConfig.cxx
@@ -0,0 +1,151 @@
+/* Copyright (C) 2002-2003 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 <malloc.h>
+
+#include <rfb_win32/RegConfig.h>
+#include <rfb/LogWriter.h>
+#include <rfb/util.h>
+#include <rdr/HexOutStream.h>
+
+using namespace rfb;
+using namespace rfb::win32;
+
+
+static LogWriter vlog("RegConfig");
+
+
+class rfb::win32::RegReaderThread : public Thread {
+public:
+ RegReaderThread(RegistryReader& reader, const HKEY key);
+ ~RegReaderThread();
+ virtual void run();
+ virtual Thread* join();
+protected:
+ RegistryReader& reader;
+ RegKey key;
+ HANDLE event;
+};
+
+RegReaderThread::RegReaderThread(RegistryReader& reader_, const HKEY key_) : Thread("RegConfig"), reader(reader_), key(key_) {
+}
+
+RegReaderThread::~RegReaderThread() {
+}
+
+void
+RegReaderThread::run() {
+ vlog.debug("RegReaderThread started");
+ while (key) {
+ // - Wait for changes
+ vlog.debug("waiting for changes");
+ key.awaitChange(true, REG_NOTIFY_CHANGE_NAME | REG_NOTIFY_CHANGE_LAST_SET);
+
+ // - Load settings
+ RegistryReader::loadRegistryConfig(key);
+
+ // - Notify specified thread of changes
+ if (reader.notifyThread)
+ PostThreadMessage(reader.notifyThread->getThreadId(),
+ reader.notifyMsg.message,
+ reader.notifyMsg.wParam,
+ reader.notifyMsg.lParam);
+ else if (reader.notifyWindow)
+ PostMessage(reader.notifyWindow,
+ reader.notifyMsg.message,
+ reader.notifyMsg.wParam,
+ reader.notifyMsg.lParam);
+ }
+}
+
+Thread*
+RegReaderThread::join() {
+ RegKey old_key = key;
+ key.close();
+ if ((HKEY)old_key) {
+ // *** Closing the key doesn't always seem to work
+ // Writing to it always will, instead...
+ vlog.debug("closing key");
+ old_key.setString(_T("dummy"), _T(""));
+ }
+ return Thread::join();
+}
+
+
+RegistryReader::RegistryReader() : thread(0), notifyThread(0) {
+ memset(&notifyMsg, 0, sizeof(notifyMsg));
+}
+
+RegistryReader::~RegistryReader() {
+ if (thread) delete thread->join();
+}
+
+bool RegistryReader::setKey(const HKEY rootkey, const TCHAR* keyname) {
+ if (thread) delete thread->join();
+ thread = 0;
+
+ RegKey key;
+ try {
+ key.createKey(rootkey, keyname);
+ loadRegistryConfig(key);
+ } catch (rdr::Exception& e) {
+ vlog.debug(e.str());
+ return false;
+ }
+ thread = new RegReaderThread(*this, key);
+ if (thread) thread->start();
+ return true;
+}
+
+void
+RegistryReader::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());
+ }
+}
+
+bool RegistryReader::setNotifyThread(Thread* thread, UINT winMsg, WPARAM wParam, LPARAM lParam) {
+ notifyMsg.message = winMsg;
+ notifyMsg.wParam = wParam;
+ notifyMsg.lParam = lParam;
+ notifyThread = thread;
+ notifyWindow = 0;
+ return true;
+}
+
+bool RegistryReader::setNotifyWindow(HWND window, UINT winMsg, WPARAM wParam, LPARAM lParam) {
+ notifyMsg.message = winMsg;
+ notifyMsg.wParam = wParam;
+ notifyMsg.lParam = lParam;
+ notifyWindow = window;
+ notifyThread = 0;
+ return true;
+}
+
diff --git a/rfb_win32/RegConfig.h b/rfb_win32/RegConfig.h
new file mode 100644
index 00000000..3fced85e
--- /dev/null
+++ b/rfb_win32/RegConfig.h
@@ -0,0 +1,56 @@
+/* Copyright (C) 2002-2003 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 <rfb/Threading.h>
+#include <rfb/Configuration.h>
+
+#include <rfb_win32/Registry.h>
+
+namespace rfb {
+
+ namespace win32 {
+
+ class RegistryReader {
+ public:
+ RegistryReader();
+ ~RegistryReader();
+ bool setKey(const HKEY rootkey, const TCHAR* keyname);
+ bool setNotifyThread(Thread* thread, UINT winMsg, WPARAM wParam=0, LPARAM lParam=0);
+ bool setNotifyWindow(HWND window, UINT winMsg, WPARAM wParam=0, LPARAM lParam=0);
+ static void loadRegistryConfig(RegKey& key);
+ protected:
+ friend class RegReaderThread;
+ Thread* thread;
+ Thread* notifyThread;
+ HWND notifyWindow;
+ MSG notifyMsg;
+ };
+
+ };
+
+};
+
+#endif // __RFB_WIN32_REG_CONFIG_H__
diff --git a/rfb_win32/Registry.cxx b/rfb_win32/Registry.cxx
new file mode 100644
index 00000000..de9238f7
--- /dev/null
+++ b/rfb_win32/Registry.cxx
@@ -0,0 +1,272 @@
+/* Copyright (C) 2002-2004 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 <rfb/LogWriter.h>
+#include <rfb_win32/Registry.h>
+#include <rdr/MemOutStream.h>
+#include <rdr/HexOutstream.h>
+#include <rdr/HexInStream.h>
+#include <rfb_win32/Security.h>
+
+#include <stdlib.h>
+
+// 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) {
+ 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);
+ }
+ 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);
+ 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) {
+ 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) const {
+ LONG result = RegNotifyChangeKeyValue(key, watchSubTree, filter, 0, FALSE);
+ 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;
+}
+
+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) {
+ // We must terminate the string, just to be sure. Stupid Win32...
+ int len = length/sizeof(TCHAR);
+ TCharArray str(len+1);
+ memcpy(str.buf, data.buf, length);
+ str.buf[len] = 0;
+ return str.takeBuf();
+ } else {
+ return tstrDup(_T(""));
+ }
+ case REG_DWORD:
+ {
+ TCharArray tmp(16);
+ _stprintf(tmp.buf, _T("%u"), *((DWORD*)data.buf));
+ return tmp.takeBuf();
+ }
+ default:
+ throw rdr::Exception("unsupported registry type");
+ }
+}
+
+bool RegKey::isValue(const TCHAR* valname) const {
+ try {
+ TCharArray tmp = getRepresentation(valname);
+ return true;
+ } catch(rdr::Exception) {
+ return false;
+ }
+}
+
+const TCHAR* RegKey::getValueName(int i) {
+ DWORD maxValueNameLen;
+ LONG result = RegQueryInfoKey(key, 0, 0, 0, 0, 0, 0, 0, &maxValueNameLen, 0, 0, 0);
+ if (result != ERROR_SUCCESS)
+ throw rdr::SystemException("RegQueryInfoKey", result);
+ if (valueNameBufLen < maxValueNameLen + 1) {
+ valueNameBufLen = maxValueNameLen + 1;
+ delete [] valueName.buf;
+ valueName.buf = new TCHAR[valueNameBufLen];
+ }
+ DWORD length = valueNameBufLen;
+ result = RegEnumValue(key, i, valueName.buf, &length, NULL, 0, 0, 0);
+ if (result == ERROR_NO_MORE_ITEMS) return 0;
+ if (result != ERROR_SUCCESS)
+ throw rdr::SystemException("RegEnumValue", result);
+ return valueName.buf;
+}
diff --git a/rfb_win32/Registry.h b/rfb_win32/Registry.h
new file mode 100644
index 00000000..1998c497
--- /dev/null
+++ b/rfb_win32/Registry.h
@@ -0,0 +1,111 @@
+/* Copyright (C) 2002-2004 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.h
+
+// C++ wrappers around the Win32 Registry APIs
+
+#ifndef __RFB_WIN32_REGISTRY_H__
+#define __RFB_WIN32_REGISTRY_H__
+
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+
+#include <rfb_win32/Security.h>
+#include <rfb/util.h>
+
+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);
+ protected:
+ HKEY operator=(const RegKey& k);
+ HKEY operator=(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;
+
+
+ void awaitChange(bool watchSubTree, DWORD filter) 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 number "i"
+ // If there are fewer than "i" values then return 0
+ // NAME IS OWNED BY RegKey OBJECT!
+ const TCHAR* getValueName(int i);
+
+ operator HKEY() const;
+ protected:
+ HKEY key;
+ bool freeKey;
+ TCharArray valueName;
+ DWORD valueNameBufLen;
+ };
+
+ };
+
+};
+
+#endif // __RFB_WIN32_REG_CONFIG_H__
diff --git a/rfb_win32/SDisplay.cxx b/rfb_win32/SDisplay.cxx
new file mode 100644
index 00000000..6fa3ff0e
--- /dev/null
+++ b/rfb_win32/SDisplay.cxx
@@ -0,0 +1,612 @@
+/* Copyright (C) 2002-2004 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 <assert.h>
+
+#include <rfb_win32/SDisplay.h>
+#include <rfb_win32/Service.h>
+#include <rfb_win32/WMShatter.h>
+#include <rfb_win32/osVersion.h>
+#include <rfb_win32/Win32Util.h>
+#include <rfb_win32/IntervalTimer.h>
+#include <rfb_win32/CleanDesktop.h>
+
+#include <rfb/util.h>
+#include <rfb/LogWriter.h>
+#include <rfb/Exception.h>
+
+#include <rfb/Configuration.h>
+
+using namespace rdr;
+using namespace rfb;
+using namespace rfb::win32;
+
+static LogWriter vlog("SDisplay");
+
+// - SDisplay-specific configuration options
+
+BoolParameter rfb::win32::SDisplay::use_hooks("UseHooks",
+ "Set hooks in the operating system to capture display updates more efficiently", true);
+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");
+
+BoolParameter rfb::win32::SDisplay::removeWallpaper("RemoveWallpaper",
+ "Remove the desktop wallpaper when the server in in use.", false);
+BoolParameter rfb::win32::SDisplay::removePattern("RemovePattern",
+ "Remove the desktop background pattern when the server in in use.", false);
+BoolParameter rfb::win32::SDisplay::disableEffects("DisableEffects",
+ "Disable desktop user interface effects when the server is in use.", false);
+
+
+// - WM_TIMER ID values
+
+#define TIMER_CURSOR 1
+#define TIMER_UPDATE 2
+#define TIMER_UPDATE_AND_POLL 3
+
+
+// -=- Polling settings
+
+const int POLLING_SEGMENTS = 16;
+
+const int FG_POLLING_FPS = 20;
+const int FG_POLLING_FS_INTERVAL = 1000 / FG_POLLING_FPS;
+const int FG_POLLING_INTERVAL = FG_POLLING_FS_INTERVAL / POLLING_SEGMENTS;
+
+const int BG_POLLING_FS_INTERVAL = 5000;
+const int BG_POLLING_INTERVAL = BG_POLLING_FS_INTERVAL / POLLING_SEGMENTS;
+
+
+//////////////////////////////////////////////////////////////////////////////
+//
+// SDisplayCore
+//
+
+// The SDisplay Core object is created by SDisplay's start() method
+// and deleted by its stop() method.
+// The Core must be created in the current input desktop in order
+// to operate - SDisplay is responsible for ensuring that.
+// The structures contained in the Core are manipulated directly
+// by the SDisplay, which is also responsible for detecting when
+// a desktop-switch is required.
+
+class rfb::win32::SDisplayCore : public MsgWindow {
+public:
+ SDisplayCore(SDisplay* display);
+ ~SDisplayCore();
+
+ void setPixelBuffer(DeviceFrameBuffer* pb_);
+
+ bool isRestartRequired();
+
+ virtual LRESULT processMessage(UINT msg, WPARAM wParam, LPARAM lParam);
+
+ // -=- Timers
+ IntervalTimer pollTimer;
+ IntervalTimer cursorTimer;
+
+ // -=- Input handling
+ rfb::win32::SPointer ptr;
+ rfb::win32::SKeyboard kbd;
+ rfb::win32::Clipboard clipboard;
+
+ // -=- Hook handling objects used outside thread run() method
+ WMCopyRect wm_copyrect;
+ WMPoller wm_poller;
+ WMCursor cursor;
+ WMMonitor wm_monitor;
+ WMHooks wm_hooks;
+ WMBlockInput wm_input;
+
+ // -=- Tidying the desktop
+ CleanDesktop cleanDesktop;
+ bool isWallpaperRemoved;
+ bool isPatternRemoved;
+ bool areEffectsDisabled;
+
+ // -=- Full screen polling
+ int poll_next_y;
+ int poll_y_increment;
+
+ // Are we using hooks?
+ bool use_hooks;
+ bool using_hooks;
+
+ // State of the display object
+ SDisplay* display;
+};
+
+SDisplayCore::SDisplayCore(SDisplay* display_)
+: MsgWindow(_T("SDisplayCore")), display(display_),
+ using_hooks(0), use_hooks(rfb::win32::SDisplay::use_hooks),
+ isWallpaperRemoved(rfb::win32::SDisplay::removeWallpaper),
+ isPatternRemoved(rfb::win32::SDisplay::removePattern),
+ areEffectsDisabled(rfb::win32::SDisplay::disableEffects),
+ pollTimer(getHandle(), TIMER_UPDATE_AND_POLL),
+ cursorTimer(getHandle(), TIMER_CURSOR) {
+ setPixelBuffer(display->pb);
+}
+
+SDisplayCore::~SDisplayCore() {
+}
+
+void SDisplayCore::setPixelBuffer(DeviceFrameBuffer* pb) {
+ poll_y_increment = (display->pb->height()+POLLING_SEGMENTS-1)/POLLING_SEGMENTS;
+ poll_next_y = display->screenRect.tl.y;
+ wm_hooks.setClipRect(display->screenRect);
+ wm_copyrect.setClipRect(display->screenRect);
+ wm_poller.setClipRect(display->screenRect);
+}
+
+
+bool SDisplayCore::isRestartRequired() {
+ // - We must restart the SDesktop if:
+ // 1. We are no longer in the input desktop.
+ // 2. The use_hooks setting has changed.
+
+ // - Check that we are in the input desktop
+ if (rfb::win32::desktopChangeRequired())
+ return true;
+
+ // - Check that the hooks setting hasn't changed
+ // NB: We can't just check using_hooks because that can be false
+ // because they failed, even though use_hooks is true!
+ if (use_hooks != rfb::win32::SDisplay::use_hooks)
+ 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 != rfb::win32::SDisplay::removeWallpaper) ||
+ (isPatternRemoved != rfb::win32::SDisplay::removePattern) ||
+ (areEffectsDisabled != rfb::win32::SDisplay::disableEffects))
+ return true;
+
+ return false;
+}
+
+LRESULT SDisplayCore::processMessage(UINT msg, WPARAM wParam, LPARAM lParam) {
+ switch (msg) {
+
+ case WM_TIMER:
+
+ if (display->server && display->server->clientsReadyForUpdate()) {
+
+ // - Check that the SDesktop doesn't need restarting
+ if (isRestartRequired()) {
+ display->restart();
+ return 0;
+ }
+
+ // - Action depends on the timer message type
+ switch (wParam) {
+
+ // POLL THE SCREEN
+ case TIMER_UPDATE_AND_POLL:
+ // Handle window dragging, polling of consoles, etc.
+ while (wm_poller.processEvent()) {}
+
+ // Poll the next strip of the screen (in Screen coordinates)
+ {
+ Rect pollrect = display->screenRect;
+ if (poll_next_y >= pollrect.br.y) {
+ // Yes. Reset the counter and return
+ poll_next_y = pollrect.tl.y;
+ } else {
+ // No. Poll the next section
+ pollrect.tl.y = poll_next_y;
+ poll_next_y += poll_y_increment;
+ pollrect.br.y = min(poll_next_y, pollrect.br.y);
+ display->add_changed(pollrect);
+ }
+ }
+ break;
+
+ case TIMER_CURSOR:
+ display->triggerUpdate();
+ break;
+
+ };
+
+ }
+ return 0;
+
+ };
+
+ return MsgWindow::processMessage(msg, wParam, lParam);
+}
+
+//////////////////////////////////////////////////////////////////////////////
+//
+// SDisplay
+//
+
+// -=- Constructor/Destructor
+
+SDisplay::SDisplay(const TCHAR* devName)
+ : server(0), change_tracker(true), pb(0),
+ deviceName(tstrDup(devName)), device(0), releaseDevice(false),
+ core(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");
+ server = vs;
+
+ // Switch to the current input desktop
+ // ***
+ if (rfb::win32::desktopChangeRequired()) {
+ if (!rfb::win32::changeDesktop())
+ throw rdr::Exception("unable to switch into input desktop");
+ }
+
+ // Clear the change tracker
+ change_tracker.clear();
+
+ // Create the framebuffer object
+ recreatePixelBuffer();
+
+ // Create the SDisplayCore
+ core = new SDisplayCore(this);
+ assert(core);
+
+ // Start display monitor and clipboard handler
+ core->wm_monitor.setNotifier(this);
+ core->clipboard.setNotifier(this);
+
+ // Apply desktop optimisations
+ if (removePattern)
+ core->cleanDesktop.disablePattern();
+ if (removeWallpaper)
+ core->cleanDesktop.disableWallpaper();
+ if (disableEffects)
+ core->cleanDesktop.disableEffects();
+
+ // Start hooks
+ core->wm_hooks.setClipRect(screenRect);
+ if (core->use_hooks) {
+ // core->wm_hooks.setDiagnosticRange(0, 0x400-1);
+ core->using_hooks = core->wm_hooks.setUpdateTracker(this);
+ if (!core->using_hooks)
+ vlog.debug("hook subsystem failed to initialise");
+ }
+
+ // Set up timers
+ core->pollTimer.start(core->using_hooks ? BG_POLLING_INTERVAL : FG_POLLING_INTERVAL);
+ core->cursorTimer.start(10);
+
+ // Register an interest in faked copyrect events
+ core->wm_copyrect.setUpdateTracker(&change_tracker);
+ core->wm_copyrect.setClipRect(screenRect);
+
+ // Polling of particular windows on the desktop
+ core->wm_poller.setUpdateTracker(&change_tracker);
+ core->wm_poller.setClipRect(screenRect);
+
+ vlog.debug("started");
+
+ if (statusLocation) *statusLocation = true;
+}
+
+void SDisplay::stop()
+{
+ vlog.debug("stopping");
+ if (core) {
+ // If SDisplay was actually active then perform the disconnect action
+ CharArray action = disconnectAction.getData();
+ if (stricmp(action.buf, "Logoff") == 0) {
+ ExitWindowsEx(EWX_LOGOFF, 0);
+ } else if (stricmp(action.buf, "Lock") == 0) {
+ typedef BOOL (WINAPI *_LockWorkStation_proto)();
+ DynamicFn<_LockWorkStation_proto> _LockWorkStation(_T("user32.dll"), "LockWorkStation");
+ if (_LockWorkStation.isValid())
+ (*_LockWorkStation)();
+ else
+ ExitWindowsEx(EWX_LOGOFF, 0);
+ }
+ }
+ delete core;
+ core = 0;
+ delete pb;
+ pb = 0;
+ if (device) {
+ if (releaseDevice)
+ ReleaseDC(0, device);
+ else
+ DeleteDC(device);
+ }
+ device = 0;
+ if (server)
+ server->setPixelBuffer(0);
+
+ server = 0;
+ vlog.debug("stopped");
+
+ if (statusLocation) *statusLocation = false;
+}
+
+void SDisplay::restart() {
+ vlog.debug("restarting");
+ // Close down the hooks
+ delete core;
+ core = 0;
+ try {
+ // Re-start the hooks if possible
+ start(server);
+ vlog.debug("restarted");
+ } catch (rdr::Exception& e) {
+ // If start() fails then we MUST disconnect all clients,
+ // to cause the server to stop using 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, rdr::U8 buttonmask) {
+ if (pb->getRect().contains(pos)) {
+ Point screenPos = pos.translate(screenRect.tl);
+ core->ptr.pointerEvent(screenPos, buttonmask);
+ }
+}
+
+void SDisplay::keyEvent(rdr::U32 key, bool down) {
+ core->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;
+ core->clipboard.setClipText(clip_sz.buf);
+}
+
+
+void SDisplay::framebufferUpdateRequest()
+{
+ triggerUpdate();
+}
+
+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) stop();
+ return result;
+}
+
+
+void
+SDisplay::add_changed(const Region& rgn) {
+ change_tracker.add_changed(rgn);
+ triggerUpdate();
+}
+
+void
+SDisplay::add_copied(const Region& dest, const Point& delta) {
+ change_tracker.add_copied(dest, delta);
+ triggerUpdate();
+}
+
+
+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 colourmap changed");
+ pb->updateColourMap();
+ if (server)
+ server->setColourMapEntries();
+ break;
+ default:
+ vlog.error("unknown display event received");
+ }
+}
+
+bool
+SDisplay::processEvent(HANDLE event) {
+ if (event == updateEvent) {
+ vlog.info("processEvent");
+ ResetEvent(updateEvent);
+
+ // - If the SDisplay isn't even started then quit now
+ if (!core) {
+ vlog.error("not start()ed");
+ return true;
+ }
+
+ // - Ensure that the disableLocalInputs flag is respected
+ core->wm_input.blockInputs(SDisplay::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 (core->isRestartRequired()) {
+ restart();
+ return true;
+ }
+
+ // *** window dragging can be improved - more frequent, more cunning about updates
+ while (core->wm_copyrect.processEvent()) {}
+
+ // Ensure the cursor is up to date
+ WMCursor::Info info = core->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.x, desktopPos.y);
+ try_update = true;
+
+ old_cursor = info;
+ }
+
+ // Flush any changes to the server
+ try_update = flushChangeTracker() || try_update;
+ if (try_update)
+ server->tryUpdate();
+ }
+ } else {
+ CloseHandle(event);
+ return false;
+ }
+ return true;
+}
+
+
+// -=- Protected methods
+
+void
+SDisplay::recreatePixelBuffer() {
+ vlog.debug("attaching to device %s", deviceName);
+
+ // Open the specified display device
+ HDC new_device;
+ if (deviceName.buf) {
+ new_device = ::CreateDC(_T("DISPLAY"), deviceName.buf, NULL, NULL);
+ releaseDevice = false;
+ } else {
+ // If no device is specified, open entire screen.
+ // Doing this with CreateDC creates problems on multi-monitor systems.
+ new_device = ::GetDC(0);
+ releaseDevice = true;
+ }
+ if (!new_device)
+ throw SystemException("cannot open the display", GetLastError());
+
+ // Get the coordinates of the entire virtual display
+ Rect newScreenRect;
+ {
+ WindowDC rootDC(0);
+ RECT r;
+ if (!GetClipBox(rootDC, &r))
+ throw rdr::SystemException("GetClipBox", GetLastError());
+ newScreenRect = Rect(r.left, r.top, r.right, r.bottom);
+ }
+
+ // Create a DeviceFrameBuffer attached to it
+ DeviceFrameBuffer* new_buffer = new DeviceFrameBuffer(new_device);
+
+ // Has anything actually changed about the screen or the buffer?
+ if (!pb ||
+ (!newScreenRect.equals(screenRect)) ||
+ (!new_buffer->getPF().equal(pb->getPF())))
+ {
+ // Yes. Update the buffer state.
+ screenRect = newScreenRect;
+ vlog.debug("creating pixel buffer for device");
+
+ // Flush any existing changes to the server
+ flushChangeTracker();
+
+ // Replace the old PixelBuffer
+ if (pb) delete pb;
+ if (device) DeleteDC(device);
+ 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 SDisplayCore if required
+ if (core)
+ core->setPixelBuffer(pb);
+
+ // Inform the server of the changes
+ if (server)
+ server->setPixelBuffer(pb);
+
+ } else {
+ delete new_buffer;
+ DeleteDC(new_device);
+ }
+}
+
+bool SDisplay::flushChangeTracker() {
+ if (change_tracker.is_empty())
+ return false;
+ // Translate the update coordinates from Screen coords to Desktop
+ change_tracker.translate(screenRect.tl.negate());
+ // Flush the updates through
+ change_tracker.get_update(*server);
+ change_tracker.clear();
+ return true;
+}
+
+void SDisplay::triggerUpdate() {
+ if (core)
+ SetEvent(updateEvent);
+}
diff --git a/rfb_win32/SDisplay.h b/rfb_win32/SDisplay.h
new file mode 100644
index 00000000..c4c08bf3
--- /dev/null
+++ b/rfb_win32/SDisplay.h
@@ -0,0 +1,149 @@
+/* Copyright (C) 2002-2004 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.
+
+// *** THIS INTERFACE NEEDS TIDYING TO SEPARATE COORDINATE SYSTEMS BETTER ***
+
+#ifndef __RFB_SDISPLAY_H__
+#define __RFB_SDISPLAY_H__
+
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+
+#include <rfb/SDesktop.h>
+#include <rfb/UpdateTracker.h>
+#include <rfb/Configuration.h>
+#include <rfb/util.h>
+
+#include <winsock2.h>
+#include <rfb_win32/Win32Util.h>
+#include <rfb_win32/SocketManager.h>
+#include <rfb_win32/DeviceFrameBuffer.h>
+#include <rfb_win32/SInput.h>
+#include <rfb_win32/Clipboard.h>
+#include <rfb_win32/MsgWindow.h>
+#include <rfb_win32/WMCursor.h>
+#include <rfb_win32/WMHooks.h>
+#include <rfb_win32/WMNotifier.h>
+#include <rfb_win32/WMWindowCopyRect.h>
+#include <rfb_win32/WMPoller.h>
+
+namespace rfb {
+
+ namespace win32 {
+
+ //
+ // -=- SDisplay
+ //
+
+ class SDisplayCore;
+
+ class SDisplay : public SDesktop,
+ WMMonitor::Notifier,
+ Clipboard::Notifier,
+ UpdateTracker,
+ public SocketManager::EventHandler
+ {
+ public:
+ SDisplay(const TCHAR* device=0);
+ virtual ~SDisplay();
+
+ // -=- SDesktop interface
+
+ virtual void start(VNCServer* vs);
+ virtual void stop();
+ virtual void pointerEvent(const Point& pos, rdr::U8 buttonmask);
+ virtual void keyEvent(rdr::U32 key, bool down);
+ virtual void clientCutText(const char* str, int len);
+ virtual void framebufferUpdateRequest();
+ virtual Point getFbSize();
+
+ // -=- UpdateTracker
+
+ virtual void add_changed(const Region& rgn);
+ virtual void add_copied(const Region& dest, const Point& delta);
+
+ // -=- 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 bool processEvent(HANDLE event);
+
+ // -=- Notification of whether or not SDisplay is started
+
+ void setStatusLocation(bool* status) {statusLocation = status;}
+
+ friend class SDisplayCore;
+
+ static BoolParameter use_hooks;
+ static BoolParameter disableLocalInputs;
+ static StringParameter disconnectAction;
+ static BoolParameter removeWallpaper;
+ static BoolParameter removePattern;
+ static BoolParameter disableEffects;
+
+ protected:
+ void restart();
+ void recreatePixelBuffer();
+ bool flushChangeTracker(); // true if flushed, false if empty
+
+ void triggerUpdate();
+
+ VNCServer* server;
+
+ // -=- Display pixel buffer
+ DeviceFrameBuffer* pb;
+ TCharArray deviceName;
+ HDC device;
+ bool releaseDevice;
+
+ // -=- The coordinates of Window's entire virtual Screen
+ Rect screenRect;
+
+ // -=- All changes are collected in Display coords and merged
+ SimpleUpdateTracker change_tracker;
+
+ // -=- Internal SDisplay implementation
+ SDisplayCore* core;
+
+ // -=- 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/rfb_win32/SInput.cxx b/rfb_win32/SInput.cxx
new file mode 100644
index 00000000..457a8619
--- /dev/null
+++ b/rfb_win32/SInput.cxx
@@ -0,0 +1,459 @@
+/* Copyright (C) 2002-2003 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 <rfb/keysymdef.h>
+
+// * Force the windows headers to include all the SendInput stuff
+#define _WIN32_WINNT 0x401
+
+#include <rfb_win32/SInput.h>
+#include <rfb_win32/Service.h>
+#include <rfb/LogWriter.h>
+#include <rfb_win32/OSVersion.h>
+#include <rfb_win32/Win32Util.h>
+#include "keymap.h"
+
+using namespace rfb;
+
+static LogWriter vlog("SInput");
+
+
+typedef UINT (WINAPI *_SendInput_proto)(UINT, LPINPUT, int);
+static win32::DynamicFn<_SendInput_proto> _SendInput(_T("user32.dll"), "SendInput");
+
+//
+// -=- 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, rdr::U8 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<<i)) != (last_buttonmask & (1<<i))) {
+ if (buttonmask & (1<<i)) {
+ flags |= buttonDownMapping[i];
+ if (buttonDataMapping[i]) {
+ if (data) vlog.info("warning - two buttons set mouse_event data field");
+ data = buttonDataMapping[i];
+ }
+ } else {
+ flags |= buttonUpMapping[i];
+ }
+ }
+ }
+
+ last_position = pos;
+ last_buttonmask = buttonmask;
+
+ Rect primaryDisplay(0,0,GetSystemMetrics(SM_CXSCREEN),GetSystemMetrics(SM_CYSCREEN));
+ if (primaryDisplay.contains(pos)) {
+ // mouse_event wants coordinates specified as a proportion of the
+ // primary display's size, scaled to the range 0 to 65535
+ Point scaled;
+ scaled.x = (pos.x * 65535) / (primaryDisplay.width()-1);
+ scaled.y = (pos.y * 65535) / (primaryDisplay.height()-1);
+ ::mouse_event(flags, scaled.x, scaled.y, data, 0);
+ } else {
+ // The event lies outside the primary monitor. Under Win2K, we can just use
+ // SendInput, which allows us to provide coordinates scaled to the virtual desktop.
+ // SendInput is available on all multi-monitor-aware platforms.
+#ifdef SM_CXVIRTUALSCREEN
+ if (osVersion.isPlatformNT) {
+ if (!_SendInput.isValid())
+ throw rdr::Exception("SendInput not available");
+ INPUT evt;
+ evt.type = INPUT_MOUSE;
+ Point vPos(pos.x-GetSystemMetrics(SM_XVIRTUALSCREEN),
+ pos.y-GetSystemMetrics(SM_YVIRTUALSCREEN));
+ evt.mi.dx = (vPos.x * 65535) / (GetSystemMetrics(SM_CXVIRTUALSCREEN)-1);
+ evt.mi.dy = (vPos.y * 65535) / (GetSystemMetrics(SM_CYVIRTUALSCREEN)-1);
+ evt.mi.dwFlags = flags | MOUSEEVENTF_VIRTUALDESK;
+ evt.mi.dwExtraInfo = 0;
+ evt.mi.mouseData = data;
+ evt.mi.time = 0;
+ if ((*_SendInput)(1, &evt, sizeof(evt)) != 1)
+ throw rdr::SystemException("SendInput", GetLastError());
+ } else {
+ // Under Win9x, this is not addressable by either mouse_event or SendInput
+ // *** STUPID KLUDGY HACK ***
+ POINT cursorPos; GetCursorPos(&cursorPos);
+ ULONG oldSpeed, newSpeed = 10;
+ ULONG mouseInfo[3];
+ if (flags & MOUSEEVENTF_MOVE) {
+ flags &= ~MOUSEEVENTF_ABSOLUTE;
+ SystemParametersInfo(SPI_GETMOUSE, 0, &mouseInfo, 0);
+ SystemParametersInfo(SPI_GETMOUSESPEED, 0, &oldSpeed, 0);
+ vlog.debug("SPI_GETMOUSE %d, %d, %d, speed %d", mouseInfo[0], mouseInfo[1], mouseInfo[2], oldSpeed);
+ ULONG idealMouseInfo[] = {10, 0, 0};
+ SystemParametersInfo(SPI_SETMOUSESPEED, 0, &newSpeed, 0);
+ SystemParametersInfo(SPI_SETMOUSE, 0, &idealMouseInfo, 0);
+ }
+ ::mouse_event(flags, pos.x-cursorPos.x, pos.y-cursorPos.y, data, 0);
+ if (flags & MOUSEEVENTF_MOVE) {
+ SystemParametersInfo(SPI_SETMOUSE, 0, &mouseInfo, 0);
+ SystemParametersInfo(SPI_SETMOUSESPEED, 0, &oldSpeed, 0);
+ }
+ }
+#endif
+ }
+}
+
+//
+// -=- Keyboard implementation
+//
+
+BoolParameter rfb::win32::SKeyboard::deadKeyAware("DeadKeyAware",
+ "Whether to assume the viewer has already interpreted dead key sequences "
+ "into latin-1 characters", true);
+
+static bool oneShift;
+
+// The keysymToAscii table transforms a couple of awkward keysyms into their
+// ASCII equivalents.
+struct keysymToAscii_t {
+ rdr::U32 keysym;
+ rdr::U8 ascii;
+};
+
+keysymToAscii_t keysymToAscii[] = {
+ { XK_KP_Space, ' ' },
+ { XK_KP_Equal, '=' },
+};
+
+rdr::U8 latin1DeadChars[] = {
+ XK_grave, XK_acute, XK_asciicircum, XK_diaeresis, XK_degree, XK_cedilla,
+ XK_asciitilde
+};
+
+struct latin1ToDeadChars_t {
+ rdr::U8 latin1Char;
+ rdr::U8 deadChar;
+ rdr::U8 baseChar;
+};
+
+latin1ToDeadChars_t latin1ToDeadChars[] = {
+
+ { XK_Agrave, XK_grave, XK_A },
+ { XK_Egrave, XK_grave, XK_E },
+ { XK_Igrave, XK_grave, XK_I },
+ { XK_Ograve, XK_grave, XK_O },
+ { XK_Ugrave, XK_grave, XK_U },
+ { XK_agrave, XK_grave, XK_a },
+ { XK_egrave, XK_grave, XK_e },
+ { XK_igrave, XK_grave, XK_i },
+ { XK_ograve, XK_grave, XK_o},
+ { XK_ugrave, XK_grave, XK_u },
+
+ { XK_Aacute, XK_acute, XK_A },
+ { XK_Eacute, XK_acute, XK_E },
+ { XK_Iacute, XK_acute, XK_I },
+ { XK_Oacute, XK_acute, XK_O },
+ { XK_Uacute, XK_acute, XK_U },
+ { XK_Yacute, XK_acute, XK_Y },
+ { XK_aacute, XK_acute, XK_a },
+ { XK_eacute, XK_acute, XK_e },
+ { XK_iacute, XK_acute, XK_i },
+ { XK_oacute, XK_acute, XK_o},
+ { XK_uacute, XK_acute, XK_u },
+ { XK_yacute, XK_acute, XK_y },
+
+ { XK_Acircumflex, XK_asciicircum, XK_A },
+ { XK_Ecircumflex, XK_asciicircum, XK_E },
+ { XK_Icircumflex, XK_asciicircum, XK_I },
+ { XK_Ocircumflex, XK_asciicircum, XK_O },
+ { XK_Ucircumflex, XK_asciicircum, XK_U },
+ { XK_acircumflex, XK_asciicircum, XK_a },
+ { XK_ecircumflex, XK_asciicircum, XK_e },
+ { XK_icircumflex, XK_asciicircum, XK_i },
+ { XK_ocircumflex, XK_asciicircum, XK_o},
+ { XK_ucircumflex, XK_asciicircum, XK_u },
+
+ { XK_Adiaeresis, XK_diaeresis, XK_A },
+ { XK_Ediaeresis, XK_diaeresis, XK_E },
+ { XK_Idiaeresis, XK_diaeresis, XK_I },
+ { XK_Odiaeresis, XK_diaeresis, XK_O },
+ { XK_Udiaeresis, XK_diaeresis, XK_U },
+ { XK_adiaeresis, XK_diaeresis, XK_a },
+ { XK_ediaeresis, XK_diaeresis, XK_e },
+ { XK_idiaeresis, XK_diaeresis, XK_i },
+ { XK_odiaeresis, XK_diaeresis, XK_o},
+ { XK_udiaeresis, XK_diaeresis, XK_u },
+ { XK_ydiaeresis, XK_diaeresis, XK_y },
+
+ { XK_Aring, XK_degree, XK_A },
+ { XK_aring, XK_degree, XK_a },
+
+ { XK_Ccedilla, XK_cedilla, XK_C },
+ { XK_ccedilla, XK_cedilla, XK_c },
+
+ { XK_Atilde, XK_asciitilde, XK_A },
+ { XK_Ntilde, XK_asciitilde, XK_N },
+ { XK_Otilde, XK_asciitilde, XK_O },
+ { XK_atilde, XK_asciitilde, XK_a },
+ { XK_ntilde, XK_asciitilde, XK_n },
+ { XK_otilde, XK_asciitilde, XK_o },
+};
+
+// doKeyboardEvent wraps the system keybd_event function and attempts to find
+// the appropriate scancode corresponding to the supplied virtual keycode.
+
+inline void doKeyboardEvent(BYTE vkCode, DWORD flags) {
+ vlog.debug("vkCode 0x%x flags 0x%x", vkCode, flags);
+ keybd_event(vkCode, MapVirtualKey(vkCode, 0), flags, 0);
+}
+
+// KeyStateModifier is a class which helps simplify generating a "fake" press
+// or release of shift, ctrl, alt, etc. An instance of the class is created
+// for every key which may need to be pressed or released. Then either press()
+// or release() may be called to make sure that the corresponding key is in the
+// right state. The destructor of the class automatically reverts to the
+// previous state.
+
+class KeyStateModifier {
+public:
+ KeyStateModifier(int vkCode_, int flags_=0)
+ : vkCode(vkCode_), flags(flags_), pressed(false), released(false)
+ {}
+ void press() {
+ if (!(GetAsyncKeyState(vkCode) & 0x8000)) {
+ doKeyboardEvent(vkCode, flags);
+ pressed = true;
+ }
+ }
+ void release() {
+ if (GetAsyncKeyState(vkCode) & 0x8000) {
+ doKeyboardEvent(vkCode, flags | KEYEVENTF_KEYUP);
+ released = true;
+ }
+ }
+ ~KeyStateModifier() {
+ if (pressed) {
+ doKeyboardEvent(vkCode, flags | KEYEVENTF_KEYUP);
+ } else if (released) {
+ doKeyboardEvent(vkCode, flags);
+ }
+ }
+ int vkCode;
+ int flags;
+ bool pressed;
+ bool released;
+};
+
+
+// doKeyEventWithModifiers() generates a key event having first "pressed" or
+// "released" the shift, ctrl or alt modifiers if necessary.
+
+void doKeyEventWithModifiers(BYTE vkCode, BYTE modifierState, bool down)
+{
+ KeyStateModifier ctrl(VK_CONTROL);
+ KeyStateModifier alt(VK_MENU);
+ KeyStateModifier shift(VK_SHIFT);
+
+ if (down) {
+ if (modifierState & 2) ctrl.press();
+ if (modifierState & 4) alt.press();
+ if (modifierState & 1) {
+ shift.press();
+ } else {
+ shift.release();
+ }
+ }
+ doKeyboardEvent(vkCode, down ? 0 : KEYEVENTF_KEYUP);
+}
+
+
+win32::SKeyboard::SKeyboard()
+{
+ oneShift = rfb::win32::osVersion.isPlatformWindows;
+ for (int i = 0; i < sizeof(keymap) / sizeof(keymap_t); i++) {
+ vkMap[keymap[i].keysym] = keymap[i].vk;
+ extendedMap[keymap[i].keysym] = keymap[i].extended;
+ }
+
+ // Find dead characters for the current keyboard layout
+ // XXX how could we handle the keyboard layout changing?
+ BYTE keystate[256];
+ memset(keystate, 0, 256);
+ for (int j = 0; j < sizeof(latin1DeadChars); j++) {
+ SHORT s = VkKeyScan(latin1DeadChars[j]);
+ if (s != -1) {
+ BYTE vkCode = LOBYTE(s);
+ BYTE modifierState = HIBYTE(s);
+ keystate[VK_SHIFT] = (modifierState & 1) ? 0x80 : 0;
+ keystate[VK_CONTROL] = (modifierState & 2) ? 0x80 : 0;
+ keystate[VK_MENU] = (modifierState & 4) ? 0x80 : 0;
+ rdr::U8 chars[2];
+ int nchars = ToAscii(vkCode, 0, keystate, (WORD*)&chars, 0);
+ if (nchars < 0) {
+ vlog.debug("Found dead key 0x%x '%c'",
+ latin1DeadChars[j], latin1DeadChars[j]);
+ deadChars.push_back(latin1DeadChars[j]);
+ ToAscii(vkCode, 0, keystate, (WORD*)&chars, 0);
+ }
+ }
+ }
+}
+
+
+void win32::SKeyboard::keyEvent(rdr::U32 keysym, bool down)
+{
+ for (int i = 0; i < sizeof(keysymToAscii) / sizeof(keysymToAscii_t); i++) {
+ if (keysymToAscii[i].keysym == keysym) {
+ keysym = keysymToAscii[i].ascii;
+ break;
+ }
+ }
+
+ if ((keysym >= 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/rfb_win32/SInput.h b/rfb_win32/SInput.h
new file mode 100644
index 00000000..dcd779ef
--- /dev/null
+++ b/rfb_win32/SInput.h
@@ -0,0 +1,70 @@
+/* Copyright (C) 2002-2003 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 <rfb/Rect.h>
+#include <rfb/Configuration.h>
+#include <rdr/types.h>
+#include <map>
+#include <vector>
+
+namespace rfb {
+
+ class CMsgWriter;
+
+ 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, rdr::U8 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<rdr::U32,rdr::U8> vkMap;
+ std::map<rdr::U32,bool> extendedMap;
+ std::vector<rdr::U8> deadChars;
+ };
+
+ }; // win32
+
+}; // rfb
+
+#endif // __RFB_WIN32_INPUT_H__
diff --git a/rfb_win32/Security.h b/rfb_win32/Security.h
new file mode 100644
index 00000000..d92e314f
--- /dev/null
+++ b/rfb_win32/Security.h
@@ -0,0 +1,198 @@
+/* Copyright (C) 2002-2004 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 <rdr/types.h>
+#include <rdr/Exception.h>
+#include <rfb_win32/Win32Util.h>
+#include <rfb_win32/TCharArray.h>
+
+#include <lmcons.h>
+#include <Accctrl.h>
+#include <aclapi.h>
+
+#include <list>
+
+namespace rfb {
+
+ namespace win32 {
+
+ struct Trustee : public TRUSTEE {
+ Trustee(const TCHAR* name,
+ TRUSTEE_FORM form=TRUSTEE_IS_NAME,
+ TRUSTEE_TYPE type=TRUSTEE_IS_UNKNOWN)
+ {
+ pMultipleTrustee = 0;
+ MultipleTrusteeOperation = NO_MULTIPLE_TRUSTEE;
+ TrusteeForm = form;
+ TrusteeType = type;
+ ptstrName = (TCHAR*)name;
+ }
+ };
+
+ struct ExplicitAccess : public EXPLICIT_ACCESS {
+ ExplicitAccess(const TCHAR* name,
+ TRUSTEE_FORM type,
+ DWORD perms,
+ ACCESS_MODE mode,
+ DWORD inherit=0)
+ {
+ Trustee = rfb::win32::Trustee(name, type);
+ grfAccessPermissions = perms;
+ grfAccessMode = mode;
+ grfInheritance = inherit;
+ }
+ };
+
+ // Helper class for building access control lists
+ struct AccessEntries {
+ AccessEntries() : entries(0), entry_count(0) {}
+ ~AccessEntries() {delete [] entries;}
+ void 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 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 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++;
+ }
+
+ EXPLICIT_ACCESS* entries;
+ int entry_count;
+ };
+
+ // Helper class for handling SIDs
+ struct Sid {
+ Sid() : sid(0) {}
+ Sid(PSID sid_) : sid(sid_) {}
+ ~Sid() {
+ if (sid) FreeSid(sid);
+ }
+ operator PSID() const {return sid;}
+ PSID operator=(const PSID sid_) {
+ if (sid) FreeSid(sid);
+ sid = sid_;
+ }
+
+ static PSID 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());
+ return sid;
+ }
+ static PSID 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());
+ return sid;
+ }
+
+ protected:
+ PSID sid;
+ };
+
+ // 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
+ static PACL CreateACL(const AccessEntries& ae, PACL existing_acl=0) {
+ 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;
+ }
+
+ // 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.
+ static PSECURITY_DESCRIPTOR CreateSdWithDacl(const PACL dacl) {
+ SECURITY_DESCRIPTOR absSD;
+ if (!InitializeSecurityDescriptor(&absSD, SECURITY_DESCRIPTOR_REVISION))
+ throw rdr::SystemException("InitializeSecurityDescriptor", GetLastError());
+ Sid owner(Sid::SYSTEM());
+ if (!SetSecurityDescriptorOwner(&absSD, owner, FALSE))
+ throw rdr::SystemException("SetSecurityDescriptorOwner", GetLastError());
+ Sid group(Sid::Administrators());
+ 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();
+ }
+
+ }
+
+}
+
+#endif
diff --git a/rfb_win32/Service.cxx b/rfb_win32/Service.cxx
new file mode 100644
index 00000000..b00c2900
--- /dev/null
+++ b/rfb_win32/Service.cxx
@@ -0,0 +1,638 @@
+/* Copyright (C) 2002-2003 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 <rfb_win32/Service.h>
+#include <rfb_win32/MsgWindow.h>
+#include <rfb_win32/Registry.h>
+#include <rfb_win32/Win32Util.h>
+#include <rfb_win32/OSVersion.h>
+#include <rfb/Threading.h>
+#include <rfb/LogWriter.h>
+#include <rfb/util.h>
+#include <rdr/Exception.h>
+
+#include <logmessages/messages.h>
+
+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) {
+ vlog.debug("service control %u", control);
+ switch (control) {
+ case SERVICE_CONTROL_INTERROGATE:
+ service->setStatus();
+ break;
+ case SERVICE_CONTROL_PARAMCHANGE:
+ service->readParams();
+ break;
+ case SERVICE_CONTROL_SHUTDOWN:
+ service->osShuttingDown();
+ break;
+ case SERVICE_CONTROL_STOP:
+ service->setStatus(SERVICE_STOP_PENDING);
+ service->stop();
+ break;
+ }
+}
+
+
+// -=- 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());
+ service->status_handle = RegisterServiceCtrlHandler(service->getName(), serviceHandler);
+ if (!service->status_handle) {
+ vlog.error("unable to register service control handler");
+ return;
+ }
+ 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.dwWin32ExitCode = GetLastError();
+ vlog.error("unable to set service status:%u", status.dwWin32ExitCode);
+ stop();
+ }
+ 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; i<argc; i++) {
+ cmdline_len += strlen(argv[i]) + 3;
+ }
+
+ // - Add the supplied extra parameters to the command line
+ TCharArray cmdline(cmdline_len+_tcslen(defaultcmdline));
+ _stprintf(cmdline.buf, _T("\"%s\" %s"), buffer.buf, defaultcmdline);
+ for (i=0; i<argc; i++) {
+ _tcscat(cmdline.buf, _T(" \""));
+ _tcscat(cmdline.buf, TStr(argv[i]));
+ _tcscat(cmdline.buf, _T("\""));
+ }
+
+ // - Register the service
+
+ 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());
+
+
+ ServiceHandle service = CreateService(scm,
+ name, desc, SC_MANAGER_ALL_ACCESS,
+ SERVICE_WIN32_OWN_PROCESS | SERVICE_INTERACTIVE_PROCESS,
+ SERVICE_AUTO_START, SERVICE_ERROR_IGNORE,
+ cmdline.buf, NULL, NULL, NULL, NULL, NULL);
+ if (!service)
+ throw rdr::SystemException("unable to create service", GetLastError());
+
+ // - Register the event log source
+ RegKey hk, hk2;
+
+ hk2.createKey(HKEY_LOCAL_MACHINE, _T("SYSTEM\\CurrentControlSet\\Services\\EventLog\\Application"));
+ hk.createKey(hk2, name);
+
+ for (i=_tcslen(buffer.buf); i>0; 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;
+}
+
+void rfb::win32::printServiceStatus(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());
+
+ printf("Service is in the ");
+ switch (status.dwCurrentState) {
+ case SERVICE_RUNNING: printf("running"); break;
+ case SERVICE_STOPPED: printf("stopped"); break;
+ case SERVICE_STOP_PENDING: printf("stop pending"); break;
+ default: printf("unknown (%lu)", status.dwCurrentState); break;
+ };
+ printf(" state.\n");
+
+ } else {
+ HWND service_window = findServiceWindow(name);
+ printf("Service is in the ");
+ if (!service_window) printf("stopped");
+ else printf("running");
+ printf(" state.\n");
+ }
+}
+
+
+bool rfb::win32::isServiceProcess() {
+ return service != 0;
+} \ No newline at end of file
diff --git a/rfb_win32/Service.h b/rfb_win32/Service.h
new file mode 100644
index 00000000..164381a1
--- /dev/null
+++ b/rfb_win32/Service.h
@@ -0,0 +1,123 @@
+/* Copyright (C) 2002-2003 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__
+
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+
+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() = 0;
+
+ // To get notified when the service parameters change
+ virtual void readParams() = 0;
+
+ // To cause the serviceMain() routine to return
+ virtual void stop() = 0;
+
+ 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);
+ void printServiceStatus(const TCHAR* name);
+
+ // -=- Routine to determine whether the host process is running a service
+ bool isServiceProcess();
+
+ };
+
+};
+
+#endif // __RFB_WIN32_SERVICE_NT_H__
diff --git a/rfb_win32/SocketManager.cxx b/rfb_win32/SocketManager.cxx
new file mode 100644
index 00000000..6ebd5c0a
--- /dev/null
+++ b/rfb_win32/SocketManager.cxx
@@ -0,0 +1,246 @@
+/* Copyright (C) 2002-2004 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
+
+#define WIN32_LEAN_AND_MEAN
+#include <winsock2.h>
+#include <assert.h>
+
+#include <rfb/LogWriter.h>
+#include <rfb_win32/SocketManager.h>
+
+using namespace rfb;
+using namespace rfb::win32;
+
+static LogWriter vlog("SocketManager");
+
+
+// -=- SocketManager
+
+SocketManager::SocketManager() : sockets(0), events(0), nSockets(0), nAvail(0) {
+}
+
+SocketManager::~SocketManager() {
+ for (int i=0; i<nSockets; i++) {
+ if (!sockets[i].is_event)
+ WSACloseEvent(events[i]);
+ }
+ delete [] events;
+ delete [] sockets;
+}
+
+
+void SocketManager::addListener(network::SocketListener* sock_, network::SocketServer* srvr) {
+ WSAEVENT event = WSACreateEvent();
+ assert(event != WSA_INVALID_EVENT);
+ addListener(sock_, event, srvr);
+}
+
+void SocketManager::addSocket(network::Socket* sock_, network::SocketServer* srvr) {
+ WSAEVENT event = WSACreateEvent();
+ assert(event != WSA_INVALID_EVENT);
+ addSocket(sock_, event, srvr);
+}
+
+
+BOOL SocketManager::getMessage(MSG* msg, HWND hwnd, UINT minMsg, UINT maxMsg) {
+ while (true) {
+ // First check for idle timeout
+
+ network::SocketServer* server = 0;
+ int timeout = 0;
+ for (int i=0; i<nSockets; i++) {
+ if (!sockets[i].is_event &&
+ sockets[i].server != server) {
+ server = sockets[i].server;
+ int t = server->checkTimeouts();
+ if (t > 0 && (timeout == 0 || t < timeout))
+ timeout = t;
+ }
+ }
+ if (timeout == 0)
+ timeout = INFINITE;
+
+ // - Network IO is less common than messages - process it first
+ DWORD result;
+ if (nSockets) {
+ result = WaitForMultipleObjects(nSockets, events, FALSE, 0);
+ if (result == WAIT_TIMEOUT) {
+ if (PeekMessage(msg, hwnd, minMsg, maxMsg, PM_REMOVE))
+ return msg->message != WM_QUIT;
+
+ result = MsgWaitForMultipleObjects(nSockets, events, FALSE, timeout,
+ QS_ALLINPUT);
+ if (result == WAIT_OBJECT_0 + nSockets) {
+ 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 + nSockets))) {
+ int index = result - WAIT_OBJECT_0;
+
+ // - Process a socket event
+
+ if (sockets[index].is_event) {
+ // Process a general Win32 event
+ // NB: The handler must reset the event!
+
+ if (!sockets[index].handler->processEvent(events[index])) {
+ removeSocket(index);
+ continue;
+ }
+ } else if (sockets[index].is_conn) {
+ // Process data from an active connection
+
+ // Cancel event notification for this socket
+ if (WSAEventSelect(sockets[index].fd, events[index], 0) == SOCKET_ERROR)
+ vlog.info("unable to disable WSAEventSelect:%u", WSAGetLastError());
+
+ // Reset the event object
+ WSAResetEvent(events[index]);
+
+ // Call the socket server to process the event
+ if (!sockets[index].server->processSocketEvent(sockets[index].sock.conn)) {
+ removeSocket(index);
+ continue;
+ }
+
+ // Re-instate the required socket event
+ // If the read event is still valid, the event object gets set here
+ if (WSAEventSelect(sockets[index].fd, events[index], FD_READ | FD_CLOSE) == SOCKET_ERROR)
+ throw rdr::SystemException("unable to re-enable WSAEventSelect:%u", WSAGetLastError());
+
+ } else {
+ // Accept an incoming connection
+ vlog.debug("accepting incoming connection");
+
+ // What kind of event is this?
+ WSANETWORKEVENTS network_events;
+ WSAEnumNetworkEvents(sockets[index].fd, events[index], &network_events);
+ if (network_events.lNetworkEvents & FD_ACCEPT) {
+ network::Socket* new_sock = sockets[index].sock.listener->accept();
+ if (new_sock) {
+ sockets[index].server->addClient(new_sock);
+ addSocket(new_sock, sockets[index].server);
+ }
+ } else if (network_events.lNetworkEvents & FD_CLOSE) {
+ vlog.info("deleting listening socket");
+ network::SocketListener* s = sockets[index].sock.listener;
+ removeSocket(index);
+ delete s;
+ } else {
+ vlog.error("unknown network event for listener");
+ }
+
+ }
+ } else if (result == WAIT_FAILED) {
+ throw rdr::SystemException("unable to wait for events", GetLastError());
+ }
+ }
+}
+
+
+void SocketManager::resizeArrays(int numSockets) {
+ if (nAvail >= numSockets) return;
+ while (nAvail < numSockets)
+ nAvail = max(16, nAvail*2);
+
+ SocketInfo* newinfo = new SocketInfo[nAvail];
+ HANDLE* newevents = new HANDLE[nAvail];
+ for (int i=0; i<nSockets; i++) {
+ newinfo[i] = sockets[i];
+ newevents[i] = events[i];
+ }
+ delete [] sockets;
+ delete [] events;
+ sockets = newinfo;
+ events = newevents;
+}
+
+void SocketManager::addSocket(network::Socket* sock, HANDLE event, network::SocketServer* server) {
+ resizeArrays(nSockets+1);
+
+ sockets[nSockets].sock.conn = sock;
+ sockets[nSockets].fd = sock->getFd();
+ sockets[nSockets].server = server;
+ events[nSockets] = event;
+ sockets[nSockets].is_conn = true;
+ sockets[nSockets].is_event = false;
+
+ if (WSAEventSelect(sock->getFd(), event, FD_READ | FD_CLOSE) == SOCKET_ERROR)
+ throw rdr::SystemException("unable to select on socket", WSAGetLastError());
+ nSockets++;
+}
+
+void SocketManager::addListener(network::SocketListener* sock, HANDLE event, network::SocketServer* server) {
+ resizeArrays(nSockets+1);
+
+ sockets[nSockets].sock.listener = sock;
+ sockets[nSockets].fd = sock->getFd();
+ sockets[nSockets].server = server;
+ events[nSockets] = event;
+ sockets[nSockets].is_conn = false;
+ sockets[nSockets].is_event = false;
+
+ if (WSAEventSelect(sock->getFd(), event, FD_ACCEPT | FD_CLOSE) == SOCKET_ERROR)
+ throw rdr::SystemException("unable to select on listener", WSAGetLastError());
+ nSockets++;
+}
+
+void SocketManager::remListener(network::SocketListener* sock) {
+ for (int index=0; index<nSockets; index++) {
+ if (!sockets[index].is_conn &&
+ !sockets[index].is_event) {
+ vlog.debug("removing listening socket");
+ removeSocket(index);
+ delete sock;
+ }
+ }
+}
+
+void SocketManager::addEvent(HANDLE event, EventHandler* ecb) {
+ resizeArrays(nSockets+1);
+
+ sockets[nSockets].handler = ecb;
+ events[nSockets] = event;
+ sockets[nSockets].is_conn = false;
+ sockets[nSockets].is_event = true;
+
+ nSockets++;
+}
+
+void SocketManager::removeSocket(int index) {
+ if (index >= nSockets)
+ throw rdr::Exception("attempting to remove unregistered socket");
+
+ if (!sockets[index].is_event)
+ WSACloseEvent(events[index]);
+
+ for (int i=index; i<nSockets-1; i++) {
+ sockets[i] = sockets[i+1];
+ events[i] = events[i+1];
+ }
+
+ nSockets--;
+}
+
diff --git a/rfb_win32/SocketManager.h b/rfb_win32/SocketManager.h
new file mode 100644
index 00000000..791370f2
--- /dev/null
+++ b/rfb_win32/SocketManager.h
@@ -0,0 +1,107 @@
+/* Copyright (C) 2002-2004 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 <list>
+
+#include <network/Socket.h>
+#include <rfb_win32/MsgWindow.h>
+
+namespace rfb {
+
+ namespace win32 {
+
+ class SocketManager {
+ public:
+ SocketManager();
+ virtual ~SocketManager();
+
+ // Add a listening socket. Incoming connections will be added to the supplied
+ // SocketServer.
+ void addListener(network::SocketListener* sock_, network::SocketServer* srvr);
+
+ // 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);
+
+ // Add a Win32 event & handler for it to the SocketManager
+ // This event will be blocked on along with the registered Sockets, and the
+ // handler called whenever it is discovered to be set.
+ // NB: SocketManager does NOT call ResetEvent on the event!
+ // NB: If processEvent returns false then the event is no longer registered,
+ // and the event object is assumed to have been closed by processEvent()
+ struct EventHandler {
+ virtual ~EventHandler() {}
+ virtual bool processEvent(HANDLE event) = 0;
+ };
+ void addEvent(HANDLE event, EventHandler* ecb);
+
+ // getMessage
+ //
+ // Either return a message from the thread's message queue or process a socket
+ // event.
+ // Returns whenever a message needs processing. Returns false if message is
+ // WM_QUIT, true for all other messages.
+ BOOL getMessage(MSG* msg, HWND hwnd, UINT minMsg, UINT maxMsg);
+
+ protected:
+ void addListener(network::SocketListener* sock, HANDLE event, network::SocketServer* server);
+ void addSocket(network::Socket* sock, HANDLE event, network::SocketServer* server);
+ void resizeArrays(int numSockets);
+ void removeSocket(int index);
+ struct SocketInfo {
+ union {
+ network::Socket* conn;
+ network::SocketListener* listener;
+ } sock;
+ SOCKET fd;
+ bool is_conn;
+ bool is_event;
+ union {
+ network::SocketServer* server;
+ EventHandler* handler;
+ };
+ };
+ SocketInfo* sockets;
+ HANDLE* events;
+ int nSockets;
+ int nAvail;
+ };
+
+ }
+
+}
+
+#endif
diff --git a/rfb_win32/TCharArray.cxx b/rfb_win32/TCharArray.cxx
new file mode 100644
index 00000000..f8f03a69
--- /dev/null
+++ b/rfb_win32/TCharArray.cxx
@@ -0,0 +1,85 @@
+/* Copyright (C) 2002-2003 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 <rfb_win32/TCharArray.h>
+
+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<l; i++)
+ if (src[i] == c) return true;
+ return false;
+ }
+
+};
diff --git a/rfb_win32/TCharArray.h b/rfb_win32/TCharArray.h
new file mode 100644
index 00000000..399e00a7
--- /dev/null
+++ b/rfb_win32/TCharArray.h
@@ -0,0 +1,119 @@
+/* Copyright (C) 2002-2004 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.
+ */
+
+// -=- TCharArray.h
+
+// This library contains the wide-character equivalent of CharArray, named
+// WCharArray. In addition to providing wide-character equivalents of
+// the char* string manipulation functions (strDup, strFree, etc), special
+// versions of those functions are provided which attempt to convert from
+// one format to the other.
+// e.g. char* t = "hello world"; WCHAR* w = wstrDup(t);
+// Results in w containing the wide-character text "hello world".
+// For convenience, the WStr and CStr classes are also provided. These
+// accept an existing (const) WCHAR* or char* null-terminated string and
+// create a read-only copy of that in the desired format. The new copy
+// will actually be the original copy if the format has not changed, otherwise
+// it will be a new buffer owned by the WStr/CStr.
+
+// In addition to providing wide character functions, this header defines
+// TCHAR* handling classes & functions. TCHAR is defined at compile time to
+// either char or WCHAR. Programs can treat this as a third data type and
+// call TStr() whenever a TCHAR* is required but a char* or WCHAR* is supplied,
+// and TStr will do the right thing.
+
+#ifndef __RFB_WIN32_TCHARARRAY_H__
+#define __RFB_WIN32_TCHARARRAY_H__
+
+#define WIN32_LEAN_AND_MEAN
+#include <tchar.h>
+
+#include <rfb/util.h>
+
+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;
+ };
+
+#ifdef _UNICODE
+#define tstrDup wstrDup
+#define tstrFree wstrFree
+#define tstrSplit wstrSplit
+#define tstrContains wstrContains
+ typedef WCharArray TCharArray;
+ typedef WStr TStr;
+#else
+#define tstrDup strDup
+#define tstrFree strFree
+#define tstrSplit strSplit
+#define tstrContains strContains
+ typedef CharArray TCharArray;
+ typedef CStr TStr;
+#endif
+
+};
+
+#endif \ No newline at end of file
diff --git a/rfb_win32/TrayIcon.h b/rfb_win32/TrayIcon.h
new file mode 100644
index 00000000..85680f3f
--- /dev/null
+++ b/rfb_win32/TrayIcon.h
@@ -0,0 +1,90 @@
+/* Copyright (C) 2002-2003 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__
+
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+#include <shellapi.h>
+#include <rfb_win32/MsgWindow.h>
+#include <rdr/Exception.h>
+
+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/rfb_win32/WMCursor.cxx b/rfb_win32/WMCursor.cxx
new file mode 100644
index 00000000..871d9376
--- /dev/null
+++ b/rfb_win32/WMCursor.cxx
@@ -0,0 +1,98 @@
+/* Copyright (C) 2002-2003 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
+#define WINVER 0x0500
+
+#include <rfb_win32/WMCursor.h>
+#include <rfb_win32/OSVersion.h>
+#include <rfb_win32/Win32Util.h>
+#include <rfb/Exception.h>
+#include <rfb/LogWriter.h>
+
+using namespace rdr;
+using namespace rfb;
+using namespace rfb::win32;
+
+static LogWriter vlog("WMCursor");
+
+
+typedef BOOL (WINAPI *_GetCursorInfo_proto)(PCURSORINFO pci);
+DynamicFn<_GetCursorInfo_proto> _GetCursorInfo(_T("user32.dll"), "GetCursorInfo");
+
+
+WMCursor::WMCursor() : hooks(0), library(0), use_getCursorInfo(false), cursor(0) {
+#if (WINVER >= 0x0500)
+ // 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();
+#else
+#pragma message ("not building in GetCursorInfo support")
+#endif
+ if (!use_getCursorInfo) {
+ hooks = new WMCursorHooks();
+ if (hooks && hooks->start()) {
+ vlog.info("falling back to cursor hooking");
+ } else {
+ delete hooks;
+ hooks = 0;
+ vlog.error("unable to monitor cursor shape");
+ }
+ } else {
+ vlog.info("using GetCursorInfo");
+ }
+ cursor = (HCURSOR)LoadImage(0, IDC_ARROW, IMAGE_CURSOR, 0, 0, LR_DEFAULTSIZE | LR_SHARED);
+}
+
+WMCursor::~WMCursor() {
+ if (hooks) delete hooks;
+ if (library) FreeLibrary(library);
+}
+
+WMCursor::Info
+WMCursor::getCursorInfo() {
+ Info result;
+#if (WINVER >= 0x0500)
+ 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/rfb_win32/WMCursor.h b/rfb_win32/WMCursor.h
new file mode 100644
index 00000000..a96822a7
--- /dev/null
+++ b/rfb_win32/WMCursor.h
@@ -0,0 +1,65 @@
+/* Copyright (C) 2002-2003 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__
+
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+#include <rfb_win32/WMHooks.h>
+
+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;
+ HMODULE library;
+ bool use_getCursorInfo;
+ HCURSOR cursor;
+ };
+
+ };
+
+};
+
+#endif // __RFB_WIN32_WM_CURSOR_H__
diff --git a/rfb_win32/WMHooks.cxx b/rfb_win32/WMHooks.cxx
new file mode 100644
index 00000000..26a2363c
--- /dev/null
+++ b/rfb_win32/WMHooks.cxx
@@ -0,0 +1,324 @@
+/* Copyright (C) 2002-2003 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 <wm_hooks/wm_hooks.h>
+
+#include <rfb_win32/WMHooks.h>
+#include <rfb_win32/Service.h>
+#include <rfb/Threading.h>
+#include <rfb/LogWriter.h>
+
+#include <list>
+
+using namespace rfb;
+using namespace rfb::win32;
+
+static LogWriter vlog("WMHooks");
+
+class WMHooksThread : public Thread {
+public:
+ WMHooksThread() : Thread("WMHookThread"), active(true) {}
+ virtual void run();
+ virtual Thread* join();
+protected:
+ bool active;
+};
+
+WMHooksThread* hook_mgr = 0;
+std::list<WMHooks*> hooks;
+std::list<WMCursorHooks*> cursor_hooks;
+Mutex hook_mgr_lock;
+HCURSOR hook_cursor = (HCURSOR)LoadImage(0, IDC_ARROW, IMAGE_CURSOR, 0, 0, LR_DEFAULTSIZE | LR_SHARED);
+
+
+bool
+StartHookThread() {
+ if (hook_mgr) return true;
+ vlog.debug("opening hook thread");
+ hook_mgr = new WMHooksThread();
+ if (!WM_Hooks_Install(hook_mgr->getThreadId(), 0)) {
+ vlog.error("failed to initialise hooks");
+ delete hook_mgr->join();
+ hook_mgr = 0;
+ return false;
+ }
+ hook_mgr->start();
+ return true;
+}
+
+void
+StopHookThread() {
+ if (!hook_mgr) return;
+ if (!hooks.empty() || !cursor_hooks.empty()) return;
+ vlog.debug("closing hook thread");
+ delete hook_mgr->join();
+ hook_mgr = 0;
+}
+
+
+bool
+AddHook(WMHooks* hook) {
+ vlog.debug("adding hook");
+ Lock l(hook_mgr_lock);
+ if (!StartHookThread()) return false;
+ hooks.push_back(hook);
+ return true;
+}
+
+bool
+AddCursorHook(WMCursorHooks* hook) {
+ vlog.debug("adding cursor hook");
+ Lock l(hook_mgr_lock);
+ if (cursor_hooks.empty()) WM_Hooks_EnableCursorShape(TRUE);
+ if (!StartHookThread()) return false;
+ cursor_hooks.push_back(hook);
+ return true;
+}
+
+bool
+RemHook(WMHooks* hook) {
+ {
+ vlog.debug("removing hook");
+ Lock l(hook_mgr_lock);
+ hooks.remove(hook);
+ }
+ StopHookThread();
+ return true;
+}
+
+bool
+RemCursorHook(WMCursorHooks* hook) {
+ {
+ vlog.debug("removing cursor hook");
+ Lock l(hook_mgr_lock);
+ cursor_hooks.remove(hook);
+ }
+ StopHookThread();
+ if (cursor_hooks.empty()) WM_Hooks_EnableCursorShape(FALSE);
+ return true;
+}
+
+void
+NotifyHooksRegion(const Region& r) {
+ Lock l(hook_mgr_lock);
+ std::list<WMHooks*>::iterator i;
+ for (i=hooks.begin(); i!=hooks.end(); i++) {
+ (*i)->new_changes.add_changed(r);
+ if (!(*i)->notified) {
+ (*i)->notified = true;
+ PostMessage((*i)->getHandle(), WM_USER, 0, 0);
+ }
+ }
+}
+
+void
+NotifyHooksCursor(HCURSOR c) {
+ Lock l(hook_mgr_lock);
+ hook_cursor = c;
+}
+
+void
+WMHooksThread::run() {
+ UINT windowMsg = WM_Hooks_WindowChanged();
+ UINT clientAreaMsg = WM_Hooks_WindowClientAreaChanged();
+ UINT borderMsg = WM_Hooks_WindowBorderChanged();
+ UINT rectangleMsg = WM_Hooks_RectangleChanged();
+ UINT cursorMsg = WM_Hooks_CursorChanged();
+#ifdef _DEBUG
+ UINT diagnosticMsg = WM_Hooks_Diagnostic();
+#endif
+ MSG msg;
+ RECT wrect;
+ HWND hwnd;
+ int count = 0;
+
+ vlog.debug("starting hook thread");
+
+ while (active && GetMessage(&msg, NULL, 0, 0)) {
+ count++;
+ if (msg.message == windowMsg) {
+ hwnd = (HWND) msg.lParam;
+ if (IsWindow(hwnd) && IsWindowVisible(hwnd) && !IsIconic(hwnd) &&
+ GetWindowRect(hwnd, &wrect) && !IsRectEmpty(&wrect))
+ {
+ NotifyHooksRegion(Rect(wrect.left, wrect.top,
+ wrect.right, wrect.bottom));
+
+ }
+ } else if (msg.message == clientAreaMsg) {
+ hwnd = (HWND) msg.lParam;
+ if (IsWindow(hwnd) && IsWindowVisible(hwnd) && !IsIconic(hwnd) &&
+ GetClientRect(hwnd, &wrect) && !IsRectEmpty(&wrect))
+ {
+ POINT pt = {0,0};
+ if (ClientToScreen(hwnd, &pt)) {
+ NotifyHooksRegion(Rect(wrect.left+pt.x, wrect.top+pt.y,
+ wrect.right+pt.x, wrect.bottom+pt.y));
+ }
+ }
+ } 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));
+ }
+ NotifyHooksRegion(changed);
+ }
+ } else if (msg.message == rectangleMsg) {
+ Rect r = Rect(LOWORD(msg.wParam), HIWORD(msg.wParam),
+ LOWORD(msg.lParam), HIWORD(msg.lParam));
+ if (!r.is_empty()) {
+ NotifyHooksRegion(r);
+ }
+ } 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()
+ : clipper(0), new_changes(true), fg_window(0),
+ notified(false), MsgWindow(_T("WMHooks")) {
+}
+
+rfb::win32::WMHooks::~WMHooks() {
+ RemHook(this);
+ if (clipper) delete clipper;
+}
+
+LRESULT
+rfb::win32::WMHooks::processMessage(UINT msg, WPARAM wParam, LPARAM lParam) {
+ switch (msg) {
+ case WM_USER:
+ {
+ // *** Yield, to allow the triggering update event to be processed
+ // BEFORE we try to grab the resulting changes.
+ // *** IMPROVES THINGS NOTICABLY ON WinXP
+ Sleep(0);
+ // ***
+
+ Lock l(hook_mgr_lock);
+ notified = false;
+ new_changes.get_update(*clipper);
+ new_changes.clear();
+ }
+ break;
+ }
+ return MsgWindow::processMessage(msg, wParam, lParam);
+}
+
+bool
+rfb::win32::WMHooks::setClipRect(const Rect& r) {
+ clip_region = r;
+ if (clipper) clipper->set_clip_region(clip_region);
+ return true;
+}
+
+bool
+rfb::win32::WMHooks::setUpdateTracker(UpdateTracker* ut) {
+ if (clipper) delete clipper;
+ clipper = new ClippedUpdateTracker(*ut);
+ clipper->set_clip_region(clip_region);
+ return AddHook(this);
+}
+
+#ifdef _DEBUG
+void
+rfb::win32::WMHooks::setDiagnosticRange(UINT min, UINT max) {
+ WM_Hooks_SetDiagnosticRange(min, max);
+}
+#endif
+
+
+// -=- WMBlockInput class
+
+Mutex blockMutex;
+int blockCount = 0;
+
+rfb::win32::WMBlockInput::WMBlockInput() : active(false) {
+}
+
+rfb::win32::WMBlockInput::~WMBlockInput() {
+ blockInputs(false);
+}
+
+bool rfb::win32::WMBlockInput::blockInputs(bool on) {
+ if (on == active) return true;
+ vlog.debug("blockInput changed");
+ Lock l(blockMutex);
+ int newCount = blockCount;
+ if (on)
+ newCount++;
+ else
+ newCount--;
+ if (WM_Hooks_EnableRealInputs(newCount==0, newCount==0)) {
+ vlog.debug("set blocking to %d", newCount);
+ blockCount = newCount;
+ active = on;
+ return true;
+ }
+ return false;
+}
+
+
+// -=- 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/rfb_win32/WMHooks.h b/rfb_win32/WMHooks.h
new file mode 100644
index 00000000..791df763
--- /dev/null
+++ b/rfb_win32/WMHooks.h
@@ -0,0 +1,85 @@
+/* Copyright (C) 2002-2003 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__
+
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+#include <rfb/UpdateTracker.h>
+#include <rdr/Exception.h>
+#include <rfb_win32/MsgWindow.h>
+
+namespace rfb {
+
+ namespace win32 {
+
+ class WMHooks : public MsgWindow {
+ public:
+ WMHooks();
+ ~WMHooks();
+
+ bool setClipRect(const Rect& cr);
+ bool setUpdateTracker(UpdateTracker* ut);
+
+ virtual LRESULT processMessage(UINT msg, WPARAM wParam, LPARAM lParam);
+
+#ifdef _DEBUG
+ // Get notifications of any messages in the given range, to any hooked window
+ void setDiagnosticRange(UINT min, UINT max);
+#endif
+
+ protected:
+ ClippedUpdateTracker* clipper;
+ Region clip_region;
+
+ void* fg_window;
+ Rect fg_window_rect;
+
+ public:
+ SimpleUpdateTracker new_changes;
+ bool notified;
+ };
+
+ 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/rfb_win32/WMNotifier.cxx b/rfb_win32/WMNotifier.cxx
new file mode 100644
index 00000000..9773abf5
--- /dev/null
+++ b/rfb_win32/WMNotifier.cxx
@@ -0,0 +1,57 @@
+/* Copyright (C) 2002-2003 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 <rfb_win32/WMNotifier.h>
+#include <rfb_win32/WMShatter.h>
+#include <rfb_win32/MsgWindow.h>
+
+#include <rfb/LogWriter.h>
+
+using namespace rfb;
+using namespace rfb::win32;
+
+static LogWriter vlog("WMMonitor");
+
+
+WMMonitor::WMMonitor() : MsgWindow(_T("WMMonitor")) {
+}
+
+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/rfb_win32/WMNotifier.h b/rfb_win32/WMNotifier.h
new file mode 100644
index 00000000..564d176f
--- /dev/null
+++ b/rfb_win32/WMNotifier.h
@@ -0,0 +1,68 @@
+/* Copyright (C) 2002-2003 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 <rfb/SDesktop.h>
+#include <rfb/Threading.h>
+#include <rfb_win32/MsgWindow.h>
+#include <rfb_win32/DeviceFrameBuffer.h>
+#include <rfb_win32/SInput.h>
+
+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/rfb_win32/WMPoller.cxx b/rfb_win32/WMPoller.cxx
new file mode 100644
index 00000000..f568b211
--- /dev/null
+++ b/rfb_win32/WMPoller.cxx
@@ -0,0 +1,101 @@
+/* Copyright (C) 2002-2003 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 <rfb_win32/WMPoller.h>
+#include <rfb/Exception.h>
+#include <rfb/LogWriter.h>
+#include <rfb/Configuration.h>
+
+#include <tchar.h>
+
+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
+
+rfb::win32::WMPoller::WMPoller() : clipper(0) {
+}
+
+rfb::win32::WMPoller::~WMPoller() {
+ if (clipper) delete clipper;
+}
+
+bool
+rfb::win32::WMPoller::processEvent() {
+ PollInfo info;
+ if (clipper && poll_console_windows) {
+ ::EnumWindows(WMPoller::enumWindowProc, (LPARAM) &info);
+ clipper->add_changed(info.poll_include);
+ }
+ return false;
+}
+
+bool
+rfb::win32::WMPoller::setClipRect(const Rect& r) {
+ clip_region = r;
+ if (clipper) clipper->set_clip_region(clip_region);
+ return true;
+}
+
+bool
+rfb::win32::WMPoller::setUpdateTracker(UpdateTracker* ut) {
+ if (clipper) delete clipper;
+ clipper = new ClippedUpdateTracker(*ut);
+ clipper->set_clip_region(clip_region);
+ 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/rfb_win32/WMPoller.h b/rfb_win32/WMPoller.h
new file mode 100644
index 00000000..3f3f402a
--- /dev/null
+++ b/rfb_win32/WMPoller.h
@@ -0,0 +1,67 @@
+/* Copyright (C) 2002-2003 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__
+
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+#include <rfb/UpdateTracker.h>
+#include <rfb/Configuration.h>
+
+namespace rfb {
+
+ namespace win32 {
+
+ class WMPoller {
+ public:
+ WMPoller();
+ ~WMPoller();
+
+ bool processEvent();
+ bool setClipRect(const Rect& cr);
+ 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);
+
+ ClippedUpdateTracker* clipper;
+ Region clip_region;
+ };
+
+ };
+
+};
+
+#endif // __RFB_WIN32_WM_POLLER_H__
diff --git a/rfb_win32/WMShatter.cxx b/rfb_win32/WMShatter.cxx
new file mode 100644
index 00000000..f6a74848
--- /dev/null
+++ b/rfb_win32/WMShatter.cxx
@@ -0,0 +1,57 @@
+/* Copyright (C) 2002-2003 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 <rfb_win32/WMShatter.h>
+
+#include <rfb/LogWriter.h>
+
+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/rfb_win32/WMShatter.h b/rfb_win32/WMShatter.h
new file mode 100644
index 00000000..7b81678f
--- /dev/null
+++ b/rfb_win32/WMShatter.h
@@ -0,0 +1,53 @@
+/* Copyright (C) 2002-2003 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__
+
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+
+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/rfb_win32/WMWindowCopyRect.cxx b/rfb_win32/WMWindowCopyRect.cxx
new file mode 100644
index 00000000..46d85eac
--- /dev/null
+++ b/rfb_win32/WMWindowCopyRect.cxx
@@ -0,0 +1,83 @@
+/* Copyright (C) 2002-2003 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 <rfb_win32/WMWindowCopyRect.h>
+#include <rfb/LogWriter.h>
+#include <windows.h>
+
+using namespace rfb;
+using namespace rfb::win32;
+
+static LogWriter vlog("WMCopyRect");
+
+// -=- WMHooks class
+
+rfb::win32::WMCopyRect::WMCopyRect() : clipper(0), fg_window(0) {
+}
+
+rfb::win32::WMCopyRect::~WMCopyRect() {
+ if (clipper) delete clipper;
+}
+
+bool
+rfb::win32::WMCopyRect::processEvent() {
+ if (clipper) {
+ // 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)) {
+ // 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;
+ clipper->add_copied(copy_dest, delta);
+ clipper->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::setClipRect(const Rect& r) {
+ clip_region = r;
+ if (clipper) clipper->set_clip_region(clip_region);
+ return true;
+}
+
+bool
+rfb::win32::WMCopyRect::setUpdateTracker(UpdateTracker* ut) {
+ if (clipper) delete clipper;
+ clipper = new ClippedUpdateTracker(*ut);
+ clipper->set_clip_region(clip_region);
+ return true;
+}
diff --git a/rfb_win32/WMWindowCopyRect.h b/rfb_win32/WMWindowCopyRect.h
new file mode 100644
index 00000000..0750d86e
--- /dev/null
+++ b/rfb_win32/WMWindowCopyRect.h
@@ -0,0 +1,56 @@
+/* Copyright (C) 2002-2003 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 <rfb/UpdateTracker.h>
+
+namespace rfb {
+
+ namespace win32 {
+
+ class WMCopyRect {
+ public:
+ WMCopyRect();
+ ~WMCopyRect();
+
+ bool processEvent();
+ bool setClipRect(const Rect& cr);
+ bool setUpdateTracker(UpdateTracker* ut);
+
+ protected:
+ ClippedUpdateTracker* clipper;
+ Region clip_region;
+ void* fg_window;
+ Rect fg_window_rect;
+ };
+
+ };
+
+};
+
+#endif // __RFB_WIN32_WM_WINDOW_COPYRECT_H__
diff --git a/rfb_win32/Win32Util.cxx b/rfb_win32/Win32Util.cxx
new file mode 100644
index 00000000..e25f43ad
--- /dev/null
+++ b/rfb_win32/Win32Util.cxx
@@ -0,0 +1,447 @@
+/* Copyright (C) 2002-2004 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 <rfb_win32/Win32Util.h>
+#include <rdr/Exception.h>
+#include <rdr/HexOutStream.h>
+
+
+namespace rfb {
+namespace win32 {
+
+LogicalPalette::LogicalPalette() : palette(0), numEntries(0) {
+ 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::~LogicalPalette() {
+ if (palette)
+ if (!DeleteObject(palette))
+ throw rdr::SystemException("del palette failed", GetLastError());
+}
+
+void LogicalPalette::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<count; i++) {
+ logpal[i].peRed = cols[i].r >> 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;
+}
+
+
+static LogWriter dcLog("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());
+ }
+
+ // -=- Munge the bitmap info here
+ switch (bi.bmiHeader.biBitCount) {
+ case 1:
+ case 4:
+ bi.bmiHeader.biBitCount = 8;
+ break;
+ case 24:
+ bi.bmiHeader.biBitCount = 32;
+ break;
+ }
+ bi.bmiHeader.biPlanes = 1;
+
+ format.trueColour = bi.bmiHeader.biBitCount > 8;
+ format.bigEndian = 0;
+ format.bpp = format.depth = 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
+ dcLog.info("16-bit High Colour");
+ rMask = 0x7c00;
+ bMask = 0x001f;
+ gMask = 0x03e0;
+ format.depth = 15;
+ break;
+ case 24:
+ case 32:
+ // RGB 888 - True Colour
+ dcLog.info("24/32-bit High Colour");
+ rMask = 0xff0000;
+ gMask = 0x00ff00;
+ bMask = 0x0000ff;
+ format.depth = 24;
+ break;
+ default:
+ dcLog.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;
+ dcLog.info("BitFields format: %lu, (%lx, %lx, %lx)",
+ bi.bmiHeader.biBitCount, rMask, gMask, bMask);
+ if (format.bpp == 32)
+ format.depth = 24; // ...probably
+ 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);
+ }
+
+ return format;
+}
+
+
+WindowDC::WindowDC(HWND wnd) : hwnd(wnd) {
+ dc = GetDC(wnd);
+ if (!dc)
+ throw rdr::SystemException("GetDC failed", GetLastError());
+}
+WindowDC::~WindowDC() {
+ if (dc)
+ ReleaseDC(hwnd, dc);
+}
+
+
+CompatibleDC::CompatibleDC(HDC existing) {
+ dc = CreateCompatibleDC(existing);
+ if (!dc)
+ throw rdr::SystemException("CreateCompatibleDC failed", GetLastError());
+}
+CompatibleDC::~CompatibleDC() {
+ if (dc)
+ DeleteDC(dc);
+}
+
+
+BitmapDC::BitmapDC(HDC hdc, HBITMAP hbitmap) : CompatibleDC(hdc){
+ oldBitmap = (HBITMAP)SelectObject(dc, hbitmap);
+ if (!oldBitmap)
+ throw rdr::SystemException("SelectObject to CompatibleDC failed",
+ GetLastError());
+}
+BitmapDC::~BitmapDC() {
+ SelectObject(dc, oldBitmap);
+}
+
+
+CompatibleBitmap::CompatibleBitmap(HDC hdc, int width, int height) {
+ hbmp = CreateCompatibleBitmap(hdc, width, height);
+ if (!hbmp)
+ throw rdr::SystemException("CreateCompatibleBitmap() failed",
+ GetLastError());
+}
+CompatibleBitmap::~CompatibleBitmap() {
+ if (hbmp) DeleteObject(hbmp);
+}
+
+
+PaletteSelector::PaletteSelector(HDC dc, HPALETTE pal) : device(dc), redrawRequired(false) {
+ oldPal = SelectPalette(dc, pal, FALSE);
+ redrawRequired = RealizePalette(dc) > 0;
+}
+PaletteSelector::~PaletteSelector() {
+ if (oldPal) SelectPalette(device, oldPal, TRUE);
+}
+
+
+IconInfo::IconInfo(HICON icon) {
+ if (!GetIconInfo(icon, this))
+ throw rdr::SystemException("GetIconInfo() failed", GetLastError());
+}
+IconInfo::~IconInfo() {
+ if (hbmColor)
+ DeleteObject(hbmColor);
+ if (hbmMask)
+ DeleteObject(hbmMask);
+}
+
+
+ModuleFileName::ModuleFileName(HMODULE module) : TCharArray(MAX_PATH) {
+ if (!module) module = GetModuleHandle(0);
+ if (!GetModuleFileName(module, buf, MAX_PATH))
+ buf[0] = 0;
+}
+
+
+FileVersionInfo::FileVersionInfo(const TCHAR* filename) {
+ // Get executable name
+ ModuleFileName exeName;
+ if (!filename) filename = exeName.buf;
+
+ // 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] = 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);
+}
+
+
+static LogWriter dfbLog("DynamicFn");
+
+DynamicFnBase::DynamicFnBase(const TCHAR* dllName, const char* fnName) : dllHandle(0), fnPtr(0) {
+ dllHandle = LoadLibrary(dllName);
+ if (!dllHandle) {
+ dfbLog.info("DLL %s not found (%d)", (const char*)CStr(dllName), GetLastError());
+ return;
+ }
+ fnPtr = GetProcAddress(dllHandle, fnName);
+ if (!fnPtr)
+ dfbLog.info("proc %s not found in %s (%d)", fnName, (const char*)CStr(dllName), GetLastError());
+}
+
+DynamicFnBase::~DynamicFnBase() {
+ if (dllHandle)
+ FreeLibrary(dllHandle);
+}
+
+
+static LogWriter miLog("MonitorInfo");
+
+MonitorInfo::MonitorInfo(HWND window) {
+#if (WINVER >= 0x0500)
+ typedef HMONITOR (WINAPI *_MonitorFromWindow_proto)(HWND,DWORD);
+ rfb::win32::DynamicFn<_MonitorFromWindow_proto> _MonitorFromWindow(_T("user32.dll"), "MonitorFromWindow");
+ typedef BOOL (WINAPI *_GetMonitorInfo_proto)(HMONITOR,LPMONITORINFO);
+ rfb::win32::DynamicFn<_GetMonitorInfo_proto> _GetMonitorInfo(_T("user32.dll"), "GetMonitorInfoA");
+
+ // Can we dynamically link to the monitor functions?
+ if (_MonitorFromWindow.isValid()) {
+ if (_GetMonitorInfo.isValid()) {
+ HMONITOR monitor = (*_MonitorFromWindow)(window, MONITOR_DEFAULTTONEAREST);
+ miLog.debug("monitor=%lx", monitor);
+ if (monitor) {
+ memset(this, 0, sizeof(MONITORINFOEXA));
+ cbSize = sizeof(MONITORINFOEXA);
+ if ((*_GetMonitorInfo)(monitor, this)) {
+ miLog.debug("monitor is %d,%d-%d,%d", rcMonitor.left, rcMonitor.top, rcMonitor.right, rcMonitor.bottom);
+ miLog.debug("work area is %d,%d-%d,%d", rcWork.left, rcWork.top, rcWork.right, rcWork.bottom);
+ miLog.debug("device is \"%s\"", szDevice);
+ return;
+ }
+ miLog.error("failed to get monitor info: %ld", GetLastError());
+ }
+ } else {
+ miLog.debug("GetMonitorInfo not found");
+ }
+ } else {
+ miLog.debug("MonitorFromWindow not found");
+ }
+#else
+#pragma message ("not building in GetMonitorInfo")
+ cbSize = sizeof(MonitorInfo);
+ szDevice[0] = 0;
+#endif
+
+ // Legacy fallbacks - just return the desktop settings
+ miLog.debug("using legacy fall-backs");
+ HWND desktop = GetDesktopWindow();
+ GetWindowRect(desktop, &rcMonitor);
+ SystemParametersInfo(SPI_GETWORKAREA, 0, &rcWork, 0);
+ dwFlags = 0;
+}
+
+
+#if (WINVER >= 0x0500)
+
+struct moveToMonitorData {
+ HWND window;
+ const char* monitorName;
+};
+
+typedef BOOL (WINAPI *_GetMonitorInfo_proto)(HMONITOR,LPMONITORINFO);
+static rfb::win32::DynamicFn<_GetMonitorInfo_proto> _GetMonitorInfo(_T("user32.dll"), "GetMonitorInfoA");
+
+static BOOL CALLBACK moveToMonitorEnumProc(HMONITOR monitor,
+ HDC dc,
+ LPRECT pos,
+ LPARAM d) {
+ moveToMonitorData* data = (moveToMonitorData*)d;
+ MONITORINFOEXA info;
+ memset(&info, 0, sizeof(info));
+ info.cbSize = sizeof(info);
+
+ if ((*_GetMonitorInfo)(monitor, &info)) {
+ if (stricmp(data->monitorName, info.szDevice) == 0) {
+ SetWindowPos(data->window, 0,
+ info.rcMonitor.left, info.rcMonitor.top,
+ info.rcMonitor.right, info.rcMonitor.bottom,
+ SWP_NOZORDER | SWP_NOSIZE | SWP_NOACTIVATE | SWP_NOOWNERZORDER);
+ return FALSE;
+ }
+ }
+
+ return TRUE;
+}
+
+#endif
+
+void moveToMonitor(HWND handle, const char* device) {
+ miLog.debug("moveToMonitor %s", device);
+
+#if (WINVER >= 0x500)
+ typedef BOOL (WINAPI *_EnumDisplayMonitors_proto)(HDC, LPCRECT, MONITORENUMPROC, LPARAM);
+ rfb::win32::DynamicFn<_EnumDisplayMonitors_proto> _EnumDisplayMonitors(_T("user32.dll"), "EnumDisplayMonitors");
+ if (!_EnumDisplayMonitors.isValid()) {
+ miLog.debug("EnumDisplayMonitors not found");
+ return;
+ }
+
+ moveToMonitorData data;
+ data.window = handle;
+ data.monitorName = device;
+
+ (*_EnumDisplayMonitors)(0, 0, &moveToMonitorEnumProc, (LPARAM)&data);
+#endif
+}
+
+
+void centerWindow(HWND handle, HWND parent, bool clipToParent) {
+ RECT r;
+ if (parent && IsWindowVisible(parent)) {
+ if (!GetWindowRect(parent, &r)) return;
+ } else {
+ MonitorInfo mi(handle);
+ r=mi.rcWork;
+ }
+ centerWindow(handle, r, clipToParent);
+}
+
+void centerWindow(HWND handle, const RECT& r, bool clipToRect) {
+ RECT wr;
+ if (!GetWindowRect(handle, &wr)) return;
+ int w = wr.right-wr.left;
+ int h = wr.bottom-wr.top;
+ if (clipToRect) {
+ w = min(r.right-r.left, w);
+ h = min(r.bottom-r.top, h);
+ }
+ int x = (r.left + r.right - w)/2;
+ int y = (r.top + r.bottom - h)/2;
+ UINT flags = SWP_NOACTIVATE | SWP_NOOWNERZORDER | SWP_NOZORDER | (clipToRect ? 0 : SWP_NOSIZE);
+ SetWindowPos(handle, 0, x, y, w, h, flags);
+}
+
+
+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);
+}
+
+
+};
+};
diff --git a/rfb_win32/Win32Util.h b/rfb_win32/Win32Util.h
new file mode 100644
index 00000000..5f0ab5a0
--- /dev/null
+++ b/rfb_win32/Win32Util.h
@@ -0,0 +1,217 @@
+/* Copyright (C) 2002-2004 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 <rfb/ColourMap.h>
+#include <rfb/PixelFormat.h>
+#include <rfb/Rect.h>
+#include <rfb_win32/TCharArray.h>
+
+namespace rfb {
+
+ namespace win32 {
+
+ class LogicalPalette {
+ public:
+ LogicalPalette();
+ ~LogicalPalette();
+ void setEntries(int start, int count, const Colour* cols);
+ HPALETTE getHandle() {return palette;}
+ protected:
+ HPALETTE palette;
+ int numEntries;
+ };
+
+ class DeviceContext {
+ public:
+ DeviceContext() : dc(0) {}
+ virtual ~DeviceContext() {}
+ operator HDC() const {return dc;}
+ PixelFormat getPF() const;
+ static PixelFormat getPF(HDC dc);
+ protected:
+ HDC dc;
+ };
+
+ class WindowDC : public DeviceContext {
+ public:
+ WindowDC(HWND wnd);
+ virtual ~WindowDC();
+ protected:
+ HWND hwnd;
+ };
+
+ class CompatibleDC : public DeviceContext {
+ public:
+ CompatibleDC(HDC existing);
+ virtual ~CompatibleDC();
+ };
+
+ class BitmapDC : public CompatibleDC {
+ public:
+ BitmapDC(HDC hdc, HBITMAP hbitmap);
+ ~BitmapDC();
+ protected:
+ HBITMAP oldBitmap;
+ };
+
+ class CompatibleBitmap {
+ public:
+ CompatibleBitmap(HDC hdc, int width, int height);
+ virtual ~CompatibleBitmap();
+ operator HBITMAP() const {return hbmp;}
+ protected:
+ HBITMAP hbmp;
+ };
+
+ 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;
+ }
+
+ class PaletteSelector {
+ public:
+ PaletteSelector(HDC dc, HPALETTE pal);
+ ~PaletteSelector();
+ bool isRedrawRequired() {return redrawRequired;}
+ protected:
+ HPALETTE oldPal;
+ HDC device;
+ bool redrawRequired;
+ };
+
+ struct IconInfo : public ICONINFO {
+ IconInfo(HICON icon);
+ ~IconInfo();
+ };
+
+ struct ModuleFileName : public TCharArray {
+ ModuleFileName(HMODULE module=0);
+ };
+
+ 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);
+
+ class DynamicFnBase {
+ public:
+ DynamicFnBase(const TCHAR* dllName, const char* fnName);
+ ~DynamicFnBase();
+ bool isValid() const {return fnPtr != 0;}
+ protected:
+ void* fnPtr;
+ HMODULE dllHandle;
+ };
+
+ template<class T> class DynamicFn : public DynamicFnBase {
+ public:
+ DynamicFn(const TCHAR* dllName, const char* fnName) : DynamicFnBase(dllName, fnName) {}
+ T operator *() const {return (T)fnPtr;};
+ };
+
+ // Structure containing info on the monitor nearest the window.
+ // Copes with multi-monitor OSes and older ones.
+#if (WINVER >= 0x0500)
+ struct MonitorInfo : MONITORINFOEXA {
+ MonitorInfo(HWND hwnd);
+ };
+#else
+ struct MonitorInfo {
+ MonitorInfo(HWND hwnd);
+ DWORD cbSize;
+ RECT rcMonitor;
+ RECT rcWork;
+ DWORD dwFlags;
+ char szDevice[1]; // Always null...
+ };
+#endif
+ void moveToMonitor(HWND handle, const char* device);
+
+ class Handle {
+ public:
+ Handle(HANDLE h_=0) : h(h_) {}
+ ~Handle() {
+ if (h) CloseHandle(h);
+ }
+ operator HANDLE() {return h;}
+ HANDLE h;
+ };
+
+ // 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, bool clipToRect=false);
+ void centerWindow(HWND handle, HWND parent, bool clipToRect=false);
+
+ // MsgBox helper function. Define rfb::win32::AppName somewhere in your
+ // code and MsgBox will use its value in informational messages.
+ extern TStr AppName;
+ int MsgBox(HWND parent, const TCHAR* message, UINT flags);
+
+ // 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(""));
+ }
+ };
+
+ // 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/rfb_win32/keymap.h b/rfb_win32/keymap.h
new file mode 100644
index 00000000..69ce66f2
--- /dev/null
+++ b/rfb_win32/keymap.h
@@ -0,0 +1,149 @@
+/* Copyright (C) 2002-2004 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/rfb_win32/msvcwarning.h b/rfb_win32/msvcwarning.h
new file mode 100644
index 00000000..e50d9c1a
--- /dev/null
+++ b/rfb_win32/msvcwarning.h
@@ -0,0 +1,20 @@
+/* Copyright (C) 2002-2003 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.
+ */
+#pragma warning( disable : 4244 ) // loss of data e.g. int to char
+#pragma warning( disable : 4800 ) // forcing bool 'true' or 'false'
+#pragma warning( disable : 4786 ) // debug info truncated
diff --git a/rfb_win32/rfb_win32.dsp b/rfb_win32/rfb_win32.dsp
new file mode 100644
index 00000000..2118bcb2
--- /dev/null
+++ b/rfb_win32/rfb_win32.dsp
@@ -0,0 +1,341 @@
+# 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"
+# 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 ".." /FI"msvcwarning.h" /D "NDEBUG" /D "_LIB" /D "WIN32" /D "_MBCS" /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"
+# 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 ".." /FI"msvcwarning.h" /D "_DEBUG" /D "_LIB" /D "WIN32" /D "_MBCS" /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"
+# 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 ".." /FI"msvcwarning.h" /D "_LIB" /D "_DEBUG" /D "WIN32" /D "_UNICODE" /D "UNICODE" /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=.\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=.\LaunchProcess.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=.\RegConfig.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\Registry.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\SDisplay.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\Service.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=.\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=.\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=.\CPointer.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\CurrentUser.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=.\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=.\MsgWindow.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\OSVersion.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=.\SDisplay.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=.\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=.\TrayIcon.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/tx/Makefile.in b/tx/Makefile.in
new file mode 100644
index 00000000..b953325a
--- /dev/null
+++ b/tx/Makefile.in
@@ -0,0 +1,18 @@
+
+SRCS = TXWindow.cxx TXScrollbar.cxx TXViewport.cxx TXImage.cxx TXMenu.cxx \
+ Timer.cxx
+
+OBJS = $(SRCS:.cxx=.o)
+
+DIR_CPPFLAGS = -I$(top_srcdir) @X_CFLAGS@ # X_CFLAGS are really CPPFLAGS
+
+library = libtx.a
+
+all:: $(library)
+
+$(library): $(OBJS)
+ rm -f $(library)
+ $(AR) $(library) $(OBJS)
+ $(RANLIB) $(library)
+
+# followed by boilerplate.mk
diff --git a/tx/TXButton.h b/tx/TXButton.h
new file mode 100644
index 00000000..3f0c57e6
--- /dev/null
+++ b/tx/TXButton.h
@@ -0,0 +1,124 @@
+/* Copyright (C) 2002-2003 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.
+ */
+//
+// TXButton.h
+//
+// A TXButton is a clickable button with some text in it. The button must be
+// big enough to contain the text - if not then it will be resized
+// appropriately.
+//
+
+#ifndef __TXBUTTON_H__
+#define __TXBUTTON_H__
+
+#include "TXWindow.h"
+#include <rfb/util.h>
+
+// TXButtonCallback's buttonActivate() method is called when a button is
+// activated.
+class TXButton;
+class TXButtonCallback {
+public:
+ virtual void buttonActivate(TXButton* button)=0;
+};
+
+
+class TXButton : public TXWindow, public TXEventHandler {
+public:
+
+ TXButton(Display* dpy_, const char* text_, TXButtonCallback* cb_=0,
+ TXWindow* parent_=0, int w=1, int h=1)
+ : TXWindow(dpy_, w, h, parent_), cb(cb_), down(false),
+ disabled_(false)
+ {
+ setEventHandler(this);
+ setText(text_);
+ gc = XCreateGC(dpy, win(), 0, 0);
+ XSetFont(dpy, gc, defaultFont);
+ addEventMask(ExposureMask | ButtonPressMask | ButtonReleaseMask);
+ }
+
+ virtual ~TXButton() {
+ XFreeGC(dpy, gc);
+ }
+
+ // setText() changes the text in the button.
+ void setText(const char* text_) {
+ text.buf = rfb::strDup(text_);
+ int textWidth = XTextWidth(defaultFS, text.buf, strlen(text.buf));
+ int textHeight = (defaultFS->ascent + defaultFS->descent);
+ int newWidth = max(width(), textWidth + xPad*2 + bevel*2);
+ int newHeight = max(height(), textHeight + yPad*2 + bevel*2);
+ if (width() < newWidth || height() < newHeight) {
+ resize(newWidth, newHeight);
+ }
+ }
+
+ // disabled() sets or queries the disabled state of the checkbox. A disabled
+ // checkbox cannot be changed via the user interface.
+ void disabled(bool b) { disabled_ = b; paint(); }
+ bool disabled() { return disabled_; }
+
+private:
+
+ void paint() {
+ int tw = XTextWidth(defaultFS, text.buf, strlen(text.buf));
+ int startx = (width() - tw) / 2;
+ int starty = (height() + defaultFS->ascent - defaultFS->descent) / 2;
+ if (down || disabled_) {
+ drawBevel(gc, 0, 0, width(), height(), bevel, defaultBg, darkBg,lightBg);
+ startx++; starty++;
+ } else {
+ drawBevel(gc, 0, 0, width(), height(), bevel, defaultBg, lightBg,darkBg);
+ }
+
+ XSetForeground(dpy, gc, disabled_ ? disabledFg : defaultFg);
+ XDrawString(dpy, win(), gc, startx, starty, text.buf, strlen(text.buf));
+ }
+
+ virtual void handleEvent(TXWindow* w, XEvent* ev) {
+ switch (ev->type) {
+ case Expose:
+ paint();
+ break;
+ case ButtonPress:
+ if (!disabled_) {
+ down = true;
+ paint();
+ }
+ break;
+ case ButtonRelease:
+ if (!down) break;
+ down = false;
+ paint();
+ if (ev->xbutton.x >= 0 && ev->xbutton.x < width() &&
+ ev->xbutton.y >= 0 && ev->xbutton.y < height()) {
+ if (cb) cb->buttonActivate(this);
+ }
+ break;
+ }
+ }
+
+ GC gc;
+ rfb::CharArray text;
+ TXButtonCallback* cb;
+ bool down;
+ bool disabled_;
+};
+
+#endif
diff --git a/tx/TXCheckbox.h b/tx/TXCheckbox.h
new file mode 100644
index 00000000..146091d0
--- /dev/null
+++ b/tx/TXCheckbox.h
@@ -0,0 +1,142 @@
+/* Copyright (C) 2002-2003 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.
+ */
+//
+// TXCheckbox.h
+//
+// A TXCheckbox has a box which may be "checked" with some text next to it.
+// The checkbox window must be big enough to contain the text - if not then it
+// will be resized appropriately.
+//
+// There are two styles of checkbox: the normal style which uses a tick in a
+// square box, and the radio style which uses a dot inside a circle. The
+// default behaviour when clicking on the checkbox is to toggle it on or off,
+// but this behaviour can be changed by the callback object. In particular to
+// get radiobutton behaviour, the callback must ensure that only one of a set
+// of radiobuttons is selected.
+//
+
+#ifndef __TXCHECKBOX_H__
+#define __TXCHECKBOX_H__
+
+#include "TXWindow.h"
+#include <rfb/util.h>
+
+// TXCheckboxCallback's checkboxSelect() method is called when the state of a
+// checkbox changes.
+class TXCheckbox;
+class TXCheckboxCallback {
+public:
+ virtual void checkboxSelect(TXCheckbox* checkbox)=0;
+};
+
+
+class TXCheckbox : public TXWindow, public TXEventHandler {
+public:
+ TXCheckbox(Display* dpy_, const char* text_, TXCheckboxCallback* cb_,
+ bool radio_=false, TXWindow* parent_=0, int w=1, int h=1)
+ : TXWindow(dpy_, w, h, parent_), cb(cb_), text(0),
+ boxSize(radio_ ? 12 : 13), boxPad(4),
+ checked_(false), disabled_(false), radio(radio_)
+ {
+ setEventHandler(this);
+ setText(text_);
+ gc = XCreateGC(dpy, win(), 0, 0);
+ XSetFont(dpy, gc, defaultFont);
+ addEventMask(ExposureMask| ButtonPressMask | ButtonReleaseMask);
+ }
+
+ virtual ~TXCheckbox() {
+ XFreeGC(dpy, gc);
+ if (text) free(text);
+ }
+
+ // setText() changes the text in the checkbox.
+ void setText(const char* text_) {
+ if (text) free(text);
+ text = strdup(text_);
+ int textWidth = XTextWidth(defaultFS, text, strlen(text));
+ int textHeight = (defaultFS->ascent + defaultFS->descent);
+ int newWidth = max(width(), textWidth + xPad*2 + boxPad*2 + boxSize);
+ int newHeight = max(height(), textHeight + yPad*2);
+ if (width() < newWidth || height() < newHeight) {
+ resize(newWidth, newHeight);
+ }
+ }
+
+ // checked() sets or queries the state of the checkbox
+ void checked(bool b) { checked_ = b; paint(); }
+ bool checked() { return checked_; }
+
+ // disabled() sets or queries the disabled state of the checkbox. A disabled
+ // checkbox cannot be changed via the user interface.
+ void disabled(bool b) { disabled_ = b; paint(); }
+ bool disabled() { return disabled_; }
+
+private:
+ void paint() {
+ if (disabled_)
+ drawBevel(gc, xPad + boxPad, (height() - boxSize) / 2, boxSize, boxSize,
+ bevel, disabledBg, darkBg, lightBg, radio);
+ else
+ drawBevel(gc, xPad + boxPad, (height() - boxSize) / 2, boxSize, boxSize,
+ bevel, enabledBg, darkBg, lightBg, radio);
+ XSetBackground(dpy, gc, disabled_ ? disabledBg : enabledBg);
+ XSetForeground(dpy, gc, disabled_ ? disabledFg : defaultFg);
+ if (checked_) {
+ Pixmap icon = radio ? dot : tick;
+ int iconSize = radio ? dotSize : tickSize;
+ XCopyPlane(dpy, icon, win(), gc, 0, 0, iconSize, iconSize,
+ xPad + boxPad + (boxSize - iconSize) / 2,
+ (height() - iconSize) / 2, 1);
+ }
+ XDrawString(dpy, win(), gc, xPad + boxSize + boxPad*2,
+ (height() + defaultFS->ascent - defaultFS->descent) / 2,
+ text, strlen(text));
+ }
+
+ virtual void handleEvent(TXWindow* w, XEvent* ev) {
+ switch (ev->type) {
+ case Expose:
+ paint();
+ break;
+ case ButtonPress:
+ break;
+ case ButtonRelease:
+ if (ev->xbutton.x >= 0 && ev->xbutton.x < width() &&
+ ev->xbutton.y >= 0 && ev->xbutton.y < height()) {
+ if (!disabled_) {
+ checked_ = !checked_;
+ if (cb) cb->checkboxSelect(this);
+ paint();
+ }
+ }
+ break;
+ }
+ }
+
+ TXCheckboxCallback* cb;
+ GC gc;
+ char* text;
+ int boxSize;
+ int boxPad;
+ bool checked_;
+ bool disabled_;
+ bool radio;
+};
+
+#endif
diff --git a/tx/TXDialog.h b/tx/TXDialog.h
new file mode 100644
index 00000000..01677a9e
--- /dev/null
+++ b/tx/TXDialog.h
@@ -0,0 +1,90 @@
+/* Copyright (C) 2002-2003 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.
+ */
+//
+// TXDialog.h
+//
+// A TXDialog is a pop-up dialog window. The dialog can be made visible by
+// calling its show() method. Dialogs can be modal or non-modal. For a modal
+// dialog box, the show() method only returns when the dialog box has been
+// dismissed. For a non-modal dialog box, the show() method returns
+// immediately.
+//
+
+#ifndef __TXDIALOG_H__
+#define __TXDIALOG_H__
+
+#include "TXWindow.h"
+#include <errno.h>
+
+class TXDialog : public TXWindow, public TXDeleteWindowCallback {
+public:
+ TXDialog(Display* dpy, int width, int height, const char* name,
+ bool modal_=false)
+ : TXWindow(dpy, width, height), done(false), ok(false), modal(modal_)
+ {
+ toplevel(name, this);
+ int dpyWidth = WidthOfScreen(DefaultScreenOfDisplay(dpy));
+ int dpyHeight = HeightOfScreen(DefaultScreenOfDisplay(dpy));
+ setUSPosition((dpyWidth - width - 10) / 2, (dpyHeight - height - 30) / 2);
+ }
+
+ virtual ~TXDialog() {}
+
+ // show() makes the dialog visible. For a modal dialog box, this processes X
+ // events until the done flag has been set, after which it returns the value
+ // of the ok flag. For a non-modal dialog box it always returns true
+ // immediately.
+ bool show() {
+ ok = false;
+ done = false;
+ initDialog();
+ raise();
+ map();
+ if (modal) {
+ while (true) {
+ TXWindow::handleXEvents(dpy);
+ if (done) {
+ return ok;
+ }
+ fd_set rfds;
+ FD_ZERO(&rfds);
+ FD_SET(ConnectionNumber(dpy), &rfds);
+ int n = select(FD_SETSIZE, &rfds, 0, 0, 0);
+ if (n < 0) throw rdr::SystemException("select",errno);
+ }
+ }
+ return true;
+ }
+
+ // initDialog() can be overridden in a derived class. Typically it is used
+ // to make sure that checkboxes have the right state, etc.
+ virtual void initDialog() {}
+
+protected:
+ virtual void deleteWindow(TXWindow* w) {
+ ok = false;
+ done = true;
+ unmap();
+ }
+
+ bool done;
+ bool ok;
+ bool modal;
+};
+
+#endif
diff --git a/tx/TXEntry.h b/tx/TXEntry.h
new file mode 100644
index 00000000..b51946e3
--- /dev/null
+++ b/tx/TXEntry.h
@@ -0,0 +1,188 @@
+/* Copyright (C) 2002-2003 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.
+ */
+//
+// TXEntry.h
+//
+// A TXEntry allows you to enter a single line of text in a window. The entry
+// must be tall enough to contain a line of text - if not then it will be
+// resized appropriately. If the passwd argument to the constructor is true,
+// then the text in the entry will be replaced by asterisks on the screen.
+//
+
+#ifndef __TXENTRY_H__
+#define __TXENTRY_H__
+
+#include "TXWindow.h"
+#include <X11/keysym.h>
+
+#ifndef XK_ISO_Left_Tab
+#define XK_ISO_Left_Tab 0xFE20
+#endif
+
+// TXEntryCallback's entryCallback() method is called when one of three special
+// key presses have happened: Enter/Return, forward tab, or backward tab.
+class TXEntry;
+class TXEntryCallback {
+public:
+ enum Detail { ENTER, NEXT_FOCUS, PREV_FOCUS };
+ virtual void entryCallback(TXEntry* entry, Detail detail, Time time)=0;
+};
+
+
+class TXEntry : public TXWindow, public TXEventHandler {
+public:
+
+ TXEntry(Display* dpy_, TXEntryCallback* cb_=0,
+ TXWindow* parent_=0, bool passwd_=false, int w=1, int h=1)
+ : TXWindow(dpy_, w, h, parent_), cb(cb_),
+ passwd(passwd_), disabled_(false), gotFocus(false)
+ {
+ setEventHandler(this);
+ gc = XCreateGC(dpy, win(), 0, 0);
+ addEventMask(ExposureMask | KeyPressMask | FocusChangeMask
+ | ButtonPressMask);
+ text[0] = 0;
+ int textHeight = (defaultFS->ascent + defaultFS->descent);
+ int newHeight = max(height(), textHeight + yPad*2 + bevel*2);
+ if (height() < newHeight) {
+ resize(width(), newHeight);
+ }
+ }
+
+ virtual ~TXEntry() {
+ XFreeGC(dpy, gc);
+ // overwrite memory used to store password - not critical, but can avoid
+ // accidental exposure of a password in uninitialised memory.
+ if (passwd)
+ memset(text, 0, maxLen);
+ }
+
+ // getText() gets the text in the entry.
+ const char* getText() { return text; }
+
+ // setText() sets the text in the entry.
+ void setText(const char* text_) {
+ strncpy(text, text_, maxLen-1);
+ text[maxLen-1] = 0;
+ paint();
+ }
+
+ // disabled() sets or queries the disabled state of the entry. A disabled
+ // entry cannot have text entered into it.
+ void disabled(bool b) { disabled_ = b; paint(); }
+ bool disabled() { return disabled_; }
+
+private:
+ void paint() {
+ if (disabled_)
+ drawBevel(gc, 0, 0, width(), height(), bevel, disabledBg,darkBg,lightBg);
+ else
+ drawBevel(gc, 0, 0, width(), height(), bevel, enabledBg, darkBg,lightBg);
+ char* str = text;
+ char stars[maxLen];
+ if (passwd) {
+ int i;
+ for (i = 0; i < (int)strlen(text); i++) stars[i] = '*';
+ stars[i] = 0;
+ str = stars;
+ }
+ int tw = XTextWidth(defaultFS, str, strlen(str));
+ int startx = bevel + xPad;
+ if (startx + tw > width() - 2*bevel) {
+ startx = width() - 2*bevel - tw;
+ }
+ XDrawString(dpy, win(), defaultGC, startx,
+ (height() + defaultFS->ascent - defaultFS->descent) / 2,
+ str, strlen(str));
+ if (!disabled_ && gotFocus)
+ XDrawLine(dpy, win(), defaultGC, startx+tw,
+ (height() - defaultFS->ascent - defaultFS->descent) / 2,
+ startx+tw,
+ (height() + defaultFS->ascent + defaultFS->descent) / 2);
+ }
+
+ virtual void handleEvent(TXWindow* w, XEvent* ev) {
+ switch (ev->type) {
+ case Expose:
+ paint();
+ break;
+
+ case FocusIn:
+ gotFocus = true;
+ paint();
+ break;
+
+ case FocusOut:
+ gotFocus = false;
+ paint();
+ break;
+
+ case ButtonPress:
+ if (!disabled_)
+ XSetInputFocus(dpy, win(), RevertToParent, ev->xbutton.time);
+ break;
+
+ case KeyPress:
+ {
+ if (disabled_ || !gotFocus) break;
+ KeySym keysym;
+ XComposeStatus compose;
+ char buf[10];
+ int count = XLookupString(&ev->xkey, buf, 10, &keysym, &compose);
+ if (count >= 1 && buf[0] >= ' ' && buf[0] <= '~') {
+ if (strlen(text) + count >= maxLen) {
+ XBell(dpy, 0);
+ } else {
+ strncat(text, buf, count);
+ paint();
+ }
+ } else if (keysym == XK_BackSpace || keysym == XK_Delete ||
+ keysym == XK_KP_Delete) {
+ if (strlen(text) > 0) {
+ text[strlen(text)-1] = 0;
+ paint();
+ }
+ } else if (keysym == XK_Return || keysym == XK_KP_Enter ||
+ keysym == XK_Linefeed) {
+ if (cb) cb->entryCallback(this, TXEntryCallback::ENTER,
+ ev->xkey.time);
+ } else if ((keysym == XK_Tab || keysym == XK_KP_Tab)
+ && !(ev->xkey.state & ShiftMask))
+ {
+ if (cb) cb->entryCallback(this, TXEntryCallback::NEXT_FOCUS,
+ ev->xkey.time);
+ } else if (((keysym == XK_Tab || keysym == XK_KP_Tab)
+ && (ev->xkey.state & ShiftMask))
+ || keysym == XK_ISO_Left_Tab)
+ {
+ if (cb) cb->entryCallback(this, TXEntryCallback::PREV_FOCUS,
+ ev->xkey.time);
+ }
+ }
+ }
+ }
+ GC gc;
+ enum { maxLen = 256 };
+ char text[maxLen];
+ TXEntryCallback* cb;
+ bool passwd;
+ bool disabled_;
+ bool gotFocus;
+};
+
+#endif
diff --git a/tx/TXImage.cxx b/tx/TXImage.cxx
new file mode 100644
index 00000000..d5b06493
--- /dev/null
+++ b/tx/TXImage.cxx
@@ -0,0 +1,340 @@
+/* Copyright (C) 2002-2004 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.
+ */
+//
+// TXImage.cxx
+//
+
+
+#include <stdio.h>
+#include <strings.h>
+#include <sys/types.h>
+#include <sys/ipc.h>
+#include <sys/shm.h>
+#include <X11/Xlib.h>
+#include <X11/Xutil.h>
+#include <list>
+#include <rfb/TransImageGetter.h>
+#include <rfb/Exception.h>
+#include <rfb/LogWriter.h>
+#include "TXWindow.h"
+#include "TXImage.h"
+
+using namespace rfb;
+
+static rfb::LogWriter vlog("TXImage");
+
+TXImage::TXImage(Display* d, int width, int height, Visual* vis_, int depth_)
+ : xim(0), dpy(d), vis(vis_), depth(depth_), shminfo(0), tig(0), cube(0)
+{
+ width_ = width;
+ height_ = height;
+ for (int i = 0; i < 256; i++)
+ colourMap[i].r = colourMap[i].g = colourMap[i].b = 0;
+
+ if (!vis)
+ vis = DefaultVisual(dpy,DefaultScreen(dpy));
+ if (!depth)
+ depth = DefaultDepth(dpy,DefaultScreen(dpy));
+
+ createXImage();
+ getNativePixelFormat(vis, depth);
+ colourmap = this;
+ format.bpp = 0; // just make it different to any valid format, so that...
+ setPF(nativePF); // ...setPF() always works
+}
+
+TXImage::~TXImage()
+{
+ if (data != (rdr::U8*)xim->data) delete [] data;
+ destroyXImage();
+ delete tig;
+ delete cube;
+}
+
+void TXImage::resize(int w, int h)
+{
+ if (w == width() && h == height()) return;
+
+ int oldStrideBytes = getStride() * (format.bpp/8);
+ int rowsToCopy = min(h, height());
+ int bytesPerRow = min(w, width()) * (format.bpp/8);
+ rdr::U8* oldData = 0;
+ bool allocData = false;
+
+ if (data != (rdr::U8*)xim->data) {
+ oldData = (rdr::U8*)data;
+ allocData = true;
+ } else {
+ oldData = new rdr::U8[xim->bytes_per_line * height()];
+ memcpy(oldData, xim->data, xim->bytes_per_line * height());
+ }
+
+ destroyXImage();
+ width_ = w;
+ height_ = h;
+ createXImage();
+
+ if (allocData)
+ data = new rdr::U8[width() * height() * (format.bpp/8)];
+ else
+ data = (rdr::U8*)xim->data;
+
+ int newStrideBytes = getStride() * (format.bpp/8);
+ for (int i = 0; i < rowsToCopy; i++)
+ memcpy((rdr::U8*)data + newStrideBytes * i, oldData + oldStrideBytes * i,
+ bytesPerRow);
+ delete [] oldData;
+}
+
+void TXImage::setPF(const PixelFormat& newPF)
+{
+ if (newPF.equal(format)) return;
+ format = newPF;
+
+ if (data != (rdr::U8*)xim->data) delete [] data;
+ delete tig;
+ tig = 0;
+
+ if (format.equal(nativePF) && format.trueColour) {
+ data = (rdr::U8*)xim->data;
+ } else {
+ data = new rdr::U8[width() * height() * (format.bpp/8)];
+ tig = new TransImageGetter();
+ tig->init(this, nativePF, 0, cube);
+ }
+}
+
+int TXImage::getStride() const
+{
+ if (data == (rdr::U8*)xim->data)
+ return xim->bytes_per_line / (xim->bits_per_pixel / 8);
+ else
+ return width();
+}
+
+void TXImage::put(Window win, GC gc, const rfb::Rect& r)
+{
+ if (r.is_empty()) return;
+ int x = r.tl.x;
+ int y = r.tl.y;
+ int w = r.width();
+ int h = r.height();
+ if (data != (rdr::U8*)xim->data) {
+ rdr::U8* ximDataStart = ((rdr::U8*)xim->data + y * xim->bytes_per_line
+ + x * (xim->bits_per_pixel / 8));
+ tig->getImage(ximDataStart, r,
+ xim->bytes_per_line / (xim->bits_per_pixel / 8));
+ }
+ if (usingShm()) {
+ XShmPutImage(dpy, win, gc, xim, x, y, x, y, w, h, False);
+ } else {
+ XPutImage(dpy, win, gc, xim, x, y, x, y, w, h);
+ }
+}
+
+void TXImage::setColourMapEntries(int firstColour, int nColours, rdr::U16* rgbs)
+{
+ for (int i = 0; i < nColours; i++) {
+ colourMap[firstColour+i].r = rgbs[i*3];
+ colourMap[firstColour+i].g = rgbs[i*3+1];
+ colourMap[firstColour+i].b = rgbs[i*3+2];
+ }
+}
+
+void TXImage::updateColourMap()
+{
+ tig->setColourMapEntries(0, 0, 0);
+}
+
+void TXImage::lookup(int index, int* r, int* g, int* b)
+{
+ *r = colourMap[index].r;
+ *g = colourMap[index].g;
+ *b = colourMap[index].b;
+}
+
+
+static bool caughtError = false;
+
+static int XShmAttachErrorHandler(Display *dpy, XErrorEvent *error)
+{
+ caughtError = true;
+ return 0;
+}
+
+class TXImageCleanup {
+public:
+ std::list<TXImage*> images;
+ ~TXImageCleanup() {
+ while (!images.empty())
+ delete images.front();
+ }
+};
+
+static TXImageCleanup imageCleanup;
+
+void TXImage::createXImage()
+{
+ if (XShmQueryExtension(dpy)) {
+ shminfo = new XShmSegmentInfo;
+
+ xim = XShmCreateImage(dpy, vis, depth, ZPixmap,
+ 0, shminfo, width(), height());
+
+ if (xim) {
+ shminfo->shmid = shmget(IPC_PRIVATE,
+ xim->bytes_per_line * xim->height,
+ IPC_CREAT|0777);
+
+ if (shminfo->shmid != -1) {
+ shminfo->shmaddr = xim->data = (char*)shmat(shminfo->shmid, 0, 0);
+
+ if (shminfo->shmaddr != (char *)-1) {
+
+ shminfo->readOnly = False;
+
+ XErrorHandler oldHdlr = XSetErrorHandler(XShmAttachErrorHandler);
+ XShmAttach(dpy, shminfo);
+ XSync(dpy, False);
+ XSetErrorHandler(oldHdlr);
+
+ if (!caughtError) {
+ vlog.debug("Using shared memory XImage");
+ imageCleanup.images.push_back(this);
+ return;
+ }
+
+ shmdt(shminfo->shmaddr);
+ } else {
+ vlog.error("shmat failed");
+ perror("shmat");
+ }
+
+ shmctl(shminfo->shmid, IPC_RMID, 0);
+ } else {
+ vlog.error("shmget failed");
+ perror("shmget");
+ }
+
+ XDestroyImage(xim);
+ xim = 0;
+ } else {
+ vlog.error("XShmCreateImage failed");
+ }
+
+ delete shminfo;
+ shminfo = 0;
+ }
+
+ xim = XCreateImage(dpy, vis, depth, ZPixmap,
+ 0, 0, width(), height(), BitmapPad(dpy), 0);
+
+ xim->data = (char*)malloc(xim->bytes_per_line * xim->height);
+ if (!xim->data) {
+ vlog.error("malloc failed");
+ exit(1);
+ }
+}
+
+void TXImage::destroyXImage()
+{
+ if (shminfo) {
+ vlog.debug("Freeing shared memory XImage");
+ shmdt(shminfo->shmaddr);
+ shmctl(shminfo->shmid, IPC_RMID, 0);
+ delete shminfo;
+ shminfo = 0;
+ imageCleanup.images.remove(this);
+ }
+ // XDestroyImage() will free(xim->data) if appropriate
+ if (xim) XDestroyImage(xim);
+ xim = 0;
+}
+
+
+static bool supportedBPP(int bpp) {
+ return (bpp == 8 || bpp == 16 || bpp == 32);
+}
+
+static int depth2bpp(Display* dpy, int depth)
+{
+ int nformats;
+ XPixmapFormatValues* format = XListPixmapFormats(dpy, &nformats);
+
+ int i;
+ for (i = 0; i < nformats; i++)
+ if (format[i].depth == depth) break;
+
+ if (i == nformats || !supportedBPP(format[i].bits_per_pixel))
+ throw rfb::Exception("Error: couldn't find suitable pixmap format");
+
+ int bpp = format[i].bits_per_pixel;
+ XFree(format);
+ return bpp;
+}
+
+void TXImage::getNativePixelFormat(Visual* vis, int depth)
+{
+ cube = 0;
+ nativePF.depth = depth;
+ nativePF.bpp = depth2bpp(dpy, depth);
+ nativePF.bigEndian = (ImageByteOrder(dpy) == MSBFirst);
+ nativePF.trueColour = (vis->c_class == TrueColor);
+
+ vlog.info("Using default colormap and visual, %sdepth %d.",
+ (vis->c_class == TrueColor) ? "TrueColor, " :
+ ((vis->c_class == PseudoColor) ? "PseudoColor, " : ""),
+ depth);
+
+ if (nativePF.trueColour) {
+
+ nativePF.redShift = ffs(vis->red_mask) - 1;
+ nativePF.greenShift = ffs(vis->green_mask) - 1;
+ nativePF.blueShift = ffs(vis->blue_mask) - 1;
+ nativePF.redMax = vis->red_mask >> nativePF.redShift;
+ nativePF.greenMax = vis->green_mask >> nativePF.greenShift;
+ nativePF.blueMax = vis->blue_mask >> nativePF.blueShift;
+
+ } else {
+
+ XColor xc[256];
+ cube = new rfb::ColourCube(6,6,6);
+ int r;
+ for (r = 0; r < cube->nRed; r++) {
+ for (int g = 0; g < cube->nGreen; g++) {
+ for (int b = 0; b < cube->nBlue; b++) {
+ int i = (r * cube->nGreen + g) * cube->nBlue + b;
+ xc[i].red = r * 65535 / (cube->nRed-1);
+ xc[i].green = g * 65535 / (cube->nGreen-1);
+ xc[i].blue = b * 65535 / (cube->nBlue-1);
+ }
+ }
+ }
+
+ TXWindow::getColours(dpy, xc, cube->size());
+
+ for (r = 0; r < cube->nRed; r++) {
+ for (int g = 0; g < cube->nGreen; g++) {
+ for (int b = 0; b < cube->nBlue; b++) {
+ int i = (r * cube->nGreen + g) * cube->nBlue + b;
+ cube->set(r, g, b, xc[i].pixel);
+ }
+ }
+ }
+ }
+}
diff --git a/tx/TXImage.h b/tx/TXImage.h
new file mode 100644
index 00000000..a90a6945
--- /dev/null
+++ b/tx/TXImage.h
@@ -0,0 +1,89 @@
+/* Copyright (C) 2002-2003 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.
+ */
+//
+// TXImage.h
+//
+// A TXImage represents a rectangular off-screen image in any RFB pixel format.
+// By default it will use the "native" pixel format for the screen, which will
+// be an 8-bit colourmap unless the X display is TrueColor. The pixel format
+// can be changed via the setPF() method. The pixel data is accessible via the
+// data member inherited from FullFramePixelBuffer, or can be set via the
+// fillRect(), imageRect(), copyRect() and maskRect() methods, also inherited
+// from PixelBuffer. A rectangle of the image can be drawn into an X Window
+// via the put() method. If using a colourmap, the setColourMapEntries() and
+// updateColourMap() methods must be called to set up the colourmap as
+// appropriate.
+
+
+#ifndef __TXIMAGE_H__
+#define __TXIMAGE_H__
+
+#include <X11/Xlib.h>
+#include <rfb/PixelBuffer.h>
+#include <rfb/ColourMap.h>
+#include <rfb/ColourCube.h>
+#include <X11/extensions/XShm.h>
+
+namespace rfb { class TransImageGetter; }
+
+class TXImage : public rfb::FullFramePixelBuffer, public rfb::ColourMap {
+public:
+ TXImage(Display* dpy, int width, int height, Visual* vis=0, int depth=0);
+ ~TXImage();
+
+ // resize() resizes the image, preserving the image data where possible.
+ void resize(int w, int h);
+
+ // put causes the given rectangle to be drawn onto the given window.
+ void put(Window win, GC gc, const rfb::Rect& r);
+
+ // setColourMapEntries() changes some of the entries in the colourmap.
+ // However these settings won't take effect until updateColourMap() is
+ // called. This is because recalculating the internal translation table can
+ // be expensive.
+ void setColourMapEntries(int firstColour, int nColours, rdr::U16* rgbs);
+ void updateColourMap();
+
+ bool usingShm() { return shminfo; }
+
+ // PixelBuffer methods
+ // width(), height(), getPF() etc are inherited from PixelBuffer
+ virtual void setPF(const rfb::PixelFormat& pf);
+ virtual int getStride() const;
+
+private:
+
+ // ColourMap method
+ virtual void lookup(int index, int* r, int* g, int* b);
+
+ void createXImage();
+ void destroyXImage();
+ void getNativePixelFormat(Visual* vis, int depth);
+
+ XImage* xim;
+ Display* dpy;
+ Visual* vis;
+ int depth;
+ XShmSegmentInfo* shminfo;
+ rfb::TransImageGetter* tig;
+ rfb::Colour colourMap[256];
+ rfb::PixelFormat nativePF;
+ rfb::ColourCube* cube;
+};
+
+#endif
diff --git a/tx/TXLabel.h b/tx/TXLabel.h
new file mode 100644
index 00000000..44f70477
--- /dev/null
+++ b/tx/TXLabel.h
@@ -0,0 +1,126 @@
+/* Copyright (C) 2002-2003 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.
+ */
+//
+// TXLabel.h
+//
+// An TXLabel allows you to put up multiline text in a window with various
+// alignments. The label must be big enough to contain the text - if not then
+// it will be resized appropriately.
+//
+
+#ifndef __TXLABEL_H__
+#define __TXLABEL_H__
+
+#include <stdlib.h>
+#include "TXWindow.h"
+#include <rfb/util.h>
+
+class TXLabel : public TXWindow, public TXEventHandler {
+public:
+ enum HAlign { left, centre, right };
+ enum VAlign { top, middle, bottom };
+
+ TXLabel(Display* dpy_, const char* text_, TXWindow* parent_=0,
+ int w=1, int h=1, HAlign ha=centre, VAlign va=middle)
+ : TXWindow(dpy_, w, h, parent_), lineSpacing(2), lines(0),
+ halign(ha), valign(va)
+ {
+ setEventHandler(this);
+ setText(text_);
+ addEventMask(ExposureMask);
+ }
+
+ // setText() changes the text in the label.
+ void setText(const char* text_) {
+ text.buf = rfb::strDup(text_);
+ lines = 0;
+ int lineStart = 0;
+ int textWidth = 0;
+ int i = -1;
+ do {
+ i++;
+ if (text.buf[i] == '\n' || text.buf[i] == 0) {
+ int tw = XTextWidth(defaultFS, &text.buf[lineStart], i-lineStart);
+ if (tw > textWidth) textWidth = tw;
+ lineStart = i+1;
+ lines++;
+ }
+ } while (text.buf[i] != 0);
+ int textHeight = ((defaultFS->ascent + defaultFS->descent + lineSpacing)
+ * lines);
+
+ int newWidth = max(width(), textWidth + xPad*2);
+ int newHeight = max(height(), textHeight + yPad*2);
+ if (width() < newWidth || height() < newHeight) {
+ resize(newWidth, newHeight);
+ }
+ }
+
+private:
+ int xOffset(int textWidth) {
+ switch (halign) {
+ case left: return xPad;
+ case right: return width() - xPad - textWidth;
+ default: return (width() - textWidth) / 2;
+ }
+ }
+
+ int yOffset(int lineNum) {
+ int textHeight = ((defaultFS->ascent + defaultFS->descent + lineSpacing)
+ * lines);
+ int lineOffset = ((defaultFS->ascent + defaultFS->descent + lineSpacing)
+ * lineNum + defaultFS->ascent);
+ switch (valign) {
+ case top: return yPad + lineOffset;
+ case bottom: return height() - yPad - textHeight + lineOffset;
+ default: return (height() - textHeight) / 2 + lineOffset;
+ }
+ }
+
+ void paint() {
+ int lineNum = 0;
+ int lineStart = 0;
+ int i = -1;
+ do {
+ i++;
+ if (text.buf[i] == '\n' || text.buf[i] == 0) {
+ int tw = XTextWidth(defaultFS, &text.buf[lineStart], i-lineStart);
+ XDrawString(dpy, win(), defaultGC, xOffset(tw), yOffset(lineNum),
+ &text.buf[lineStart], i-lineStart);
+ lineStart = i+1;
+ lineNum++;
+ }
+ } while (text.buf[i] != 0);
+ }
+
+ virtual void handleEvent(TXWindow* w, XEvent* ev) {
+ switch (ev->type) {
+ case Expose:
+ paint();
+ break;
+ }
+ }
+
+ int lineSpacing;
+ rfb::CharArray text;
+ int lines;
+ HAlign halign;
+ VAlign valign;
+};
+
+#endif
diff --git a/tx/TXMenu.cxx b/tx/TXMenu.cxx
new file mode 100644
index 00000000..4069f2d2
--- /dev/null
+++ b/tx/TXMenu.cxx
@@ -0,0 +1,186 @@
+/* Copyright (C) 2002-2003 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.
+ */
+//
+// TXMenu.cxx
+//
+
+#include "TXMenu.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+#include <rfb/util.h>
+#include <X11/keysym.h>
+
+TXMenu::TXMenu(Display* dpy_, TXMenuCallback* cb_, int w, int h,
+ TXWindow* parent_)
+ : TXWindow(dpy_, w, h, parent_), cb(cb_), nEntries(0),
+ highlight(-1)
+{
+ setEventHandler(this);
+ gc = XCreateGC(dpy, win(), 0, 0);
+ addEventMask(ExposureMask | ButtonPressMask | ButtonReleaseMask |
+ PointerMotionMask | EnterWindowMask | LeaveWindowMask);
+}
+
+TXMenu::~TXMenu()
+{
+ XFreeGC(dpy, gc);
+ for (int i = 0; i < nEntries; i++)
+ delete [] text[i];
+}
+
+inline int TXMenu::entryHeight(int i)
+{
+ if (text[i])
+ return defaultFS->ascent + defaultFS->descent + bevel*2 + yPad*2;
+ else
+ return yPad*2 + 1;
+}
+
+void TXMenu::addEntry(const char* text_, long id_)
+{
+ assert(nEntries < maxEntries);
+ text[nEntries] = rfb::strDup(text_);
+ checked[nEntries] = false;
+ id[nEntries++] = id_;
+ int tw = 0;
+ if (text_)
+ tw = XTextWidth(defaultFS, text_, strlen(text_));
+ int newWidth = width();
+ if (tw + bevel*2 + xPad*5 + tickSize > width())
+ newWidth = tw + bevel*2 + xPad*5 + tickSize;
+ int newHeight = 0;
+ for (int i = 0; i < nEntries; i++)
+ newHeight += entryHeight(i);
+ resize(newWidth, newHeight);
+}
+
+void TXMenu::check(long id_, bool checked_)
+{
+ for (int i = 0; i < nEntries; i++) {
+ if (id[i] == id_) {
+ checked[i] = checked_;
+ break;
+ }
+ }
+}
+
+void TXMenu::paint()
+{
+ int y = 0;
+ for (int i = 0; i < nEntries; i++) {
+ if (text[i]) {
+ if (i == highlight)
+ drawBevel(gc, 0, y, width(), entryHeight(i), bevel,
+ defaultBg, darkBg, lightBg);
+ else
+ XClearArea(dpy, win(), 0, y, width(), entryHeight(i), false);
+ if (checked[i])
+ XCopyPlane(dpy, tick, win(), defaultGC, 0, 0, tickSize, tickSize,
+ bevel + xPad,
+ y + bevel + yPad + defaultFS->ascent - tickSize, 1);
+
+ XDrawImageString(dpy, win(), defaultGC, bevel + xPad*2 + tickSize,
+ y + bevel + yPad + defaultFS->ascent,
+ text[i], strlen(text[i]));
+ } else {
+ XDrawLine(dpy, win(), defaultGC, bevel + xPad, y + entryHeight(i) / 2,
+ width() - bevel - xPad, y + entryHeight(i) / 2);
+ }
+ y += entryHeight(i);
+ }
+}
+
+void TXMenu::handleEvent(TXWindow* w, XEvent* ev)
+{
+ switch (ev->type) {
+ case Expose:
+ paint();
+ break;
+
+ case ButtonRelease:
+ {
+ int y = ev->xmotion.y;
+ int entryY = 0;
+ for (int i = 0; i < nEntries; i++) {
+ if (y >= entryY && y <= entryY + entryHeight(i)) {
+ if (cb && text[i])
+ cb->menuSelect(id[i], this);
+ break;
+ }
+ entryY += entryHeight(i);
+ }
+ highlight = -1;
+ paint();
+ break;
+ }
+
+ case ButtonPress:
+ case MotionNotify:
+ {
+ int y = ev->xmotion.y;
+ int entryY = 0;
+ for (int i = 0; i < nEntries; i++) {
+ if (y >= entryY && y <= entryY + entryHeight(i)) {
+ if (highlight != i) {
+ highlight = i;
+ paint();
+ }
+ break;
+ }
+ entryY += entryHeight(i);
+ }
+ break;
+ }
+
+ case KeyPress:
+ {
+ KeySym ks;
+ char str[256];
+ XLookupString(&ev->xkey, str, 256, &ks, NULL);
+ if (ks == XK_Escape) {
+ highlight = -1;
+ unmap();
+ } else if (ks == XK_Down || ks == XK_Up) {
+ if (nEntries < 1) break;
+ if (highlight < 0)
+ highlight = (ks == XK_Down ? nEntries-1 : 0);
+ int start = highlight;
+ int inc = (ks == XK_Down ? 1 : nEntries-1);
+ do {
+ highlight = (highlight + inc) % nEntries;
+ } while (highlight != start && !text[highlight]);
+ paint();
+ } else if (ks == XK_space || ks == XK_KP_Space ||
+ ks == XK_Return || ks == XK_KP_Enter) {
+ if (cb && highlight >= 0 && text[highlight])
+ cb->menuSelect(id[highlight], this);
+ highlight = -1;
+ paint();
+ }
+ break;
+ }
+
+ case EnterNotify:
+ case LeaveNotify:
+ highlight = -1;
+ paint();
+ break;
+ }
+}
diff --git a/tx/TXMenu.h b/tx/TXMenu.h
new file mode 100644
index 00000000..82dafa56
--- /dev/null
+++ b/tx/TXMenu.h
@@ -0,0 +1,67 @@
+/* Copyright (C) 2002-2003 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.
+ */
+//
+// TXMenu.h
+//
+// A TXMenu consists of multiple entries which can be added one at a time.
+// Each entry consists of some text, and has an associated integer identifier.
+// A callback is made when a menu entry is selected.
+//
+
+#ifndef __TXMENU_H__
+#define __TXMENU_H__
+
+#include "TXWindow.h"
+
+// TXMenuCallback's menuSelect() method is called when a particular menu entry
+// is selected. The id argument identifies the menu entry.
+class TXMenu;
+class TXMenuCallback {
+public:
+ virtual void menuSelect(long id, TXMenu* menu)=0;
+};
+
+class TXMenu : public TXWindow, public TXEventHandler {
+public:
+ TXMenu(Display* dpy_, TXMenuCallback* cb=0, int width=1, int height=1,
+ TXWindow* parent_=0);
+ virtual ~TXMenu();
+
+ // addEntry() adds an entry to the end of the menu with the given text and
+ // identifier.
+ void addEntry(const char* text, long id);
+
+ // check() sets whether the given menu entry should have a tick next to it.
+ void check(long id, bool checked);
+
+private:
+ int entryHeight(int i);
+ virtual void handleEvent(TXWindow* w, XEvent* ev);
+ void paint();
+
+ GC gc;
+ TXMenuCallback* cb;
+ enum { maxEntries = 64 };
+ char* text[maxEntries];
+ long id[maxEntries];
+ bool checked[maxEntries];
+ int nEntries;
+ int highlight;
+};
+
+#endif
diff --git a/tx/TXMsgBox.h b/tx/TXMsgBox.h
new file mode 100644
index 00000000..00c4eb5f
--- /dev/null
+++ b/tx/TXMsgBox.h
@@ -0,0 +1,111 @@
+/* Copyright (C) 2002-2004 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.
+ */
+//
+// TXMsgBox.h
+//
+// A TXMsgBox is a specialised pop-up dialog window, designed to present
+// the user with a small amount of textual information, and potentially to
+// obtain their response.
+// TXMsgBoxes are always modal, and may have an Ok button, Ok+Cancel buttons,
+// or Yes+No buttons.
+// The MsgBox helper function creates a TXMsgBox on the fly, runs it, and
+// returns the result.
+//
+
+#ifndef __TXMSGBOX_H__
+#define __TXMSGBOX_H__
+
+#include "TXDialog.h"
+#include "TXLabel.h"
+#include "TXButton.h"
+
+enum TXMsgBoxFlags {
+ MB_OK = 0,
+ MB_OKCANCEL = 1,
+ MB_YESNO = 4,
+ MB_ICONERROR = 0x10,
+ MB_ICONQUESTION = 0x20,
+ MB_ICONWARNING = 0x30,
+ MB_ICONINFORMATION = 0x40,
+ MB_DEFBUTTON1 = 0,
+ MB_DEFBUTTON2 = 0x100
+};
+
+class TXMsgBox : public TXDialog, public TXButtonCallback {
+public:
+ TXMsgBox(Display* dpy, const char* text, unsigned int flags, const char* title=0)
+ : TXDialog(dpy, 1, 1, "Message"),
+ textLabel(dpy, "", this),
+ okButton(dpy, "OK", this, this, 60),
+ cancelButton(dpy, "Cancel", this, this, 60)
+ {
+ textLabel.xPad = 8;
+ textLabel.move(0, yPad*4);
+ textLabel.setText(text);
+ resize(textLabel.width(),
+ textLabel.height() + okButton.height() + yPad*12);
+
+ switch (flags & 0x30) {
+ case MB_ICONERROR:
+ toplevel("Error", this); break;
+ case MB_ICONQUESTION:
+ toplevel("Question", this); break;
+ case MB_ICONWARNING:
+ toplevel("Warning", this); break;
+ case MB_ICONINFORMATION:
+ toplevel("Information", this); break;
+ default:
+ if (title)
+ toplevel(title, this);
+ break;
+ };
+
+ switch (flags & 0x7) {
+ default:
+ okButton.move((width() - okButton.width()) / 2,
+ height() - yPad*4 - okButton.height());
+ cancelButton.unmap();
+ break;
+ case MB_OKCANCEL:
+ case MB_YESNO:
+
+ okButton.move(((width()/2) - okButton.width()) / 2,
+ height() - yPad*4 - okButton.height());
+ cancelButton.move(((width()*3/2) - cancelButton.width()) / 2,
+ height() - yPad*4 - cancelButton.height());
+ if ((flags & 0x7) == MB_YESNO) {
+ okButton.setText("Yes");
+ cancelButton.setText("No");
+ }
+ break;
+ };
+
+ setBorderWidth(1);
+ }
+
+ virtual void buttonActivate(TXButton* b) {
+ ok = (b == &okButton);
+ unmap();
+ }
+
+ TXLabel textLabel;
+ TXButton okButton;
+ TXButton cancelButton;
+};
+
+#endif
diff --git a/tx/TXScrollbar.cxx b/tx/TXScrollbar.cxx
new file mode 100644
index 00000000..946ccffc
--- /dev/null
+++ b/tx/TXScrollbar.cxx
@@ -0,0 +1,119 @@
+/* Copyright (C) 2002-2003 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.
+ */
+//
+// TXScrollbar.cxx
+//
+
+#include "TXScrollbar.h"
+#include <stdio.h>
+#include <assert.h>
+
+TXScrollbar::TXScrollbar(Display* dpy_, int width, int height, bool vert,
+ TXScrollbarCallback* cb_, TXWindow* parent_)
+ : TXWindow(dpy_, width, height, parent_), cb(cb_), vertical(vert),
+ clickedInThumb(false)
+{
+ setEventHandler(this);
+ gc = XCreateGC(dpy, win(), 0, 0);
+ addEventMask(ExposureMask | ButtonPressMask | ButtonReleaseMask |
+ ButtonMotionMask);
+ setBg(scrollbarBg);
+ limit[0] = len[0] = limit[1] = len[1] = 1;
+ start[0] = start[1] = 0;
+}
+
+TXScrollbar::~TXScrollbar()
+{
+ XFreeGC(dpy, gc);
+}
+
+void TXScrollbar::set(int limit_, int start_, int len_, bool vert)
+{
+ assert(limit_ > 0 && len_ >= 0 && len_ <= limit_);
+
+ if (start_ < 0) start_ = 0;
+ if (start_ > limit_ - len_) start_ = limit_ - len_;
+
+ if (limit[vert] != limit_ || start[vert] != start_ || len[vert] != len_) {
+ limit[vert] = limit_;
+ start[vert] = start_;
+ len[vert] = len_;
+ paint();
+ }
+}
+
+void TXScrollbar::paint()
+{
+ int x = scaleToBarX(start[0]);
+ int y = scaleToBarY(start[1]);
+ int w = scaleToBarX(len[0]);
+ int h = scaleToBarY(len[1]);
+ if (y > 0) XClearArea(dpy, win(), 0, 0, 0, y, false);
+ if (x > 0) XClearArea(dpy, win(), 0, y, x, y+h, false);
+ XClearArea(dpy, win(), x+w, y, 0, y+h, false);
+ XClearArea(dpy, win(), 0, y+h, 0, 0, false);
+ drawBevel(gc, x, y, w, h, bevel, defaultBg, lightBg, darkBg);
+}
+
+void TXScrollbar::handleEvent(TXWindow* w, XEvent* ev)
+{
+ switch (ev->type) {
+ case Expose:
+ paint();
+ break;
+
+ case ButtonPress:
+ {
+ xDown = ev->xbutton.x;
+ yDown = ev->xbutton.y;
+ xStart = start[0];
+ yStart = start[1];
+ bool clickedInThumbX = false;
+ if (xDown < scaleToBarX(start[0])) {
+ set(limit[0], start[0] - len[0], len[0], false);
+ } else if (xDown >= scaleToBarX(start[0]+len[0])) {
+ set(limit[0], start[0] + len[0], len[0], false);
+ } else {
+ clickedInThumbX = true;
+ }
+ bool clickedInThumbY = false;
+ if (yDown < scaleToBarY(start[1])) {
+ set(limit[1], start[1] - len[1], len[1], true);
+ } else if (yDown >= scaleToBarY(start[1]+len[1])) {
+ set(limit[1], start[1] + len[1], len[1], true);
+ } else {
+ clickedInThumbY = true;
+ }
+ clickedInThumb = clickedInThumbX && clickedInThumbY;
+ if (cb) cb->scrollbarPos(start[0], start[1], this);
+ }
+ break;
+
+ case ButtonRelease:
+ case MotionNotify:
+ while (XCheckTypedWindowEvent(dpy, win(), MotionNotify, ev));
+ if (clickedInThumb) {
+ int dx = ev->xmotion.x - xDown;
+ int dy = ev->xmotion.y - yDown;
+ set(limit[0], xStart + barToScaleX(dx), len[0], false);
+ set(limit[1], yStart + barToScaleY(dy), len[1], true);
+ if (cb) cb->scrollbarPos(start[0], start[1], this);
+ }
+ break;
+ }
+}
diff --git a/tx/TXScrollbar.h b/tx/TXScrollbar.h
new file mode 100644
index 00000000..4cc2afa3
--- /dev/null
+++ b/tx/TXScrollbar.h
@@ -0,0 +1,82 @@
+/* Copyright (C) 2002-2003 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.
+ */
+//
+// TXScrollbar.h
+//
+// A TXScrollbar represents a range of values starting at start, of length len,
+// between zero and limit. The vertical argument to the constructor says
+// whether the scrollbar is horizontal or vertical.
+//
+// In fact it can represent a range in each dimension but usually one of the
+// dimensions is fixed, according to the vertical flag (for a vertical
+// scrollbar, the horizontal dimension is fixed, and vice-versa).
+//
+// The TXScrollbarCallback argument is an object which will be notified when
+// the user has attempted to move the scrollbar. The x and y arguments to the
+// scrollbarPos() method give the start values in the respective dimensions.
+// They are guaranteed to be between 0 and limit-len.
+//
+
+#ifndef __TXSCROLLBAR_H__
+#define __TXSCROLLBAR_H__
+
+#include "TXWindow.h"
+
+class TXScrollbarCallback;
+
+class TXScrollbar : public TXWindow, public TXEventHandler {
+public:
+ TXScrollbar(Display* dpy_, int width=1, int height=1, bool vertical=false,
+ TXScrollbarCallback* cb=0, TXWindow* parent_=0);
+ virtual ~TXScrollbar();
+
+ // set() sets the limit, start and length of the range represented by the
+ // scrollbar. The values of limit and len passed in must be valid
+ // (i.e. limit > 0 and 0 <= len <= limit). Values of start are clipped to
+ // the range 0 to limit-len.
+ void set(int limit, int start, int len) { set(limit, start, len, vertical); }
+
+ // set() with an extra argument vert can be used to represent a range in both
+ // dimensions simultaneously.
+ void set(int limit, int start, int len, bool vert);
+
+ virtual void handleEvent(TXWindow* w, XEvent* ev);
+
+private:
+ int scaleToBarX(int x) { return (x * width() + limit[0]/2) / limit[0]; }
+ int scaleToBarY(int y) { return (y * height() + limit[1]/2) / limit[1]; }
+ int barToScaleX(int x) { return (x * limit[0] + width()/2) / width(); }
+ int barToScaleY(int y) { return (y * limit[1] + height()/2) / height(); }
+ void paint();
+
+ GC gc;
+ TXScrollbarCallback* cb;
+ int limit[2];
+ int start[2];
+ int len[2];
+ int xDown, yDown;
+ int xStart, yStart;
+ bool vertical;
+ bool clickedInThumb;
+};
+
+class TXScrollbarCallback {
+public:
+ virtual void scrollbarPos(int x, int y, TXScrollbar* sb)=0;
+};
+#endif
diff --git a/tx/TXViewport.cxx b/tx/TXViewport.cxx
new file mode 100644
index 00000000..67982e9f
--- /dev/null
+++ b/tx/TXViewport.cxx
@@ -0,0 +1,157 @@
+/* Copyright (C) 2002-2003 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.
+ */
+//
+// TXViewport.cxx
+//
+
+#include "TXViewport.h"
+#include <stdio.h>
+
+TXViewport::TXViewport(Display* dpy_, int w, int h, TXWindow* parent_)
+ : TXWindow(dpy_, w, h, parent_), child(0), hScrollbar(0),
+ vScrollbar(0), scrollbarSize(15), xOff(0), yOff(0), bumpScrollTimer(this),
+ bumpScroll(false), needScrollbars(false), bumpScrollX(0), bumpScrollY(0)
+{
+ clipper = new TXWindow(dpy, width()-scrollbarSize, height()-scrollbarSize,
+ this);
+ clipper->setBg(black);
+ hScrollbar = new TXScrollbar(dpy, width()-scrollbarSize, scrollbarSize,
+ false, this, this);
+ vScrollbar = new TXScrollbar(dpy, scrollbarSize, height()-scrollbarSize,
+ true, this, this);
+}
+
+TXViewport::~TXViewport()
+{
+ delete clipper;
+ delete hScrollbar;
+ delete vScrollbar;
+}
+
+void TXViewport::setChild(TXWindow* child_)
+{
+ child = child_;
+ XReparentWindow(dpy, child->win(), clipper->win(), 0, 0);
+ xOff = yOff = 0;
+ child->map();
+ resizeNotify();
+}
+
+bool TXViewport::setOffset(int x, int y)
+{
+ if (clipper->width() >= child->width()) {
+ x = (clipper->width() - child->width()) / 2;
+ } else {
+ if (x > 0) x = 0;
+ if (x + child->width() < clipper->width())
+ x = clipper->width() - child->width();
+ }
+
+ if (clipper->height() >= child->height()) {
+ y = (clipper->height() - child->height()) / 2;
+ } else {
+ if (y > 0) y = 0;
+ if (y + child->height() < clipper->height())
+ y = clipper->height() - child->height();
+ }
+
+ if (x != xOff || y != yOff) {
+ xOff = x;
+ yOff = y;
+ child->move(xOff, yOff);
+ return true;
+ }
+
+ return false;
+}
+
+void TXViewport::setBumpScroll(bool b)
+{
+ bumpScroll = b;
+ resizeNotify();
+}
+
+// Note: bumpScrollEvent() only works if the viewport is positioned at 0,0 and
+// is the same width and height as the screen.
+bool TXViewport::bumpScrollEvent(XMotionEvent* ev)
+{
+ if (!bumpScroll) return false;
+ int bumpScrollPixels = 20;
+ bumpScrollX = bumpScrollY = 0;
+
+ if (ev->x_root == width()-1) bumpScrollX = -bumpScrollPixels;
+ else if (ev->x_root == 0) bumpScrollX = bumpScrollPixels;
+ if (ev->y_root == height()-1) bumpScrollY = -bumpScrollPixels;
+ else if (ev->y_root == 0) bumpScrollY = bumpScrollPixels;
+
+ if (bumpScrollX || bumpScrollY) {
+ if (bumpScrollTimer.isSet()) return true;
+ if (setOffset(xOff + bumpScrollX, yOff + bumpScrollY)) {
+ bumpScrollTimer.reset(25);
+ return true;
+ }
+ }
+
+ bumpScrollTimer.cancel();
+ return false;
+}
+
+void TXViewport::timerCallback(Timer* timer)
+{
+ if (setOffset(xOff + bumpScrollX, yOff + bumpScrollY))
+ bumpScrollTimer.reset(25);
+}
+
+void TXViewport::resizeNotify()
+{
+ needScrollbars = (!bumpScroll &&
+ (width() < child->width() || height() < child->height()) &&
+ (width() > scrollbarSize && height() > scrollbarSize));
+ if (needScrollbars) {
+ clipper->resize(width()-scrollbarSize, height()-scrollbarSize);
+ hScrollbar->map();
+ vScrollbar->map();
+ } else {
+ clipper->resize(width(), height());
+ hScrollbar->unmap();
+ vScrollbar->unmap();
+ }
+
+ setOffset(xOff, yOff);
+
+ if (needScrollbars) {
+ hScrollbar->move(0, height()-scrollbarSize);
+ hScrollbar->resize(width()-scrollbarSize, scrollbarSize);
+ hScrollbar->set(child->width(), -xOff, width()-scrollbarSize);
+ vScrollbar->move(width()-scrollbarSize, 0);
+ vScrollbar->resize(scrollbarSize, height()-scrollbarSize);
+ vScrollbar->set(child->height(), -yOff, height()-scrollbarSize);
+ }
+}
+
+void TXViewport::scrollbarPos(int x, int y, TXScrollbar* sb)
+{
+ if (sb == hScrollbar) {
+ x = -x;
+ y = yOff;
+ } else {
+ x = xOff;
+ y = -y;
+ }
+ setOffset(x, y);
+}
diff --git a/tx/TXViewport.h b/tx/TXViewport.h
new file mode 100644
index 00000000..9dddc2f8
--- /dev/null
+++ b/tx/TXViewport.h
@@ -0,0 +1,77 @@
+/* Copyright (C) 2002-2003 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.
+ */
+//
+// TXViewport.h
+//
+// A TXViewport allows a large window to be viewed by adding scrollbars to the
+// right and bottom if necessary. It also has a bump-scroll mode where there
+// are no scrollbars, and scrolling is achieved by bumping up against the edge
+// of the screen instead. Note that this only works when the viewport fills
+// the entire screen. If the child window is smaller than the viewport, it is
+// always positioned centrally in the viewport.
+
+#ifndef __TXVIEWPORT_H__
+#define __TXVIEWPORT_H__
+
+#include "TXWindow.h"
+#include "TXScrollbar.h"
+#include "Timer.h"
+
+class TXViewport : public TXWindow, public TXScrollbarCallback,
+ public TimerCallback {
+public:
+ TXViewport(Display* dpy_, int width, int height, TXWindow* parent_=0);
+ virtual ~TXViewport();
+
+ // setChild() sets the child window which is to be viewed in the viewport.
+ void setChild(TXWindow* child_);
+
+ // setOffset() sets the position of the child in the viewport. Note that the
+ // offsets are negative. For example when the offset is (-100,-30), position
+ // (100,30) in the child window is at the top-left of the viewport. The
+ // offsets given are clipped to keep the child window filling the viewport
+ // (except where the child window is smaller than the viewport, in which case
+ // it is always positioned centrally in the viewport). It returns true if
+ // the child was repositioned.
+ bool setOffset(int x, int y);
+
+ // setBumpScroll() puts the viewport in bump-scroll mode.
+ void setBumpScroll(bool b);
+
+ // bumpScrollEvent() can be called with a MotionNotify event which may
+ // potentially be against the edge of the screen. It returns true if the
+ // event was used for bump-scrolling, false if it should be processed
+ // normally.
+ bool bumpScrollEvent(XMotionEvent* ev);
+
+private:
+ virtual void resizeNotify();
+ virtual void scrollbarPos(int x, int y, TXScrollbar* sb);
+ virtual void timerCallback(Timer* timer);
+ TXWindow* clipper;
+ TXWindow* child;
+ TXScrollbar* hScrollbar;
+ TXScrollbar* vScrollbar;
+ const int scrollbarSize;
+ int xOff, yOff;
+ Timer bumpScrollTimer;
+ bool bumpScroll;
+ bool needScrollbars;
+ int bumpScrollX, bumpScrollY;
+};
+#endif
diff --git a/tx/TXWindow.cxx b/tx/TXWindow.cxx
new file mode 100644
index 00000000..a649de40
--- /dev/null
+++ b/tx/TXWindow.cxx
@@ -0,0 +1,486 @@
+/* Copyright (C) 2002-2004 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.
+ */
+//
+// TXWindow.cxx
+//
+
+#include <X11/Xatom.h>
+#include "TXWindow.h"
+#include <list>
+#include <stdio.h>
+#include <stdlib.h>
+#include <rfb/util.h>
+
+std::list<TXWindow*> windows;
+
+Atom wmProtocols, wmDeleteWindow, wmTakeFocus;
+Atom xaTIMESTAMP, xaTARGETS, xaSELECTION_TIME, xaSELECTION_STRING;
+Atom xaCLIPBOARD;
+unsigned long TXWindow::black, TXWindow::white;
+unsigned long TXWindow::defaultFg, TXWindow::defaultBg;
+unsigned long TXWindow::lightBg, TXWindow::darkBg;
+unsigned long TXWindow::disabledFg, TXWindow::disabledBg;
+unsigned long TXWindow::enabledBg;
+unsigned long TXWindow::scrollbarBg;
+Colormap TXWindow::cmap = 0;
+GC TXWindow::defaultGC = 0;
+Font TXWindow::defaultFont = 0;
+XFontStruct* TXWindow::defaultFS = 0;
+Time TXWindow::cutBufferTime = 0;
+Pixmap TXWindow::dot = 0, TXWindow::tick = 0;
+const int TXWindow::dotSize = 4, TXWindow::tickSize = 8;
+char* TXWindow::defaultWindowClass;
+
+void TXWindow::init(Display* dpy, const char* defaultWindowClass_)
+{
+ cmap = DefaultColormap(dpy,DefaultScreen(dpy));
+ wmProtocols = XInternAtom(dpy, "WM_PROTOCOLS", False);
+ wmDeleteWindow = XInternAtom(dpy, "WM_DELETE_WINDOW", False);
+ wmTakeFocus = XInternAtom(dpy, "WM_TAKE_FOCUS", False);
+ xaTIMESTAMP = XInternAtom(dpy, "TIMESTAMP", False);
+ xaTARGETS = XInternAtom(dpy, "TARGETS", False);
+ xaSELECTION_TIME = XInternAtom(dpy, "SELECTION_TIME", False);
+ xaSELECTION_STRING = XInternAtom(dpy, "SELECTION_STRING", False);
+ xaCLIPBOARD = XInternAtom(dpy, "CLIPBOARD", False);
+ XColor cols[6];
+ cols[0].red = cols[0].green = cols[0].blue = 0x0000;
+ cols[1].red = cols[1].green = cols[1].blue = 0xbbbb;
+ cols[2].red = cols[2].green = cols[2].blue = 0xeeee;
+ cols[3].red = cols[3].green = cols[3].blue = 0x5555;
+ cols[4].red = cols[4].green = cols[4].blue = 0x8888;
+ cols[5].red = cols[5].green = cols[5].blue = 0xffff;
+ getColours(dpy, cols, 6);
+ black = defaultFg = cols[0].pixel;
+ defaultBg = disabledBg = cols[1].pixel;
+ lightBg = cols[2].pixel;
+ darkBg = disabledFg = cols[3].pixel;
+ scrollbarBg = cols[4].pixel;
+ white = enabledBg = cols[5].pixel;
+ defaultGC = XCreateGC(dpy, DefaultRootWindow(dpy), 0, 0);
+ defaultFS
+ = XLoadQueryFont(dpy, "-*-helvetica-medium-r-*-*-12-*-*-*-*-*-*-*");
+ if (!defaultFS) {
+ defaultFS = XLoadQueryFont(dpy, "fixed");
+ if (!defaultFS) {
+ fprintf(stderr,"Failed to load any font\n");
+ exit(1);
+ }
+ }
+ defaultFont = defaultFS->fid;
+ XSetForeground(dpy, defaultGC, defaultFg);
+ XSetBackground(dpy, defaultGC, defaultBg);
+ XSetFont(dpy, defaultGC, defaultFont);
+ XSelectInput(dpy, DefaultRootWindow(dpy), PropertyChangeMask);
+
+ static char dotBits[] = { 0x06, 0x0f, 0x0f, 0x06};
+ dot = XCreateBitmapFromData(dpy, DefaultRootWindow(dpy), dotBits,
+ dotSize, dotSize);
+ static char tickBits[] = { 0x80, 0xc0, 0xe2, 0x76, 0x3e, 0x1c, 0x08, 0x00};
+ tick = XCreateBitmapFromData(dpy, DefaultRootWindow(dpy), tickBits,
+ tickSize, tickSize);
+ defaultWindowClass = rfb::strDup(defaultWindowClass_);
+}
+
+void TXWindow::handleXEvents(Display* dpy)
+{
+ while (XPending(dpy)) {
+ XEvent ev;
+ XNextEvent(dpy, &ev);
+ if (ev.type == MappingNotify) {
+ XRefreshKeyboardMapping(&ev.xmapping);
+ } else if (ev.type == PropertyNotify &&
+ ev.xproperty.window == DefaultRootWindow(dpy) &&
+ ev.xproperty.atom == XA_CUT_BUFFER0) {
+ cutBufferTime = ev.xproperty.time;
+ } else {
+ std::list<TXWindow*>::iterator i;
+ for (i = windows.begin(); i != windows.end(); i++) {
+ if ((*i)->win() == ev.xany.window)
+ (*i)->handleXEvent(&ev);
+ }
+ }
+ }
+}
+
+void TXWindow::getColours(Display* dpy, XColor* cols, int nCols)
+{
+ bool* got = new bool[nCols];
+ bool failed = false;
+ int i;
+ for (i = 0; i < nCols; i++) {
+ if (XAllocColor(dpy, cmap, &cols[i])) {
+ got[i] = true;
+ } else {
+ got[i] = false;
+ failed = true;
+ }
+ }
+
+ if (!failed) {
+ delete [] got;
+ return;
+ }
+
+ // AllocColor has failed. This is because the colormap is full. So the
+ // only thing we can do is use the "shared" pixels in the colormap. The
+ // code below is designed to work even when the colormap isn't full so is
+ // more complex than it needs to be in this case. However it would be
+ // useful one day to be able to restrict the number of colours allocated by
+ // an application so I'm leaving it in here.
+
+ // For each pixel in the colormap, try to allocate exactly its RGB values.
+ // If this returns a different pixel then it must be a private or
+ // unallocated pixel, so we can't use it. If it returns us the same pixel
+ // again, it's almost certainly a shared colour, so we can use it (actually
+ // it is possible that it was an unallocated pixel which we've now
+ // allocated - by going through the pixels in reverse order we make this
+ // unlikely except for the lowest unallocated pixel - this works because of
+ // the way the X server allocates new pixels).
+
+ int cmapSize = DisplayCells(dpy,DefaultScreen(dpy));
+
+ XColor* cm = new XColor[cmapSize];
+ bool* shared = new bool[cmapSize];
+ bool* usedAsNearest = new bool[cmapSize];
+
+ for (i = 0; i < cmapSize; i++) {
+ cm[i].pixel = i;
+ shared[i] = usedAsNearest[i] = false;
+ }
+
+ XQueryColors(dpy, cmap, cm, cmapSize);
+
+ for (i = cmapSize-1; i >= 0; i--) {
+ if (XAllocColor(dpy, cmap, &cm[i])) {
+ if (cm[i].pixel == (unsigned long)i) {
+ shared[i] = true;
+ } else {
+ XFreeColors(dpy, cmap, &cm[i].pixel, 1, 0);
+ }
+ }
+ }
+
+ for (int j = 0; j < nCols; j++) {
+ unsigned long minDistance = ULONG_MAX;
+ unsigned long nearestPixel = 0;
+ if (!got[j]) {
+ for (i = 0; i < cmapSize; i++) {
+ if (shared[i]) {
+ unsigned long rd = (cm[i].red - cols[j].red)/2;
+ unsigned long gd = (cm[i].green - cols[j].green)/2;
+ unsigned long bd = (cm[i].blue - cols[j].blue)/2;
+ unsigned long distance = (rd*rd + gd*gd + bd*bd);
+
+ if (distance < minDistance) {
+ minDistance = distance;
+ nearestPixel = i;
+ }
+ }
+ }
+
+ cols[j].pixel = nearestPixel;
+ usedAsNearest[nearestPixel] = true;
+ }
+ }
+
+ for (i = 0; i < cmapSize; i++) {
+ if (shared[i] && !usedAsNearest[i]) {
+ unsigned long p = i;
+ XFreeColors(dpy, cmap, &p, 1, 0);
+ }
+ }
+}
+
+Window TXWindow::windowWithName(Display* dpy, Window top, const char* name)
+{
+ char* windowName;
+ if (XFetchName(dpy, top, &windowName)) {
+ if (strcmp(windowName, name) == 0) {
+ XFree(windowName);
+ return top;
+ }
+ XFree(windowName);
+ }
+
+ Window* children;
+ Window dummy;
+ unsigned int nchildren;
+ if (!XQueryTree(dpy, top, &dummy, &dummy, &children,&nchildren) || !children)
+ return 0;
+
+ for (int i = 0; i < (int)nchildren; i++) {
+ Window w = windowWithName(dpy, children[i], name);
+ if (w) {
+ XFree((char*)children);
+ return w;
+ }
+ }
+ XFree((char*)children);
+ return 0;
+}
+
+
+TXWindow::TXWindow(Display* dpy_, int w, int h, TXWindow* parent_,
+ int borderWidth)
+ : dpy(dpy_), xPad(3), yPad(3), bevel(2), parent(parent_), width_(w),
+ height_(h), eventHandler(0), dwc(0), eventMask(0), toplevel_(false)
+{
+ sizeHints.flags = 0;
+ XSetWindowAttributes attr;
+ attr.background_pixel = defaultBg;
+ attr.border_pixel = 0;
+ Window par = parent ? parent->win() : DefaultRootWindow(dpy);
+ win_ = XCreateWindow(dpy, par, 0, 0, width_, height_, borderWidth,
+ CopyFromParent, CopyFromParent, CopyFromParent,
+ CWBackPixel | CWBorderPixel, &attr);
+ if (parent) map();
+
+ windows.push_back(this);
+}
+
+TXWindow::~TXWindow()
+{
+ windows.remove(this);
+ XDestroyWindow(dpy, win());
+}
+
+void TXWindow::toplevel(const char* name, TXDeleteWindowCallback* dwc_,
+ int argc, char** argv, const char* windowClass,
+ bool iconic)
+{
+ toplevel_ = true;
+ XWMHints wmHints;
+ wmHints.flags = InputHint|StateHint;
+ wmHints.input = True;
+ wmHints.initial_state = iconic ? IconicState : NormalState;
+ XClassHint classHint;
+ if (!windowClass) windowClass = defaultWindowClass;
+ classHint.res_name = (char*)name;
+ classHint.res_class = (char*)windowClass;
+ XSetWMProperties(dpy, win(), 0, 0, argv, argc,
+ &sizeHints, &wmHints, &classHint);
+ XStoreName(dpy, win(), name);
+ XSetIconName(dpy, win(), name);
+ Atom protocols[10];
+ int nProtocols = 0;
+ protocols[nProtocols++] = wmTakeFocus;
+ dwc = dwc_;
+ if (dwc)
+ protocols[nProtocols++] = wmDeleteWindow;
+ XSetWMProtocols(dpy, win(), protocols, nProtocols);
+ addEventMask(StructureNotifyMask);
+}
+
+void TXWindow::setMaxSize(int w, int h)
+{
+ sizeHints.flags |= PMaxSize;
+ sizeHints.max_width = w;
+ sizeHints.max_height = h;
+ XSetWMNormalHints(dpy, win(), &sizeHints);
+}
+
+void TXWindow::setUSPosition(int x, int y)
+{
+ sizeHints.flags |= USPosition;
+ sizeHints.x = x;
+ sizeHints.y = y;
+ XSetWMNormalHints(dpy, win(), &sizeHints);
+ move(x, y);
+}
+
+void TXWindow::setGeometry(const char* geom, int x, int y, int w, int h)
+{
+ char defGeom[256];
+ sprintf(defGeom,"%dx%d+%d+%d",w,h,x,y);
+ XWMGeometry(dpy, DefaultScreen(dpy), strEmptyToNull((char*)geom), defGeom,
+ 0, &sizeHints, &x, &y, &w, &h, &sizeHints.win_gravity);
+ sizeHints.flags |= PWinGravity;
+ setUSPosition(x, y);
+ resize(w, h);
+}
+
+TXEventHandler* TXWindow::setEventHandler(TXEventHandler* h)
+{
+ TXEventHandler* old = eventHandler;
+ eventHandler = h;
+ return old;
+}
+
+void TXWindow::addEventMask(long mask)
+{
+ eventMask |= mask;
+ XSelectInput(dpy, win(), eventMask);
+}
+
+void TXWindow::removeEventMask(long mask)
+{
+ eventMask &= ~mask;
+ XSelectInput(dpy, win(), eventMask);
+}
+
+void TXWindow::unmap()
+{
+ XUnmapWindow(dpy, win());
+ if (toplevel_) {
+ XUnmapEvent ue;
+ ue.type = UnmapNotify;
+ ue.display = dpy;
+ ue.event = DefaultRootWindow(dpy);
+ ue.window = win();
+ ue.from_configure = False;
+ XSendEvent(dpy, DefaultRootWindow(dpy), False,
+ (SubstructureRedirectMask|SubstructureNotifyMask),
+ (XEvent*)&ue);
+ }
+}
+
+void TXWindow::resize(int w, int h)
+{
+ //if (w == width_ && h == height_) return;
+ XResizeWindow(dpy, win(), w, h);
+ width_ = w;
+ height_ = h;
+ resizeNotify();
+}
+
+void TXWindow::setBorderWidth(int bw)
+{
+ XWindowChanges c;
+ c.border_width = bw;
+ XConfigureWindow(dpy, win(), CWBorderWidth, &c);
+}
+
+void TXWindow::ownSelection(Atom selection, Time time)
+{
+ XSetSelectionOwner(dpy, selection, win(), time);
+ if (XGetSelectionOwner(dpy, selection) == win()) {
+ selectionOwner_[selection] = true;
+ selectionOwnTime[selection] = time;
+ }
+}
+
+void TXWindow::handleXEvent(XEvent* ev)
+{
+ switch (ev->type) {
+
+ case ClientMessage:
+ if (ev->xclient.message_type == wmProtocols) {
+ if ((Atom)ev->xclient.data.l[0] == wmDeleteWindow) {
+ if (dwc) dwc->deleteWindow(this);
+ } else if ((Atom)ev->xclient.data.l[0] == wmTakeFocus) {
+ takeFocus(ev->xclient.data.l[1]);
+ }
+ }
+ break;
+
+ case ConfigureNotify:
+ if (ev->xconfigure.width != width_ || ev->xconfigure.height != height_) {
+ width_ = ev->xconfigure.width;
+ height_ = ev->xconfigure.height;
+ resizeNotify();
+ }
+ break;
+
+ case SelectionNotify:
+ if (ev->xselection.property != None) {
+ Atom type;
+ int format;
+ unsigned long nitems, after;
+ unsigned char *data;
+ XGetWindowProperty(dpy, win(), ev->xselection.property, 0, 16384, True,
+ AnyPropertyType, &type, &format,
+ &nitems, &after, &data);
+ if (type != None) {
+ selectionNotify(&ev->xselection, type, format, nitems, data);
+ XFree(data);
+ break;
+ }
+ }
+ selectionNotify(&ev->xselection, 0, 0, 0, 0);
+ break;
+
+ case SelectionRequest:
+ {
+ XSelectionEvent se;
+ se.type = SelectionNotify;
+ se.display = ev->xselectionrequest.display;
+ se.requestor = ev->xselectionrequest.requestor;
+ se.selection = ev->xselectionrequest.selection;
+ se.time = ev->xselectionrequest.time;
+ se.target = ev->xselectionrequest.target;
+ if (ev->xselectionrequest.property == None)
+ ev->xselectionrequest.property = ev->xselectionrequest.target;
+ if (!selectionOwner_[se.selection]) {
+ se.property = None;
+ } else {
+ se.property = ev->xselectionrequest.property;
+ if (se.target == xaTARGETS) {
+ Atom targets[2];
+ targets[0] = xaTIMESTAMP;
+ targets[1] = XA_STRING;
+ XChangeProperty(dpy, se.requestor, se.property, XA_ATOM, 32,
+ PropModeReplace, (unsigned char*)targets, 2);
+ } else if (se.target == xaTIMESTAMP) {
+ rdr::U32 t = selectionOwnTime[se.selection];
+ XChangeProperty(dpy, se.requestor, se.property, XA_INTEGER, 32,
+ PropModeReplace, (unsigned char*)&t, 1);
+ } else if (se.target == XA_STRING) {
+ if (!selectionRequest(se.requestor, se.selection, se.property))
+ se.property = None;
+ }
+ }
+ XSendEvent(dpy, se.requestor, False, 0, (XEvent*)&se);
+ break;
+ }
+
+ case SelectionClear:
+ selectionOwner_[ev->xselectionclear.selection] = false;
+ break;
+ }
+
+ if (eventHandler) eventHandler->handleEvent(this, ev);
+}
+
+void TXWindow::drawBevel(GC gc, int x, int y, int w, int h, int b,
+ unsigned long middle, unsigned long tl,
+ unsigned long br, bool round)
+{
+ if (round) {
+ XGCValues gcv;
+ gcv.line_width = b;
+ XChangeGC(dpy, gc, GCLineWidth, &gcv);
+ XSetForeground(dpy, gc, middle);
+ XFillArc(dpy, win(), gc, x, y, w-b/2, h-b/2, 0, 360*64);
+ XSetForeground(dpy, gc, tl);
+ XDrawArc(dpy, win(), gc, x, y, w-b/2, h-b/2, 45*64, 180*64);
+ XSetForeground(dpy, gc, br);
+ XDrawArc(dpy, win(), gc, x, y, w-b/2, h-b/2, 225*64, 180*64);
+ } else {
+ XSetForeground(dpy, gc, middle);
+ if (w-2*b > 0 && h-2*b > 0)
+ XFillRectangle(dpy, win(), gc, x+b, y+b, w-2*b, h-2*b);
+ XSetForeground(dpy, gc, tl);
+ XFillRectangle(dpy, win(), gc, x, y, w, b);
+ XFillRectangle(dpy, win(), gc, x, y, b, h);
+ XSetForeground(dpy, gc, br);
+ for (int i = 0; i < b; i++) {
+ if (w-i > 0) XFillRectangle(dpy, win(), gc, x+i, y+h-1-i, w-i, 1);
+ if (h-1-i > 0) XFillRectangle(dpy, win(), gc, x+w-1-i, y+i+1, 1, h-1-i);
+ }
+ }
+}
diff --git a/tx/TXWindow.h b/tx/TXWindow.h
new file mode 100644
index 00000000..20c31c95
--- /dev/null
+++ b/tx/TXWindow.h
@@ -0,0 +1,210 @@
+/* Copyright (C) 2002-2004 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.
+ */
+//
+// TXWindow.h
+//
+// A TXWindow is the base class for all tx windows (widgets). In addition it
+// contains a number of static methods and members which are used throughout
+// tx.
+//
+// Before calling any other tx methods, TXWindow::init() must be called with
+// the X display to use.
+
+#ifndef __TXWINDOW_H__
+#define __TXWINDOW_H__
+
+#include <rdr/types.h>
+#include <X11/X.h>
+#include <X11/Xlib.h>
+#include <X11/Xutil.h>
+#include <map>
+
+
+// TXDeleteWindowCallback's deleteWindow() method is called when a top-level
+// window is "deleted" (closed) by the user using the window manager.
+class TXWindow;
+class TXDeleteWindowCallback {
+public:
+ virtual void deleteWindow(TXWindow* w) = 0;
+};
+
+// TXEventHandler is an interface implemented by classes wanting to handle X
+// events on a window. Most derived classes of window are their own event
+// handlers.
+class TXEventHandler {
+public:
+ virtual void handleEvent(TXWindow* w, XEvent* ev) = 0;
+};
+
+class TXWindow {
+public:
+
+ // Constructor - creates a window of the given size, with the default
+ // background (currently grey). It is mapped by default if it has a parent.
+ // If no parent is specified its parent is the root window and it will remain
+ // unmapped.
+ TXWindow(Display* dpy_, int width=1, int height=1, TXWindow* parent_=0,
+ int borderWidth=0);
+ virtual ~TXWindow();
+
+ // toplevel() declares that this is a top-level window. Various
+ // window-manager-related properties are set on the window. The given
+ // TXDeleteWindowCallback is notified when the window is "deleted" (cloesd)
+ // by the user.
+ void toplevel(const char* name, TXDeleteWindowCallback* dwc=0,
+ int argc=0, char** argv=0, const char* windowClass=0,
+ bool iconic=false);
+
+ // setMaxSize() tells the window manager the maximum size to allow a
+ // top-level window. It has no effect on a non-top-level window.
+ void setMaxSize(int w, int h);
+
+ // setUSPosition() tells the window manager the position which the "user" has
+ // asked for a top-level window. Most window managers ignore requests by a
+ // program for position, so you have to tell it that the "user" asked for the
+ // position. This has no effect on a non-top-level window.
+ void setUSPosition(int x, int y);
+
+ void setGeometry(const char* geom, int x, int y, int w, int h);
+
+ // setTransientFor() tells the window manager that this window is "owned" by
+ // the given window. The window manager can use this information as it sees
+ // fit.
+ void setTransientFor(Window w) { XSetTransientForHint(dpy, win(), w); }
+
+ // setEventHandler() sets the TXEventHandler to handle X events for this
+ // window. It returns the previous event handler, so that handlers can chain
+ // themselves.
+ TXEventHandler* setEventHandler(TXEventHandler* h);
+
+ // Accessor methods
+ Window win() { return win_; }
+ int width() { return width_; }
+ int height() { return height_; }
+
+ // selectionOwner() returns true if this window owns the given selection.
+ bool selectionOwner(Atom selection) { return selectionOwner_[selection]; }
+
+ // Wrappers around common Xlib calls
+ void addEventMask(long mask);
+ void removeEventMask(long mask);
+ void map() { XMapWindow(dpy, win()); }
+ void unmap();
+ void setBg(unsigned long bg) { XSetWindowBackground(dpy, win(), bg); }
+ void move(int x, int y) { XMoveWindow(dpy, win(), x, y); }
+ void resize(int w, int h);
+ void raise() { XRaiseWindow(dpy, win()); }
+ void setBorderWidth(int bw);
+
+ // ownSelection requests that the window owns the given selection from the
+ // given time (the time should be taken from an X event).
+ void ownSelection(Atom selection, Time time);
+
+
+ // drawBevel draws a rectangular or circular bevel filling the given
+ // rectangle, using the given colours for the middle, the top/left and the
+ // bottom/right.
+ void drawBevel(GC gc, int x, int y, int w, int h, int b,
+ unsigned long middle, unsigned long tl, unsigned long br,
+ bool round=false);
+
+ // Methods to be overridden in a derived class
+
+ // resizeNotify() is called whenever the window's dimensions may have
+ // changed.
+ virtual void resizeNotify() {}
+
+ // takeFocus() is called when the window has received keyboard focus from the
+ // window manager.
+ virtual void takeFocus(Time time) {}
+
+ // selectionNotify() is called when the selection owner has replied to a
+ // request for information about a selection from the selection owner.
+ virtual void selectionNotify(XSelectionEvent* ev, Atom type, int format,
+ int nitems, void* data) {}
+
+ // selectionRequest() is called when this window is the selection owner and
+ // another X client has requested the selection. It should set the given
+ // property on the given window to the value of the given selection,
+ // returning true if successful, false otherwise.
+ virtual bool selectionRequest(Window requestor,
+ Atom selection, Atom property) { return false;}
+
+ // Static methods
+
+ // init() must be called before any other tx methods.
+ static void init(Display* dpy, const char* defaultWindowClass);
+
+ // getColours() sets the pixel values in the cols array to the best available
+ // for the given rgb values, even in the case of a full colormap.
+ static void getColours(Display* dpy, XColor* cols, int nCols);
+
+ // handleXEvents() should be called whenever there are events to handle on
+ // the connection to the X display. It process all available events, then
+ // returns when there are no more events to process.
+ static void handleXEvents(Display* dpy);
+
+ // windowWithName() locates a window with a given name on a display.
+ static Window windowWithName(Display* dpy, Window top, const char* name);
+
+ // strEmptyToNull() returns the string it's given but turns an empty string
+ // into null, which can be useful for passing rfb parameters to Xlib calls.
+ static char* strEmptyToNull(char* s) { return s && s[0] ? s : 0; }
+
+ // The following are default values for various things.
+ static unsigned long black, white;
+ static unsigned long defaultFg, defaultBg, lightBg, darkBg;
+ static unsigned long disabledFg, disabledBg, enabledBg;
+ static unsigned long scrollbarBg;
+ static GC defaultGC;
+ static Colormap cmap;
+ static Font defaultFont;
+ static XFontStruct* defaultFS;
+ static Time cutBufferTime;
+ static Pixmap dot, tick;
+ static const int dotSize, tickSize;
+ static char* defaultWindowClass;
+
+ Display* const dpy;
+
+ int xPad, yPad, bevel;
+
+private:
+
+ // handleXEvent() is called from handleXEvents() when an event for this
+ // window arrives. It does general event processing before calling on to the
+ // event handler.
+ void handleXEvent(XEvent* ev);
+
+ TXWindow* parent;
+ Window win_;
+ int width_, height_;
+ TXEventHandler* eventHandler;
+ TXDeleteWindowCallback* dwc;
+ long eventMask;
+ XSizeHints sizeHints;
+ std::map<Atom,Time> selectionOwnTime;
+ std::map<Atom,bool> selectionOwner_;
+ bool toplevel_;
+};
+
+extern Atom wmProtocols, wmDeleteWindow, wmTakeFocus;
+extern Atom xaTIMESTAMP, xaTARGETS, xaSELECTION_TIME, xaSELECTION_STRING;
+extern Atom xaCLIPBOARD;
+
+#endif
diff --git a/tx/Timer.cxx b/tx/Timer.cxx
new file mode 100644
index 00000000..78cff1c2
--- /dev/null
+++ b/tx/Timer.cxx
@@ -0,0 +1,105 @@
+/* Copyright (C) 2002-2003 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.
+ */
+//
+// Timer.cxx
+//
+
+#include "Timer.h"
+
+static Timer* timers;
+
+void Timer::callTimers()
+{
+ struct timeval now;
+ gettimeofday(&now, 0);
+ while (timers) {
+ Timer* timer = timers;
+ if (timer->before(now)) {
+ timers = timers->next;
+ timer->cb->timerCallback(timer);
+ } else {
+ break;
+ }
+ }
+}
+
+bool Timer::getTimeout(struct timeval* timeout)
+{
+ if (!timers) return false;
+ Timer* timer = timers;
+ struct timeval now;
+ gettimeofday(&now, 0);
+ if (timer->before(now)) {
+ timeout->tv_sec = 0;
+ timeout->tv_usec = 0;
+ return true;
+ }
+ timeout->tv_sec = timer->tv.tv_sec - now.tv_sec;
+ if (timer->tv.tv_usec < now.tv_usec) {
+ timeout->tv_usec = timer->tv.tv_usec + 1000000 - now.tv_usec;
+ timeout->tv_sec--;
+ } else {
+ timeout->tv_usec = timer->tv.tv_usec - now.tv_usec;
+ }
+ return true;
+}
+
+Timer::Timer(TimerCallback* cb_) : cb(cb_) {}
+Timer::~Timer()
+{
+ cancel();
+}
+
+void Timer::reset(int ms) {
+ cancel();
+ gettimeofday(&tv, 0);
+ tv.tv_sec += ms / 1000;
+ tv.tv_usec += (ms % 1000) * 1000;
+ if (tv.tv_usec > 1000000) {
+ tv.tv_sec += 1;
+ tv.tv_usec -= 1000000;
+ }
+
+ Timer** timerPtr;
+ for (timerPtr = &timers; *timerPtr; timerPtr = &(*timerPtr)->next) {
+ if (before((*timerPtr)->tv)) {
+ next = *timerPtr;
+ *timerPtr = this;
+ break;
+ }
+ }
+ if (!*timerPtr) {
+ next = 0;
+ *timerPtr = this;
+ }
+}
+
+void Timer::cancel() {
+ for (Timer** timerPtr = &timers; *timerPtr; timerPtr = &(*timerPtr)->next) {
+ if (*timerPtr == this) {
+ *timerPtr = (*timerPtr)->next;
+ break;
+ }
+ }
+}
+
+bool Timer::isSet() {
+ for (Timer* timer = timers; timer; timer = timer->next)
+ if (timer == this) return true;
+ return false;
+}
diff --git a/tx/Timer.h b/tx/Timer.h
new file mode 100644
index 00000000..dabe4cdf
--- /dev/null
+++ b/tx/Timer.h
@@ -0,0 +1,51 @@
+/* Copyright (C) 2002-2003 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 __TIMER_H__
+#define __TIMER_H__
+
+#include <sys/time.h>
+#include <unistd.h>
+
+class TimerCallback;
+
+class Timer {
+public:
+ static void callTimers();
+ static bool getTimeout(struct timeval* timeout);
+
+ Timer(TimerCallback* cb_);
+ ~Timer();
+ void reset(int ms);
+ void cancel();
+ bool isSet();
+ bool before(struct timeval other) {
+ return (tv.tv_sec < other.tv_sec ||
+ (tv.tv_sec == other.tv_sec && tv.tv_usec < other.tv_usec));
+ }
+private:
+ struct timeval tv;
+ TimerCallback* cb;
+ Timer* next;
+};
+
+class TimerCallback {
+public:
+ virtual void timerCallback(Timer* timer) = 0;
+};
+
+#endif
diff --git a/vnc.dsw b/vnc.dsw
new file mode 100644
index 00000000..e6b377c1
--- /dev/null
+++ b/vnc.dsw
@@ -0,0 +1,206 @@
+Microsoft Developer Studio Workspace File, Format Version 6.00
+# WARNING: DO NOT EDIT OR DELETE THIS WORKSPACE FILE!
+
+###############################################################################
+
+Project: "Xregion"=.\Xregion\Xregion.dsp - Package Owner=<4>
+
+Package=<5>
+{{{
+}}}
+
+Package=<4>
+{{{
+}}}
+
+###############################################################################
+
+Project: "logmessages"=.\logmessages\logmessages.dsp - Package Owner=<4>
+
+Package=<5>
+{{{
+}}}
+
+Package=<4>
+{{{
+}}}
+
+###############################################################################
+
+Project: "network"=.\network\network.dsp - Package Owner=<4>
+
+Package=<5>
+{{{
+}}}
+
+Package=<4>
+{{{
+}}}
+
+###############################################################################
+
+Project: "rdr"=.\rdr\rdr.dsp - Package Owner=<4>
+
+Package=<5>
+{{{
+}}}
+
+Package=<4>
+{{{
+}}}
+
+###############################################################################
+
+Project: "rfb"=.\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: "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 wm_hooks
+ 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 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 wm_hooks
+ End Project Dependency
+ Begin Project Dependency
+ Project_Dep_Name zlib
+ 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"=.\zlib\zlib.dsp - Package Owner=<4>
+
+Package=<5>
+{{{
+}}}
+
+Package=<4>
+{{{
+}}}
+
+###############################################################################
+
+Global:
+
+Package=<5>
+{{{
+}}}
+
+Package=<3>
+{{{
+}}}
+
+###############################################################################
+
diff --git a/vncconfig/Authentication.h b/vncconfig/Authentication.h
new file mode 100644
index 00000000..5923c2cb
--- /dev/null
+++ b/vncconfig/Authentication.h
@@ -0,0 +1,137 @@
+/* Copyright (C) 2002-2003 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 <rfb_win32/Registry.h>
+#include <rfb_win32/Dialog.h>
+#include <rfb_win32/Win32Util.h>
+#include <rfb/ServerCore.h>
+#include <rfb/secTypes.h>
+#include <rfb/vncAuth.h>
+
+
+extern rfb::VncAuthPasswdConfigParameter vncAuthPasswd;
+
+namespace rfb {
+
+ namespace win32 {
+
+ class VncPasswdDialog : public Dialog {
+ public:
+ VncPasswdDialog(const RegKey& rk) : Dialog(GetModuleHandle(0)), regKey(rk), warnPasswdInsecure(false) {}
+ bool showDialog() {
+ return Dialog::showDialog(MAKEINTRESOURCE(IDD_AUTH_VNC_PASSWD));
+ }
+ bool onOk() {
+ TCharArray password1 = getItemString(IDC_PASSWORD1);
+ TCharArray 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 (warnPasswdInsecure &&
+ (MsgBox(0, _T("Please note that your VNC password cannot be stored securely on this system. ")
+ _T("Are you sure you wish to continue?"),
+ MB_YESNO | MB_ICONWARNING) == IDNO))
+ return false;
+ char passwd[9];
+ memset(passwd, 0, sizeof(passwd));
+ strCopy(passwd, CStr(password1.buf), sizeof(passwd));
+ vncAuthObfuscatePasswd(passwd);
+ regKey.setBinary(_T("Password"), passwd, 8);
+ return true;
+ }
+ void setWarnPasswdInsecure(bool warn) {
+ warnPasswdInsecure = warn;
+ }
+ protected:
+ const RegKey& regKey;
+ bool warnPasswdInsecure;
+ };
+
+ class AuthenticationPage : public PropSheetPage {
+ public:
+ AuthenticationPage(const RegKey& rk)
+ : PropSheetPage(GetModuleHandle(0), MAKEINTRESOURCE(IDD_AUTHENTICATION)),
+ passwd(rk), regKey(rk) {}
+ void initDialog() {
+ CharArray sec_types_str;
+ sec_types_str.buf = rfb::Server::sec_types.getData();
+ std::list<int> sec_types = parseSecTypes(sec_types_str.buf);
+
+ useNone = useVNC = false;
+ std::list<int>::iterator i;
+ for (i=sec_types.begin(); i!=sec_types.end(); i++) {
+ if ((*i) == secTypeNone) useNone = true;
+ else if ((*i) == secTypeVncAuth) useVNC = true;
+ }
+
+ setItemChecked(IDC_AUTH_NONE, useNone);
+ setItemChecked(IDC_AUTH_VNC, useVNC);
+ setItemChecked(IDC_QUERY_CONNECT, rfb::Server::queryConnect);
+ }
+ bool onCommand(int id, int cmd) {
+ switch (id) {
+ case IDC_AUTH_VNC_PASSWD:
+ passwd.showDialog();
+ return true;
+ case IDC_AUTH_NONE:
+ case IDC_AUTH_VNC:
+ case IDC_QUERY_CONNECT:
+ setChanged((rfb::Server::queryConnect != isItemChecked(IDC_QUERY_CONNECT)) ||
+ (useNone != isItemChecked(IDC_AUTH_NONE)) ||
+ (useVNC != isItemChecked(IDC_AUTH_VNC)));
+ return false;
+ };
+ return false;
+ }
+ bool onOk() {
+ useVNC = isItemChecked(IDC_AUTH_VNC);
+ useNone = isItemChecked(IDC_AUTH_NONE);
+ if (useVNC) {
+ CharArray password = vncAuthPasswd.getVncAuthPasswd();
+ if (!password.buf || strlen(password.buf) == 0) {
+ MsgBox(0, _T("The VNC authentication method is enabled, but no password is specified! ")
+ _T("The password dialog will now be shown."), MB_ICONEXCLAMATION | MB_OK);
+ passwd.showDialog();
+ }
+ regKey.setString(_T("SecurityTypes"), _T("VncAuth"));
+ } else if (useNone) {
+ regKey.setString(_T("SecurityTypes"), _T("None"));
+ }
+ regKey.setString(_T("ReverseSecurityTypes"), _T("None"));
+ regKey.setBool(_T("QueryConnect"), isItemChecked(IDC_QUERY_CONNECT));
+ return true;
+ }
+ void setWarnPasswdInsecure(bool warn) {
+ passwd.setWarnPasswdInsecure(warn);
+ }
+ protected:
+ RegKey regKey;
+ VncPasswdDialog passwd;
+ bool useNone;
+ bool useVNC;
+ };
+
+ };
+
+};
+
+#endif
diff --git a/vncconfig/Connections.h b/vncconfig/Connections.h
new file mode 100644
index 00000000..133e81c1
--- /dev/null
+++ b/vncconfig/Connections.h
@@ -0,0 +1,278 @@
+/* Copyright (C) 2002-2004 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 <vector>
+
+#include <rfb_win32/Registry.h>
+#include <rfb_win32/Dialog.h>
+#include <rfb/Configuration.h>
+#include <rfb/Blacklist.h>
+#include <network/TcpSocket.h>
+
+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) {
+ delete [] pattern.buf;
+ pattern.buf = tstrDup(pat);
+ return Dialog::showDialog(MAKEINTRESOURCE(IDD_CONN_HOST));
+ }
+ void initDialog() {
+ if (_tcslen(pattern.buf) == 0) {
+ delete [] pattern.buf;
+ pattern.buf = tstrDup(_T("+"));
+ }
+
+ if (pattern.buf[0] == _T('+'))
+ setItemChecked(IDC_ALLOW, true);
+ else
+ setItemChecked(IDC_DENY, true);
+
+ setItemString(IDC_HOST_PATTERN, &pattern.buf[1]);
+
+ delete [] pattern.buf;
+ pattern.buf = 0;
+ }
+ bool onOk() {
+ delete [] pattern.buf;
+ pattern.buf = 0;
+
+ TCharArray host = getItemString(IDC_HOST_PATTERN);
+
+ TCharArray newPat(_tcslen(host.buf)+2);
+ if (isItemChecked(IDC_ALLOW))
+ 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.buf = 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);
+ setItemInt(IDC_IDLE_TIMEOUT, rfb::Server::idleTimeout);
+ vlog.debug("set IDC_HTTP_PORT %d", (int)http_port);
+ setItemInt(IDC_HTTP_PORT, http_port);
+ 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_HOSTS, 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 {
+ if (getItemInt(IDC_PORT) > 100)
+ setItemInt(IDC_HTTP_PORT, getItemInt(IDC_PORT)-100);
+ } catch (...) {
+ }
+ }
+ case IDC_HTTP_PORT:
+ case IDC_IDLE_TIMEOUT:
+ if (cmd == EN_CHANGE)
+ setChanged(isChanged());
+ return false;
+
+ case IDC_HTTP_ENABLE:
+ enableItem(IDC_HTTP_PORT, isItemChecked(IDC_HTTP_ENABLE));
+ setChanged(isChanged());
+ return false;
+
+ case IDC_LOCALHOST:
+ enableItem(IDC_HOSTS, !isItemChecked(IDC_LOCALHOST));
+ enableItem(IDC_HOST_REMOVE, !isItemChecked(IDC_LOCALHOST));
+ enableItem(IDC_HOST_UP, !isItemChecked(IDC_LOCALHOST));
+ enableItem(IDC_HOST_DOWN, !isItemChecked(IDC_LOCALHOST));
+ enableItem(IDC_HOST_EDIT, !isItemChecked(IDC_LOCALHOST));
+ enableItem(IDC_HOST_ADD, !isItemChecked(IDC_LOCALHOST));
+ 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"), getItemInt(IDC_PORT));
+ regKey.setInt(_T("LocalHost"), isItemChecked(IDC_LOCALHOST));
+ regKey.setInt(_T("IdleTimeout"), getItemInt(IDC_IDLE_TIMEOUT));
+ regKey.setInt(_T("HTTPPortNumber"), isItemChecked(IDC_HTTP_ENABLE) ? getItemInt(IDC_HTTP_PORT) : 0);
+
+ 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<SendMessage(listBox, LB_GETCOUNT, 0, 0); i++)
+ bufLen+=SendMessage(listBox, LB_GETTEXTLEN, i, 0)+1;
+ TCharArray hosts_str(bufLen);
+ hosts_str.buf[0] = 0;
+ TCHAR* outPos = hosts_str.buf;
+ for (i=0; i<SendMessage(listBox, LB_GETCOUNT, 0, 0); i++) {
+ outPos += SendMessage(listBox, LB_GETTEXT, i, (LPARAM)outPos);
+ outPos[0] = ',';
+ outPos[1] = 0;
+ outPos++;
+ }
+ return strDup(hosts_str.buf);
+ }
+ protected:
+ RegKey regKey;
+ ConnHostDialog hostDialog;
+ };
+
+ };
+
+};
+
+#endif \ No newline at end of file
diff --git a/vncconfig/Desktop.h b/vncconfig/Desktop.h
new file mode 100644
index 00000000..a0a325a3
--- /dev/null
+++ b/vncconfig/Desktop.h
@@ -0,0 +1,93 @@
+/* Copyright (C) 2002-2003 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_DESKTOP
+#define WINVNCCONF_DESKTOP
+
+#include <rfb_win32/Registry.h>
+#include <rfb_win32/Dialog.h>
+#include <rfb_win32/SDisplay.h>
+
+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 \ No newline at end of file
diff --git a/vncconfig/Hooking.h b/vncconfig/Hooking.h
new file mode 100644
index 00000000..b9bec15a
--- /dev/null
+++ b/vncconfig/Hooking.h
@@ -0,0 +1,65 @@
+/* Copyright (C) 2002-2003 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 <rfb_win32/Registry.h>
+#include <rfb_win32/Dialog.h>
+#include <rfb_win32/SDisplay.h>
+#include <rfb/ServerCore.h>
+
+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_USEHOOKS, rfb::win32::SDisplay::use_hooks);
+ setItemChecked(IDC_POLLCONSOLES, rfb::win32::WMPoller::poll_console_windows);
+ setItemChecked(IDC_COMPAREFB, rfb::Server::compareFB);
+ }
+ bool onCommand(int id, int cmd) {
+ switch (id) {
+ case IDC_USEHOOKS:
+ case IDC_POLLCONSOLES:
+ case IDC_COMPAREFB:
+ setChanged((rfb::win32::SDisplay::use_hooks != isItemChecked(IDC_USEHOOKS)) ||
+ (rfb::win32::WMPoller::poll_console_windows != isItemChecked(IDC_POLLCONSOLES)) ||
+ (rfb::Server::compareFB != isItemChecked(IDC_COMPAREFB)));
+ break;
+ }
+ return false;
+ }
+ bool onOk() {
+ regKey.setBool(_T("UseHooks"), isItemChecked(IDC_USEHOOKS));
+ regKey.setBool(_T("PollConsoleWindows"), isItemChecked(IDC_POLLCONSOLES));
+ regKey.setBool(_T("CompareFB"), isItemChecked(IDC_COMPAREFB));
+ return true;
+ }
+ protected:
+ RegKey regKey;
+ };
+
+ };
+
+};
+
+#endif \ No newline at end of file
diff --git a/vncconfig/Inputs.h b/vncconfig/Inputs.h
new file mode 100644
index 00000000..2735d24a
--- /dev/null
+++ b/vncconfig/Inputs.h
@@ -0,0 +1,84 @@
+/* Copyright (C) 2002-2003 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 <rfb_win32/Registry.h>
+#include <rfb_win32/Dialog.h>
+#include <rfb/ServerCore.h>
+
+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);
+ 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 \ No newline at end of file
diff --git a/vncconfig/Legacy.cxx b/vncconfig/Legacy.cxx
new file mode 100644
index 00000000..59266514
--- /dev/null
+++ b/vncconfig/Legacy.cxx
@@ -0,0 +1,248 @@
+/* Copyright (C) 2002-2004 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 <vncconfig/Legacy.h>
+
+#include <rfb/LogWriter.h>
+#include <rfb_win32/CurrentUser.h>
+
+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/<user>. 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));
+
+ rfb::CharArray passwd;
+ int length;
+ key.getBinary(_T("Password"), (void**)&passwd.buf, &length, 0, 0);
+ regKey.setBinary(_T("Password"), passwd.buf, 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/vncconfig/Legacy.h b/vncconfig/Legacy.h
new file mode 100644
index 00000000..e66dc56b
--- /dev/null
+++ b/vncconfig/Legacy.h
@@ -0,0 +1,86 @@
+/* Copyright (C) 2002-2003 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
+
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+#include <lmcons.h>
+
+#include <vncconfig/resource.h>
+#include <rfb_win32/Registry.h>
+#include <rfb_win32/Dialog.h>
+#include <rfb_win32/Win32Util.h>
+#include <rfb/ServerCore.h>
+
+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 \ No newline at end of file
diff --git a/vncconfig/Sharing.h b/vncconfig/Sharing.h
new file mode 100644
index 00000000..b63b39ad
--- /dev/null
+++ b/vncconfig/Sharing.h
@@ -0,0 +1,59 @@
+/* Copyright (C) 2002-2003 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 <rfb_win32/Registry.h>
+#include <rfb_win32/Dialog.h>
+#include <rfb/ServerCore.h>
+
+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 \ No newline at end of file
diff --git a/vncconfig/resource.h b/vncconfig/resource.h
new file mode 100644
index 00000000..99e2cea3
--- /dev/null
+++ b/vncconfig/resource.h
@@ -0,0 +1,86 @@
+//{{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_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 A 1045
+#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_QUERY_CONNECT 1051
+#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 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 1062
+#define _APS_NEXT_SYMED_VALUE 101
+#endif
+#endif
diff --git a/vncconfig/vncconfig.cxx b/vncconfig/vncconfig.cxx
new file mode 100644
index 00000000..2a98dbca
--- /dev/null
+++ b/vncconfig/vncconfig.cxx
@@ -0,0 +1,193 @@
+/* Copyright (C) 2002-2004 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.
+ */
+
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+#include <commctrl.h>
+#include <string.h>
+#ifdef WIN32
+#define strcasecmp _stricmp
+#endif
+
+#include "resource.h"
+#include <rfb/Logger_stdio.h>
+#include <rfb/LogWriter.h>
+#include <rfb/SSecurityFactoryStandard.h>
+#include <rfb_win32/Dialog.h>
+#include <rfb_win32/RegConfig.h>
+#include <rfb_win32/CurrentUser.h>
+
+using namespace rfb;
+using namespace rfb::win32;
+
+static LogWriter vlog("main");
+
+
+#include <vncconfig/Authentication.h>
+#include <vncconfig/Connections.h>
+#include <vncconfig/Sharing.h>
+#include <vncconfig/Hooking.h>
+#include <vncconfig/Inputs.h>
+#include <vncconfig/Legacy.h>
+#include <vncconfig/Desktop.h>
+
+
+TStr rfb::win32::AppName("VNC Config");
+
+
+VncAuthPasswdConfigParameter vncAuthPasswd;
+
+#ifdef _DEBUG
+BoolParameter captureDialogs("CaptureDialogs", "", false);
+#endif
+
+HKEY configKey = HKEY_CURRENT_USER;
+
+
+void
+processParams(int argc, char* argv[]) {
+ for (int i=1; i<argc; i++) {
+ if (strcasecmp(argv[i], "-service") == 0) {
+ configKey = HKEY_LOCAL_MACHINE;
+ } else if (strcasecmp(argv[i], "-user") == 0) {
+ configKey = HKEY_CURRENT_USER;
+ } else {
+ // Try to process <option>=<value>, or -<bool>
+ if (Configuration::setParam(argv[i], true))
+ continue;
+ // Try to process -<option> <value>
+ if ((argv[i][0] == '-') && (i+1 < argc)) {
+ if (Configuration::setParam(&argv[i][1], argv[i+1], true)) {
+ i++;
+ continue;
+ }
+ }
+ }
+ }
+}
+
+
+int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, char* cmdLine, int cmdShow) {
+
+ // Configure debugging output
+#ifdef _DEBUG
+ AllocConsole();
+ freopen("CONIN$","rb",stdin);
+ freopen("CONOUT$","wb",stdout);
+ freopen("CONOUT$","wb",stderr);
+ setbuf(stderr, 0);
+ initStdIOLoggers();
+ LogWriter vlog("main");
+ logParams.setParam("*:stderr:100");
+ vlog.info("Starting vncconfig applet");
+#endif
+
+ try {
+ try {
+ // Process command-line args
+ int argc = __argc;
+ char** argv = __argv;
+ processParams(argc, argv);
+
+ /* *** Required if we wish to use IP address control
+ INITCOMMONCONTROLSEX icce;
+ icce.dwSize = sizeof(icce);
+ icce.dwICC = ICC_INTERNET_CLASSES;
+ InitCommonControlsEx(&icce);
+ */
+
+ // Create the required configuration registry key
+ RegKey rootKey;
+ rootKey.createKey(configKey, _T("Software\\RealVNC\\WinVNC4"));
+
+ // Override whatever security it already had (NT only)
+ bool warnOnChangePassword = false;
+ try {
+ AccessEntries access;
+ Sid adminSID = Sid::Administrators();
+ Sid systemSID = Sid::SYSTEM();
+ access.addEntry(adminSID, KEY_ALL_ACCESS, GRANT_ACCESS);
+ access.addEntry(systemSID, KEY_ALL_ACCESS, GRANT_ACCESS);
+ UserName user;
+ if (configKey == HKEY_CURRENT_USER)
+ access.addEntry(user.buf, KEY_ALL_ACCESS, GRANT_ACCESS);
+ AccessControlList acl = CreateACL(access);
+
+ // Set the DACL, and don't allow the key to inherit its parent's DACL
+ rootKey.setDACL(acl, false);
+ } catch (rdr::SystemException& e) {
+ // Something weird happens on NT 4.0 SP5 but I can't reproduce it on other
+ // NT 4.0 service pack revisions.
+ if (e.err == ERROR_INVALID_PARAMETER) {
+ MsgBox(0, _T("Windows reported an error trying to secure the VNC Server settings for this user. ")
+ _T("Your settings may not be secure!"), MB_ICONWARNING | MB_OK);
+ } else if (e.err != ERROR_CALL_NOT_IMPLEMENTED &&
+ e.err != ERROR_NOT_LOGGED_ON) {
+ // If the call is not implemented, ignore the error and continue
+ // If we are on Win9x and no user is logged on, ignore error and continue
+ throw;
+ }
+ warnOnChangePassword = true;
+ }
+
+ // Start the RegConfig reader, to load in existing settings
+ RegistryReader config;
+ config.setKey(configKey, _T("Software\\RealVNC\\WinVNC4"));
+
+ // Build the dialog
+ std::list<PropSheetPage*> pages;
+ AuthenticationPage auth(rootKey); pages.push_back(&auth);
+ auth.setWarnPasswdInsecure(warnOnChangePassword);
+ ConnectionsPage conn(rootKey); pages.push_back(&conn);
+ InputsPage inputs(rootKey); pages.push_back(&inputs);
+ SharingPage sharing(rootKey); pages.push_back(&sharing);
+ DesktopPage desktop(rootKey); pages.push_back(&desktop);
+ HookingPage hooks(rootKey); pages.push_back(&hooks);
+ LegacyPage legacy(rootKey, configKey == HKEY_CURRENT_USER); pages.push_back(&legacy);
+
+ // Load the default icon to use
+ HICON icon = (HICON)LoadImage(inst, MAKEINTRESOURCE(IDI_ICON), IMAGE_ICON, 0, 0, LR_SHARED);
+
+ // Create the PropertySheet handler
+ TCHAR* propSheetTitle = _T("VNC Server Properties (Service-Mode)");
+ if (configKey == HKEY_CURRENT_USER)
+ propSheetTitle = _T("VNC Server Properties (User-Mode)");
+ PropSheet sheet(inst, propSheetTitle, pages, icon);
+
+#ifdef _DEBUG
+ vlog.debug("capture dialogs=%s", captureDialogs ? "true" : "false");
+ sheet.showPropSheet(0, true, false, captureDialogs);
+#else
+ sheet.showPropSheet(0, true, false);
+#endif
+
+ return 0;
+ } catch (rdr::SystemException& e) {
+ switch (e.err) {
+ case ERROR_ACCESS_DENIED:
+ MsgBox(0, _T("You do not have sufficient access rights to run the VNC Configuration applet"),
+ MB_ICONSTOP | MB_OK);
+ return 1;
+ };
+ throw;
+ }
+ } catch (rdr::Exception& e) {
+ MsgBox(NULL, TStr(e.str()), MB_ICONEXCLAMATION | MB_OK);
+ return 1;
+ }
+}
diff --git a/vncconfig/vncconfig.dsp b/vncconfig/vncconfig.dsp
new file mode 100644
index 00000000..b557dfae
--- /dev/null
+++ b/vncconfig/vncconfig.dsp
@@ -0,0 +1,188 @@
+# Microsoft Developer Studio Project File - Name="vncconfig" - Package Owner=<4>
+# Microsoft Developer Studio Generated Build File, Format Version 6.00
+# ** DO NOT EDIT **
+
+# TARGTYPE "Win32 (x86) Application" 0x0101
+
+CFG=vncconfig - 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 "vncconfig.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 "vncconfig.mak" CFG="vncconfig - Win32 Debug Unicode"
+!MESSAGE
+!MESSAGE Possible choices for configuration are:
+!MESSAGE
+!MESSAGE "vncconfig - Win32 Release" (based on "Win32 (x86) Application")
+!MESSAGE "vncconfig - Win32 Debug" (based on "Win32 (x86) Application")
+!MESSAGE "vncconfig - Win32 Debug Unicode" (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)" == "vncconfig - 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"
+# 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 ".." /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 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 /subsystem:windows /machine:I386
+# ADD LINK32 user32.lib gdi32.lib advapi32.lib version.lib shell32.lib comctl32.lib ws2_32.lib ole32.lib /nologo /subsystem:windows /machine:I386
+
+!ELSEIF "$(CFG)" == "vncconfig - Win32 Debug"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 1
+# PROP BASE Output_Dir "vncconfig___Win32_Debug"
+# PROP BASE Intermediate_Dir "vncconfig___Win32_Debug"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 1
+# PROP Output_Dir "..\Debug"
+# PROP Intermediate_Dir "Debug"
+# 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 ".." /D "_DEBUG" /D "_WINDOWS" /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 /subsystem:windows /debug /machine:I386 /pdbtype:sept
+# ADD LINK32 user32.lib gdi32.lib advapi32.lib version.lib shell32.lib comctl32.lib ws2_32.lib ole32.lib /nologo /subsystem:windows /incremental:no /debug /machine:I386 /pdbtype:sept
+
+!ELSEIF "$(CFG)" == "vncconfig - Win32 Debug Unicode"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 1
+# PROP BASE Output_Dir "vncconfig___Win32_Debug_Unicode"
+# PROP BASE Intermediate_Dir "vncconfig___Win32_Debug_Unicode"
+# PROP BASE Ignore_Export_Lib 0
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 1
+# PROP Output_Dir "..\Debug_Unicode"
+# PROP Intermediate_Dir "Debug_Unicode"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /I ".." /D "_DEBUG" /D "_WINDOWS" /D "WIN32" /D "_MBCS" /YX /FD /GZ /c
+# ADD CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /I ".." /D "_WINDOWS" /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 user32.lib gdi32.lib advapi32.lib version.lib shell32.lib comctl32.lib ws2_32.lib /nologo /subsystem:windows /debug /machine:I386 /pdbtype:sept
+# ADD LINK32 user32.lib gdi32.lib advapi32.lib version.lib shell32.lib comctl32.lib ws2_32.lib ole32.lib /nologo /subsystem:windows /debug /machine:I386 /pdbtype:sept
+
+!ENDIF
+
+# Begin Target
+
+# Name "vncconfig - Win32 Release"
+# Name "vncconfig - Win32 Debug"
+# Name "vncconfig - Win32 Debug Unicode"
+# Begin Group "Source Files"
+
+# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat"
+# Begin Source File
+
+SOURCE=.\Legacy.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\vncconfig.cxx
+# End Source File
+# End Group
+# Begin Group "Header Files"
+
+# PROP Default_Filter "h;hpp;hxx;hm;inl"
+# Begin Source File
+
+SOURCE=.\Authentication.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\Connections.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\Desktop.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\Hooking.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\Inputs.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\Legacy.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\resource.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\Sharing.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=.\connected.ico
+# End Source File
+# Begin Source File
+
+SOURCE=.\vncconfig.ico
+# End Source File
+# Begin Source File
+
+SOURCE=.\vncconfig.rc
+# End Source File
+# End Group
+# Begin Source File
+
+SOURCE=.\vncconfig.exe.manifest
+# End Source File
+# End Target
+# End Project
diff --git a/vncconfig/vncconfig.exe.manifest b/vncconfig/vncconfig.exe.manifest
new file mode 100644
index 00000000..35a4164b
--- /dev/null
+++ b/vncconfig/vncconfig.exe.manifest
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
+<assemblyIdentity
+ version="4.0.0.26"
+ processorArchitecture="X86"
+ name="RealVNC.vncconfig.exe"
+ type="win32"
+/>
+<description>.NET control deployment tool</description>
+<dependency>
+ <dependentAssembly>
+ <assemblyIdentity
+ type="win32"
+ name="Microsoft.Windows.Common-Controls"
+ version="6.0.0.0"
+ processorArchitecture="X86"
+ publicKeyToken="6595b64144ccf1df"
+ language="*"
+ />
+ </dependentAssembly>
+</dependency>
+</assembly>
diff --git a/vncconfig/vncconfig.ico b/vncconfig/vncconfig.ico
new file mode 100644
index 00000000..5b555dd4
--- /dev/null
+++ b/vncconfig/vncconfig.ico
Binary files differ
diff --git a/vncconfig/vncconfig.rc b/vncconfig/vncconfig.rc
new file mode 100644
index 00000000..e74c8f13
--- /dev/null
+++ b/vncconfig/vncconfig.rc
@@ -0,0 +1,479 @@
+//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.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
+
+#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
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Icon
+//
+
+// Icon with lowest ID value placed first to ensure application icon
+// remains consistent on all systems.
+IDI_ICON ICON DISCARDABLE "vncconfig.ico"
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Dialog
+//
+
+IDD_AUTHENTICATION DIALOG DISCARDABLE 0, 0, 225, 86
+STYLE DS_MODALFRAME | DS_CONTROL | WS_POPUP | WS_CAPTION | WS_SYSMENU
+CAPTION "Authentication"
+FONT 8, "MS Sans Serif"
+BEGIN
+ CONTROL "No Authentication or Encryption",IDC_AUTH_NONE,"Button",
+ BS_AUTORADIOBUTTON | WS_GROUP,7,10,143,15
+ CONTROL "VNC 3.3 Authentication, no Encryption",IDC_AUTH_VNC,
+ "Button",BS_AUTORADIOBUTTON,7,30,143,15
+ PUSHBUTTON "Set Password",IDC_AUTH_VNC_PASSWD,155,30,55,15
+ CONTROL "Prompt local user to accept incoming connections",
+ IDC_QUERY_CONNECT,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,
+ 7,50,211,15
+END
+
+IDD_CONNECTIONS DIALOG DISCARDABLE 0, 0, 218, 198
+STYLE DS_MODALFRAME | DS_CONTROL | WS_POPUP | WS_CAPTION | WS_SYSMENU
+CAPTION "Connections"
+FONT 8, "MS Sans Serif"
+BEGIN
+ LTEXT "Accept connections on port:",IDC_STATIC,7,10,138,15,
+ SS_CENTERIMAGE
+ LTEXT "Disconnect idle clients after (seconds):",IDC_STATIC,7,
+ 25,138,15,SS_CENTERIMAGE
+ EDITTEXT IDC_PORT,150,10,61,15,ES_AUTOHSCROLL | ES_NUMBER
+ EDITTEXT IDC_IDLE_TIMEOUT,150,25,61,15,ES_AUTOHSCROLL | ES_NUMBER
+ CONTROL "Serve Java viewer via HTTP on port:",IDC_HTTP_ENABLE,
+ "Button",BS_AUTOCHECKBOX | WS_TABSTOP,7,40,138,15
+ EDITTEXT IDC_HTTP_PORT,150,40,61,15,ES_AUTOHSCROLL | ES_NUMBER
+ GROUPBOX "Access Control",IDC_STATIC,7,55,204,135
+ CONTROL "Only accept connections from the local machine",
+ IDC_LOCALHOST,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,15,
+ 70,190,15
+ LISTBOX IDC_HOSTS,15,90,130,95,LBS_NOINTEGRALHEIGHT | WS_VSCROLL |
+ WS_TABSTOP
+ PUSHBUTTON "&Add",IDC_HOST_ADD,150,90,55,15
+ PUSHBUTTON "&Remove",IDC_HOST_REMOVE,150,110,55,15
+ PUSHBUTTON "Move Up",IDC_HOST_UP,150,130,55,15
+ PUSHBUTTON "Move Down",IDC_HOST_DOWN,150,150,55,15
+ PUSHBUTTON "&Edit",IDC_HOST_EDIT,150,170,55,15
+END
+
+IDD_HOOKING DIALOG DISCARDABLE 0, 0, 198, 95
+STYLE DS_MODALFRAME | DS_CONTROL | WS_POPUP | WS_CAPTION | WS_SYSMENU
+CAPTION "Hooks"
+FONT 8, "MS Sans Serif"
+BEGIN
+ CONTROL "Use VNC hooks to track graphical updates",IDC_USEHOOKS,
+ "Button",BS_AUTOCHECKBOX | WS_TABSTOP,7,10,184,15
+ CONTROL "Poll console windows for updates",IDC_POLLCONSOLES,
+ "Button",BS_AUTOCHECKBOX | WS_TABSTOP,7,25,184,15
+ CONTROL "Filter out updates that have no effect (recommended)",
+ IDC_COMPAREFB,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,7,40,
+ 184,15
+END
+
+IDD_AUTH_VNC_PASSWD DIALOG DISCARDABLE 0, 0, 212, 70
+STYLE DS_MODALFRAME | DS_SETFOREGROUND | DS_CENTER | WS_POPUP | WS_CAPTION |
+ WS_SYSMENU
+CAPTION "WinVNC Password"
+FONT 8, "MS Sans Serif"
+BEGIN
+ EDITTEXT IDC_PASSWORD1,75,10,130,15,ES_PASSWORD | ES_AUTOHSCROLL
+ EDITTEXT IDC_PASSWORD2,75,30,130,14,ES_PASSWORD | ES_AUTOHSCROLL
+ LTEXT "New Password:",IDC_STATIC,7,10,63,15
+ DEFPUSHBUTTON "OK",IDOK,100,50,50,15
+ PUSHBUTTON "Cancel",IDCANCEL,155,50,50,15
+ LTEXT "Confirm Password:",IDC_STATIC,7,30,63,14
+END
+
+IDD_LEGACY DIALOG DISCARDABLE 0, 0, 166, 92
+STYLE DS_MODALFRAME | DS_CONTROL | WS_POPUP | WS_CAPTION | WS_SYSMENU
+CAPTION "Legacy"
+FONT 8, "MS Sans Serif"
+BEGIN
+ PUSHBUTTON "&Import VNC 3.3 Settings",IDC_LEGACY_IMPORT,7,10,92,20
+ CONTROL "Only use protocol version 3.3",IDC_PROTOCOL_3_3,"Button",
+ BS_AUTOCHECKBOX | WS_TABSTOP,7,35,152,15
+END
+
+IDD_CONN_HOST DIALOG DISCARDABLE 0, 0, 225, 47
+STYLE DS_SYSMODAL | DS_MODALFRAME | DS_CENTER | WS_POPUP | WS_CAPTION
+CAPTION "Specify Host IP Address Pattern"
+FONT 8, "MS Sans Serif"
+BEGIN
+ DEFPUSHBUTTON "OK",IDOK,168,7,50,14
+ PUSHBUTTON "Cancel",IDCANCEL,168,24,50,14
+ CONTROL "&Allow",IDC_ALLOW,"Button",BS_AUTORADIOBUTTON |
+ WS_GROUP,7,7,53,16
+ CONTROL "&Deny",IDC_DENY,"Button",BS_AUTORADIOBUTTON,7,23,53,15
+ EDITTEXT IDC_HOST_PATTERN,65,7,100,16,ES_AUTOHSCROLL
+ LTEXT "e.g. 192.168.0.0/255.255.0.0",IDC_STATIC,65,25,100,15
+END
+
+IDD_SHARING DIALOG DISCARDABLE 0, 0, 186, 95
+STYLE DS_MODALFRAME | DS_CONTROL | WS_POPUP | WS_CAPTION | WS_SYSMENU
+CAPTION "Sharing"
+FONT 8, "MS Sans Serif"
+BEGIN
+ CONTROL "Always treat new connections as shared",
+ IDC_SHARE_ALWAYS,"Button",BS_AUTORADIOBUTTON | WS_GROUP,
+ 7,10,172,15
+ CONTROL "Never treat new connections as shared",IDC_SHARE_NEVER,
+ "Button",BS_AUTORADIOBUTTON,7,25,172,15
+ CONTROL "Use client's preferred sharing setting",
+ IDC_SHARE_CLIENT,"Button",BS_AUTORADIOBUTTON,7,40,172,15
+ CONTROL "Non-shared connections replace existing ones",
+ IDC_DISCONNECT_CLIENTS,"Button",BS_AUTOCHECKBOX |
+ WS_TABSTOP,7,55,172,15
+END
+
+IDD_INPUTS DIALOG DISCARDABLE 0, 0, 186, 119
+STYLE DS_MODALFRAME | DS_CONTROL | WS_POPUP | WS_CAPTION | WS_SYSMENU
+CAPTION "Inputs"
+FONT 8, "MS Sans Serif"
+BEGIN
+ CONTROL "Accept keyboard events from clients",IDC_ACCEPT_KEYS,
+ "Button",BS_AUTOCHECKBOX | WS_TABSTOP,7,25,172,15
+ CONTROL "Accept pointer events from clients",IDC_ACCEPT_PTR,
+ "Button",BS_AUTOCHECKBOX | WS_TABSTOP,7,10,172,15
+ CONTROL "Accept clipboard updates from clients",
+ IDC_ACCEPT_CUTTEXT,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,
+ 7,40,172,15
+ CONTROL "Send clipboard updates to clients",IDC_SEND_CUTTEXT,
+ "Button",BS_AUTOCHECKBOX | WS_TABSTOP,7,55,172,15
+ CONTROL "Allow input events to affect the screen-saver",
+ IDC_AFFECT_SCREENSAVER,"Button",BS_AUTOCHECKBOX |
+ WS_TABSTOP,7,70,172,15
+ CONTROL "Disable local inputs while server is in use",
+ IDC_DISABLE_LOCAL_INPUTS,"Button",BS_AUTOCHECKBOX |
+ WS_TABSTOP,7,95,172,15
+END
+
+IDD_ABOUT DIALOG DISCARDABLE 0, 0, 249, 92
+STYLE DS_MODALFRAME | DS_SETFOREGROUND | DS_CENTER | WS_POPUP | WS_CAPTION |
+ WS_SYSMENU
+CAPTION "About VNC Config for Windows"
+FONT 8, "MS Sans Serif"
+BEGIN
+ DEFPUSHBUTTON "OK",IDOK,195,70,47,15
+ ICON IDI_ICON,IDC_STATIC,7,7,20,20
+ LTEXT ">appname<",IDC_DESCRIPTION,40,7,125,18
+ LTEXT ">version<",IDC_VERSION,165,7,77,18
+ LTEXT ">buildtime<",IDC_BUILDTIME,40,25,202,15
+ LTEXT ">copyright<",IDC_COPYRIGHT,40,40,202,15
+ LTEXT "See http://www.realvnc.com for more information on VNC.",
+ IDC_STATIC,40,55,202,15
+END
+
+IDD_DESKTOP DIALOG DISCARDABLE 0, 0, 185, 137
+STYLE DS_MODALFRAME | DS_SETFOREGROUND | DS_CONTROL | WS_POPUP | WS_CAPTION |
+ WS_SYSMENU
+CAPTION "Desktop"
+FONT 8, "MS Sans Serif"
+BEGIN
+ CONTROL "Do nothing",IDC_DISCONNECT_NONE,"Button",
+ BS_AUTORADIOBUTTON,15,80,155,15
+ CONTROL "Lock workstation",IDC_DISCONNECT_LOCK,"Button",
+ BS_AUTORADIOBUTTON,15,95,155,15
+ CONTROL "Logoff user",IDC_DISCONNECT_LOGOFF,"Button",
+ BS_AUTORADIOBUTTON,15,110,155,15
+ GROUPBOX "When last client disconnects",IDC_STATIC,7,70,171,60
+ GROUPBOX "While connected",IDC_STATIC,7,5,171,60
+ CONTROL "Remove wallpaper",IDC_REMOVE_WALLPAPER,"Button",
+ BS_AUTOCHECKBOX | WS_TABSTOP,15,15,155,15
+ CONTROL "Remove background pattern",IDC_REMOVE_PATTERN,"Button",
+ BS_AUTOCHECKBOX | WS_TABSTOP,15,30,155,15
+ CONTROL "Disable user interface effects",IDC_DISABLE_EFFECTS,
+ "Button",BS_AUTOCHECKBOX | WS_TABSTOP,15,46,155,14
+END
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// DESIGNINFO
+//
+
+#ifdef APSTUDIO_INVOKED
+GUIDELINES DESIGNINFO DISCARDABLE
+BEGIN
+ IDD_AUTHENTICATION, DIALOG
+ BEGIN
+ LEFTMARGIN, 7
+ RIGHTMARGIN, 218
+ VERTGUIDE, 150
+ VERTGUIDE, 155
+ VERTGUIDE, 210
+ TOPMARGIN, 7
+ BOTTOMMARGIN, 79
+ HORZGUIDE, 10
+ HORZGUIDE, 25
+ HORZGUIDE, 30
+ HORZGUIDE, 45
+ HORZGUIDE, 50
+ HORZGUIDE, 65
+ END
+
+ IDD_CONNECTIONS, DIALOG
+ BEGIN
+ LEFTMARGIN, 7
+ RIGHTMARGIN, 211
+ VERTGUIDE, 15
+ VERTGUIDE, 145
+ VERTGUIDE, 150
+ VERTGUIDE, 205
+ TOPMARGIN, 7
+ BOTTOMMARGIN, 191
+ HORZGUIDE, 10
+ HORZGUIDE, 25
+ HORZGUIDE, 40
+ HORZGUIDE, 55
+ HORZGUIDE, 70
+ HORZGUIDE, 85
+ HORZGUIDE, 90
+ HORZGUIDE, 105
+ HORZGUIDE, 110
+ HORZGUIDE, 125
+ HORZGUIDE, 130
+ HORZGUIDE, 145
+ HORZGUIDE, 150
+ HORZGUIDE, 165
+ HORZGUIDE, 170
+ HORZGUIDE, 185
+ HORZGUIDE, 190
+ END
+
+ IDD_HOOKING, DIALOG
+ BEGIN
+ LEFTMARGIN, 7
+ RIGHTMARGIN, 191
+ TOPMARGIN, 7
+ BOTTOMMARGIN, 88
+ HORZGUIDE, 10
+ HORZGUIDE, 25
+ HORZGUIDE, 40
+ HORZGUIDE, 55
+ END
+
+ IDD_AUTH_VNC_PASSWD, DIALOG
+ BEGIN
+ LEFTMARGIN, 7
+ RIGHTMARGIN, 205
+ VERTGUIDE, 70
+ VERTGUIDE, 75
+ VERTGUIDE, 100
+ VERTGUIDE, 150
+ VERTGUIDE, 155
+ VERTGUIDE, 205
+ TOPMARGIN, 7
+ BOTTOMMARGIN, 65
+ HORZGUIDE, 10
+ HORZGUIDE, 25
+ HORZGUIDE, 30
+ HORZGUIDE, 44
+ HORZGUIDE, 50
+ HORZGUIDE, 65
+ END
+
+ IDD_LEGACY, DIALOG
+ BEGIN
+ LEFTMARGIN, 7
+ RIGHTMARGIN, 159
+ TOPMARGIN, 7
+ BOTTOMMARGIN, 85
+ HORZGUIDE, 10
+ HORZGUIDE, 30
+ HORZGUIDE, 35
+ HORZGUIDE, 50
+ END
+
+ IDD_CONN_HOST, DIALOG
+ BEGIN
+ LEFTMARGIN, 7
+ RIGHTMARGIN, 218
+ VERTGUIDE, 60
+ VERTGUIDE, 65
+ VERTGUIDE, 165
+ TOPMARGIN, 7
+ BOTTOMMARGIN, 40
+ END
+
+ IDD_SHARING, DIALOG
+ BEGIN
+ LEFTMARGIN, 7
+ RIGHTMARGIN, 179
+ TOPMARGIN, 7
+ BOTTOMMARGIN, 88
+ HORZGUIDE, 10
+ HORZGUIDE, 25
+ HORZGUIDE, 40
+ HORZGUIDE, 55
+ HORZGUIDE, 70
+ END
+
+ IDD_INPUTS, DIALOG
+ BEGIN
+ LEFTMARGIN, 7
+ RIGHTMARGIN, 179
+ TOPMARGIN, 7
+ BOTTOMMARGIN, 112
+ HORZGUIDE, 10
+ HORZGUIDE, 25
+ HORZGUIDE, 40
+ HORZGUIDE, 55
+ HORZGUIDE, 70
+ HORZGUIDE, 85
+ HORZGUIDE, 95
+ HORZGUIDE, 110
+ END
+
+ IDD_ABOUT, DIALOG
+ BEGIN
+ LEFTMARGIN, 7
+ RIGHTMARGIN, 242
+ VERTGUIDE, 40
+ VERTGUIDE, 165
+ VERTGUIDE, 195
+ TOPMARGIN, 7
+ BOTTOMMARGIN, 85
+ HORZGUIDE, 7
+ HORZGUIDE, 25
+ HORZGUIDE, 40
+ HORZGUIDE, 55
+ HORZGUIDE, 70
+ END
+
+ IDD_DESKTOP, DIALOG
+ BEGIN
+ LEFTMARGIN, 7
+ RIGHTMARGIN, 178
+ VERTGUIDE, 15
+ VERTGUIDE, 170
+ TOPMARGIN, 5
+ BOTTOMMARGIN, 130
+ HORZGUIDE, 15
+ HORZGUIDE, 30
+ HORZGUIDE, 45
+ HORZGUIDE, 60
+ HORZGUIDE, 65
+ HORZGUIDE, 70
+ HORZGUIDE, 80
+ HORZGUIDE, 95
+ HORZGUIDE, 110
+ HORZGUIDE, 125
+ END
+END
+#endif // APSTUDIO_INVOKED
+
+
+#ifndef _MAC
+/////////////////////////////////////////////////////////////////////////////
+//
+// Version
+//
+
+VS_VERSION_INFO VERSIONINFO
+ FILEVERSION 4,0,0,26
+ PRODUCTVERSION 4,0,0,26
+ 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", "RealVNC Ltd.\0"
+ VALUE "FileDescription", "VNC Server Configuration Applet for Win32\0"
+ VALUE "FileVersion", "4.0\0"
+ VALUE "InternalName", "VNC Config 4.0\0"
+ VALUE "LegalCopyright", "Copyright © RealVNC Ltd. 2002-2004\0"
+ VALUE "LegalTrademarks", "RealVNC\0"
+ VALUE "OriginalFilename", "vncconfig.exe\0"
+ VALUE "PrivateBuild", "\0"
+ VALUE "ProductName", "VNC Config 4.0\0"
+ VALUE "ProductVersion", "4.0\0"
+ VALUE "SpecialBuild", "\0"
+ END
+ END
+ BLOCK "VarFileInfo"
+ BEGIN
+ VALUE "Translation", 0x809, 1200
+ END
+END
+
+#endif // !_MAC
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// 24
+//
+
+IDR_MANIFEST 24 DISCARDABLE "vncconfig.exe.manifest"
+#endif // English (U.K.) resources
+/////////////////////////////////////////////////////////////////////////////
+
+
+
+#ifndef APSTUDIO_INVOKED
+/////////////////////////////////////////////////////////////////////////////
+//
+// Generated from the TEXTINCLUDE 3 resource.
+//
+
+
+/////////////////////////////////////////////////////////////////////////////
+#endif // not APSTUDIO_INVOKED
+
diff --git a/vncconfig_unix/Makefile.in b/vncconfig_unix/Makefile.in
new file mode 100644
index 00000000..58277c3a
--- /dev/null
+++ b/vncconfig_unix/Makefile.in
@@ -0,0 +1,23 @@
+
+SRCS = vncExt.c vncconfig.cxx
+
+OBJS = vncExt.o vncconfig.o
+
+program = vncconfig
+
+DEP_LIBS = ../tx/libtx.a ../rfb/librfb.a ../network/libnetwork.a \
+ ../rdr/librdr.a
+
+EXTRA_LIBS = @X_PRE_LIBS@ @X_LIBS@ -lX11 -lXext @X_EXTRA_LIBS@
+
+DIR_CPPFLAGS = -I$(top_srcdir) -I$(top_srcdir)/tx @X_CFLAGS@ # X_CFLAGS are really CPPFLAGS
+
+all:: $(program)
+
+$(program): $(OBJS) buildtime.o $(DEP_LIBS)
+ rm -f $(program)
+ $(CXXLD) $(CXXFLAGS) $(LDFLAGS) -o $@ $(OBJS) buildtime.o $(DEP_LIBS) $(LIBS) $(EXTRA_LIBS)
+
+buildtime.o: $(OBJS) $(DEP_LIBS)
+
+# followed by boilerplate.mk
diff --git a/vncconfig_unix/buildtime.c b/vncconfig_unix/buildtime.c
new file mode 100644
index 00000000..a96031cc
--- /dev/null
+++ b/vncconfig_unix/buildtime.c
@@ -0,0 +1,18 @@
+/* Copyright (C) 2002-2003 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.
+ */
+char buildtime[] = __DATE__ " " __TIME__;
diff --git a/vncconfig_unix/vncExt.c b/vncconfig_unix/vncExt.c
new file mode 100644
index 00000000..ac1dab37
--- /dev/null
+++ b/vncconfig_unix/vncExt.c
@@ -0,0 +1,316 @@
+/* Copyright (C) 2002-2003 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 <stdio.h>
+
+#define NEED_REPLIES
+#include <X11/Xlibint.h>
+#define _VNCEXT_PROTO_
+#include "vncExt.h"
+
+static Bool XVncExtClientCutTextNotifyWireToEvent(Display* dpy, XEvent* e,
+ xEvent* w);
+static Bool XVncExtSelectionChangeNotifyWireToEvent(Display* dpy, XEvent* e,
+ xEvent* w);
+
+static Bool extensionInited = False;
+static XExtCodes* codes = 0;
+
+static Bool checkExtension(Display* dpy)
+{
+ if (!extensionInited) {
+ extensionInited = True;
+ codes = XInitExtension(dpy, VNCEXTNAME);
+ if (!codes) return False;
+ XESetWireToEvent(dpy, codes->first_event + VncExtClientCutTextNotify,
+ XVncExtClientCutTextNotifyWireToEvent);
+ XESetWireToEvent(dpy, codes->first_event + VncExtSelectionChangeNotify,
+ XVncExtSelectionChangeNotifyWireToEvent);
+ }
+ return codes != 0;
+}
+
+Bool XVncExtQueryExtension(Display* dpy, int* event_basep, int* error_basep)
+{
+ if (!checkExtension(dpy)) return False;
+ *event_basep = codes->first_event;
+ *error_basep = codes->first_error;
+ return True;
+}
+
+Bool XVncExtSetParam(Display* dpy, const char* param)
+{
+ xVncExtSetParamReq* req;
+ xVncExtSetParamReply rep;
+
+ int paramLen = strlen(param);
+ if (paramLen > 255) return False;
+ if (!checkExtension(dpy)) return False;
+
+ LockDisplay(dpy);
+ GetReq(VncExtSetParam, req);
+ req->reqType = codes->major_opcode;
+ req->vncExtReqType = X_VncExtSetParam;
+ req->length += (paramLen + 3) >> 2;
+ req->paramLen = paramLen;
+ Data(dpy, param, paramLen);
+ if (!_XReply(dpy, (xReply *)&rep, 0, xFalse)) {
+ UnlockDisplay(dpy);
+ SyncHandle();
+ return False;
+ }
+ UnlockDisplay(dpy);
+ SyncHandle();
+ return rep.success;
+}
+
+Bool XVncExtGetParam(Display* dpy, const char* param, char** value, int* len)
+{
+ xVncExtGetParamReq* req;
+ xVncExtGetParamReply rep;
+
+ int paramLen = strlen(param);
+ *value = 0;
+ *len = 0;
+ if (paramLen > 255) return False;
+ if (!checkExtension(dpy)) return False;
+
+ LockDisplay(dpy);
+ GetReq(VncExtGetParam, req);
+ req->reqType = codes->major_opcode;
+ req->vncExtReqType = X_VncExtGetParam;
+ req->length += (paramLen + 3) >> 2;
+ req->paramLen = paramLen;
+ Data(dpy, param, paramLen);
+ if (!_XReply(dpy, (xReply *)&rep, 0, xFalse)) {
+ UnlockDisplay(dpy);
+ SyncHandle();
+ return False;
+ }
+ if (rep.success) {
+ *len = rep.valueLen;
+ *value = (char*) Xmalloc (*len+1);
+ _XReadPad(dpy, *value, *len);
+ (*value)[*len] = 0;
+ }
+ UnlockDisplay(dpy);
+ SyncHandle();
+ return rep.success;
+}
+
+char* XVncExtGetParamDesc(Display* dpy, const char* param)
+{
+ xVncExtGetParamDescReq* req;
+ xVncExtGetParamDescReply rep;
+ char* desc = 0;
+
+ int paramLen = strlen(param);
+ if (paramLen > 255) return False;
+ if (!checkExtension(dpy)) return False;
+
+ LockDisplay(dpy);
+ GetReq(VncExtGetParamDesc, req);
+ req->reqType = codes->major_opcode;
+ req->vncExtReqType = X_VncExtGetParamDesc;
+ req->length += (paramLen + 3) >> 2;
+ req->paramLen = paramLen;
+ Data(dpy, param, paramLen);
+ if (!_XReply(dpy, (xReply *)&rep, 0, xFalse)) {
+ UnlockDisplay(dpy);
+ SyncHandle();
+ return False;
+ }
+ if (rep.success) {
+ desc = (char*)Xmalloc(rep.descLen+1);
+ _XReadPad(dpy, desc, rep.descLen);
+ desc[rep.descLen] = 0;
+ }
+ UnlockDisplay(dpy);
+ SyncHandle();
+ return desc;
+}
+
+char** XVncExtListParams(Display* dpy, int* nParams)
+{
+ xVncExtListParamsReq* req;
+ xVncExtListParamsReply rep;
+ char** list = 0;
+ char* ch;
+ int rlen, paramLen, i;
+
+ if (!checkExtension(dpy)) return False;
+
+ LockDisplay(dpy);
+ GetReq(VncExtListParams, req);
+ req->reqType = codes->major_opcode;
+ req->vncExtReqType = X_VncExtListParams;
+ if (!_XReply(dpy, (xReply *)&rep, 0, xFalse)) {
+ UnlockDisplay(dpy);
+ SyncHandle();
+ return False;
+ }
+ UnlockDisplay(dpy);
+ SyncHandle();
+ if (rep.nParams) {
+ list = (char**)Xmalloc(rep.nParams * sizeof(char*));
+ rlen = rep.length << 2;
+ ch = (char*)Xmalloc(rlen + 1);
+ if (!list || !ch) {
+ if (list) Xfree((char*)list);
+ if (ch) Xfree(ch);
+ _XEatData(dpy, rlen);
+ UnlockDisplay(dpy);
+ SyncHandle();
+ return 0;
+ }
+ _XReadPad(dpy, ch, rlen);
+ paramLen = *ch++;
+ for (i = 0; i < rep.nParams; i++) {
+ list[i] = ch;
+ ch += paramLen;
+ paramLen = *ch;
+ *ch++ = 0;
+ }
+ }
+ *nParams = rep.nParams;
+ UnlockDisplay(dpy);
+ SyncHandle();
+ return list;
+}
+
+void XVncExtFreeParamList(char** list)
+{
+ if (list) {
+ Xfree(list[0]-1);
+ Xfree((char*)list);
+ }
+}
+
+Bool XVncExtSetServerCutText(Display* dpy, const char* str, int len)
+{
+ xVncExtSetServerCutTextReq* req;
+
+ if (!checkExtension(dpy)) return False;
+
+ LockDisplay(dpy);
+ GetReq(VncExtSetServerCutText, req);
+ req->reqType = codes->major_opcode;
+ req->vncExtReqType = X_VncExtSetServerCutText;
+ req->length += (len + 3) >> 2;
+ req->textLen = len;
+ Data(dpy, str, len);
+ UnlockDisplay(dpy);
+ SyncHandle();
+ return True;
+}
+
+Bool XVncExtGetClientCutText(Display* dpy, char** str, int* len)
+{
+ xVncExtGetClientCutTextReq* req;
+ xVncExtGetClientCutTextReply rep;
+
+ if (!checkExtension(dpy)) return False;
+
+ LockDisplay(dpy);
+ GetReq(VncExtGetClientCutText, req);
+ req->reqType = codes->major_opcode;
+ req->vncExtReqType = X_VncExtGetClientCutText;
+ if (!_XReply(dpy, (xReply *)&rep, 0, xFalse)) {
+ UnlockDisplay(dpy);
+ SyncHandle();
+ return False;
+ }
+ UnlockDisplay(dpy);
+ SyncHandle();
+ *len = rep.textLen;
+ *str = (char*) Xmalloc (*len+1);
+ _XReadPad(dpy, *str, *len);
+ (*str)[*len] = 0;
+ return True;
+}
+
+Bool XVncExtSelectInput(Display* dpy, Window w, int mask)
+{
+ xVncExtSelectInputReq* req;
+
+ if (!checkExtension(dpy)) return False;
+
+ LockDisplay(dpy);
+ GetReq(VncExtSelectInput, req);
+ req->reqType = codes->major_opcode;
+ req->vncExtReqType = X_VncExtSelectInput;
+ req->window = w;
+ req->mask = mask;
+ UnlockDisplay(dpy);
+ SyncHandle();
+ return True;
+}
+
+Bool XVncExtConnect(Display* dpy, char* hostAndPort)
+{
+ xVncExtConnectReq* req;
+ xVncExtConnectReply rep;
+
+ int strLen = strlen(hostAndPort);
+ if (strLen > 255) return False;
+ if (!checkExtension(dpy)) return False;
+
+ LockDisplay(dpy);
+ GetReq(VncExtConnect, req);
+ req->reqType = codes->major_opcode;
+ req->vncExtReqType = X_VncExtConnect;
+ req->length += (strLen + 3) >> 2;
+ req->strLen = strLen;
+ Data(dpy, hostAndPort, strLen);
+ if (!_XReply(dpy, (xReply *)&rep, 0, xFalse)) {
+ UnlockDisplay(dpy);
+ SyncHandle();
+ return False;
+ }
+ UnlockDisplay(dpy);
+ SyncHandle();
+ return rep.success;
+}
+
+static Bool XVncExtClientCutTextNotifyWireToEvent(Display* dpy, XEvent* e,
+ xEvent* w)
+{
+ XVncExtClientCutTextEvent* ev = (XVncExtClientCutTextEvent*)e;
+ xVncExtClientCutTextNotifyEvent* wire = (xVncExtClientCutTextNotifyEvent*)w;
+ ev->type = wire->type & 0x7f;
+ ev->serial = _XSetLastRequestRead(dpy,(xGenericReply*)wire);
+ ev->send_event = (wire->type & 0x80) != 0;
+ ev->display = dpy;
+ ev->window = wire->window;
+ ev->time = wire->time;
+ return True;
+}
+
+static Bool XVncExtSelectionChangeNotifyWireToEvent(Display* dpy, XEvent* e,
+ xEvent* w)
+{
+ XVncExtSelectionChangeEvent* ev = (XVncExtSelectionChangeEvent*)e;
+ xVncExtSelectionChangeNotifyEvent* wire
+ = (xVncExtSelectionChangeNotifyEvent*)w;
+ ev->type = wire->type & 0x7f;
+ ev->serial = _XSetLastRequestRead(dpy,(xGenericReply*)wire);
+ ev->send_event = (wire->type & 0x80) != 0;
+ ev->display = dpy;
+ ev->window = wire->window;
+ ev->selection = wire->selection;
+ return True;
+}
diff --git a/vncconfig_unix/vncExt.h b/vncconfig_unix/vncExt.h
new file mode 100644
index 00000000..de69f4ed
--- /dev/null
+++ b/vncconfig_unix/vncExt.h
@@ -0,0 +1,279 @@
+/* Copyright (C) 2002-2003 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 _VNCEXT_H_
+#define _VNCEXT_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define X_VncExtSetParam 0
+#define X_VncExtGetParam 1
+#define X_VncExtGetParamDesc 2
+#define X_VncExtListParams 3
+#define X_VncExtSetServerCutText 4
+#define X_VncExtGetClientCutText 5
+#define X_VncExtSelectInput 6
+#define X_VncExtConnect 7
+
+#define VncExtClientCutTextNotify 0
+#define VncExtSelectionChangeNotify 1
+#define VncExtClientCutTextMask (1 << VncExtClientCutTextNotify)
+#define VncExtSelectionChangeMask (1 << VncExtSelectionChangeNotify)
+
+#define VncExtNumberEvents 2
+#define VncExtNumberErrors 0
+
+#ifndef _VNCEXT_SERVER_
+
+Bool XVncExtQueryExtension(Display* dpy, int* event_basep, int* error_basep);
+Bool XVncExtSetParam(Display* dpy, const char* param);
+Bool XVncExtGetParam(Display* dpy, const char* param, char** value, int* len);
+char* XVncExtGetParamDesc(Display* dpy, const char* param);
+char** XVncExtListParams(Display* dpy, int* nParams);
+void XVncExtFreeParamList(char** list);
+Bool XVncExtSetServerCutText(Display* dpy, const char* str, int len);
+Bool XVncExtGetClientCutText(Display* dpy, char** str, int* len);
+Bool XVncExtSelectInput(Display* dpy, Window w, int mask);
+Bool XVncExtConnect(Display* dpy, char* hostAndPort);
+
+typedef struct {
+ int type;
+ unsigned long serial;
+ Bool send_event;
+ Display *display;
+ Window window;
+ Time time;
+} XVncExtClientCutTextEvent;
+
+typedef struct {
+ int type;
+ unsigned long serial;
+ Bool send_event;
+ Display *display;
+ Window window;
+ Atom selection;
+} XVncExtSelectionChangeEvent;
+
+#endif
+
+#ifdef _VNCEXT_PROTO_
+
+#define VNCEXTNAME "VNC-EXTENSION"
+
+typedef struct {
+ CARD8 reqType; /* always VncExtReqCode */
+ CARD8 vncExtReqType; /* always VncExtSetParam */
+ CARD16 length B16;
+ CARD8 paramLen;
+ CARD8 pad0;
+ CARD16 pad1 B16;
+} xVncExtSetParamReq;
+#define sz_xVncExtSetParamReq 8
+
+typedef struct {
+ BYTE type; /* X_Reply */
+ BYTE success;
+ CARD16 sequenceNumber B16;
+ CARD32 length B32;
+ CARD32 pad0 B32;
+ CARD32 pad1 B32;
+ CARD32 pad2 B32;
+ CARD32 pad3 B32;
+ CARD32 pad4 B32;
+ CARD32 pad5 B32;
+} xVncExtSetParamReply;
+#define sz_xVncExtSetParamReply 32
+
+
+typedef struct {
+ CARD8 reqType; /* always VncExtReqCode */
+ CARD8 vncExtReqType; /* always VncExtGetParam */
+ CARD16 length B16;
+ CARD8 paramLen;
+ CARD8 pad0;
+ CARD16 pad1 B16;
+} xVncExtGetParamReq;
+#define sz_xVncExtGetParamReq 8
+
+typedef struct {
+ BYTE type; /* X_Reply */
+ BYTE success;
+ CARD16 sequenceNumber B16;
+ CARD32 length B32;
+ CARD16 valueLen B16;
+ CARD16 pad0 B16;
+ CARD32 pad1 B32;
+ CARD32 pad2 B32;
+ CARD32 pad3 B32;
+ CARD32 pad4 B32;
+ CARD32 pad5 B32;
+} xVncExtGetParamReply;
+#define sz_xVncExtGetParamReply 32
+
+
+typedef struct {
+ CARD8 reqType; /* always VncExtReqCode */
+ CARD8 vncExtReqType; /* always VncExtGetParamDesc */
+ CARD16 length B16;
+ CARD8 paramLen;
+ CARD8 pad0;
+ CARD16 pad1 B16;
+} xVncExtGetParamDescReq;
+#define sz_xVncExtGetParamDescReq 8
+
+typedef struct {
+ BYTE type; /* X_Reply */
+ BYTE success;
+ CARD16 sequenceNumber B16;
+ CARD32 length B32;
+ CARD16 descLen B16;
+ CARD16 pad0 B16;
+ CARD32 pad1 B32;
+ CARD32 pad2 B32;
+ CARD32 pad3 B32;
+ CARD32 pad4 B32;
+ CARD32 pad5 B32;
+} xVncExtGetParamDescReply;
+#define sz_xVncExtGetParamDescReply 32
+
+
+typedef struct {
+ CARD8 reqType; /* always VncExtReqCode */
+ CARD8 vncExtReqType; /* always VncExtListParams */
+ CARD16 length B16;
+} xVncExtListParamsReq;
+#define sz_xVncExtListParamsReq 4
+
+typedef struct {
+ BYTE type; /* X_Reply */
+ BYTE pad0;
+ CARD16 sequenceNumber B16;
+ CARD32 length B32;
+ CARD16 nParams B16;
+ CARD16 pad1 B16;
+ CARD32 pad2 B32;
+ CARD32 pad3 B32;
+ CARD32 pad4 B32;
+ CARD32 pad5 B32;
+ CARD32 pad6 B32;
+} xVncExtListParamsReply;
+#define sz_xVncExtListParamsReply 32
+
+
+typedef struct {
+ CARD8 reqType; /* always VncExtReqCode */
+ CARD8 vncExtReqType; /* always VncExtSetServerCutText */
+ CARD16 length B16;
+ CARD32 textLen B32;
+} xVncExtSetServerCutTextReq;
+#define sz_xVncExtSetServerCutTextReq 8
+
+
+typedef struct {
+ CARD8 reqType; /* always VncExtReqCode */
+ CARD8 vncExtReqType; /* always VncExtGetClientCutText */
+ CARD16 length B16;
+} xVncExtGetClientCutTextReq;
+#define sz_xVncExtGetClientCutTextReq 4
+
+typedef struct {
+ BYTE type; /* X_Reply */
+ BYTE pad0;
+ CARD16 sequenceNumber B16;
+ CARD32 length B32;
+ CARD32 textLen B32;
+ CARD32 pad1 B32;
+ CARD32 pad2 B32;
+ CARD32 pad3 B32;
+ CARD32 pad4 B32;
+ CARD32 pad5 B32;
+} xVncExtGetClientCutTextReply;
+#define sz_xVncExtGetClientCutTextReply 32
+
+
+typedef struct {
+ CARD8 reqType; /* always VncExtReqCode */
+ CARD8 vncExtReqType; /* always VncExtSelectInput */
+ CARD16 length B16;
+ CARD32 window B32;
+ CARD32 mask B32;
+} xVncExtSelectInputReq;
+#define sz_xVncExtSelectInputReq 12
+
+
+typedef struct {
+ CARD8 reqType; /* always VncExtReqCode */
+ CARD8 vncExtReqType; /* always VncExtConnect */
+ CARD16 length B16;
+ CARD8 strLen;
+ CARD8 pad0;
+ CARD16 pad1 B16;
+} xVncExtConnectReq;
+#define sz_xVncExtConnectReq 8
+
+typedef struct {
+ BYTE type; /* X_Reply */
+ BYTE success;
+ CARD16 sequenceNumber B16;
+ CARD32 length B32;
+ CARD32 pad0 B32;
+ CARD32 pad1 B32;
+ CARD32 pad2 B32;
+ CARD32 pad3 B32;
+ CARD32 pad4 B32;
+ CARD32 pad5 B32;
+} xVncExtConnectReply;
+#define sz_xVncExtConnectReply 32
+
+
+typedef struct {
+ BYTE type; /* always eventBase + VncExtClientCutTextNotify */
+ BYTE pad0;
+ CARD16 sequenceNumber B16;
+ CARD32 window B32;
+ CARD32 time B32;
+ CARD32 pad1 B32;
+ CARD32 pad2 B32;
+ CARD32 pad3 B32;
+ CARD32 pad4 B32;
+ CARD32 pad5 B32;
+} xVncExtClientCutTextNotifyEvent;
+#define sz_xVncExtClientCutTextNotifyEvent 32
+
+typedef struct {
+ BYTE type; /* always eventBase + VncExtSelectionChangeNotify */
+ BYTE pad0;
+ CARD16 sequenceNumber B16;
+ CARD32 window B32;
+ CARD32 selection B32;
+ CARD32 pad1 B32;
+ CARD32 pad2 B32;
+ CARD32 pad3 B32;
+ CARD32 pad4 B32;
+ CARD32 pad5 B32;
+} xVncExtSelectionChangeNotifyEvent;
+#define sz_xVncExtSelectionChangeNotifyEvent 32
+
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/vncconfig_unix/vncconfig.cxx b/vncconfig_unix/vncconfig.cxx
new file mode 100644
index 00000000..e707ffb9
--- /dev/null
+++ b/vncconfig_unix/vncconfig.cxx
@@ -0,0 +1,313 @@
+/* Copyright (C) 2002-2004 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.
+ */
+//
+// VNC server configuration utility
+//
+
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <errno.h>
+
+#include <signal.h>
+#include <X11/X.h>
+#include <X11/Xlib.h>
+#include <X11/Xatom.h>
+#include <X11/Xutil.h>
+#include <X11/keysym.h>
+#include "vncExt.h"
+#include <rdr/Exception.h>
+#include <rfb/Configuration.h>
+#include <rfb/Logger_stdio.h>
+#include <rfb/LogWriter.h>
+#include "TXWindow.h"
+#include "TXCheckbox.h"
+
+using namespace rfb;
+
+LogWriter vlog("vncconfig");
+
+StringParameter displayname("display", "The X display", "");
+BoolParameter noWindow("nowin", "Don't display a window", 0);
+BoolParameter iconic("iconic", "Start with window iconified", 0);
+
+#define ACCEPT_CUT_TEXT "AcceptCutText"
+#define SEND_CUT_TEXT "SendCutText"
+
+char* programName = 0;
+Display* dpy;
+int vncExtEventBase, vncExtErrorBase;
+
+static bool getBoolParam(Display* dpy, const char* param) {
+ char* data;
+ int len;
+ if (XVncExtGetParam(dpy, param, &data, &len)) {
+ if (strcmp(data,"1") == 0) return true;
+ }
+ return false;
+}
+
+class VncConfigWindow : public TXWindow, public TXEventHandler,
+ public TXDeleteWindowCallback,
+ public TXCheckboxCallback {
+public:
+ VncConfigWindow(Display* dpy)
+ : TXWindow(dpy, 300, 100), clientCutText(0), clientCutTextLen(0),
+ acceptClipboard(dpy, "Accept clipboard from viewers", this, false, this),
+ sendClipboard(dpy, "Send clipboard to viewers", this, false, this),
+ sendPrimary(dpy, "Send primary selection to viewers", this, false, this)
+ {
+ int y = yPad;
+ acceptClipboard.move(xPad, y);
+ acceptClipboard.checked(getBoolParam(dpy, ACCEPT_CUT_TEXT));
+ y += acceptClipboard.height();
+ sendClipboard.move(xPad, y);
+ sendClipboard.checked(getBoolParam(dpy, SEND_CUT_TEXT));
+ y += sendClipboard.height();
+ sendPrimary.move(xPad, y);
+ sendPrimary.checked(true);
+ sendPrimary.disabled(!sendClipboard.checked());
+ y += sendPrimary.height();
+ setEventHandler(this);
+ toplevel("VNC config", this, 0, 0, 0, iconic);
+ XVncExtSelectInput(dpy, win(),
+ VncExtClientCutTextMask|VncExtSelectionChangeMask);
+ }
+
+ // handleEvent(). If we get a ClientCutTextNotify event from Xvnc, set the
+ // primary and clipboard selections to the clientCutText. If we get a
+ // SelectionChangeNotify event from Xvnc, set the serverCutText to the value
+ // of the new selection.
+
+ virtual void handleEvent(TXWindow* w, XEvent* ev) {
+ if (acceptClipboard.checked()) {
+ if (ev->type == vncExtEventBase + VncExtClientCutTextNotify) {
+ XVncExtClientCutTextEvent* cutEv = (XVncExtClientCutTextEvent*)ev;
+ if (clientCutText)
+ XFree(clientCutText);
+ clientCutText = 0;
+ if (XVncExtGetClientCutText(dpy, &clientCutText, &clientCutTextLen)) {
+ vlog.debug("Got client cut text");
+ XStoreBytes(dpy, clientCutText, clientCutTextLen);
+ ownSelection(XA_PRIMARY, cutEv->time);
+ ownSelection(xaCLIPBOARD, cutEv->time);
+ }
+ }
+ }
+ if (sendClipboard.checked()) {
+ if (ev->type == vncExtEventBase + VncExtSelectionChangeNotify) {
+ XVncExtSelectionChangeEvent* selEv = (XVncExtSelectionChangeEvent*)ev;
+ if (selEv->selection == xaCLIPBOARD ||
+ (selEv->selection == XA_PRIMARY && sendPrimary.checked())) {
+ if (!selectionOwner(selEv->selection))
+ XConvertSelection(dpy, selEv->selection, XA_STRING,
+ selEv->selection, win(), CurrentTime);
+ }
+ }
+ }
+ }
+
+
+ // selectionRequest() is called when we are the selection owner and another X
+ // client has requested the selection. We simply put the server's cut text
+ // into the requested property. TXWindow will handle the rest.
+ bool selectionRequest(Window requestor, Atom selection, Atom property)
+ {
+ if (clientCutText)
+ XChangeProperty(dpy, requestor, property, XA_STRING, 8,
+ PropModeReplace, (unsigned char*)clientCutText,
+ clientCutTextLen);
+ return clientCutText;
+ }
+
+ // selectionNotify() is called when we have requested the selection from the
+ // selection owner.
+ void selectionNotify(XSelectionEvent* ev, Atom type, int format,
+ int nitems, void* data)
+ {
+ if (ev->requestor != win() || ev->target != XA_STRING)
+ return;
+
+ if (data && format == 8) {
+ vlog.debug("setting selection as server cut text");
+ XVncExtSetServerCutText(dpy, (char*)data, nitems);
+ }
+ }
+
+ // TXDeleteWindowCallback method
+ virtual void deleteWindow(TXWindow* w) {
+ exit(1);
+ }
+
+ // TXCheckboxCallback method
+ virtual void checkboxSelect(TXCheckbox* checkbox) {
+ if (checkbox == &acceptClipboard) {
+ XVncExtSetParam(dpy, (acceptClipboard.checked()
+ ? ACCEPT_CUT_TEXT "=1" : ACCEPT_CUT_TEXT "=0"));
+ } else if (checkbox == &sendClipboard) {
+ XVncExtSetParam(dpy, (sendClipboard.checked()
+ ? SEND_CUT_TEXT "=1" : SEND_CUT_TEXT "=0"));
+ sendPrimary.disabled(!sendClipboard.checked());
+ }
+ }
+
+private:
+ char* clientCutText;
+ int clientCutTextLen;
+ TXCheckbox acceptClipboard, sendClipboard, sendPrimary;
+};
+
+static void usage()
+{
+ fprintf(stderr,"usage: %s [-display <display>] [-nowin] [-iconic]\n",
+ programName);
+ fprintf(stderr," %s [-display <display>] -connect <host>[:<port>]\n",
+ programName);
+ fprintf(stderr," %s [-display <display>] -disconnect\n", programName);
+ fprintf(stderr," %s [-display <display>] [-set] <param>=<value> ...\n",
+ programName);
+ fprintf(stderr," %s [-display <display>] -list\n", programName);
+ fprintf(stderr," %s [-display <display>] -get <param>\n", programName);
+ fprintf(stderr," %s [-display <display>] -desc <param>\n",programName);
+ exit(1);
+}
+
+void removeArgs(int* argc, char** argv, int first, int n)
+{
+ if (first + n > *argc) return;
+ for (int i = first + n; i < *argc; i++)
+ argv[i-n] = argv[i];
+ *argc -= n;
+}
+
+int main(int argc, char** argv)
+{
+ programName = argv[0];
+ rfb::initStdIOLoggers();
+ rfb::LogWriter::setLogParams("*:stderr:30");
+
+ // Process vncconfig's own parameters first, then we process the
+ // other arguments when we have the X display.
+ int i;
+ for (i = 1; i < argc; i++) {
+ if (Configuration::setParam(argv[i]))
+ continue;
+
+ if (argv[i][0] == '-' && i+1 < argc &&
+ Configuration::setParam(&argv[i][1], argv[i+1])) {
+ i++;
+ continue;
+ }
+ break;
+ }
+
+ CharArray displaynameStr(displayname.getData());
+ if (!(dpy = XOpenDisplay(displaynameStr.buf))) {
+ fprintf(stderr,"%s: unable to open display \"%s\"\n",
+ programName, XDisplayName(displaynameStr.buf));
+ exit(1);
+ }
+
+ if (!XVncExtQueryExtension(dpy, &vncExtEventBase, &vncExtErrorBase)) {
+ fprintf(stderr,"No VNC extension on display %s\n",
+ XDisplayName(displaynameStr.buf));
+ exit(1);
+ }
+
+ if (i < argc) {
+ for (; i < argc; i++) {
+ if (strcmp(argv[i], "-connect") == 0) {
+ i++;
+ if (i >= argc) usage();
+ if (!XVncExtConnect(dpy, argv[i])) {
+ fprintf(stderr,"connecting to %s failed\n",argv[i]);
+ }
+ } else if (strcmp(argv[i], "-disconnect") == 0) {
+ if (!XVncExtConnect(dpy, "")) {
+ fprintf(stderr,"disconnecting all clients failed\n");
+ }
+ } else if (strcmp(argv[i], "-get") == 0) {
+ i++;
+ if (i >= argc) usage();
+ char* data;
+ int len;
+ if (XVncExtGetParam(dpy, argv[i], &data, &len)) {
+ printf("%.*s\n",len,data);
+ } else {
+ fprintf(stderr,"getting param %s failed\n",argv[i]);
+ }
+ XFree(data);
+ } else if (strcmp(argv[i], "-desc") == 0) {
+ i++;
+ if (i >= argc) usage();
+ char* desc = XVncExtGetParamDesc(dpy, argv[i]);
+ if (desc) {
+ printf("%s\n",desc);
+ } else {
+ fprintf(stderr,"getting description for param %s failed\n",argv[i]);
+ }
+ XFree(desc);
+ } else if (strcmp(argv[i], "-list") == 0) {
+ int nParams;
+ char** list = XVncExtListParams(dpy, &nParams);
+ for (int i = 0; i < nParams; i++) {
+ printf("%s\n",list[i]);
+ }
+ XVncExtFreeParamList(list);
+ } else if (strcmp(argv[i], "-set") == 0) {
+ i++;
+ if (i >= argc) usage();
+ if (!XVncExtSetParam(dpy, argv[i])) {
+ fprintf(stderr,"setting param %s failed\n",argv[i]);
+ }
+ } else if (XVncExtSetParam(dpy, argv[i])) {
+ fprintf(stderr,"set parameter %s\n",argv[i]);
+ } else {
+ usage();
+ }
+ }
+
+ return 0;
+ }
+
+ try {
+ TXWindow::init(dpy,"Vncconfig");
+
+ VncConfigWindow w(dpy);
+ if (!noWindow) w.map();
+
+ while (true) {
+ TXWindow::handleXEvents(dpy);
+ fd_set rfds;
+ FD_ZERO(&rfds);
+ FD_SET(ConnectionNumber(dpy), &rfds);
+ int n = select(FD_SETSIZE, &rfds, 0, 0, 0);
+ if (n < 0) throw rdr::SystemException("select",errno);
+ }
+
+ XCloseDisplay(dpy);
+
+ } catch (rdr::Exception &e) {
+ vlog.error(e.str());
+ }
+
+ return 0;
+}
diff --git a/vncconfig_unix/vncconfig.man b/vncconfig_unix/vncconfig.man
new file mode 100644
index 00000000..9a7d8ce8
--- /dev/null
+++ b/vncconfig_unix/vncconfig.man
@@ -0,0 +1,115 @@
+.TH vncconfig 1 "3 June 2003" "RealVNC Ltd" "Virtual Network Computing"
+.SH NAME
+vncconfig \- configure and control a VNC server
+.SH SYNOPSIS
+.B vncconfig
+[\fB\-display\fP \fIXdisplay\fP] [\fB\-nowin\fP] [\fB\-iconic\fP]
+.br
+.B vncconfig
+[\fB\-display\fP \fIXdisplay\fP]
+.B \-connect
+.IR host [: port ]
+.br
+.B vncconfig
+[\fB\-display\fP \fIXdisplay\fP]
+.B \-disconnect
+.br
+.B vncconfig
+[\fB\-display\fP \fIXdisplay\fP]
+.IR param = value " ..."
+.br
+.B vncconfig
+[\fB\-display\fP \fIXdisplay\fP]
+.B \-list
+.br
+.B vncconfig
+[\fB\-display\fP \fIXdisplay\fP]
+\fB\-get\fP \fIparam\fP
+.br
+.B vncconfig
+[\fB\-display\fP \fIXdisplay\fP]
+\fB\-desc\fP \fIparam\fP
+.SH DESCRIPTION
+.B vncconfig
+is used to configure and control a running instance of Xvnc, or any other X
+server with the VNC extension. Note that it cannot be used to control VNC
+servers prior to version 4.
+
+When run with no options, it runs as a kind of "helper" application for Xvnc.
+Its main purpose when run in this mode is to support clipboard transfer to and
+from the VNC viewer(s). Note that without a running instance of
+\fBvncconfig\fP there will be no clipboard support. It puts up a window with
+some checkboxes which can be used to disable clipboard transfers if required
+(in the future there may be more functions available from this window). The
+\fB-nowin\fP flag can be used if you always want clipboard support but don't
+wish to clutter the desktop with this window - alternatively the \fB-iconic\fP
+option can be used to make it iconified by default.
+
+When run in any other mode, \fBvncconfig\fP is a one-shot program used to
+configure or control Xvnc as appropriate. It can be used to tell Xvnc to
+connect or disconnect from listening viewers, and to set and retrieve Xvnc's
+parameters.
+
+Note that the DISPLAY environment variable or the \fB\-display\fP option
+must be set as appropriate to control Xvnc. If you run it on an ordinary X
+server (or on a version 3 Xvnc) you will get an error message saying that there
+is no VNC extension.
+
+.SH OPTIONS
+.TP
+.B \-display \fIXdisplay\fP
+Specifies the Xvnc server to control.
+
+.TP
+.B \-nowin
+When run as a "helper" app, don't put up a window.
+
+.TP
+.B \-iconic
+When run as a "helper" app, make the window iconified at startup.
+
+.TP
+.B \-connect \fIhost\fP[:\fIport\fP]
+Tells an Xvnc server to make a "reverse" connection to a listening VNC viewer
+(normally connections are made the other way round - the viewer connects to the
+server). \fIhost\fP is the host where the listening viewer is running. If it's
+not listening on the default port of 5500, you can specify \fIhost:port\fP
+instead.
+
+.TP
+.B \-disconnect
+This causes Xvnc to disconnect from all viewers so that the VNC desktop is not
+displayed anywhere.
+
+.TP
+.IR param = value
+Sets an Xvnc parameter to the given value. Note that some of Xvnc's parameters
+are read only once at startup so that changing them in this way may not have
+any effect.
+
+.TP
+.B \-list
+Lists all the parameters supported by Xvnc.
+
+.TP
+.B \-get \fIparam\fP
+Prints the current value of the given Xvnc parameter.
+
+.TP
+.B \-desc \fIparam\fP
+Prints a short description of the given Xvnc parameter.
+
+.SH SEE ALSO
+.BR vncpasswd (1),
+.BR vncviewer (1),
+.BR vncserver (1),
+.BR Xvnc (1)
+.br
+http://www.realvnc.com
+
+.SH AUTHOR
+Tristan Richardson, RealVNC Ltd.
+
+VNC was originally developed by the RealVNC team while at Olivetti Research Ltd
+/ AT&T Laboratories Cambridge. It is now being maintained by RealVNC Ltd. See
+http://www.realvnc.com for details.
diff --git a/vncinstall b/vncinstall
new file mode 100755
index 00000000..e5ab0172
--- /dev/null
+++ b/vncinstall
@@ -0,0 +1,98 @@
+#!/bin/sh
+#
+# Copyright (C) 2002-2003 RealVNC Ltd.
+#
+# 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.
+#
+
+#
+# vncinstall - copy the VNC programs to an installation directory.
+# Also tries to install the manual pages somewhere sensible.
+#
+
+if [ $# -lt 1 -o $# -gt 3 -o ! -d "$1" ]; then
+ echo "usage: $0 <installation-directory> [<man-page-directory>] [<module-directory>]"
+ echo "e.g. $0 /usr/local/bin"
+ exit 1
+fi
+
+dst=$1
+shift
+if [ $# -gt 0 ]; then
+ mandst="$1/man1"
+ shift
+ if [ $# -gt 0 ]; then
+ moduledst=$1
+ shift
+ else
+ moduledst=/usr/X11R6/lib/modules/extensions
+ fi
+else
+ if [ "`basename $dst`" = bin ]; then
+ mandst="`dirname $dst`/man/man1"
+ if [ ! -d "$mandst" -a "$dst" = /usr/bin ]; then
+ mandst=/usr/share/man/man1
+ fi
+ fi
+fi
+
+if [ "$mandst" != "" ]; then
+ if [ ! -d "$mandst" -o ! -w "$mandst" ]; then
+ echo "Can't install manual pages to $mandst"
+ mandst=""
+ fi
+fi
+
+for f in xc/programs/Xserver/Xvnc vncviewer/vncviewer vncpasswd/vncpasswd \
+ vncconfig/vncconfig vncserver x0vncserver/x0vncserver; do
+
+ if [ ! -f $f ]; then
+ echo "Couldn't find $f"
+ else
+ if cmp -s $f $dst/`basename $f`; then
+ echo "`basename $f` hasn't changed"
+ else
+ echo "Copying $f to $dst"
+ cp -pf $f $dst
+ chmod 0555 $dst/`basename $f`
+ fi
+
+
+ if [ -f $f.man ]; then
+ if [ "$mandst" != "" -a -d "$mandst" ]; then
+ if cmp -s $f.man $mandst/`basename $f.1`; then
+ echo "`basename $f.man` hasn't changed"
+ else
+ echo "Copying $f.man to $mandst/`basename $f.1`"
+ cp -pf $f.man $mandst/`basename $f.1`
+ chmod 0444 $mandst/`basename $f.1`
+ fi
+ fi
+ fi
+ fi
+
+done
+
+vncModule=xc/programs/Xserver/vnc/module/vnc.so
+if [ -f "$vncModule" -a -d "$moduledst" ]; then
+ if cmp -s $vncModule $moduledst/`basename $vncModule`; then
+ echo "`basename $vncModule` hasn't changed"
+ else
+ echo "Copying $vncModule to $moduledst"
+ cp -pf $vncModule $moduledst
+ chmod 0555 $moduledst/`basename $vncModule`
+ fi
+fi
diff --git a/vncmkdepend/Makefile b/vncmkdepend/Makefile
new file mode 100644
index 00000000..32783681
--- /dev/null
+++ b/vncmkdepend/Makefile
@@ -0,0 +1,4 @@
+SRCS = include.c main.c parse.c pr.c cppsetup.c ifparser.c
+OBJS = $(SRCS:.c=.o)
+vncmkdepend: $(OBJS)
+ $(CC) $^ -o $@
diff --git a/vncmkdepend/README b/vncmkdepend/README
new file mode 100644
index 00000000..338b62c4
--- /dev/null
+++ b/vncmkdepend/README
@@ -0,0 +1,39 @@
+vncmkdepend
+===========
+
+This code is taken from the X11R6 makedepend sources. All I have done is:
+
+ * Remove all predefined symbols, include directories and other
+ platform-specific behaviour. Its behaviour should be the same on all
+ platforms. All symbols and include directories must be explicitly given on
+ the command line with -D and -I flags.
+ * Fix processing of numbers in #if expressions.
+ * Fix processing of -D flag.
+ * Fix C++ comment handling.
+ * Improve #error warnings.
+ * Change the output so that processing <name>.<suffix> produces <name>.d
+
+The copyright on the code is:
+
+Copyright (c) 1993, 1994 X Consortium
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+X CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+Except as contained in this notice, the name of the X Consortium shall not be
+used in advertising or otherwise to promote the sale, use or other dealings
+in this Software without prior written authorization from the X Consortium.
diff --git a/vncmkdepend/cppsetup.c b/vncmkdepend/cppsetup.c
new file mode 100644
index 00000000..bf1c4fc5
--- /dev/null
+++ b/vncmkdepend/cppsetup.c
@@ -0,0 +1,242 @@
+/* $XConsortium: cppsetup.c,v 1.13 94/04/17 20:10:32 gildea Exp $ */
+/*
+
+Copyright (c) 1993, 1994 X Consortium
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+X CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+Except as contained in this notice, the name of the X Consortium shall not be
+used in advertising or otherwise to promote the sale, use or other dealings
+in this Software without prior written authorization from the X Consortium.
+
+*/
+
+#include "def.h"
+
+#ifdef CPP
+/*
+ * This file is strictly for the sake of cpy.y and yylex.c (if
+ * you indeed have the source for cpp).
+ */
+#define IB 1
+#define SB 2
+#define NB 4
+#define CB 8
+#define QB 16
+#define WB 32
+#define SALT '#'
+#if pdp11 | vax | ns16000 | mc68000 | ibm032
+#define COFF 128
+#else
+#define COFF 0
+#endif
+/*
+ * These variables used by cpy.y and yylex.c
+ */
+extern char *outp, *inp, *newp, *pend;
+extern char *ptrtab;
+extern char fastab[];
+extern char slotab[];
+
+/*
+ * cppsetup
+ */
+struct filepointer *currentfile;
+struct inclist *currentinc;
+
+cppsetup(line, filep, inc)
+ register char *line;
+ register struct filepointer *filep;
+ register struct inclist *inc;
+{
+ register char *p, savec;
+ static boolean setupdone = FALSE;
+ boolean value;
+
+ if (!setupdone) {
+ cpp_varsetup();
+ setupdone = TRUE;
+ }
+
+ currentfile = filep;
+ currentinc = inc;
+ inp = newp = line;
+ for (p=newp; *p; p++)
+ ;
+
+ /*
+ * put a newline back on the end, and set up pend, etc.
+ */
+ *p++ = '\n';
+ savec = *p;
+ *p = '\0';
+ pend = p;
+
+ ptrtab = slotab+COFF;
+ *--inp = SALT;
+ outp=inp;
+ value = yyparse();
+ *p = savec;
+ return(value);
+}
+
+struct symtab *lookup(symbol)
+ char *symbol;
+{
+ static struct symtab undefined;
+ struct symtab *sp;
+
+ sp = isdefined(symbol, currentinc, NULL);
+ if (sp == NULL) {
+ sp = &undefined;
+ sp->s_value = NULL;
+ }
+ return (sp);
+}
+
+pperror(tag, x0,x1,x2,x3,x4)
+ int tag,x0,x1,x2,x3,x4;
+{
+ warning("\"%s\", line %d: ", currentinc->i_file, currentfile->f_line);
+ warning(x0,x1,x2,x3,x4);
+}
+
+
+yyerror(s)
+ register char *s;
+{
+ fatalerr("Fatal error: %s\n", s);
+}
+#else /* not CPP */
+
+#include "ifparser.h"
+struct _parse_data {
+ struct filepointer *filep;
+ struct inclist *inc;
+ const char *line;
+};
+
+static const char *
+_my_if_errors (ip, cp, expecting)
+ IfParser *ip;
+ const char *cp;
+ const char *expecting;
+{
+ struct _parse_data *pd = (struct _parse_data *) ip->data;
+ int lineno = pd->filep->f_line;
+ char *filename = pd->inc->i_file;
+ char prefix[300];
+ int prefixlen;
+ int i;
+
+ sprintf (prefix, "\"%s\":%d", filename, lineno);
+ prefixlen = strlen(prefix);
+ fprintf (stderr, "%s: %s", prefix, pd->line);
+ i = cp - pd->line;
+ if (i > 0 && pd->line[i-1] != '\n') {
+ putc ('\n', stderr);
+ }
+ for (i += prefixlen + 3; i > 0; i--) {
+ putc (' ', stderr);
+ }
+ fprintf (stderr, "^--- expecting %s\n", expecting);
+ return NULL;
+}
+
+
+#define MAXNAMELEN 256
+
+static struct symtab *
+_lookup_variable (ip, var, len)
+ IfParser *ip;
+ const char *var;
+ int len;
+{
+ char tmpbuf[MAXNAMELEN + 1];
+ struct _parse_data *pd = (struct _parse_data *) ip->data;
+
+ if (len > MAXNAMELEN)
+ return 0;
+
+ strncpy (tmpbuf, var, len);
+ tmpbuf[len] = '\0';
+ return isdefined (tmpbuf, pd->inc, NULL);
+}
+
+
+static int
+_my_eval_defined (ip, var, len)
+ IfParser *ip;
+ const char *var;
+ int len;
+{
+ if (_lookup_variable (ip, var, len))
+ return 1;
+ else
+ return 0;
+}
+
+#define isvarfirstletter(ccc) (isalpha(ccc) || (ccc) == '_')
+
+static int
+_my_eval_variable (ip, var, len)
+ IfParser *ip;
+ const char *var;
+ int len;
+{
+ struct symtab *s;
+
+ s = _lookup_variable (ip, var, len);
+ if (!s)
+ return 0;
+ do {
+ var = s->s_value;
+ if (!isvarfirstletter(*var))
+ break;
+ s = _lookup_variable (ip, var, strlen(var));
+ } while (s);
+
+ return atoi(var);
+}
+
+
+cppsetup(line, filep, inc)
+ register char *line;
+ register struct filepointer *filep;
+ register struct inclist *inc;
+{
+ IfParser ip;
+ struct _parse_data pd;
+ int val = 0;
+
+ pd.filep = filep;
+ pd.inc = inc;
+ pd.line = line;
+ ip.funcs.handle_error = _my_if_errors;
+ ip.funcs.eval_defined = _my_eval_defined;
+ ip.funcs.eval_variable = _my_eval_variable;
+ ip.data = (char *) &pd;
+
+ (void) ParseIfExpression (&ip, line, &val);
+ if (val)
+ return IF;
+ else
+ return IFFALSE;
+}
+#endif /* CPP */
+
diff --git a/vncmkdepend/def.h b/vncmkdepend/def.h
new file mode 100644
index 00000000..8fb4c23f
--- /dev/null
+++ b/vncmkdepend/def.h
@@ -0,0 +1,140 @@
+/* $XConsortium: def.h,v 1.25 94/04/17 20:10:33 gildea Exp $ */
+/*
+
+Copyright (c) 1993, 1994 X Consortium
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+X CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+Except as contained in this notice, the name of the X Consortium shall not be
+used in advertising or otherwise to promote the sale, use or other dealings
+in this Software without prior written authorization from the X Consortium.
+
+*/
+
+#include <stdio.h>
+#include <ctype.h>
+#ifndef X_NOT_POSIX
+#ifndef _POSIX_SOURCE
+#define _POSIX_SOURCE
+#endif
+#endif
+#include <sys/types.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+
+#define MAXDEFINES 512
+#define MAXFILES 512
+#define MAXDIRS 64
+#define SYMTABINC 10 /* must be > 1 for define() to work right */
+#define TRUE 1
+#define FALSE 0
+
+/* the following must match the directives table in main.c */
+#define IF 0
+#define IFDEF 1
+#define IFNDEF 2
+#define ELSE 3
+#define ENDIF 4
+#define DEFINE 5
+#define UNDEF 6
+#define INCLUDE 7
+#define LINE 8
+#define PRAGMA 9
+#define ERROR 10
+#define IDENT 11
+#define SCCS 12
+#define ELIF 13
+#define EJECT 14
+#define IFFALSE 15 /* pseudo value --- never matched */
+#define ELIFFALSE 16 /* pseudo value --- never matched */
+#define INCLUDEDOT 17 /* pseudo value --- never matched */
+#define IFGUESSFALSE 18 /* pseudo value --- never matched */
+#define ELIFGUESSFALSE 19 /* pseudo value --- never matched */
+
+#ifdef DEBUG
+extern int _debugmask;
+/*
+ * debug levels are:
+ *
+ * 0 show ifn*(def)*,endif
+ * 1 trace defined/!defined
+ * 2 show #include
+ * 3 show #include SYMBOL
+ * 4-6 unused
+ */
+#define debug(level,arg) { if (_debugmask & (1 << level)) warning arg; }
+#else
+#define debug(level,arg) /**/
+#endif /* DEBUG */
+
+typedef unsigned char boolean;
+
+struct symtab {
+ char *s_name;
+ char *s_value;
+};
+
+struct inclist {
+ char *i_incstring; /* string from #include line */
+ char *i_file; /* path name of the include file */
+ struct inclist **i_list; /* list of files it itself includes */
+ int i_listlen; /* length of i_list */
+ struct symtab *i_defs; /* symbol table for this file */
+ int i_ndefs; /* current # defines */
+ int i_deflen; /* amount of space in table */
+ boolean i_defchecked; /* whether defines have been checked */
+ boolean i_notified; /* whether we have revealed includes */
+ boolean i_marked; /* whether it's in the makefile */
+ boolean i_searched; /* whether we have read this */
+ boolean i_included_sym; /* whether #include SYMBOL was found */
+ /* Can't use i_list if TRUE */
+};
+
+struct filepointer {
+ char *f_p;
+ char *f_base;
+ char *f_end;
+ long f_len;
+ long f_line;
+};
+
+#ifndef X_NOT_STDC_ENV
+#include <stdlib.h>
+#if defined(macII) && !defined(__STDC__) /* stdlib.h fails to define these */
+char *malloc(), *realloc();
+#endif /* macII */
+#else
+char *malloc();
+char *realloc();
+#endif
+
+char *copy();
+char *base_name();
+char *getline();
+struct symtab *slookup();
+struct symtab *isdefined();
+struct symtab *fdefined();
+struct filepointer *getfile();
+struct inclist *newinclude();
+struct inclist *inc_path();
+
+#if NeedVarargsPrototypes
+extern fatalerr(char *, ...);
+extern warning(char *, ...);
+extern warning1(char *, ...);
+#endif
diff --git a/vncmkdepend/ifparser.c b/vncmkdepend/ifparser.c
new file mode 100644
index 00000000..60740bcc
--- /dev/null
+++ b/vncmkdepend/ifparser.c
@@ -0,0 +1,445 @@
+/*
+ * $XConsortium: ifparser.c,v 1.7 94/01/18 21:30:50 rws Exp $
+ *
+ * Copyright 1992 Network Computing Devices, Inc.
+ *
+ * Permission to use, copy, modify, and distribute this software and its
+ * documentation for any purpose and without fee is hereby granted, provided
+ * that the above copyright notice appear in all copies and that both that
+ * copyright notice and this permission notice appear in supporting
+ * documentation, and that the name of Network Computing Devices may not be
+ * used in advertising or publicity pertaining to distribution of the software
+ * without specific, written prior permission. Network Computing Devices makes
+ * no representations about the suitability of this software for any purpose.
+ * It is provided ``as is'' without express or implied warranty.
+ *
+ * NETWORK COMPUTING DEVICES DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS,
+ * IN NO EVENT SHALL NETWORK COMPUTING DEVICES BE LIABLE FOR ANY SPECIAL,
+ * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+ * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+ * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+ * PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Author: Jim Fulton
+ * Network Computing Devices, Inc.
+ *
+ * Simple if statement processor
+ *
+ * This module can be used to evaluate string representations of C language
+ * if constructs. It accepts the following grammar:
+ *
+ * EXPRESSION := VALUE
+ * | VALUE BINOP EXPRESSION
+ *
+ * VALUE := '(' EXPRESSION ')'
+ * | '!' VALUE
+ * | '-' VALUE
+ * | 'defined' '(' variable ')'
+ * | 'defined' variable
+ * | # variable '(' variable-list ')'
+ * | variable
+ * | number
+ *
+ * BINOP := '*' | '/' | '%'
+ * | '+' | '-'
+ * | '<<' | '>>'
+ * | '<' | '>' | '<=' | '>='
+ * | '==' | '!='
+ * | '&' | '|'
+ * | '&&' | '||'
+ *
+ * The normal C order of precidence is supported.
+ *
+ *
+ * External Entry Points:
+ *
+ * ParseIfExpression parse a string for #if
+ */
+
+#include "ifparser.h"
+#include <ctype.h>
+
+/****************************************************************************
+ Internal Macros and Utilities for Parser
+ ****************************************************************************/
+
+#define DO(val) if (!(val)) return NULL
+#define CALLFUNC(ggg,fff) (*((ggg)->funcs.fff))
+#define SKIPSPACE(ccc) while (isspace(*ccc)) ccc++
+#define isvarfirstletter(ccc) (isalpha(ccc) || (ccc) == '_')
+
+
+static const char *
+parse_variable (g, cp, varp)
+ IfParser *g;
+ const char *cp;
+ const char **varp;
+{
+ SKIPSPACE (cp);
+
+ if (!isvarfirstletter (*cp))
+ return CALLFUNC(g, handle_error) (g, cp, "variable name");
+
+ *varp = cp;
+ /* EMPTY */
+ for (cp++; isalnum(*cp) || *cp == '_'; cp++) ;
+ return cp;
+}
+
+
+static const char *
+parse_number (g, cp, valp)
+ IfParser *g;
+ const char *cp;
+ int *valp;
+{
+ SKIPSPACE (cp);
+
+ if (!isdigit(*cp))
+ return CALLFUNC(g, handle_error) (g, cp, "number");
+
+ *valp = strtol(cp, &cp, 0);
+ return cp;
+}
+
+
+static const char *
+parse_value (g, cp, valp)
+ IfParser *g;
+ const char *cp;
+ int *valp;
+{
+ const char *var;
+
+ *valp = 0;
+
+ SKIPSPACE (cp);
+ if (!*cp)
+ return cp;
+
+ switch (*cp) {
+ case '(':
+ DO (cp = ParseIfExpression (g, cp + 1, valp));
+ SKIPSPACE (cp);
+ if (*cp != ')')
+ return CALLFUNC(g, handle_error) (g, cp, ")");
+
+ return cp + 1; /* skip the right paren */
+
+ case '!':
+ DO (cp = parse_value (g, cp + 1, valp));
+ *valp = !(*valp);
+ return cp;
+
+ case '-':
+ DO (cp = parse_value (g, cp + 1, valp));
+ *valp = -(*valp);
+ return cp;
+
+ case '#':
+ DO (cp = parse_variable (g, cp + 1, &var));
+ SKIPSPACE (cp);
+ if (*cp != '(')
+ return CALLFUNC(g, handle_error) (g, cp, "(");
+ do {
+ DO (cp = parse_variable (g, cp + 1, &var));
+ SKIPSPACE (cp);
+ } while (*cp && *cp != ')');
+ if (*cp != ')')
+ return CALLFUNC(g, handle_error) (g, cp, ")");
+ *valp = 1; /* XXX */
+ return cp + 1;
+
+ case 'd':
+ if (strncmp (cp, "defined", 7) == 0 && !isalnum(cp[7])) {
+ int paren = 0;
+ cp += 7;
+ SKIPSPACE (cp);
+ if (*cp == '(') {
+ paren = 1;
+ cp++;
+ }
+ DO (cp = parse_variable (g, cp, &var));
+ SKIPSPACE (cp);
+ if (paren && *cp != ')')
+ return CALLFUNC(g, handle_error) (g, cp, ")");
+ *valp = (*(g->funcs.eval_defined)) (g, var, cp - var);
+ return cp + paren; /* skip the right paren */
+ }
+ /* fall out */
+ }
+
+ if (isdigit(*cp)) {
+ DO (cp = parse_number (g, cp, valp));
+ } else if (!isvarfirstletter(*cp))
+ return CALLFUNC(g, handle_error) (g, cp, "variable or number");
+ else {
+ DO (cp = parse_variable (g, cp, &var));
+ *valp = (*(g->funcs.eval_variable)) (g, var, cp - var);
+ }
+
+ return cp;
+}
+
+
+
+static const char *
+parse_product (g, cp, valp)
+ IfParser *g;
+ const char *cp;
+ int *valp;
+{
+ int rightval;
+
+ DO (cp = parse_value (g, cp, valp));
+ SKIPSPACE (cp);
+
+ switch (*cp) {
+ case '*':
+ DO (cp = parse_product (g, cp + 1, &rightval));
+ *valp = (*valp * rightval);
+ break;
+
+ case '/':
+ DO (cp = parse_product (g, cp + 1, &rightval));
+ *valp = (*valp / rightval);
+ break;
+
+ case '%':
+ DO (cp = parse_product (g, cp + 1, &rightval));
+ *valp = (*valp % rightval);
+ break;
+ }
+ return cp;
+}
+
+
+static const char *
+parse_sum (g, cp, valp)
+ IfParser *g;
+ const char *cp;
+ int *valp;
+{
+ int rightval;
+
+ DO (cp = parse_product (g, cp, valp));
+ SKIPSPACE (cp);
+
+ switch (*cp) {
+ case '+':
+ DO (cp = parse_sum (g, cp + 1, &rightval));
+ *valp = (*valp + rightval);
+ break;
+
+ case '-':
+ DO (cp = parse_sum (g, cp + 1, &rightval));
+ *valp = (*valp - rightval);
+ break;
+ }
+ return cp;
+}
+
+
+static const char *
+parse_shift (g, cp, valp)
+ IfParser *g;
+ const char *cp;
+ int *valp;
+{
+ int rightval;
+
+ DO (cp = parse_sum (g, cp, valp));
+ SKIPSPACE (cp);
+
+ switch (*cp) {
+ case '<':
+ if (cp[1] == '<') {
+ DO (cp = parse_shift (g, cp + 2, &rightval));
+ *valp = (*valp << rightval);
+ }
+ break;
+
+ case '>':
+ if (cp[1] == '>') {
+ DO (cp = parse_shift (g, cp + 2, &rightval));
+ *valp = (*valp >> rightval);
+ }
+ break;
+ }
+ return cp;
+}
+
+
+static const char *
+parse_inequality (g, cp, valp)
+ IfParser *g;
+ const char *cp;
+ int *valp;
+{
+ int rightval;
+
+ DO (cp = parse_shift (g, cp, valp));
+ SKIPSPACE (cp);
+
+ switch (*cp) {
+ case '<':
+ if (cp[1] == '=') {
+ DO (cp = parse_inequality (g, cp + 2, &rightval));
+ *valp = (*valp <= rightval);
+ } else {
+ DO (cp = parse_inequality (g, cp + 1, &rightval));
+ *valp = (*valp < rightval);
+ }
+ break;
+
+ case '>':
+ if (cp[1] == '=') {
+ DO (cp = parse_inequality (g, cp + 2, &rightval));
+ *valp = (*valp >= rightval);
+ } else {
+ DO (cp = parse_inequality (g, cp + 1, &rightval));
+ *valp = (*valp > rightval);
+ }
+ break;
+ }
+ return cp;
+}
+
+
+static const char *
+parse_equality (g, cp, valp)
+ IfParser *g;
+ const char *cp;
+ int *valp;
+{
+ int rightval;
+
+ DO (cp = parse_inequality (g, cp, valp));
+ SKIPSPACE (cp);
+
+ switch (*cp) {
+ case '=':
+ if (cp[1] == '=')
+ cp++;
+ DO (cp = parse_equality (g, cp + 1, &rightval));
+ *valp = (*valp == rightval);
+ break;
+
+ case '!':
+ if (cp[1] != '=')
+ break;
+ DO (cp = parse_equality (g, cp + 2, &rightval));
+ *valp = (*valp != rightval);
+ break;
+ }
+ return cp;
+}
+
+
+static const char *
+parse_band (g, cp, valp)
+ IfParser *g;
+ const char *cp;
+ int *valp;
+{
+ int rightval;
+
+ DO (cp = parse_equality (g, cp, valp));
+ SKIPSPACE (cp);
+
+ switch (*cp) {
+ case '&':
+ if (cp[1] != '&') {
+ DO (cp = parse_band (g, cp + 1, &rightval));
+ *valp = (*valp & rightval);
+ }
+ break;
+ }
+ return cp;
+}
+
+
+static const char *
+parse_bor (g, cp, valp)
+ IfParser *g;
+ const char *cp;
+ int *valp;
+{
+ int rightval;
+
+ DO (cp = parse_band (g, cp, valp));
+ SKIPSPACE (cp);
+
+ switch (*cp) {
+ case '|':
+ if (cp[1] != '|') {
+ DO (cp = parse_bor (g, cp + 1, &rightval));
+ *valp = (*valp | rightval);
+ }
+ break;
+ }
+ return cp;
+}
+
+
+static const char *
+parse_land (g, cp, valp)
+ IfParser *g;
+ const char *cp;
+ int *valp;
+{
+ int rightval;
+
+ DO (cp = parse_bor (g, cp, valp));
+ SKIPSPACE (cp);
+
+ switch (*cp) {
+ case '&':
+ if (cp[1] != '&')
+ return CALLFUNC(g, handle_error) (g, cp, "&&");
+ DO (cp = parse_land (g, cp + 2, &rightval));
+ *valp = (*valp && rightval);
+ break;
+ }
+ return cp;
+}
+
+
+static const char *
+parse_lor (g, cp, valp)
+ IfParser *g;
+ const char *cp;
+ int *valp;
+{
+ int rightval;
+
+ DO (cp = parse_land (g, cp, valp));
+ SKIPSPACE (cp);
+
+ switch (*cp) {
+ case '|':
+ if (cp[1] != '|')
+ return CALLFUNC(g, handle_error) (g, cp, "||");
+ DO (cp = parse_lor (g, cp + 2, &rightval));
+ *valp = (*valp || rightval);
+ break;
+ }
+ return cp;
+}
+
+
+/****************************************************************************
+ External Entry Points
+ ****************************************************************************/
+
+const char *
+ParseIfExpression (g, cp, valp)
+ IfParser *g;
+ const char *cp;
+ int *valp;
+{
+ return parse_lor (g, cp, valp);
+}
+
+
diff --git a/vncmkdepend/ifparser.h b/vncmkdepend/ifparser.h
new file mode 100644
index 00000000..c3337982
--- /dev/null
+++ b/vncmkdepend/ifparser.h
@@ -0,0 +1,76 @@
+/*
+ * $XConsortium: ifparser.h,v 1.1 92/08/22 13:05:39 rws Exp $
+ *
+ * Copyright 1992 Network Computing Devices, Inc.
+ *
+ * Permission to use, copy, modify, and distribute this software and its
+ * documentation for any purpose and without fee is hereby granted, provided
+ * that the above copyright notice appear in all copies and that both that
+ * copyright notice and this permission notice appear in supporting
+ * documentation, and that the name of Network Computing Devices may not be
+ * used in advertising or publicity pertaining to distribution of the software
+ * without specific, written prior permission. Network Computing Devices makes
+ * no representations about the suitability of this software for any purpose.
+ * It is provided ``as is'' without express or implied warranty.
+ *
+ * NETWORK COMPUTING DEVICES DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS,
+ * IN NO EVENT SHALL NETWORK COMPUTING DEVICES BE LIABLE FOR ANY SPECIAL,
+ * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+ * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+ * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+ * PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Author: Jim Fulton
+ * Network Computing Devices, Inc.
+ *
+ * Simple if statement processor
+ *
+ * This module can be used to evaluate string representations of C language
+ * if constructs. It accepts the following grammar:
+ *
+ * EXPRESSION := VALUE
+ * | VALUE BINOP EXPRESSION
+ *
+ * VALUE := '(' EXPRESSION ')'
+ * | '!' VALUE
+ * | '-' VALUE
+ * | 'defined' '(' variable ')'
+ * | variable
+ * | number
+ *
+ * BINOP := '*' | '/' | '%'
+ * | '+' | '-'
+ * | '<<' | '>>'
+ * | '<' | '>' | '<=' | '>='
+ * | '==' | '!='
+ * | '&' | '|'
+ * | '&&' | '||'
+ *
+ * The normal C order of precidence is supported.
+ *
+ *
+ * External Entry Points:
+ *
+ * ParseIfExpression parse a string for #if
+ */
+
+#include <stdio.h>
+
+#define const /**/
+typedef int Bool;
+#define False 0
+#define True 1
+
+typedef struct _if_parser {
+ struct { /* functions */
+ char *(*handle_error) (/* struct _if_parser *, const char *,
+ const char * */);
+ int (*eval_variable) (/* struct _if_parser *, const char *, int */);
+ int (*eval_defined) (/* struct _if_parser *, const char *, int */);
+ } funcs;
+ char *data;
+} IfParser;
+
+char *ParseIfExpression (/* IfParser *, const char *, int * */);
+
diff --git a/vncmkdepend/include.c b/vncmkdepend/include.c
new file mode 100644
index 00000000..d6cd139d
--- /dev/null
+++ b/vncmkdepend/include.c
@@ -0,0 +1,312 @@
+/* $XConsortium: include.c,v 1.17 94/12/05 19:33:08 gildea Exp $ */
+/*
+
+Copyright (c) 1993, 1994 X Consortium
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+X CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+Except as contained in this notice, the name of the X Consortium shall not be
+used in advertising or otherwise to promote the sale, use or other dealings
+in this Software without prior written authorization from the X Consortium.
+
+*/
+
+
+#include "def.h"
+
+extern struct inclist inclist[ MAXFILES ],
+ *inclistp;
+extern char *includedirs[ ];
+extern char *notdotdot[ ];
+extern boolean show_where_not;
+extern boolean warn_multiple;
+
+struct inclist *inc_path(file, include, dot)
+ register char *file,
+ *include;
+ boolean dot;
+{
+ static char path[ BUFSIZ ];
+ register char **pp, *p;
+ register struct inclist *ip;
+ struct stat st;
+ boolean found = FALSE;
+
+ /*
+ * Check all previously found include files for a path that
+ * has already been expanded.
+ */
+ for (ip = inclist; ip->i_file; ip++)
+ if ((strcmp(ip->i_incstring, include) == 0) && !ip->i_included_sym)
+ {
+ found = TRUE;
+ break;
+ }
+
+ /*
+ * If the path was surrounded by "" or is an absolute path,
+ * then check the exact path provided.
+ */
+ if (!found && (dot || *include == '/')) {
+ if (stat(include, &st) == 0) {
+ ip = newinclude(include, include);
+ found = TRUE;
+ }
+ else if (show_where_not)
+ warning1("\tnot in %s\n", include);
+ }
+
+ /*
+ * See if this include file is in the directory of the
+ * file being compiled.
+ */
+ if (!found) {
+ for (p=file+strlen(file); p>file; p--)
+ if (*p == '/')
+ break;
+ if (p == file)
+ strcpy(path, include);
+ else {
+ strncpy(path, file, (p-file) + 1);
+ path[ (p-file) + 1 ] = '\0';
+ strcpy(path + (p-file) + 1, include);
+ }
+ remove_dotdot(path);
+ if (stat(path, &st) == 0) {
+ ip = newinclude(path, include);
+ found = TRUE;
+ }
+ else if (show_where_not)
+ warning1("\tnot in %s\n", path);
+ }
+
+ /*
+ * Check the include directories specified. (standard include dir
+ * should be at the end.)
+ */
+ if (!found)
+ for (pp = includedirs; *pp; pp++) {
+#ifdef WIN32
+ sprintf(path, "%s\\%s", *pp, include);
+#else
+ sprintf(path, "%s/%s", *pp, include);
+#endif
+ remove_dotdot(path);
+ if (stat(path, &st) == 0) {
+ ip = newinclude(path, include);
+ found = TRUE;
+ break;
+ }
+ else if (show_where_not)
+ warning1("\tnot in %s\n", path);
+ }
+
+ if (!found)
+ ip = NULL;
+ return(ip);
+}
+
+/*
+ * Occasionally, pathnames are created that look like .../x/../y
+ * Any of the 'x/..' sequences within the name can be eliminated.
+ * (but only if 'x' is not a symbolic link!!)
+ */
+remove_dotdot(path)
+ char *path;
+{
+ register char *end, *from, *to, **cp;
+ char *components[ MAXFILES ],
+ newpath[ BUFSIZ ];
+ boolean component_copied;
+
+ /*
+ * slice path up into components.
+ */
+ to = newpath;
+ if (*path == '/')
+ *to++ = '/';
+ *to = '\0';
+ cp = components;
+ for (from=end=path; *end; end++)
+ if (*end == '/') {
+ while (*end == '/')
+ *end++ = '\0';
+ if (*from)
+ *cp++ = from;
+ from = end;
+ }
+ *cp++ = from;
+ *cp = NULL;
+
+ /*
+ * Recursively remove all 'x/..' component pairs.
+ */
+ cp = components;
+ while(*cp) {
+ if (!isdot(*cp) && !isdotdot(*cp) && isdotdot(*(cp+1))
+ && !issymbolic(newpath, *cp))
+ {
+ char **fp = cp + 2;
+ char **tp = cp;
+
+ do
+ *tp++ = *fp; /* move all the pointers down */
+ while (*fp++);
+ if (cp != components)
+ cp--; /* go back and check for nested ".." */
+ } else {
+ cp++;
+ }
+ }
+ /*
+ * Concatenate the remaining path elements.
+ */
+ cp = components;
+ component_copied = FALSE;
+ while(*cp) {
+ if (component_copied)
+ *to++ = '/';
+ component_copied = TRUE;
+ for (from = *cp; *from; )
+ *to++ = *from++;
+ *to = '\0';
+ cp++;
+ }
+ *to++ = '\0';
+
+ /*
+ * copy the reconstituted path back to our pointer.
+ */
+ strcpy(path, newpath);
+}
+
+isdot(p)
+ register char *p;
+{
+ if(p && *p++ == '.' && *p++ == '\0')
+ return(TRUE);
+ return(FALSE);
+}
+
+isdotdot(p)
+ register char *p;
+{
+ if(p && *p++ == '.' && *p++ == '.' && *p++ == '\0')
+ return(TRUE);
+ return(FALSE);
+}
+
+issymbolic(dir, component)
+ register char *dir, *component;
+{
+#ifdef S_IFLNK
+ struct stat st;
+ char buf[ BUFSIZ ], **pp;
+
+ sprintf(buf, "%s%s%s", dir, *dir ? "/" : "", component);
+ for (pp=notdotdot; *pp; pp++)
+ if (strcmp(*pp, buf) == 0)
+ return (TRUE);
+ if (lstat(buf, &st) == 0
+ && (st.st_mode & S_IFMT) == S_IFLNK) {
+ *pp++ = copy(buf);
+ if (pp >= &notdotdot[ MAXDIRS ])
+ fatalerr("out of .. dirs, increase MAXDIRS\n");
+ return(TRUE);
+ }
+#endif
+ return(FALSE);
+}
+
+/*
+ * Add an include file to the list of those included by 'file'.
+ */
+struct inclist *newinclude(newfile, incstring)
+ register char *newfile, *incstring;
+{
+ register struct inclist *ip;
+
+ /*
+ * First, put this file on the global list of include files.
+ */
+ ip = inclistp++;
+ if (inclistp == inclist + MAXFILES - 1)
+ fatalerr("out of space: increase MAXFILES\n");
+ ip->i_file = copy(newfile);
+ ip->i_included_sym = FALSE;
+ if (incstring == NULL)
+ ip->i_incstring = ip->i_file;
+ else
+ ip->i_incstring = copy(incstring);
+
+ return(ip);
+}
+
+included_by(ip, newfile)
+ register struct inclist *ip, *newfile;
+{
+ register i;
+
+ if (ip == NULL)
+ return;
+ /*
+ * Put this include file (newfile) on the list of files included
+ * by 'file'. If 'file' is NULL, then it is not an include
+ * file itself (i.e. was probably mentioned on the command line).
+ * If it is already on the list, don't stick it on again.
+ */
+ if (ip->i_list == NULL)
+ ip->i_list = (struct inclist **)
+ malloc(sizeof(struct inclist *) * ++ip->i_listlen);
+ else {
+ for (i=0; i<ip->i_listlen; i++)
+ if (ip->i_list[ i ] == newfile) {
+ i = strlen(newfile->i_file);
+ if (!ip->i_included_sym &&
+ !(i > 2 &&
+ newfile->i_file[i-1] == 'c' &&
+ newfile->i_file[i-2] == '.'))
+ {
+ /* only bitch if ip has */
+ /* no #include SYMBOL lines */
+ /* and is not a .c file */
+ if (warn_multiple)
+ {
+ warning("%s includes %s more than once!\n",
+ ip->i_file, newfile->i_file);
+ warning1("Already have\n");
+ for (i=0; i<ip->i_listlen; i++)
+ warning1("\t%s\n", ip->i_list[i]->i_file);
+ }
+ }
+ return;
+ }
+ ip->i_list = (struct inclist **) realloc(ip->i_list,
+ sizeof(struct inclist *) * ++ip->i_listlen);
+ }
+ ip->i_list[ ip->i_listlen-1 ] = newfile;
+}
+
+inc_clean ()
+{
+ register struct inclist *ip;
+
+ for (ip = inclist; ip < inclistp; ip++) {
+ ip->i_marked = FALSE;
+ }
+}
diff --git a/vncmkdepend/main.c b/vncmkdepend/main.c
new file mode 100644
index 00000000..73057ae7
--- /dev/null
+++ b/vncmkdepend/main.c
@@ -0,0 +1,593 @@
+/* $XConsortium: main.c,v 1.84 94/11/30 16:10:44 kaleb Exp $ */
+/*
+
+Copyright (c) 1993, 1994 X Consortium
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+X CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+Except as contained in this notice, the name of the X Consortium shall not be
+used in advertising or otherwise to promote the sale, use or other dealings
+in this Software without prior written authorization from the X Consortium.
+
+*/
+
+#include "def.h"
+#ifdef hpux
+#define sigvec sigvector
+#endif /* hpux */
+
+#ifdef X_POSIX_C_SOURCE
+#define _POSIX_C_SOURCE X_POSIX_C_SOURCE
+#include <signal.h>
+#undef _POSIX_C_SOURCE
+#else
+#if defined(X_NOT_POSIX) || defined(_POSIX_SOURCE)
+#include <signal.h>
+#else
+#define _POSIX_SOURCE
+#include <signal.h>
+#undef _POSIX_SOURCE
+#endif
+#endif
+
+#if NeedVarargsPrototypes
+#include <stdarg.h>
+#endif
+
+#ifdef DEBUG
+int _debugmask;
+#endif
+
+char *ProgramName;
+
+char *directives[] = {
+ "if",
+ "ifdef",
+ "ifndef",
+ "else",
+ "endif",
+ "define",
+ "undef",
+ "include",
+ "line",
+ "pragma",
+ "error",
+ "ident",
+ "sccs",
+ "elif",
+ "eject",
+ NULL
+};
+
+struct symtab predefs[] = { {NULL, NULL} };
+
+struct inclist inclist[ MAXFILES ],
+ *inclistp = inclist,
+ maininclist;
+
+char *filelist[ MAXFILES ];
+char *includedirs[ MAXDIRS + 1 ];
+char *notdotdot[ MAXDIRS ];
+char *objprefix = "";
+char *objsuffix = ".o";
+int width = 78;
+boolean append = FALSE;
+boolean printed = FALSE;
+boolean verbose = FALSE;
+boolean show_where_not = FALSE;
+boolean warn_multiple = FALSE; /* Warn on multiple includes of same file */
+
+static
+#ifdef SIGNALRETURNSINT
+int
+#else
+void
+#endif
+catch (sig)
+ int sig;
+{
+ fflush (stdout);
+ fatalerr ("got signal %d\n", sig);
+}
+
+#if defined(USG) || (defined(i386) && defined(SYSV)) || defined(WIN32) || defined(__nextstep__)
+#define USGISH
+#endif
+
+#ifndef USGISH
+#ifndef _POSIX_SOURCE
+#define sigaction sigvec
+#define sa_handler sv_handler
+#define sa_mask sv_mask
+#define sa_flags sv_flags
+#endif
+struct sigaction sig_act;
+#endif /* USGISH */
+
+main(argc, argv)
+ int argc;
+ char **argv;
+{
+ register char **fp = filelist;
+ register char **incp = includedirs;
+ register char *p;
+ register struct inclist *ip;
+ char *makefile = NULL;
+ struct filepointer *filecontent;
+ struct symtab *psymp = predefs;
+ char *endmarker = NULL;
+ char *defincdir = NULL;
+
+ ProgramName = argv[0];
+
+ while (psymp->s_name)
+ {
+ define2(psymp->s_name, psymp->s_value, &maininclist);
+ psymp++;
+ }
+ if (argc == 2 && argv[1][0] == '@') {
+ struct stat ast;
+ int afd;
+ char *args;
+ char **nargv;
+ int nargc;
+ char quotechar = '\0';
+
+ nargc = 1;
+ if ((afd = open(argv[1]+1, O_RDONLY)) < 0)
+ fatalerr("cannot open \"%s\"\n", argv[1]+1);
+ fstat(afd, &ast);
+ args = (char *)malloc(ast.st_size + 1);
+ if ((ast.st_size = read(afd, args, ast.st_size)) < 0)
+ fatalerr("failed to read %s\n", argv[1]+1);
+ args[ast.st_size] = '\0';
+ close(afd);
+ for (p = args; *p; p++) {
+ if (quotechar) {
+ if (quotechar == '\\' ||
+ (*p == quotechar && p[-1] != '\\'))
+ quotechar = '\0';
+ continue;
+ }
+ switch (*p) {
+ case '\\':
+ case '"':
+ case '\'':
+ quotechar = *p;
+ break;
+ case ' ':
+ case '\n':
+ *p = '\0';
+ if (p > args && p[-1])
+ nargc++;
+ break;
+ }
+ }
+ if (p[-1])
+ nargc++;
+ nargv = (char **)malloc(nargc * sizeof(char *));
+ nargv[0] = argv[0];
+ argc = 1;
+ for (p = args; argc < nargc; p += strlen(p) + 1)
+ if (*p) nargv[argc++] = p;
+ argv = nargv;
+ }
+ for(argc--, argv++; argc; argc--, argv++) {
+ /* if looking for endmarker then check before parsing */
+ if (endmarker && strcmp (endmarker, *argv) == 0) {
+ endmarker = NULL;
+ continue;
+ }
+ if (**argv != '-') {
+ /* treat +thing as an option for C++ */
+ if (endmarker && **argv == '+')
+ continue;
+ *fp++ = argv[0];
+ continue;
+ }
+ switch(argv[0][1]) {
+ case '-':
+ endmarker = &argv[0][2];
+ if (endmarker[0] == '\0') endmarker = "--";
+ break;
+ case 'D':
+ {
+ int offset = 2;
+ if (argv[0][2] == '\0') {
+ argv++;
+ argc--;
+ offset = 0;
+ }
+ for (p=argv[0] + offset; *p ; p++)
+ if (*p == '=') {
+ *p = ' ';
+ break;
+ }
+ define(argv[0] + offset, &maininclist);
+ break;
+ }
+ case 'I':
+ if (incp >= includedirs + MAXDIRS)
+ fatalerr("Too many -I flags.\n");
+ *incp++ = argv[0]+2;
+ if (**(incp-1) == '\0') {
+ *(incp-1) = *(++argv);
+ argc--;
+ }
+ break;
+ case 'Y':
+ defincdir = argv[0]+2;
+ break;
+ /* do not use if endmarker processing */
+ case 'a':
+ if (endmarker) break;
+ append = TRUE;
+ break;
+ case 'w':
+ if (endmarker) break;
+ if (argv[0][2] == '\0') {
+ argv++;
+ argc--;
+ width = atoi(argv[0]);
+ } else
+ width = atoi(argv[0]+2);
+ break;
+ case 'o':
+ if (endmarker) break;
+ if (argv[0][2] == '\0') {
+ argv++;
+ argc--;
+ objsuffix = argv[0];
+ } else
+ objsuffix = argv[0]+2;
+ break;
+ case 'p':
+ if (endmarker) break;
+ if (argv[0][2] == '\0') {
+ argv++;
+ argc--;
+ objprefix = argv[0];
+ } else
+ objprefix = argv[0]+2;
+ break;
+ case 'v':
+ if (endmarker) break;
+ verbose = TRUE;
+#ifdef DEBUG
+ if (argv[0][2])
+ _debugmask = atoi(argv[0]+2);
+#endif
+ break;
+ case 'm':
+ warn_multiple = TRUE;
+ break;
+
+ /* Ignore -O, -g so we can just pass ${CFLAGS} to
+ makedepend
+ */
+ case 'O':
+ case 'g':
+ break;
+ default:
+ if (endmarker) break;
+ /* fatalerr("unknown opt = %s\n", argv[0]); */
+ warning("ignoring option %s\n", argv[0]);
+ }
+ }
+ if (!defincdir) {
+ } else if (*defincdir) {
+ if (incp >= includedirs + MAXDIRS)
+ fatalerr("Too many -I flags.\n");
+ *incp++ = defincdir;
+ }
+
+ /*
+ * catch signals.
+ */
+#ifdef USGISH
+/* should really reset SIGINT to SIG_IGN if it was. */
+#ifdef SIGHUP
+ signal (SIGHUP, catch);
+#endif
+ signal (SIGINT, catch);
+#ifdef SIGQUIT
+ signal (SIGQUIT, catch);
+#endif
+ signal (SIGILL, catch);
+#ifdef SIGBUS
+ signal (SIGBUS, catch);
+#endif
+ signal (SIGSEGV, catch);
+#ifdef SIGSYS
+ signal (SIGSYS, catch);
+#endif
+#else
+ sig_act.sa_handler = catch;
+#ifdef _POSIX_SOURCE
+ sigemptyset(&sig_act.sa_mask);
+ sigaddset(&sig_act.sa_mask, SIGINT);
+ sigaddset(&sig_act.sa_mask, SIGQUIT);
+#ifdef SIGBUS
+ sigaddset(&sig_act.sa_mask, SIGBUS);
+#endif
+ sigaddset(&sig_act.sa_mask, SIGILL);
+ sigaddset(&sig_act.sa_mask, SIGSEGV);
+ sigaddset(&sig_act.sa_mask, SIGHUP);
+ sigaddset(&sig_act.sa_mask, SIGPIPE);
+#ifdef SIGSYS
+ sigaddset(&sig_act.sa_mask, SIGSYS);
+#endif
+#else
+ sig_act.sa_mask = ((1<<(SIGINT -1))
+ |(1<<(SIGQUIT-1))
+#ifdef SIGBUS
+ |(1<<(SIGBUS-1))
+#endif
+ |(1<<(SIGILL-1))
+ |(1<<(SIGSEGV-1))
+ |(1<<(SIGHUP-1))
+ |(1<<(SIGPIPE-1))
+#ifdef SIGSYS
+ |(1<<(SIGSYS-1))
+#endif
+ );
+#endif /* _POSIX_SOURCE */
+ sig_act.sa_flags = 0;
+ sigaction(SIGHUP, &sig_act, (struct sigaction *)0);
+ sigaction(SIGINT, &sig_act, (struct sigaction *)0);
+ sigaction(SIGQUIT, &sig_act, (struct sigaction *)0);
+ sigaction(SIGILL, &sig_act, (struct sigaction *)0);
+#ifdef SIGBUS
+ sigaction(SIGBUS, &sig_act, (struct sigaction *)0);
+#endif
+ sigaction(SIGSEGV, &sig_act, (struct sigaction *)0);
+#ifdef SIGSYS
+ sigaction(SIGSYS, &sig_act, (struct sigaction *)0);
+#endif
+#endif /* USGISH */
+
+ /*
+ * now peruse through the list of files.
+ */
+ for(fp=filelist; *fp; fp++) {
+ char *base = base_name(*fp);
+ char *depfile = (char *)malloc(strlen(base) + 3);
+ sprintf(depfile,"%s.d",base);
+ if (!freopen(depfile, "wb", stdout))
+ fatalerr("cannot open \"%s\"\n", depfile);
+ free(depfile);
+ free(base);
+ printed = FALSE;
+ filecontent = getfile(*fp);
+ ip = newinclude(*fp, (char *)NULL);
+
+ find_includes(filecontent, ip, ip, 0, TRUE);
+ freefile(filecontent);
+ recursive_pr_include(ip, ip->i_file, base_name(*fp));
+ inc_clean();
+ if (printed)
+ printf("\n");
+ }
+ exit(0);
+}
+
+struct filepointer *getfile(file)
+ char *file;
+{
+ register int fd;
+ struct filepointer *content;
+ struct stat st;
+
+ content = (struct filepointer *)malloc(sizeof(struct filepointer));
+ if ((fd = open(file, O_RDONLY)) < 0) {
+ warning("cannot open \"%s\"\n", file);
+ content->f_p = content->f_base = content->f_end = (char *)malloc(1);
+ *content->f_p = '\0';
+ return(content);
+ }
+ fstat(fd, &st);
+ content->f_base = (char *)malloc(st.st_size+1);
+ if (content->f_base == NULL)
+ fatalerr("cannot allocate mem\n");
+ if ((st.st_size = read(fd, content->f_base, st.st_size)) < 0)
+ fatalerr("failed to read %s\n", file);
+ close(fd);
+ content->f_len = st.st_size+1;
+ content->f_p = content->f_base;
+ content->f_end = content->f_base + st.st_size;
+ *content->f_end = '\0';
+ content->f_line = 0;
+ return(content);
+}
+
+freefile(fp)
+ struct filepointer *fp;
+{
+ free(fp->f_base);
+ free(fp);
+}
+
+char *copy(str)
+ register char *str;
+{
+ register char *p = (char *)malloc(strlen(str) + 1);
+
+ strcpy(p, str);
+ return(p);
+}
+
+match(str, list)
+ register char *str, **list;
+{
+ register int i;
+
+ for (i=0; *list; i++, list++)
+ if (strcmp(str, *list) == 0)
+ return(i);
+ return(-1);
+}
+
+/*
+ * Get the next line. We only return lines beginning with '#' since that
+ * is all this program is ever interested in.
+ */
+char *getline(filep)
+ register struct filepointer *filep;
+{
+ register char *p, /* walking pointer */
+ *eof, /* end of file pointer */
+ *bol; /* beginning of line pointer */
+ register lineno; /* line number */
+
+ p = filep->f_p;
+ eof = filep->f_end;
+ if (p >= eof)
+ return((char *)NULL);
+ lineno = filep->f_line;
+
+ for(bol = p--; ++p < eof; ) {
+ if (*p == '/' && *(p+1) == '*') { /* consume comments */
+ *p++ = ' ', *p++ = ' ';
+ while (*p) {
+ if (*p == '*' && *(p+1) == '/') {
+ *p++ = ' ', *p = ' ';
+ break;
+ }
+ else if (*p == '\n')
+ lineno++;
+ *p++ = ' ';
+ }
+ continue;
+ }
+ else if (*p == '/' && *(p+1) == '/') { /* consume comments */
+ *p++ = ' ', *p++ = ' ';
+ while (*p && *p != '\n')
+ *p++ = ' ';
+ p--; /* go back to before newline */
+ lineno++;
+ continue;
+ }
+ else if (*p == '\\') {
+ if (*(p+1) == '\n') {
+ *p = ' ';
+ *(p+1) = ' ';
+ lineno++;
+ }
+ }
+ else if (*p == '\n') {
+ lineno++;
+ if (*bol == '#') {
+ register char *cp;
+
+ *p++ = '\0';
+ /* punt lines with just # (yacc generated) */
+ for (cp = bol+1;
+ *cp && (*cp == ' ' || *cp == '\t'); cp++);
+ if (*cp) goto done;
+ }
+ bol = p+1;
+ }
+ }
+ if (*bol != '#')
+ bol = NULL;
+done:
+ filep->f_p = p;
+ filep->f_line = lineno;
+ return(bol);
+}
+
+/*
+ * Strip the file name down to what we want to see in the Makefile.
+ * It will have objprefix and objsuffix around it.
+ */
+char *base_name(file)
+ register char *file;
+{
+ register char *p;
+
+ for(p=file+strlen(file); p>file && *p != '/' && *p != '\\'; p--) ;
+ if (p>file) p++;
+ file = copy(p);
+ for(p=file+strlen(file); p>file && *p != '.'; p--) ;
+
+ if (*p == '.')
+ *p = '\0';
+ return(file);
+}
+
+
+#if NeedVarargsPrototypes
+fatalerr(char *msg, ...)
+#else
+/*VARARGS*/
+fatalerr(msg,x1,x2,x3,x4,x5,x6,x7,x8,x9)
+ char *msg;
+#endif
+{
+#if NeedVarargsPrototypes
+ va_list args;
+#endif
+ fprintf(stderr, "%s: error: ", ProgramName);
+#if NeedVarargsPrototypes
+ va_start(args, msg);
+ vfprintf(stderr, msg, args);
+ va_end(args);
+#else
+ fprintf(stderr, msg,x1,x2,x3,x4,x5,x6,x7,x8,x9);
+#endif
+ exit (1);
+}
+
+#if NeedVarargsPrototypes
+warning(char *msg, ...)
+#else
+/*VARARGS0*/
+warning(msg,x1,x2,x3,x4,x5,x6,x7,x8,x9)
+ char *msg;
+#endif
+{
+#if NeedVarargsPrototypes
+ va_list args;
+#endif
+ fprintf(stderr, "%s: warning: ", ProgramName);
+#if NeedVarargsPrototypes
+ va_start(args, msg);
+ vfprintf(stderr, msg, args);
+ va_end(args);
+#else
+ fprintf(stderr, msg,x1,x2,x3,x4,x5,x6,x7,x8,x9);
+#endif
+}
+
+#if NeedVarargsPrototypes
+warning1(char *msg, ...)
+#else
+/*VARARGS0*/
+warning1(msg,x1,x2,x3,x4,x5,x6,x7,x8,x9)
+ char *msg;
+#endif
+{
+#if NeedVarargsPrototypes
+ va_list args;
+ va_start(args, msg);
+ vfprintf(stderr, msg, args);
+ va_end(args);
+#else
+ fprintf(stderr, msg,x1,x2,x3,x4,x5,x6,x7,x8,x9);
+#endif
+}
diff --git a/vncmkdepend/parse.c b/vncmkdepend/parse.c
new file mode 100644
index 00000000..3f9b2fec
--- /dev/null
+++ b/vncmkdepend/parse.c
@@ -0,0 +1,568 @@
+/* $XConsortium: parse.c,v 1.30 94/04/17 20:10:38 gildea Exp $ */
+/*
+
+Copyright (c) 1993, 1994 X Consortium
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+X CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+Except as contained in this notice, the name of the X Consortium shall not be
+used in advertising or otherwise to promote the sale, use or other dealings
+in this Software without prior written authorization from the X Consortium.
+
+*/
+
+#include "def.h"
+
+extern char *directives[];
+extern struct inclist maininclist;
+
+find_includes(filep, file, file_red, recursion, failOK)
+ struct filepointer *filep;
+ struct inclist *file, *file_red;
+ int recursion;
+ boolean failOK;
+{
+ register char *line;
+ register int type;
+ boolean recfailOK;
+
+ while (line = getline(filep)) {
+ switch(type = deftype(line, filep, file_red, file, TRUE)) {
+ case IF:
+ doif:
+ type = find_includes(filep, file,
+ file_red, recursion+1, failOK);
+ while ((type == ELIF) || (type == ELIFFALSE) ||
+ (type == ELIFGUESSFALSE))
+ type = gobble(filep, file, file_red);
+ if (type == ELSE)
+ gobble(filep, file, file_red);
+ break;
+ case IFFALSE:
+ case IFGUESSFALSE:
+ doiffalse:
+ if (type == IFGUESSFALSE || type == ELIFGUESSFALSE)
+ recfailOK = TRUE;
+ else
+ recfailOK = failOK;
+ type = gobble(filep, file, file_red);
+ if (type == ELSE)
+ find_includes(filep, file,
+ file_red, recursion+1, recfailOK);
+ else
+ if (type == ELIF)
+ goto doif;
+ else
+ if ((type == ELIFFALSE) || (type == ELIFGUESSFALSE))
+ goto doiffalse;
+ break;
+ case IFDEF:
+ case IFNDEF:
+ if ((type == IFDEF && isdefined(line, file_red, NULL))
+ || (type == IFNDEF && !isdefined(line, file_red, NULL))) {
+ debug(1,(type == IFNDEF ?
+ "line %d: %s !def'd in %s via %s%s\n" : "",
+ filep->f_line, line,
+ file->i_file, file_red->i_file, ": doit"));
+ type = find_includes(filep, file,
+ file_red, recursion+1, failOK);
+ while (type == ELIF || type == ELIFFALSE || type == ELIFGUESSFALSE)
+ type = gobble(filep, file, file_red);
+ if (type == ELSE)
+ gobble(filep, file, file_red);
+ }
+ else {
+ debug(1,(type == IFDEF ?
+ "line %d: %s !def'd in %s via %s%s\n" : "",
+ filep->f_line, line,
+ file->i_file, file_red->i_file, ": gobble"));
+ type = gobble(filep, file, file_red);
+ if (type == ELSE)
+ find_includes(filep, file,
+ file_red, recursion+1, failOK);
+ else if (type == ELIF)
+ goto doif;
+ else if (type == ELIFFALSE || type == ELIFGUESSFALSE)
+ goto doiffalse;
+ }
+ break;
+ case ELSE:
+ case ELIFFALSE:
+ case ELIFGUESSFALSE:
+ case ELIF:
+ if (!recursion)
+ gobble(filep, file, file_red);
+ case ENDIF:
+ if (recursion)
+ return(type);
+ case DEFINE:
+ define(line, file);
+ break;
+ case UNDEF:
+ if (!*line) {
+ warning("%s, line %d: incomplete undef == \"%s\"\n",
+ file_red->i_file, filep->f_line, line);
+ break;
+ }
+ undefine(line, file_red);
+ break;
+ case INCLUDE:
+ add_include(filep, file, file_red, line, FALSE, failOK);
+ break;
+ case INCLUDEDOT:
+ add_include(filep, file, file_red, line, TRUE, failOK);
+ break;
+ case ERROR:
+ warning("(from %s) %s: %d: %s\n",
+ file_red->i_file, file->i_file,
+ filep->f_line, line);
+ break;
+
+ case PRAGMA:
+ case IDENT:
+ case SCCS:
+ case EJECT:
+ break;
+ case -1:
+ warning("%s", file_red->i_file);
+ if (file_red != file)
+ warning1(" (reading %s)", file->i_file);
+ warning1(", line %d: unknown directive == \"%s\"\n",
+ filep->f_line, line);
+ break;
+ case -2:
+ warning("%s", file_red->i_file);
+ if (file_red != file)
+ warning1(" (reading %s)", file->i_file);
+ warning1(", line %d: incomplete include == \"%s\"\n",
+ filep->f_line, line);
+ break;
+ }
+ }
+ return(-1);
+}
+
+gobble(filep, file, file_red)
+ register struct filepointer *filep;
+ struct inclist *file, *file_red;
+{
+ register char *line;
+ register int type;
+
+ while (line = getline(filep)) {
+ switch(type = deftype(line, filep, file_red, file, FALSE)) {
+ case IF:
+ case IFFALSE:
+ case IFGUESSFALSE:
+ case IFDEF:
+ case IFNDEF:
+ type = gobble(filep, file, file_red);
+ while ((type == ELIF) || (type == ELIFFALSE) ||
+ (type == ELIFGUESSFALSE))
+ type = gobble(filep, file, file_red);
+ if (type == ELSE)
+ (void)gobble(filep, file, file_red);
+ break;
+ case ELSE:
+ case ENDIF:
+ debug(0,("%s, line %d: #%s\n",
+ file->i_file, filep->f_line,
+ directives[type]));
+ return(type);
+ case DEFINE:
+ case UNDEF:
+ case INCLUDE:
+ case INCLUDEDOT:
+ case PRAGMA:
+ case ERROR:
+ case IDENT:
+ case SCCS:
+ case EJECT:
+ break;
+ case ELIF:
+ case ELIFFALSE:
+ case ELIFGUESSFALSE:
+ return(type);
+ case -1:
+ warning("%s, line %d: unknown directive == \"%s\"\n",
+ file_red->i_file, filep->f_line, line);
+ break;
+ }
+ }
+ return(-1);
+}
+
+/*
+ * Decide what type of # directive this line is.
+ */
+int deftype (line, filep, file_red, file, parse_it)
+ register char *line;
+ register struct filepointer *filep;
+ register struct inclist *file_red, *file;
+ int parse_it;
+{
+ register char *p;
+ char *directive, savechar;
+ register int ret;
+
+ /*
+ * Parse the directive...
+ */
+ directive=line+1;
+ while (*directive == ' ' || *directive == '\t')
+ directive++;
+
+ p = directive;
+ while (*p >= 'a' && *p <= 'z')
+ p++;
+ savechar = *p;
+ *p = '\0';
+ ret = match(directive, directives);
+ *p = savechar;
+
+ /* If we don't recognize this compiler directive or we happen to just
+ * be gobbling up text while waiting for an #endif or #elif or #else
+ * in the case of an #elif we must check the zero_value and return an
+ * ELIF or an ELIFFALSE.
+ */
+
+ if (ret == ELIF && !parse_it)
+ {
+ while (*p == ' ' || *p == '\t')
+ p++;
+ /*
+ * parse an expression.
+ */
+ debug(0,("%s, line %d: #elif %s ",
+ file->i_file, filep->f_line, p));
+ ret = zero_value(p, filep, file_red);
+ if (ret != IF)
+ {
+ debug(0,("false...\n"));
+ if (ret == IFFALSE)
+ return(ELIFFALSE);
+ else
+ return(ELIFGUESSFALSE);
+ }
+ else
+ {
+ debug(0,("true...\n"));
+ return(ELIF);
+ }
+ }
+
+ if (ret < 0 || ! parse_it)
+ return(ret);
+
+ /*
+ * now decide how to parse the directive, and do it.
+ */
+ while (*p == ' ' || *p == '\t')
+ p++;
+ switch (ret) {
+ case IF:
+ /*
+ * parse an expression.
+ */
+ ret = zero_value(p, filep, file_red);
+ debug(0,("%s, line %d: %s #if %s\n",
+ file->i_file, filep->f_line, ret?"false":"true", p));
+ break;
+ case IFDEF:
+ case IFNDEF:
+ debug(0,("%s, line %d: #%s %s\n",
+ file->i_file, filep->f_line, directives[ret], p));
+ case UNDEF:
+ /*
+ * separate the name of a single symbol.
+ */
+ while (isalnum(*p) || *p == '_')
+ *line++ = *p++;
+ *line = '\0';
+ break;
+ case INCLUDE:
+ debug(2,("%s, line %d: #include %s\n",
+ file->i_file, filep->f_line, p));
+
+ /* Support ANSI macro substitution */
+ {
+ struct symtab *sym = isdefined(p, file_red, NULL);
+ while (sym) {
+ p = sym->s_value;
+ debug(3,("%s : #includes SYMBOL %s = %s\n",
+ file->i_incstring,
+ sym -> s_name,
+ sym -> s_value));
+ /* mark file as having included a 'soft include' */
+ file->i_included_sym = TRUE;
+ sym = isdefined(p, file_red, NULL);
+ }
+ }
+
+ /*
+ * Separate the name of the include file.
+ */
+ while (*p && *p != '"' && *p != '<')
+ p++;
+ if (! *p)
+ return(-2);
+ if (*p++ == '"') {
+ ret = INCLUDEDOT;
+ while (*p && *p != '"')
+ *line++ = *p++;
+ } else
+ while (*p && *p != '>')
+ *line++ = *p++;
+ *line = '\0';
+ break;
+ case DEFINE:
+ /*
+ * copy the definition back to the beginning of the line.
+ */
+ strcpy (line, p);
+ break;
+ case ELSE:
+ case ENDIF:
+ case ELIF:
+ case PRAGMA:
+ case ERROR:
+ case IDENT:
+ case SCCS:
+ case EJECT:
+ debug(0,("%s, line %d: #%s\n",
+ file->i_file, filep->f_line, directives[ret]));
+ /*
+ * nothing to do.
+ */
+ break;
+ }
+ return(ret);
+}
+
+struct symtab *isdefined(symbol, file, srcfile)
+ register char *symbol;
+ struct inclist *file;
+ struct inclist **srcfile;
+{
+ register struct symtab *val;
+
+ if (val = slookup(symbol, &maininclist)) {
+ debug(1,("%s defined on command line\n", symbol));
+ if (srcfile != NULL) *srcfile = &maininclist;
+ return(val);
+ }
+ if (val = fdefined(symbol, file, srcfile))
+ return(val);
+ debug(1,("%s not defined in %s\n", symbol, file->i_file));
+ return(NULL);
+}
+
+struct symtab *fdefined(symbol, file, srcfile)
+ register char *symbol;
+ struct inclist *file;
+ struct inclist **srcfile;
+{
+ register struct inclist **ip;
+ register struct symtab *val;
+ register int i;
+ static int recurse_lvl = 0;
+
+ if (file->i_defchecked)
+ return(NULL);
+ file->i_defchecked = TRUE;
+ if (val = slookup(symbol, file))
+ debug(1,("%s defined in %s as %s\n", symbol, file->i_file, val->s_value));
+ if (val == NULL && file->i_list)
+ {
+ for (ip = file->i_list, i=0; i < file->i_listlen; i++, ip++)
+ if (val = fdefined(symbol, *ip, srcfile)) {
+ break;
+ }
+ }
+ else if (val != NULL && srcfile != NULL) *srcfile = file;
+ recurse_lvl--;
+ file->i_defchecked = FALSE;
+
+ return(val);
+}
+
+/*
+ * Return type based on if the #if expression evaluates to 0
+ */
+zero_value(exp, filep, file_red)
+ register char *exp;
+ register struct filepointer *filep;
+ register struct inclist *file_red;
+{
+ if (cppsetup(exp, filep, file_red))
+ return(IFFALSE);
+ else
+ return(IF);
+}
+
+define(def, file)
+ char *def;
+ struct inclist *file;
+{
+ char *val;
+
+ /* Separate symbol name and its value */
+ val = def;
+ while (isalnum(*val) || *val == '_')
+ val++;
+ if (*val)
+ *val++ = '\0';
+ while (*val == ' ' || *val == '\t')
+ val++;
+
+ if (!*val)
+ val = "1";
+ define2(def, val, file);
+}
+
+define2(name, val, file)
+ char *name, *val;
+ struct inclist *file;
+{
+ int first, last, below;
+ register struct symtab *sp = NULL, *dest;
+
+ /* Make space if it's needed */
+ if (file->i_defs == NULL)
+ {
+ file->i_defs = (struct symtab *)
+ malloc(sizeof (struct symtab) * SYMTABINC);
+ file->i_deflen = SYMTABINC;
+ file->i_ndefs = 0;
+ }
+ else if (file->i_ndefs == file->i_deflen)
+ file->i_defs = (struct symtab *)
+ realloc(file->i_defs,
+ sizeof(struct symtab)*(file->i_deflen+=SYMTABINC));
+
+ if (file->i_defs == NULL)
+ fatalerr("malloc()/realloc() failure in insert_defn()\n");
+
+ below = first = 0;
+ last = file->i_ndefs - 1;
+ while (last >= first)
+ {
+ /* Fast inline binary search */
+ register char *s1;
+ register char *s2;
+ register int middle = (first + last) / 2;
+
+ /* Fast inline strchr() */
+ s1 = name;
+ s2 = file->i_defs[middle].s_name;
+ while (*s1++ == *s2++)
+ if (s2[-1] == '\0') break;
+
+ /* If exact match, set sp and break */
+ if (*--s1 == *--s2)
+ {
+ sp = file->i_defs + middle;
+ break;
+ }
+
+ /* If name > i_defs[middle] ... */
+ if (*s1 > *s2)
+ {
+ below = first;
+ first = middle + 1;
+ }
+ /* else ... */
+ else
+ {
+ below = last = middle - 1;
+ }
+ }
+
+ /* Search is done. If we found an exact match to the symbol name,
+ just replace its s_value */
+ if (sp != NULL)
+ {
+ free(sp->s_value);
+ sp->s_value = copy(val);
+ return;
+ }
+
+ sp = file->i_defs + file->i_ndefs++;
+ dest = file->i_defs + below + 1;
+ while (sp > dest)
+ {
+ *sp = sp[-1];
+ sp--;
+ }
+ sp->s_name = copy(name);
+ sp->s_value = copy(val);
+}
+
+struct symtab *slookup(symbol, file)
+ register char *symbol;
+ register struct inclist *file;
+{
+ register int first = 0;
+ register int last = file->i_ndefs - 1;
+
+ if (file) while (last >= first)
+ {
+ /* Fast inline binary search */
+ register char *s1;
+ register char *s2;
+ register int middle = (first + last) / 2;
+
+ /* Fast inline strchr() */
+ s1 = symbol;
+ s2 = file->i_defs[middle].s_name;
+ while (*s1++ == *s2++)
+ if (s2[-1] == '\0') break;
+
+ /* If exact match, we're done */
+ if (*--s1 == *--s2)
+ {
+ return file->i_defs + middle;
+ }
+
+ /* If symbol > i_defs[middle] ... */
+ if (*s1 > *s2)
+ {
+ first = middle + 1;
+ }
+ /* else ... */
+ else
+ {
+ last = middle - 1;
+ }
+ }
+ return(NULL);
+}
+
+undefine(symbol, file)
+ char *symbol;
+ register struct inclist *file;
+{
+ register struct symtab *ptr;
+ struct inclist *srcfile;
+ while ((ptr = isdefined(symbol, file, &srcfile)) != NULL)
+ {
+ srcfile->i_ndefs--;
+ for (; ptr < srcfile->i_defs + srcfile->i_ndefs; ptr++)
+ *ptr = ptr[1];
+ }
+}
diff --git a/vncmkdepend/pr.c b/vncmkdepend/pr.c
new file mode 100644
index 00000000..318046d3
--- /dev/null
+++ b/vncmkdepend/pr.c
@@ -0,0 +1,130 @@
+/* $XConsortium: pr.c,v 1.17 94/04/17 20:10:38 gildea Exp $ */
+/*
+
+Copyright (c) 1993, 1994 X Consortium
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+X CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+Except as contained in this notice, the name of the X Consortium shall not be
+used in advertising or otherwise to promote the sale, use or other dealings
+in this Software without prior written authorization from the X Consortium.
+
+*/
+
+#include "def.h"
+
+extern struct inclist inclist[ MAXFILES ],
+ *inclistp;
+extern char *objprefix;
+extern char *objsuffix;
+extern int width;
+extern boolean printed;
+extern boolean verbose;
+extern boolean show_where_not;
+
+add_include(filep, file, file_red, include, dot, failOK)
+ struct filepointer *filep;
+ struct inclist *file, *file_red;
+ char *include;
+ boolean dot;
+{
+ register struct inclist *newfile;
+ register struct filepointer *content;
+
+ /*
+ * First decide what the pathname of this include file really is.
+ */
+ newfile = inc_path(file->i_file, include, dot);
+ if (newfile == NULL) {
+ if (failOK)
+ return;
+ if (file != file_red)
+ warning("%s (reading %s, line %d): ",
+ file_red->i_file, file->i_file, filep->f_line);
+ else
+ warning("%s, line %d: ", file->i_file, filep->f_line);
+ warning1("cannot find include file \"%s\"\n", include);
+ show_where_not = TRUE;
+ newfile = inc_path(file->i_file, include, dot);
+ show_where_not = FALSE;
+ }
+
+ if (newfile) {
+ included_by(file, newfile);
+ if (!newfile->i_searched) {
+ newfile->i_searched = TRUE;
+ content = getfile(newfile->i_file);
+ find_includes(content, newfile, file_red, 0, failOK);
+ freefile(content);
+ }
+ }
+}
+
+recursive_pr_include(head, file, base)
+ register struct inclist *head;
+ register char *file, *base;
+{
+ register int i;
+
+ if (head->i_marked)
+ return;
+ head->i_marked = TRUE;
+ if (head->i_file != file)
+ pr(head, file, base);
+ for (i=0; i<head->i_listlen; i++)
+ recursive_pr_include(head->i_list[ i ], file, base);
+}
+
+pr(ip, file, base)
+ register struct inclist *ip;
+ char *file, *base;
+{
+ static char *lastfile;
+ register int len, i;
+ char buf[ BUFSIZ ];
+#ifdef WIN32
+ char *transfile = TranslateFileNameD2U(ip->i_file,0);
+#else
+ char *transfile = ip->i_file;
+#endif
+
+ printed = TRUE;
+ if (file != lastfile) {
+ lastfile = file;
+ sprintf(buf, "%s%s%s %s.d: %s", objprefix, base, objsuffix,
+ base, transfile);
+ }
+ else {
+ sprintf(buf, " \\\n %s", transfile);
+ }
+ fwrite(buf, strlen(buf), 1, stdout);
+
+ /*
+ * If verbose is set, then print out what this file includes.
+ */
+ if (! verbose || ip->i_list == NULL || ip->i_notified)
+ return;
+ ip->i_notified = TRUE;
+ lastfile = NULL;
+ printf("\n# %s includes:", transfile);
+ for (i=0; i<ip->i_listlen; i++)
+ printf("\n#\t%s", ip->i_list[ i ]->i_incstring);
+#ifdef WIN32
+ free(transfile);
+#endif
+}
diff --git a/vncpasswd/Makefile.in b/vncpasswd/Makefile.in
new file mode 100644
index 00000000..fb625e0b
--- /dev/null
+++ b/vncpasswd/Makefile.in
@@ -0,0 +1,18 @@
+
+SRCS = vncpasswd.cxx
+
+OBJS = vncpasswd.o
+
+program = vncpasswd
+
+DEP_LIBS = ../rfb/librfb.a # ../network/libnetwork.a ../rdr/librdr.a
+
+DIR_CPPFLAGS = -I$(top_srcdir)
+
+all:: $(program)
+
+$(program): $(OBJS) $(DEP_LIBS)
+ rm -f $(program)
+ $(CXXLD) $(CXXFLAGS) $(LDFLAGS) -o $@ $(OBJS) $(DEP_LIBS) $(LIBS)
+
+# followed by boilerplate.mk
diff --git a/vncpasswd/vncpasswd.cxx b/vncpasswd/vncpasswd.cxx
new file mode 100644
index 00000000..c8dd777d
--- /dev/null
+++ b/vncpasswd/vncpasswd.cxx
@@ -0,0 +1,119 @@
+/* Copyright (C) 2002-2003 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 <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <rfb/vncAuth.h>
+#include <rfb/util.h>
+
+using namespace rfb;
+
+char* prog;
+
+static void usage()
+{
+ fprintf(stderr,"usage: %s [file]\n",prog);
+ exit(1);
+}
+
+int main(int argc, char** argv)
+{
+ prog = argv[0];
+
+ char* fname = 0;
+
+ for (int i = 1; i < argc; i++) {
+ if (strcmp(argv[i], "-q") == 0) { // allowed for backwards compatibility
+ } else if (argv[i][0] == '-') {
+ usage();
+ } else if (!fname) {
+ fname = argv[i];
+ } else {
+ usage();
+ }
+ }
+
+ if (!fname) {
+ if (!getenv("HOME")) {
+ fprintf(stderr,"HOME is not set\n");
+ exit(1);
+ }
+ fname = new char[strlen(getenv("HOME")) + 20];
+ sprintf(fname, "%s/.vnc", getenv("HOME"));
+ mkdir(fname, 0777);
+ sprintf(fname, "%s/.vnc/passwd", getenv("HOME"));
+ }
+
+ while (true) {
+ char* passwd = getpass("Password: ");
+ if (!passwd) {
+ perror("getpass error");
+ exit(1);
+ }
+ if (strlen(passwd) < 6) {
+ if (strlen(passwd) == 0) {
+ fprintf(stderr,"Password not changed\n");
+ exit(1);
+ }
+ fprintf(stderr,"Password must be at least 6 characters - try again\n");
+ continue;
+ }
+
+ if (strlen(passwd) > 8)
+ passwd[8] = '\0';
+
+ CharArray passwdCopy(strDup(passwd));
+
+ passwd = getpass("Verify: ");
+ if (!passwd) {
+ perror("getpass error");
+ exit(1);
+ }
+ if (strlen(passwd) > 8)
+ passwd[8] = '\0';
+
+ if (strcmp(passwdCopy.buf, passwd) != 0) {
+ fprintf(stderr,"Passwords don't match - try again\n");
+ continue;
+ }
+
+ FILE* fp = fopen(fname,"w");
+ if (!fp) {
+ fprintf(stderr,"Couldn't open %s for writing\n",fname);
+ exit(1);
+ }
+ chmod(fname, S_IRUSR|S_IWUSR);
+
+ vncAuthObfuscatePasswd(passwd);
+
+ if (fwrite(passwd, 8, 1, fp) != 1) {
+ fprintf(stderr,"Writing to %s failed\n",fname);
+ exit(1);
+ }
+
+ fclose(fp);
+
+ for (unsigned int i = 0; i < strlen(passwd); i++)
+ passwd[i] = passwdCopy.buf[i] = 0;
+
+ return 0;
+ }
+}
diff --git a/vncpasswd/vncpasswd.man b/vncpasswd/vncpasswd.man
new file mode 100644
index 00000000..ab5e7616
--- /dev/null
+++ b/vncpasswd/vncpasswd.man
@@ -0,0 +1,42 @@
+.TH vncpasswd 1 "29 July 2003" "RealVNC Ltd" "Virtual Network Computing"
+.SH NAME
+vncpasswd \- change a VNC password
+.SH SYNOPSIS
+.B vncpasswd
+.RI [ passwd-file ]
+.SH DESCRIPTION
+.B vncpasswd
+allows you to set the password used to access VNC desktops. It stores an
+obfuscated version of the password in the given file (default
+$HOME/.vnc/passwd). The \fBvncserver\fP script runs \fBvncpasswd\fP the first
+time you start a VNC desktop, and invokes \fBXvnc\fP with the appropriate
+\fB\-rfbauth\fP option. \fBvncviewer\fP can also be given a password file to
+use via the \fB\-passwd\fP option.
+
+The password must be at least six characters long, and only the first eight
+characters are significant. Note that the stored password is \fBnot\fP
+encrypted securely - anyone who has access to this file can trivially find out
+the plaintext password, so \fBvncpasswd\fP always sets appropriate permissions
+(read and write only by the owner). However, when accessing a VNC desktop a
+challenge-response mechanism is used over the wire making it hard for anyone to
+crack the password simply by snooping on the network.
+
+.SH FILES
+.TP
+$HOME/.vnc/passwd
+Default location of the VNC password file.
+
+.SH SEE ALSO
+.BR vncviewer (1),
+.BR vncserver (1),
+.BR Xvnc (1)
+.BR vncconfig (1),
+.br
+http://www.realvnc.com
+
+.SH AUTHOR
+Tristan Richardson, RealVNC Ltd.
+
+VNC was originally developed by the RealVNC team while at Olivetti Research Ltd
+/ AT&T Laboratories Cambridge. It is now being maintained by RealVNC Ltd. See
+http://www.realvnc.com for details.
diff --git a/vncserver b/vncserver
new file mode 100755
index 00000000..19333cb8
--- /dev/null
+++ b/vncserver
@@ -0,0 +1,570 @@
+#!/usr/bin/perl
+#
+# Copyright (C) 2002-2003 RealVNC Ltd.
+# Copyright (C) 1999 AT&T Laboratories Cambridge. 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.
+#
+
+#
+# vncserver - wrapper script to start an X VNC server.
+#
+
+#
+# First make sure we're operating in a sane environment.
+#
+
+&SanityCheck();
+
+#
+# Global variables. You may want to configure some of these for your site.
+#
+
+$geometry = "1024x768";
+$depth = 16;
+$vncJavaFiles = (((-d "/usr/share/vnc/classes") && "/usr/share/vnc/classes") ||
+ ((-d "/usr/local/vnc/classes") && "/usr/local/vnc/classes"));
+$vncUserDir = "$ENV{HOME}/.vnc";
+$xauthorityFile = "$ENV{XAUTHORITY}" || "$ENV{HOME}/.Xauthority";
+
+$defaultXStartup
+ = ("#!/bin/sh\n\n".
+ "[ -r \$HOME/.Xresources ] && xrdb \$HOME/.Xresources\n".
+ "xsetroot -solid grey\n".
+ "vncconfig -iconic &\n".
+ "xterm -geometry 80x24+10+10 -ls -title \"\$VNCDESKTOP Desktop\" &\n".
+ "twm &\n");
+
+chop($host = `uname -n`);
+
+
+# Check command line options
+
+&ParseOptions("-geometry",1,"-depth",1,"-pixelformat",1,"-name",1,"-kill",1,
+ "-help",0,"-h",0,"--help",0);
+
+&Usage() if ($opt{'-help'} || $opt{'-h'} || $opt{'--help'});
+
+&Kill() if ($opt{'-kill'});
+
+# Uncomment this line if you want default geometry, depth and pixelformat
+# to match the current X display:
+# &GetXDisplayDefaults();
+
+if ($opt{'-geometry'}) {
+ $geometry = $opt{'-geometry'};
+}
+if ($opt{'-depth'}) {
+ $depth = $opt{'-depth'};
+ $pixelformat = "";
+}
+if ($opt{'-pixelformat'}) {
+ $pixelformat = $opt{'-pixelformat'};
+}
+
+&CheckGeometryAndDepth();
+
+
+# Create the user's vnc directory if necessary.
+
+if (!(-e $vncUserDir)) {
+ if (!mkdir($vncUserDir,0755)) {
+ die "$prog: Could not create $vncUserDir.\n";
+ }
+}
+
+# Make sure the user has a password.
+
+($z,$z,$mode) = stat("$vncUserDir/passwd");
+if (!(-e "$vncUserDir/passwd") || ($mode & 077)) {
+ warn "\nYou will require a password to access your desktops.\n\n";
+ system("vncpasswd -q $vncUserDir/passwd");
+ if (($? >> 8) != 0) {
+ exit 1;
+ }
+}
+
+# Find display number.
+
+if ((@ARGV > 0) && ($ARGV[0] =~ /^:(\d+)$/)) {
+ $displayNumber = $1;
+ shift(@ARGV);
+ if (!&CheckDisplayNumber($displayNumber)) {
+ die "A VNC server is already running as :$displayNumber\n";
+ }
+} elsif ((@ARGV > 0) && ($ARGV[0] !~ /^-/)) {
+ &Usage();
+} else {
+ $displayNumber = &GetDisplayNumber();
+}
+
+$vncPort = 5900 + $displayNumber;
+
+$desktopLog = "$vncUserDir/$host:$displayNumber.log";
+unlink($desktopLog);
+
+# Make an X server cookie - use as the seed the sum of the current time, our
+# PID and part of the encrypted form of the password. Ideally we'd use
+# /dev/urandom, but that's only available on Linux.
+
+srand(time+$$+unpack("L",`cat $vncUserDir/passwd`));
+$cookie = "";
+for (1..16) {
+ $cookie .= sprintf("%02x", int(rand(256)) % 256);
+}
+
+system("xauth -f $xauthorityFile add $host:$displayNumber . $cookie");
+system("xauth -f $xauthorityFile add $host/unix:$displayNumber . $cookie");
+
+if ($opt{'-name'}) {
+ $desktopName = $opt{'-name'};
+} else {
+ $desktopName = "$host:$displayNumber ($ENV{USER})";
+}
+
+# Now start the X VNC Server
+
+$cmd = "Xvnc :$displayNumber";
+$cmd .= " -desktop " . &quotedString($desktopName);
+$cmd .= " -httpd $vncJavaFiles" if ($vncJavaFiles);
+$cmd .= " -auth $xauthorityFile";
+$cmd .= " -geometry $geometry" if ($geometry);
+$cmd .= " -depth $depth" if ($depth);
+$cmd .= " -pixelformat $pixelformat" if ($pixelformat);
+$cmd .= " -rfbwait 30000";
+$cmd .= " -rfbauth $vncUserDir/passwd";
+$cmd .= " -rfbport $vncPort";
+$cmd .= " -pn";
+
+# Add font path and color database stuff here, e.g.:
+#
+# $cmd .= " -fp /usr/lib/X11/fonts/misc/,/usr/lib/X11/fonts/75dpi/";
+# $cmd .= " -co /usr/lib/X11/rgb";
+#
+
+foreach $arg (@ARGV) {
+ $cmd .= " " . &quotedString($arg);
+}
+$cmd .= " >> " . &quotedString($desktopLog) . " 2>&1";
+
+# Run $cmd and record the process ID.
+
+$pidFile = "$vncUserDir/$host:$displayNumber.pid";
+system("$cmd & echo \$! >$pidFile");
+
+# Give Xvnc a chance to start up
+
+sleep(3);
+
+warn "\nNew '$desktopName' desktop is $host:$displayNumber\n\n";
+
+# Create the user's xstartup script if necessary.
+
+if (!(-e "$vncUserDir/xstartup")) {
+ warn "Creating default startup script $vncUserDir/xstartup\n";
+ open(XSTARTUP, ">$vncUserDir/xstartup");
+ print XSTARTUP $defaultXStartup;
+ close(XSTARTUP);
+ chmod 0755, "$vncUserDir/xstartup";
+}
+
+# Run the X startup script.
+
+warn "Starting applications specified in $vncUserDir/xstartup\n";
+warn "Log file is $desktopLog\n\n";
+
+# If the unix domain socket exists then use that (DISPLAY=:n) otherwise use
+# TCP (DISPLAY=host:n)
+
+if (-e "/tmp/.X11-unix/X$displayNumber" ||
+ -e "/usr/spool/sockets/X11/$displayNumber")
+{
+ $ENV{DISPLAY}= ":$displayNumber";
+} else {
+ $ENV{DISPLAY}= "$host:$displayNumber";
+}
+$ENV{VNCDESKTOP}= $desktopName;
+
+system("$vncUserDir/xstartup >> " . &quotedString($desktopLog) . " 2>&1 &");
+
+exit;
+
+
+###############################################################################
+#
+# CheckGeometryAndDepth simply makes sure that the geometry and depth values
+# are sensible.
+#
+
+sub CheckGeometryAndDepth
+{
+ if ($geometry =~ /^(\d+)x(\d+)$/) {
+ $width = $1; $height = $2;
+
+ if (($width<1) || ($height<1)) {
+ die "$prog: geometry $geometry is invalid\n";
+ }
+
+ while (($width % 4)!=0) {
+ $width = $width + 1;
+ }
+
+ while (($height % 2)!=0) {
+ $height = $height + 1;
+ }
+
+ $geometry = "${width}x$height";
+ } else {
+ die "$prog: geometry $geometry is invalid\n";
+ }
+
+ if (($depth < 8) || ($depth > 32)) {
+ die "Depth must be between 8 and 32\n";
+ }
+}
+
+
+#
+# GetDisplayNumber gets the lowest available display number. A display number
+# n is taken if something is listening on the VNC server port (5900+n) or the
+# X server port (6000+n).
+#
+
+sub GetDisplayNumber
+{
+ foreach $n (1..99) {
+ if (&CheckDisplayNumber($n)) {
+ return $n+0; # Bruce Mah's workaround for bug in perl 5.005_02
+ }
+ }
+
+ die "$prog: no free display number on $host.\n";
+}
+
+
+#
+# CheckDisplayNumber checks if the given display number is available. A
+# display number n is taken if something is listening on the VNC server port
+# (5900+n) or the X server port (6000+n).
+#
+
+sub CheckDisplayNumber
+{
+ local ($n) = @_;
+
+ socket(S, $AF_INET, $SOCK_STREAM, 0) || die "$prog: socket failed: $!\n";
+ eval 'setsockopt(S, &SOL_SOCKET, &SO_REUSEADDR, pack("l", 1))';
+ if (!bind(S, pack('S n x12', $AF_INET, 6000 + $n))) {
+ close(S);
+ return 0;
+ }
+ close(S);
+
+ socket(S, $AF_INET, $SOCK_STREAM, 0) || die "$prog: socket failed: $!\n";
+ eval 'setsockopt(S, &SOL_SOCKET, &SO_REUSEADDR, pack("l", 1))';
+ if (!bind(S, pack('S n x12', $AF_INET, 5900 + $n))) {
+ close(S);
+ return 0;
+ }
+ close(S);
+
+ if (-e "/tmp/.X$n-lock") {
+ warn "\nWarning: $host:$n is taken because of /tmp/.X$n-lock\n";
+ warn "Remove this file if there is no X server $host:$n\n";
+ return 0;
+ }
+
+ if (-e "/tmp/.X11-unix/X$n") {
+ warn "\nWarning: $host:$n is taken because of /tmp/.X11-unix/X$n\n";
+ warn "Remove this file if there is no X server $host:$n\n";
+ return 0;
+ }
+
+ if (-e "/usr/spool/sockets/X11/$n") {
+ warn("\nWarning: $host:$n is taken because of ".
+ "/usr/spool/sockets/X11/$n\n");
+ warn "Remove this file if there is no X server $host:$n\n";
+ return 0;
+ }
+
+ return 1;
+}
+
+
+#
+# GetXDisplayDefaults uses xdpyinfo to find out the geometry, depth and pixel
+# format of the current X display being used. If successful, it sets the
+# options as appropriate so that the X VNC server will use the same settings
+# (minus an allowance for window manager decorations on the geometry). Using
+# the same depth and pixel format means that the VNC server won't have to
+# translate pixels when the desktop is being viewed on this X display (for
+# TrueColor displays anyway).
+#
+
+sub GetXDisplayDefaults
+{
+ local (@lines, @matchlines, $width, $height, $defaultVisualId, $i,
+ $red, $green, $blue);
+
+ $wmDecorationWidth = 4; # a guess at typical size for window manager
+ $wmDecorationHeight = 24; # decoration size
+
+ return if (!defined($ENV{DISPLAY}));
+
+ @lines = `xdpyinfo 2>/dev/null`;
+
+ return if ($? != 0);
+
+ @matchlines = grep(/dimensions/, @lines);
+ if (@matchlines) {
+ ($width, $height) = ($matchlines[0] =~ /(\d+)x(\d+) pixels/);
+
+ $width -= $wmDecorationWidth;
+ $height -= $wmDecorationHeight;
+
+ $geometry = "${width}x$height";
+ }
+
+ @matchlines = grep(/default visual id/, @lines);
+ if (@matchlines) {
+ ($defaultVisualId) = ($matchlines[0] =~ /id:\s+(\S+)/);
+
+ for ($i = 0; $i < @lines; $i++) {
+ if ($lines[$i] =~ /^\s*visual id:\s+$defaultVisualId$/) {
+ if (($lines[$i+1] !~ /TrueColor/) ||
+ ($lines[$i+2] !~ /depth/) ||
+ ($lines[$i+4] !~ /red, green, blue masks/))
+ {
+ return;
+ }
+ last;
+ }
+ }
+
+ return if ($i >= @lines);
+
+ ($depth) = ($lines[$i+2] =~ /depth:\s+(\d+)/);
+ ($red,$green,$blue)
+ = ($lines[$i+4]
+ =~ /masks:\s+0x([0-9a-f]+), 0x([0-9a-f]+), 0x([0-9a-f]+)/);
+
+ $red = hex($red);
+ $green = hex($green);
+ $blue = hex($blue);
+
+ if ($red > $blue) {
+ $red = int(log($red) / log(2)) - int(log($green) / log(2));
+ $green = int(log($green) / log(2)) - int(log($blue) / log(2));
+ $blue = int(log($blue) / log(2)) + 1;
+ $pixelformat = "rgb$red$green$blue";
+ } else {
+ $blue = int(log($blue) / log(2)) - int(log($green) / log(2));
+ $green = int(log($green) / log(2)) - int(log($red) / log(2));
+ $red = int(log($red) / log(2)) + 1;
+ $pixelformat = "bgr$blue$green$red";
+ }
+ }
+}
+
+
+#
+# quotedString returns a string which yields the original string when parsed
+# by a shell.
+#
+
+sub quotedString
+{
+ local ($in) = @_;
+
+ $in =~ s/\'/\'\"\'\"\'/g;
+
+ return "'$in'";
+}
+
+
+#
+# removeSlashes turns slashes into underscores for use as a file name.
+#
+
+sub removeSlashes
+{
+ local ($in) = @_;
+
+ $in =~ s|/|_|g;
+
+ return "$in";
+}
+
+
+#
+# Usage
+#
+
+sub Usage
+{
+ die("\nusage: $prog [:<number>] [-name <desktop-name>] [-depth <depth>]\n".
+ " [-geometry <width>x<height>]\n".
+ " [-pixelformat rgbNNN|bgrNNN]\n".
+ " <Xvnc-options>...\n\n".
+ " $prog -kill <X-display>\n\n");
+}
+
+
+#
+# Kill
+#
+
+sub Kill
+{
+ $opt{'-kill'} =~ s/(:\d+)\.\d+$/$1/; # e.g. turn :1.0 into :1
+
+ if ($opt{'-kill'} =~ /^:\d+$/) {
+ $pidFile = "$vncUserDir/$host$opt{'-kill'}.pid";
+ } else {
+ if ($opt{'-kill'} !~ /^$host:/) {
+ die "\nCan't tell if $opt{'-kill'} is on $host\n".
+ "Use -kill :<number> instead\n\n";
+ }
+ $pidFile = "$vncUserDir/$opt{'-kill'}.pid";
+ }
+
+ if (! -r $pidFile) {
+ die "\nCan't find file $pidFile\n".
+ "You'll have to kill the Xvnc process manually\n\n";
+ }
+
+ $SIG{'HUP'} = 'IGNORE';
+ chop($pid = `cat $pidFile`);
+ warn "Killing Xvnc process ID $pid\n";
+ system("kill $pid");
+ unlink $pidFile;
+ exit;
+}
+
+
+#
+# ParseOptions takes a list of possible options and a boolean indicating
+# whether the option has a value following, and sets up an associative array
+# %opt of the values of the options given on the command line. It removes all
+# the arguments it uses from @ARGV and returns them in @optArgs.
+#
+
+sub ParseOptions
+{
+ local (@optval) = @_;
+ local ($opt, @opts, %valFollows, @newargs);
+
+ while (@optval) {
+ $opt = shift(@optval);
+ push(@opts,$opt);
+ $valFollows{$opt} = shift(@optval);
+ }
+
+ @optArgs = ();
+ %opt = ();
+
+ arg: while (defined($arg = shift(@ARGV))) {
+ foreach $opt (@opts) {
+ if ($arg eq $opt) {
+ push(@optArgs, $arg);
+ if ($valFollows{$opt}) {
+ if (@ARGV == 0) {
+ &Usage();
+ }
+ $opt{$opt} = shift(@ARGV);
+ push(@optArgs, $opt{$opt});
+ } else {
+ $opt{$opt} = 1;
+ }
+ next arg;
+ }
+ }
+ push(@newargs,$arg);
+ }
+
+ @ARGV = @newargs;
+}
+
+
+#
+# Routine to make sure we're operating in a sane environment.
+#
+
+sub SanityCheck
+{
+ local ($cmd);
+
+ #
+ # Get the program name
+ #
+
+ ($prog) = ($0 =~ m|([^/]+)$|);
+
+ #
+ # Check we have all the commands we'll need on the path.
+ #
+
+ cmd:
+ foreach $cmd ("uname","xauth","Xvnc","vncpasswd") {
+ for (split(/:/,$ENV{PATH})) {
+ if (-x "$_/$cmd") {
+ next cmd;
+ }
+ }
+ die "$prog: couldn't find \"$cmd\" on your PATH.\n";
+ }
+
+ #
+ # Check the HOME environment variable is set
+ #
+
+ if (!defined($ENV{HOME})) {
+ die "$prog: The HOME environment variable is not set.\n";
+ }
+ chdir($ENV{HOME});
+
+ #
+ # Find socket constants. 'use Socket' is a perl5-ism, so we wrap it in an
+ # eval, and if it fails we try 'require "sys/socket.ph"'. If this fails,
+ # we just guess at the values. If you find perl moaning here, just
+ # hard-code the values of AF_INET and SOCK_STREAM. You can find these out
+ # for your platform by looking in /usr/include/sys/socket.h and related
+ # files.
+ #
+
+ chop($os = `uname`);
+ chop($osrev = `uname -r`);
+
+ eval 'use Socket';
+ if ($@) {
+ eval 'require "sys/socket.ph"';
+ if ($@) {
+ if (($os eq "SunOS") && ($osrev !~ /^4/)) {
+ $AF_INET = 2;
+ $SOCK_STREAM = 2;
+ } else {
+ $AF_INET = 2;
+ $SOCK_STREAM = 1;
+ }
+ } else {
+ $AF_INET = &AF_INET;
+ $SOCK_STREAM = &SOCK_STREAM;
+ }
+ } else {
+ $AF_INET = &AF_INET;
+ $SOCK_STREAM = &SOCK_STREAM;
+ }
+}
diff --git a/vncserver.man b/vncserver.man
new file mode 100644
index 00000000..cd243ffe
--- /dev/null
+++ b/vncserver.man
@@ -0,0 +1,120 @@
+.TH vncserver 1 "18 May 2004" "RealVNC Ltd" "Virtual Network Computing"
+.SH NAME
+vncserver \- start or stop a VNC server
+.SH SYNOPSIS
+.B vncserver
+.RI [: display# ]
+.RB [ \-name
+.IR desktop-name ]
+.RB [ \-geometry
+.IR width x height ]
+.RB [ \-depth
+.IR depth ]
+.RB [ \-pixelformat
+.IR format ]
+.RI [ Xvnc-options... ]
+.br
+.BI "vncserver \-kill :" display#
+.SH DESCRIPTION
+.B vncserver
+is used to start a VNC (Virtual Network Computing) desktop.
+.B vncserver
+is a Perl script which simplifies the process of starting an Xvnc server. It
+runs Xvnc with appropriate options and starts some X applications to be
+displayed in the VNC desktop.
+
+.B vncserver
+can be run with no options at all. In this case it will choose the first
+available display number (usually :1), start Xvnc as that display, and run a
+couple of basic applications to get you started. You can also specify the
+display number, in which case it will use that number if it is available and
+exit if not, eg:
+
+.RS
+vncserver :13
+.RE
+
+Editing the file $HOME/.vnc/xstartup allows you to change the applications run
+at startup (but note that this will not affect an existing desktop).
+
+.SH OPTIONS
+You can get a list of options by giving \fB\-h\fP as an option to vncserver.
+In addition to the options listed below, any unrecognised options will be
+passed to Xvnc - see the Xvnc man page, or "Xvnc \-help" for details.
+
+.TP
+.B \-name \fIdesktop-name\fP
+Each desktop has a name which may be displayed by the viewer. It defaults to
+"\fIhost\fP:\fIdisplay#\fP (\fIusername\fP)" but you can change it with this
+option. It is passed in to the xstartup script via the $VNCDESKTOP environment
+variable, allowing you to run a different set of applications according to the
+name of the desktop.
+
+.TP
+.B \-geometry \fIwidth\fPx\fIheight\fP
+Specify the size of the desktop to be created. Default is 1024x768.
+
+.TP
+.B \-depth \fIdepth\fP
+Specify the pixel depth in bits of the desktop to be created. Default is 16,
+other possible values are 8, 15 and 24 - anything else is likely to cause
+strange behaviour by applications.
+
+.TP
+.B \-pixelformat \fIformat\fP
+Specify pixel format for server to use (BGRnnn or RGBnnn). The default for
+depth 8 is BGR233 (meaning the most significant two bits represent blue, the
+next three green, and the least significant three represent red), the default
+for depth 16 is RGB565 and for depth 24 is RGB888.
+
+.TP
+.B \-cc 3
+As an alternative to the default TrueColor visual, this allows you to run an
+Xvnc server with a PseudoColor visual (i.e. one which uses a colour map or
+palette), which can be useful for running some old X applications which only
+work on such a display. Values other than 3 (PseudoColor) and 4 (TrueColor)
+for the \-cc option may result in strange behaviour, and PseudoColor desktops
+must be 8 bits deep.
+
+.TP
+.B \-kill :\fIdisplay#\fP
+This kills a VNC desktop previously started with vncserver. It does this by
+killing the Xvnc process, whose process ID is stored in the file
+"$HOME/.vnc/\fIhost\fP:\fIdisplay#\fP.pid". It actually ignores anything
+preceding a ":" in its argument. This can be useful so you can write
+"vncserver \-kill $DISPLAY", for example at the end of your xstartup file after
+a particular application exits.
+
+.SH FILES
+Several VNC-related files are found in the directory $HOME/.vnc:
+.TP
+$HOME/.vnc/xstartup
+A shell script specifying X applications to be run when a VNC desktop is
+started. If it doesn't exist, vncserver will create a new one which runs a
+couple of basic applications.
+.TP
+$HOME/.vnc/passwd
+The VNC password file.
+.TP
+$HOME/.vnc/\fIhost\fP:\fIdisplay#\fP.log
+The log file for Xvnc and applications started in xstartup.
+.TP
+$HOME/.vnc/\fIhost\fP:\fIdisplay#\fP.pid
+Identifies the Xvnc process ID, used by the
+.B \-kill
+option.
+
+.SH SEE ALSO
+.BR vncviewer (1),
+.BR vncpasswd (1),
+.BR vncconfig (1),
+.BR Xvnc (1)
+.br
+http://www.realvnc.com
+
+.SH AUTHOR
+Tristan Richardson, RealVNC Ltd.
+
+VNC was originally developed by the RealVNC team while at Olivetti Research Ltd
+/ AT&T Laboratories Cambridge. It is now being maintained by RealVNC Ltd. See
+http://www.realvnc.com for details.
diff --git a/vncviewer/CViewManager.cxx b/vncviewer/CViewManager.cxx
new file mode 100644
index 00000000..09ed1fe8
--- /dev/null
+++ b/vncviewer/CViewManager.cxx
@@ -0,0 +1,252 @@
+/* Copyright (C) 2002-2003 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 <winsock2.h>
+#include <vncviewer/CViewManager.h>
+#include <vncviewer/CView.h>
+#include <vncviewer/ConnectionDialog.h>
+#include <vncviewer/ConnectingDialog.h>
+#include <rfb/Hostname.h>
+#include <rfb/util.h>
+#include <rfb/LogWriter.h>
+#include <rfb/vncAuth.h>
+#include <rdr/HexInStream.h>
+#include <network/TcpSocket.h>
+
+using namespace rfb;
+using namespace win32;
+
+static LogWriter vlog("CViewManager");
+
+
+// -=- Custom thread class used internally
+
+class CViewThread : public Thread {
+public:
+ CViewThread(network::Socket* s, CViewManager& cvm);
+ CViewThread(const char* conninfo, CViewManager& cvm, bool infoIsConfigFile);
+ virtual ~CViewThread();
+
+ virtual void run();
+protected:
+ void setSocket(network::Socket* sock);
+
+ network::Socket* sock;
+ CharArray hostname;
+ CViewManager& manager;
+
+ bool useConfigFile;
+};
+
+
+CViewThread::CViewThread(network::Socket* s, CViewManager& cvm)
+: Thread("CView"), sock(s), manager(cvm) {
+ setDeleteAfterRun();
+}
+
+CViewThread::CViewThread(const char* h, CViewManager& cvm, bool hIsConfigFile)
+: Thread("CView"), sock(0), manager(cvm), useConfigFile(hIsConfigFile) {
+ setDeleteAfterRun();
+ if (h) hostname.buf = strDup(h);
+}
+
+
+CViewThread::~CViewThread() {
+ vlog.debug("~CViewThread");
+ manager.remThread(this);
+ delete sock;
+}
+
+
+void CViewThread::run() {
+ try {
+ CView view;
+ view.setManager(&manager);
+
+ if (!sock) {
+ try {
+ // If the hostname is actually a config filename then read it
+ if (useConfigFile) {
+ CharArray filename = hostname.takeBuf();
+ CViewOptions options;
+ options.readFromFile(filename.buf);
+ if (options.host.buf)
+ hostname.buf = strDup(options.host.buf);
+ view.applyOptions(options);
+ }
+
+ // If there is no hostname then present the connection
+ // dialog
+ if (!hostname.buf) {
+ ConnectionDialog conn(&view);
+ if (!conn.showDialog())
+ return;
+ hostname.buf = strDup(conn.hostname.buf);
+
+ // *** hack - Tell the view object the hostname
+ CViewOptions opt(view.getOptions());
+ opt.setHost(hostname.buf);
+ view.applyOptions(opt);
+ }
+
+ // Parse the host name & port
+ CharArray host;
+ int port;
+ getHostAndPort(hostname.buf, &host.buf, &port);
+
+ // Attempt to connect
+ ConnectingDialog dlg;
+ // this is a nasty hack to get round a Win2K and later "feature" which
+ // puts your second window in the background unless the first window
+ // you put up actually gets some input. Just generate a fake shift
+ // event, which seems to do the trick.
+ keybd_event(VK_SHIFT, MapVirtualKey(VK_SHIFT, 0), 0, 0);
+ keybd_event(VK_SHIFT, MapVirtualKey(VK_SHIFT, 0), KEYEVENTF_KEYUP, 0);
+ sock = new network::TcpSocket(host.buf, port);
+ } catch(rdr::Exception& e) {
+ vlog.error("unable to connect to %s (%s)", hostname, e.str());
+ MsgBox(NULL, TStr(e.str()), MB_ICONERROR | MB_OK);
+ return;
+ }
+
+ // Try to add the caller to the MRU
+ MRU::addToMRU(hostname.buf);
+ }
+
+ view.initialise(sock);
+ try {
+ view.postQuitOnDestroy(true);
+ while (true) {
+ // - processMsg is designed to be callable in response to select().
+ // As a result, it can be called when FdInStream data is available,
+ // BUT there may be no actual RFB data available. This is the case
+ // for example when reading data over an encrypted stream - an
+ // entire block must be read from the FdInStream before any data
+ // becomes available through the top-level encrypted stream.
+ // Since we are using blockCallback and not doing a select() here,
+ // we simply check() for some data on the top-level RFB stream.
+ // This ensures that processMsg will only be called when there is
+ // actually something to do. In the meantime, blockCallback()
+ // will be called, keeping the user interface responsive.
+ view.getInStream()->check(1,1);
+ view.processMsg();
+ }
+ } catch (CView::QuitMessage& e) {
+ // - Cope silently with WM_QUIT messages
+ vlog.debug("QuitMessage received (wParam=%d)", e.wParam);
+ } catch (rdr::EndOfStream& e) {
+ // - Copy silently with disconnection if in NORMAL state
+ if (view.state() == CConnection::RFBSTATE_NORMAL)
+ vlog.debug(e.str());
+ else {
+ view.postQuitOnDestroy(false);
+ throw rfb::Exception("server closed connection unexpectedly");
+ }
+ } catch (rdr::Exception&) {
+ // - We MUST do this, otherwise ~CView will cause a
+ // PostQuitMessage and any MessageBox call will quit immediately.
+ view.postQuitOnDestroy(false);
+ throw;
+ }
+ } catch(rdr::Exception& e) {
+ // - Something went wrong - display the error
+ vlog.error("error: %s", e.str());
+ MsgBox(NULL, TStr(e.str()), MB_ICONERROR | MB_OK);
+ }
+}
+
+
+// -=- CViewManager itself
+
+CViewManager::CViewManager()
+: MsgWindow(_T("CViewManager")), threadsSig(threadsMutex),
+ mainThread(Thread::self()) {
+}
+
+CViewManager::~CViewManager() {
+ while (!socks.empty()) {
+ network::SocketListener* sock = socks.front();
+ delete sock;
+ socks.pop_front();
+ }
+ awaitEmpty();
+}
+
+
+void CViewManager::awaitEmpty() {
+ Lock l(threadsMutex);
+ while (!threads.empty()) {
+ threadsSig.wait(true);
+ }
+}
+
+
+void CViewManager::addThread(Thread* t) {
+ Lock l(threadsMutex);
+ threads.push_front(t);
+}
+
+void CViewManager::remThread(Thread* t) {
+ Lock l(threadsMutex);
+ threads.remove(t);
+ threadsSig.signal();
+
+ // If there are no listening sockets then post a quit message when the
+ // last client disconnects
+ if (socks.empty())
+ PostThreadMessage(mainThread->getThreadId(), WM_QUIT, 0, 0);
+}
+
+
+bool CViewManager::addClient(const char* hostinfo, bool isConfigFile) {
+ CViewThread* thread = new CViewThread(hostinfo, *this, isConfigFile);
+ addThread(thread);
+ thread->start();
+ return true;
+}
+
+bool CViewManager::addListener(network::SocketListener* sock) {
+ socks.push_back(sock);
+ WSAAsyncSelect(sock->getFd(), getHandle(), WM_USER, FD_ACCEPT);
+ return true;
+}
+
+bool CViewManager::addDefaultTCPListener(int port) {
+ return addListener(new network::TcpListener(port));
+}
+
+
+LRESULT CViewManager::processMessage(UINT msg, WPARAM wParam, LPARAM lParam) {
+ switch (msg) {
+ case WM_USER:
+ std::list<network::SocketListener*>::iterator i;
+ for (i=socks.begin(); i!=socks.end(); i++) {
+ if (wParam == (*i)->getFd()) {
+ network::Socket* new_sock = (*i)->accept();
+ CharArray connname;
+ connname.buf = new_sock->getPeerEndpoint();
+ vlog.debug("accepted connection: %s", connname);
+ CViewThread* thread = new CViewThread(new_sock, *this);
+ addThread(thread);
+ thread->start();
+ break;
+ }
+ }
+ break;
+ }
+ return MsgWindow::processMessage(msg, wParam, lParam);
+}
diff --git a/vncviewer/CViewManager.h b/vncviewer/CViewManager.h
new file mode 100644
index 00000000..3d11dd96
--- /dev/null
+++ b/vncviewer/CViewManager.h
@@ -0,0 +1,64 @@
+/* Copyright (C) 2002-2003 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.
+ */
+
+// -=- CViewManager.h
+
+// Creates and manages threads to run CView instances.
+
+#ifndef __RFB_WIN32_CVIEW_MANAGER_H__
+#define __RFB_WIN32_CVIEW_MANAGER_H__
+
+#include <list>
+#include <network/Socket.h>
+#include <rfb/Threading.h>
+#include <rfb_win32/MsgWindow.h>
+
+namespace rfb {
+
+ namespace win32 {
+
+ class CViewManager : public MsgWindow {
+ public:
+ CViewManager();
+ ~CViewManager();
+
+ void awaitEmpty();
+
+ void addThread(Thread* t);
+ void remThread(Thread* t);
+
+ bool addClient(const char* hostinfo, bool isConfigFile=false);
+
+ bool addListener(network::SocketListener* sock);
+ bool addDefaultTCPListener(int port);
+
+ LRESULT processMessage(UINT msg, WPARAM wParam, LPARAM lParam);
+
+ protected:
+ std::list<network::SocketListener*> socks;
+ std::list<Thread*> threads;
+ Mutex threadsMutex;
+ Condition threadsSig;
+ Thread* mainThread;
+ };
+
+ };
+
+};
+
+#endif
diff --git a/vncviewer/CViewOptions.cxx b/vncviewer/CViewOptions.cxx
new file mode 100644
index 00000000..089c4c43
--- /dev/null
+++ b/vncviewer/CViewOptions.cxx
@@ -0,0 +1,364 @@
+/* Copyright (C) 2002-2004 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 <vncviewer/CViewOptions.h>
+#include <rfb/Configuration.h>
+#include <rfb/encodings.h>
+#include <rfb/vncAuth.h>
+#include <rfb/LogWriter.h>
+#include <rfb_win32/Win32Util.h>
+#include <rfb_win32/Registry.h>
+#include <rdr/HexInStream.h>
+#include <rdr/HexOutStream.h>
+#include <stdlib.h>
+
+using namespace rfb;
+using namespace rfb::win32;
+
+
+static BoolParameter useLocalCursor("UseLocalCursor", "Render the mouse cursor locally", true);
+static BoolParameter useDesktopResize("UseDesktopResize", "Support dynamic desktop resizing", true);
+
+static BoolParameter fullColour("FullColour",
+ "Use full colour (default is to use low colour "
+ "unless auto select decides the link is fast enough)",
+ false);
+static IntParameter lowColourLevel("LowColourLevel",
+ "Colour level to use on slow connections. "
+ "0 = Very Low (8 colours), 1 = Low (64 colours), 2 = Medium (256 colours)",
+ 1);
+static BoolParameter fullScreen("FullScreen",
+ "Use the whole display to show the remote desktop."
+ "(Press F8 to access the viewer menu)",
+ false);
+static StringParameter preferredEncoding("PreferredEncoding",
+ "Preferred graphical encoding to use - overridden by AutoSelect if set. "
+ "(ZRLE, Hextile or Raw)", "ZRLE");
+
+static BoolParameter autoSelect("AutoSelect", "Auto select pixel format and encoding", true);
+static BoolParameter sharedConnection("Shared",
+ "Allow existing connections to the server to continue."
+ "(Default is to disconnect all other clients)",
+ false);
+
+static BoolParameter sendPtrEvents("SendPointerEvents",
+ "Send pointer (mouse) events to the server.", true);
+static BoolParameter sendKeyEvents("SendKeyEvents",
+ "Send key presses (and releases) to the server.", true);
+
+static BoolParameter clientCutText("ClientCutText",
+ "Send clipboard changes to the server.", true);
+static BoolParameter serverCutText("ServerCutText",
+ "Accept clipboard changes from the server.", true);
+
+static BoolParameter protocol3_3("Protocol3.3",
+ "Only use protocol version 3.3", false);
+
+static IntParameter ptrEventInterval("PointerEventInterval",
+ "The interval to delay between sending one pointer event "
+ "and the next.", 0);
+static BoolParameter emulate3("Emulate3",
+ "Emulate middle mouse button when left and right buttons "
+ "are used simulatenously.", false);
+
+static BoolParameter acceptBell("AcceptBell",
+ "Produce a system beep when requested to by the server.",
+ true);
+
+static StringParameter monitor("Monitor", "The monitor to open the VNC Viewer window on, if available.", "");
+static StringParameter menuKey("MenuKey", "The key which brings up the popup menu", "F8");
+
+
+CViewOptions::CViewOptions()
+: useLocalCursor(::useLocalCursor), useDesktopResize(::useDesktopResize),
+autoSelect(::autoSelect), fullColour(::fullColour), fullScreen(::fullScreen),
+shared(::sharedConnection), sendPtrEvents(::sendPtrEvents), sendKeyEvents(::sendKeyEvents),
+preferredEncoding(encodingZRLE), clientCutText(::clientCutText), serverCutText(::serverCutText),
+protocol3_3(::protocol3_3), acceptBell(::acceptBell), lowColourLevel(::lowColourLevel),
+pointerEventInterval(ptrEventInterval), emulate3(::emulate3), monitor(::monitor.getData())
+{
+ CharArray encodingName(::preferredEncoding.getData());
+ preferredEncoding = encodingNum(encodingName.buf);
+ setMenuKey(CharArray(::menuKey.getData()).buf);
+}
+
+
+void CViewOptions::readFromFile(const char* filename) {
+ FILE* f = fopen(filename, "r");
+ if (!f)
+ throw rdr::Exception("Failed to read configuration file");
+
+ try {
+ char line[4096];
+ CharArray section;
+
+ CharArray hostTmp;
+ int portTmp = 0;
+
+ while (!feof(f)) {
+ // Read the next line
+ if (!fgets(line, sizeof(line), f)) {
+ if (feof(f))
+ break;
+ throw rdr::SystemException("fgets", ferror(f));
+ }
+ int len=strlen(line);
+ if (line[len-1] == '\n') {
+ line[len-1] = 0;
+ len--;
+ }
+
+ // Process the line
+ if (line[0] == ';') {
+ // Comment
+ } else if (line[0] == '[') {
+ // Entering a new section
+ if (!strSplit(&line[1], ']', &section.buf, 0))
+ throw rdr::Exception("bad Section");
+ } else {
+ // Reading an option
+ CharArray name;
+ CharArray value;
+ if (!strSplit(line, '=', &name.buf, &value.buf))
+ throw rdr::Exception("bad Name/Value pair");
+
+ if (stricmp(section.buf, "Connection") == 0) {
+ if (stricmp(name.buf, "Host") == 0) {
+ hostTmp.replaceBuf(value.takeBuf());
+ } else if (stricmp(name.buf, "Port") == 0) {
+ portTmp = atoi(value.buf);
+ } else if (stricmp(name.buf, "UserName") == 0) {
+ userName.replaceBuf(value.takeBuf());
+ } else if (stricmp(name.buf, "Password") == 0) {
+ int len = 0;
+ CharArray obfuscated;
+ rdr::HexInStream::hexStrToBin(value.buf, &obfuscated.buf, &len);
+ if (len == 8) {
+ password.replaceBuf(new char[9]);
+ memcpy(password.buf, obfuscated.buf, 8);
+ vncAuthUnobfuscatePasswd(password.buf);
+ password.buf[8] = 0;
+ }
+ }
+ } else if (stricmp(section.buf, "Options") == 0) {
+ // V4 options
+ if (stricmp(name.buf, "UseLocalCursor") == 0) {
+ useLocalCursor = atoi(value.buf);
+ } else if (stricmp(name.buf, "UseDesktopResize") == 0) {
+ useDesktopResize = atoi(value.buf);
+ } else if (stricmp(name.buf, "FullScreen") == 0) {
+ fullScreen = atoi(value.buf);
+ } else if (stricmp(name.buf, "FullColour") == 0) {
+ fullColour = atoi(value.buf);
+ } else if (stricmp(name.buf, "LowColourLevel") == 0) {
+ lowColourLevel = atoi(value.buf);
+ } else if (stricmp(name.buf, "PreferredEncoding") == 0) {
+ preferredEncoding = encodingNum(value.buf);
+ } else if ((stricmp(name.buf, "AutoDetect") == 0) ||
+ (stricmp(name.buf, "AutoSelect") == 0)) {
+ autoSelect = atoi(value.buf);
+ } else if (stricmp(name.buf, "Shared") == 0) {
+ shared = atoi(value.buf);
+ } else if (stricmp(name.buf, "SendPtrEvents") == 0) {
+ sendPtrEvents = atoi(value.buf);
+ } else if (stricmp(name.buf, "SendKeyEvents") == 0) {
+ sendKeyEvents = atoi(value.buf);
+ } else if (stricmp(name.buf, "SendCutText") == 0) {
+ clientCutText = atoi(value.buf);
+ } else if (stricmp(name.buf, "AcceptCutText") == 0) {
+ serverCutText = atoi(value.buf);
+ } else if (stricmp(name.buf, "Emulate3") == 0) {
+ emulate3 = atoi(value.buf);
+ } else if (stricmp(name.buf, "PointerEventInterval") == 0) {
+ pointerEventInterval = atoi(value.buf);
+ } else if (stricmp(name.buf, "Monitor") == 0) {
+ monitor.replaceBuf(value.takeBuf());
+ } else if (stricmp(name.buf, "MenuKey") == 0) {
+ setMenuKey(value.buf);
+
+ // Legacy options
+ } else if (stricmp(name.buf, "Preferred_Encoding") == 0) {
+ preferredEncoding = atoi(value.buf);
+ } else if (stricmp(name.buf, "8bit") == 0) {
+ fullColour = !atoi(value.buf);
+ } else if (stricmp(name.buf, "FullScreen") == 0) {
+ fullScreen = atoi(value.buf);
+ } else if (stricmp(name.buf, "ViewOnly") == 0) {
+ sendPtrEvents = sendKeyEvents = !atoi(value.buf);
+ } else if (stricmp(name.buf, "DisableClipboard") == 0) {
+ clientCutText = serverCutText = !atoi(value.buf);
+ }
+ }
+ }
+ }
+ fclose(f); f=0;
+
+ // Process the Host and Port
+ if (hostTmp.buf) {
+ int hostLen = strlen(hostTmp.buf) + 2 + 17;
+ host.replaceBuf(new char[hostLen]);
+ strCopy(host.buf, hostTmp.buf, hostLen);
+ if (portTmp) {
+ strncat(host.buf, "::", hostLen-1);
+ char tmp[16];
+ sprintf(tmp, "%d", portTmp);
+ strncat(host.buf, tmp, hostLen-1);
+ }
+ }
+
+ setConfigFileName(filename);
+ } catch (rdr::Exception&) {
+ if (f) fclose(f);
+ throw;
+ }
+}
+
+void CViewOptions::writeToFile(const char* filename) {
+ FILE* f = fopen(filename, "w");
+ if (!f)
+ throw rdr::Exception("Failed to write configuration file");
+
+ try {
+ // - Split server into host and port and save
+ fprintf(f, "[Connection]\n");
+
+ fprintf(f, "Host=%s\n", host.buf);
+ if (userName.buf)
+ fprintf(f, "UserName=%s\n", userName.buf);
+ if (password.buf) {
+ // - Warn the user before saving the password
+ if (MsgBox(0, _T("Do you want to include the VNC Password in this configuration file?\n")
+ _T("Storing the password is more convenient but poses a security risk."),
+ MB_YESNO | MB_DEFBUTTON2 | MB_ICONWARNING) == IDYES) {
+ char obfuscated[9];
+ memset(obfuscated, 0, sizeof(obfuscated));
+ strCopy(obfuscated, password.buf, sizeof(obfuscated));
+ vncAuthObfuscatePasswd(obfuscated);
+ CharArray obfuscatedHex = rdr::HexOutStream::binToHexStr(obfuscated, 8);
+ fprintf(f, "Password=%s\n", obfuscatedHex.buf);
+ }
+ }
+
+ // - Save the other options
+ fprintf(f, "[Options]\n");
+
+ fprintf(f, "UseLocalCursor=%d\n", (int)useLocalCursor);
+ fprintf(f, "UseDesktopResize=%d\n", (int)useDesktopResize);
+ fprintf(f, "FullScreen=%d\n", (int)fullScreen);
+ fprintf(f, "FullColour=%d\n", (int)fullColour);
+ fprintf(f, "LowColourLevel=%d\n", lowColourLevel);
+ fprintf(f, "PreferredEncoding=%s\n", encodingName(preferredEncoding));
+ fprintf(f, "AutoSelect=%d\n", (int)autoSelect);
+ fprintf(f, "Shared=%d\n", (int)shared);
+ fprintf(f, "SendPtrEvents=%d\n", (int)sendPtrEvents);
+ fprintf(f, "SendKeyEvents=%d\n", (int)sendKeyEvents);
+ fprintf(f, "SendCutText=%d\n", (int)clientCutText);
+ fprintf(f, "AcceptCutText=%d\n", (int)serverCutText);
+ fprintf(f, "Emulate3=%d\n", (int)emulate3);
+ fprintf(f, "PointerEventInterval=%d\n", pointerEventInterval);
+ if (monitor.buf)
+ fprintf(f, "Monitor=%s\n", monitor.buf);
+ fprintf(f, "MenuKey=%s\n", CharArray(menuKeyName()).buf);
+ fclose(f); f=0;
+
+ setConfigFileName(filename);
+ } catch (rdr::Exception&) {
+ if (f) fclose(f);
+ throw;
+ }
+}
+
+
+void CViewOptions::writeDefaults() {
+ RegKey key;
+ key.createKey(HKEY_CURRENT_USER, _T("Software\\RealVNC\\VNCviewer4"));
+ key.setBool(_T("UseLocalCursor"), useLocalCursor);
+ key.setBool(_T("UseDesktopResize"), useDesktopResize);
+ key.setBool(_T("FullScreen"), fullScreen);
+ key.setBool(_T("FullColour"), fullColour);
+ key.setInt(_T("LowColourLevel"), lowColourLevel);
+ key.setString(_T("PreferredEncoding"), TStr(encodingName(preferredEncoding)));
+ key.setBool(_T("AutoSelect"), autoSelect);
+ key.setBool(_T("Shared"), shared);
+ key.setBool(_T("SendPointerEvents"), sendPtrEvents);
+ key.setBool(_T("SendKeyEvents"), sendKeyEvents);
+ key.setBool(_T("ClientCutText"), clientCutText);
+ key.setBool(_T("ServerCutText"), serverCutText);
+ key.setBool(_T("Protocol3.3"), protocol3_3);
+ key.setBool(_T("AcceptBell"), acceptBell);
+ key.setBool(_T("Emulate3"), emulate3);
+ key.setInt(_T("PointerEventInterval"), pointerEventInterval);
+ if (monitor.buf)
+ key.setString(_T("Monitor"), TStr(monitor.buf));
+ key.setString(_T("MenuKey"), TCharArray(menuKeyName()).buf);
+}
+
+
+void CViewOptions::setUserName(const char* user) {userName.replaceBuf(strDup(user));}
+void CViewOptions::setPassword(const char* pwd) {password.replaceBuf(strDup(pwd));}
+void CViewOptions::setConfigFileName(const char* cfn) {configFileName.replaceBuf(strDup(cfn));}
+void CViewOptions::setHost(const char* h) {host.replaceBuf(strDup(h));}
+void CViewOptions::setMonitor(const char* m) {monitor.replaceBuf(strDup(m));}
+
+void CViewOptions::setMenuKey(const char* keyName) {
+ if (!keyName[0]) {
+ menuKey = 0;
+ } else {
+ menuKey = VK_F8;
+ if (keyName[0] == 'F') {
+ UINT fKey = atoi(&keyName[1]);
+ if (fKey >= 1 && fKey <= 12)
+ menuKey = fKey-1 + VK_F1;
+ }
+ }
+}
+char* CViewOptions::menuKeyName() {
+ int fNum = (menuKey-VK_F1)+1;
+ if (fNum<1 || fNum>12)
+ return strDup("");
+ CharArray menuKeyStr(4);
+ sprintf(menuKeyStr.buf, "F%d", fNum);
+ return menuKeyStr.takeBuf();
+}
+
+
+CViewOptions& CViewOptions::operator=(const CViewOptions& o) {
+ useLocalCursor = o.useLocalCursor;
+ useDesktopResize = o.useDesktopResize;
+ fullScreen = o.fullScreen;
+ fullColour = o.fullColour;
+ lowColourLevel = o.lowColourLevel;
+ preferredEncoding = o.preferredEncoding;
+ autoSelect = o.autoSelect;
+ shared = o.shared;
+ sendPtrEvents = o.sendPtrEvents;
+ sendKeyEvents = o.sendKeyEvents;
+ clientCutText = o.clientCutText;
+ serverCutText = o.serverCutText;
+ emulate3 = o.emulate3;
+ pointerEventInterval = o.pointerEventInterval;
+ protocol3_3 = o.protocol3_3;
+ acceptBell = o.acceptBell;
+ setUserName(o.userName.buf);
+ setPassword(o.password.buf);
+ setConfigFileName(o.configFileName.buf);
+ setHost(o.host.buf);
+ setMonitor(o.monitor.buf);
+ menuKey = o.menuKey;
+ return *this;
+} \ No newline at end of file
diff --git a/vncviewer/CViewOptions.h b/vncviewer/CViewOptions.h
new file mode 100644
index 00000000..9120bde2
--- /dev/null
+++ b/vncviewer/CViewOptions.h
@@ -0,0 +1,87 @@
+/* Copyright (C) 2002-2004 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.
+ */
+
+// -=- CViewOptions.h
+
+// Definition of the CViewOptions class, responsible for storing the
+// current & requested VNCviewer options.
+
+#ifndef __RFB_WIN32_CVIEW_OPTIONS_H__
+#define __RFB_WIN32_CVIEW_OPTIONS_H__
+
+#include <rfb/util.h>
+
+namespace rfb {
+
+ namespace win32 {
+
+ //
+ // -=- Options structure. Each viewer option has a corresponding
+ // entry in CViewOptions. The viewer options are set by calling
+ // CView::applyOptions(...)
+ // The CViewOptions structure automatically picks up the default
+ // value of each option from the Configuration system
+ // The readFromFile and writeFromFile methods can be used to load
+ // and save VNC configuration files. readFromFile is backwards
+ // compatible with 3.3 releases, while writeToFile is not.
+
+ class CViewOptions {
+ public:
+ CViewOptions();
+ CViewOptions(const CViewOptions& o) {operator=(o);}
+ CViewOptions& operator=(const CViewOptions& o);
+ void readFromFile(const char* filename_);
+ void writeToFile(const char* filename_);
+ void writeDefaults();
+ bool useLocalCursor;
+ bool useDesktopResize;
+ bool fullScreen;
+ bool fullColour;
+ int lowColourLevel;
+ int preferredEncoding;
+ bool autoSelect;
+ bool shared;
+ bool sendPtrEvents;
+ bool sendKeyEvents;
+ bool clientCutText;
+ bool serverCutText;
+ bool emulate3;
+ int pointerEventInterval;
+ bool protocol3_3;
+ bool acceptBell;
+ CharArray userName;
+ void setUserName(const char* user);
+ CharArray password;
+ void setPassword(const char* pwd);
+ CharArray configFileName;
+ void setConfigFileName(const char* cfn);
+ CharArray host;
+ void setHost(const char* h);
+ CharArray monitor;
+ void setMonitor(const char* m);
+ unsigned int menuKey;
+ void setMenuKey(const char* keyName);
+ char* menuKeyName();
+ };
+
+
+ };
+
+};
+
+#endif
diff --git a/vncviewer/ConnectingDialog.h b/vncviewer/ConnectingDialog.h
new file mode 100644
index 00000000..b146ced6
--- /dev/null
+++ b/vncviewer/ConnectingDialog.h
@@ -0,0 +1,95 @@
+/* Copyright (C) 2002-2003 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.
+ */
+
+// -=- ConnectingDialog.h
+
+// Dialog to indicate to the user that the viewer is attempting to make an
+// outgoing connection.
+
+#ifndef __RFB_WIN32_CONNECTING_DLG_H__
+#define __RFB_WIN32_CONNECTING_DLG_H__
+
+#include <rfb_win32/Dialog.h>
+#include <rfb/Threading.h>
+#include <vncviewer/resource.h>
+
+namespace rfb {
+
+ namespace win32 {
+
+ BOOL CALLBACK ConnectingDlgProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam ) {
+ switch (uMsg) {
+ case WM_INITDIALOG:
+ {
+ SetWindowLong(hwnd, GWL_USERDATA, lParam);
+ return TRUE;
+ }
+ case WM_COMMAND:
+ switch (LOWORD(wParam)) {
+ case IDCANCEL:
+ network::Socket* sock = (network::Socket*) GetWindowLong(hwnd, GWL_USERDATA);
+ sock->shutdown();
+ EndDialog(hwnd, FALSE);
+ return TRUE;
+ }
+ break;
+ case WM_DESTROY:
+ EndDialog(hwnd, TRUE);
+ return TRUE;
+ }
+ return 0;
+ }
+
+ // *** hacky bit - should use async connect so dialog behaves properly
+ class ConnectingDialog : public Thread {
+ public:
+ ConnectingDialog() : Thread("ConnectingDialog") {
+ dialog = 0;
+ active = true;
+ start();
+ }
+ virtual ~ConnectingDialog() {
+ // *** join() required here because otherwise ~Thread calls Thread::join()
+ join();
+ }
+ virtual void run() {
+ dialog = CreateDialogParam(GetModuleHandle(0),
+ MAKEINTRESOURCE(IDD_CONNECTING_DLG), 0, &ConnectingDlgProc, 0);
+ ShowWindow(dialog, SW_SHOW);
+ MSG msg;
+ while (active && GetMessage(&msg, dialog, 0, 0)) {
+ DispatchMessage(&msg);
+ }
+ DestroyWindow(dialog);
+ }
+ virtual Thread* join() {
+ active = false;
+ if (dialog)
+ PostMessage(dialog, WM_QUIT, 0, 0);
+ return Thread::join();
+ }
+ protected:
+ HWND dialog;
+ bool active;
+ };
+
+ };
+
+};
+
+#endif \ No newline at end of file
diff --git a/vncviewer/ConnectionDialog.cxx b/vncviewer/ConnectionDialog.cxx
new file mode 100644
index 00000000..c083444c
--- /dev/null
+++ b/vncviewer/ConnectionDialog.cxx
@@ -0,0 +1,70 @@
+/* Copyright (C) 2002-2003 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 <vncviewer/ConnectionDialog.h>
+#include <vncviewer/CView.h>
+#include <vncviewer/resource.h>
+
+#include <tchar.h>
+
+using namespace rfb;
+using namespace rfb::win32;
+
+
+ConnectionDialog::ConnectionDialog(CView* view_) : Dialog(GetModuleHandle(0)), view(view_) {
+}
+
+
+bool ConnectionDialog::showDialog() {
+ return Dialog::showDialog(MAKEINTRESOURCE(IDD_CONNECTION_DLG));
+}
+
+void ConnectionDialog::initDialog() {
+ HWND box = GetDlgItem(handle, IDC_SERVER_EDIT);
+
+ std::list<char*> mru = MRU::getEntries();
+ std::list<char*>::iterator i;
+
+ // Locate the combo-box
+ // NB: TCharArray converts the supplied char* and assumes ownership!
+ for (i=mru.begin(); i!=mru.end(); i++) {
+ int index = SendMessage(box, CB_ADDSTRING, 0, (LPARAM)TCharArray(*i).buf);
+ }
+
+ // Select the first item in the list
+ SendMessage(box, CB_SETCURSEL, 0, 0);
+}
+
+
+bool ConnectionDialog::onOk() {
+ delete [] hostname.buf;
+ hostname.buf = 0;
+ hostname.buf = getItemString(IDC_SERVER_EDIT);
+ return hostname.buf[0] != 0;
+}
+
+bool ConnectionDialog::onCommand(int id, int cmd) {
+ switch (id) {
+ case IDC_ABOUT:
+ AboutDialog::instance.showDialog();
+ return true;
+ case IDC_OPTIONS:
+ view->optionsDialog.showDialog(view);
+ return true;
+ };
+ return false;
+}
diff --git a/vncviewer/ConnectionDialog.h b/vncviewer/ConnectionDialog.h
new file mode 100644
index 00000000..554c86fb
--- /dev/null
+++ b/vncviewer/ConnectionDialog.h
@@ -0,0 +1,52 @@
+/* Copyright (C) 2002-2003 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.
+ */
+
+// -=- ConnectionDialog.h
+
+// Connection dialog for VNC Viewer 4.0
+
+#ifndef __RFB_WIN32_CONN_DIALOG_H__
+#define __RFB_WIN32_CONN_DIALOG_H__
+
+#include <rfb_win32/Dialog.h>
+#include <vncviewer/MRU.h>
+#include <rfb/util.h>
+
+namespace rfb {
+
+ namespace win32 {
+
+ class CView;
+
+ class ConnectionDialog : Dialog {
+ public:
+ ConnectionDialog(CView* view);
+ virtual bool showDialog();
+ virtual void initDialog();
+ virtual bool onOk();
+ virtual bool onCommand(int id, int cmd);
+ TCharArray hostname;
+ protected:
+ CView* view;
+ };
+
+ };
+
+};
+
+#endif
diff --git a/vncviewer/InfoDialog.cxx b/vncviewer/InfoDialog.cxx
new file mode 100644
index 00000000..0d2313a5
--- /dev/null
+++ b/vncviewer/InfoDialog.cxx
@@ -0,0 +1,65 @@
+/* Copyright (C) 2002-2004 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 <vncviewer/InfoDialog.h>
+#include <vncviewer/resource.h>
+#include <vncviewer/CView.h>
+#include <rfb/secTypes.h>
+#include <rfb/encodings.h>
+#include <rfb/CSecurity.h>
+#include <rfb/LogWriter.h>
+
+using namespace rfb;
+using namespace rfb::win32;
+
+static LogWriter vlog("Info");
+
+
+bool InfoDialog::showDialog(CView* vw) {
+ view = vw;
+ return Dialog::showDialog(MAKEINTRESOURCE(IDD_CONNECTION_INFO));
+}
+
+void InfoDialog::initDialog() {
+ char buf[256];
+
+ setItemString(IDC_INFO_NAME, TStr(view->cp.name()));
+
+ setItemString(IDC_INFO_HOST, TCharArray(view->sock->getPeerAddress()).buf);
+
+ Rect bufRect = view->buffer->getRect();
+ sprintf(buf, "%dx%d", bufRect.width(), bufRect.height());
+ setItemString(IDC_INFO_SIZE, TStr(buf));
+
+ view->cp.pf().print(buf, 256);
+ setItemString(IDC_INFO_PF, TStr(buf));
+
+ view->serverDefaultPF.print(buf, 256);
+ setItemString(IDC_INFO_DEF_PF, TStr(buf));
+
+ setItemString(IDC_REQUESTED_ENCODING, TStr(encodingName(view->getOptions().preferredEncoding)));
+ setItemString(IDC_LAST_ENCODING, TStr(encodingName(view->lastUsedEncoding())));
+
+ sprintf(buf, "%d kbits/s", view->sock->inStream().kbitsPerSecond());
+ setItemString(IDC_INFO_LINESPEED, TStr(buf));
+
+ sprintf(buf, "%d.%d", view->cp.majorVersion, view->cp.minorVersion);
+ setItemString(IDC_INFO_VERSION, TStr(buf));
+
+ int secType = view->getCurrentCSecurity()->getType();
+ setItemString(IDC_INFO_SECURITY, TStr(secTypeName(secType)));
+}
diff --git a/vncviewer/InfoDialog.h b/vncviewer/InfoDialog.h
new file mode 100644
index 00000000..7a64d383
--- /dev/null
+++ b/vncviewer/InfoDialog.h
@@ -0,0 +1,48 @@
+/* Copyright (C) 2002-2003 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.
+ */
+
+// -=- InfoDialog.h
+
+// Info dialog for VNC Viewer 4.0
+
+#ifndef __RFB_WIN32_INFO_DIALOG_H__
+#define __RFB_WIN32_INFO_DIALOG_H__
+
+#include <rfb_win32/Dialog.h>
+#include <rfb/util.h>
+
+namespace rfb {
+
+ namespace win32 {
+
+ class CView;
+
+ class InfoDialog : Dialog {
+ public:
+ InfoDialog() : Dialog(GetModuleHandle(0)), view(0) {}
+ virtual bool showDialog(CView* vw);
+ virtual void initDialog();
+ protected:
+ CView* view;
+ };
+
+ };
+
+};
+
+#endif
diff --git a/vncviewer/MRU.h b/vncviewer/MRU.h
new file mode 100644
index 00000000..9e993956
--- /dev/null
+++ b/vncviewer/MRU.h
@@ -0,0 +1,140 @@
+/* Copyright (C) 2002-2003 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 __VIEWER_MRU_H__
+#define __VIEWER_MRU_H__
+
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+#include <list>
+#include <set>
+
+#include <rfb_win32/Registry.h>
+
+#include <rfb/util.h>
+#include <rdr/HexOutStream.h>
+
+namespace rfb {
+
+ namespace win32 {
+
+ namespace MRU {
+
+ static const RegKey RegRoot = HKEY_CURRENT_USER;
+ static const TCHAR* RegPath = _T("Software\\RealVNC\\VNCViewer4\\MRU");
+ static const int MaxMRUEntries = 256;
+ static const int MRUEntries = 10;
+
+ static std::list<char*> getEntries() {
+ std::list<char*> mru;
+
+ try {
+ RegKey key;
+ key.openKey(RegRoot, RegPath);
+
+ CharArray order;
+ int length;
+ key.getBinary(_T("Order"), (void**)&order.buf, &length);
+
+ for (int i=0; i<length; i++) {
+ TCharArray keyname = rdr::HexOutStream::binToHexStr(&order.buf[i], 1);
+ try {
+ TCharArray entry = key.getString(keyname.buf);
+ mru.push_back(strDup(entry.buf));
+ } catch (rdr::Exception) {
+ }
+ }
+ } catch (rdr::Exception) {
+ }
+
+ return mru;
+ }
+
+ static void addToMRU(const char* name) {
+ RegKey key;
+ key.createKey(RegRoot, RegPath);
+
+ BYTE keycode;
+ CharArray old_order;
+ char order[MaxMRUEntries];
+ int orderlen;
+
+ try {
+ key.getBinary(_T("Order"), (void**)&old_order.buf, &orderlen);
+ if (orderlen)
+ memcpy(order, old_order.buf, orderlen);
+
+ std::set<int> ordercodes;
+ keycode = 0;
+ bool found = false;
+ for (int i=0; i<orderlen; i++) {
+ TCharArray keyname = rdr::HexOutStream::binToHexStr(&order[i], 1);
+ try {
+ TCharArray hostname = key.getString(keyname.buf);
+ if (strcmp(name, CStr(hostname.buf)) == 0) {
+ keycode = order[i];
+ found = true;
+ break;
+ }
+ } catch (rdr::Exception) {
+ }
+ ordercodes.insert(order[i]);
+ }
+
+ if (!found) {
+ if (orderlen <= MRUEntries) {
+ while (ordercodes.find(keycode) != ordercodes.end()) keycode++;
+ } else {
+ keycode = order[orderlen-1];
+ orderlen--;
+ }
+ }
+
+ } catch (rdr::Exception) {
+ keycode = 0;
+ orderlen = 0;
+ }
+
+ printf("keycode=%d\n", (int)keycode);
+
+ orderlen++;
+ int i, j=orderlen-1;
+ for (i=0; i<orderlen-1; i++) {
+ if (order[i] == keycode) {
+ j = i;
+ orderlen--;
+ break;
+ }
+ }
+ for (i=j; i>0; i--)
+ order[i] = order[i-1];
+ order[0] = keycode;
+
+ printf("selected %d\n", (int)keycode);
+
+ TCharArray keyname = rdr::HexOutStream::binToHexStr((char*)&keycode, 1);
+ key.setString(keyname.buf, TStr(name));
+ key.setBinary(_T("Order"), order, orderlen);
+ }
+
+ };
+
+ };
+
+};
+
+#endif \ No newline at end of file
diff --git a/vncviewer/OptionsDialog.cxx b/vncviewer/OptionsDialog.cxx
new file mode 100644
index 00000000..ab45f8ce
--- /dev/null
+++ b/vncviewer/OptionsDialog.cxx
@@ -0,0 +1,309 @@
+/* Copyright (C) 2002-2004 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.
+ */
+
+#define WIN32_LEAN_AND_MEAN
+#if (_WIN32_WINNT < 0x0400)
+#define _WIN32_WINNT 0x0400
+#endif
+#include <windows.h>
+#include <commdlg.h>
+
+#include <vncviewer/OptionsDialog.h>
+#include <vncviewer/CView.h>
+#include <vncviewer/resource.h>
+#include <rfb_win32/Registry.h>
+#include <rfb/LogWriter.h>
+#include <rfb/encodings.h>
+#include <rfb/CConnection.h>
+
+using namespace rfb;
+using namespace rfb::win32;
+
+static LogWriter vlog("Options");
+
+
+struct OptionsInfo {
+ CView* view;
+ CViewOptions options;
+};
+
+
+OptionsDialog rfb::win32::OptionsDialog::global;
+
+
+class VNCviewerOptions : public PropSheet {
+public:
+ VNCviewerOptions(OptionsInfo& info_, std::list<PropSheetPage*> pages)
+ : PropSheet(GetModuleHandle(0),
+ info_.view ? _T("VNC Viewer Options") : _T("VNC Viewer Defaults"), pages),
+ info(info_), changed(false) {
+ }
+ ~VNCviewerOptions() {
+ if (changed) {
+ if (info.view)
+ // Apply the settings to the supplied session object
+ info.view->applyOptions(info.options);
+ else {
+ // Commit the settings to the user's registry area
+ info.options.writeDefaults();
+ }
+ }
+ }
+
+ void setChanged() {changed = true;}
+
+ bool changed;
+ OptionsInfo& info;
+};
+
+
+class FormatPage : public PropSheetPage {
+public:
+ FormatPage(OptionsInfo* dlg_)
+ : PropSheetPage(GetModuleHandle(0), MAKEINTRESOURCE(IDD_FORMAT)), dlg(dlg_) {
+ }
+ virtual void initDialog() {
+ setItemChecked(IDC_ENCODING_AUTO, dlg->options.autoSelect);
+ setItemChecked(IDC_FORMAT_FULLCOLOUR, dlg->options.fullColour);
+ if (!dlg->options.fullColour) {
+ switch (dlg->options.lowColourLevel) {
+ case 0: setItemChecked(IDC_FORMAT_VERYLOWCOLOUR, true); break;
+ case 1: setItemChecked(IDC_FORMAT_LOWCOLOUR, true); break;
+ case 2: setItemChecked(IDC_FORMAT_MEDIUMCOLOUR, true); break;
+ }
+ }
+ switch (dlg->options.preferredEncoding) {
+ case encodingZRLE: setItemChecked(IDC_ENCODING_ZRLE, true); break;
+ case encodingHextile: setItemChecked(IDC_ENCODING_HEXTILE, true); break;
+ case encodingRaw: setItemChecked(IDC_ENCODING_RAW, true); break;
+ }
+ onCommand(IDC_ENCODING_AUTO, 0 /* ? */); // Force enableItem status to refresh
+ }
+ virtual bool onOk() {
+ dlg->options.autoSelect = isItemChecked(IDC_ENCODING_AUTO);
+ dlg->options.fullColour = isItemChecked(IDC_FORMAT_FULLCOLOUR);
+ if (isItemChecked(IDC_FORMAT_VERYLOWCOLOUR))
+ dlg->options.lowColourLevel = 0;
+ if (isItemChecked(IDC_FORMAT_LOWCOLOUR))
+ dlg->options.lowColourLevel = 1;
+ if (isItemChecked(IDC_FORMAT_MEDIUMCOLOUR))
+ dlg->options.lowColourLevel = 2;
+ dlg->options.preferredEncoding = encodingZRLE;
+ if (isItemChecked(IDC_ENCODING_HEXTILE))
+ dlg->options.preferredEncoding = encodingHextile;
+ if (isItemChecked(IDC_ENCODING_RAW))
+ dlg->options.preferredEncoding = encodingRaw;
+ ((VNCviewerOptions*)propSheet)->setChanged();
+ return true;
+ }
+ virtual bool onCommand(int id, int cmd) {
+ if (id == IDC_ENCODING_AUTO) {
+ bool ok = !isItemChecked(IDC_ENCODING_AUTO);
+ enableItem(IDC_ENCODING_ZRLE, ok);
+ enableItem(IDC_ENCODING_HEXTILE, ok);
+ enableItem(IDC_ENCODING_RAW, ok);
+ return true;
+ }
+ return false;
+ }
+protected:
+ OptionsInfo* dlg;
+};
+
+class MiscPage : public PropSheetPage {
+public:
+ MiscPage(OptionsInfo* dlg_)
+ : PropSheetPage(GetModuleHandle(0), MAKEINTRESOURCE(IDD_MISC)), dlg(dlg_) {
+ }
+ virtual void initDialog() {
+ setItemChecked(IDC_CONN_SHARED, dlg->options.shared);
+ enableItem(IDC_CONN_SHARED, (!dlg->view) || (dlg->view->state() != CConnection::RFBSTATE_NORMAL));
+ setItemChecked(IDC_FULL_SCREEN, dlg->options.fullScreen);
+ setItemChecked(IDC_LOCAL_CURSOR, dlg->options.useLocalCursor);
+ setItemChecked(IDC_DESKTOP_RESIZE, dlg->options.useDesktopResize);
+ enableItem(IDC_PROTOCOL_3_3, (!dlg->view) || (dlg->view->state() != CConnection::RFBSTATE_NORMAL));
+ setItemChecked(IDC_PROTOCOL_3_3, dlg->options.protocol3_3);
+ setItemChecked(IDC_ACCEPT_BELL, dlg->options.acceptBell);
+ }
+ virtual bool onOk() {
+ dlg->options.shared = isItemChecked(IDC_CONN_SHARED);
+ dlg->options.fullScreen = isItemChecked(IDC_FULL_SCREEN);
+ dlg->options.useLocalCursor = isItemChecked(IDC_LOCAL_CURSOR);
+ dlg->options.useDesktopResize = isItemChecked(IDC_DESKTOP_RESIZE);
+ dlg->options.protocol3_3 = isItemChecked(IDC_PROTOCOL_3_3);
+ dlg->options.acceptBell = isItemChecked(IDC_ACCEPT_BELL);
+ ((VNCviewerOptions*)propSheet)->setChanged();
+ return true;
+ }
+protected:
+ OptionsInfo* dlg;
+};
+
+class InputsPage : public PropSheetPage {
+public:
+ InputsPage(OptionsInfo* dlg_)
+ : PropSheetPage(GetModuleHandle(0), MAKEINTRESOURCE(IDD_INPUTS)), dlg(dlg_) {
+ }
+ virtual void initDialog() {
+ setItemChecked(IDC_SEND_POINTER, dlg->options.sendPtrEvents);
+ setItemChecked(IDC_SEND_KEYS, dlg->options.sendKeyEvents);
+ setItemChecked(IDC_CLIENT_CUTTEXT, dlg->options.clientCutText);
+ setItemChecked(IDC_SERVER_CUTTEXT, dlg->options.serverCutText);
+ setItemChecked(IDC_EMULATE3, dlg->options.emulate3);
+ setItemChecked(IDC_POINTER_INTERVAL, dlg->options.pointerEventInterval != 0);
+
+ // Populate the Menu Key tab
+ HWND menuKey = GetDlgItem(handle, IDC_MENU_KEY);
+ SendMessage(menuKey, CB_RESETCONTENT, 0, 0);
+ SendMessage(menuKey, CB_ADDSTRING, 0, (LPARAM)_T("none"));
+ if (!dlg->options.menuKey)
+ SendMessage(menuKey, CB_SETCURSEL, 0, 0);
+ for (int i=0; i<12; i++) {
+ TCHAR buf[4];
+ _stprintf(buf, _T("F%d"), i+1);
+ int index = SendMessage(menuKey, CB_ADDSTRING, 0, (LPARAM)buf);
+ if (i == (dlg->options.menuKey - VK_F1))
+ SendMessage(menuKey, CB_SETCURSEL, index, 0);
+ }
+ }
+ virtual bool onOk() {
+ dlg->options.sendPtrEvents = isItemChecked(IDC_SEND_POINTER);
+ dlg->options.sendKeyEvents = isItemChecked(IDC_SEND_KEYS);
+ dlg->options.clientCutText = isItemChecked(IDC_CLIENT_CUTTEXT);
+ dlg->options.serverCutText = isItemChecked(IDC_SERVER_CUTTEXT);
+ dlg->options.emulate3 = isItemChecked(IDC_EMULATE3);
+ dlg->options.pointerEventInterval =
+ isItemChecked(IDC_POINTER_INTERVAL) ? 200 : 0;
+
+ HWND mkHwnd = GetDlgItem(handle, IDC_MENU_KEY);
+ int index = SendMessage(mkHwnd, CB_GETCURSEL, 0, 0);
+ TCharArray keyName(SendMessage(mkHwnd, CB_GETLBTEXTLEN, index, 0)+1);
+ SendMessage(mkHwnd, CB_GETLBTEXT, index, (LPARAM)keyName.buf);
+ if (_tcscmp(keyName.buf, _T("none")) == 0)
+ dlg->options.setMenuKey("");
+ else
+ dlg->options.setMenuKey(CStr(keyName.buf));
+
+ ((VNCviewerOptions*)propSheet)->setChanged();
+ return true;
+ }
+protected:
+ OptionsInfo* dlg;
+};
+
+
+class DefaultsPage : public PropSheetPage {
+public:
+ DefaultsPage(OptionsInfo* dlg_)
+ : PropSheetPage(GetModuleHandle(0), MAKEINTRESOURCE(IDD_DEFAULTS)), dlg(dlg_) {
+ }
+ virtual void initDialog() {
+ enableItem(IDC_LOAD_CONFIG, dlg->options.configFileName.buf);
+ enableItem(IDC_SAVE_CONFIG, dlg->options.configFileName.buf);
+ }
+ virtual bool onCommand(int id, int cmd) {
+ HWND hwnd = dlg->view ? dlg->view->getHandle() : 0;
+ switch (id) {
+ case IDC_LOAD_DEFAULTS:
+ dlg->options = CViewOptions();
+ break;
+ case IDC_SAVE_DEFAULTS:
+ propSheet->commitPages();
+ dlg->options.writeDefaults();
+ break;
+ case IDC_LOAD_CONFIG:
+ dlg->options.readFromFile(dlg->options.configFileName.buf);
+ break;
+ case IDC_SAVE_CONFIG:
+ propSheet->commitPages();
+ dlg->options.writeToFile(dlg->options.configFileName.buf);
+ MsgBox(hwnd, _T("Options saved successfully"),
+ MB_OK | MB_ICONINFORMATION);
+ return 0;
+ case IDC_SAVE_CONFIG_AS:
+ propSheet->commitPages();
+ // Get a filename to save to
+ TCHAR newFilename[4096];
+ TCHAR currentDir[4096];
+ if (dlg->options.configFileName.buf)
+ _tcscpy(newFilename, TStr(dlg->options.configFileName.buf));
+ else
+ newFilename[0] = 0;
+ OPENFILENAME ofn;
+ memset(&ofn, 0, sizeof(ofn));
+#ifdef OPENFILENAME_SIZE_VERSION_400
+ ofn.lStructSize = OPENFILENAME_SIZE_VERSION_400;
+#else
+ ofn.lStructSize = sizeof(ofn);
+#endif
+ ofn.hwndOwner = hwnd;
+ ofn.lpstrFilter = _T("VNC Connection Options\000*.vnc\000");
+ ofn.lpstrFile = newFilename;
+ currentDir[0] = 0;
+ GetCurrentDirectory(4096, currentDir);
+ ofn.lpstrInitialDir = currentDir;
+ ofn.nMaxFile = 4096;
+ ofn.lpstrDefExt = _T(".vnc");
+ ofn.Flags = OFN_NOREADONLYRETURN | OFN_OVERWRITEPROMPT | OFN_PATHMUSTEXIST;
+ if (!GetSaveFileName(&ofn)) {
+ if (CommDlgExtendedError())
+ throw rdr::Exception("GetSaveFileName failed");
+ return 0;
+ }
+
+ // Save the Options
+ dlg->options.writeToFile(CStr(newFilename));
+ MsgBox(hwnd, _T("Options saved successfully"),
+ MB_OK | MB_ICONINFORMATION);
+ return 0;
+ };
+ propSheet->reInitPages();
+ return true;
+ }
+protected:
+ OptionsInfo* dlg;
+};
+
+
+OptionsDialog::OptionsDialog() : visible(false) {
+}
+
+bool OptionsDialog::showDialog(CView* view, bool capture) {
+ if (visible) return false;
+ visible = true;
+
+ // Grab the current properties
+ OptionsInfo info;
+ if (view)
+ info.options = view->getOptions();
+ info.view = view;
+
+ // Build a list of pages to display
+ std::list<PropSheetPage*> pages;
+ FormatPage formatPage(&info); pages.push_back(&formatPage);
+ InputsPage inputsPage(&info); pages.push_back(&inputsPage);
+ MiscPage miscPage(&info); pages.push_back(&miscPage);
+ DefaultsPage defPage(&info); if (view) pages.push_back(&defPage);
+
+ // Show the property sheet
+ VNCviewerOptions dialog(info, pages);
+ dialog.showPropSheet(view ? view->getHandle() : 0, false, false, capture);
+
+ visible = false;
+ return dialog.changed;
+}
diff --git a/vncviewer/OptionsDialog.h b/vncviewer/OptionsDialog.h
new file mode 100644
index 00000000..eec9b96a
--- /dev/null
+++ b/vncviewer/OptionsDialog.h
@@ -0,0 +1,49 @@
+/* Copyright (C) 2002-2003 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.
+ */
+
+// -=- OptionsDialog.h
+
+// Options dialog for VNC Viewer 4.0
+
+#ifndef __RFB_WIN32_OPTIONS_DIALOG_H__
+#define __RFB_WIN32_OPTIONS_DIALOG_H__
+
+#include <vncviewer/CViewOptions.h>
+#include <rfb_win32/Dialog.h>
+
+namespace rfb {
+
+ namespace win32 {
+
+ class CView;
+
+ class OptionsDialog {
+ public:
+ OptionsDialog();
+ virtual bool showDialog(CView* cfg, bool capture=false);
+
+ static OptionsDialog global;
+ protected:
+ bool visible;
+ };
+
+ };
+
+};
+
+#endif
diff --git a/vncviewer/UserPasswdDialog.cxx b/vncviewer/UserPasswdDialog.cxx
new file mode 100644
index 00000000..8ab4ba4c
--- /dev/null
+++ b/vncviewer/UserPasswdDialog.cxx
@@ -0,0 +1,84 @@
+/* Copyright (C) 2002-2004 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 <vncviewer/UserPasswdDialog.h>
+#include <vncviewer/resource.h>
+
+using namespace rfb;
+using namespace rfb::win32;
+
+
+UserPasswdDialog::UserPasswdDialog() : Dialog(GetModuleHandle(0)), showUsername(false) {
+}
+
+
+void UserPasswdDialog::setCSecurity(const CSecurity* cs) {
+ description.replaceBuf(tstrDup(cs->description()));
+}
+
+bool UserPasswdDialog::showDialog() {
+ return Dialog::showDialog(MAKEINTRESOURCE(IDD_VNC_AUTH_DLG));
+}
+
+void UserPasswdDialog::initDialog() {
+ if (username.buf) {
+ setItemString(IDC_USERNAME, username.buf);
+ tstrFree(username.takeBuf());
+ }
+ if (password.buf) {
+ setItemString(IDC_PASSWORD, password.buf);
+ tstrFree(password.takeBuf());
+ }
+ if (!showUsername) {
+ setItemString(IDC_USERNAME, _T(""));
+ enableItem(IDC_USERNAME, false);
+ }
+ if (description.buf) {
+ TCharArray title(128);
+ GetWindowText(handle, title.buf, 128);
+ _tcsncat(title.buf, _T(" ["), 128);
+ _tcsncat(title.buf, description.buf, 128);
+ _tcsncat(title.buf, _T("]"), 128);
+ SetWindowText(handle, title.buf);
+ }
+}
+
+bool UserPasswdDialog::onOk() {
+ username.buf = getItemString(IDC_USERNAME);
+ password.buf = getItemString(IDC_PASSWORD);
+ return true;
+}
+
+
+bool UserPasswdDialog::getUserPasswd(char** user, char** passwd) {
+ bool result = false;
+ showUsername = user != 0;
+ if (user && *user)
+ username.buf = tstrDup(*user);
+ if (passwd && *passwd)
+ password.buf = tstrDup(*passwd);
+ if (showDialog()) {
+ if (user)
+ *user = strDup(username.buf);
+ *passwd = strDup(password.buf);
+ result = true;
+ }
+ tstrFree(username.takeBuf());
+ tstrFree(password.takeBuf());
+ return result;
+}
diff --git a/vncviewer/UserPasswdDialog.h b/vncviewer/UserPasswdDialog.h
new file mode 100644
index 00000000..998a49f1
--- /dev/null
+++ b/vncviewer/UserPasswdDialog.h
@@ -0,0 +1,54 @@
+/* Copyright (C) 2002-2004 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.
+ */
+
+// -=- UserPasswdDialog.h
+
+// Username and password dialog for VNC Viewer 4.0
+
+#ifndef __RFB_WIN32_USERPASSWD_DIALOG_H__
+#define __RFB_WIN32_USERPASSWD_DIALOG_H__
+
+#include <rfb_win32/Dialog.h>
+#include <rfb_win32/TCharArray.h>
+#include <rfb/CSecurity.h>
+#include <rfb/UserPasswdGetter.h>
+
+namespace rfb {
+
+ namespace win32 {
+
+ class UserPasswdDialog : Dialog, public UserPasswdGetter {
+ public:
+ UserPasswdDialog();
+ virtual bool showDialog();
+ virtual void initDialog();
+ virtual bool onOk();
+ virtual bool getUserPasswd(char** user, char** passwd);
+ void setCSecurity(const CSecurity* cs);
+ protected:
+ TCharArray username;
+ TCharArray password;
+ bool showUsername;
+ TCharArray description;
+ };
+
+ };
+
+};
+
+#endif
diff --git a/vncviewer/buildTime.cxx b/vncviewer/buildTime.cxx
new file mode 100644
index 00000000..bab2e137
--- /dev/null
+++ b/vncviewer/buildTime.cxx
@@ -0,0 +1 @@
+const char* buildTime = "Built on " __DATE__ " at " __TIME__; \ No newline at end of file
diff --git a/vncviewer/cursor1.cur b/vncviewer/cursor1.cur
new file mode 100644
index 00000000..20a713f7
--- /dev/null
+++ b/vncviewer/cursor1.cur
Binary files differ
diff --git a/vncviewer/cview.cxx b/vncviewer/cview.cxx
new file mode 100644
index 00000000..7d5653df
--- /dev/null
+++ b/vncviewer/cview.cxx
@@ -0,0 +1,1468 @@
+/* Copyright (C) 2002-2004 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.
+ */
+#define WIN32_LEAN_AND_MEAN
+#if (_WIN32_WINNT < 0x0400)
+#define _WIN32_WINNT 0x0400
+#endif
+#include <windows.h>
+#include <winsock2.h>
+#include <tchar.h>
+#include <commctrl.h>
+
+#include <network/TcpSocket.h>
+
+#include <vncviewer/CView.h>
+#include <vncviewer/UserPasswdDialog.h>
+#include <vncviewer/resource.h>
+
+#include <rfb/encodings.h>
+#include <rfb/secTypes.h>
+#include <rfb/CSecurityNone.h>
+#include <rfb/CSecurityVncAuth.h>
+#include <rfb/CMsgWriter.h>
+#include <rfb/Configuration.h>
+#include <rfb/LogWriter.h>
+
+#include <rfb_win32/WMShatter.h>
+
+using namespace rfb;
+using namespace rfb::win32;
+using namespace rdr;
+
+// - Statics & consts
+
+static LogWriter vlog("CView");
+
+const int IDM_FULLSCREEN = 1;
+const int IDM_SEND_MENU_KEY = 2;
+const int IDM_SEND_CAD = 3;
+const int IDM_ABOUT = 4;
+const int IDM_OPTIONS = 5;
+const int IDM_INFO = 6;
+const int IDM_NEWCONN = 7;
+const int IDM_REQUEST_REFRESH = 9;
+const int IDM_CTRL_KEY = 10;
+const int IDM_ALT_KEY = 11;
+
+const int TIMER_BUMPSCROLL = 1;
+const int TIMER_POINTER_INTERVAL = 2;
+const int TIMER_POINTER_3BUTTON = 3;
+
+
+IntParameter debugDelay("DebugDelay","Milliseconds to display inverted "
+ "pixel data - a debugging feature", 0);
+
+
+//
+// -=- CViewClass
+
+//
+// Window class used as the basis for all CView instances
+//
+
+class CViewClass {
+public:
+ CViewClass();
+ ~CViewClass();
+ ATOM classAtom;
+ HINSTANCE instance;
+};
+
+LRESULT CALLBACK CViewProc(HWND wnd, UINT msg, WPARAM wParam, LPARAM lParam) {
+ LRESULT result;
+
+ // *** vlog.debug("CViewMsg %x->(%x, %x, %x)", wnd, msg, wParam, lParam);
+
+ if (msg == WM_CREATE)
+ SetWindowLong(wnd, GWL_USERDATA, (long)((CREATESTRUCT*)lParam)->lpCreateParams);
+ else if (msg == WM_DESTROY)
+ SetWindowLong(wnd, GWL_USERDATA, 0);
+ CView* _this = (CView*) GetWindowLong(wnd, GWL_USERDATA);
+ if (!_this) {
+ vlog.info("null _this in %x, message %u", wnd, msg);
+ return rfb::win32::SafeDefWindowProc(wnd, msg, wParam, lParam);
+ }
+
+ try {
+ result = _this->processMessage(msg, wParam, lParam);
+ } catch (rdr::Exception& e) {
+ vlog.error("untrapped: %s", e.str());
+ }
+
+ return result;
+};
+
+HCURSOR dotCursor = (HCURSOR)LoadImage(GetModuleHandle(0), MAKEINTRESOURCE(IDC_DOT_CURSOR), IMAGE_CURSOR, 0, 0, LR_SHARED);
+HCURSOR arrowCursor = (HCURSOR)LoadImage(NULL, IDC_ARROW, IMAGE_CURSOR, 0, 0, LR_SHARED);
+
+CViewClass::CViewClass() : classAtom(0) {
+ WNDCLASS wndClass;
+ wndClass.style = 0;
+ wndClass.lpfnWndProc = CViewProc;
+ 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 = NULL;
+ wndClass.hbrBackground = NULL;
+ wndClass.lpszMenuName = 0;
+ wndClass.lpszClassName = _T("rfb::win32::CViewClass");
+ classAtom = RegisterClass(&wndClass);
+ if (!classAtom) {
+ throw rdr::SystemException("unable to register CView window class", GetLastError());
+ }
+}
+
+CViewClass::~CViewClass() {
+ if (classAtom) {
+ UnregisterClass((const TCHAR*)classAtom, instance);
+ }
+}
+
+CViewClass baseClass;
+
+
+//
+// -=- CView instance implementation
+//
+
+RegKey CView::userConfigKey;
+
+
+CView::CView()
+ : quit_on_destroy(false), buffer(0), sock(0), readyToRead(false),
+ client_size(0, 0, 16, 16), window_size(0, 0, 32, 32),
+ cursorVisible(false), cursorAvailable(false), cursorInBuffer(false),
+ systemCursorVisible(true), trackingMouseLeave(false),
+ hwnd(0), requestUpdate(false), has_focus(false), palette_changed(false),
+ sameMachine(false), encodingChange(false), formatChange(false),
+ lastUsedEncoding_(encodingRaw), fullScreenActive(false),
+ bumpScroll(false), manager(0) {
+
+ // Create the window
+ const TCHAR* name = _T("VNC Viewer 4.0b");
+ hwnd = CreateWindow((const TCHAR*)baseClass.classAtom, name, WS_OVERLAPPEDWINDOW,
+ 0, 0, 10, 10, 0, 0, baseClass.instance, this);
+ if (!hwnd) {
+ throw rdr::SystemException("unable to create WMNotifier window instance", GetLastError());
+ }
+ vlog.debug("created window \"%s\" (%x)", (const char*)CStr(name), hwnd);
+
+ // Initialise the CPointer pointer handler
+ ptr.setHWND(getHandle());
+ ptr.setIntervalTimerId(TIMER_POINTER_INTERVAL);
+ ptr.set3ButtonTimerId(TIMER_POINTER_3BUTTON);
+
+ // Initialise the bumpscroll timer
+ bumpScrollTimer.setHWND(getHandle());
+ bumpScrollTimer.setId(TIMER_BUMPSCROLL);
+
+ // Hook the clipboard
+ clipboard.setNotifier(this);
+
+ // Create the backing buffer
+ buffer = new win32::DIBSectionBuffer(getHandle());
+}
+
+CView::~CView() {
+ vlog.debug("~CView");
+ showSystemCursor();
+ if (hwnd) {
+ setVisible(false);
+ DestroyWindow(hwnd);
+ hwnd = 0;
+ }
+ delete buffer;
+ vlog.debug("~CView done");
+}
+
+bool CView::initialise(network::Socket* s) {
+ // Update the window menu
+ HMENU wndmenu = GetSystemMenu(hwnd, FALSE);
+ AppendMenu(wndmenu, MF_SEPARATOR, 0, 0);
+ AppendMenu(wndmenu, MF_STRING, IDM_FULLSCREEN, _T("&Full screen"));
+ AppendMenu(wndmenu, MF_SEPARATOR, 0, 0);
+ AppendMenu(wndmenu, MF_STRING, IDM_CTRL_KEY, _T("Ctr&l"));
+ AppendMenu(wndmenu, MF_STRING, IDM_ALT_KEY, _T("Al&t"));
+ AppendMenu(wndmenu, MF_STRING, IDM_SEND_CAD, _T("Send Ctrl-Alt-&Del"));
+ AppendMenu(wndmenu, MF_STRING, IDM_REQUEST_REFRESH, _T("Refres&h Screen"));
+ AppendMenu(wndmenu, MF_SEPARATOR, 0, 0);
+ if (manager) AppendMenu(wndmenu, MF_STRING, IDM_NEWCONN, _T("Ne&w Connection..."));
+ AppendMenu(wndmenu, MF_STRING, IDM_OPTIONS, _T("&Options..."));
+ AppendMenu(wndmenu, MF_STRING, IDM_INFO, _T("Connection &Info..."));
+ AppendMenu(wndmenu, MF_STRING, IDM_ABOUT, _T("&About..."));
+
+ // Set the server's name for MRU purposes
+ CharArray endpoint(s->getPeerEndpoint());
+ setServerName(endpoint.buf);
+ if (!options.host.buf)
+ options.setHost(endpoint.buf);
+
+ // Initialise the underlying CConnection
+ setStreams(&s->inStream(), &s->outStream());
+
+ // Enable processing of window messages while blocked on I/O
+ s->inStream().setBlockCallback(this);
+
+ // Initialise the viewer options
+ applyOptions(options);
+
+ // - Set which auth schemes we support
+ addSecType(secTypeNone);
+ addSecType(secTypeVncAuth);
+
+ initialiseProtocol();
+ WSAAsyncSelect(s->getFd(), getHandle(), WM_USER, FD_READ | FD_CLOSE);
+ sock = s;
+
+ return true;
+}
+
+
+void
+CView::applyOptions(CViewOptions& opt) {
+ // *** CHANGE THIS TO USE CViewOptions::operator= ***
+
+ // - Take the username, password, config filename, and host spec
+ options.setUserName(opt.userName.buf);
+ options.setPassword(opt.password.buf);
+ options.setHost(opt.host.buf);
+ options.setConfigFileName(opt.configFileName.buf);
+ options.setMonitor(opt.monitor.buf);
+
+ // - Set optional features in ConnParams
+ encodingChange |= ((options.useLocalCursor != opt.useLocalCursor) ||
+ (options.useDesktopResize != opt.useDesktopResize));
+ cp.supportsLocalCursor = options.useLocalCursor = opt.useLocalCursor;
+ cp.supportsDesktopResize = options.useDesktopResize = opt.useDesktopResize;
+ if (cursorAvailable)
+ hideLocalCursor();
+ cursorAvailable = cursorAvailable && options.useLocalCursor;
+
+ // - Switch full-screen mode on/off
+ options.fullScreen = opt.fullScreen;
+ setFullscreen(options.fullScreen);
+
+ // - Handle format/encoding options
+ encodingChange |= (options.preferredEncoding != opt.preferredEncoding);
+ options.preferredEncoding = opt.preferredEncoding;
+
+ formatChange |= (options.fullColour != opt.fullColour);
+ options.fullColour = opt.fullColour;
+
+ if (!options.fullColour)
+ formatChange |= (options.lowColourLevel != opt.lowColourLevel);
+ options.lowColourLevel = opt.lowColourLevel;
+
+ options.autoSelect = opt.autoSelect;
+
+ // - Sharing
+ options.shared = opt.shared;
+ setShared(options.shared);
+
+ // - Inputs
+ options.sendPtrEvents = opt.sendPtrEvents;
+ options.sendKeyEvents = opt.sendKeyEvents;
+ options.clientCutText = opt.clientCutText;
+ options.serverCutText = opt.serverCutText;
+ options.emulate3 = opt.emulate3;
+ ptr.enableEmulate3(opt.emulate3);
+ options.pointerEventInterval = opt.pointerEventInterval;
+ ptr.enableInterval(opt.pointerEventInterval);
+ options.menuKey = opt.menuKey;
+
+ // - Protocol version override
+ options.protocol3_3 = opt.protocol3_3;
+ setProtocol3_3(options.protocol3_3);
+
+ // - Bell
+ options.acceptBell = opt.acceptBell;
+}
+
+void
+CView::setFullscreen(bool fs) {
+ // Set the menu fullscreen option tick
+ CheckMenuItem(GetSystemMenu(getHandle(), FALSE), IDM_FULLSCREEN,
+ (options.fullScreen ? MF_CHECKED : 0) | MF_BYCOMMAND);
+
+ // If the window is not visible then we ignore the request.
+ // setVisible() will call us to correct the full-screen state when
+ // the window is visible, to keep things consistent.
+ if (!IsWindowVisible(getHandle()))
+ return;
+
+ if (fs && !fullScreenActive) {
+ fullScreenActive = bumpScroll = true;
+
+ // Un-minimize the window if required
+ if (GetWindowLong(getHandle(), GWL_STYLE) & WS_MINIMIZE)
+ ShowWindow(getHandle(), SW_RESTORE);
+
+ // Save the non-fullscreen window position
+ RECT wrect;
+ GetWindowRect(getHandle(), &wrect);
+ fullScreenOldRect = Rect(wrect.left, wrect.top, wrect.right, wrect.bottom);
+
+ // Find the size of the display the window is on
+ MonitorInfo mi(getHandle());
+
+ // Set the window full-screen
+ DWORD flags = GetWindowLong(getHandle(), GWL_STYLE);
+ fullScreenOldFlags = flags;
+ flags = flags & ~(WS_CAPTION | WS_THICKFRAME | WS_MAXIMIZE | WS_MINIMIZE);
+ vlog.debug("flags=%x", flags);
+
+ SetWindowLong(getHandle(), GWL_STYLE, flags);
+ SetWindowPos(getHandle(), HWND_TOP, mi.rcMonitor.left, mi.rcMonitor.top,
+ mi.rcMonitor.right-mi.rcMonitor.left,
+ mi.rcMonitor.bottom-mi.rcMonitor.top,
+ SWP_FRAMECHANGED);
+ } else if (!fs && fullScreenActive) {
+ fullScreenActive = bumpScroll = false;
+
+ // Set the window non-fullscreen
+ SetWindowLong(getHandle(), GWL_STYLE, fullScreenOldFlags);
+ SetWindowPos(getHandle(), HWND_NOTOPMOST,
+ fullScreenOldRect.tl.x, fullScreenOldRect.tl.y,
+ fullScreenOldRect.width(), fullScreenOldRect.height(),
+ SWP_FRAMECHANGED);
+ }
+
+ // Adjust the viewport offset to cope with change in size between FS
+ // and previous window state.
+ setViewportOffset(scrolloffset);
+}
+
+
+bool CView::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(getHandle(), -delta.x, -delta.y, 0, 0, 0, 0, SW_INVALIDATE);
+ UpdateWindow(getHandle());
+ return true;
+ }
+ return false;
+}
+
+
+bool CView::processBumpScroll(const Point& pos)
+{
+ if (!bumpScroll) return false;
+ int bumpScrollPixels = 20;
+ bumpScrollDelta = Point();
+
+ if (pos.x == client_size.width()-1)
+ bumpScrollDelta.x = bumpScrollPixels;
+ else if (pos.x == 0)
+ bumpScrollDelta.x = -bumpScrollPixels;
+ if (pos.y == client_size.height()-1)
+ bumpScrollDelta.y = bumpScrollPixels;
+ else if (pos.y == 0)
+ bumpScrollDelta.y = -bumpScrollPixels;
+
+ if (bumpScrollDelta.x || bumpScrollDelta.y) {
+ if (bumpScrollTimer.isActive()) return true;
+ if (setViewportOffset(scrolloffset.translate(bumpScrollDelta))) {
+ bumpScrollTimer.start(25);
+ return true;
+ }
+ }
+
+ bumpScrollTimer.stop();
+ return false;
+}
+
+
+LRESULT
+CView::processMessage(UINT msg, WPARAM wParam, LPARAM lParam) {
+ switch (msg) {
+
+ // -=- Process standard window messages
+
+ case WM_DISPLAYCHANGE:
+ // Display has changed - use new pixel format
+ calculateFullColourPF();
+ break;
+
+ case WM_PAINT:
+ {
+ PAINTSTRUCT ps;
+ HDC paintDC = BeginPaint(getHandle(), &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()) {
+
+ // Draw using the correct palette
+ PaletteSelector pSel(paintDC, windowPalette.getHandle());
+
+ if (buffer->bitmap) {
+ // Update the bitmap's palette
+ if (palette_changed) {
+ palette_changed = false;
+ buffer->refreshPalette();
+ }
+
+ // 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(getHandle(), &ps);
+
+ // - Request the next update from the server, if required
+ requestNewUpdate();
+ }
+ return 0;
+
+ // -=- Palette management
+
+ case WM_PALETTECHANGED:
+ vlog.debug("WM_PALETTECHANGED");
+ if ((HWND)wParam == getHandle()) {
+ vlog.debug("ignoring");
+ break;
+ }
+ case WM_QUERYNEWPALETTE:
+ vlog.debug("re-selecting palette");
+ {
+ WindowDC wdc(getHandle());
+ PaletteSelector pSel(wdc, windowPalette.getHandle());
+ if (pSel.isRedrawRequired()) {
+ InvalidateRect(getHandle(), 0, FALSE);
+ UpdateWindow(getHandle());
+ }
+ }
+ return TRUE;
+
+ // -=- Window position
+
+ // Prevent the window from being resized to be too large if in normal mode.
+ // If maximized or fullscreen the allow oversized windows.
+
+ case WM_WINDOWPOSCHANGING:
+ {
+ WINDOWPOS* wpos = (WINDOWPOS*)lParam;
+ if (wpos->flags & SWP_NOSIZE)
+ break;
+
+ // Work out how big the window should ideally be
+ DWORD current_style = GetWindowLong(getHandle(), GWL_STYLE);
+ DWORD style = current_style & ~(WS_VSCROLL | WS_HSCROLL);
+ RECT r;
+ SetRect(&r, 0, 0, buffer->width(), buffer->height());
+ AdjustWindowRect(&r, style, FALSE);
+ Rect reqd_size = Rect(r.left, r.top, r.right, r.bottom);
+ if (current_style & WS_VSCROLL)
+ reqd_size.br.x += GetSystemMetrics(SM_CXVSCROLL);
+ if (current_style & WS_HSCROLL)
+ reqd_size.br.y += GetSystemMetrics(SM_CXHSCROLL);
+ RECT current;
+ GetWindowRect(getHandle(), &current);
+
+ // Ensure that the window isn't resized too large
+ // If the window is maximized or full-screen then any size is allowed
+ if (!(GetWindowLong(getHandle(), GWL_STYLE) & WS_MAXIMIZE) && !fullScreenActive) {
+ if (wpos->cx > reqd_size.width()) {
+ wpos->cx = reqd_size.width();
+ wpos->x = current.left;
+ }
+ if (wpos->cy > reqd_size.height()) {
+ wpos->cy = reqd_size.height();
+ wpos->y = current.top;
+ }
+ }
+
+ }
+ break;
+
+ // Add scrollbars if required and update window size info we have cached.
+
+ case WM_SIZE:
+ {
+ Point old_offset = bufferToClient(Point(0, 0));
+
+ // Update the cached sizing information
+ RECT r;
+ GetWindowRect(getHandle(), &r);
+ window_size = Rect(r.left, r.top, r.right, r.bottom);
+ GetClientRect(getHandle(), &r);
+ client_size = Rect(r.left, r.top, r.right, r.bottom);
+
+ // Determine whether scrollbars are required
+ calculateScrollBars();
+
+ // Redraw if required
+ if (!old_offset.equals(bufferToClient(Point(0, 0))))
+ InvalidateRect(getHandle(), 0, TRUE);
+ }
+ break;
+
+ 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(getHandle(), (msg == WM_VSCROLL) ? SB_VERT : SB_HORZ, &si, TRUE);
+ }
+ break;
+
+ // -=- Bump-scrolling
+
+ case WM_TIMER:
+ switch (wParam) {
+ case TIMER_BUMPSCROLL:
+ if (!setViewportOffset(scrolloffset.translate(bumpScrollDelta)))
+ bumpScrollTimer.stop();
+ break;
+ case TIMER_POINTER_INTERVAL:
+ case TIMER_POINTER_3BUTTON:
+ try {
+ ptr.handleTimer(writer(), wParam);
+ } catch (rdr::Exception& e) {
+ close(e.str());
+ }
+ break;
+ }
+ break;
+
+ // -=- Cursor shape/visibility handling
+
+ case WM_SETCURSOR:
+ if (LOWORD(lParam) != HTCLIENT)
+ break;
+ SetCursor(cursorInBuffer ? dotCursor : arrowCursor);
+ return TRUE;
+
+ case WM_MOUSELEAVE:
+ trackingMouseLeave = false;
+ cursorOutsideBuffer();
+ return 0;
+
+ // -=- Mouse input handling
+
+ case WM_MOUSEMOVE:
+ case WM_LBUTTONUP:
+ case WM_MBUTTONUP:
+ case WM_RBUTTONUP:
+ case WM_LBUTTONDOWN:
+ case WM_MBUTTONDOWN:
+ case WM_RBUTTONDOWN:
+ case WM_MOUSEWHEEL:
+ if (has_focus)
+ {
+ if (!trackingMouseLeave) {
+ TRACKMOUSEEVENT tme;
+ tme.cbSize = sizeof(TRACKMOUSEEVENT);
+ tme.dwFlags = TME_LEAVE;
+ tme.hwndTrack = hwnd;
+ _TrackMouseEvent(&tme);
+ trackingMouseLeave = true;
+ }
+ int mask = 0;
+ if (LOWORD(wParam) & MK_LBUTTON) mask |= 1;
+ if (LOWORD(wParam) & MK_MBUTTON) mask |= 2;
+ if (LOWORD(wParam) & MK_RBUTTON) mask |= 4;
+
+ if (msg == WM_MOUSEWHEEL) {
+ int delta = (short)HIWORD(wParam);
+ int repeats = (abs(delta)+119) / 120;
+ int wheelMask = (delta > 0) ? 8 : 16;
+ vlog.debug("repeats %d, mask %d\n",repeats,wheelMask);
+ for (int i=0; i<repeats; i++) {
+ writePointerEvent(oldpos.x, oldpos.y, mask | wheelMask);
+ writePointerEvent(oldpos.x, oldpos.y, mask);
+ }
+ } else {
+ Point clientPos = Point(LOWORD(lParam), HIWORD(lParam));
+ Point p = clientToBuffer(clientPos);
+
+ // If the mouse is not within the server buffer area, do nothing
+ cursorInBuffer = buffer->getRect().contains(p);
+ if (!cursorInBuffer) {
+ cursorOutsideBuffer();
+ break;
+ }
+
+ // If we're locally rendering the cursor then redraw it
+ if (cursorAvailable) {
+ // - Render the cursor!
+ if (!p.equals(cursorPos)) {
+ hideLocalCursor();
+ cursorPos = p;
+ showLocalCursor();
+ if (cursorVisible)
+ hideSystemCursor();
+ }
+ }
+
+ // If we are doing bump-scrolling then try that first...
+ if (processBumpScroll(clientPos))
+ break;
+
+ // Send a pointer event to the server
+ writePointerEvent(p.x, p.y, mask);
+ oldpos = p;
+ }
+ } else {
+ cursorOutsideBuffer();
+ }
+ break;
+
+ // -=- Track whether or not the window has focus
+
+ case WM_SETFOCUS:
+ has_focus = true;
+ break;
+ case WM_KILLFOCUS:
+ has_focus = false;
+ cursorOutsideBuffer();
+ // Restore the remote keys to consistent states
+ try {
+ kbd.releaseAllKeys(writer());
+ } catch (rdr::Exception& e) {
+ close(e.str());
+ }
+ break;
+
+ // -=- Handle the extra window menu items
+
+ // Process the items added to the system menu
+ case WM_SYSCOMMAND:
+
+ // - First check whether it's one of our messages
+ switch (wParam) {
+ case IDM_FULLSCREEN:
+ options.fullScreen = !options.fullScreen;
+ setFullscreen(options.fullScreen);
+ return 0;
+ case IDM_CTRL_KEY:
+ writeKeyEvent(VK_CONTROL, 0, !kbd.keyPressed(VK_CONTROL));
+ return 0;
+ case IDM_ALT_KEY:
+ writeKeyEvent(VK_MENU, 0, !kbd.keyPressed(VK_MENU));
+ return 0;
+ case IDM_SEND_MENU_KEY:
+ writeKeyEvent(options.menuKey, 0, true);
+ writeKeyEvent(options.menuKey, 0, false);
+ return 0;
+ case IDM_SEND_CAD:
+ writeKeyEvent(VK_CONTROL, 0, true);
+ writeKeyEvent(VK_MENU, 0, true);
+ writeKeyEvent(VK_DELETE, 0, true);
+ writeKeyEvent(VK_DELETE, 0, false);
+ writeKeyEvent(VK_MENU, 0, false);
+ writeKeyEvent(VK_CONTROL, 0, false);
+ return 0;
+ case IDM_REQUEST_REFRESH:
+ try {
+ writer()->writeFramebufferUpdateRequest(Rect(0,0,cp.width,cp.height), false);
+ requestUpdate = false;
+ } catch (rdr::Exception& e) {
+ close(e.str());
+ }
+ return 0;
+ case IDM_NEWCONN:
+ manager->addClient(0);
+ return 0;
+ case IDM_OPTIONS:
+ // Update the monitor device name in the CViewOptions instance
+ {
+ MonitorInfo mi(getHandle());
+ options.setMonitor(mi.szDevice);
+ optionsDialog.showDialog(this);
+ return 0;
+ }
+ case IDM_INFO:
+ infoDialog.showDialog(this);
+ return 0;
+ case IDM_ABOUT:
+ AboutDialog::instance.showDialog();
+ return 0;
+ };
+
+ // - Not one of our messages, so process it as a system message
+ switch (wParam & 0xfff0) {
+
+ // When restored, ensure that full-screen mode is re-enabled if required.
+ case SC_RESTORE:
+ rfb::win32::SafeDefWindowProc(getHandle(), msg, wParam, lParam);
+ setFullscreen(options.fullScreen);
+ return 0;
+
+ // If we are maximized or minimized then that cancels full-screen mode.
+ case SC_MINIMIZE:
+ case SC_MAXIMIZE:
+ setFullscreen(false);
+ break;
+
+ // If the system menu is shown then make sure it's up to date
+ case SC_KEYMENU:
+ case SC_MOUSEMENU:
+ updateF8Menu(false);
+ break;
+
+ };
+ break;
+
+ // Treat all menu commands as system menu commands
+ case WM_COMMAND:
+ SendMessage(getHandle(), WM_SYSCOMMAND, wParam, lParam);
+ return 0;
+
+ case WM_MENUCHAR:
+ vlog.debug("menuchar");
+ break;
+
+ // -=- Handle keyboard input
+
+ case WM_KEYUP:
+ case WM_KEYDOWN:
+ // Hook the MenuKey to pop-up the window menu
+ if (options.menuKey && (wParam == options.menuKey)) {
+
+ bool ctrlDown = (GetAsyncKeyState(VK_CONTROL) & 0x8000) != 0;
+ bool altDown = (GetAsyncKeyState(VK_MENU) & 0x8000) != 0;
+ bool shiftDown = (GetAsyncKeyState(VK_SHIFT) & 0x8000) != 0;
+ if (!(ctrlDown || altDown || shiftDown)) {
+
+ // If MenuKey is being released then pop-up the menu
+ if ((msg == WM_KEYDOWN)) {
+ // Make sure it's up to date
+ updateF8Menu(true);
+
+ // Show it under the pointer
+ POINT pt;
+ GetCursorPos(&pt);
+ cursorInBuffer = false;
+ TrackPopupMenu(GetSystemMenu(getHandle(), FALSE),
+ TPM_CENTERALIGN | TPM_VCENTERALIGN, pt.x, pt.y, 0, getHandle(), 0);
+ }
+
+ // Ignore the MenuKey keypress for both press & release events
+ return 0;
+ }
+ }
+ case WM_SYSKEYDOWN:
+ case WM_SYSKEYUP:
+ writeKeyEvent(wParam, lParam, (msg == WM_KEYDOWN) || (msg == WM_SYSKEYDOWN));
+ return 0;
+
+ // -=- Handle the window closing
+
+ case WM_CLOSE:
+ vlog.debug("WM_CLOSE %x", getHandle());
+ if (quit_on_destroy) {
+ vlog.debug("posting WM_QUIT");
+ PostQuitMessage(0);
+ } else {
+ vlog.debug("not posting WM_QUIT");
+ }
+ break;
+
+ // -=- Process incoming socket data
+
+ case WM_USER:
+ readyToRead = true;
+ break;
+
+ }
+
+ return rfb::win32::SafeDefWindowProc(getHandle(), msg, wParam, lParam);
+}
+
+void CView::blockCallback() {
+ // - An InStream has blocked on I/O while processing an RFB message
+ // We re-enable socket event notifications, so we'll know when more
+ // data is available, then we sit and dispatch window events until
+ // the notification arrives.
+ readyToRead = false;
+ WSAAsyncSelect(sock->getFd(), getHandle(), WM_USER, FD_READ | FD_CLOSE);
+ MSG msg;
+ while (true) {
+ if (readyToRead) {
+ // - Network event notification. Return control to I/O routine.
+ WSAAsyncSelect(sock->getFd(), getHandle(), WM_USER, 0);
+ return;
+ }
+
+ DWORD result = GetMessage(&msg, NULL, 0, 0);
+ if (result == 0) {
+ vlog.debug("WM_QUIT");
+ throw QuitMessage(msg.wParam);
+ } else if (result < 0) {
+ throw rdr::SystemException("GetMessage error", GetLastError());
+ }
+
+ // IMPORTANT: We mustn't call TranslateMessage() here, because instead we
+ // call ToAscii() in CKeyboard::keyEvent(). ToAscii() stores dead key
+ // state from one call to the next, which would be messed up by calls to
+ // TranslateMessage() (actually it looks like TranslateMessage() calls
+ // ToAscii() internally).
+ DispatchMessage(&msg);
+ }
+}
+
+
+void
+CView::hideLocalCursor() {
+ // - Blit the cursor backing store over the cursor
+ // *** ALWAYS call this BEFORE changing buffer PF!!!
+ if (cursorVisible) {
+ cursorVisible = false;
+ buffer->imageRect(cursorBackingRect, cursorBacking.data);
+ invalidateBufferRect(cursorBackingRect);
+ }
+}
+
+void
+CView::showLocalCursor() {
+ if (cursorAvailable && !cursorVisible && cursorInBuffer) {
+ if (!cp.pf().equal(cursor.getPF()) ||
+ cursor.getRect().is_empty()) {
+ vlog.info("attempting to render invalid local cursor");
+ cursorAvailable = false;
+ showSystemCursor();
+ return;
+ }
+ cursorVisible = true;
+
+ cursorBackingRect = cursor.getRect().translate(cursorPos).translate(cursor.hotspot.negate());
+ cursorBackingRect = cursorBackingRect.intersect(buffer->getRect());
+ buffer->getImage(cursorBacking.data, cursorBackingRect);
+
+ renderLocalCursor();
+
+ invalidateBufferRect(cursorBackingRect);
+ }
+}
+
+void CView::cursorOutsideBuffer()
+{
+ cursorInBuffer = false;
+ hideLocalCursor();
+ showSystemCursor();
+}
+
+void
+CView::renderLocalCursor()
+{
+ Rect r = cursor.getRect();
+ r = r.translate(cursorPos).translate(cursor.hotspot.negate());
+ buffer->maskRect(r, cursor.data, cursor.mask.buf);
+}
+
+void
+CView::hideSystemCursor() {
+ if (systemCursorVisible) {
+ vlog.debug("hide system cursor");
+ systemCursorVisible = false;
+ ShowCursor(FALSE);
+ }
+}
+
+void
+CView::showSystemCursor() {
+ if (!systemCursorVisible) {
+ vlog.debug("show system cursor");
+ systemCursorVisible = true;
+ ShowCursor(TRUE);
+ }
+}
+
+
+bool
+CView::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(getHandle(), &invalid, FALSE);
+ return true;
+}
+
+
+void
+CView::notifyClipboardChanged(const char* text, int len) {
+ if (!options.clientCutText) return;
+ if (state() != RFBSTATE_NORMAL) return;
+ try {
+ writer()->writeClientCutText(text, len);
+ } catch (rdr::Exception& e) {
+ close(e.str());
+ }
+}
+
+
+CSecurity* CView::getCSecurity(int secType)
+{
+ switch (secType) {
+ case secTypeNone:
+ return new CSecurityNone();
+ case secTypeVncAuth:
+ return new CSecurityVncAuth(this);
+ default:
+ throw Exception("Unsupported secType?");
+ }
+}
+
+
+void
+CView::setColourMapEntries(int first, int count, U16* rgbs) {
+ vlog.debug("setColourMapEntries: first=%d, count=%d", first, count);
+ int i;
+ for (i=0;i<count;i++) {
+ buffer->setColour(i+first, rgbs[i*3], rgbs[i*3+1], rgbs[i*3+2]);
+ }
+ // *** change to 0, 256?
+ refreshWindowPalette(first, count);
+ palette_changed = true;
+ InvalidateRect(getHandle(), 0, FALSE);
+}
+
+void
+CView::bell() {
+ if (options.acceptBell)
+ MessageBeep(-1);
+}
+
+
+void
+CView::setDesktopSize(int w, int h) {
+ vlog.debug("setDesktopSize %dx%d", w, h);
+
+ // If the locally-rendered cursor is visible then remove it
+ hideLocalCursor();
+
+ // Resize the backing buffer
+ buffer->setSize(w, h);
+
+ // If the window is not maximised or full-screen then resize it
+ if (!(GetWindowLong(getHandle(), GWL_STYLE) & WS_MAXIMIZE) && !fullScreenActive) {
+ // Resize the window to the required size
+ RECT r = {0, 0, w, h};
+ AdjustWindowRect(&r, GetWindowLong(getHandle(), GWL_STYLE), FALSE);
+ SetWindowPos(getHandle(), 0, 0, 0, r.right-r.left, r.bottom-r.top,
+ SWP_NOZORDER | SWP_NOMOVE | SWP_NOACTIVATE | SWP_NOOWNERZORDER);
+
+ // Move the window to the desired monitor
+ if (options.monitor.buf)
+ moveToMonitor(getHandle(), options.monitor.buf);
+
+ // Clip to the system work area
+ centerWindow(getHandle(), 0, true);
+ } else {
+ // Ensure the screen contents are consistent
+ InvalidateRect(getHandle(), 0, FALSE);
+ }
+
+ // Tell the underlying CConnection
+ CConnection::setDesktopSize(w, h);
+
+ // Enable/disable scrollbars as appropriate
+ calculateScrollBars();
+}
+
+void
+CView::setCursor(const Point& hotspot, const Point& size, void* data, void* mask) {
+ if (!options.useLocalCursor) return;
+ hideLocalCursor();
+
+ cursor.hotspot = hotspot;
+
+ cursor.setSize(size.x, size.y);
+ cursor.setPF(cp.pf());
+ cursor.imageRect(cursor.getRect(), data);
+ memcpy(cursor.mask.buf, mask, cursor.maskLen());
+ cursor.crop();
+
+ cursorBacking.setSize(size.x, size.y);
+ cursorBacking.setPF(cp.pf());
+
+ cursorAvailable = true;
+
+ showLocalCursor();
+}
+
+PixelFormat
+CView::getNativePF() const {
+ vlog.debug("getNativePF()");
+ return WindowDC(getHandle()).getPF();
+}
+
+void
+CView::setVisible(bool visible) {
+ ShowWindow(getHandle(), visible ? SW_SHOW : SW_HIDE);
+ if (visible) {
+ // When the window becomes visible, make it active
+ SetForegroundWindow(getHandle());
+ SetActiveWindow(getHandle());
+ // If the window should be full-screen, then do so
+ setFullscreen(options.fullScreen);
+ } else {
+ // Disable full-screen mode
+ setFullscreen(false);
+ }
+}
+
+void
+CView::close(const char* reason) {
+ setVisible(false);
+ if (reason) {
+ vlog.info("closing - %s", reason);
+ MsgBox(NULL, TStr(reason), MB_ICONINFORMATION | MB_OK);
+ }
+ SendMessage(getHandle(), WM_CLOSE, 0, 0);
+}
+
+
+void
+CView::framebufferUpdateEnd() {
+ if (debugDelay != 0) {
+ vlog.debug("debug delay %d",(int)debugDelay);
+ UpdateWindow(getHandle());
+ Sleep(debugDelay);
+ std::list<rfb::Rect>::iterator i;
+ for (i = debugRects.begin(); i != debugRects.end(); i++) {
+ invertRect(*i);
+ }
+ debugRects.clear();
+ }
+ if (options.autoSelect)
+ autoSelectFormatAndEncoding();
+
+ // Always request the next update
+ requestUpdate = true;
+
+ // Check that at least part of the window has changed
+ if (!GetUpdateRect(getHandle(), 0, FALSE)) {
+ if (!(GetWindowLong(getHandle(), GWL_STYLE) & WS_MINIMIZE))
+ requestNewUpdate();
+ }
+
+ showLocalCursor();
+}
+
+// autoSelectFormatAndEncoding() chooses the format and encoding appropriate
+// to the connection speed:
+// Above 16Mbps (timing for at least a second), same machine, switch to raw
+// Above 3Mbps, switch to hextile
+// Below 1.5Mbps, switch to ZRLE
+// Above 1Mbps, switch to full colour mode
+void
+CView::autoSelectFormatAndEncoding() {
+ int kbitsPerSecond = sock->inStream().kbitsPerSecond();
+ unsigned int newEncoding = options.preferredEncoding;
+
+ if (kbitsPerSecond > 16000 && sameMachine &&
+ sock->inStream().timeWaited() >= 10000) {
+ newEncoding = encodingRaw;
+ } else if (kbitsPerSecond > 3000) {
+ newEncoding = encodingHextile;
+ } else if (kbitsPerSecond < 1500) {
+ newEncoding = encodingZRLE;
+ }
+
+ if (newEncoding != options.preferredEncoding) {
+ vlog.info("Throughput %d kbit/s - changing to %s encoding",
+ kbitsPerSecond, encodingName(newEncoding));
+ options.preferredEncoding = newEncoding;
+ encodingChange = true;
+ }
+
+ if (kbitsPerSecond > 1000) {
+ if (!options.fullColour) {
+ vlog.info("Throughput %d kbit/s - changing to full colour",
+ kbitsPerSecond);
+ options.fullColour = true;
+ formatChange = true;
+ }
+ }
+}
+
+void
+CView::requestNewUpdate() {
+ if (!requestUpdate) return;
+
+ if (formatChange) {
+ // Hide the rendered cursor, if any, to prevent
+ // the backing buffer being used in the wrong format
+ hideLocalCursor();
+
+ // Select the required pixel format
+ if (options.fullColour) {
+ buffer->setPF(fullColourPF);
+ } else {
+ switch (options.lowColourLevel) {
+ case 0:
+ buffer->setPF(PixelFormat(8,3,0,1,1,1,1,2,1,0));
+ break;
+ case 1:
+ buffer->setPF(PixelFormat(8,6,0,1,3,3,3,4,2,0));
+ break;
+ case 2:
+ buffer->setPF(PixelFormat(8,8,0,0,0,0,0,0,0,0));
+ break;
+ }
+ }
+
+ // Print the current pixel format
+ char str[256];
+ buffer->getPF().print(str, 256);
+ vlog.info("Using pixel format %s",str);
+
+ // Save the connection pixel format and tell server to use it
+ cp.setPF(buffer->getPF());
+ writer()->writeSetPixelFormat(cp.pf());
+
+ // Correct the local window's palette
+ if (!getNativePF().trueColour)
+ refreshWindowPalette(0, 1 << cp.pf().depth);
+ }
+
+ if (encodingChange) {
+ vlog.info("Using %s encoding",encodingName(options.preferredEncoding));
+ writer()->writeSetEncodings(options.preferredEncoding, true);
+ }
+
+ writer()->writeFramebufferUpdateRequest(Rect(0, 0, cp.width, cp.height),
+ !formatChange);
+
+ encodingChange = formatChange = requestUpdate = false;
+}
+
+
+void
+CView::writeKeyEvent(rdr::U8 vkey, rdr::U32 flags, bool down) {
+ if (!options.sendKeyEvents) return;
+ try {
+ kbd.keyEvent(writer(), vkey, flags, down);
+ } catch (rdr::Exception& e) {
+ close(e.str());
+ }
+}
+
+void
+CView::writePointerEvent(int x, int y, int buttonMask) {
+ if (!options.sendPtrEvents) return;
+ try {
+ ptr.pointerEvent(writer(), x, y, buttonMask);
+ } catch (rdr::Exception& e) {
+ close(e.str());
+ }
+}
+
+
+void
+CView::refreshWindowPalette(int start, int count) {
+ vlog.debug("refreshWindowPalette(%d, %d)", start, count);
+
+ Colour colours[256];
+ if (count > 256) {
+ vlog.debug("%d palette entries", count);
+ throw rdr::Exception("too many palette entries");
+ }
+
+ // Copy the palette from the DIBSectionBuffer
+ ColourMap* cm = buffer->getColourMap();
+ if (!cm) return;
+ for (int i=0; i<count; i++) {
+ int r, g, b;
+ cm->lookup(i, &r, &g, &b);
+ colours[i].r = r;
+ colours[i].g = g;
+ colours[i].b = b;
+ }
+
+ // Set the window palette
+ windowPalette.setEntries(start, count, colours);
+
+ // Cause the window to be redrawn
+ InvalidateRect(getHandle(), 0, 0);
+}
+
+
+void CView::calculateScrollBars() {
+ // Calculate the required size of window
+ DWORD current_style = GetWindowLong(getHandle(), GWL_STYLE);
+ DWORD style = current_style & ~(WS_VSCROLL | WS_HSCROLL);
+ DWORD old_style;
+ RECT r;
+ SetRect(&r, 0, 0, buffer->width(), buffer->height());
+ AdjustWindowRect(&r, style, FALSE);
+ Rect reqd_size = Rect(r.left, r.top, r.right, r.bottom);
+
+ if (!bumpScroll) {
+ // We only enable scrollbars if bump-scrolling is not active.
+ // Effectively, this means if full-screen is not active,
+ // but I think it's better to make these things explicit.
+
+ // 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(getHandle(), GWL_STYLE, style);
+ SetWindowPos(getHandle(), 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(getHandle(), 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(getHandle(), SB_HORZ, &si, TRUE);
+ }
+}
+
+
+void
+CView::calculateFullColourPF() {
+ // If the server is palette based then use palette locally
+ // Also, don't bother doing bgr222
+ if (!serverDefaultPF.trueColour || (serverDefaultPF.depth < 6)) {
+ fullColourPF = serverDefaultPF;
+ options.fullColour = true;
+ } else {
+ // If server is trueColour, use lowest depth PF
+ PixelFormat native = getNativePF();
+ if ((serverDefaultPF.bpp < native.bpp) ||
+ ((serverDefaultPF.bpp == native.bpp) &&
+ (serverDefaultPF.depth < native.depth)))
+ fullColourPF = serverDefaultPF;
+ else
+ fullColourPF = getNativePF();
+ }
+ formatChange = true;
+}
+
+
+void
+CView::updateF8Menu(bool hideSystemCommands) {
+ HMENU menu = GetSystemMenu(getHandle(), FALSE);
+
+ if (hideSystemCommands) {
+ // Gray out menu items that might cause a World Of Pain
+ HMENU menu = GetSystemMenu(getHandle(), FALSE);
+ EnableMenuItem(menu, SC_SIZE, MF_BYCOMMAND | MF_GRAYED);
+ EnableMenuItem(menu, SC_MOVE, MF_BYCOMMAND | MF_GRAYED);
+ EnableMenuItem(menu, SC_RESTORE, MF_BYCOMMAND | MF_ENABLED);
+ EnableMenuItem(menu, SC_MINIMIZE, MF_BYCOMMAND | MF_ENABLED);
+ EnableMenuItem(menu, SC_MAXIMIZE, MF_BYCOMMAND | MF_ENABLED);
+ }
+
+ // Update the modifier key menu items
+ UINT ctrlCheckFlags = kbd.keyPressed(VK_CONTROL) ? MF_CHECKED : MF_UNCHECKED;
+ UINT altCheckFlags = kbd.keyPressed(VK_MENU) ? MF_CHECKED : MF_UNCHECKED;
+ CheckMenuItem(menu, IDM_CTRL_KEY, MF_BYCOMMAND | ctrlCheckFlags);
+ CheckMenuItem(menu, IDM_ALT_KEY, MF_BYCOMMAND | altCheckFlags);
+
+ // Ensure that the Send <MenuKey> menu item has the correct text
+ if (options.menuKey) {
+ TCharArray menuKeyStr(options.menuKeyName());
+ TCharArray tmp(_tcslen(menuKeyStr.buf) + 6);
+ _stprintf(tmp.buf, _T("Send %s"), menuKeyStr.buf);
+ if (!ModifyMenu(menu, IDM_SEND_MENU_KEY, MF_BYCOMMAND | MF_STRING, IDM_SEND_MENU_KEY, tmp.buf))
+ InsertMenu(menu, IDM_SEND_CAD, MF_BYCOMMAND | MF_STRING, IDM_SEND_MENU_KEY, tmp.buf);
+ } else {
+ RemoveMenu(menu, IDM_SEND_MENU_KEY, MF_BYCOMMAND);
+ }
+}
+
+
+void
+CView::setName(const char* name) {
+ vlog.debug("setName %s", name);
+ ::SetWindowText(getHandle(), TStr(name));
+ CConnection::setName(name);
+}
+
+
+void CView::serverInit() {
+ CConnection::serverInit();
+
+ // Save the server's current format
+ serverDefaultPF = cp.pf();
+
+ // Calculate the full-colour format to use
+ calculateFullColourPF();
+
+ // Request the initial update
+ vlog.info("requesting initial update");
+ formatChange = encodingChange = requestUpdate = true;
+ requestNewUpdate();
+
+ // Show the window
+ setVisible(true);
+}
+
+void
+CView::serverCutText(const char* str, int len) {
+ if (!options.serverCutText) return;
+ CharArray t(len+1);
+ memcpy(t.buf, str, len);
+ t.buf[len] = 0;
+ clipboard.setClipText(t.buf);
+}
+
+
+void CView::beginRect(const Rect& r, unsigned int encoding) {
+ sock->inStream().startTiming();
+}
+
+void CView::endRect(const Rect& r, unsigned int encoding) {
+ sock->inStream().stopTiming();
+ lastUsedEncoding_ = encoding;
+ if (debugDelay != 0) {
+ invertRect(r);
+ debugRects.push_back(r);
+ }
+}
+
+void CView::fillRect(const Rect& r, Pixel pix) {
+ if (cursorBackingRect.overlaps(r)) hideLocalCursor();
+ buffer->fillRect(r, pix);
+ invalidateBufferRect(r);
+}
+void CView::imageRect(const Rect& r, void* pixels) {
+ if (cursorBackingRect.overlaps(r)) hideLocalCursor();
+ buffer->imageRect(r, pixels);
+ invalidateBufferRect(r);
+}
+void CView::copyRect(const Rect& r, int srcX, int srcY) {
+ if (cursorBackingRect.overlaps(r) ||
+ cursorBackingRect.overlaps(Rect(srcX, srcY, srcX+r.width(), srcY+r.height())))
+ hideLocalCursor();
+ buffer->copyRect(r, Point(r.tl.x-srcX, r.tl.y-srcY));
+ invalidateBufferRect(r);
+}
+
+void CView::invertRect(const Rect& r) {
+ int stride;
+ rdr::U8* p = buffer->getPixelsRW(r, &stride);
+ for (int y = 0; y < r.height(); y++) {
+ for (int x = 0; x < r.width(); x++) {
+ switch (buffer->getPF().bpp) {
+ case 8: ((rdr::U8* )p)[x+y*stride] ^= 0xff; break;
+ case 16: ((rdr::U16*)p)[x+y*stride] ^= 0xffff; break;
+ case 32: ((rdr::U32*)p)[x+y*stride] ^= 0xffffffff; break;
+ }
+ }
+ }
+ invalidateBufferRect(r);
+}
+
+bool CView::getUserPasswd(char** user, char** password) {
+ if (user && options.userName.buf)
+ *user = strDup(options.userName.buf);
+ if (password && options.password.buf)
+ *password = strDup(options.password.buf);
+ if ((user && !*user) || (password && !*password)) {
+ // Missing username or password - prompt the user
+ UserPasswdDialog userPasswdDialog;
+ userPasswdDialog.setCSecurity(getCurrentCSecurity());
+ if (!userPasswdDialog.getUserPasswd(user, password))
+ return false;
+ }
+ if (user) options.setUserName(*user);
+ if (password) options.setPassword(*password);
+ return true;
+}
+
diff --git a/vncviewer/cview.h b/vncviewer/cview.h
new file mode 100644
index 00000000..2bee1c48
--- /dev/null
+++ b/vncviewer/cview.h
@@ -0,0 +1,296 @@
+/* Copyright (C) 2002-2004 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_CVIEW_H__
+#define __RFB_WIN32_CVIEW_H__
+
+#include <network/Socket.h>
+
+#include <rfb/CConnection.h>
+#include <rfb/Cursor.h>
+#include <rfb/UserPasswdGetter.h>
+
+#include <rfb_win32/Clipboard.h>
+#include <rfb_win32/DIBSectionBuffer.h>
+#include <rfb_win32/Win32Util.h>
+#include <rfb_win32/Registry.h>
+#include <rfb_win32/AboutDialog.h>
+#include <rfb_win32/CKeyboard.h>
+#include <rfb_win32/CPointer.h>
+
+#include <vncviewer/InfoDialog.h>
+#include <vncviewer/OptionsDialog.h>
+#include <vncviewer/CViewOptions.h>
+#include <vncviewer/CViewManager.h>
+#include <list>
+
+
+namespace rfb {
+
+ namespace win32 {
+
+ class CView : public CConnection,
+ public UserPasswdGetter,
+ rfb::win32::Clipboard::Notifier,
+ rdr::FdInStreamBlockCallback
+ {
+ public:
+ CView();
+ virtual ~CView();
+
+ bool initialise(network::Socket* s);
+
+ void setManager(CViewManager* m) {manager = m;}
+
+ void applyOptions(CViewOptions& opt);
+ const CViewOptions& getOptions() const {return options;};
+
+ // -=- Window Message handling
+
+ virtual LRESULT processMessage(UINT msg, WPARAM wParam, LPARAM lParam);
+
+ // -=- Socket blocking handling
+ // blockCallback will throw QuitMessage(result) when
+ // it processes a WM_QUIT message.
+ // The caller may catch that to cope gracefully with
+ // a request to quit.
+
+ class QuitMessage : public rdr::Exception {
+ public:
+ QuitMessage(WPARAM wp) : rdr::Exception("QuitMessage") {}
+ WPARAM wParam;
+ };
+ virtual void blockCallback();
+
+ // -=- Window interface
+
+ void postQuitOnDestroy(bool qod) {quit_on_destroy = qod;}
+ PixelFormat getNativePF() const;
+ void setVisible(bool visible);
+ void close(const char* reason=0);
+ HWND getHandle() const {return hwnd;}
+
+ void notifyClipboardChanged(const char* text, int len);
+
+ // -=- 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));
+ }
+
+ void setFullscreen(bool fs);
+
+ bool setViewportOffset(const Point& tl);
+
+ bool processBumpScroll(const Point& cursorPos);
+ void setBumpScroll(bool on);
+
+ int lastUsedEncoding() const { return lastUsedEncoding_; }
+
+ // -=- CConnection interface overrides
+
+ virtual CSecurity* getCSecurity(int secType);
+
+ virtual void setColourMapEntries(int firstColour, int nColours, rdr::U16* rgbs);
+ virtual void bell();
+
+ virtual void framebufferUpdateEnd();
+
+ virtual void setDesktopSize(int w, int h);
+ virtual void setCursor(const Point& hotspot, const Point& size, void* data, void* mask);
+ virtual void setName(const char* name);
+ virtual void serverInit();
+
+ virtual void serverCutText(const char* str, int len);
+
+ 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);
+
+ void invertRect(const Rect& r);
+
+ // VNCviewer dialog objects
+
+ OptionsDialog optionsDialog;
+
+ friend class InfoDialog;
+ InfoDialog infoDialog;
+
+ // UserPasswdGetter overrides, used to support a pre-supplied VNC password
+ virtual bool getUserPasswd(char** user, char** password);
+
+ // Global user-config registry key
+ static RegKey userConfigKey;
+
+ protected:
+
+ // Locally-rendered VNC cursor
+ void hideLocalCursor();
+ void showLocalCursor();
+ void renderLocalCursor();
+
+ // The system-rendered cursor
+ void hideSystemCursor();
+ void showSystemCursor();
+
+ // cursorOutsideBuffer() is called whenever we detect that the mouse has
+ // moved outside the desktop. It restores the system arrow cursor.
+ void cursorOutsideBuffer();
+
+ // Returns true if part of the supplied rect is visible, false otherwise
+ bool invalidateBufferRect(const Rect& crect);
+
+ // Auto-encoding selector
+ void autoSelectFormatAndEncoding();
+
+ // Request an update with appropriate setPixelFormat and setEncodings calls
+ void requestNewUpdate();
+
+ // Update the window palette if the display is palette-based.
+ // Colours are pulled from the DIBSectionBuffer's ColourMap.
+ // Only the specified range of indexes is dealt with.
+ // After the update, the entire window is redrawn.
+ void refreshWindowPalette(int start, int count);
+
+ // Determine whether or not we need to enable/disable scrollbars and set the
+ // window style accordingly
+ void calculateScrollBars();
+
+ // Recalculate the most suitable full-colour pixel format
+ void calculateFullColourPF();
+
+ // Enable/disable/check/uncheck the F8 menu items as appropriate.
+ void updateF8Menu(bool hideSystemCommands);
+
+ // VNCviewer options
+
+ CViewOptions options;
+
+ // Input handling
+ void writeKeyEvent(rdr::U8 vkey, rdr::U32 flags, bool down);
+ void writePointerEvent(int x, int y, int buttonMask);
+ rfb::win32::CKeyboard kbd;
+ rfb::win32::CPointer ptr;
+ Point oldpos;
+
+ // Clipboard handling
+ rfb::win32::Clipboard clipboard;
+
+ // Pixel format and encoding
+ PixelFormat serverDefaultPF;
+ PixelFormat fullColourPF;
+ bool sameMachine;
+ bool encodingChange;
+ bool formatChange;
+ int lastUsedEncoding_;
+
+ // Networking and RFB protocol
+ network::Socket* sock;
+ bool readyToRead;
+ bool requestUpdate;
+
+ // Palette handling
+ LogicalPalette windowPalette;
+ bool palette_changed;
+
+ // - Full-screen mode
+ Rect fullScreenOldRect;
+ DWORD fullScreenOldFlags;
+ bool fullScreenActive;
+
+ // Bump-scrolling (used in full-screen mode)
+ bool bumpScroll;
+ Point bumpScrollDelta;
+ IntervalTimer bumpScrollTimer;
+
+ // Cursor handling
+ Cursor cursor;
+ bool systemCursorVisible; // Should system-cursor be drawn?
+ bool trackingMouseLeave;
+ bool cursorInBuffer; // Is cursor position within server buffer? (ONLY for LocalCursor)
+ bool cursorVisible; // Is cursor currently rendered?
+ bool cursorAvailable; // Is cursor available for rendering?
+ Point cursorPos;
+ ManagedPixelBuffer cursorBacking;
+ Rect cursorBackingRect;
+
+ // ** Debugging/logging
+ /*
+ int update_rect_count;
+ int update_pixel_count;
+ Rect update_extent;
+ */
+ std::list<Rect> debugRects;
+
+ // Local window state
+ win32::DIBSectionBuffer* buffer;
+ bool has_focus;
+ bool quit_on_destroy;
+ Rect window_size;
+ Rect client_size;
+ Point scrolloffset;
+ Point maxscrolloffset;
+ HWND hwnd;
+
+ // Handle back to CViewManager instance, if any
+ CViewManager* manager;
+
+ };
+
+ };
+
+};
+
+#endif
+
+
diff --git a/vncviewer/msvcwarning.h b/vncviewer/msvcwarning.h
new file mode 100644
index 00000000..53a0678d
--- /dev/null
+++ b/vncviewer/msvcwarning.h
@@ -0,0 +1,19 @@
+/* Copyright (C) 2002-2003 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.
+ */
+#pragma warning( disable : 4800 ) // forcing bool 'true' or 'false'
+#pragma warning( disable : 4786 ) // debug identifier truncated
diff --git a/vncviewer/resource.h b/vncviewer/resource.h
new file mode 100644
index 00000000..351a2b0a
--- /dev/null
+++ b/vncviewer/resource.h
@@ -0,0 +1,80 @@
+//{{NO_DEPENDENCIES}}
+// Microsoft Developer Studio generated include file.
+// Used by vncviewer.rc
+//
+#define IDR_MANIFEST 1
+#define IDI_ICON 101
+#define IDD_VNC_AUTH_DLG 102
+#define IDD_CONNECTING_DLG 103
+#define IDD_CONNECTION_DLG 104
+#define IDC_DOT_CURSOR 105
+#define IDD_ABOUT 107
+#define IDD_FORMAT 108
+#define IDD_MISC 109
+#define IDD_INPUTS 110
+#define IDD_SERVER_KEYS 111
+#define IDR_TRAY 112
+#define IDD_CONNECTION_INFO 113
+#define IDD_DEFAULTS 116
+#define IDC_PASSWORD 1000
+#define IDC_CONNECTING_TEXT 1001
+#define IDC_SERVER_EDIT 1002
+#define IDC_USERNAME 1005
+#define IDC_VERSION 1008
+#define IDC_BUILDTIME 1009
+#define IDC_ENCODING_AUTO 1010
+#define IDC_FORMAT_FULLCOLOUR 1011
+#define IDC_ENCODING_ZRLE 1012
+#define IDC_ENCODING_HEXTILE 1013
+#define IDC_CONN_SHARED 1013
+#define IDC_ENCODING_RAW 1014
+#define IDC_FULL_SCREEN 1014
+#define IDC_SEND_POINTER 1015
+#define IDC_SEND_KEYS 1016
+#define IDC_CLIENT_CUTTEXT 1017
+#define IDC_SERVER_CUTTEXT 1018
+#define IDC_LOCAL_CURSOR 1019
+#define IDC_DESKTOP_RESIZE 1020
+#define IDC_COPYRIGHT 1021
+#define IDC_DESCRIPTION 1022
+#define IDC_OPTIONS 1023
+#define IDC_ABOUT 1024
+#define IDC_LIST1 1025
+#define IDC_INFO_NAME 1026
+#define IDC_INFO_HOST 1027
+#define IDC_INFO_SIZE 1028
+#define IDC_INFO_PF 1029
+#define IDC_INFO_DEF_PF 1030
+#define IDC_INFO_LINESPEED 1031
+#define IDC_INFO_VERSION 1032
+#define IDC_PROTOCOL_3_3 1034
+#define IDC_ACCEPT_BELL 1035
+#define IDC_FORMAT_VERYLOWCOLOUR 1036
+#define IDC_FORMAT_LOWCOLOUR 1037
+#define IDC_FORMAT_MEDIUMCOLOUR 1038
+#define IDC_LOAD_DEFAULTS 1040
+#define IDC_SAVE_DEFAULTS 1041
+#define IDC_LOAD_CONFIG 1042
+#define IDC_EMULATE3 1043
+#define IDC_POINTER_INTERVAL 1044
+#define IDC_SAVE_CONFIG 1045
+#define IDC_INFO_SECURITY 1046
+#define IDC_SAVE_CONFIG_AS 1048
+#define IDC_MENU_KEY 1051
+#define IDC_REQUESTED_ENCODING 1052
+#define IDC_LAST_ENCODING 1053
+#define ID_CLOSE 40002
+#define ID_OPTIONS 40003
+#define ID_NEW_CONNECTION 40004
+#define ID_ABOUT 40005
+
+// Next default values for new objects
+//
+#ifdef APSTUDIO_INVOKED
+#ifndef APSTUDIO_READONLY_SYMBOLS
+#define _APS_NEXT_RESOURCE_VALUE 120
+#define _APS_NEXT_COMMAND_VALUE 40006
+#define _APS_NEXT_CONTROL_VALUE 1054
+#define _APS_NEXT_SYMED_VALUE 101
+#endif
+#endif
diff --git a/vncviewer/vncviewer.cxx b/vncviewer/vncviewer.cxx
new file mode 100644
index 00000000..59e2d00b
--- /dev/null
+++ b/vncviewer/vncviewer.cxx
@@ -0,0 +1,370 @@
+/* Copyright (C) 2002-2004 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.
+ */
+
+// -=- VNC Viewer for Win32
+
+#include <string.h>
+#ifdef WIN32
+#define strcasecmp _stricmp
+#endif
+#include <list>
+
+#include <vncviewer/resource.h>
+#include <vncviewer/CViewManager.h>
+#include <vncviewer/CView.h>
+#include <vncviewer/OptionsDialog.h>
+
+#include <rfb/Logger_stdio.h>
+#include <rfb/Logger_file.h>
+#include <rfb/LogWriter.h>
+#include <rfb/Exception.h>
+
+#include <rfb_win32/RegConfig.h>
+#include <rfb_win32/TrayIcon.h>
+#include <rfb_win32/Win32Util.h>
+#include <rfb_win32/AboutDialog.h>
+
+#include <network/TcpSocket.h>
+
+#ifdef _DIALOG_CAPTURE
+#include <extra/LoadBMP.h>
+#endif
+
+using namespace rfb;
+using namespace rfb::win32;
+using namespace rdr;
+using namespace network;
+
+static LogWriter vlog("main");
+
+TStr rfb::win32::AppName("VNC Viewer");
+
+
+#ifdef _DIALOG_CAPTURE
+BoolParameter captureDialogs("CaptureDialogs", "", false);
+#endif
+
+//
+// -=- Listener
+// Class to handle listening on a particular port for incoming connections
+// from servers, and spawning of clients
+//
+
+static BoolParameter acceptIncoming("Listen", "Accept incoming connections from VNC servers.", false);
+
+
+//
+// -=- 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;
+
+
+//
+// -=- VNCviewer Tray Icon
+//
+
+class CViewTrayIcon : public TrayIcon {
+public:
+ CViewTrayIcon(CViewManager& mgr) : manager(mgr) {
+ setIcon(IDI_ICON);
+ setToolTip(_T("VNC Viewer"));
+ }
+ virtual LRESULT processMessage(UINT msg, WPARAM wParam, LPARAM lParam) {
+ switch(msg) {
+
+ case WM_USER:
+ switch (lParam) {
+ case WM_LBUTTONDBLCLK:
+ SendMessage(getHandle(), WM_COMMAND, ID_NEW_CONNECTION, 0);
+ break;
+ case WM_RBUTTONUP:
+ HMENU menu = LoadMenu(GetModuleHandle(0), MAKEINTRESOURCE(IDR_TRAY));
+ HMENU trayMenu = GetSubMenu(menu, 0);
+
+ // First item is New Connection, the default
+ SetMenuDefaultItem(trayMenu, ID_NEW_CONNECTION, FALSE);
+
+ // SetForegroundWindow is required, otherwise Windows ignores the
+ // TrackPopupMenu because the window isn't the foreground one, on
+ // some older Windows versions...
+ SetForegroundWindow(getHandle());
+
+ // Display the menu
+ POINT pos;
+ GetCursorPos(&pos);
+ TrackPopupMenu(trayMenu, 0, pos.x, pos.y, 0, getHandle(), 0);
+ break;
+ }
+ return 0;
+
+ case WM_COMMAND:
+ switch (LOWORD(wParam)) {
+ case ID_NEW_CONNECTION:
+ manager.addClient(0);
+ break;
+ case ID_OPTIONS:
+ OptionsDialog::global.showDialog(0);
+ break;
+ case ID_ABOUT:
+ AboutDialog::instance.showDialog();
+ break;
+ case ID_CLOSE:
+ SendMessage(getHandle(), WM_CLOSE, 0, 0);
+ break;
+ }
+ return 0;
+
+ case WM_CLOSE:
+ PostQuitMessage(0);
+ return 0;
+ }
+
+ return TrayIcon::processMessage(msg, wParam, lParam);
+ }
+protected:
+ CViewManager& manager;
+};
+
+//
+// -=- processParams
+// Read in the command-line parameters and interpret them.
+//
+
+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() {
+ printf("usage: vncviewer <options> <hostname>[:<display>]\n");
+ printf("Command-line options:\n");
+ printf(" -help - Provide usage information.\n");
+ printf(" -config <file> - Load connection settings from VNCViewer 3.3 settings file\n");
+ printf(" -console - Run with a console window visible.\n");
+ printf(" <setting>=<value> - Set the named configuration parameter.\n");
+ printf(" (Parameter values specified on the command-line override those specified by other configuration methods.)\n");
+ printf("\nLog names:\n");
+ LogWriter::listLogWriters();
+ printf("\nLog destinations:\n");
+ Logger::listLoggers();
+ printf("\nParameters:\n");
+ Configuration::listParams();
+}
+
+
+bool print_usage = false;
+bool close_console = true;
+std::list<char*> hosts;
+std::list<char*> configFiles;
+
+void
+processParams(int argc, char* argv[]) {
+ for (int i=1; i<argc; i++) {
+ try {
+
+ if (strcasecmp(argv[i], "-console") == 0) {
+ close_console = false;
+
+ } else if (((strcasecmp(argv[i], "-config") == 0) ||
+ (strcasecmp(argv[i], "/config") == 0)) && (i < argc-1)) {
+ configFiles.push_back(strDup(argv[i+1]));
+ i++;
+
+ } else if ((strcasecmp(argv[i], "-help") == 0) ||
+ (strcasecmp(argv[i], "--help") == 0) ||
+ (strcasecmp(argv[i], "-h") == 0) ||
+ (strcasecmp(argv[i], "/?") == 0)) {
+ print_usage = true;
+ close_console = false;
+ break;
+
+ } else {
+ // Try to process <option>=<value>, or -<bool>
+ if (Configuration::setParam(argv[i], true))
+ continue;
+ // Try to process -<option> <value>
+ if ((argv[i][0] == '-') && (i+1 < argc)) {
+ if (Configuration::setParam(&argv[i][1], argv[i+1], true)) {
+ i++;
+ continue;
+ }
+ }
+ // If it's -<option> then it's not recognised - error
+ // If it's <host> then add it to the list to connect to.
+ if ((argv[i][0] == '-') || (argv[i][0] == '/')) {
+ const char* fmt = "The option %s was not recognized. Use -help to see VNC Viewer usage";
+ CharArray tmp(strlen(argv[i])+strlen(fmt)+1);
+ sprintf(tmp.buf, fmt, argv[i]);
+ MsgBox(0, TStr(tmp.buf), MB_ICONSTOP | MB_OK);
+ exit(1);
+ } else {
+ hosts.push_back(strDup(argv[i]));
+ }
+ }
+
+ } catch (rdr::Exception& e) {
+ vlog.error(e.str());
+ }
+ }
+}
+
+
+//
+// -=- main
+//
+
+int WINAPI WinMain(HINSTANCE inst, HINSTANCE prevInst, char* cmdLine, int cmdShow) {
+
+ try {
+
+ // - Initialise the available loggers
+ initStdIOLoggers();
+ initFileLogger("C:\\temp\\vncviewer4.log");
+
+ // - By default, just log errors to stderr
+ logParams.setDefault("*:stderr:0");
+
+ // - Process the command-line
+ int argc = __argc;
+ char** argv = __argv;
+ processParams(argc, argv);
+
+ // - By default the console will be closed
+ if (close_console) {
+ if (!FreeConsole())
+ vlog.info("unable to close console:%u", GetLastError());
+ } else {
+ AllocConsole();
+ freopen("CONIN$","rb",stdin);
+ freopen("CONOUT$","wb",stdout);
+ freopen("CONOUT$","wb",stderr);
+ setbuf(stderr, 0);
+ }
+
+#ifdef _DIALOG_CAPTURE
+ if (captureDialogs) {
+ CView::userConfigKey.openKey(HKEY_CURRENT_USER, _T("Software\\RealVNC\\VNCViewer4"));
+ OptionsDialog::global.showDialog(0, true);
+ return 0;
+ }
+#endif
+
+ // - If no clients are specified, bring up a connection dialog
+ if (configFiles.empty() && hosts.empty() && !acceptIncoming && !print_usage)
+ hosts.push_back(0);
+
+ programInfo();
+
+ // - Connect to the clients
+ if (!configFiles.empty() || !hosts.empty() || acceptIncoming) {
+ // - Configure the registry configuration reader
+ win32::RegistryReader reg_reader;
+ reg_reader.setKey(HKEY_CURRENT_USER, _T("Software\\RealVNC\\VNCViewer4"));
+
+ // - Tell the rest of VNC Viewer where to write config data to
+ CView::userConfigKey.openKey(HKEY_CURRENT_USER, _T("Software\\RealVNC\\VNCViewer4"));
+
+ // - Start the Socket subsystem for TCP
+ TcpSocket::initTcpSockets();
+
+ // Create the client connection manager
+ CViewManager view_manager;
+
+ if (acceptIncoming) {
+ int port = 5500;
+
+ // Listening viewer
+ if (hosts.size() > 1) {
+ programUsage();
+ exit(2);
+ }
+ if (!hosts.empty()) {
+ port = atoi(hosts.front());
+ }
+
+ vlog.debug("opening listener");
+
+ CViewTrayIcon tray(view_manager);
+
+ view_manager.addDefaultTCPListener(port);
+
+ // Run the view manager
+ // Also processes the tray icon if necessary
+ MSG msg;
+ while (GetMessage(&msg, NULL, 0, 0) > 0) {
+ TranslateMessage(&msg);
+ DispatchMessage(&msg);
+ }
+
+ vlog.debug("quitting viewer");
+ } else {
+ // Read each config file in turn
+ while (!configFiles.empty()) {
+ char* filename = configFiles.front();
+ view_manager.addClient(filename, true);
+ strFree(filename);
+ configFiles.pop_front();
+ }
+
+ // Connect to each client in turn
+ while (!hosts.empty()) {
+ char* hostinfo = hosts.front();
+ view_manager.addClient(hostinfo);
+ strFree(hostinfo);
+ hosts.pop_front();
+ }
+
+ // Run the view manager
+ MSG msg;
+ while (GetMessage(&msg, NULL, 0, 0) > 0) {
+ TranslateMessage(&msg);
+ DispatchMessage(&msg);
+ }
+
+ vlog.debug("quitting viewer");
+ }
+
+ }
+
+ // - If necessary, print the program's usage info
+ if (print_usage)
+ programUsage();
+
+ if (!close_console) {
+ printf("Press Enter/Return key to continue\n");
+ char c = getchar();
+ }
+
+ } catch (rdr::Exception& e) {
+ MsgBox(0, TStr(e.str()), MB_ICONSTOP | MB_OK);
+ }
+
+ return 0;
+}
diff --git a/vncviewer/vncviewer.dsp b/vncviewer/vncviewer.dsp
new file mode 100644
index 00000000..25d358f8
--- /dev/null
+++ b/vncviewer/vncviewer.dsp
@@ -0,0 +1,229 @@
+# Microsoft Developer Studio Project File - Name="vncviewer" - Package Owner=<4>
+# Microsoft Developer Studio Generated Build File, Format Version 6.00
+# ** DO NOT EDIT **
+
+# TARGTYPE "Win32 (x86) Application" 0x0101
+
+CFG=vncviewer - 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 "vncviewer.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 "vncviewer.mak" CFG="vncviewer - Win32 Debug Unicode"
+!MESSAGE
+!MESSAGE Possible choices for configuration are:
+!MESSAGE
+!MESSAGE "vncviewer - Win32 Release" (based on "Win32 (x86) Application")
+!MESSAGE "vncviewer - Win32 Debug" (based on "Win32 (x86) Application")
+!MESSAGE "vncviewer - Win32 Debug Unicode" (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)" == "vncviewer - 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"
+# 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 ".." /FI"msvcwarning.h" /D "NDEBUG" /D "_WINDOWS" /D "WIN32" /D "_MBCS" /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
+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 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 ws2_32.lib version.lib comctl32.lib shell32.lib comdlg32.lib /nologo /subsystem:windows /machine:I386
+# Begin Special Build Tool
+SOURCE="$(InputPath)"
+PreLink_Desc=Updating buildTime
+PreLink_Cmds=cl /c /nologo /FoRelease\ /FdRelease\ /MT buildTime.cxx
+# End Special Build Tool
+
+!ELSEIF "$(CFG)" == "vncviewer - 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"
+# 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 ".." /FI"msvcwarning.h" /D "_DEBUG" /D "_WINDOWS" /D "WIN32" /D "_MBCS" /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
+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 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 /incremental:no /debug /machine:I386 /pdbtype:sept
+# Begin Special Build Tool
+SOURCE="$(InputPath)"
+PreLink_Desc=Updating buildTime
+PreLink_Cmds=cl /c /nologo /FoDebug\ /FdDebug\ /MTd buildTime.cxx
+# End Special Build Tool
+
+!ELSEIF "$(CFG)" == "vncviewer - Win32 Debug Unicode"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 1
+# PROP BASE Output_Dir "vncviewer___Win32_Debug_Unicode"
+# PROP BASE Intermediate_Dir "vncviewer___Win32_Debug_Unicode"
+# PROP BASE Ignore_Export_Lib 0
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 1
+# PROP Output_Dir "..\Debug_Unicode"
+# PROP Intermediate_Dir "Debug_Unicode"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /MTd /W3 /Gm /GX /Zi /Od /I ".." /FI"msvcwarning.h" /D "_DEBUG" /D "_WINDOWS" /D "WIN32" /D "_MBCS" /YX /FD /GZ /c
+# ADD CPP /nologo /MTd /W3 /Gm /GX /Zi /Od /I ".." /FI"msvcwarning.h" /D "_WINDOWS" /D "_DEBUG" /D "WIN32" /D "_UNICODE" /D "UNICODE" /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
+LINK32=link.exe
+# ADD BASE LINK32 user32.lib gdi32.lib advapi32.lib ws2_32.lib version.lib comctl32.lib shell32.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_Desc=Updating buildTime
+PreLink_Cmds=cl /c /nologo /FoDebug_Unicode\ /FdDebug_Unicode\ /MTd buildTime.cxx
+# End Special Build Tool
+
+!ENDIF
+
+# Begin Target
+
+# Name "vncviewer - Win32 Release"
+# Name "vncviewer - Win32 Debug"
+# Name "vncviewer - Win32 Debug Unicode"
+# 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=.\ConnectionDialog.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\cview.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\CViewManager.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\CViewOptions.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\InfoDialog.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\OptionsDialog.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\UserPasswdDialog.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\vncviewer.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\vncviewer.rc
+# End Source File
+# End Group
+# Begin Group "Header Files"
+
+# PROP Default_Filter "h;hpp;hxx;hm;inl"
+# Begin Source File
+
+SOURCE=.\ConnectingDialog.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\ConnectionDialog.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\cview.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\CViewManager.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\CViewOptions.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\InfoDialog.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\MRU.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\OptionsDialog.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\UserPasswdDialog.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=.\cursor1.cur
+# End Source File
+# Begin Source File
+
+SOURCE=.\vncviewer.exe.manifest
+# End Source File
+# Begin Source File
+
+SOURCE=.\vncviewer.ico
+# End Source File
+# End Group
+# End Target
+# End Project
diff --git a/vncviewer/vncviewer.exe.manifest b/vncviewer/vncviewer.exe.manifest
new file mode 100644
index 00000000..557e4563
--- /dev/null
+++ b/vncviewer/vncviewer.exe.manifest
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
+<assemblyIdentity
+ version="4.0.0.26"
+ processorArchitecture="X86"
+ name="RealVNC.vncviewer.exe"
+ type="win32"
+/>
+<description>.NET control deployment tool</description>
+<dependency>
+ <dependentAssembly>
+ <assemblyIdentity
+ type="win32"
+ name="Microsoft.Windows.Common-Controls"
+ version="6.0.0.0"
+ processorArchitecture="X86"
+ publicKeyToken="6595b64144ccf1df"
+ language="*"
+ />
+ </dependentAssembly>
+</dependency>
+</assembly>
diff --git a/vncviewer/vncviewer.ico b/vncviewer/vncviewer.ico
new file mode 100644
index 00000000..98e5578d
--- /dev/null
+++ b/vncviewer/vncviewer.ico
Binary files differ
diff --git a/vncviewer/vncviewer.rc b/vncviewer/vncviewer.rc
new file mode 100644
index 00000000..bd9ab6d0
--- /dev/null
+++ b/vncviewer/vncviewer.rc
@@ -0,0 +1,500 @@
+//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.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
+
+#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
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Icon
+//
+
+// Icon with lowest ID value placed first to ensure application icon
+// remains consistent on all systems.
+IDI_ICON ICON DISCARDABLE "vncviewer.ico"
+
+#ifndef _MAC
+/////////////////////////////////////////////////////////////////////////////
+//
+// Version
+//
+
+VS_VERSION_INFO VERSIONINFO
+ FILEVERSION 4,0,0,26
+ PRODUCTVERSION 4,0,0,26
+ 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", "RealVNC Ltd.\0"
+ VALUE "FileDescription", "VNC Viewer for Win32\0"
+ VALUE "FileVersion", "4.0\0"
+ VALUE "InternalName", "VNCViewer 4.0\0"
+ VALUE "LegalCopyright", "Copyright © RealVNC Ltd. 2002-2004\0"
+ VALUE "LegalTrademarks", "RealVNC\0"
+ VALUE "OriginalFilename", "vncviewer.exe\0"
+ VALUE "PrivateBuild", "\0"
+ VALUE "ProductName", "VNC Viewer 4.0\0"
+ VALUE "ProductVersion", "4.0\0"
+ VALUE "SpecialBuild", "\0"
+ END
+ END
+ BLOCK "VarFileInfo"
+ BEGIN
+ VALUE "Translation", 0x809, 1200
+ END
+END
+
+#endif // !_MAC
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Dialog
+//
+
+IDD_VNC_AUTH_DLG DIALOG DISCARDABLE 0, 0, 241, 46
+STYLE DS_SETFOREGROUND | DS_CENTER | WS_POPUP | WS_CAPTION
+CAPTION "VNC Viewer : Authentication"
+FONT 8, "MS Sans Serif"
+BEGIN
+ EDITTEXT IDC_USERNAME,75,6,95,14,ES_AUTOHSCROLL
+ EDITTEXT IDC_PASSWORD,75,25,95,15,ES_PASSWORD | ES_AUTOHSCROLL |
+ ES_WANTRETURN
+ DEFPUSHBUTTON "OK",IDOK,181,6,53,14
+ PUSHBUTTON "Cancel",IDCANCEL,181,25,53,15
+ ICON IDI_ICON,IDI_ICON,7,6,20,20
+ LTEXT "Username:",IDC_STATIC,35,6,35,14
+ LTEXT "Password:",IDC_STATIC,35,25,35,15
+END
+
+IDD_CONNECTING_DLG DIALOG DISCARDABLE 0, 0, 185, 47
+STYLE DS_MODALFRAME | DS_SETFOREGROUND | DS_CENTER | WS_POPUP | WS_CAPTION
+CAPTION "VNC Viewer : Connecting"
+FONT 8, "MS Sans Serif"
+BEGIN
+ PUSHBUTTON "Cancel",IDCANCEL,128,26,50,14,WS_DISABLED
+ CTEXT "Attempting to connect to host...",IDC_CONNECTING_TEXT,7,
+ 7,171,14,SS_CENTERIMAGE
+END
+
+IDD_CONNECTION_DLG DIALOG DISCARDABLE 0, 0, 241, 54
+STYLE DS_MODALFRAME | DS_SETFOREGROUND | DS_CENTER | WS_POPUP | WS_CAPTION |
+ WS_SYSMENU
+CAPTION "VNC Viewer : Connection Details"
+FONT 8, "MS Sans Serif"
+BEGIN
+ COMBOBOX IDC_SERVER_EDIT,70,6,110,234,CBS_DROPDOWN |
+ CBS_AUTOHSCROLL | WS_VSCROLL | WS_TABSTOP
+ PUSHBUTTON "&About...",IDC_ABOUT,15,30,50,17
+ PUSHBUTTON "&Options...",IDC_OPTIONS,70,30,50,17
+ DEFPUSHBUTTON "OK",IDOK,130,30,50,17
+ PUSHBUTTON "Cancel",IDCANCEL,185,30,50,17
+ ICON IDI_ICON,IDI_ICON,5,6,20,20
+ LTEXT "Server:",IDC_STATIC,35,6,30,14
+END
+
+IDD_ABOUT DIALOG DISCARDABLE 0, 0, 249, 92
+STYLE DS_MODALFRAME | DS_SETFOREGROUND | DS_CENTER | WS_POPUP | WS_CAPTION |
+ WS_SYSMENU
+CAPTION "About VNC Viewer 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.realvnc.com for more information on VNC.",
+ IDC_STATIC,40,55,202,15
+END
+
+IDD_FORMAT DIALOG DISCARDABLE 0, 0, 201, 101
+STYLE DS_MODALFRAME | DS_CONTROL | WS_POPUP | WS_CAPTION | WS_SYSMENU
+CAPTION "Colour/Encoding"
+FONT 8, "MS Sans Serif"
+BEGIN
+ CONTROL "&Auto select",IDC_ENCODING_AUTO,"Button",
+ BS_AUTOCHECKBOX | WS_TABSTOP,7,7,88,13
+ GROUPBOX "Preferred encoding",IDC_STATIC,7,25,83,60
+ CONTROL "ZRLE",IDC_ENCODING_ZRLE,"Button",BS_AUTORADIOBUTTON |
+ WS_GROUP,10,35,75,14
+ CONTROL "Hextile",IDC_ENCODING_HEXTILE,"Button",
+ BS_AUTORADIOBUTTON,10,49,75,16
+ CONTROL "Raw",IDC_ENCODING_RAW,"Button",BS_AUTORADIOBUTTON,10,65,
+ 75,15
+ GROUPBOX "Colour level",IDC_STATIC,95,10,99,75
+ CONTROL "&Full (all available colours)",IDC_FORMAT_FULLCOLOUR,
+ "Button",BS_AUTORADIOBUTTON | WS_GROUP,100,20,90,15
+ CONTROL "&Medium (256 colours)",IDC_FORMAT_MEDIUMCOLOUR,"Button",
+ BS_AUTORADIOBUTTON,100,35,90,14
+ CONTROL "&Low (64 colours)",IDC_FORMAT_LOWCOLOUR,"Button",
+ BS_AUTORADIOBUTTON,100,49,90,16
+ CONTROL "&Very low (8 colours)",IDC_FORMAT_VERYLOWCOLOUR,"Button",
+ BS_AUTORADIOBUTTON,100,65,90,15
+END
+
+IDD_MISC DIALOG DISCARDABLE 0, 0, 213, 137
+STYLE DS_MODALFRAME | DS_CONTROL | WS_POPUP | WS_CAPTION | WS_SYSMENU
+CAPTION "Misc"
+FONT 8, "MS Sans Serif"
+BEGIN
+ CONTROL "Shared connection (do not disconnect other viewers)",
+ IDC_CONN_SHARED,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,7,
+ 10,199,15
+ CONTROL "Full-screen mode",IDC_FULL_SCREEN,"Button",
+ BS_AUTOCHECKBOX | WS_TABSTOP,7,25,199,15
+ CONTROL "Render cursor locally",IDC_LOCAL_CURSOR,"Button",
+ BS_AUTOCHECKBOX | WS_TABSTOP,7,40,199,15
+ CONTROL "Allow dynamic desktop resizing",IDC_DESKTOP_RESIZE,
+ "Button",BS_AUTOCHECKBOX | WS_TABSTOP,7,55,199,15
+ CONTROL "Only use protocol version 3.3",IDC_PROTOCOL_3_3,"Button",
+ BS_AUTOCHECKBOX | WS_TABSTOP,7,70,199,15
+ CONTROL "Beep when requested to by the server",IDC_ACCEPT_BELL,
+ "Button",BS_AUTOCHECKBOX | WS_TABSTOP,7,86,199,15
+END
+
+IDD_INPUTS DIALOG DISCARDABLE 0, 0, 186, 138
+STYLE DS_MODALFRAME | DS_CONTROL | WS_POPUP | WS_CAPTION | WS_SYSMENU
+CAPTION "Inputs"
+FONT 8, "MS Sans Serif"
+BEGIN
+ CONTROL "Send pointer events to server",IDC_SEND_POINTER,"Button",
+ BS_AUTOCHECKBOX | WS_TABSTOP,7,10,172,15
+ CONTROL "Send keyboard events to server",IDC_SEND_KEYS,"Button",
+ BS_AUTOCHECKBOX | WS_TABSTOP,7,25,172,15
+ CONTROL "Send clipboard changes to server",IDC_CLIENT_CUTTEXT,
+ "Button",BS_AUTOCHECKBOX | WS_TABSTOP,7,40,172,15
+ CONTROL "Accept clipboard changes from server",
+ IDC_SERVER_CUTTEXT,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,
+ 7,55,172,15
+ CONTROL "Enable 3-button mouse emulation",IDC_EMULATE3,"Button",
+ BS_AUTOCHECKBOX | WS_TABSTOP,7,70,172,15
+ CONTROL "Rate-limit mouse move events",IDC_POINTER_INTERVAL,
+ "Button",BS_AUTOCHECKBOX | WS_TABSTOP,7,86,172,14
+ LTEXT "Menu key",IDC_STATIC,7,100,98,15,SS_CENTERIMAGE
+ COMBOBOX IDC_MENU_KEY,105,100,74,105,CBS_DROPDOWNLIST | CBS_SORT |
+ WS_VSCROLL | WS_TABSTOP
+END
+
+IDD_CONNECTION_INFO DIALOG DISCARDABLE 0, 0, 239, 186
+STYLE DS_MODALFRAME | DS_SETFOREGROUND | DS_CENTER | WS_POPUP | WS_CAPTION |
+ WS_SYSMENU
+CAPTION "VNC Connection Info"
+FONT 8, "MS Sans Serif"
+BEGIN
+ DEFPUSHBUTTON "OK",IDOK,182,165,50,14
+ LTEXT "Desktop Name:",IDC_STATIC,7,10,73,15
+ LTEXT "Host:",IDC_STATIC,7,25,73,15
+ LTEXT "Size:",IDC_STATIC,7,40,73,15
+ LTEXT "Pixel Format:",IDC_STATIC,7,55,73,15
+ LTEXT "Server Default:",IDC_STATIC,7,70,73,15
+ LTEXT "Line Speed Estimate:",IDC_STATIC,7,115,73,15
+ LTEXT "Protocol Version:",IDC_STATIC,7,130,73,15
+ LTEXT "",IDC_INFO_NAME,80,10,152,15
+ LTEXT "",IDC_INFO_HOST,80,25,152,15
+ LTEXT "",IDC_INFO_SIZE,80,40,152,15
+ LTEXT "",IDC_INFO_PF,80,55,152,15
+ LTEXT "",IDC_INFO_DEF_PF,80,70,152,15
+ LTEXT "",IDC_INFO_LINESPEED,80,115,152,15
+ LTEXT "",IDC_INFO_VERSION,80,130,152,15
+ LTEXT "Security Method:",IDC_STATIC,7,145,73,15
+ LTEXT "",IDC_INFO_SECURITY,80,145,152,15
+ LTEXT "Requested Encoding:",IDC_STATIC,7,85,73,15
+ LTEXT "Last Used Encoding:",IDC_STATIC,7,100,73,15
+ LTEXT "",IDC_REQUESTED_ENCODING,80,86,152,15
+ LTEXT "",IDC_LAST_ENCODING,80,100,152,15
+END
+
+IDD_DEFAULTS DIALOG DISCARDABLE 0, 0, 131, 113
+STYLE DS_MODALFRAME | DS_CONTROL | WS_POPUP | WS_CAPTION | WS_SYSMENU
+CAPTION "Defaults"
+FONT 8, "MS Sans Serif"
+BEGIN
+ PUSHBUTTON "Reload &Defaults",IDC_LOAD_DEFAULTS,7,10,117,15
+ PUSHBUTTON "&Save As Defaults",IDC_SAVE_DEFAULTS,7,30,117,15
+ PUSHBUTTON "Reload Configuration &File",IDC_LOAD_CONFIG,7,50,117,15
+ PUSHBUTTON "Save &Configuration File",IDC_SAVE_CONFIG,7,70,117,15
+ PUSHBUTTON "Save Configuration File &As ...",IDC_SAVE_CONFIG_AS,7,
+ 90,117,15
+END
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// DESIGNINFO
+//
+
+#ifdef APSTUDIO_INVOKED
+GUIDELINES DESIGNINFO DISCARDABLE
+BEGIN
+ IDD_VNC_AUTH_DLG, DIALOG
+ BEGIN
+ LEFTMARGIN, 7
+ RIGHTMARGIN, 234
+ VERTGUIDE, 35
+ VERTGUIDE, 70
+ VERTGUIDE, 75
+ TOPMARGIN, 6
+ BOTTOMMARGIN, 40
+ HORZGUIDE, 20
+ HORZGUIDE, 25
+ HORZGUIDE, 40
+ END
+
+ IDD_CONNECTING_DLG, DIALOG
+ BEGIN
+ LEFTMARGIN, 7
+ RIGHTMARGIN, 178
+ TOPMARGIN, 7
+ BOTTOMMARGIN, 40
+ HORZGUIDE, 21
+ HORZGUIDE, 26
+ END
+
+ IDD_CONNECTION_DLG, DIALOG
+ BEGIN
+ LEFTMARGIN, 5
+ RIGHTMARGIN, 235
+ VERTGUIDE, 15
+ VERTGUIDE, 35
+ VERTGUIDE, 65
+ VERTGUIDE, 70
+ VERTGUIDE, 120
+ VERTGUIDE, 130
+ VERTGUIDE, 180
+ VERTGUIDE, 185
+ TOPMARGIN, 6
+ BOTTOMMARGIN, 47
+ HORZGUIDE, 20
+ HORZGUIDE, 30
+ HORZGUIDE, 40
+ END
+
+ IDD_ABOUT, DIALOG
+ BEGIN
+ LEFTMARGIN, 7
+ RIGHTMARGIN, 242
+ VERTGUIDE, 40
+ VERTGUIDE, 165
+ VERTGUIDE, 195
+ TOPMARGIN, 7
+ BOTTOMMARGIN, 85
+ HORZGUIDE, 10
+ HORZGUIDE, 25
+ HORZGUIDE, 40
+ HORZGUIDE, 55
+ HORZGUIDE, 70
+ END
+
+ IDD_FORMAT, DIALOG
+ BEGIN
+ LEFTMARGIN, 7
+ RIGHTMARGIN, 194
+ VERTGUIDE, 10
+ VERTGUIDE, 85
+ VERTGUIDE, 90
+ VERTGUIDE, 95
+ VERTGUIDE, 100
+ VERTGUIDE, 105
+ VERTGUIDE, 190
+ TOPMARGIN, 7
+ BOTTOMMARGIN, 94
+ HORZGUIDE, 10
+ HORZGUIDE, 20
+ HORZGUIDE, 25
+ HORZGUIDE, 35
+ HORZGUIDE, 49
+ HORZGUIDE, 65
+ HORZGUIDE, 80
+ HORZGUIDE, 85
+ END
+
+ IDD_MISC, DIALOG
+ BEGIN
+ LEFTMARGIN, 7
+ RIGHTMARGIN, 206
+ TOPMARGIN, 7
+ BOTTOMMARGIN, 130
+ HORZGUIDE, 10
+ HORZGUIDE, 25
+ HORZGUIDE, 40
+ HORZGUIDE, 55
+ HORZGUIDE, 70
+ HORZGUIDE, 85
+ HORZGUIDE, 100
+ END
+
+ IDD_INPUTS, DIALOG
+ BEGIN
+ LEFTMARGIN, 7
+ RIGHTMARGIN, 179
+ VERTGUIDE, 105
+ TOPMARGIN, 7
+ BOTTOMMARGIN, 131
+ HORZGUIDE, 10
+ HORZGUIDE, 25
+ HORZGUIDE, 40
+ HORZGUIDE, 55
+ HORZGUIDE, 70
+ HORZGUIDE, 85
+ HORZGUIDE, 100
+ HORZGUIDE, 115
+ END
+
+ IDD_CONNECTION_INFO, DIALOG
+ BEGIN
+ LEFTMARGIN, 7
+ RIGHTMARGIN, 232
+ VERTGUIDE, 80
+ TOPMARGIN, 7
+ BOTTOMMARGIN, 179
+ HORZGUIDE, 10
+ HORZGUIDE, 25
+ HORZGUIDE, 40
+ HORZGUIDE, 55
+ HORZGUIDE, 70
+ HORZGUIDE, 85
+ HORZGUIDE, 100
+ HORZGUIDE, 115
+ HORZGUIDE, 130
+ HORZGUIDE, 145
+ HORZGUIDE, 160
+ END
+
+ IDD_DEFAULTS, DIALOG
+ BEGIN
+ LEFTMARGIN, 7
+ RIGHTMARGIN, 124
+ TOPMARGIN, 7
+ BOTTOMMARGIN, 106
+ HORZGUIDE, 10
+ HORZGUIDE, 25
+ HORZGUIDE, 30
+ HORZGUIDE, 45
+ HORZGUIDE, 50
+ HORZGUIDE, 65
+ HORZGUIDE, 70
+ HORZGUIDE, 85
+ HORZGUIDE, 90
+ HORZGUIDE, 105
+ END
+END
+#endif // APSTUDIO_INVOKED
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Cursor
+//
+
+IDC_DOT_CURSOR CURSOR DISCARDABLE "cursor1.cur"
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Dialog Info
+//
+
+IDD_CONNECTION_DLG DLGINIT
+BEGIN
+ IDC_SERVER_EDIT, 0x403, 16, 0
+0x796d, 0x616d, 0x6863, 0x6e69, 0x2e65, 0x726f, 0x3a67, 0x0031,
+ 0
+END
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Menu
+//
+
+IDR_TRAY MENU DISCARDABLE
+BEGIN
+ POPUP "Tray Menu"
+ BEGIN
+ MENUITEM "&New Connection...", ID_NEW_CONNECTION
+ MENUITEM SEPARATOR
+ MENUITEM "Default &Options...", ID_OPTIONS
+ MENUITEM SEPARATOR
+ MENUITEM "&Close Daemon", ID_CLOSE
+ MENUITEM "&About...", ID_ABOUT
+ END
+END
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// 24
+//
+
+IDR_MANIFEST 24 DISCARDABLE "vncviewer.exe.manifest"
+#endif // English (U.K.) resources
+/////////////////////////////////////////////////////////////////////////////
+
+
+
+#ifndef APSTUDIO_INVOKED
+/////////////////////////////////////////////////////////////////////////////
+//
+// Generated from the TEXTINCLUDE 3 resource.
+//
+
+
+/////////////////////////////////////////////////////////////////////////////
+#endif // not APSTUDIO_INVOKED
+
diff --git a/vncviewer_unix/AboutDialog.h b/vncviewer_unix/AboutDialog.h
new file mode 100644
index 00000000..2f54f66b
--- /dev/null
+++ b/vncviewer_unix/AboutDialog.h
@@ -0,0 +1,37 @@
+/* Copyright (C) 2002-2004 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 __ABOUTDIALOG_H__
+#define __ABOUTDIALOG_H__
+
+#include "TXMsgBox.h"
+#include "parameters.h"
+
+extern char buildtime[];
+
+class AboutDialog : public TXMsgBox {
+public:
+ AboutDialog(Display* dpy)
+ : TXMsgBox(dpy, aboutText, MB_OK, "About VNC Viewer") {
+ }
+};
+
+#endif
diff --git a/vncviewer_unix/CConn.cxx b/vncviewer_unix/CConn.cxx
new file mode 100644
index 00000000..0b6431bb
--- /dev/null
+++ b/vncviewer_unix/CConn.cxx
@@ -0,0 +1,669 @@
+/* Copyright (C) 2002-2004 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.
+ */
+//
+// CConn.cxx
+//
+
+#include <unistd.h>
+#include "CConn.h"
+#include <rfb/CMsgWriter.h>
+#include <rfb/encodings.h>
+#include <rfb/secTypes.h>
+#include <rfb/CSecurityNone.h>
+#include <rfb/CSecurityVncAuth.h>
+#include <rfb/Hostname.h>
+#include <rfb/LogWriter.h>
+#include <rfb/util.h>
+#include <network/TcpSocket.h>
+
+#include "TXViewport.h"
+#include "DesktopWindow.h"
+#include "ServerDialog.h"
+#include "PasswdDialog.h"
+#include "parameters.h"
+
+using namespace rfb;
+
+static rfb::LogWriter vlog("CConn");
+
+IntParameter debugDelay("DebugDelay","Milliseconds to display inverted "
+ "pixel data - a debugging feature", 0);
+
+StringParameter menuKey("MenuKey", "The key which brings up the popup menu",
+ "F8");
+StringParameter windowName("name", "The X window name", "");
+
+CConn::CConn(Display* dpy_, int argc_, char** argv_, network::Socket* sock_,
+ char* vncServerName)
+ : dpy(dpy_), argc(argc_),
+ argv(argv_), serverHost(0), serverPort(0), sock(sock_), viewport(0),
+ desktop(0), desktopEventHandler(0),
+ currentEncoding(encodingZRLE), lastServerEncoding((unsigned int)-1),
+ fullColour(::fullColour),
+ autoSelect(::autoSelect), shared(::shared), formatChange(false),
+ encodingChange(false), sameMachine(false), fullScreen(::fullScreen),
+ ctrlDown(false), altDown(false),
+ menuKeysym(0), menu(dpy, this), options(dpy, this), about(dpy), info(dpy)
+{
+ CharArray menuKeyStr(menuKey.getData());
+ menuKeysym = XStringToKeysym(menuKeyStr.buf);
+
+ setShared(shared);
+ addSecType(secTypeNone);
+ addSecType(secTypeVncAuth);
+ CharArray encStr(preferredEncoding.getData());
+ int encNum = encodingNum(encStr.buf);
+ if (encNum != -1) {
+ currentEncoding = encNum;
+ autoSelect = false;
+ }
+ cp.supportsDesktopResize = true;
+ cp.supportsLocalCursor = useLocalCursor;
+ initMenu();
+
+ if (sock) {
+ char* name = sock->getPeerEndpoint();
+ vlog.info("Accepted connection from %s", name);
+ if (name) free(name);
+ } else {
+ if (vncServerName) {
+ getHostAndPort(vncServerName, &serverHost, &serverPort);
+ } else {
+ ServerDialog dlg(dpy, &options, &about);
+ if (!dlg.show() || dlg.entry.getText()[0] == 0) {
+ exit(1);
+ }
+ getHostAndPort(dlg.entry.getText(), &serverHost, &serverPort);
+ }
+
+ sock = new network::TcpSocket(serverHost, serverPort);
+ vlog.info("connected to host %s port %d", serverHost, serverPort);
+ }
+
+ sameMachine = sock->sameMachine();
+ sock->inStream().setBlockCallback(this);
+ setServerName(sock->getPeerEndpoint());
+ setStreams(&sock->inStream(), &sock->outStream());
+ initialiseProtocol();
+}
+
+CConn::~CConn() {
+ free(serverHost);
+ delete desktop;
+ delete viewport;
+ delete sock;
+}
+
+// deleteWindow() is called when the user closes the desktop or menu windows.
+
+void CConn::deleteWindow(TXWindow* w) {
+ if (w == &menu) {
+ menu.unmap();
+ } else if (w == viewport) {
+ exit(1);
+ }
+}
+
+// handleEvent() filters all events on the desktop and menu. Most are passed
+// straight through. The exception is the F8 key. When pressed on the
+// desktop, it is used to bring up the menu. An F8 press or release on the
+// menu is passed through as if it were on the desktop.
+
+void CConn::handleEvent(TXWindow* w, XEvent* ev)
+{
+ KeySym ks;
+ char str[256];
+
+ switch (ev->type) {
+ case KeyPress:
+ case KeyRelease:
+ XLookupString(&ev->xkey, str, 256, &ks, NULL);
+ if (ks == menuKeysym && (ev->xkey.state & (ShiftMask|ControlMask)) == 0) {
+ if (w == desktop && ev->type == KeyPress) {
+ showMenu(ev->xkey.x_root, ev->xkey.y_root);
+ break;
+ } else if (w == &menu) {
+ if (ev->type == KeyPress) menu.unmap();
+ desktopEventHandler->handleEvent(w, ev);
+ break;
+ }
+ }
+ // drop through
+
+ default:
+ if (w == desktop) desktopEventHandler->handleEvent(w, ev);
+ else if (w == &menu) menuEventHandler->handleEvent(w, ev);
+ }
+}
+
+// blockCallback() is called when reading from the socket would block. We
+// process X events until the socket is ready for reading again.
+
+void CConn::blockCallback() {
+ fd_set rfds;
+ do {
+ TXWindow::handleXEvents(dpy);
+ struct timeval tv;
+ struct timeval* tvp;
+ if (Timer::getTimeout(&tv))
+ tvp = &tv;
+ else
+ tvp = 0;
+ FD_ZERO(&rfds);
+ FD_SET(ConnectionNumber(dpy), &rfds);
+ FD_SET(sock->getFd(), &rfds);
+ int n = select(FD_SETSIZE, &rfds, 0, 0, tvp);
+ if (n < 0) throw rdr::SystemException("select",errno);
+ Timer::callTimers();
+ } while (!(FD_ISSET(sock->getFd(), &rfds)));
+}
+
+
+// getPasswd() is called by the CSecurity object when it needs us to read a
+// password from the user.
+
+bool CConn::getUserPasswd(char** user, char** password)
+{
+ CharArray passwordFileStr(passwordFile.getData());
+ if (!user && passwordFileStr.buf[0]) {
+ FILE* fp = fopen(passwordFileStr.buf, "r");
+ if (!fp) return false;
+ char data[256];
+ int datalen = fread(data, 1, 256, fp);
+ fclose(fp);
+ if (datalen != 8) return false;
+ vncAuthUnobfuscatePasswd(data);
+ *password = strDup(data);
+ memset(data, 0, strlen(data));
+ return true;
+ }
+
+ const char* secType = secTypeName(getCurrentCSecurity()->getType());
+ const char* titlePrefix = "VNC Authentication";
+ CharArray title(strlen(titlePrefix) + strlen(secType) + 4);
+ sprintf(title.buf, "%s [%s]", titlePrefix, secType);
+ PasswdDialog dlg(dpy, title.buf, !user);
+ if (!dlg.show()) return false;
+ if (user)
+ *user = strDup(dlg.userEntry.getText());
+ *password = strDup(dlg.passwdEntry.getText());
+ return true;
+}
+
+
+// CConnection callback methods
+
+// getCSecurity() gets the appropriate CSecurity object for the security
+// types which we support.
+CSecurity* CConn::getCSecurity(int secType) {
+ switch (secType) {
+ case secTypeNone:
+ return new CSecurityNone();
+ case secTypeVncAuth:
+ return new CSecurityVncAuth(this);
+ default:
+ throw rfb::Exception("Unsupported secType?");
+ }
+}
+
+// serverInit() is called when the serverInit message has been received. At
+// this point we create the desktop window and display it. We also tell the
+// server the pixel format and encodings to use and request the first update.
+void CConn::serverInit() {
+ CConnection::serverInit();
+ serverPF = cp.pf();
+ desktop = new DesktopWindow(dpy, cp.width, cp.height, serverPF, this);
+ desktopEventHandler = desktop->setEventHandler(this);
+ desktop->addEventMask(KeyPressMask | KeyReleaseMask);
+ fullColourPF = desktop->getPF();
+ if (!serverPF.trueColour)
+ fullColour = true;
+ recreateViewport();
+ formatChange = encodingChange = true;
+ requestNewUpdate();
+}
+
+// setDesktopSize() is called when the desktop size changes (including when
+// it is set initially).
+void CConn::setDesktopSize(int w, int h) {
+ CConnection::setDesktopSize(w,h);
+ if (desktop) {
+ desktop->resize(w, h);
+ recreateViewport();
+ }
+}
+
+// framebufferUpdateEnd() is called at the end of an update.
+// For each rectangle, the FdInStream will have timed the speed
+// of the connection, allowing us to select format and encoding
+// appropriately, and then request another incremental update.
+void CConn::framebufferUpdateEnd() {
+ if (debugDelay != 0) {
+ XSync(dpy, False);
+ struct timeval tv;
+ tv.tv_sec = debugDelay / 1000;
+ tv.tv_usec = (debugDelay % 1000) * 1000;
+ select(0, 0, 0, 0, &tv);
+ std::list<rfb::Rect>::iterator i;
+ for (i = debugRects.begin(); i != debugRects.end(); i++) {
+ desktop->invertRect(*i);
+ }
+ debugRects.clear();
+ }
+ desktop->framebufferUpdateEnd();
+ if (autoSelect)
+ autoSelectFormatAndEncoding();
+ requestNewUpdate();
+}
+
+// The rest of the callbacks are fairly self-explanatory...
+
+void CConn::setColourMapEntries(int firstColour, int nColours, rdr::U16* rgbs)
+{
+ desktop->setColourMapEntries(firstColour, nColours, rgbs);
+}
+
+void CConn::bell() { XBell(dpy, 0); }
+
+void CConn::serverCutText(const char* str, int len) {
+ desktop->serverCutText(str,len);
+}
+
+// We start timing on beginRect and stop timing on endRect, to
+// avoid skewing the bandwidth estimation as a result of the server
+// being slow or the network having high latency
+void CConn::beginRect(const Rect& r, unsigned int encoding)
+{
+ sock->inStream().startTiming();
+ if (encoding != encodingCopyRect) {
+ lastServerEncoding = encoding;
+ }
+}
+
+void CConn::endRect(const Rect& r, unsigned int encoding)
+{
+ sock->inStream().stopTiming();
+ if (debugDelay != 0) {
+ desktop->invertRect(r);
+ debugRects.push_back(r);
+ }
+}
+
+void CConn::fillRect(const rfb::Rect& r, rfb::Pixel p) {
+ desktop->fillRect(r,p);
+}
+void CConn::imageRect(const rfb::Rect& r, void* p) {
+ desktop->imageRect(r,p);
+}
+void CConn::copyRect(const rfb::Rect& r, int sx, int sy) {
+ desktop->copyRect(r,sx,sy);
+}
+void CConn::setCursor(const Point& hotspot, const Point& size,
+ void* data, void* mask) {
+ desktop->setCursor(hotspot, size, data, mask);
+}
+
+
+// Menu stuff - menuSelect() is called when the user selects a menu option.
+
+enum { ID_OPTIONS, ID_INFO, ID_FULLSCREEN, ID_REFRESH, ID_F8, ID_CTRLALTDEL,
+ ID_ABOUT, ID_DISMISS, ID_EXIT, ID_NEWCONN, ID_CTRL, ID_ALT };
+
+void CConn::initMenu() {
+ menuEventHandler = menu.setEventHandler(this);
+ menu.addEventMask(KeyPressMask | KeyReleaseMask);
+ menu.addEntry("Exit viewer", ID_EXIT);
+ menu.addEntry(0, 0);
+ menu.addEntry("Full screen", ID_FULLSCREEN);
+ menu.check(ID_FULLSCREEN, fullScreen);
+ menu.addEntry(0, 0);
+ menu.addEntry("Ctrl", ID_CTRL);
+ menu.addEntry("Alt", ID_ALT);
+ CharArray menuKeyStr(menuKey.getData());
+ CharArray sendMenuKey(6+strlen(menuKeyStr.buf));
+ sprintf(sendMenuKey.buf, "Send %s", menuKeyStr.buf);
+ menu.addEntry(sendMenuKey.buf, ID_F8);
+ menu.addEntry("Send Ctrl-Alt-Del", ID_CTRLALTDEL);
+ menu.addEntry(0, 0);
+ menu.addEntry("Refresh screen", ID_REFRESH);
+ menu.addEntry(0, 0);
+ menu.addEntry("New connection...", ID_NEWCONN);
+ menu.addEntry("Options...", ID_OPTIONS);
+ menu.addEntry("Connection info...", ID_INFO);
+ menu.addEntry("About VNCviewer...", ID_ABOUT);
+ menu.addEntry(0, 0);
+ menu.addEntry("Dismiss menu", ID_DISMISS);
+ menu.toplevel("VNC Menu", this);
+ menu.setBorderWidth(1);
+}
+
+void CConn::showMenu(int x, int y) {
+ menu.check(ID_FULLSCREEN, fullScreen);
+ menu.move(x, y);
+ menu.raise();
+ menu.map();
+}
+
+void CConn::menuSelect(long id, TXMenu* m) {
+ switch (id) {
+ case ID_NEWCONN:
+ {
+ menu.unmap();
+ if (fullScreen) {
+ fullScreen = false;
+ if (viewport) recreateViewport();
+ }
+ int pid = fork();
+ if (pid < 0) { perror("fork"); exit(1); }
+ if (pid == 0) {
+ delete sock;
+ close(ConnectionNumber(dpy));
+ struct timeval tv;
+ tv.tv_sec = 0;
+ tv.tv_usec = 200*1000;
+ select(0, 0, 0, 0, &tv);
+ execlp(programName, programName, 0);
+ perror("execlp"); exit(1);
+ }
+ break;
+ }
+ case ID_OPTIONS:
+ menu.unmap();
+ options.show();
+ break;
+ case ID_INFO:
+ {
+ menu.unmap();
+ char pfStr[100];
+ char spfStr[100];
+ cp.pf().print(pfStr, 100);
+ serverPF.print(spfStr, 100);
+ int secType = getCurrentCSecurity()->getType();
+ char infoText[1024];
+ sprintf(infoText,
+ "Desktop name: %.80s\n"
+ "Host: %.80s port: %d\n"
+ "Size: %d x %d\n"
+ "Pixel format: %s\n"
+ "(server default %s)\n"
+ "Requested encoding: %s\n"
+ "Last used encoding: %s\n"
+ "Line speed estimate: %d kbit/s\n"
+ "Protocol version: %d.%d\n"
+ "Security method: %s\n",
+ cp.name(), serverHost, serverPort, cp.width, cp.height,
+ pfStr, spfStr, encodingName(currentEncoding),
+ encodingName(lastServerEncoding),
+ sock->inStream().kbitsPerSecond(),
+ cp.majorVersion, cp.minorVersion,
+ secTypeName(secType));
+ info.setText(infoText);
+ info.show();
+ break;
+ }
+ case ID_FULLSCREEN:
+ menu.unmap();
+ fullScreen = !fullScreen;
+ if (viewport) recreateViewport();
+ break;
+ case ID_REFRESH:
+ menu.unmap();
+ writer()->writeFramebufferUpdateRequest(Rect(0, 0, cp.width, cp.height),
+ false);
+ break;
+ case ID_F8:
+ menu.unmap();
+ if (!viewOnly) {
+ writer()->writeKeyEvent(menuKeysym, true);
+ writer()->writeKeyEvent(menuKeysym, false);
+ }
+ break;
+ case ID_CTRLALTDEL:
+ menu.unmap();
+ if (!viewOnly) {
+ writer()->writeKeyEvent(XK_Control_L, true);
+ writer()->writeKeyEvent(XK_Alt_L, true);
+ writer()->writeKeyEvent(XK_Delete, true);
+ writer()->writeKeyEvent(XK_Delete, false);
+ writer()->writeKeyEvent(XK_Alt_L, false);
+ writer()->writeKeyEvent(XK_Control_L, false);
+ }
+ break;
+ case ID_CTRL:
+ menu.unmap();
+ if (!viewOnly) {
+ ctrlDown = !ctrlDown;
+ writer()->writeKeyEvent(XK_Control_L, ctrlDown);
+ menu.check(ID_CTRL, ctrlDown);
+ }
+ break;
+ case ID_ALT:
+ menu.unmap();
+ if (!viewOnly) {
+ altDown = !altDown;
+ writer()->writeKeyEvent(XK_Alt_L, altDown);
+ menu.check(ID_ALT, altDown);
+ }
+ break;
+ case ID_ABOUT:
+ menu.unmap();
+ about.show();
+ break;
+ case ID_DISMISS:
+ menu.unmap();
+ break;
+ case ID_EXIT:
+ exit(1);
+ break;
+ }
+}
+
+
+// OptionsDialogCallback. setOptions() sets the options dialog's checkboxes
+// etc to reflect our flags. getOptions() sets our flags according to the
+// options dialog's checkboxes.
+
+void CConn::setOptions() {
+ options.autoSelect.checked(autoSelect);
+ options.fullColour.checked(fullColour);
+ options.veryLowColour.checked(!fullColour && lowColourLevel == 0);
+ options.lowColour.checked(!fullColour && lowColourLevel == 1);
+ options.mediumColour.checked(!fullColour && lowColourLevel == 2);
+ options.zrle.checked(currentEncoding == encodingZRLE);
+ options.hextile.checked(currentEncoding == encodingHextile);
+ options.raw.checked(currentEncoding == encodingRaw);
+ options.viewOnly.checked(viewOnly);
+ options.acceptClipboard.checked(acceptClipboard);
+ options.sendClipboard.checked(sendClipboard);
+ options.sendPrimary.checked(sendPrimary);
+ if (state() == RFBSTATE_NORMAL)
+ options.shared.disabled(true);
+ else
+ options.shared.checked(shared);
+ options.fullScreen.checked(fullScreen);
+ options.useLocalCursor.checked(useLocalCursor);
+ options.dotWhenNoCursor.checked(dotWhenNoCursor);
+}
+
+void CConn::getOptions() {
+ autoSelect = options.autoSelect.checked();
+ if (fullColour != options.fullColour.checked())
+ formatChange = true;
+ fullColour = options.fullColour.checked();
+ if (!fullColour) {
+ int newLowColourLevel = (options.veryLowColour.checked() ? 0 :
+ options.lowColour.checked() ? 1 : 2);
+ if (newLowColourLevel != lowColourLevel) {
+ lowColourLevel.setParam(newLowColourLevel);
+ formatChange = true;
+ }
+ }
+ unsigned int newEncoding = (options.zrle.checked() ? encodingZRLE :
+ options.hextile.checked() ? encodingHextile :
+ encodingRaw);
+ if (newEncoding != currentEncoding) {
+ currentEncoding = newEncoding;
+ encodingChange = true;
+ }
+ viewOnly.setParam(options.viewOnly.checked());
+ acceptClipboard.setParam(options.acceptClipboard.checked());
+ sendClipboard.setParam(options.sendClipboard.checked());
+ sendPrimary.setParam(options.sendPrimary.checked());
+ shared = options.shared.checked();
+ setShared(shared);
+ if (fullScreen != options.fullScreen.checked()) {
+ fullScreen = options.fullScreen.checked();
+ if (viewport) recreateViewport();
+ }
+ useLocalCursor.setParam(options.useLocalCursor.checked());
+ if (cp.supportsLocalCursor != useLocalCursor) {
+ cp.supportsLocalCursor = useLocalCursor;
+ encodingChange = true;
+ if (desktop)
+ desktop->resetLocalCursor();
+ }
+ dotWhenNoCursor.setParam(options.dotWhenNoCursor.checked());
+ checkEncodings();
+}
+
+void CConn::recreateViewport()
+{
+ TXViewport* oldViewport = viewport;
+ viewport = new TXViewport(dpy, cp.width, cp.height);
+ desktop->setViewport(viewport);
+ CharArray windowNameStr(windowName.getData());
+ if (!windowNameStr.buf[0]) {
+ windowNameStr.replaceBuf(new char[256]);
+ sprintf(windowNameStr.buf,"VNC: %.240s",cp.name());
+ }
+ viewport->toplevel(windowNameStr.buf, this, argc, argv);
+ viewport->setBumpScroll(fullScreen);
+ XSetWindowAttributes attr;
+ attr.override_redirect = fullScreen;
+ XChangeWindowAttributes(dpy, viewport->win(), CWOverrideRedirect, &attr);
+ XChangeWindowAttributes(dpy, menu.win(), CWOverrideRedirect, &attr);
+ XChangeWindowAttributes(dpy, options.win(), CWOverrideRedirect, &attr);
+ XChangeWindowAttributes(dpy, about.win(), CWOverrideRedirect, &attr);
+ XChangeWindowAttributes(dpy, info.win(), CWOverrideRedirect, &attr);
+ reconfigureViewport();
+ menu.setTransientFor(viewport->win());
+ viewport->map();
+ if (fullScreen) {
+ XGrabKeyboard(dpy, desktop->win(), True, GrabModeAsync, GrabModeAsync,
+ CurrentTime);
+ } else {
+ XUngrabKeyboard(dpy, CurrentTime);
+ }
+ if (oldViewport) delete oldViewport;
+}
+
+void CConn::reconfigureViewport()
+{
+ viewport->setMaxSize(cp.width, cp.height);
+ if (fullScreen) {
+ viewport->resize(DisplayWidth(dpy,DefaultScreen(dpy)),
+ DisplayHeight(dpy,DefaultScreen(dpy)));
+ } else {
+ int w = cp.width;
+ int h = cp.height;
+ if (w + wmDecorationWidth >= DisplayWidth(dpy,DefaultScreen(dpy)))
+ w = DisplayWidth(dpy,DefaultScreen(dpy)) - wmDecorationWidth;
+ if (h + wmDecorationHeight >= DisplayHeight(dpy,DefaultScreen(dpy)))
+ h = DisplayHeight(dpy,DefaultScreen(dpy)) - wmDecorationHeight;
+
+ int x = (DisplayWidth(dpy,DefaultScreen(dpy)) - w - wmDecorationWidth) / 2;
+ int y = (DisplayHeight(dpy,DefaultScreen(dpy)) - h - wmDecorationHeight)/2;
+
+ CharArray geometryStr(geometry.getData());
+ viewport->setGeometry(geometryStr.buf, x, y, w, h);
+ }
+}
+
+// autoSelectFormatAndEncoding() chooses the format and encoding appropriate
+// to the connection speed:
+// Above 16Mbps (timing for at least a second), same machine, switch to raw
+// Above 3Mbps, switch to hextile
+// Below 1.5Mbps, switch to ZRLE
+// Above 1Mbps, switch to full colour mode
+void CConn::autoSelectFormatAndEncoding()
+{
+ int kbitsPerSecond = sock->inStream().kbitsPerSecond();
+ unsigned int newEncoding = currentEncoding;
+
+ if (kbitsPerSecond > 16000 && sameMachine &&
+ sock->inStream().timeWaited() >= 10000) {
+ newEncoding = encodingRaw;
+ } else if (kbitsPerSecond > 3000) {
+ newEncoding = encodingHextile;
+ } else if (kbitsPerSecond < 1500) {
+ newEncoding = encodingZRLE;
+ }
+
+ if (newEncoding != currentEncoding) {
+ vlog.info("Throughput %d kbit/s - changing to %s encoding",
+ kbitsPerSecond, encodingName(newEncoding));
+ currentEncoding = newEncoding;
+ encodingChange = true;
+ }
+
+ if (kbitsPerSecond > 1000) {
+ if (!fullColour) {
+ vlog.info("Throughput %d kbit/s - changing to full colour",
+ kbitsPerSecond);
+ fullColour = true;
+ formatChange = true;
+ }
+ }
+}
+
+// checkEncodings() sends a setEncodings message if one is needed.
+void CConn::checkEncodings()
+{
+ if (encodingChange && writer()) {
+ vlog.info("Using %s encoding",encodingName(currentEncoding));
+ writer()->writeSetEncodings(currentEncoding, true);
+ encodingChange = false;
+ }
+}
+
+// requestNewUpdate() requests an update from the server, having set the
+// format and encoding appropriately.
+void CConn::requestNewUpdate()
+{
+ if (formatChange) {
+ if (fullColour) {
+ desktop->setPF(fullColourPF);
+ } else {
+ if (lowColourLevel == 0)
+ desktop->setPF(PixelFormat(8,3,0,1,1,1,1,2,1,0));
+ else if (lowColourLevel == 1)
+ desktop->setPF(PixelFormat(8,6,0,1,3,3,3,4,2,0));
+ else
+ desktop->setPF(PixelFormat(8,8,0,0));
+ }
+ char str[256];
+ desktop->getPF().print(str, 256);
+ vlog.info("Using pixel format %s",str);
+ cp.setPF(desktop->getPF());
+ writer()->writeSetPixelFormat(cp.pf());
+ }
+ checkEncodings();
+ writer()->writeFramebufferUpdateRequest(Rect(0, 0, cp.width, cp.height),
+ !formatChange);
+ formatChange = false;
+}
diff --git a/vncviewer_unix/CConn.h b/vncviewer_unix/CConn.h
new file mode 100644
index 00000000..c95951bb
--- /dev/null
+++ b/vncviewer_unix/CConn.h
@@ -0,0 +1,129 @@
+/* Copyright (C) 2002-2004 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.
+ */
+//
+// CConn represents a client connection to a VNC server.
+//
+
+#ifndef __CCONN_H__
+#define __CCONN_H__
+
+#include <rfb/CConnection.h>
+#include <rfb/Exception.h>
+#include <rfb/UserPasswdGetter.h>
+#include <rdr/FdInStream.h>
+#include <list>
+
+#include "TXWindow.h"
+#include "AboutDialog.h"
+#include "InfoDialog.h"
+#include "TXMenu.h"
+#include "OptionsDialog.h"
+
+class TXWindow;
+class TXViewport;
+class DesktopWindow;
+namespace network { class Socket; }
+
+class CConn : public rfb::CConnection, public rfb::UserPasswdGetter,
+ public TXDeleteWindowCallback,
+ public rdr::FdInStreamBlockCallback,
+ public TXMenuCallback , public OptionsDialogCallback,
+ public TXEventHandler
+{
+public:
+
+ CConn(Display* dpy_, int argc_, char** argv_, network::Socket* sock_,
+ char* vncServerName);
+ ~CConn();
+
+ // TXDeleteWindowCallback methods
+ void deleteWindow(TXWindow* w);
+
+ // FdInStreamBlockCallback methods
+ void blockCallback();
+
+ // UserPasswdGetter methods
+ virtual bool getUserPasswd(char** user, char** password);
+
+ // TXMenuCallback methods
+ void menuSelect(long id, TXMenu* m);
+
+ // OptionsDialogCallback methods
+ virtual void setOptions();
+ virtual void getOptions();
+
+ // TXEventHandler callback method
+ virtual void handleEvent(TXWindow* w, XEvent* ev);
+
+ // CConnection callback methods
+ rfb::CSecurity* getCSecurity(int secType);
+ void serverInit();
+ void setDesktopSize(int w, int h);
+ void setColourMapEntries(int firstColour, int nColours, rdr::U16* rgbs);
+ void bell();
+ void serverCutText(const char* str, int len);
+ void framebufferUpdateEnd();
+ void beginRect(const rfb::Rect& r, unsigned int encoding);
+ void endRect(const rfb::Rect& r, unsigned int encoding);
+ void fillRect(const rfb::Rect& r, rfb::Pixel p);
+ void imageRect(const rfb::Rect& r, void* p);
+ void copyRect(const rfb::Rect& r, int sx, int sy);
+ void setCursor(const rfb::Point& hotspot, const rfb::Point& size,
+ void* data, void* mask);
+
+private:
+
+ void recreateViewport();
+ void reconfigureViewport();
+ void initMenu();
+ void showMenu(int x, int y);
+ void autoSelectFormatAndEncoding();
+ void checkEncodings();
+ void requestNewUpdate();
+
+ Display* dpy;
+ int argc;
+ char** argv;
+ char* serverHost;
+ int serverPort;
+ network::Socket* sock;
+ rfb::PixelFormat serverPF;
+ TXViewport* viewport;
+ DesktopWindow* desktop;
+ TXEventHandler* desktopEventHandler;
+ rfb::PixelFormat fullColourPF;
+ std::list<rfb::Rect> debugRects;
+ unsigned int currentEncoding, lastServerEncoding;
+ bool fullColour;
+ bool autoSelect;
+ bool shared;
+ bool formatChange;
+ bool encodingChange;
+ bool sameMachine;
+ bool fullScreen;
+ bool ctrlDown;
+ bool altDown;
+ KeySym menuKeysym;
+ TXMenu menu;
+ TXEventHandler* menuEventHandler;
+ OptionsDialog options;
+ AboutDialog about;
+ InfoDialog info;
+};
+
+#endif
diff --git a/vncviewer_unix/DesktopWindow.cxx b/vncviewer_unix/DesktopWindow.cxx
new file mode 100644
index 00000000..d0e4cb53
--- /dev/null
+++ b/vncviewer_unix/DesktopWindow.cxx
@@ -0,0 +1,573 @@
+/* Copyright (C) 2002-2004 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.
+ */
+//
+// DesktopWindow.cxx
+//
+
+#include "DesktopWindow.h"
+#include "CConn.h"
+#include <rfb/CMsgWriter.h>
+#include <rfb/LogWriter.h>
+#include <X11/keysym.h>
+#include <X11/Xatom.h>
+#include <stdio.h>
+#include <string.h>
+#include "parameters.h"
+
+#ifndef XK_ISO_Left_Tab
+#define XK_ISO_Left_Tab 0xFE20
+#endif
+
+static rdr::U8 reverseBits[] = {
+ 0x00, 0x80, 0x40, 0xc0, 0x20, 0xa0, 0x60, 0xe0, 0x10, 0x90, 0x50, 0xd0,
+ 0x30, 0xb0, 0x70, 0xf0, 0x08, 0x88, 0x48, 0xc8, 0x28, 0xa8, 0x68, 0xe8,
+ 0x18, 0x98, 0x58, 0xd8, 0x38, 0xb8, 0x78, 0xf8, 0x04, 0x84, 0x44, 0xc4,
+ 0x24, 0xa4, 0x64, 0xe4, 0x14, 0x94, 0x54, 0xd4, 0x34, 0xb4, 0x74, 0xf4,
+ 0x0c, 0x8c, 0x4c, 0xcc, 0x2c, 0xac, 0x6c, 0xec, 0x1c, 0x9c, 0x5c, 0xdc,
+ 0x3c, 0xbc, 0x7c, 0xfc, 0x02, 0x82, 0x42, 0xc2, 0x22, 0xa2, 0x62, 0xe2,
+ 0x12, 0x92, 0x52, 0xd2, 0x32, 0xb2, 0x72, 0xf2, 0x0a, 0x8a, 0x4a, 0xca,
+ 0x2a, 0xaa, 0x6a, 0xea, 0x1a, 0x9a, 0x5a, 0xda, 0x3a, 0xba, 0x7a, 0xfa,
+ 0x06, 0x86, 0x46, 0xc6, 0x26, 0xa6, 0x66, 0xe6, 0x16, 0x96, 0x56, 0xd6,
+ 0x36, 0xb6, 0x76, 0xf6, 0x0e, 0x8e, 0x4e, 0xce, 0x2e, 0xae, 0x6e, 0xee,
+ 0x1e, 0x9e, 0x5e, 0xde, 0x3e, 0xbe, 0x7e, 0xfe, 0x01, 0x81, 0x41, 0xc1,
+ 0x21, 0xa1, 0x61, 0xe1, 0x11, 0x91, 0x51, 0xd1, 0x31, 0xb1, 0x71, 0xf1,
+ 0x09, 0x89, 0x49, 0xc9, 0x29, 0xa9, 0x69, 0xe9, 0x19, 0x99, 0x59, 0xd9,
+ 0x39, 0xb9, 0x79, 0xf9, 0x05, 0x85, 0x45, 0xc5, 0x25, 0xa5, 0x65, 0xe5,
+ 0x15, 0x95, 0x55, 0xd5, 0x35, 0xb5, 0x75, 0xf5, 0x0d, 0x8d, 0x4d, 0xcd,
+ 0x2d, 0xad, 0x6d, 0xed, 0x1d, 0x9d, 0x5d, 0xdd, 0x3d, 0xbd, 0x7d, 0xfd,
+ 0x03, 0x83, 0x43, 0xc3, 0x23, 0xa3, 0x63, 0xe3, 0x13, 0x93, 0x53, 0xd3,
+ 0x33, 0xb3, 0x73, 0xf3, 0x0b, 0x8b, 0x4b, 0xcb, 0x2b, 0xab, 0x6b, 0xeb,
+ 0x1b, 0x9b, 0x5b, 0xdb, 0x3b, 0xbb, 0x7b, 0xfb, 0x07, 0x87, 0x47, 0xc7,
+ 0x27, 0xa7, 0x67, 0xe7, 0x17, 0x97, 0x57, 0xd7, 0x37, 0xb7, 0x77, 0xf7,
+ 0x0f, 0x8f, 0x4f, 0xcf, 0x2f, 0xaf, 0x6f, 0xef, 0x1f, 0x9f, 0x5f, 0xdf,
+ 0x3f, 0xbf, 0x7f, 0xff
+};
+
+using namespace rfb;
+
+static rfb::LogWriter vlog("DesktopWindow");
+
+DesktopWindow::DesktopWindow(Display* dpy, int w, int h,
+ const rfb::PixelFormat& serverPF,
+ CConn* cc_, TXWindow* parent)
+ : TXWindow(dpy, w, h, parent), cc(cc_), im(0),
+ cursorVisible(false), cursorAvailable(false), currentSelectionTime(0),
+ newSelection(0), gettingInitialSelectionTime(true),
+ newServerCutText(false), serverCutText_(0),
+ setColourMapEntriesTimer(this), viewport(0),
+ pointerEventTimer(this), lastPointerX(0), lastPointerY(0),
+ lastButtonMask(0)
+{
+ setEventHandler(this);
+ gc = XCreateGC(dpy, win(), 0, 0);
+ addEventMask(ExposureMask | ButtonPressMask | ButtonReleaseMask |
+ PointerMotionMask | KeyPressMask | KeyReleaseMask |
+ EnterWindowMask | LeaveWindowMask);
+ createXCursors();
+ XDefineCursor(dpy, win(), dotCursor);
+ im = new TXImage(dpy, width(), height());
+ if (!serverPF.trueColour)
+ im->setPF(serverPF);
+ XConvertSelection(dpy, sendPrimary ? XA_PRIMARY : xaCLIPBOARD, xaTIMESTAMP,
+ xaSELECTION_TIME, win(), CurrentTime);
+ memset(downKeysym, 0, 256*4);
+}
+
+DesktopWindow::~DesktopWindow()
+{
+ XFreeGC(dpy, gc);
+ XFreeCursor(dpy, dotCursor);
+ XFreeCursor(dpy, noCursor);
+ if (localXCursor)
+ XFreeCursor(dpy, localXCursor);
+ delete im;
+}
+
+void DesktopWindow::setViewport(TXViewport* viewport_)
+{
+ viewport = viewport_;
+ viewport->setChild(this);
+}
+
+// Cursor stuff
+
+void DesktopWindow::createXCursors()
+{
+ static char dotSource[] = { 0x00, 0x0e, 0x0e, 0x0e, 0x00 };
+ static char dotMask[] = { 0x1f, 0x1f, 0x1f, 0x1f, 0x1f };
+ Pixmap source = XCreateBitmapFromData(dpy, win(), dotSource, 5, 5);
+ Pixmap mask = XCreateBitmapFromData(dpy, win(), dotMask, 5, 5);
+ XColor fg, bg;
+ fg.red = fg.green = fg.blue = 0;
+ bg.red = bg.green = bg.blue = 0xffff;
+ dotCursor = XCreatePixmapCursor(dpy, source, mask, &fg, &bg, 2, 2);
+ XFreePixmap(dpy, source);
+ XFreePixmap(dpy, mask);
+ char zero = 0;
+ Pixmap empty = XCreateBitmapFromData(dpy, win(), &zero, 1, 1);
+ noCursor = XCreatePixmapCursor(dpy, empty, empty, &fg, &bg, 0, 0);
+ XFreePixmap(dpy, empty);
+ localXCursor = 0;
+}
+
+void DesktopWindow::setCursor(const Point& hotspot, const Point& size,
+ void* data, void* mask)
+{
+ if (!useLocalCursor) return;
+
+ hideLocalCursor();
+
+ int mask_len = ((size.x+7)/8) * size.y;
+
+ int i;
+ for (i = 0; i < mask_len; i++)
+ if (((rdr::U8*)mask)[i]) break;
+
+ if (i == mask_len) {
+ if (dotWhenNoCursor) {
+ vlog.debug("cursor is empty - using dot");
+ XDefineCursor(dpy, win(), dotCursor);
+ } else {
+ XDefineCursor(dpy, win(), noCursor);
+ }
+ cursorAvailable = false;
+ return;
+ }
+
+ cursor.hotspot = hotspot;
+
+ cursor.setSize(size.x, size.y);
+ cursor.setPF(getPF());
+ cursor.imageRect(cursor.getRect(), data);
+
+ cursorBacking.setSize(size.x, size.y);
+ cursorBacking.setPF(getPF());
+
+ delete [] cursor.mask.buf;
+ cursor.mask.buf = new rdr::U8[mask_len];
+ memcpy(cursor.mask.buf, mask, mask_len);
+
+ Pixel pix0, pix1;
+ rdr::U8Array bitmap(cursor.getBitmap(&pix0, &pix1));
+ if (bitmap.buf && cursor.getRect().contains(cursor.hotspot)) {
+ int bytesPerRow = (cursor.width() + 7) / 8;
+ for (int j = 0; j < cursor.height(); j++) {
+ for (int i = 0; i < bytesPerRow; i++) {
+ bitmap.buf[j * bytesPerRow + i]
+ = reverseBits[bitmap.buf[j * bytesPerRow + i]];
+ cursor.mask.buf[j * bytesPerRow + i]
+ = reverseBits[cursor.mask.buf[j * bytesPerRow + i]];
+ }
+ }
+ Pixmap source = XCreateBitmapFromData(dpy, win(), (char*)bitmap.buf,
+ cursor.width(), cursor.height());
+ Pixmap mask = XCreateBitmapFromData(dpy, win(), (char*)cursor.mask.buf,
+ cursor.width(), cursor.height());
+ Colour rgb;
+ XColor fg, bg;
+ getPF().rgbFromPixel(pix1, im->getColourMap(), &rgb);
+ fg.red = rgb.r; fg.green = rgb.g; fg.blue = rgb.b;
+ getPF().rgbFromPixel(pix0, im->getColourMap(), &rgb);
+ bg.red = rgb.r; bg.green = rgb.g; bg.blue = rgb.b;
+ if (localXCursor)
+ XFreeCursor(dpy, localXCursor);
+ localXCursor = XCreatePixmapCursor(dpy, source, mask, &fg, &bg,
+ cursor.hotspot.x, cursor.hotspot.y);
+ XDefineCursor(dpy, win(), localXCursor);
+ XFreePixmap(dpy, source);
+ XFreePixmap(dpy, mask);
+ cursorAvailable = false;
+ return;
+ }
+
+ if (!cursorAvailable) {
+ XDefineCursor(dpy, win(), noCursor);
+ cursorAvailable = true;
+ }
+
+ showLocalCursor();
+}
+
+void DesktopWindow::resetLocalCursor()
+{
+ hideLocalCursor();
+ XDefineCursor(dpy, win(), dotCursor);
+ cursorAvailable = false;
+}
+
+void DesktopWindow::hideLocalCursor()
+{
+ // - Blit the cursor backing store over the cursor
+ if (cursorVisible) {
+ cursorVisible = false;
+ im->imageRect(cursorBackingRect, cursorBacking.data);
+ im->put(win(), gc, cursorBackingRect);
+ }
+}
+
+void DesktopWindow::showLocalCursor()
+{
+ if (cursorAvailable && !cursorVisible) {
+ if (!getPF().equal(cursor.getPF()) ||
+ cursor.getRect().is_empty()) {
+ vlog.error("attempting to render invalid local cursor");
+ XDefineCursor(dpy, win(), dotCursor);
+ cursorAvailable = false;
+ return;
+ }
+ cursorVisible = true;
+
+ rfb::Rect cursorRect = (cursor.getRect().translate(cursorPos).
+ translate(cursor.hotspot.negate()));
+ cursorBackingRect = cursorRect.intersect(im->getRect());
+ im->getImage(cursorBacking.data, cursorBackingRect);
+
+ im->maskRect(cursorRect, cursor.data, cursor.mask.buf);
+ im->put(win(), gc, cursorBackingRect);
+ }
+}
+
+// setColourMapEntries() changes some of the entries in the colourmap.
+// Unfortunately these messages are often sent one at a time, so we delay the
+// settings taking effect by 100ms. This is because recalculating the internal
+// translation table can be expensive.
+void DesktopWindow::setColourMapEntries(int firstColour, int nColours,
+ rdr::U16* rgbs)
+{
+ im->setColourMapEntries(firstColour, nColours, rgbs);
+ if (!setColourMapEntriesTimer.isSet())
+ setColourMapEntriesTimer.reset(100);
+}
+
+void DesktopWindow::serverCutText(const char* str, int len)
+{
+ if (acceptClipboard) {
+ newServerCutText = true;
+ delete [] serverCutText_;
+ serverCutText_ = new char[len+1];
+ memcpy(serverCutText_, str, len);
+ serverCutText_[len] = 0;
+ }
+}
+
+
+// Call XSync() at the end of an update. We do this because we'd like to
+// ensure that the current update has actually been drawn by the X server
+// before the next update arrives - this is necessary for copyRect to
+// behave correctly. In particular, if part of the source of a copyRect is
+// not actually displayed in the window, then XCopyArea results in
+// GraphicsExpose events, which require us to draw from the off-screen
+// image. By the time XSync returns, the GraphicsExpose events will be in
+// Xlib's queue, so hopefully will be processed before the next update.
+// Possibly we should process the GraphicsExpose events here explicitly?
+
+void DesktopWindow::framebufferUpdateEnd()
+{
+ XSync(dpy, False);
+}
+
+
+// invertRect() flips all the bits in every pixel in the given rectangle
+
+void DesktopWindow::invertRect(const Rect& r)
+{
+ int stride;
+ rdr::U8* p = im->getPixelsRW(r, &stride);
+ for (int y = 0; y < r.height(); y++) {
+ for (int x = 0; x < r.width(); x++) {
+ switch (getPF().bpp) {
+ case 8: ((rdr::U8* )p)[x+y*stride] ^= 0xff; break;
+ case 16: ((rdr::U16*)p)[x+y*stride] ^= 0xffff; break;
+ case 32: ((rdr::U32*)p)[x+y*stride] ^= 0xffffffff; break;
+ }
+ }
+ }
+ im->put(win(), gc, r);
+}
+
+
+// resize() - resize the window and the image, taking care to remove the local
+// cursor first.
+void DesktopWindow::resize(int w, int h)
+{
+ hideLocalCursor();
+ TXWindow::resize(w, h);
+ im->resize(w, h);
+}
+
+
+void DesktopWindow::timerCallback(Timer* timer)
+{
+ if (timer == &setColourMapEntriesTimer) {
+ im->updateColourMap();
+ im->put(win(), gc, im->getRect());
+ } else if (timer == &pointerEventTimer) {
+ if (!viewOnly) {
+ cc->writer()->writePointerEvent(lastPointerX, lastPointerY,
+ lastButtonMask);
+ }
+ }
+}
+
+
+void DesktopWindow::handlePointerEvent(int x, int y, int buttonMask)
+{
+ if (!viewOnly) {
+ if (pointerEventInterval == 0 || buttonMask != lastButtonMask) {
+ cc->writer()->writePointerEvent(x, y, buttonMask);
+ } else {
+ if (!pointerEventTimer.isSet())
+ pointerEventTimer.reset(pointerEventInterval);
+ }
+ lastPointerX = x;
+ lastPointerY = y;
+ lastButtonMask = buttonMask;
+ }
+ // - If local cursor rendering is enabled then use it
+ if (cursorAvailable) {
+ // - Render the cursor!
+ Point p(x, y);
+ if (!p.equals(cursorPos)) {
+ hideLocalCursor();
+ if (im->getRect().contains(p)) {
+ cursorPos = p;
+ showLocalCursor();
+ }
+ }
+ }
+}
+
+
+// handleXEvent() handles the various X events on the window
+void DesktopWindow::handleEvent(TXWindow* w, XEvent* ev)
+{
+ switch (ev->type) {
+ case GraphicsExpose:
+ case Expose:
+ im->put(win(), gc, Rect(ev->xexpose.x, ev->xexpose.y,
+ ev->xexpose.x + ev->xexpose.width,
+ ev->xexpose.y + ev->xexpose.height));
+ break;
+
+ case MotionNotify:
+ while (XCheckTypedWindowEvent(dpy, win(), MotionNotify, ev));
+ if (viewport && viewport->bumpScrollEvent(&ev->xmotion)) break;
+ handlePointerEvent(ev->xmotion.x, ev->xmotion.y,
+ (ev->xmotion.state & 0x1f00) >> 8);
+ break;
+
+ case ButtonPress:
+ handlePointerEvent(ev->xbutton.x, ev->xbutton.y,
+ (((ev->xbutton.state & 0x1f00) >> 8) |
+ (1 << (ev->xbutton.button-1))));
+ break;
+
+ case ButtonRelease:
+ handlePointerEvent(ev->xbutton.x, ev->xbutton.y,
+ (((ev->xbutton.state & 0x1f00) >> 8) &
+ ~(1 << (ev->xbutton.button-1))));
+ break;
+
+ case KeyPress:
+ if (!viewOnly) {
+ KeySym ks;
+ char keyname[256];
+ XLookupString(&ev->xkey, keyname, 256, &ks, NULL);
+ bool fakeShiftPress = false;
+
+ // Turn ISO_Left_Tab into shifted Tab
+ if (ks == XK_ISO_Left_Tab) {
+ fakeShiftPress = !(ev->xkey.state & ShiftMask);
+ ks = XK_Tab;
+ }
+
+ if (fakeShiftPress)
+ cc->writer()->writeKeyEvent(XK_Shift_L, true);
+
+ downKeysym[ev->xkey.keycode] = ks;
+ cc->writer()->writeKeyEvent(ks, true);
+
+ if (fakeShiftPress)
+ cc->writer()->writeKeyEvent(XK_Shift_L, false);
+ break;
+ }
+
+ case KeyRelease:
+ if (!viewOnly) {
+ if (downKeysym[ev->xkey.keycode]) {
+ cc->writer()->writeKeyEvent(downKeysym[ev->xkey.keycode], false);
+ downKeysym[ev->xkey.keycode] = 0;
+ }
+ }
+ break;
+
+ case EnterNotify:
+ newSelection = 0;
+ if (sendPrimary && !selectionOwner(XA_PRIMARY)) {
+ XConvertSelection(dpy, XA_PRIMARY, xaTIMESTAMP, xaSELECTION_TIME,
+ win(), ev->xcrossing.time);
+ } else if (!selectionOwner(xaCLIPBOARD)) {
+ XConvertSelection(dpy, xaCLIPBOARD, xaTIMESTAMP, xaSELECTION_TIME,
+ win(), ev->xcrossing.time);
+ }
+ break;
+
+ case LeaveNotify:
+ if (serverCutText_ && newServerCutText) {
+ newServerCutText = false;
+ vlog.debug("acquiring primary and clipboard selections");
+ XStoreBytes(dpy, serverCutText_, strlen(serverCutText_));
+ ownSelection(XA_PRIMARY, ev->xcrossing.time);
+ ownSelection(xaCLIPBOARD, ev->xcrossing.time);
+ currentSelectionTime = ev->xcrossing.time;
+ }
+ // Release all keys - this should probably done on a FocusOut event, but
+ // LeaveNotify is near enough...
+ for (int i = 8; i < 256; i++) {
+ if (downKeysym[i]) {
+ cc->writer()->writeKeyEvent(downKeysym[i], false);
+ downKeysym[i] = 0;
+ }
+ }
+ break;
+ }
+}
+
+// selectionRequest() is called when we are the selection owner and another X
+// client has requested the selection. We simply put the server's cut text
+// into the requested property. TXWindow will handle the rest.
+bool DesktopWindow::selectionRequest(Window requestor,
+ Atom selection, Atom property)
+{
+ XChangeProperty(dpy, requestor, property, XA_STRING, 8,
+ PropModeReplace, (unsigned char*)serverCutText_,
+ strlen(serverCutText_));
+ return true;
+}
+
+
+// selectionNotify() is called when we have requested any information about a
+// selection from the selection owner. Note that there are two selections,
+// PRIMARY and CLIPBOARD, plus the cut buffer, and we try to use whichever is
+// the most recent of the three.
+//
+// There are two different "targets" for which selectionNotify() is called, the
+// timestamp and the actual string value of the selection. We always use the
+// timestamp to decide which selection to retrieve.
+//
+// The first time selectionNotify() is called is when we are trying to find the
+// timestamp of the selections at initialisation. This should be called first
+// for PRIMARY, then we call XConvertSelection() for CLIPBOARD. The second
+// time should be the result for CLIPBOARD. At this stage we've got the
+// "currentSelectionTime" so we return.
+//
+// Subsequently selectionNotify() is called whenever the mouse enters the
+// viewer window. Again, the first time it is called should be the timestamp
+// for PRIMARY, and we then request the timestamp for CLIPBOARD. When
+// selectionNotify() is called again with the timestamp for CLIPBOARD, we now
+// know if either selection is "new" i.e. later than the previous value of
+// currentSelectionTime. The last thing to check is the timestamp on the cut
+// buffer. If the cut buffer is newest we send that to the server, otherwise
+// if one of the selections was newer, we request the string value of that
+// selection.
+//
+// Finally, if we get selectionNotify() called for the string value of a
+// selection, we sent that to the server.
+//
+// As a final minor complication, when one of the selections is actually owned
+// by us, we don't request the details for it.
+
+// TIME_LATER treats 0 as meaning a long time ago, so a==0 means a cannot be
+// later than b. This is different to the usual meaning of CurrentTime.
+#define TIME_LATER(a, b) ((a) != 0 && ((b) == 0 || (long)((a) - (b)) > 0))
+
+
+void DesktopWindow::selectionNotify(XSelectionEvent* ev, Atom type, int format,
+ int nitems, void* data)
+{
+ if (ev->requestor != win())
+ return;
+
+ if (ev->target == xaTIMESTAMP) {
+ if (ev->property == xaSELECTION_TIME) {
+ if (data && format == 32 && nitems == 1) {
+ Time t = *(rdr::U32 *)data;
+ vlog.debug("selection (%d) time is %d, later %d",
+ ev->selection, t, TIME_LATER(t, currentSelectionTime));
+ if (TIME_LATER(t, currentSelectionTime)) {
+ currentSelectionTime = t;
+ newSelection = ev->selection;
+ }
+ }
+ } else {
+ vlog.debug("no selection (%d)",ev->selection);
+ }
+
+ if (ev->selection == XA_PRIMARY) {
+ if (!selectionOwner(xaCLIPBOARD)) {
+ XConvertSelection(dpy, xaCLIPBOARD, xaTIMESTAMP, xaSELECTION_TIME,
+ win(), ev->time);
+ return;
+ }
+ } else if (ev->selection != xaCLIPBOARD) {
+ vlog.error("unknown selection %d",ev->selection);
+ return;
+ }
+
+ if (gettingInitialSelectionTime) {
+ gettingInitialSelectionTime = false;
+ return;
+ }
+
+ if (!sendClipboard) return;
+ if (sendPrimary) {
+ vlog.debug("cut buffer time is %d, later %d", cutBufferTime,
+ TIME_LATER(cutBufferTime, currentSelectionTime));
+ if (TIME_LATER(cutBufferTime, currentSelectionTime)) {
+ currentSelectionTime = cutBufferTime;
+ int len;
+ char* str = XFetchBytes(dpy, &len);
+ if (str) {
+ if (!viewOnly) {
+ vlog.debug("sending cut buffer to server");
+ cc->writer()->writeClientCutText(str, len);
+ }
+ XFree(str);
+ return;
+ }
+ }
+ }
+ if (newSelection) {
+ XConvertSelection(dpy, newSelection, XA_STRING, xaSELECTION_STRING,
+ win(), CurrentTime);
+ }
+
+ } else if (ev->target == XA_STRING) {
+ if (!sendClipboard) return;
+ if (ev->property == xaSELECTION_STRING) {
+ if (data && format == 8) {
+ if (!viewOnly) {
+ vlog.debug("sending %s selection to server",
+ ev->selection == XA_PRIMARY ? "primary" :
+ ev->selection == xaCLIPBOARD ? "clipboard" : "unknown" );
+ cc->writer()->writeClientCutText((char*)data, nitems);
+ }
+ }
+ }
+ }
+}
diff --git a/vncviewer_unix/DesktopWindow.h b/vncviewer_unix/DesktopWindow.h
new file mode 100644
index 00000000..9274f9d3
--- /dev/null
+++ b/vncviewer_unix/DesktopWindow.h
@@ -0,0 +1,128 @@
+/* Copyright (C) 2002-2004 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.
+ */
+//
+// DesktopWindow is a TXWindow representing a VNC desktop.
+//
+
+#ifndef __DESKTOPWINDOW_H__
+#define __DESKTOPWINDOW_H__
+
+#include <rfb/Cursor.h>
+#include <rfb/Rect.h>
+#include "TXWindow.h"
+#include "TXViewport.h"
+#include "TXImage.h"
+#include "Timer.h"
+
+class CConn;
+
+class DesktopWindow : public TXWindow, public TXEventHandler,
+ public TimerCallback {
+public:
+
+ DesktopWindow(Display* dpy, int w, int h,
+ const rfb::PixelFormat& serverPF, CConn* cc_,
+ TXWindow* parent=0);
+ ~DesktopWindow();
+
+ void setViewport(TXViewport* viewport);
+
+ // getPF() and setPF() get and set the TXImage's pixel format
+ const rfb::PixelFormat& getPF() { return im->getPF(); }
+ void setPF(const rfb::PixelFormat& pf) { im->setPF(pf); }
+
+ // setCursor() sets the shape of the local cursor
+ void setCursor(const rfb::Point& hotspot, const rfb::Point& size,
+ void* data, void* mask);
+
+ // resetLocalCursor() stops the rendering of the local cursor
+ void resetLocalCursor();
+
+ // Methods forwarded from CConn
+ void setColourMapEntries(int firstColour, int nColours, rdr::U16* rgbs);
+ void serverCutText(const char* str, int len);
+ void framebufferUpdateEnd();
+
+ void fillRect(const rfb::Rect& r, rfb::Pixel pix) {
+ if (r.overlaps(cursorBackingRect)) hideLocalCursor();
+ im->fillRect(r, pix);
+ im->put(win(), gc, r);
+ showLocalCursor();
+ }
+ void imageRect(const rfb::Rect& r, void* pixels) {
+ if (r.overlaps(cursorBackingRect)) hideLocalCursor();
+ im->imageRect(r, pixels);
+ im->put(win(), gc, r);
+ showLocalCursor();
+ }
+ void copyRect(const rfb::Rect& r, int srcX, int srcY) {
+ if (r.overlaps(cursorBackingRect) ||
+ cursorBackingRect.overlaps(rfb::Rect(srcX, srcY,
+ srcX+r.width(), srcY+r.height())))
+ hideLocalCursor();
+ if (im->usingShm())
+ XSync(dpy, False);
+ im->copyRect(r, rfb::Point(r.tl.x-srcX, r.tl.y-srcY));
+ XCopyArea(dpy, win(), win(), gc, srcX, srcY,
+ r.width(), r.height(), r.tl.x, r.tl.y);
+ showLocalCursor();
+ }
+ void invertRect(const rfb::Rect& r);
+
+ // TXWindow methods
+ virtual void resize(int w, int h);
+ virtual bool selectionRequest(Window requestor,
+ Atom selection, Atom property);
+ virtual void selectionNotify(XSelectionEvent* ev, Atom type, int format,
+ int nitems, void* data);
+ virtual void handleEvent(TXWindow* w, XEvent* ev);
+
+private:
+
+ void createXCursors();
+ void hideLocalCursor();
+ void showLocalCursor();
+ void timerCallback(Timer* timer);
+ void handlePointerEvent(int x, int y, int buttonMask);
+
+ CConn* cc;
+ TXImage* im;
+ GC gc;
+ ::Cursor dotCursor, noCursor, localXCursor;
+
+ rfb::Cursor cursor;
+ bool cursorVisible; // Is cursor currently rendered?
+ bool cursorAvailable; // Is cursor available for rendering?
+ rfb::Point cursorPos;
+ rfb::ManagedPixelBuffer cursorBacking;
+ rfb::Rect cursorBackingRect;
+
+ Time currentSelectionTime;
+ Atom newSelection;
+ bool gettingInitialSelectionTime;
+ bool newServerCutText;
+ char* serverCutText_;
+
+ Timer setColourMapEntriesTimer;
+ TXViewport* viewport;
+ Timer pointerEventTimer;
+ int lastPointerX, lastPointerY, lastButtonMask;
+ rdr::U32 downKeysym[256];
+};
+
+#endif
diff --git a/vncviewer_unix/InfoDialog.h b/vncviewer_unix/InfoDialog.h
new file mode 100644
index 00000000..b228bfcb
--- /dev/null
+++ b/vncviewer_unix/InfoDialog.h
@@ -0,0 +1,60 @@
+/* Copyright (C) 2002-2003 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.
+ */
+//
+// InfoDialog.h
+//
+
+#ifndef __INFODIALOG_H__
+#define __INFODIALOG_H__
+
+#include "TXDialog.h"
+#include "TXLabel.h"
+#include "TXButton.h"
+
+extern char buildtime[];
+
+class InfoDialog : public TXDialog, public TXButtonCallback {
+public:
+ InfoDialog(Display* dpy)
+ : TXDialog(dpy, 1, 1, "VNC connection info"),
+ infoLabel(dpy, "", this, 1, 1, TXLabel::left),
+ okButton(dpy, "OK", this, this, 60)
+ {
+ infoLabel.xPad = 8;
+ infoLabel.move(0, yPad*4);
+ setBorderWidth(1);
+ }
+
+ void setText(char* infoText) {
+ infoLabel.setText(infoText);
+ resize(infoLabel.width(),
+ infoLabel.height() + okButton.height() + yPad*12);
+
+ okButton.move((width() - okButton.width()) / 2,
+ height() - yPad*4 - okButton.height());
+ }
+
+ virtual void buttonActivate(TXButton* b) {
+ unmap();
+ }
+
+ TXLabel infoLabel;
+ TXButton okButton;
+};
+
+#endif
diff --git a/vncviewer_unix/Makefile.in b/vncviewer_unix/Makefile.in
new file mode 100644
index 00000000..a49dacc9
--- /dev/null
+++ b/vncviewer_unix/Makefile.in
@@ -0,0 +1,23 @@
+
+SRCS = DesktopWindow.cxx CConn.cxx vncviewer.cxx
+
+OBJS = $(SRCS:.cxx=.o)
+
+program = vncviewer
+
+DEP_LIBS = ../tx/libtx.a ../rfb/librfb.a ../network/libnetwork.a \
+ ../rdr/librdr.a
+
+EXTRA_LIBS = @ZLIB_LIB@ @X_PRE_LIBS@ @X_LIBS@ -lXext -lX11 @X_EXTRA_LIBS@
+
+DIR_CPPFLAGS = -I$(top_srcdir) -I$(top_srcdir)/tx @X_CFLAGS@ # X_CFLAGS are really CPPFLAGS
+
+all:: $(program)
+
+$(program): $(OBJS) buildtime.o $(DEP_LIBS)
+ rm -f $(program)
+ $(CXXLD) $(CXXFLAGS) $(LDFLAGS) -o $@ $(OBJS) buildtime.o $(DEP_LIBS) $(LIBS) $(EXTRA_LIBS)
+
+buildtime.o: $(OBJS) $(DEP_LIBS)
+
+# followed by boilerplate.mk
diff --git a/vncviewer_unix/OptionsDialog.h b/vncviewer_unix/OptionsDialog.h
new file mode 100644
index 00000000..96268cc7
--- /dev/null
+++ b/vncviewer_unix/OptionsDialog.h
@@ -0,0 +1,168 @@
+/* Copyright (C) 2002-2003 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.
+ */
+//
+// OptionsDialog.h
+//
+
+#ifndef __OPTIONSDIALOG_H__
+#define __OPTIONSDIALOG_H__
+
+#include "TXDialog.h"
+#include "TXLabel.h"
+#include "TXEntry.h"
+#include "TXButton.h"
+#include "TXCheckbox.h"
+#include "parameters.h"
+
+class OptionsDialogCallback {
+public:
+ virtual void setOptions() = 0;
+ virtual void getOptions() = 0;
+};
+
+class OptionsDialog : public TXDialog, public TXButtonCallback,
+ public TXCheckboxCallback {
+public:
+ OptionsDialog(Display* dpy, OptionsDialogCallback* cb_)
+ : TXDialog(dpy, 400, 400, "VNC Viewer: Connection Options"), cb(cb_),
+ formatAndEnc(dpy, "Encoding and Colour Level:", this),
+ inputs(dpy, "Inputs:", this),
+ misc(dpy, "Misc:", this),
+ autoSelect(dpy, "Auto select", this, false, this),
+ fullColour(dpy, "Full (all available colours)", this, true, this),
+ mediumColour(dpy, "Medium (256 colours)", this, true, this),
+ lowColour(dpy, "Low (64 colours)", this, true, this),
+ veryLowColour(dpy, "Very low (8 colours)", this, true, this),
+ zrle(dpy, "ZRLE", this, true, this),
+ hextile(dpy, "Hextile", this, true, this),
+ raw(dpy, "Raw", this, true, this),
+ viewOnly(dpy, "View only (ignore mouse & keyboard)", this, false, this),
+ acceptClipboard(dpy, "Accept clipboard from server", this, false, this),
+ sendClipboard(dpy, "Send clipboard to server", this, false, this),
+ sendPrimary(dpy, "Send primary selection & cut buffer as clipboard",
+ this, false, this),
+ shared(dpy, "Shared (don't disconnect other viewers)", this, false,this),
+ fullScreen(dpy, "Full-screen mode", this, false, this),
+ useLocalCursor(dpy, "Render cursor locally", this, false, this),
+ dotWhenNoCursor(dpy, "Show dot when no cursor", this, false, this),
+ okButton(dpy, "OK", this, this, 60),
+ cancelButton(dpy, "Cancel", this, this, 60)
+ {
+ int y = yPad;
+ formatAndEnc.move(xPad, y);
+ y += formatAndEnc.height();
+ autoSelect.move(xPad, y);
+ int x2 = xPad + autoSelect.width() + xPad*5;
+ fullColour.move(x2, y);
+ y += autoSelect.height();
+ zrle.move(xPad, y);
+ mediumColour.move(x2, y);
+ y += zrle.height();
+ hextile.move(xPad, y);
+ lowColour.move(x2, y);
+ y += hextile.height();
+ raw.move(xPad, y);
+ veryLowColour.move(x2, y);
+ y += raw.height();
+
+ y += yPad*4;
+ inputs.move(xPad, y);
+ y += inputs.height();
+ viewOnly.move(xPad, y);
+ y += viewOnly.height();
+ acceptClipboard.move(xPad, y);
+ y += acceptClipboard.height();
+ sendClipboard.move(xPad, y);
+ y += sendClipboard.height();
+ sendPrimary.move(xPad, y);
+ y += sendPrimary.height();
+
+ y += yPad*4;
+ misc.move(xPad, y);
+ y += misc.height();
+ shared.move(xPad, y);
+ y += shared.height();
+ fullScreen.move(xPad, y);
+ y += fullScreen.height();
+ useLocalCursor.move(xPad, y);
+ y += useLocalCursor.height();
+ dotWhenNoCursor.move(xPad, y);
+ y += dotWhenNoCursor.height();
+
+ okButton.move(width() - xPad*12 - cancelButton.width() - okButton.width(),
+ height() - yPad*4 - okButton.height());
+ cancelButton.move(width() - xPad*6 - cancelButton.width(),
+ height() - yPad*4 - cancelButton.height());
+ setBorderWidth(1);
+ }
+
+ virtual void initDialog() {
+ if (cb) cb->setOptions();
+ zrle.disabled(autoSelect.checked());
+ hextile.disabled(autoSelect.checked());
+ raw.disabled(autoSelect.checked());
+ sendPrimary.disabled(!sendClipboard.checked());
+ dotWhenNoCursor.disabled(!useLocalCursor.checked());
+ }
+
+ virtual void takeFocus(Time time) {
+ //XSetInputFocus(dpy, entry.win, RevertToParent, time);
+ }
+
+ virtual void buttonActivate(TXButton* b) {
+ if (b == &okButton) {
+ if (cb) cb->getOptions();
+ unmap();
+ } else if (b == &cancelButton) {
+ unmap();
+ }
+ }
+
+ virtual void checkboxSelect(TXCheckbox* checkbox) {
+ if (checkbox == &autoSelect) {
+ zrle.disabled(autoSelect.checked());
+ hextile.disabled(autoSelect.checked());
+ raw.disabled(autoSelect.checked());
+ } else if (checkbox == &fullColour || checkbox == &mediumColour ||
+ checkbox == &lowColour || checkbox == &veryLowColour) {
+ fullColour.checked(checkbox == &fullColour);
+ mediumColour.checked(checkbox == &mediumColour);
+ lowColour.checked(checkbox == &lowColour);
+ veryLowColour.checked(checkbox == &veryLowColour);
+ } else if (checkbox == &zrle || checkbox == &hextile || checkbox == &raw) {
+ zrle.checked(checkbox == &zrle);
+ hextile.checked(checkbox == &hextile);
+ raw.checked(checkbox == &raw);
+ } else if (checkbox == &sendClipboard) {
+ sendPrimary.disabled(!sendClipboard.checked());
+ } else if (checkbox == &useLocalCursor) {
+ dotWhenNoCursor.disabled(!useLocalCursor.checked());
+ }
+ }
+
+ OptionsDialogCallback* cb;
+ TXLabel formatAndEnc, inputs, misc;
+ TXCheckbox autoSelect;
+ TXCheckbox fullColour, mediumColour, lowColour, veryLowColour;
+ TXCheckbox zrle, hextile, raw;
+ TXCheckbox viewOnly, acceptClipboard, sendClipboard, sendPrimary;
+ TXCheckbox shared, fullScreen, useLocalCursor, dotWhenNoCursor;
+ TXButton okButton, cancelButton;
+};
+
+#endif
diff --git a/vncviewer_unix/PasswdDialog.h b/vncviewer_unix/PasswdDialog.h
new file mode 100644
index 00000000..3c1ee8dc
--- /dev/null
+++ b/vncviewer_unix/PasswdDialog.h
@@ -0,0 +1,72 @@
+/* Copyright (C) 2002-2003 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.
+ */
+//
+// PasswdDialog.h
+//
+
+#ifndef __PASSWDDIALOG_H__
+#define __PASSWDDIALOG_H__
+
+#include "TXDialog.h"
+#include "TXLabel.h"
+#include "TXEntry.h"
+
+class PasswdDialog : public TXDialog, public TXEntryCallback {
+public:
+ PasswdDialog(Display* dpy, const char* title, bool userDisabled)
+ : TXDialog(dpy, 320, 100, title, true),
+ userLabel(dpy, "Username:", this, 120),
+ userEntry(dpy, this, this, false, 180),
+ passwdLabel(dpy, "Password:", this, 120),
+ passwdEntry(dpy, this, this, true, 180)
+ {
+ userLabel.move(0, 20);
+ userEntry.move(userLabel.width(), 18);
+ userEntry.disabled(userDisabled);
+ passwdLabel.move(0, 60);
+ passwdEntry.move(passwdLabel.width(), 58);
+ }
+
+ void takeFocus(Time time) {
+ if (!userEntry.disabled())
+ XSetInputFocus(dpy, userEntry.win(), RevertToParent, time);
+ else
+ XSetInputFocus(dpy, passwdEntry.win(), RevertToParent, time);
+ }
+
+ void entryCallback(TXEntry* e, Detail detail, Time time) {
+ if (e == &userEntry) {
+ if (detail == ENTER || detail == NEXT_FOCUS || detail == PREV_FOCUS)
+ XSetInputFocus(dpy, passwdEntry.win(), RevertToParent, time);
+ } else if (e == &passwdEntry) {
+ if (detail == ENTER) {
+ ok = true;
+ done = true;
+ } else if (detail == NEXT_FOCUS || detail == PREV_FOCUS) {
+ XSetInputFocus(dpy, userEntry.win(), RevertToParent, time);
+ }
+ }
+ }
+
+ TXLabel userLabel;
+ TXEntry userEntry;
+ TXLabel passwdLabel;
+ TXEntry passwdEntry;
+};
+
+#endif
diff --git a/vncviewer_unix/ServerDialog.h b/vncviewer_unix/ServerDialog.h
new file mode 100644
index 00000000..245743f8
--- /dev/null
+++ b/vncviewer_unix/ServerDialog.h
@@ -0,0 +1,91 @@
+/* Copyright (C) 2002-2003 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.
+ */
+//
+// ServerDialog.h
+//
+
+#ifndef __SERVERDIALOG_H__
+#define __SERVERDIALOG_H__
+
+#include "TXDialog.h"
+#include "TXLabel.h"
+#include "TXEntry.h"
+#include "TXButton.h"
+#include "OptionsDialog.h"
+#include "AboutDialog.h"
+
+class ServerDialog : public TXDialog, public TXEntryCallback,
+ public TXButtonCallback {
+public:
+ ServerDialog(Display* dpy, OptionsDialog* options_, AboutDialog* about_)
+ : TXDialog(dpy, 332, 120, "VNC Viewer: Connection Details", true),
+ label(dpy, "VNC server:", this, 100),
+ entry(dpy, this, this, false, 180),
+ aboutButton(dpy, "About...", this, this, 60),
+ optionsButton(dpy, "Options...", this, this, 60),
+ okButton(dpy, "OK", this, this, 60),
+ cancelButton(dpy, "Cancel", this, this, 60),
+ options(options_), about(about_)
+ {
+ label.move(0, 30);
+ entry.move(label.width(), 28);
+ int x = width();
+ int y = height() - yPad*4 - cancelButton.height();
+ x -= cancelButton.width() + xPad*6;
+ cancelButton.move(x, y);
+ x -= okButton.width() + xPad*6;
+ okButton.move(x, y);
+ x -= optionsButton.width() + xPad*6;
+ optionsButton.move(x, y);
+ x -= aboutButton.width() + xPad*6;
+ aboutButton.move(x, y);
+ }
+
+ virtual void takeFocus(Time time) {
+ XSetInputFocus(dpy, entry.win(), RevertToParent, time);
+ }
+
+ virtual void entryCallback(TXEntry* e, Detail detail, Time time) {
+ if (detail == ENTER) {
+ ok = true;
+ done = true;
+ }
+ }
+
+ virtual void buttonActivate(TXButton* b) {
+ if (b == &okButton) {
+ ok = true;
+ done = true;
+ } else if (b == &cancelButton) {
+ ok = false;
+ done = true;
+ } else if (b == &optionsButton) {
+ options->show();
+ } else if (b == &aboutButton) {
+ about->show();
+ }
+ }
+
+ TXLabel label;
+ TXEntry entry;
+ TXButton aboutButton, optionsButton, okButton, cancelButton;
+ OptionsDialog* options;
+ AboutDialog* about;
+};
+
+#endif
diff --git a/vncviewer_unix/buildtime.c b/vncviewer_unix/buildtime.c
new file mode 100644
index 00000000..a96031cc
--- /dev/null
+++ b/vncviewer_unix/buildtime.c
@@ -0,0 +1,18 @@
+/* Copyright (C) 2002-2003 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.
+ */
+char buildtime[] = __DATE__ " " __TIME__;
diff --git a/vncviewer_unix/parameters.h b/vncviewer_unix/parameters.h
new file mode 100644
index 00000000..815f5f12
--- /dev/null
+++ b/vncviewer_unix/parameters.h
@@ -0,0 +1,44 @@
+/* Copyright (C) 2002-2003 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 __PARAMETERS_H__
+#define __PARAMETERS_H__
+
+#include <rfb/Configuration.h>
+
+extern rfb::IntParameter pointerEventInterval;
+extern rfb::IntParameter wmDecorationWidth;
+extern rfb::IntParameter wmDecorationHeight;
+extern rfb::StringParameter passwordFile;
+extern rfb::BoolParameter useLocalCursor;
+extern rfb::BoolParameter dotWhenNoCursor;
+extern rfb::BoolParameter autoSelect;
+extern rfb::BoolParameter fullColour;
+extern rfb::IntParameter lowColourLevel;
+extern rfb::StringParameter preferredEncoding;
+extern rfb::BoolParameter viewOnly;
+extern rfb::BoolParameter shared;
+extern rfb::BoolParameter acceptClipboard;
+extern rfb::BoolParameter sendClipboard;
+extern rfb::BoolParameter sendPrimary;
+extern rfb::BoolParameter fullScreen;
+extern rfb::StringParameter geometry;
+
+extern char aboutText[];
+extern char* programName;
+
+#endif
diff --git a/vncviewer_unix/vncviewer.cxx b/vncviewer_unix/vncviewer.cxx
new file mode 100644
index 00000000..7b9e37d2
--- /dev/null
+++ b/vncviewer_unix/vncviewer.cxx
@@ -0,0 +1,244 @@
+/* Copyright (C) 2002-2004 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.
+ */
+//
+// All-new VNC viewer for X.
+//
+
+#include <string.h>
+#include <stdio.h>
+#include <ctype.h>
+#include <stdlib.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <unistd.h>
+#include <errno.h>
+#include <signal.h>
+#include <rfb/Logger_stdio.h>
+#include <rfb/LogWriter.h>
+#include <network/TcpSocket.h>
+#include "TXWindow.h"
+#include "CConn.h"
+
+rfb::LogWriter vlog("main");
+
+using namespace network;
+using namespace rfb;
+
+IntParameter pointerEventInterval("PointerEventInterval",
+ "Time in milliseconds to rate-limit"
+ " successive pointer events", 0);
+IntParameter wmDecorationWidth("WMDecorationWidth", "Width of window manager "
+ "decoration around a window", 6);
+IntParameter wmDecorationHeight("WMDecorationHeight", "Height of window "
+ "manager decoration around a window", 24);
+StringParameter passwordFile("PasswordFile",
+ "Password file for VNC authentication", "");
+AliasParameter rfbauth("passwd", "Alias for PasswordFile", &passwordFile);
+
+BoolParameter useLocalCursor("UseLocalCursor",
+ "Render the mouse cursor locally", true);
+BoolParameter dotWhenNoCursor("DotWhenNoCursor",
+ "Show the dot cursor when the server sends an "
+ "invisible cursor", true);
+BoolParameter autoSelect("AutoSelect",
+ "Auto select pixel format and encoding", true);
+BoolParameter fullColour("FullColour",
+ "Use full colour - otherwise low colour level is used"
+ " until AutoSelect decides the link is fast enough",
+ false);
+AliasParameter fullColor("FullColor", "Alias for FullColour", &fullColour);
+IntParameter lowColourLevel("LowColourLevel",
+ "Colour level to use on slow connections. "
+ "0 = Very Low (8 colours), 1 = Low (64 colours), "
+ "2 = Medium (256 colours)", 1);
+StringParameter preferredEncoding("PreferredEncoding",
+ "Preferred encoding to use (ZRLE, hextile or"
+ " raw) - implies AutoSelect=0", "");
+BoolParameter fullScreen("FullScreen", "Full screen mode", false);
+BoolParameter viewOnly("ViewOnly",
+ "Don't send any mouse or keyboard events to the server",
+ false);
+BoolParameter shared("Shared",
+ "Don't disconnect other viewers upon connection - "
+ "share the desktop instead",
+ false);
+BoolParameter acceptClipboard("AcceptClipboard",
+ "Accept clipboard changes from the server",
+ true);
+BoolParameter sendClipboard("SendClipboard",
+ "Send clipboard changes to the server", true);
+BoolParameter sendPrimary("SendPrimary",
+ "Send the primary selection and cut buffer to the "
+ "server as well as the clipboard selection",
+ true);
+
+BoolParameter listenMode("listen", "Listen for connections from VNC servers",
+ false);
+StringParameter geometry("geometry", "X geometry specification", "");
+StringParameter displayname("display", "The X display", "");
+
+char aboutText[256];
+char* programName;
+extern char buildtime[];
+
+static void CleanupSignalHandler(int sig)
+{
+ // CleanupSignalHandler allows C++ object cleanup to happen because it calls
+ // exit() rather than the default which is to abort.
+ vlog.info("CleanupSignalHandler called");
+ exit(1);
+}
+
+// XLoginIconifier is a class which iconifies the XDM login window when it has
+// grabbed the keyboard, thus releasing the grab, allowing the viewer to use
+// the keyboard. It remaps the xlogin window on exit.
+class XLoginIconifier {
+public:
+ Display* dpy;
+ Window xlogin;
+ XLoginIconifier() : dpy(0), xlogin(0) {}
+ void iconify(Display* dpy_) {
+ dpy = dpy_;
+ if (XGrabKeyboard(dpy, DefaultRootWindow(dpy), False, GrabModeSync,
+ GrabModeSync, CurrentTime) == GrabSuccess) {
+ XUngrabKeyboard(dpy, CurrentTime);
+ } else {
+ xlogin = TXWindow::windowWithName(dpy, DefaultRootWindow(dpy), "xlogin");
+ if (xlogin) {
+ XIconifyWindow(dpy, xlogin, DefaultScreen(dpy));
+ XSync(dpy, False);
+ }
+ }
+ }
+ ~XLoginIconifier() {
+ if (xlogin) {
+ fprintf(stderr,"~XLoginIconifier remapping xlogin\n");
+ XMapWindow(dpy, xlogin);
+ XFlush(dpy);
+ sleep(1);
+ }
+ }
+};
+
+static XLoginIconifier xloginIconifier;
+
+static void usage()
+{
+ fprintf(stderr,
+ "\nusage: %s [parameters] [host:displayNum] [parameters]\n"
+ " %s [parameters] -listen [port] [parameters]\n",
+ programName,programName);
+ fprintf(stderr,"\n"
+ "Parameters can be turned on with -<param> or off with -<param>=0\n"
+ "Parameters which take a value can be specified as "
+ "-<param> <value>\n"
+ "Other valid forms are <param>=<value> -<param>=<value> "
+ "--<param>=<value>\n"
+ "Parameter names are case-insensitive. The parameters are:\n\n");
+ Configuration::listParams(79, 14);
+ exit(1);
+}
+
+int main(int argc, char** argv)
+{
+ sprintf(aboutText, "VNC viewer for X version 4.0 - built %s\n"
+ "Copyright (C) 2002-2004 RealVNC Ltd.\n"
+ "See http://www.realvnc.com for information on VNC.",
+ buildtime);
+ fprintf(stderr,"\n%s\n", aboutText);
+
+ rfb::initStdIOLoggers();
+ rfb::LogWriter::setLogParams("*:stderr:30");
+
+ signal(SIGHUP, CleanupSignalHandler);
+ signal(SIGINT, CleanupSignalHandler);
+ signal(SIGTERM, CleanupSignalHandler);
+
+ programName = argv[0];
+ char* vncServerName = 0;
+ Display* dpy;
+
+ for (int i = 1; i < argc; i++) {
+ if (Configuration::setParam(argv[i]))
+ continue;
+
+ if (argv[i][0] == '-') {
+ if (i+1 < argc) {
+ if (Configuration::setParam(&argv[i][1], argv[i+1])) {
+ i++;
+ continue;
+ }
+ }
+ usage();
+ }
+
+ if (vncServerName)
+ usage();
+ vncServerName = argv[i];
+ }
+
+ try {
+ TcpSocket::initTcpSockets();
+
+ Socket* sock = 0;
+
+ if (listenMode) {
+ int port = 5500;
+ if (vncServerName && isdigit(vncServerName[0]))
+ port = atoi(vncServerName);
+
+ TcpListener listener(port);
+
+ vlog.info("Listening on port %d\n",port);
+
+ while (true) {
+ sock = listener.accept();
+ int pid = fork();
+ if (pid < 0) { perror("fork"); exit(1); }
+ if (pid == 0) break; // child
+ delete sock;
+ int status;
+ while (wait3(&status, WNOHANG, 0) > 0) ;
+ }
+ }
+
+ CharArray displaynameStr(displayname.getData());
+ if (!(dpy = XOpenDisplay(TXWindow::strEmptyToNull(displaynameStr.buf)))) {
+ fprintf(stderr,"%s: unable to open display \"%s\"\n",
+ programName, XDisplayName(displaynameStr.buf));
+ exit(1);
+ }
+
+ TXWindow::init(dpy, "Vncviewer");
+ xloginIconifier.iconify(dpy);
+ CConn cc(dpy, argc, argv, sock, vncServerName);
+
+ // X events are processed whenever reading from the socket would block.
+
+ while (true) {
+ cc.getInStream()->check(1);
+ cc.processMsg();
+ }
+
+ } catch (rdr::Exception &e) {
+ vlog.error(e.str());
+ }
+
+ return 0;
+}
diff --git a/vncviewer_unix/vncviewer.man b/vncviewer_unix/vncviewer.man
new file mode 100644
index 00000000..a0db4369
--- /dev/null
+++ b/vncviewer_unix/vncviewer.man
@@ -0,0 +1,189 @@
+.TH vncviewer 1 "18 May 2004" "RealVNC Ltd" "Virtual Network Computing"
+.SH NAME
+vncviewer \- VNC viewer for X
+.SH SYNOPSIS
+.B vncviewer
+.RI [ options ]
+.RI [ host ][: display# ]
+.br
+.B vncviewer
+.RI [ options ]
+.B \-listen
+.RI [ port ]
+.SH DESCRIPTION
+.B vncviewer
+is a viewer (client) for Virtual Network Computing. This manual page documents
+version 4 for the X window system.
+
+If you run the viewer with no arguments it will prompt you for a VNC server to
+connect to. Alternatively, specify the VNC server as an argument, e.g.:
+
+.RS
+vncviewer snoopy:2
+.RE
+
+where 'snoopy' is the name of the machine, and '2' is the display number of the
+VNC server on that machine. Either the machine name or display number can be
+omitted. So for example ":1" means display number 1 on the same machine, and
+"snoopy" means "snoopy:0" i.e. display 0 on machine "snoopy".
+
+If the VNC server is successfully contacted, you will be prompted for a
+password to authenticate you. If the password is correct, a window will appear
+showing the desktop of the VNC server.
+
+.SH AUTOMATIC PROTOCOL SELECTION
+
+The viewer tests the speed of the connection to the server and chooses the
+encoding and pixel format (colour level) appropriately. This makes it much
+easier to use than previous versions where the user had to specify arcane
+command line arguments.
+
+The viewer normally starts out assuming the link is slow, using a low colour
+level and using the encoding with the best compression. If it turns out that
+the link is fast enough it switches to full-colour mode and will use an
+encoding which compresses less but is faster to generate, thus improving the
+interactive feel. Automatic selection can be turned off by setting the
+\fBAutoSelect\fP parameter to false, or from the options dialog.
+
+.SH POPUP MENU
+The viewer has a popup menu containing entries which perform various actions.
+It is usually brought up by pressing F8, but this can be configured with the
+MenuKey parameter. Actions which the popup menu can perform include:
+.RS 2
+.IP * 2
+switching in and out of full-screen mode
+.IP *
+quitting the viewer
+.IP *
+generating key events, e.g. sending ctrl-alt-del
+.IP *
+accessing the options dialog and various other dialogs
+.RE
+.PP
+By default, key presses in the popup menu get sent to the VNC server and
+dismiss the popup. So to get an F8 through to the VNC server simply press it
+twice.
+
+.SH FULL SCREEN MODE
+A full-screen mode is supported. This is particularly useful when connecting
+to a remote screen which is the same size as your local one. If the remote
+screen is bigger, you can scroll by bumping the mouse against the edge of the
+screen.
+
+Unfortunately this mode doesn't work completely with all window managers, since
+it breaks the X window management conventions.
+
+.SH OPTIONS (PARAMETERS)
+You can get a list of parameters by giving \fB\-h\fP as a command-line option
+to vncviewer. Parameters can be turned on with -\fIparam\fP or off with
+-\fIparam\fP=0. Parameters which take a value can be specified as
+-\fIparam\fP \fIvalue\fP. Other valid forms are \fIparam\fP\fB=\fP\fIvalue\fP
+-\fIparam\fP=\fIvalue\fP --\fIparam\fP=\fIvalue\fP. Parameter names are
+case-insensitive.
+
+Many of the parameters can also be set graphically via the options dialog box.
+This can be accessed from the popup menu or from the "Connection details"
+dialog box.
+
+.TP
+.B \-display \fIXdisplay\fP
+Specifies the X display on which the VNC viewer window should appear.
+
+.TP
+.B \-geometry \fIgeometry\fP
+Standard X position and sizing specification.
+
+.TP
+.B \-listen \fI[port]\fP
+Causes vncviewer to listen on the given port (default 5500) for reverse
+connections from a VNC server. WinVNC supports reverse connections initiated
+using the 'Add New Client' menu option or the '\-connect' command-line option.
+Xvnc supports reverse connections with a helper program called
+.B vncconfig.
+
+.TP
+.B \-passwd \fIpassword-file\fP
+If you are on a filesystem which gives you access to the password file used by
+the server, you can specify it here to avoid typing it in. It will usually be
+"~/.vnc/passwd".
+
+.TP
+.B \-Shared
+When you make a connection to a VNC server, all other existing connections are
+normally closed. This option requests that they be left open, allowing you to
+share the desktop with someone already using it.
+
+.TP
+.B \-ViewOnly
+Specifies that no keyboard or mouse events should be sent to the server.
+Useful if you want to view a desktop without interfering; often needs to be
+combined with
+.B \-Shared.
+
+.TP
+.B \-FullScreen
+Start in full-screen mode.
+
+.TP
+.B \-AutoSelect
+Use automatic selection of encoding and pixel format (default is on). Normally
+the viewer tests the speed of the connection to the server and chooses the
+encoding and pixel format appropriately. Turn it off with \fB-AutoSelect=0\fP.
+
+.TP
+.B \-FullColour, \-FullColor
+Tells the VNC server to send full-colour pixels in the best format for this
+display. By default a low colour mode is used until AutoSelect decides the
+link is fast enough. However if the server's native pixel format is
+colourmapped (as opposed to truecolour) then the server's format is used by
+default.
+
+.TP
+.B \-LowColourLevel \fIlevel\fP
+Selects the reduced colour level to use on slow links. \fIlevel\fP can range
+from 0 to 2, 0 meaning 8 colours, 1 meaning 64 colours (the default), 2 meaning
+256 colours.
+
+.TP
+.B \-PreferredEncoding \fIencoding\fP
+This option specifies the preferred encoding to use from one of "ZRLE",
+"hextile" or "raw".
+
+.TP
+.B -UseLocalCursor
+Render the mouse cursor locally if the server supports it (default is on).
+This can make the interactive performance feel much better over slow links.
+
+.TP
+.B \-WMDecorationWidth \fIw\fP, \-WMDecorationHeight \fIh\fP
+The total width and height taken up by window manager decorations. This is
+used to calculate the maximum size of the VNC viewer window. Default is
+width 6, height 24.
+
+.TP
+.B \-log \fIlogname\fP:\fIdest\fP:\fIlevel\fP
+Configures the debug log settings. \fIdest\fP can currently be \fBstderr\fP or
+\fBstdout\fP, and \fIlevel\fP is between 0 and 100, 100 meaning most verbose
+output. \fIlogname\fP is usually \fB*\fP meaning all, but you can target a
+specific source file if you know the name of its "LogWriter". Default is
+\fB*:stderr:30\fP.
+
+.TP
+.B \-MenuKey \fIkeysym-name\fP
+This option specifies the key which brings up the popup menu. The key is
+specified as an X11 keysym name (these can be obtained by removing the XK_
+prefix from the entries in "/usr/include/X11/keysymdef.h"). Default is F8.
+
+.SH SEE ALSO
+.BR Xvnc (1)
+.BR vncconfig (1),
+.BR vncserver (1),
+.br
+http://www.realvnc.com
+
+.SH AUTHOR
+Tristan Richardson, RealVNC Ltd.
+
+VNC was originally developed by the RealVNC team while at Olivetti Research Ltd
+/ AT&T Laboratories Cambridge. It is now being maintained by RealVNC Ltd. See
+http://www.realvnc.com for details.
diff --git a/winvnc/AddNewClientDialog.h b/winvnc/AddNewClientDialog.h
new file mode 100644
index 00000000..d8e0af36
--- /dev/null
+++ b/winvnc/AddNewClientDialog.h
@@ -0,0 +1,56 @@
+/* Copyright (C) 2002-2004 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.
+ */
+
+// -=- AddnewClientDialog.h
+
+#ifndef __WINVNC_ADD_NEW_CLIENT_DIALOG_H__
+#define __WINVNC_ADD_NEW_CLIENT_DIALOG_H__
+
+#include <winvnc/resource.h>
+#include <rfb_win32/Dialog.h>
+//#include <rfb_win32/TCharArray.h>
+
+namespace winvnc {
+
+ class AddNewClientDialog : public rfb::win32::Dialog {
+ public:
+ AddNewClientDialog() : Dialog(GetModuleHandle(0)) {}
+ // - Show the dialog and return true if OK was clicked,
+ // false in case of error or Cancel
+ virtual bool showDialog() {
+ return Dialog::showDialog(MAKEINTRESOURCE(IDD_ADD_NEW_CLIENT));
+ }
+ const char* getHostName() const {return hostName.buf;}
+ protected:
+
+ // Dialog methods (protected)
+ virtual void initDialog() {
+ if (hostName.buf)
+ setItemString(IDC_HOST, rfb::TStr(hostName.buf));
+ }
+ virtual bool onOk() {
+ hostName.replaceBuf(rfb::strDup(rfb::CStr(getItemString(IDC_HOST))));
+ return true;
+ }
+
+ rfb::CharArray hostName;
+ };
+
+};
+
+#endif
diff --git a/winvnc/JavaViewer.cxx b/winvnc/JavaViewer.cxx
new file mode 100644
index 00000000..fecbcee0
--- /dev/null
+++ b/winvnc/JavaViewer.cxx
@@ -0,0 +1,95 @@
+/* Copyright (C) 2002-2004 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.
+ */
+
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+#include <winvnc/JavaViewer.h>
+#include <winvnc/resource.h>
+#include <rdr/MemInStream.h>
+#include <rfb/LogWriter.h>
+#include <rfb/VNCserverST.h>
+#include <rfb_win32/TCharArray.h>
+
+#define strcasecmp _stricmp
+
+using namespace winvnc;
+using namespace rfb;
+
+
+static rfb::LogWriter vlog("JavaViewerServer");
+
+JavaViewerServer::JavaViewerServer(rfb::VNCServerST* svr) : server(svr) {
+}
+
+JavaViewerServer::~JavaViewerServer() {
+}
+
+rdr::InStream* JavaViewerServer::getFile(const char* name, const char** contentType) {
+ if (strcmp(name, "/") == 0)
+ name = "/index.vnc";
+
+ HRSRC resource = FindResource(0, TStr(name), _T("HTTPFILE"));
+ if (!resource) return 0;
+ HGLOBAL handle = LoadResource(0, resource);
+ if (!handle) return 0;
+ void* buf = LockResource(handle);
+ int len = SizeofResource(0, resource);
+
+ rdr::InStream* is = new rdr::MemInStream(buf, len);
+ if (strlen(name) > 4 && strcasecmp(&name[strlen(name)-4], ".vnc") == 0) {
+ is = new rdr::SubstitutingInStream(is, this, 20);
+ *contentType = "text/html";
+ }
+ return is;
+}
+
+char* JavaViewerServer::substitute(const char* varName)
+{
+ if (strcmp(varName, "$$") == 0) {
+ return rfb::strDup("$");
+ }
+ if (strcmp(varName, "$PORT") == 0) {
+ char* str = new char[10];
+ sprintf(str, "%d", rfbPort);
+ return str;
+ }
+ if (strcmp(varName, "$WIDTH") == 0) {
+ char* str = new char[10];
+ sprintf(str, "%d", server->getDesktopSize().x);
+ return str;
+ }
+ if (strcmp(varName, "$HEIGHT") == 0) {
+ char* str = new char[10];
+ sprintf(str, "%d", server->getDesktopSize().y);
+ return str;
+ }
+ if (strcmp(varName, "$APPLETWIDTH") == 0) {
+ char* str = new char[10];
+ sprintf(str, "%d", server->getDesktopSize().x);
+ return str;
+ }
+ if (strcmp(varName, "$APPLETHEIGHT") == 0) {
+ char* str = new char[10];
+ sprintf(str, "%d", server->getDesktopSize().y + 32);
+ return str;
+ }
+ if (strcmp(varName, "$DESKTOP") == 0) {
+ return rfb::strDup(server->getName());
+ }
+ return 0;
+}
diff --git a/winvnc/JavaViewer.h b/winvnc/JavaViewer.h
new file mode 100644
index 00000000..20af786b
--- /dev/null
+++ b/winvnc/JavaViewer.h
@@ -0,0 +1,54 @@
+/* Copyright (C) 2002-2004 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.
+ */
+
+// -=- JavaViewer.h
+
+// Custom HTTPServer-derived class which serves the Java VNC Viewer
+// to clients, using resource files compiled in to the WinVNC executable.
+
+#ifndef WINVNC_JAVA_VIEWER
+#define WINVNC_JAVA_VIEWER
+
+#include <rfb/HTTPServer.h>
+#include <rfb/VNCServerST.h>
+#include <rdr/SubstitutingInStream.h>
+
+namespace winvnc {
+
+ class JavaViewerServer : public rfb::HTTPServer, public rdr::Substitutor {
+ public:
+ JavaViewerServer(rfb::VNCServerST* desktop);
+ virtual ~JavaViewerServer();
+
+ virtual rdr::InStream* getFile(const char* name, const char** contentType);
+
+ // rdr::Substitutor callback
+ virtual char* substitute(const char* varName);
+
+ void setRFBport(int port) {
+ rfbPort = port;
+ }
+ protected:
+ int rfbPort;
+ rfb::VNCServerST* server;
+ };
+
+};
+
+#endif
+
diff --git a/winvnc/QueryConnectDialog.cxx b/winvnc/QueryConnectDialog.cxx
new file mode 100644
index 00000000..52d7249d
--- /dev/null
+++ b/winvnc/QueryConnectDialog.cxx
@@ -0,0 +1,100 @@
+/* Copyright (C) 2002-2003 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 <winvnc/QueryConnectDialog.h>
+#include <winvnc/VNCServerWin32.h>
+#include <winvnc/resource.h>
+#include <rfb_win32/Win32Util.h>
+#include <rfb_win32/TCharArray.h>
+#include <rfb_win32/Service.h>
+#include <rfb/LogWriter.h>
+
+using namespace rfb;
+using namespace win32;
+using namespace winvnc;
+
+static LogWriter vlog("QueryConnectDialog");
+
+static IntParameter timeout("QueryConnectTimeout",
+ "Number of seconds to show the Accept Connection dialog before "
+ "rejecting the connection",
+ 10);
+
+
+// - Visible methods
+
+QueryConnectDialog::QueryConnectDialog(network::Socket* sock_,
+ const char* userName_,
+ VNCServerWin32* s)
+: Thread("QueryConnectDialog"), Dialog(GetModuleHandle(0)),
+ sock(sock_), approve(false), server(s) {
+ peerIp.buf = sock->getPeerAddress();
+ userName.buf = strDup(userName_);
+}
+
+void QueryConnectDialog::startDialog() {
+ start();
+}
+
+
+// - Thread overrides
+
+void QueryConnectDialog::run() {
+ countdown = timeout;
+ try {
+ if (desktopChangeRequired() && !changeDesktop())
+ throw rdr::Exception("changeDesktop failed");
+ approve = Dialog::showDialog(MAKEINTRESOURCE(IDD_QUERY_CONNECT));
+ server->queryConnectionComplete();
+ } catch (...) {
+ server->queryConnectionComplete();
+ throw;
+ }
+}
+
+
+// - Dialog overrides
+
+void QueryConnectDialog::initDialog() {
+ if (!SetTimer(handle, 1, 1000, 0))
+ throw rdr::SystemException("SetTimer", GetLastError());
+ setItemString(IDC_QUERY_HOST, TStr(peerIp.buf));
+ if (!userName.buf)
+ userName.buf = strDup("(anonymous)");
+ setItemString(IDC_QUERY_USER, TStr(userName.buf));
+ setCountdownLabel();
+}
+
+void QueryConnectDialog::setCountdownLabel() {
+ TCHAR buf[16];
+ _stprintf(buf, _T("%d"), countdown);
+ setItemString(IDC_QUERY_COUNTDOWN, buf);
+}
+
+BOOL QueryConnectDialog::dialogProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) {
+ if (msg == WM_TIMER) {
+ if (--countdown == 0 || desktopChangeRequired()) {
+ DestroyWindow(hwnd);
+ } else {
+ setCountdownLabel();
+ }
+ return TRUE;
+ } else {
+ return Dialog::dialogProc(hwnd, msg, wParam, lParam);
+ }
+}
diff --git a/winvnc/QueryConnectDialog.h b/winvnc/QueryConnectDialog.h
new file mode 100644
index 00000000..30dd2705
--- /dev/null
+++ b/winvnc/QueryConnectDialog.h
@@ -0,0 +1,60 @@
+/* Copyright (C) 2002-2003 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.
+ */
+
+// -=- QueryConnectDialog.h
+
+#ifndef __WINVNC_QUERY_CONNECT_DIALOG_H__
+#define __WINVNC_QUERY_CONNECT_DIALOG_H__
+
+#include <rfb/Threading.h>
+#include <rfb_win32/Dialog.h>
+#include <rfb/util.h>
+
+namespace network { class Socket; }
+
+namespace winvnc {
+
+ class VNCServerWin32;
+
+ class QueryConnectDialog : public rfb::Thread, rfb::win32::Dialog {
+ public:
+ QueryConnectDialog(network::Socket* sock, const char* userName, VNCServerWin32* s);
+ virtual void startDialog();
+ virtual void run();
+ network::Socket* getSock() {return sock;}
+ bool isAccepted() const {return approve;}
+ protected:
+
+ // Dialog methods (protected)
+ virtual void initDialog();
+ virtual BOOL dialogProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam);
+
+ // Custom internal methods
+ void setCountdownLabel();
+
+ int countdown;
+ network::Socket* sock;
+ rfb::CharArray peerIp;
+ rfb::CharArray userName;
+ bool approve;
+ VNCServerWin32* server;
+ };
+
+};
+
+#endif
diff --git a/winvnc/STrayIcon.cxx b/winvnc/STrayIcon.cxx
new file mode 100644
index 00000000..7cfea3c2
--- /dev/null
+++ b/winvnc/STrayIcon.cxx
@@ -0,0 +1,234 @@
+/* Copyright (C) 2002-2004 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.
+ */
+
+// -=- WinVNC Version 4.0 Tray Icon implementation
+
+#include <winvnc/STrayIcon.h>
+#include <winvnc/resource.h>
+
+#include <rfb/LogWriter.h>
+#include <rfb/Configuration.h>
+#include <rfb_win32/LaunchProcess.h>
+#include <rfb_win32/TrayIcon.h>
+#include <rfb_win32/AboutDialog.h>
+#include <rfb_win32/Win32Util.h>
+#include <rfb_win32/Service.h>
+#include <rfb_win32/CurrentUser.h>
+
+using namespace rfb;
+using namespace win32;
+using namespace winvnc;
+
+static LogWriter vlog("STrayIcon");
+
+BoolParameter STrayIconThread::disableOptions("DisableOptions", "Disable the Options entry in the VNC Server tray menu.", false);
+
+
+//
+// -=- 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;
+
+
+//
+// -=- Internal tray icon class
+//
+
+const UINT WM_SET_TOOLTIP = WM_USER + 1;
+
+
+class winvnc::STrayIcon : public TrayIcon {
+public:
+ STrayIcon(STrayIconThread& t) : thread(t),
+ vncConfig(_T("vncconfig.exe"), isServiceProcess() ? _T("-noconsole -service") : _T("-noconsole")),
+ vncConnect(_T("winvnc4.exe"), _T("-connect")) {
+
+ // ***
+ SetWindowText(getHandle(), _T("winvnc::IPC_Interface"));
+ // ***
+
+ SetTimer(getHandle(), 1, 3000, 0);
+ PostMessage(getHandle(), WM_TIMER, 1, 0);
+ PostMessage(getHandle(), WM_SET_TOOLTIP, 0, 0);
+ }
+
+ virtual LRESULT processMessage(UINT msg, WPARAM wParam, LPARAM lParam) {
+ switch(msg) {
+
+ case WM_USER:
+ {
+ bool userKnown = CurrentUserToken().isValid();
+ bool allowOptions = !STrayIconThread::disableOptions && userKnown;
+
+ switch (lParam) {
+ case WM_LBUTTONDBLCLK:
+ SendMessage(getHandle(), WM_COMMAND, allowOptions ? ID_OPTIONS : ID_ABOUT, 0);
+ break;
+ case WM_RBUTTONUP:
+ HMENU menu = LoadMenu(GetModuleHandle(0), MAKEINTRESOURCE(thread.menu));
+ HMENU trayMenu = GetSubMenu(menu, 0);
+
+
+ // Default item is Options, if available, or About if not
+ SetMenuDefaultItem(trayMenu, allowOptions ? ID_OPTIONS : ID_ABOUT, FALSE);
+
+ // Enable/disable options as required
+ EnableMenuItem(trayMenu, ID_OPTIONS, (!allowOptions ? MF_GRAYED : MF_ENABLED) | MF_BYCOMMAND);
+ EnableMenuItem(trayMenu, ID_CONNECT, (!userKnown ? MF_GRAYED : MF_ENABLED) | MF_BYCOMMAND);
+ EnableMenuItem(trayMenu, ID_CLOSE, (isServiceProcess() ? MF_GRAYED : MF_ENABLED) | MF_BYCOMMAND);
+
+ // SetForegroundWindow is required, otherwise Windows ignores the
+ // TrackPopupMenu because the window isn't the foreground one, on
+ // some older Windows versions...
+ SetForegroundWindow(getHandle());
+
+ // Display the menu
+ POINT pos;
+ GetCursorPos(&pos);
+ TrackPopupMenu(trayMenu, 0, pos.x, pos.y, 0, getHandle(), 0);
+ break;
+ }
+ return 0;
+ }
+
+ // Handle tray icon menu commands
+ case WM_COMMAND:
+ switch (LOWORD(wParam)) {
+ case ID_OPTIONS:
+ {
+ CurrentUserToken token;
+ if (token.isValid())
+ vncConfig.start(isServiceProcess() ? (HANDLE)token : 0);
+ else
+ vlog.error("Options: unknown current user");
+ }
+ break;
+ case ID_CONNECT:
+ {
+ CurrentUserToken token;
+ if (token.isValid())
+ vncConnect.start(isServiceProcess() ? (HANDLE)token : 0);
+ else
+ vlog.error("Options: unknown current user");
+ }
+ break;
+ case ID_DISCONNECT:
+ thread.server.disconnectClients("tray menu disconnect");
+ break;
+ case ID_CLOSE:
+ if (!isServiceProcess())
+ thread.server.stop();
+ break;
+ case ID_ABOUT:
+ AboutDialog::instance.showDialog();
+ break;
+ }
+ return 0;
+
+ // Handle commands send by other processes
+ case WM_COPYDATA:
+ {
+ COPYDATASTRUCT* command = (COPYDATASTRUCT*)lParam;
+ switch (command->dwData) {
+ case 1:
+ {
+ CharArray viewer = new char[command->cbData + 1];
+ memcpy(viewer.buf, command->lpData, command->cbData);
+ viewer.buf[command->cbData] = 0;
+ thread.server.addNewClient(viewer.buf);
+ }
+ break;
+ case 2:
+ thread.server.disconnectClients("IPC disconnect");
+ break;
+ };
+ };
+ break;
+
+ case WM_CLOSE:
+ PostQuitMessage(0);
+ break;
+
+ case WM_TIMER:
+ if (rfb::win32::desktopChangeRequired()) {
+ SendMessage(getHandle(), WM_CLOSE, 0, 0);
+ return 0;
+ }
+ setIcon(thread.server.isServerInUse() ? thread.activeIcon : thread.inactiveIcon);
+ return 0;
+
+ case WM_SET_TOOLTIP:
+ {
+ rfb::Lock l(thread.lock);
+ if (thread.toolTip.buf)
+ setToolTip(thread.toolTip.buf);
+ }
+ return 0;
+
+ }
+
+ return TrayIcon::processMessage(msg, wParam, lParam);
+ }
+
+protected:
+ LaunchProcess vncConfig;
+ LaunchProcess vncConnect;
+ STrayIconThread& thread;
+};
+
+
+STrayIconThread::STrayIconThread(VNCServerWin32& sm, UINT inactiveIcon_, UINT activeIcon_, UINT menu_)
+: server(sm), inactiveIcon(inactiveIcon_), activeIcon(activeIcon_), menu(menu_),
+ windowHandle(0), runTrayIcon(true) {
+ start();
+}
+
+
+void STrayIconThread::run() {
+ while (runTrayIcon) {
+ if (rfb::win32::desktopChangeRequired() &&
+ !rfb::win32::changeDesktop())
+ Sleep(2000);
+
+ STrayIcon icon(*this);
+ windowHandle = icon.getHandle();
+
+ MSG msg;
+ while (runTrayIcon && ::GetMessage(&msg, 0, 0, 0) > 0) {
+ TranslateMessage(&msg);
+ DispatchMessage(&msg);
+ }
+
+ windowHandle = 0;
+ }
+}
+
+void STrayIconThread::setToolTip(const TCHAR* text) {
+ if (!windowHandle) return;
+ Lock l(lock);
+ delete [] toolTip.buf;
+ toolTip.buf = tstrDup(text);
+ PostMessage(windowHandle, WM_SET_TOOLTIP, 0, 0);
+}
+
+
diff --git a/winvnc/STrayIcon.h b/winvnc/STrayIcon.h
new file mode 100644
index 00000000..cfd5ec01
--- /dev/null
+++ b/winvnc/STrayIcon.h
@@ -0,0 +1,58 @@
+/* Copyright (C) 2002-2004 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 WINVNC_TRAYICON_H
+#define WINVNC_TRAYICON_H
+
+#include <winvnc/VNCServerWin32.h>
+#include <rfb_win32/TCharArray.h>
+#include <rfb/Configuration.h>
+#include <rfb/Threading.h>
+
+namespace winvnc {
+
+ class STrayIconThread : rfb::Thread {
+ public:
+ STrayIconThread(VNCServerWin32& sm, UINT inactiveIcon,
+ UINT activeIcon, UINT menu);
+ virtual ~STrayIconThread() {
+ runTrayIcon = false;
+ PostThreadMessage(getThreadId(), WM_QUIT, 0, 0);
+ }
+
+ virtual void run();
+
+ void setToolTip(const TCHAR* text);
+
+ static rfb::BoolParameter disableOptions;
+
+ friend class STrayIcon;
+ protected:
+ rfb::Mutex lock;
+ HWND windowHandle;
+ rfb::TCharArray toolTip;
+ VNCServerWin32& server;
+ UINT inactiveIcon;
+ UINT activeIcon;
+ UINT menu;
+ bool runTrayIcon;
+ };
+
+};
+
+#endif \ No newline at end of file
diff --git a/winvnc/VNCServerService.cxx b/winvnc/VNCServerService.cxx
new file mode 100644
index 00000000..c967a5a5
--- /dev/null
+++ b/winvnc/VNCServerService.cxx
@@ -0,0 +1,52 @@
+/* Copyright (C) 2002-2003 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.
+ */
+
+// -=- WinVNC Version 4.0 Service-Mode implementation
+
+#include <winvnc/VNCServerService.h>
+#include <rfb_win32/OSVersion.h>
+
+using namespace winvnc;
+using namespace rfb;
+using namespace win32;
+
+const TCHAR* winvnc::VNCServerService::Name = _T("WinVNC4");
+
+
+VNCServerService::VNCServerService(VNCServerWin32& s)
+ : Service(Name), server(s) {
+ // - Set the service-mode logging defaults
+ // These will be overridden by the Log option in the
+ // registry, if present.
+ if (osVersion.isPlatformNT)
+ logParams.setParam("*:EventLog:0,Connections:EventLog:100");
+ else
+ logParams.setParam("*:file:0,Connections:file:100");
+}
+
+
+DWORD VNCServerService::serviceMain(int argc, TCHAR* argv[]) {
+ setStatus(SERVICE_RUNNING);
+ int result = server.run();
+ setStatus(SERVICE_STOP_PENDING);
+ return result;
+}
+
+void VNCServerService::stop() {
+ server.stop();
+}
diff --git a/winvnc/VNCServerService.h b/winvnc/VNCServerService.h
new file mode 100644
index 00000000..378ad0c5
--- /dev/null
+++ b/winvnc/VNCServerService.h
@@ -0,0 +1,44 @@
+/* Copyright (C) 2002-2003 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 __WINVNC_SERVICEMODE_H__
+#define __WINVNC_SERVICEMODE_H__
+
+#include <winvnc/VNCServerWin32.h>
+#include <rfb_win32/Service.h>
+
+namespace winvnc {
+
+ class VNCServerService : public rfb::win32::Service {
+ public:
+ VNCServerService(VNCServerWin32& s);
+
+ DWORD serviceMain(int argc, TCHAR* argv[]);
+ void stop();
+
+ void osShuttingDown() {}
+ void readParams() {}
+
+ static const TCHAR* Name;
+ protected:
+ VNCServerWin32& server;
+ };
+
+};
+
+#endif
diff --git a/winvnc/VNCServerWin32.cxx b/winvnc/VNCServerWin32.cxx
new file mode 100644
index 00000000..a870cb1d
--- /dev/null
+++ b/winvnc/VNCServerWin32.cxx
@@ -0,0 +1,341 @@
+/* Copyright (C) 2002-2004 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.
+ */
+
+// -=- WinVNC Version 4.0 Main Routine
+
+#include <winvnc/VNCServerWin32.h>
+#include <winvnc/resource.h>
+#include <winvnc/STrayIcon.h>
+
+#include <rfb_win32/Win32Util.h>
+#include <rfb_win32/Service.h>
+#include <rfb/SSecurityFactoryStandard.h>
+#include <rfb/Hostname.h>
+#include <rfb/LogWriter.h>
+
+using namespace rfb;
+using namespace win32;
+using namespace winvnc;
+using namespace network;
+
+static LogWriter vlog("VNCServerWin32");
+
+
+const TCHAR* winvnc::VNCServerWin32::RegConfigPath = _T("Software\\RealVNC\\WinVNC4");
+
+const UINT VNCM_REG_CHANGED = WM_USER;
+const UINT VNCM_COMMAND = WM_USER + 1;
+
+
+static IntParameter http_port("HTTPPortNumber",
+ "TCP/IP port on which the server will serve the Java applet VNC Viewer ", 5800);
+static IntParameter port_number("PortNumber",
+ "TCP/IP port on which the server will accept connections", 5900);
+static StringParameter hosts("Hosts",
+ "Filter describing which hosts are allowed access to this server", "+");
+static VncAuthPasswdConfigParameter vncAuthPasswd;
+static BoolParameter localHost("LocalHost",
+ "Only accept connections from via the local loop-back network interface", false);
+
+
+// -=- ManagedListener
+// Wrapper class which simplifies the management of a listening socket
+// on a specified port, attached to a SocketManager and SocketServer.
+// Ensures that socket and filter are deleted and updated appropriately.
+
+class ManagedListener {
+public:
+ ManagedListener(win32::SocketManager* mgr, SocketServer* svr)
+ : sock(0), filter(0), port(0), manager(mgr),
+ server(svr), localOnly(0) {}
+ ~ManagedListener() {setPort(0);}
+ void setPort(int port, bool localOnly=false);
+ void setFilter(const char* filter);
+ TcpListener* sock;
+protected:
+ TcpFilter* filter;
+ win32::SocketManager* manager;
+ SocketServer* server;
+ int port;
+ bool localOnly;
+};
+
+// - If the port number/localHost setting has changed then tell the
+// SocketManager to shutdown and delete it. Also remove &
+// delete the filter. Then try to open a socket on the new port.
+void ManagedListener::setPort(int newPort, bool newLocalOnly) {
+ if ((port == newPort) && (localOnly == newLocalOnly) && sock) return;
+ if (sock) {
+ vlog.info("Closed TcpListener on port %d", port);
+ sock->setFilter(0);
+ delete filter;
+ manager->remListener(sock);
+ sock = 0;
+ filter = 0;
+ }
+ port = newPort;
+ localOnly = newLocalOnly;
+ if (port != 0) {
+ try {
+ sock = new TcpListener(port, localOnly);
+ vlog.info("Created TcpListener on port %d%s", port,
+ localOnly ? "(localhost)" : "(any)");
+ } catch (rdr::Exception& e) {
+ vlog.error("TcpListener on port %d failed (%s)", port, e.str());
+ }
+ }
+ if (sock)
+ manager->addListener(sock, server);
+}
+
+void ManagedListener::setFilter(const char* newFilter) {
+ if (!sock) return;
+ vlog.info("Updating TcpListener filter");
+ sock->setFilter(0);
+ delete filter;
+ filter = new TcpFilter(newFilter);
+ sock->setFilter(filter);
+}
+
+
+VNCServerWin32::VNCServerWin32()
+ : vncServer(CStr(ComputerName().buf), &desktop),
+ httpServer(0), runServer(false),
+ isDesktopStarted(false),
+ command(NoCommand), commandSig(commandLock),
+ queryConnectDialog(0) {
+ // Create the Java-viewer HTTP server
+ httpServer = new JavaViewerServer(&vncServer);
+
+ // Initialise the desktop
+ desktop.setStatusLocation(&isDesktopStarted);
+
+ // Initialise the VNC server
+ vncServer.setQueryConnectionHandler(this);
+
+ // Register the desktop's event to be handled
+ sockMgr.addEvent(desktop.getUpdateEvent(), &desktop);
+}
+
+VNCServerWin32::~VNCServerWin32() {
+ // Stop the SDisplay from updating our state
+ desktop.setStatusLocation(0);
+
+ // Destroy the HTTP server
+ delete httpServer;
+}
+
+
+int VNCServerWin32::run() {
+ { Lock l(runLock);
+ hostThread = Thread::self();
+ runServer = true;
+ }
+
+ // - Register for notification of configuration changes
+ if (isServiceProcess())
+ config.setKey(HKEY_LOCAL_MACHINE, RegConfigPath);
+ else
+ config.setKey(HKEY_CURRENT_USER, RegConfigPath);
+ config.setNotifyThread(Thread::self(), VNCM_REG_CHANGED);
+
+ // - Create the tray icon if possible
+ STrayIconThread trayIcon(*this, IDI_ICON, IDI_CONNECTED, IDR_TRAY);
+
+ DWORD result = 0;
+ try {
+ // - Create some managed listening sockets
+ ManagedListener rfb(&sockMgr, &vncServer);
+ ManagedListener http(&sockMgr, httpServer);
+
+ // - Continue to operate until WM_QUIT is processed
+ MSG msg;
+ do {
+ // -=- Make sure we're listening on the right ports.
+ rfb.setPort(port_number, localHost);
+ http.setPort(http_port, localHost);
+
+ // -=- Update the Java viewer's web page port number.
+ httpServer->setRFBport(rfb.sock ? port_number : 0);
+
+ // -=- Update the TCP address filter for both ports, if open.
+ CharArray pattern;
+ pattern.buf = hosts.getData();
+ if (!localHost) {
+ rfb.setFilter(pattern.buf);
+ http.setFilter(pattern.buf);
+ }
+
+ // - If there is a listening port then add the address to the
+ // tray icon's tool-tip text.
+ {
+ const TCHAR* prefix = isServiceProcess() ?
+ _T("VNC Server (Service):") : _T("VNC Server (User):");
+
+ std::list<char*> addrs;
+ if (rfb.sock)
+ rfb.sock->getMyAddresses(&addrs);
+ else
+ addrs.push_front(strDup("Not accepting connections"));
+
+ std::list<char*>::iterator i, next_i;
+ int length = _tcslen(prefix)+1;
+ for (i=addrs.begin(); i!= addrs.end(); i++)
+ length += strlen(*i) + 1;
+
+ TCharArray toolTip(length);
+ _tcscpy(toolTip.buf, prefix);
+ for (i=addrs.begin(); i!= addrs.end(); i=next_i) {
+ next_i = i; next_i ++;
+ TCharArray addr = *i; // Assumes ownership of string
+ _tcscat(toolTip.buf, addr.buf);
+ if (next_i != addrs.end())
+ _tcscat(toolTip.buf, _T(","));
+ }
+ trayIcon.setToolTip(toolTip.buf);
+ }
+
+ vlog.debug("Entering message loop");
+
+ // - Run the server until the registry changes, or we're told to quit
+ while (sockMgr.getMessage(&msg, NULL, 0, 0)) {
+ if (msg.hwnd == 0) {
+ if (msg.message == VNCM_REG_CHANGED)
+ break;
+ if (msg.message == VNCM_COMMAND)
+ doCommand();
+ }
+ TranslateMessage(&msg);
+ DispatchMessage(&msg);
+ }
+
+ } while ((msg.message != WM_QUIT) || runServer);
+
+ vlog.debug("Server exited cleanly");
+ } catch (rdr::SystemException &s) {
+ vlog.error(s.str());
+ result = s.err;
+ } catch (rdr::Exception &e) {
+ vlog.error(e.str());
+ }
+
+ { Lock l(runLock);
+ runServer = false;
+ hostThread = 0;
+ }
+
+ return result;
+}
+
+void VNCServerWin32::stop() {
+ Lock l(runLock);
+ runServer = false;
+ PostThreadMessage(hostThread->getThreadId(), WM_QUIT, 0, 0);
+}
+
+
+bool VNCServerWin32::disconnectClients(const char* reason) {
+ return queueCommand(DisconnectClients, reason, 0);
+}
+
+bool VNCServerWin32::addNewClient(const char* client) {
+ TcpSocket* sock = 0;
+ try {
+ CharArray hostname;
+ int port;
+ getHostAndPort(client, &hostname.buf, &port, 5500);
+ vlog.error("port=%d", port);
+ sock = new TcpSocket(hostname.buf, port);
+ if (queueCommand(AddClient, sock, 0))
+ return true;
+ delete sock;
+ } catch (...) {
+ delete sock;
+ }
+ return false;
+}
+
+
+VNCServerST::queryResult VNCServerWin32::queryConnection(network::Socket* sock,
+ const char* userName,
+ char** reason)
+{
+ if (queryConnectDialog) {
+ *reason = rfb::strDup("Another connection is currently being queried.");
+ return VNCServerST::REJECT;
+ }
+ queryConnectDialog = new QueryConnectDialog(sock, userName, this);
+ queryConnectDialog->startDialog();
+ return VNCServerST::PENDING;
+}
+
+void VNCServerWin32::queryConnectionComplete() {
+ Thread* qcd = queryConnectDialog;
+ queueCommand(QueryConnectionComplete, 0, 0);
+ delete qcd->join();
+}
+
+
+bool VNCServerWin32::queueCommand(Command cmd, const void* data, int len) {
+ Lock l(commandLock);
+ while (command != NoCommand) commandSig.wait();
+ command = cmd;
+ commandData = data;
+ commandDataLen = len;
+ if (PostThreadMessage(hostThread->getThreadId(), VNCM_COMMAND, 0, 0))
+ while (command != NoCommand) commandSig.wait();
+ else
+ return false;
+ return true;
+}
+
+void VNCServerWin32::doCommand() {
+ Lock l(commandLock);
+ if (command == NoCommand) return;
+
+ // Perform the required command
+ switch (command) {
+
+ case DisconnectClients:
+ // Disconnect all currently active VNC Viewers
+ vncServer.closeClients((const char*)commandData);
+ break;
+
+ case AddClient:
+ // Make a reverse connection to a VNC Viewer
+ vncServer.addClient((network::Socket*)commandData, true);
+ sockMgr.addSocket((network::Socket*)commandData, &vncServer);
+ break;
+
+ case QueryConnectionComplete:
+ // The Accept/Reject dialog has completed
+ // Get the result, then clean it up
+ vncServer.approveConnection(queryConnectDialog->getSock(),
+ queryConnectDialog->isAccepted(),
+ "Connection rejected by user");
+ queryConnectDialog = 0;
+ break;
+
+ default:
+ vlog.error("unknown command %d queued", command);
+ };
+
+ // Clear the command and signal completion
+ command = NoCommand;
+ commandSig.signal();
+}
diff --git a/winvnc/VNCServerWin32.h b/winvnc/VNCServerWin32.h
new file mode 100644
index 00000000..c824d542
--- /dev/null
+++ b/winvnc/VNCServerWin32.h
@@ -0,0 +1,99 @@
+/* Copyright (C) 2002-2004 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 __VNCSERVER_WIN32_H__
+#define __VNCSERVER_WIN32_H__
+
+#include <winsock2.h>
+#include <network/TcpSocket.h>
+#include <rfb/VNCServerST.h>
+#include <rfb_win32/RegConfig.h>
+#include <rfb_win32/SDisplay.h>
+#include <rfb_win32/SocketManager.h>
+#include <rfb_win32/TCharArray.h>
+#include <winvnc/QueryConnectDialog.h>
+#include <winvnc/JavaViewer.h>
+
+namespace winvnc {
+
+ class VNCServerWin32 : rfb::VNCServerST::QueryConnectionHandler {
+ public:
+ VNCServerWin32();
+ virtual ~VNCServerWin32();
+
+ // Run the server in the current thread
+ int run();
+
+ // Cause the run() call to return
+ // THREAD-SAFE
+ void stop();
+
+ // Determine whether a viewer is active
+ // THREAD-SAFE
+ bool isServerInUse() const {return isDesktopStarted;}
+
+ // Connect out to the specified VNC Viewer
+ // THREAD-SAFE
+ bool addNewClient(const char* client);
+
+ // Disconnect all connected clients
+ // THREAD-SAFE
+ bool disconnectClients(const char* reason=0);
+
+ // Call used to notify VNCServerST of user accept/reject query completion
+ // CALLED FROM AcceptConnectDialog THREAD
+ void queryConnectionComplete();
+
+ // Overridden VNCServerST::QueryConnectionHandler callback,
+ // used to prompt user to accept or reject a connection.
+ // CALLBACK IN VNCServerST "HOST" THREAD
+ virtual rfb::VNCServerST::queryResult queryConnection(network::Socket* sock,
+ const char* userName,
+ char** reason);
+
+ // Where to read the configuration settings from
+ static const TCHAR* RegConfigPath;
+
+ protected:
+
+ // Perform a particular internal function in the server thread
+ typedef enum {NoCommand, DisconnectClients, AddClient, QueryConnectionComplete} Command;
+ bool queueCommand(Command cmd, const void* data, int len);
+ void doCommand();
+ Command command;
+ const void* commandData;
+ int commandDataLen;
+ rfb::Mutex commandLock;
+ rfb::Condition commandSig;
+
+ // VNCServerWin32 Server-internal state
+ rfb::win32::SDisplay desktop;
+ rfb::VNCServerST vncServer;
+ rfb::Mutex runLock;
+ rfb::Thread* hostThread;
+ bool runServer;
+ bool isDesktopStarted;
+ JavaViewerServer* httpServer;
+ rfb::win32::RegistryReader config;
+ rfb::win32::SocketManager sockMgr;
+ QueryConnectDialog* queryConnectDialog;
+ };
+
+};
+
+#endif
diff --git a/winvnc/buildTime.cxx b/winvnc/buildTime.cxx
new file mode 100644
index 00000000..bab2e137
--- /dev/null
+++ b/winvnc/buildTime.cxx
@@ -0,0 +1 @@
+const char* buildTime = "Built on " __DATE__ " at " __TIME__; \ No newline at end of file
diff --git a/winvnc/connected.ico b/winvnc/connected.ico
new file mode 100644
index 00000000..d996bcda
--- /dev/null
+++ b/winvnc/connected.ico
Binary files differ
diff --git a/winvnc/java/index.vnc b/winvnc/java/index.vnc
new file mode 100644
index 00000000..aecb6131
--- /dev/null
+++ b/winvnc/java/index.vnc
@@ -0,0 +1,13 @@
+<HTML>
+<HEAD>
+<TITLE>
+VNC viewer for Java
+</TITLE>
+</HEAD>
+<BODY>
+<APPLET CODE=vncviewer/VNCViewer.class ARCHIVE=vncviewer.jar
+ WIDTH=400 HEIGHT=250>
+<PARAM name="port" value="$PORT">
+</APPLET>
+</BODY>
+</HTML>
diff --git a/winvnc/java/logo150x150.gif b/winvnc/java/logo150x150.gif
new file mode 100644
index 00000000..f1699ba5
--- /dev/null
+++ b/winvnc/java/logo150x150.gif
Binary files differ
diff --git a/winvnc/java/vncviewer.jar b/winvnc/java/vncviewer.jar
new file mode 100644
index 00000000..3c92b5bb
--- /dev/null
+++ b/winvnc/java/vncviewer.jar
Binary files differ
diff --git a/winvnc/msvcwarning.h b/winvnc/msvcwarning.h
new file mode 100644
index 00000000..53a0678d
--- /dev/null
+++ b/winvnc/msvcwarning.h
@@ -0,0 +1,19 @@
+/* Copyright (C) 2002-2003 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.
+ */
+#pragma warning( disable : 4800 ) // forcing bool 'true' or 'false'
+#pragma warning( disable : 4786 ) // debug identifier truncated
diff --git a/winvnc/resource.h b/winvnc/resource.h
new file mode 100644
index 00000000..81c89e22
--- /dev/null
+++ b/winvnc/resource.h
@@ -0,0 +1,37 @@
+//{{NO_DEPENDENCIES}}
+// Microsoft Developer Studio generated include file.
+// Used by winvnc.rc
+//
+#define IDR_MANIFEST 1
+#define IDI_ICON 101
+#define IDR_TRAY 102
+#define IDD_DIALOG1 103
+#define IDD_ABOUT 104
+#define IDI_CONNECTED 105
+#define IDR_VNCVIEWER_JAR 106
+#define IDD_QUERY_CONNECT 107
+#define IDD_ADD_NEW_CLIENT 108
+#define IDC_DESCRIPTION 1000
+#define IDC_BUILDTIME 1001
+#define IDC_VERSION 1002
+#define IDC_COPYRIGHT 1003
+#define IDC_QUERY_COUNTDOWN 1008
+#define IDC_QUERY_USER 1009
+#define IDC_QUERY_HOST 1010
+#define IDC_HOST 1011
+#define ID_OPTIONS 40001
+#define ID_CLOSE 40002
+#define ID_ABOUT 40003
+#define ID_DISCONNECT 40004
+#define ID_CONNECT 40005
+
+// Next default values for new objects
+//
+#ifdef APSTUDIO_INVOKED
+#ifndef APSTUDIO_READONLY_SYMBOLS
+#define _APS_NEXT_RESOURCE_VALUE 109
+#define _APS_NEXT_COMMAND_VALUE 40006
+#define _APS_NEXT_CONTROL_VALUE 1012
+#define _APS_NEXT_SYMED_VALUE 101
+#endif
+#endif
diff --git a/winvnc/winvnc.cxx b/winvnc/winvnc.cxx
new file mode 100644
index 00000000..5ba6ebc5
--- /dev/null
+++ b/winvnc/winvnc.cxx
@@ -0,0 +1,249 @@
+/* Copyright (C) 2002-2004 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.
+ */
+
+// -=- VNC Server 4.0 for Windows (WinVNC4)
+
+#include <string.h>
+#ifdef WIN32
+#define strcasecmp _stricmp
+#endif
+
+#include <winvnc/VNCServerWin32.h>
+#include <winvnc/VNCServerService.h>
+#include <winvnc/AddNewClientDialog.h>
+
+#include <rfb/Logger_stdio.h>
+#include <rfb/Logger_file.h>
+#include <rfb/LogWriter.h>
+#include <rfb_win32/AboutDialog.h>
+#include <rfb_win32/Win32Util.h>
+#include <network/TcpSocket.h>
+
+using namespace winvnc;
+using namespace rfb;
+using namespace win32;
+
+static LogWriter vlog("main");
+
+TStr rfb::win32::AppName("VNC Server");
+
+
+static bool runAsService = false;
+static bool runServer = true;
+static bool close_console = false;
+
+
+//
+// -=- processParams
+// Read in the command-line parameters and interpret them.
+//
+
+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() {
+ printf("Command-line options:\n");
+ printf(" -connect [<host[::port]>] - Connect an existing WinVNC server to a listening viewer.\n");
+ printf(" -disconnect - Disconnect all clients from an existing WinVNC server.\n");
+ printf(" -register <options...> - Register WinVNC server as a system service.\n");
+ printf(" -unregister - Remove WinVNC server from the list of system services.\n");
+ printf(" -start - Start the WinVNC server system service.\n");
+ printf(" -stop - Stop the WinVNC server system service.\n");
+ printf(" -status - Query the WinVNC service status.\n");
+ printf(" -help - Provide usage information.\n");
+ printf(" -noconsole - Run without a console (i.e. no stderr/stdout)\n");
+ printf(" <setting>=<value> - Set the named configuration parameter.\n");
+ printf(" (Parameter values specified on the command-line override those specified by other configuration methods.)\n");
+ printf("\nLog names:\n");
+ LogWriter::listLogWriters();
+ printf("\nLog destinations:\n");
+ Logger::listLoggers();
+ printf("\nAvailable configuration parameters:\n");
+ Configuration::listParams();
+}
+
+void
+processParams(int argc, const char* argv[]) {
+ for (int i=1; i<argc; i++) {
+ try {
+
+ if (strcasecmp(argv[i], "-connect") == 0) {
+ runServer = false;
+ CharArray host;
+ if (i+1 < argc) {
+ host.buf = strDup(argv[i+1]);
+ } else {
+ AddNewClientDialog ancd;
+ if (ancd.showDialog())
+ host.buf = strDup(ancd.getHostName());
+ }
+ if (host.buf) {
+ HWND hwnd = FindWindow(0, _T("winvnc::IPC_Interface"));
+ COPYDATASTRUCT copyData;
+ copyData.dwData = 1; // *** AddNewClient
+ copyData.cbData = strlen(host.buf);
+ copyData.lpData = (void*)host.buf;
+ i++;
+ SendMessage(hwnd, WM_COPYDATA, 0, (LPARAM)&copyData);
+ printf("Sent connect request to VNC Server...\n");
+ }
+ } else if (strcasecmp(argv[i], "-disconnect") == 0) {
+ HWND hwnd = FindWindow(0, _T("winvnc::IPC_Interface"));
+ COPYDATASTRUCT copyData;
+ copyData.dwData = 2; // *** DisconnectClients
+ copyData.lpData = 0;
+ copyData.cbData = 0;
+ SendMessage(hwnd, WM_COPYDATA, 0, (LPARAM)&copyData);
+ printf("Sent disconnect request to VNC Server...\n");
+ runServer = false;
+ } else if (strcasecmp(argv[i], "-start") == 0) {
+ printf("Attempting to start service...\n");
+ runServer = false;
+ if (rfb::win32::startService(VNCServerService::Name))
+ printf("Started service successfully\n");
+ } else if (strcasecmp(argv[i], "-stop") == 0) {
+ printf("Attempting to stop service...\n");
+ runServer = false;
+ if (rfb::win32::stopService(VNCServerService::Name))
+ printf("Stopped service successfully\n");
+ } else if (strcasecmp(argv[i], "-status") == 0) {
+ printf("Querying service status...\n");
+ runServer = false;
+ rfb::win32::printServiceStatus(VNCServerService::Name);
+
+ } else if (strcasecmp(argv[i], "-service") == 0) {
+ printf("Run in service mode\n");
+ runAsService = true;
+
+ } else if (strcasecmp(argv[i], "-register") == 0) {
+ printf("Attempting to register service...\n");
+ runServer = false;
+ int j = i;
+ i = argc;
+ if (rfb::win32::registerService(VNCServerService::Name,
+ _T("VNC Server Version 4"),
+ argc-(j+1), &argv[j+1]))
+ printf("Registered service successfully\n");
+ } else if (strcasecmp(argv[i], "-unregister") == 0) {
+ printf("Attempting to unregister service...\n");
+ runServer = false;
+ if (rfb::win32::unregisterService(VNCServerService::Name))
+ printf("Unregistered service successfully\n");
+
+ } else if (strcasecmp(argv[i], "-noconsole") == 0) {
+ close_console = true;
+
+ } else if ((strcasecmp(argv[i], "-help") == 0) ||
+ (strcasecmp(argv[i], "--help") == 0) ||
+ (strcasecmp(argv[i], "-h") == 0) ||
+ (strcasecmp(argv[i], "/?") == 0)) {
+ runServer = false;
+ programUsage();
+ break;
+
+ } else {
+ // Try to process <option>=<value>, or -<bool>
+ if (Configuration::setParam(argv[i], true))
+ continue;
+ // Try to process -<option> <value>
+ if ((argv[i][0] == '-') && (i+1 < argc)) {
+ if (Configuration::setParam(&argv[i][1], argv[i+1], true)) {
+ i++;
+ continue;
+ }
+ }
+ // Nope. Show them usage and don't run the server
+ runServer = false;
+ programUsage();
+ break;
+ }
+
+ } catch (rdr::Exception& e) {
+ vlog.error(e.str());
+ }
+ }
+}
+
+
+//
+// -=- main
+//
+
+int main(int argc, const char* argv[]) {
+ int result = 0;
+
+ try {
+ // - Initialise the available loggers
+ //freopen("\\\\drupe\\tjr\\WinVNC4.log","ab",stderr);
+ //setbuf(stderr, 0);
+ initStdIOLoggers();
+ initFileLogger("C:\\temp\\WinVNC4.log");
+ rfb::win32::initEventLogLogger(VNCServerService::Name);
+
+ // - By default, just log errors to stderr
+ logParams.setParam("*:stderr:0");
+
+ // - Print program details and process the command line
+ programInfo();
+ processParams(argc, argv);
+
+ // - Run the server if required
+ if (runServer) {
+ if (close_console) {
+ vlog.info("closing console");
+ if (!FreeConsole())
+ vlog.info("unable to close console:%u", GetLastError());
+ }
+
+ network::TcpSocket::initTcpSockets();
+ VNCServerWin32 server;
+
+ if (runAsService) {
+ printf("Starting Service-Mode VNC Server.\n");
+ VNCServerService service(server);
+ service.start();
+ result = service.getStatus().dwWin32ExitCode;
+ } else {
+ printf("Starting User-Mode VNC Server.\n");
+ result = server.run();
+ }
+ }
+
+ vlog.debug("WinVNC service destroyed");
+ } catch (rdr::Exception& e) {
+ try {
+ vlog.error("Fatal Error: %s", e.str());
+ } catch (...) {
+ fprintf(stderr, "WinVNC: Fatal Error: %s\n", e.str());
+ }
+ if (!runAsService)
+ MsgBox(0, TStr(e.str()), MB_ICONERROR | MB_OK);
+ }
+
+ vlog.debug("WinVNC process quitting");
+ return result;
+}
diff --git a/winvnc/winvnc.dsp b/winvnc/winvnc.dsp
new file mode 100644
index 00000000..aa9580ab
--- /dev/null
+++ b/winvnc/winvnc.dsp
@@ -0,0 +1,228 @@
+# Microsoft Developer Studio Project File - Name="winvnc" - Package Owner=<4>
+# Microsoft Developer Studio Generated Build File, Format Version 6.00
+# ** DO NOT EDIT **
+
+# TARGTYPE "Win32 (x86) Console Application" 0x0103
+
+CFG=winvnc - 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 "winvnc.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 "winvnc.mak" CFG="winvnc - Win32 Debug Unicode"
+!MESSAGE
+!MESSAGE Possible choices for configuration are:
+!MESSAGE
+!MESSAGE "winvnc - Win32 Release" (based on "Win32 (x86) Console Application")
+!MESSAGE "winvnc - Win32 Debug" (based on "Win32 (x86) Console Application")
+!MESSAGE "winvnc - Win32 Debug Unicode" (based on "Win32 (x86) Console Application")
+!MESSAGE
+
+# Begin Project
+# PROP AllowPerConfigDependencies 0
+# PROP Scc_ProjName ""
+# PROP Scc_LocalPath ""
+CPP=cl.exe
+RSC=rc.exe
+
+!IF "$(CFG)" == "winvnc - 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"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c
+# ADD CPP /nologo /MT /W3 /GX /O2 /I ".." /FI"msvcwarning.h" /D "NDEBUG" /D "_CONSOLE" /D "WIN32" /D "_MBCS" /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
+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 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:console /machine:I386
+# ADD LINK32 advapi32.lib user32.lib gdi32.lib ws2_32.lib version.lib comctl32.lib shell32.lib ole32.lib /nologo /subsystem:console /machine:I386 /out:"../Release/winvnc4.exe"
+# SUBTRACT LINK32 /profile
+# Begin Special Build Tool
+SOURCE="$(InputPath)"
+PreLink_Desc=Updating buildTime
+PreLink_Cmds=cl /c /nologo /FoRelease\ /FdRelease\ /MT buildTime.cxx
+# End Special Build Tool
+
+!ELSEIF "$(CFG)" == "winvnc - 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"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c
+# ADD CPP /nologo /MTd /W3 /Gm /GX /Zi /Od /I ".." /FI"msvcwarning.h" /D "_DEBUG" /D "_CONSOLE" /D "WIN32" /D "_MBCS" /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
+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 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:console /debug /machine:I386 /pdbtype:sept
+# ADD LINK32 advapi32.lib user32.lib gdi32.lib ws2_32.lib version.lib comctl32.lib shell32.lib ole32.lib /nologo /subsystem:console /incremental:no /debug /machine:I386 /out:"../Debug/winvnc4.exe" /fixed:no
+# SUBTRACT LINK32 /profile
+# Begin Special Build Tool
+SOURCE="$(InputPath)"
+PreLink_Desc=Updating buildTime
+PreLink_Cmds=cl /c /nologo /FoDebug\ /FdDebug\ /MTd buildTime.cxx
+# End Special Build Tool
+
+!ELSEIF "$(CFG)" == "winvnc - Win32 Debug Unicode"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 1
+# PROP BASE Output_Dir "winvnc___Win32_Debug_Unicode"
+# PROP BASE Intermediate_Dir "winvnc___Win32_Debug_Unicode"
+# PROP BASE Ignore_Export_Lib 0
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 1
+# PROP Output_Dir "..\Debug_Unicode"
+# PROP Intermediate_Dir "Debug_Unicode"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /MTd /W3 /Gm /GX /Zi /Od /I ".." /FI"msvcwarning.h" /D "_DEBUG" /D "_CONSOLE" /D "WIN32" /D "_MBCS" /YX /FD /GZ /c
+# ADD CPP /nologo /MTd /W3 /Gm /GX /Zi /Od /I ".." /FI"msvcwarning.h" /D "_CONSOLE" /D "_DEBUG" /D "WIN32" /D "_UNICODE" /D "UNICODE" /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
+LINK32=link.exe
+# ADD BASE LINK32 user32.lib gdi32.lib advapi32.lib ws2_32.lib version.lib comctl32.lib shell32.lib /nologo /subsystem:console /incremental:no /debug /machine:I386 /out:"../Debug/winvnc4.exe" /fixed:no
+# SUBTRACT BASE LINK32 /pdb:none
+# ADD LINK32 advapi32.lib user32.lib gdi32.lib ws2_32.lib version.lib comctl32.lib shell32.lib ole32.lib /nologo /subsystem:console /incremental:no /debug /machine:I386 /out:"..\Debug_Unicode/winvnc4.exe" /fixed:no
+# SUBTRACT LINK32 /pdb:none
+# Begin Special Build Tool
+SOURCE="$(InputPath)"
+PreLink_Desc=Updating buildTime
+PreLink_Cmds=cl /c /nologo /FoDebug\ /FdDebug\ /MTd buildTime.cxx
+# End Special Build Tool
+
+!ENDIF
+
+# Begin Target
+
+# Name "winvnc - Win32 Release"
+# Name "winvnc - Win32 Debug"
+# Name "winvnc - Win32 Debug Unicode"
+# 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=.\JavaViewer.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\QueryConnectDialog.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\STrayIcon.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\VNCServerService.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\VNCServerWin32.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\winvnc.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\winvnc.rc
+# End Source File
+# End Group
+# Begin Group "Header Files"
+
+# PROP Default_Filter "h;hpp;hxx;hm;inl"
+# Begin Source File
+
+SOURCE=.\AddNewClientDialog.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\JavaViewer.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\QueryConnectDialog.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\STrayIcon.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\VNCServerService.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\VNCServerWin32.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=.\connected.ico
+# End Source File
+# Begin Source File
+
+SOURCE=.\java\logo150x150.gif
+# End Source File
+# Begin Source File
+
+SOURCE=.\winvnc.ico
+# End Source File
+# End Group
+# Begin Source File
+
+SOURCE=.\java\index.vnc
+# End Source File
+# Begin Source File
+
+SOURCE=.\java\vncviewer.jar
+# End Source File
+# Begin Source File
+
+SOURCE=.\vncviewer.jar
+# End Source File
+# Begin Source File
+
+SOURCE=.\winvnc4.exe.manifest
+# End Source File
+# End Target
+# End Project
diff --git a/winvnc/winvnc.ico b/winvnc/winvnc.ico
new file mode 100644
index 00000000..55b16bd9
--- /dev/null
+++ b/winvnc/winvnc.ico
Binary files differ
diff --git a/winvnc/winvnc.rc b/winvnc/winvnc.rc
new file mode 100644
index 00000000..22d0ba99
--- /dev/null
+++ b/winvnc/winvnc.rc
@@ -0,0 +1,254 @@
+//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.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
+
+#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
+
+
+#ifndef _MAC
+/////////////////////////////////////////////////////////////////////////////
+//
+// Version
+//
+
+VS_VERSION_INFO VERSIONINFO
+ FILEVERSION 4,0,0,26
+ PRODUCTVERSION 4,0,0,26
+ 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", "RealVNC Ltd.\0"
+ VALUE "FileDescription", "VNC Server for Win32\0"
+ VALUE "FileVersion", "4.0\0"
+ VALUE "InternalName", "WinVNC 4.0\0"
+ VALUE "LegalCopyright", "Copyright © RealVNC Ltd. 2002-2004\0"
+ VALUE "LegalTrademarks", "RealVNC\0"
+ VALUE "OriginalFilename", "winvnc4.exe\0"
+ VALUE "PrivateBuild", "\0"
+ VALUE "ProductName", "VNC Server 4.0\0"
+ VALUE "ProductVersion", "4.0\0"
+ VALUE "SpecialBuild", "\0"
+ END
+ END
+ BLOCK "VarFileInfo"
+ BEGIN
+ VALUE "Translation", 0x809, 1200
+ END
+END
+
+#endif // !_MAC
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Icon
+//
+
+// Icon with lowest ID value placed first to ensure application icon
+// remains consistent on all systems.
+IDI_ICON ICON DISCARDABLE "winvnc.ico"
+IDI_CONNECTED ICON DISCARDABLE "connected.ico"
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Menu
+//
+
+IDR_TRAY MENU DISCARDABLE
+BEGIN
+ POPUP "Tray Menu"
+ BEGIN
+ MENUITEM "&Options...", ID_OPTIONS
+ MENUITEM SEPARATOR
+ MENUITEM "Add &New Client", ID_CONNECT
+ MENUITEM "&Disconnect Clients", ID_DISCONNECT
+ MENUITEM SEPARATOR
+ MENUITEM "&Close VNC Server", ID_CLOSE
+ MENUITEM "&About...", ID_ABOUT
+ END
+END
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Dialog
+//
+
+IDD_ABOUT DIALOG DISCARDABLE 0, 0, 249, 92
+STYLE DS_MODALFRAME | DS_SETFOREGROUND | DS_CENTER | WS_POPUP | WS_CAPTION |
+ WS_SYSMENU
+CAPTION "About VNC Server for Windows"
+FONT 8, "MS Sans Serif"
+BEGIN
+ DEFPUSHBUTTON "OK",IDOK,195,70,47,15
+ ICON IDI_ICON,IDC_STATIC,7,7,20,20
+ LTEXT ">appname<",IDC_DESCRIPTION,40,7,125,18
+ LTEXT ">version<",IDC_VERSION,165,7,77,18
+ LTEXT ">buildtime<",IDC_BUILDTIME,40,25,202,15
+ LTEXT ">copyright<",IDC_COPYRIGHT,40,40,202,15
+ LTEXT "See http://www.realvnc.com for more information on VNC.",
+ IDC_STATIC,40,55,202,15
+END
+
+IDD_QUERY_CONNECT DIALOG DISCARDABLE 0, 0, 164, 93
+STYLE DS_MODALFRAME | DS_SETFOREGROUND | DS_CENTER | WS_POPUP | WS_CAPTION |
+ WS_SYSMENU
+CAPTION "VNC Server : Accept Connection?"
+FONT 8, "MS Sans Serif"
+BEGIN
+ DEFPUSHBUTTON "&Reject",IDCANCEL,105,72,52,14
+ PUSHBUTTON "&Accept",IDOK,7,72,53,14
+ RTEXT "User:",IDC_STATIC,7,10,28,15,SS_CENTERIMAGE
+ RTEXT "Host:",IDC_STATIC,7,30,28,15,SS_CENTERIMAGE
+ CTEXT "Seconds until automatic reject:",IDC_STATIC,7,50,113,15,
+ SS_CENTERIMAGE
+ LTEXT "-",IDC_QUERY_COUNTDOWN,125,50,32,15,SS_CENTERIMAGE
+ LTEXT "-",IDC_QUERY_USER,40,10,117,15,SS_CENTERIMAGE
+ LTEXT "-",IDC_QUERY_HOST,40,30,117,15,SS_CENTERIMAGE
+END
+
+IDD_ADD_NEW_CLIENT DIALOG DISCARDABLE 0, 0, 183, 53
+STYLE DS_MODALFRAME | DS_SETFOREGROUND | DS_CENTER | WS_POPUP | WS_VISIBLE |
+ WS_CAPTION | WS_SYSMENU
+CAPTION "VNC Server : Add New Client"
+FONT 8, "MS Sans Serif"
+BEGIN
+ EDITTEXT IDC_HOST,70,10,105,15,ES_AUTOHSCROLL
+ DEFPUSHBUTTON "OK",IDOK,70,32,50,14
+ PUSHBUTTON "Cancel",IDCANCEL,125,32,50,14
+ ICON IDI_ICON,IDC_STATIC,7,10,21,20,SS_REALSIZEIMAGE
+ CONTROL "Viewer:",IDC_STATIC,"Static",SS_LEFTNOWORDWRAP |
+ SS_CENTERIMAGE | WS_GROUP,35,10,30,15
+END
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// HTTPFILE
+//
+
+/VNCVIEWER.JAR HTTPFILE DISCARDABLE "java\\vncviewer.jar"
+/LOGO150X150.GIF HTTPFILE DISCARDABLE "java\\logo150x150.gif"
+/INDEX.VNC HTTPFILE DISCARDABLE "java\\index.vnc"
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// 24
+//
+
+IDR_MANIFEST 24 DISCARDABLE "winvnc4.exe.manifest"
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// DESIGNINFO
+//
+
+#ifdef APSTUDIO_INVOKED
+GUIDELINES DESIGNINFO DISCARDABLE
+BEGIN
+ IDD_QUERY_CONNECT, DIALOG
+ BEGIN
+ LEFTMARGIN, 7
+ RIGHTMARGIN, 157
+ VERTGUIDE, 35
+ VERTGUIDE, 40
+ VERTGUIDE, 60
+ VERTGUIDE, 120
+ VERTGUIDE, 125
+ TOPMARGIN, 7
+ BOTTOMMARGIN, 86
+ HORZGUIDE, 10
+ HORZGUIDE, 25
+ HORZGUIDE, 30
+ HORZGUIDE, 45
+ HORZGUIDE, 50
+ HORZGUIDE, 65
+ END
+
+ IDD_ADD_NEW_CLIENT, DIALOG
+ BEGIN
+ LEFTMARGIN, 7
+ RIGHTMARGIN, 176
+ VERTGUIDE, 35
+ VERTGUIDE, 65
+ VERTGUIDE, 70
+ VERTGUIDE, 120
+ VERTGUIDE, 125
+ VERTGUIDE, 175
+ TOPMARGIN, 7
+ BOTTOMMARGIN, 46
+ HORZGUIDE, 10
+ HORZGUIDE, 25
+ 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/winvnc/winvnc4.exe.manifest b/winvnc/winvnc4.exe.manifest
new file mode 100644
index 00000000..d5a2b87a
--- /dev/null
+++ b/winvnc/winvnc4.exe.manifest
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
+<assemblyIdentity
+ version="4.0.0.26"
+ processorArchitecture="X86"
+ name="RealVNC.winvnc4.exe"
+ type="win32"
+/>
+<description>.NET control deployment tool</description>
+<dependency>
+ <dependentAssembly>
+ <assemblyIdentity
+ type="win32"
+ name="Microsoft.Windows.Common-Controls"
+ version="6.0.0.0"
+ processorArchitecture="X86"
+ publicKeyToken="6595b64144ccf1df"
+ language="*"
+ />
+ </dependentAssembly>
+</dependency>
+</assembly>
diff --git a/wm_hooks/msvcwarning.h b/wm_hooks/msvcwarning.h
new file mode 100644
index 00000000..d0c98c3c
--- /dev/null
+++ b/wm_hooks/msvcwarning.h
@@ -0,0 +1,19 @@
+/* Copyright (C) 2002-2003 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.
+ */
+#pragma warning( disable : 4244 ) // loss of data e.g. int to char
+#pragma warning( disable : 4800 ) // forcing bool 'true' or 'false'
diff --git a/wm_hooks/resource.h b/wm_hooks/resource.h
new file mode 100644
index 00000000..0d8f646f
--- /dev/null
+++ b/wm_hooks/resource.h
@@ -0,0 +1,15 @@
+//{{NO_DEPENDENCIES}}
+// Microsoft Developer Studio generated include file.
+// Used by wm_hooks.rc
+//
+
+// Next default values for new objects
+//
+#ifdef APSTUDIO_INVOKED
+#ifndef APSTUDIO_READONLY_SYMBOLS
+#define _APS_NEXT_RESOURCE_VALUE 101
+#define _APS_NEXT_COMMAND_VALUE 40001
+#define _APS_NEXT_CONTROL_VALUE 1000
+#define _APS_NEXT_SYMED_VALUE 101
+#endif
+#endif
diff --git a/wm_hooks/wm_hooks.cxx b/wm_hooks/wm_hooks.cxx
new file mode 100644
index 00000000..6923db7e
--- /dev/null
+++ b/wm_hooks/wm_hooks.cxx
@@ -0,0 +1,462 @@
+/* Copyright (C) 2002-2003 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.
+ */
+
+// -=- wm_hooks.cxx
+//
+// Window Message Hooks Dynamic Link library
+
+#define _WIN32_WINNT 0x0400
+#include <tchar.h>
+
+#include <wm_hooks/wm_hooks.h>
+
+UINT WM_HK_PingThread = RegisterWindowMessage(_T("RFB.WM_Hooks.PingThread"));
+
+UINT WM_HK_WindowChanged = RegisterWindowMessage(_T("RFB.WM_Hooks.WindowChanged"));
+UINT WM_Hooks_WindowChanged() {
+ return WM_HK_WindowChanged;
+}
+
+UINT WM_HK_WindowClientAreaChanged = RegisterWindowMessage(_T("RFB.WM_Hooks.WindowClientAreaChanged"));
+UINT WM_Hooks_WindowClientAreaChanged() {
+ return WM_HK_WindowClientAreaChanged;
+}
+
+UINT WM_HK_WindowBorderChanged = RegisterWindowMessage(_T("RFB.WM_Hooks.WindowBorderChanged"));
+UINT WM_Hooks_WindowBorderChanged() {
+ return WM_HK_WindowBorderChanged;
+}
+
+UINT WM_HK_RectangleChanged = RegisterWindowMessage(_T("RFB.WM_Hooks.RectangleChanged"));
+UINT WM_Hooks_RectangleChanged() {
+ return WM_HK_RectangleChanged;
+}
+
+UINT WM_HK_CursorChanged = RegisterWindowMessage(_T("RFB.WM_Hooks.CursorChanged"));
+UINT WM_Hooks_CursorChanged() {
+ return WM_HK_CursorChanged;
+}
+
+#ifdef _DEBUG
+UINT WM_HK_Diagnostic = RegisterWindowMessage(_T("RFB.WM_Hooks.Diagnostic"));
+UINT WM_Hooks_Diagnostic() {
+ return WM_HK_Diagnostic;
+}
+#endif
+
+ATOM ATOM_Popup_Selection = GlobalAddAtom(_T("RFB.WM_Hooks.PopupSelectionAtom"));
+
+//
+// -=- DLL entry point
+//
+
+HINSTANCE dll_instance = 0;
+
+BOOL WINAPI DllMain(HANDLE instance, ULONG reason, LPVOID reserved) {
+ switch (reason) {
+ case DLL_PROCESS_ATTACH:
+ dll_instance = (HINSTANCE)instance;
+ return TRUE;
+ case DLL_PROCESS_DETACH:
+ return TRUE;
+ case DLL_THREAD_DETACH:
+ WM_Hooks_Remove(GetCurrentThreadId());
+ return TRUE;
+ default:
+ return TRUE;
+ };
+}
+
+//
+// -=- Display update hooks
+//
+
+#pragma data_seg(".WM_Hooks_Shared")
+DWORD hook_owner = 0;
+DWORD hook_target = 0;
+HHOOK hook_CallWndProc = 0;
+HHOOK hook_CallWndProcRet = 0;
+HHOOK hook_GetMessage = 0;
+HHOOK hook_DialogMessage = 0;
+BOOL enable_cursor_shape = FALSE;
+HCURSOR cursor = 0;
+#ifdef _DEBUG
+UINT diagnostic_min=1;
+UINT diagnostic_max=0;
+#endif
+#pragma data_seg()
+
+#ifdef _DEBUG
+DLLEXPORT void WM_Hooks_SetDiagnosticRange(UINT min, UINT max) {
+ diagnostic_min = min; diagnostic_max=max;
+}
+#endif
+
+bool NotifyHookOwner(UINT event, WPARAM wParam, LPARAM lParam) {
+ if (hook_owner) {
+ return PostThreadMessage(hook_owner, event, wParam, lParam)!=0;
+ /*
+ if (last_event)
+ return PostThreadMessage(hook_owner, last_event, last_wParam, last_lParam);
+ last_event = event;
+ last_wParam = wParam;
+ last_lParam = lParam;
+ return true;
+ */
+ }
+ return false;
+}
+
+bool NotifyWindow(HWND hwnd, UINT msg) {
+ return NotifyHookOwner(WM_HK_WindowChanged, msg, (LPARAM)hwnd);
+}
+bool NotifyWindowBorder(HWND hwnd, UINT msg) {
+ return NotifyHookOwner(WM_HK_WindowBorderChanged, msg, (LPARAM)hwnd);
+}
+bool NotifyWindowClientArea(HWND hwnd, UINT msg) {
+ return NotifyHookOwner(WM_HK_WindowClientAreaChanged, msg, (LPARAM)hwnd);
+}
+bool NotifyRectangle(RECT* rect) {
+ WPARAM w = MAKELONG((SHORT)rect->left, (SHORT)rect->top);
+ LPARAM l = MAKELONG((SHORT)rect->right, (SHORT)rect->bottom);
+ return NotifyHookOwner(WM_HK_RectangleChanged, w, l);
+}
+bool NotifyCursor(HCURSOR cursor) {
+ return NotifyHookOwner(WM_HK_CursorChanged, 0, (LPARAM)cursor);
+}
+
+void ProcessWindowMessage(UINT msg, HWND wnd, WPARAM wParam, LPARAM lParam) {
+#ifdef _DEBUG
+ if ((msg >= diagnostic_min) && (msg <= diagnostic_max))
+ PostThreadMessage(hook_owner, WM_HK_Diagnostic, msg, (LPARAM)wnd);
+#endif
+ if (!IsWindowVisible(wnd)) return;
+ switch (msg) {
+
+ // -=- Border update events
+ case WM_NCPAINT:
+ case WM_NCACTIVATE:
+ NotifyWindowBorder(wnd, msg);
+ break;
+
+ // -=- Client area update events
+ case BM_SETCHECK:
+ case BM_SETSTATE:
+ case EM_SETSEL:
+ case WM_CHAR:
+ case WM_ENABLE:
+ case WM_KEYUP:
+ case WM_LBUTTONUP:
+ case WM_MBUTTONUP:
+ case WM_PALETTECHANGED:
+ case WM_RBUTTONUP:
+ case WM_SYSCOLORCHANGE:
+ case WM_SETTEXT:
+ case WM_SETFOCUS:
+ //case WM_TIMER:
+ NotifyWindowClientArea(wnd, msg);
+ break;
+ case WM_HSCROLL:
+ case WM_VSCROLL:
+ if (((int) LOWORD(wParam) == SB_THUMBTRACK) || ((int) LOWORD(wParam) == SB_ENDSCROLL))
+ NotifyWindow(wnd, msg);
+ break;
+
+ case WM_WINDOWPOSCHANGING:
+ case WM_DESTROY:
+ {
+ RECT wrect;
+ if (GetWindowRect(wnd, &wrect)) {
+ NotifyRectangle(&wrect);
+ }
+ }
+ break;
+
+ case WM_PAINT:
+ // *** could improve this
+ NotifyWindowClientArea(wnd, msg);
+ break;
+
+ // Handle pop-up menus appearing
+ case 482:
+ NotifyWindow(wnd, 482);
+ break;
+
+ // Handle pop-up menus having items selected
+ case 485:
+ {
+ HANDLE prop = GetProp(wnd, (LPCTSTR) MAKELONG(ATOM_Popup_Selection, 0));
+ if (prop != (HANDLE) wParam) {
+ NotifyWindow(wnd, 485);
+ SetProp(wnd,
+ (LPCTSTR) MAKELONG(ATOM_Popup_Selection, 0),
+ (HANDLE) wParam);
+ }
+ }
+ break;
+
+ case WM_NCMOUSEMOVE:
+ case WM_MOUSEMOVE:
+ if (enable_cursor_shape) {
+ HCURSOR new_cursor = GetCursor();
+ if (new_cursor != cursor) {
+ cursor = new_cursor;
+ NotifyCursor(cursor);
+ }
+ }
+ break;
+
+ /* ***
+ if (prf_use_GetUpdateRect)
+ {
+ HRGN region;
+ region = CreateRectRgn(0, 0, 0, 0);
+
+ // Get the affected region
+ if (GetUpdateRgn(hWnd, region, FALSE) != ERROR)
+ {
+ int buffsize;
+ UINT x;
+ RGNDATA *buff;
+ POINT TopLeft;
+
+ // Get the top-left point of the client area
+ TopLeft.x = 0;
+ TopLeft.y = 0;
+ if (!ClientToScreen(hWnd, &TopLeft))
+ break;
+
+ // Get the size of buffer required
+ buffsize = GetRegionData(region, 0, 0);
+ if (buffsize != 0)
+ {
+ buff = (RGNDATA *) new BYTE [buffsize];
+ if (buff == NULL)
+ break;
+
+ // Now get the region data
+ if(GetRegionData(region, buffsize, buff))
+ {
+ for (x=0; x<(buff->rdh.nCount); x++)
+ {
+ // Obtain the rectangles from the list
+ RECT *urect = (RECT *) (((BYTE *) buff) + sizeof(RGNDATAHEADER) + (x * sizeof(RECT)));
+ SendDeferredUpdateRect(
+ hWnd,
+ (SHORT) (TopLeft.x + urect->left),
+ (SHORT) (TopLeft.y + urect->top),
+ (SHORT) (TopLeft.x + urect->right),
+ (SHORT) (TopLeft.y + urect->bottom)
+ );
+ }
+ }
+
+ delete [] buff;
+ }
+ }
+
+ // Now free the region
+ if (region != NULL)
+ DeleteObject(region);
+ }
+ */
+ };
+}
+
+LRESULT CALLBACK HookCallWndProc(int nCode, WPARAM wParam, LPARAM lParam) {
+ if (nCode == HC_ACTION) {
+ CWPSTRUCT* info = (CWPSTRUCT*) lParam;
+ ProcessWindowMessage(info->message, info->hwnd, info->wParam, info->lParam);
+ }
+ return CallNextHookEx(hook_CallWndProc, nCode, wParam, lParam);
+}
+
+LRESULT CALLBACK HookCallWndProcRet(int nCode, WPARAM wParam, LPARAM lParam) {
+ if (nCode == HC_ACTION) {
+ CWPRETSTRUCT* info = (CWPRETSTRUCT*) lParam;
+ ProcessWindowMessage(info->message, info->hwnd, info->wParam, info->lParam);
+ }
+ return CallNextHookEx(hook_CallWndProcRet, nCode, wParam, lParam);
+}
+
+LRESULT CALLBACK HookGetMessage(int nCode, WPARAM wParam, LPARAM lParam) {
+ if (nCode == HC_ACTION) {
+ if (wParam & PM_REMOVE) {
+ MSG* msg = (MSG*) lParam;
+ ProcessWindowMessage(msg->message, msg->hwnd, msg->wParam, msg->lParam);
+ }
+ }
+ return CallNextHookEx(hook_GetMessage, nCode, wParam, lParam);
+}
+
+LRESULT CALLBACK HookDialogMessage(int nCode, WPARAM wParam, LPARAM lParam) {
+ if (nCode == HC_ACTION) {
+ MSG* msg = (MSG*) lParam;
+ ProcessWindowMessage(msg->message, msg->hwnd, msg->wParam, msg->lParam);
+ }
+ return CallNextHookEx(hook_DialogMessage, nCode, wParam, lParam);
+}
+
+// - WM_Hooks_Install
+
+BOOL WM_Hooks_Install(DWORD owner, DWORD thread) {
+ // - Are there already hooks set?
+ if (hook_owner) {
+ if (!PostThreadMessage(hook_owner, WM_HK_PingThread, 0, 0)) {
+ WM_Hooks_Remove(hook_owner);
+ } else {
+ return FALSE;
+ }
+ }
+
+ // - Initialise the hooks
+ hook_owner = owner;
+ hook_target = thread;
+
+ hook_CallWndProc = SetWindowsHookEx(WH_CALLWNDPROC, HookCallWndProc, dll_instance, thread);
+ //hook_CallWndProcRet = SetWindowsHookEx(WH_CALLWNDPROCRET, HookCallWndProcRet, dll_instance, thread);
+ hook_GetMessage = SetWindowsHookEx(WH_GETMESSAGE, HookGetMessage, dll_instance, thread);
+ hook_DialogMessage = SetWindowsHookEx(WH_SYSMSGFILTER, HookDialogMessage, dll_instance, thread);
+
+ if (!hook_CallWndProc /*|| !hook_CallWndProcRet*/ || !hook_GetMessage || !hook_DialogMessage) {
+ WM_Hooks_Remove(owner);
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+// - WM_Hooks_Remove
+
+BOOL WM_Hooks_Remove(DWORD owner) {
+ if (owner != hook_owner) return FALSE;
+ if (hook_CallWndProc) {
+ UnhookWindowsHookEx(hook_CallWndProc);
+ hook_CallWndProc = 0;
+ }
+ if (hook_CallWndProcRet) {
+ UnhookWindowsHookEx(hook_CallWndProcRet);
+ hook_CallWndProcRet = 0;
+ }
+ if (hook_GetMessage) {
+ UnhookWindowsHookEx(hook_GetMessage);
+ hook_GetMessage = 0;
+ }
+ if (hook_DialogMessage) {
+ UnhookWindowsHookEx(hook_DialogMessage);
+ hook_DialogMessage = 0;
+ }
+ hook_owner = 0;
+ hook_target = 0;
+ return TRUE;
+}
+
+//
+// -=- User input hooks
+//
+
+#pragma data_seg(".WM_Hooks_Shared")
+HHOOK hook_keyboard = 0;
+HHOOK hook_pointer = 0;
+bool enable_real_ptr = true;
+bool enable_synth_ptr = true;
+bool enable_real_kbd = true;
+bool enable_synth_kbd = true;
+#pragma data_seg()
+
+#ifdef WH_KEYBOARD_LL
+LRESULT CALLBACK HookKeyboardHook(int nCode, WPARAM wParam, LPARAM lParam) {
+ if (nCode >= 0) {
+ KBDLLHOOKSTRUCT* info = (KBDLLHOOKSTRUCT*) lParam;
+ bool real_event = (info->flags & LLKHF_INJECTED) == 0;
+ if ((real_event && !enable_real_kbd) ||
+ (!real_event && !enable_synth_kbd)) {
+ return 1;
+ }
+ }
+ return CallNextHookEx(hook_keyboard, nCode, wParam, lParam);
+}
+
+LRESULT CALLBACK HookPointerHook(int nCode, WPARAM wParam, LPARAM lParam) {
+ if (nCode >= 0) {
+ MSLLHOOKSTRUCT* info = (MSLLHOOKSTRUCT*) lParam;
+ bool real_event = (info->flags & LLMHF_INJECTED) == 0;
+ if ((real_event && !enable_real_ptr) ||
+ (!real_event && !enable_synth_ptr)) {
+ return 1;
+ }
+ }
+ return CallNextHookEx(hook_keyboard, nCode, wParam, lParam);
+}
+
+bool RefreshInputHooks() {
+ bool success = true;
+ bool set_ptr_hook = !enable_real_ptr || !enable_synth_ptr;
+ bool set_kbd_hook = !enable_real_kbd || !enable_synth_kbd;
+ if (hook_keyboard && !set_kbd_hook) {
+ UnhookWindowsHookEx(hook_keyboard);
+ hook_keyboard = 0;
+ }
+ if (hook_pointer && !set_ptr_hook) {
+ UnhookWindowsHookEx(hook_pointer);
+ hook_pointer = 0;
+ }
+ if (!hook_keyboard && set_kbd_hook) {
+ hook_keyboard = SetWindowsHookEx(WH_KEYBOARD_LL, HookKeyboardHook, dll_instance, 0);
+ if (!hook_keyboard) success = false;
+ }
+ if (!hook_pointer && set_ptr_hook) {
+ hook_pointer = SetWindowsHookEx(WH_MOUSE_LL, HookPointerHook, dll_instance, 0);
+ if (!hook_pointer) success = false;
+ }
+ return success;
+}
+#else
+#pragma message("WARNING: low-level mouse and keyboard hooks not supported")
+#endif
+
+// - WM_Hooks_EnableRealInputs
+
+BOOL WM_Hooks_EnableRealInputs(BOOL pointer, BOOL keyboard) {
+#ifdef WH_KEYBOARD_LL
+ enable_real_ptr = pointer!=0;
+ enable_real_kbd = keyboard!=0;
+ return RefreshInputHooks();
+#else
+ return FALSE;
+#endif
+}
+
+// - WM_Hooks_EnableSynthInputs
+
+BOOL WM_Hooks_EnableSynthInputs(BOOL pointer, BOOL keyboard) {
+#ifdef WH_KEYBOARD_LL
+ enable_synth_ptr = pointer!=0;
+ enable_synth_kbd = keyboard!=0;
+ return RefreshInputHooks();
+#else
+ return FALSE;
+#endif
+}
+
+// - WM_Hooks_EnableCursorShape
+
+BOOL WM_Hooks_EnableCursorShape(BOOL enable) {
+ enable_cursor_shape = enable;
+ return FALSE;
+}
diff --git a/wm_hooks/wm_hooks.def b/wm_hooks/wm_hooks.def
new file mode 100644
index 00000000..b9198ab9
--- /dev/null
+++ b/wm_hooks/wm_hooks.def
@@ -0,0 +1,5 @@
+LIBRARY "wm_hooks"
+DESCRIPTION 'Window Message Hooks Dynamic Link Library'
+
+SECTIONS
+ .WM_Hooks_Shared read write shared
diff --git a/wm_hooks/wm_hooks.dsp b/wm_hooks/wm_hooks.dsp
new file mode 100644
index 00000000..3c5c1fd6
--- /dev/null
+++ b/wm_hooks/wm_hooks.dsp
@@ -0,0 +1,149 @@
+# Microsoft Developer Studio Project File - Name="wm_hooks" - Package Owner=<4>
+# Microsoft Developer Studio Generated Build File, Format Version 6.00
+# ** DO NOT EDIT **
+
+# TARGTYPE "Win32 (x86) Dynamic-Link Library" 0x0102
+
+CFG=wm_hooks - 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 "wm_hooks.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 "wm_hooks.mak" CFG="wm_hooks - Win32 Debug Unicode"
+!MESSAGE
+!MESSAGE Possible choices for configuration are:
+!MESSAGE
+!MESSAGE "wm_hooks - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library")
+!MESSAGE "wm_hooks - Win32 Debug" (based on "Win32 (x86) Dynamic-Link Library")
+!MESSAGE "wm_hooks - 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)" == "wm_hooks - 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"
+# 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 "WM_HOOKS_EXPORTS" /YX /FD /c
+# ADD CPP /nologo /MT /W3 /GX /O2 /I ".." /D "NDEBUG" /D "_WINDOWS" /D "_USRDLL" /D "WM_HOOKS_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 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
+
+!ELSEIF "$(CFG)" == "wm_hooks - 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"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "WM_HOOKS_EXPORTS" /YX /FD /GZ /c
+# ADD CPP /nologo /MTd /W3 /Gm /GX /Zi /Od /I ".." /FI"msvcwarning.h" /D "_DEBUG" /D "_WINDOWS" /D "_USRDLL" /D "WM_HOOKS_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 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 /incremental:no /debug /machine:I386 /pdbtype:sept
+# SUBTRACT LINK32 /profile
+
+!ELSEIF "$(CFG)" == "wm_hooks - Win32 Debug Unicode"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 1
+# PROP BASE Output_Dir "wm_hooks___Win32_Debug_Unicode"
+# PROP BASE Intermediate_Dir "wm_hooks___Win32_Debug_Unicode"
+# PROP BASE Ignore_Export_Lib 0
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 1
+# PROP Output_Dir "..\Debug_Unicode"
+# PROP Intermediate_Dir "Debug_Unicode"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /MTd /W3 /Gm /GX /Zi /Od /I ".." /FI"msvcwarning.h" /D "_DEBUG" /D "_WINDOWS" /D "_USRDLL" /D "WM_HOOKS_EXPORTS" /D "WIN32" /D "_MBCS" /YX /FD /GZ /c
+# ADD CPP /nologo /MTd /W3 /Gm /GX /Zi /Od /I ".." /FI"msvcwarning.h" /D "_WINDOWS" /D "_USRDLL" /D "WM_HOOKS_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 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 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
+
+!ENDIF
+
+# Begin Target
+
+# Name "wm_hooks - Win32 Release"
+# Name "wm_hooks - Win32 Debug"
+# Name "wm_hooks - Win32 Debug Unicode"
+# Begin Group "Source Files"
+
+# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat"
+# Begin Source File
+
+SOURCE=.\wm_hooks.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\wm_hooks.def
+# End Source File
+# Begin Source File
+
+SOURCE=.\wm_hooks.rc
+# End Source File
+# End Group
+# Begin Group "Header Files"
+
+# PROP Default_Filter "h;hpp;hxx;hm;inl"
+# Begin Source File
+
+SOURCE=.\wm_hooks.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"
+# End Group
+# End Target
+# End Project
diff --git a/wm_hooks/wm_hooks.h b/wm_hooks/wm_hooks.h
new file mode 100644
index 00000000..afff4be7
--- /dev/null
+++ b/wm_hooks/wm_hooks.h
@@ -0,0 +1,103 @@
+/* Copyright (C) 2002-2003 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.
+ */
+
+// -=- wm_hooks.h
+//
+// Window Message Hooks Dynamic Link library
+//
+// This interface is used by the WMHooks class in rfb_win32 to hook the
+// windows on the desktop and receive notifications of changes in their
+// state.
+
+#ifndef __WM_HOOKS_H__
+#define __WM_HOOKS_H__
+
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+
+#define DLLEXPORT __declspec(dllexport)
+
+extern "C"
+{
+ //
+ // -=- Display hook message types
+ //
+
+ DLLEXPORT UINT WM_Hooks_WindowChanged();
+ DLLEXPORT UINT WM_Hooks_WindowBorderChanged();
+ DLLEXPORT UINT WM_Hooks_WindowClientAreaChanged();
+ DLLEXPORT UINT WM_Hooks_RectangleChanged();
+ DLLEXPORT UINT WM_Hooks_CursorChanged();
+
+ //
+ // -=- Display update hooks
+ //
+
+ // - WM_Hooks_Install
+ // Add the current thread to the list of threads that will receive
+ // notifications of changes to the display.
+ // If thread is NULL then the entire display will be hooked.
+ // If thread is !NULL and then the specified
+ // thread will be hooked.
+ // Each thread may only register one hook at a time.
+ // The call will fail (return FALSE) if the thread already has hooks
+ // set, or if the hooks cannot be set, or some other error occurs.
+
+ DLLEXPORT BOOL WM_Hooks_Install(DWORD owner, DWORD thread);
+
+ // - WM_Hooks_Remove
+ // Removes any hook set by the current thread.
+ // The return indicates whether anything went wrong removing the hooks,
+ // that might cause problems later.
+
+ DLLEXPORT BOOL WM_Hooks_Remove(DWORD owner);
+
+ //
+ // -=- User input hooks
+ //
+
+ // - WM_Hooks_EnableRealInputs
+ // If TRUE is passed, then "real" input is enabled, otherwise it is disabled.
+
+ DLLEXPORT BOOL WM_Hooks_EnableRealInputs(BOOL pointer, BOOL keyboard);
+
+ // - WM_Hooks_EnableSynthInputs
+ // If TRUE is passed, then synthetic inputs are enabled, otherwise disabled.
+
+ DLLEXPORT BOOL WM_Hooks_EnableSynthInputs(BOOL pointer, BOOL keyboard);
+
+ //
+ // -=- Cursor shape hooking
+ //
+
+ // - WM_Hooks_EnableCursorShape
+ // If TRUE is passed, then hooks will produce notifications when cursor shape
+ // changes.
+
+ DLLEXPORT BOOL WM_Hooks_EnableCursorShape(BOOL enable);
+
+#ifdef _DEBUG
+ // - WM_Hooks_SetDiagnosticRange
+ // Select a range of messages that will be reported while hooks are active
+ DLLEXPORT void WM_Hooks_SetDiagnosticRange(UINT min, UINT max);
+ DLLEXPORT UINT WM_Hooks_Diagnostic();
+#endif
+
+}
+
+#endif // __WM_HOOKS_H__
diff --git a/wm_hooks/wm_hooks.rc b/wm_hooks/wm_hooks.rc
new file mode 100644
index 00000000..87d282bb
--- /dev/null
+++ b/wm_hooks/wm_hooks.rc
@@ -0,0 +1,109 @@
+//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.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
+
+#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
+
+
+#ifndef _MAC
+/////////////////////////////////////////////////////////////////////////////
+//
+// Version
+//
+
+VS_VERSION_INFO VERSIONINFO
+ FILEVERSION 4,0,0,26
+ PRODUCTVERSION 4,0,0,26
+ FILEFLAGSMASK 0x3fL
+#ifdef _DEBUG
+ FILEFLAGS 0x1L
+#else
+ FILEFLAGS 0x0L
+#endif
+ FILEOS 0x40004L
+ FILETYPE 0x2L
+ FILESUBTYPE 0x0L
+BEGIN
+ BLOCK "StringFileInfo"
+ BEGIN
+ BLOCK "080904b0"
+ BEGIN
+ VALUE "Comments", "\0"
+ VALUE "CompanyName", "RealVNC Ltd.\0"
+ VALUE "FileDescription", "VNC Server for Win32 Hooking DLL\0"
+ VALUE "FileVersion", "4.0\0"
+ VALUE "InternalName", "WMHooks 4.0\0"
+ VALUE "LegalCopyright", "Copyright © RealVNC Ltd. 2002-2004\0"
+ VALUE "LegalTrademarks", "RealVNC\0"
+ VALUE "OriginalFilename", "wm_hooks.dll\0"
+ VALUE "PrivateBuild", "\0"
+ VALUE "ProductName", "VNC Server 4.0\0"
+ VALUE "ProductVersion", "4.0\0"
+ VALUE "SpecialBuild", "\0"
+ END
+ END
+ BLOCK "VarFileInfo"
+ BEGIN
+ VALUE "Translation", 0x809, 1200
+ END
+END
+
+#endif // !_MAC
+
+#endif // English (U.K.) resources
+/////////////////////////////////////////////////////////////////////////////
+
+
+
+#ifndef APSTUDIO_INVOKED
+/////////////////////////////////////////////////////////////////////////////
+//
+// Generated from the TEXTINCLUDE 3 resource.
+//
+
+
+/////////////////////////////////////////////////////////////////////////////
+#endif // not APSTUDIO_INVOKED
+
diff --git a/x0vncserver/Image.cxx b/x0vncserver/Image.cxx
new file mode 100644
index 00000000..56a197e7
--- /dev/null
+++ b/x0vncserver/Image.cxx
@@ -0,0 +1,149 @@
+/* Copyright (C) 2002-2003 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.
+ */
+//
+// Image.cxx
+//
+
+
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/ipc.h>
+#include <sys/shm.h>
+#include <X11/Xlib.h>
+#include <X11/Xutil.h>
+#include <X11/extensions/XShm.h>
+#include "Image.h"
+#include <list>
+
+class ImageCleanup {
+public:
+ std::list<Image*> images;
+
+ ~ImageCleanup()
+ {
+ fprintf(stderr,"~ImageCleanup called\n");
+
+ while (!images.empty()) {
+ delete images.front();
+ }
+ }
+};
+
+ImageCleanup imageCleanup;
+
+static bool caughtShmError = false;
+
+static int ShmCreationXErrorHandler(Display *dpy, XErrorEvent *error)
+{
+ caughtShmError = true;
+ return 0;
+}
+
+Image::Image(Display* d, int width, int height)
+ : xim(0), dpy(d), shminfo(0), usingShm(false)
+{
+ if (createShmImage(width, height)) return;
+
+ xim = XCreateImage(dpy, DefaultVisual(dpy, DefaultScreen(dpy)),
+ DefaultDepth(dpy,DefaultScreen(dpy)), ZPixmap,
+ 0, 0, width, height, BitmapPad(dpy), 0);
+
+ xim->data = (char*)malloc(xim->bytes_per_line * xim->height);
+ if (!xim->data) {
+ fprintf(stderr,"malloc failed\n");
+ exit(1);
+ }
+}
+
+Image::~Image()
+{
+ fprintf(stderr,"~Image called - usingShm %d\n",usingShm);
+ if (usingShm) {
+ usingShm = false;
+ shmdt(shminfo->shmaddr);
+ shmctl(shminfo->shmid, IPC_RMID, 0);
+ imageCleanup.images.remove(this);
+ }
+ delete shminfo;
+ if (xim) XDestroyImage(xim);
+}
+
+void Image::get(Window w)
+{
+ if (usingShm) {
+ XShmGetImage(dpy, w, xim, 0, 0, AllPlanes);
+ } else {
+ XGetSubImage(dpy, w, 0, 0, xim->width, xim->height,
+ AllPlanes, ZPixmap, xim, 0, 0);
+ }
+}
+
+bool Image::createShmImage(int width, int height)
+{
+ if (XShmQueryExtension(dpy)) {
+ shminfo = new XShmSegmentInfo;
+
+ xim = XShmCreateImage(dpy, DefaultVisual(dpy, DefaultScreen(dpy)),
+ DefaultDepth(dpy,DefaultScreen(dpy)), ZPixmap,
+ 0, shminfo, width, height);
+
+ if (xim) {
+ shminfo->shmid = shmget(IPC_PRIVATE,
+ xim->bytes_per_line * xim->height,
+ IPC_CREAT|0777);
+
+ if (shminfo->shmid != -1) {
+ shminfo->shmaddr = xim->data = (char*)shmat(shminfo->shmid, 0, 0);
+
+ if (shminfo->shmaddr != (char *)-1) {
+
+ shminfo->readOnly = False;
+
+ XErrorHandler oldHdlr = XSetErrorHandler(ShmCreationXErrorHandler);
+ XShmAttach(dpy, shminfo);
+ XSync(dpy, False);
+ XSetErrorHandler(oldHdlr);
+
+ if (!caughtShmError) {
+ fprintf(stderr,"Using shared memory XImage\n");
+ usingShm = true;
+ imageCleanup.images.push_back(this);
+ return true;
+ }
+
+ shmdt(shminfo->shmaddr);
+ } else {
+ fprintf(stderr,"shmat failed\n");
+ perror("shmat");
+ }
+
+ shmctl(shminfo->shmid, IPC_RMID, 0);
+ } else {
+ fprintf(stderr,"shmget failed\n");
+ perror("shmget");
+ }
+
+ XDestroyImage(xim);
+ xim = 0;
+ } else {
+ fprintf(stderr,"XShmCreateImage failed\n");
+ }
+ }
+
+ return false;
+}
diff --git a/x0vncserver/Image.h b/x0vncserver/Image.h
new file mode 100644
index 00000000..1a9ff1cc
--- /dev/null
+++ b/x0vncserver/Image.h
@@ -0,0 +1,48 @@
+/* Copyright (C) 2002-2003 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.
+ */
+//
+// Image.h
+//
+
+#ifndef __IMAGE_H__
+#define __IMAGE_H__
+
+#include <X11/Xlib.h>
+#include <X11/extensions/XShm.h>
+
+class Image {
+
+public:
+
+ Image(Display* dpy, int width, int height);
+ ~Image();
+
+ void get(Window w);
+
+ XImage* xim;
+
+private:
+
+ bool createShmImage(int width, int height);
+
+ Display* dpy;
+ XShmSegmentInfo* shminfo;
+ bool usingShm;
+};
+
+#endif
diff --git a/x0vncserver/Makefile.in b/x0vncserver/Makefile.in
new file mode 100644
index 00000000..e0b06cb1
--- /dev/null
+++ b/x0vncserver/Makefile.in
@@ -0,0 +1,22 @@
+
+SRCS = Image.cxx x0vncserver.cxx
+
+OBJS = $(SRCS:.cxx=.o)
+
+program = x0vncserver
+
+DEP_LIBS = ../rfb/librfb.a ../network/libnetwork.a ../rdr/librdr.a
+
+EXTRA_LIBS = @ZLIB_LIB@ @X_PRE_LIBS@ @X_LIBS@ -lXtst -lXext -lX11 @X_EXTRA_LIBS@
+
+DIR_CPPFLAGS = -I$(top_srcdir) @X_CFLAGS@ # X_CFLAGS are really CPPFLAGS
+
+all:: $(program)
+
+$(program): $(OBJS) buildtime.o $(DEP_LIBS)
+ rm -f $(program)
+ $(CXXLD) $(CXXFLAGS) $(LDFLAGS) -o $@ $(OBJS) buildtime.o $(DEP_LIBS) $(LIBS) $(EXTRA_LIBS)
+
+buildtime.o: $(OBJS) $(DEP_LIBS)
+
+# followed by boilerplate.mk
diff --git a/x0vncserver/buildtime.c b/x0vncserver/buildtime.c
new file mode 100644
index 00000000..a96031cc
--- /dev/null
+++ b/x0vncserver/buildtime.c
@@ -0,0 +1,18 @@
+/* Copyright (C) 2002-2003 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.
+ */
+char buildtime[] = __DATE__ " " __TIME__;
diff --git a/x0vncserver/x0vncserver.cxx b/x0vncserver/x0vncserver.cxx
new file mode 100644
index 00000000..df40c33a
--- /dev/null
+++ b/x0vncserver/x0vncserver.cxx
@@ -0,0 +1,277 @@
+/* Copyright (C) 2002-2004 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 <strings.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <errno.h>
+#include <rfb/Logger_stdio.h>
+#include <rfb/LogWriter.h>
+#include <rfb/VNCServerST.h>
+#include <rfb/Configuration.h>
+#include <rfb/SSecurityFactoryStandard.h>
+
+#include <network/TcpSocket.h>
+
+#include "Image.h"
+#include <signal.h>
+#include <X11/X.h>
+#include <X11/Xlib.h>
+#include <X11/Xutil.h>
+#include <X11/extensions/XTest.h>
+
+
+#include <rfb/Encoder.h>
+
+using namespace rfb;
+using namespace rdr;
+using namespace network;
+
+LogWriter vlog("main");
+
+StringParameter displayname("display", "The X display", "");
+IntParameter rfbport("rfbport", "TCP port to listen for RFB protocol",5900);
+VncAuthPasswdFileParameter vncAuthPasswdFile;
+
+static void CleanupSignalHandler(int sig)
+{
+ // CleanupSignalHandler allows C++ object cleanup to happen because it calls
+ // exit() rather than the default which is to abort.
+ fprintf(stderr,"CleanupSignalHandler called\n");
+ exit(1);
+}
+
+
+class XDesktop : public SDesktop, public rfb::ColourMap
+{
+public:
+ XDesktop(Display* dpy_)
+ : dpy(dpy_), pb(0), server(0), oldButtonMask(0), haveXtest(false)
+ {
+ int xtestEventBase;
+ int xtestErrorBase;
+ int major, minor;
+
+ if (XTestQueryExtension(dpy, &xtestEventBase,
+ &xtestErrorBase, &major, &minor)) {
+ XTestGrabControl(dpy, True);
+ vlog.info("XTest extension present - version %d.%d",major,minor);
+ haveXtest = true;
+ } else {
+ vlog.info("XTest extension not present");
+ vlog.info("unable to inject events or display while server is grabbed");
+ }
+
+ int dpyWidth = DisplayWidth(dpy, DefaultScreen(dpy));
+ int dpyHeight = DisplayHeight(dpy, DefaultScreen(dpy));
+ Visual* vis = DefaultVisual(dpy, DefaultScreen(dpy));
+
+ image = new Image(dpy, dpyWidth, dpyHeight);
+ image->get(DefaultRootWindow(dpy));
+
+ pf.bpp = image->xim->bits_per_pixel;
+ pf.depth = image->xim->depth;
+ pf.bigEndian = (image->xim->byte_order == MSBFirst);
+ pf.trueColour = (vis->c_class == TrueColor);
+ pf.redShift = ffs(vis->red_mask) - 1;
+ pf.greenShift = ffs(vis->green_mask) - 1;
+ pf.blueShift = ffs(vis->blue_mask) - 1;
+ pf.redMax = vis->red_mask >> pf.redShift;
+ pf.greenMax = vis->green_mask >> pf.greenShift;
+ pf.blueMax = vis->blue_mask >> pf.blueShift;
+
+ pb = new FullFramePixelBuffer(pf, dpyWidth, dpyHeight,
+ (rdr::U8*)image->xim->data, this);
+ }
+ virtual ~XDesktop() {
+ delete pb;
+ }
+
+ void setVNCServer(VNCServer* s) {
+ server = s;
+ server->setPixelBuffer(pb);
+ }
+
+ // -=- SDesktop interface
+
+ virtual void pointerEvent(const Point& pos, rdr::U8 buttonMask) {
+ if (!haveXtest) return;
+ XTestFakeMotionEvent(dpy, DefaultScreen(dpy), pos.x, pos.y, CurrentTime);
+ if (buttonMask != oldButtonMask) {
+ for (int i = 0; i < 5; i++) {
+ if ((buttonMask ^ oldButtonMask) & (1<<i)) {
+ if (buttonMask & (1<<i)) {
+ XTestFakeButtonEvent(dpy, i+1, True, CurrentTime);
+ } else {
+ XTestFakeButtonEvent(dpy, i+1, False, CurrentTime);
+ }
+ }
+ }
+ }
+ oldButtonMask = buttonMask;
+ }
+
+ virtual void keyEvent(rdr::U32 key, bool down) {
+ if (!haveXtest) return;
+ int keycode = XKeysymToKeycode(dpy, key);
+ if (keycode)
+ XTestFakeKeyEvent(dpy, keycode, down, CurrentTime);
+ }
+
+ virtual void clientCutText(const char* str, int len) {
+ }
+
+ virtual Point getFbSize() {
+ return Point(pb->width(), pb->height());
+ }
+
+ // rfb::ColourMap callbacks
+ virtual void lookup(int index, int* r, int* g, int* b) {
+ XColor xc;
+ xc.pixel = index;
+ if (index < DisplayCells(dpy,DefaultScreen(dpy))) {
+ XQueryColor(dpy, DefaultColormap(dpy,DefaultScreen(dpy)), &xc);
+ } else {
+ xc.red = xc.green = xc.blue = 0;
+ }
+ *r = xc.red;
+ *g = xc.green;
+ *b = xc.blue;
+ }
+
+ virtual void poll() {
+ if (server && server->clientsReadyForUpdate()) {
+ image->get(DefaultRootWindow(dpy));
+ server->add_changed(pb->getRect());
+ server->tryUpdate();
+ }
+ }
+
+protected:
+ Display* dpy;
+ PixelFormat pf;
+ PixelBuffer* pb;
+ VNCServer* server;
+ Image* image;
+ int oldButtonMask;
+ bool haveXtest;
+};
+
+char* programName;
+
+static void usage()
+{
+ fprintf(stderr, "\nusage: %s [<parameters>]\n", programName);
+ fprintf(stderr,"\n"
+ "Parameters can be turned on with -<param> or off with -<param>=0\n"
+ "Parameters which take a value can be specified as "
+ "-<param> <value>\n"
+ "Other valid forms are <param>=<value> -<param>=<value> "
+ "--<param>=<value>\n"
+ "Parameter names are case-insensitive. The parameters are:\n\n");
+ Configuration::listParams(79, 14);
+ exit(1);
+}
+
+int main(int argc, char** argv)
+{
+ initStdIOLoggers();
+ LogWriter::setLogParams("*:stderr:30");
+
+ programName = argv[0];
+ Display* dpy;
+
+ for (int i = 1; i < argc; i++) {
+ if (Configuration::setParam(argv[i]))
+ continue;
+
+ if (argv[i][0] == '-') {
+ if (i+1 < argc) {
+ if (Configuration::setParam(&argv[i][1], argv[i+1])) {
+ i++;
+ continue;
+ }
+ }
+ usage();
+ }
+
+ usage();
+ }
+
+ CharArray dpyStr(displayname.getData());
+ if (!(dpy = XOpenDisplay(dpyStr.buf[0] ? dpyStr.buf : 0))) {
+ fprintf(stderr,"%s: unable to open display \"%s\"\r\n",
+ programName, XDisplayName(displayname.getData()));
+ exit(1);
+ }
+
+ signal(SIGHUP, CleanupSignalHandler);
+ signal(SIGINT, CleanupSignalHandler);
+ signal(SIGTERM, CleanupSignalHandler);
+
+ try {
+ XDesktop desktop(dpy);
+ VNCServerST server("x0vncserver", &desktop);
+ desktop.setVNCServer(&server);
+
+ TcpSocket::initTcpSockets();
+ TcpListener listener((int)rfbport);
+ vlog.info("Listening on port %d", (int)rfbport);
+
+ while (true) {
+ fd_set rfds;
+ struct timeval tv;
+
+ tv.tv_sec = 0;
+ tv.tv_usec = 50*1000;
+
+ FD_ZERO(&rfds);
+ FD_SET(listener.getFd(), &rfds);
+
+ std::list<Socket*> sockets;
+ server.getSockets(&sockets);
+ std::list<Socket*>::iterator i;
+ for (i = sockets.begin(); i != sockets.end(); i++) {
+ FD_SET((*i)->getFd(), &rfds);
+ }
+
+ int n = select(FD_SETSIZE, &rfds, 0, 0, &tv);
+ if (n < 0) throw rdr::SystemException("select",errno);
+
+ if (FD_ISSET(listener.getFd(), &rfds)) {
+ Socket* sock = listener.accept();
+ server.addClient(sock);
+ }
+
+ server.getSockets(&sockets);
+ for (i = sockets.begin(); i != sockets.end(); i++) {
+ if (FD_ISSET((*i)->getFd(), &rfds)) {
+ server.processSocketEvent(*i);
+ }
+ }
+
+ server.checkTimeouts();
+ desktop.poll();
+ }
+
+ } catch (rdr::Exception &e) {
+ vlog.error(e.str());
+ };
+
+ return 0;
+}
diff --git a/x0vncserver/x0vncserver.man b/x0vncserver/x0vncserver.man
new file mode 100644
index 00000000..9f813965
--- /dev/null
+++ b/x0vncserver/x0vncserver.man
@@ -0,0 +1,32 @@
+.TH x0vncserver 1 "19 September 2003" "RealVNC Ltd" "Virtual Network Computing"
+.SH NAME
+x0vncserver \- VNC server which continuously polls an X display
+.SH SYNOPSIS
+.B x0vncserver
+[\fIparameters\fP]
+.SH DESCRIPTION
+.B x0vncserver
+is a VNC server which continuously polls any X display, allowing it to be
+controlled via VNC. How usable it will be depends a lot on the machine it's
+running on, and what you're expecting. It won't be as fast as Xvnc or a native
+X server with VNC support compiled in, but in many cases it is the best option
+since it is just an ordinary X application requiring no special installation.
+
+It has many of the same parameters as Xvnc. Running \fBx0vncserver -h\fP will
+give a list of parameters with descriptions. Note that you need to explicitly
+specify an appropriate password file using the PasswordFile parameter.
+
+.SH SEE ALSO
+.BR Xvnc (1)
+.BR vncpasswd (1),
+.BR vncviewer (1),
+.BR vncserver (1),
+.br
+http://www.realvnc.com
+
+.SH AUTHOR
+Tristan Richardson, RealVNC Ltd.
+
+VNC was originally developed by the RealVNC team while at Olivetti Research Ltd
+/ AT&T Laboratories Cambridge. It is now being maintained by RealVNC Ltd. See
+http://www.realvnc.com for details.
diff --git a/xc.patch b/xc.patch
new file mode 100644
index 00000000..fb9baf6f
--- /dev/null
+++ b/xc.patch
@@ -0,0 +1,191 @@
+*** xc/programs/Xserver/Imakefile.orig Fri Jun 6 11:02:36 2003
+--- xc/programs/Xserver/Imakefile Fri Jun 6 11:14:39 2003
+***************
+*** 409,412 ****
+--- 409,429 ----
+ #endif
+ #endif /* XsunServer */
++ XCOMM
++ XCOMM X VNC server
++ XCOMM
++ MFBDIR = mfb
++ CFB8DIR = cfb
++ CFB16DIR = cfb16
++ CFB24DIR = cfb24
++ CFB32DIR = cfb32
++ XVNCDDXDIR = vnc/Xvnc
++ XVNCDIRS = $(STDDIRS) $(MFBDIR) \
++ $(CFB8DIR) $(CFB16DIR) $(CFB24DIR) $(CFB32DIR) \
++ $(XVNCDDXDIR) $(DEPDIRS)
++ XVNCOBJS = $(XVNCDDXDIR)/stubs.o $(XVNCDDXDIR)/miinitext.o
++ XVNCLIBS = PreFbLibs vnc/Xvnc/LibraryTargetName(xvnc) CFBLibs PostFbLibs
++ XVNCSYSLIBS = $(FONTLIBS) $(SYSLIBS)
++ ServerTarget(Xvnc,$(XVNCDIRS),$(XVNCOBJS), \
++ $(XVNCLIBS) $(LOADABLEEXTS) $(LIBCWRAPPER),$(XVNCSYSLIBS))
+
+
+*** xc/programs/Xserver/mi/miinitext.c.orig Fri Jun 6 11:02:59 2003
+--- xc/programs/Xserver/mi/miinitext.c Fri Jun 6 11:17:15 2003
+***************
+*** 150,153 ****
+--- 150,156 ----
+ extern void MITMiscExtensionInit(INITARGS);
+ #endif
++ #ifdef VNCEXT
++ extern void vncExtensionInit(INITARGS);
++ #endif
+ #ifdef XIDLE
+ extern void XIdleExtensionInit(INITARGS);
+***************
+*** 285,288 ****
+--- 288,294 ----
+ MITMiscExtensionInit();
+ #endif
++ #ifdef VNCEXT
++ vncExtensionInit();
++ #endif
+ #ifdef XIDLE
+ XIdleExtensionInit();
+*** xc/programs/Xserver/cfb/cfb8line.c.orig Wed Sep 18 18:11:47 2002
+--- xc/programs/Xserver/cfb/cfb8line.c Thu Jun 5 18:32:04 2003
+***************
+*** 688,707 ****
+ y1_or_e1 = xOffset & 3;
+ # else
+! # if PGSZ == 64 /* PIM value from <cfbmskbits.h> is not it! (for 16/32 PSZ)*/
+! y1_or_e1 = ((long) addrp) & 0x7;
+! addrp = (PixelType *) (((unsigned char *) addrp) - y1_or_e1);
+! # else
+! y1_or_e1 = ((long) addrp) & PIM;
+! addrp = (PixelType *) (((unsigned char *) addrp) - y1_or_e1);
+! # endif
+! #if PGSZ == 32
+! # if PWSH != 2
+! y1_or_e1 >>= (2 - PWSH);
+! # endif
+! #else /* PGSZ == 64 */
+! # if PWSH != 3
+! y1_or_e1 >>= (3 - PWSH);
+! # endif
+! #endif /* PGSZ */
+ # endif /* PSZ == 24 */
+ #if PSZ == 24
+--- 688,696 ----
+ y1_or_e1 = xOffset & 3;
+ # else
+! /* Round addrp down to the next PixelGroup boundary, and
+! * set y1_or_e1 to the excess (in pixels)
+! * (assumes PGSZB is a power of 2). */
+! y1_or_e1 = (((unsigned long) addrp) & (PGSZB - 1)) / (PSZ / 8);
+! addrp -= y1_or_e1;
+ # endif /* PSZ == 24 */
+ #if PSZ == 24
+*** xc/programs/Xserver/cfb/cfbtile32.c.orig Fri Dec 14 19:59:25 2001
+--- xc/programs/Xserver/cfb/cfbtile32.c Thu Jun 5 18:16:48 2003
+***************
+*** 73,77 ****
+ (p)++,(*(p) = MROP_PREBUILT_SOLID(((srcpix<<8)|(srcpix>>16)),*(p))))
+
+! #if (MROP == Mcopy) && defined(FAST_CONSTANT_OFFSET_MODE) && defined(SHARED_IDCACHE)
+ # define Expand(left,right) {\
+ int part = nlwMiddle & ((PGSZB*2)-1); \
+--- 73,83 ----
+ (p)++,(*(p) = MROP_PREBUILT_SOLID(((srcpix<<8)|(srcpix>>16)),*(p))))
+
+!
+!
+! /* XXX TJR: I doubt that this optimised case works (because the non-24 bit case
+! was broken), so I've added the #if 0 below. Someone who knows what they're
+! doing can re-enable it if they fix it */
+!
+! #if (MROP == Mcopy) && defined(FAST_CONSTANT_OFFSET_MODE) && defined(SHARED_IDCACHE) && 0
+ # define Expand(left,right) {\
+ int part = nlwMiddle & ((PGSZB*2)-1); \
+***************
+*** 145,150 ****
+ #if (MROP == Mcopy) && defined(FAST_CONSTANT_OFFSET_MODE) && defined(SHARED_IDCACHE)
+ # define Expand(left,right) {\
+! int part = nlwMiddle & ((PGSZB*2)-1); \
+! nlwMiddle >>= PWSH + 1; \
+ while (h--) { \
+ srcpix = psrc[srcy]; \
+--- 151,156 ----
+ #if (MROP == Mcopy) && defined(FAST_CONSTANT_OFFSET_MODE) && defined(SHARED_IDCACHE)
+ # define Expand(left,right) {\
+! int part = nlwMiddle & 7; \
+! nlwMiddle >>= 3; \
+ while (h--) { \
+ srcpix = psrc[srcy]; \
+*** xc/programs/Xserver/cfb/cfbglblt8.c.orig Fri Dec 14 19:59:23 2001
+--- xc/programs/Xserver/cfb/cfbglblt8.c Tue Aug 12 10:05:57 2003
+***************
+*** 284,288 ****
+ register glyphPointer glyphBits;
+ register int xoff;
+! #if defined(USE_LEFT_BITS) || (!defined(STIPPLE) && !defined(USE_STIPPLE_CODE))
+ register CfbBits *dst;
+ #endif
+--- 284,288 ----
+ register glyphPointer glyphBits;
+ register int xoff;
+! #if defined(USE_LEFTBITS) || (!defined(STIPPLE) && !defined(USE_STIPPLE_CODE))
+ register CfbBits *dst;
+ #endif
+***************
+*** 292,296 ****
+ CfbBits *dstLine;
+ CfbBits *pdstBase;
+! #ifdef USE_LEFT_BITS
+ CARD32 *cTmp;
+ #endif
+--- 292,296 ----
+ CfbBits *dstLine;
+ CfbBits *pdstBase;
+! #ifdef USE_LEFTBITS
+ CARD32 *cTmp;
+ #endif
+***************
+*** 399,403 ****
+ } while (--hTmp);
+ break;
+! #else /* !USE_LEFT_BITS */
+ {
+ int h;
+--- 399,403 ----
+ } while (--hTmp);
+ break;
+! #else /* !USE_LEFTBITS */
+ {
+ int h;
+***************
+*** 412,416 ****
+ glyphBits = clips;
+ /* fall through */
+! #endif /* USE_LEFT_BITS */
+ case rgnIN:
+ #ifdef STIPPLE
+--- 412,416 ----
+ glyphBits = clips;
+ /* fall through */
+! #endif /* USE_LEFTBITS */
+ case rgnIN:
+ #ifdef STIPPLE
+*** xc/programs/Xserver/cfb/cfbcppl.c.orig Fri Dec 14 19:59:22 2001
+--- xc/programs/Xserver/cfb/cfbcppl.c Sun Apr 18 12:53:36 2004
+***************
+*** 383,389 ****
+ psrcLine = (unsigned int *)psrcBase + srcy * widthSrc + srcx;
+ #endif
+ pdstLine = (unsigned int *)pdstBase + dsty * widthDst + (dstx >> 5);
+! if (dstx + width <= 32)
+ {
+ mfbmaskpartialbits(dstx, width, startmask);
+ nlMiddle = 0;
+--- 383,389 ----
+ psrcLine = (unsigned int *)psrcBase + srcy * widthSrc + srcx;
+ #endif
+ pdstLine = (unsigned int *)pdstBase + dsty * widthDst + (dstx >> 5);
+! if ((dstx & 0x1f) + width <= 32)
+ {
+ mfbmaskpartialbits(dstx, width, startmask);
+ nlMiddle = 0;
diff --git a/xc/config/cf/host.def b/xc/config/cf/host.def
new file mode 100644
index 00000000..2de89af0
--- /dev/null
+++ b/xc/config/cf/host.def
@@ -0,0 +1 @@
+#include <vnc.def>
diff --git a/xc/config/cf/vnc.def b/xc/config/cf/vnc.def
new file mode 100644
index 00000000..08b64968
--- /dev/null
+++ b/xc/config/cf/vnc.def
@@ -0,0 +1,32 @@
+#define BuildServersOnly YES
+#define BuildFonts NO
+#define BuildClients NO
+#define BuildDocs NO
+#define BuildPexExt NO
+#define BuildNls NO
+#define BuildXIE NO
+#define BuildGlxExt NO
+#define XnestServer NO
+#define XprtServer NO
+
+#ifdef SunArchitecture
+#define ProjectRoot /usr/openwin
+#define HasGcc2 YES
+#define BuildXKB NO
+#endif
+
+#define BuildVNCExt YES
+#define VNCExtDefines -DVNCEXT
+#define SiteExtensionDefines VNCExtDefines
+#define SiteExtensionDirs vnc
+
+#define VncExtLibs $(TOP)/../rfb/librfb.a \
+ $(TOP)/../Xregion/libXregion.a \
+ $(TOP)/../network/libnetwork.a \
+ $(TOP)/../rdr/librdr.a
+
+#define SiteExtensionLibs vnc/LibraryTargetName(vnc) VncExtLibs
+
+#define ServerTarget(server,subdirs,objects,libs,syslibs) @@\
+CCLINK = $(CXXENVSETUP) $(CXX) @@\
+ServerTargetWithFlags(server,subdirs,objects,libs,syslibs,$(_NOOP_))
diff --git a/xc/programs/Xserver/Xvnc.man b/xc/programs/Xserver/Xvnc.man
new file mode 100644
index 00000000..1852f4d3
--- /dev/null
+++ b/xc/programs/Xserver/Xvnc.man
@@ -0,0 +1,258 @@
+.TH Xvnc 1 "18 May 2004" "RealVNC Ltd" "Virtual Network Computing"
+.SH NAME
+Xvnc \- the X VNC server
+.SH SYNOPSIS
+.B Xvnc
+.RI [ options ]
+.RI : display#
+.SH DESCRIPTION
+.B Xvnc
+is the X VNC (Virtual Network Computing) server. It is based on a standard X
+server, but it has a "virtual" screen rather than a physical one. X
+applications display themselves on it as if it were a normal X display, but
+they can only be accessed via a VNC viewer - see \fBvncviewer\fP(1).
+
+So Xvnc is really two servers in one. To the applications it is an X server,
+and to the remote VNC users it is a VNC server. By convention we have arranged
+that the VNC server display number will be the same as the X server display
+number, which means you can use eg. snoopy:2 to refer to display 2 on machine
+"snoopy" in both the X world and the VNC world.
+
+The best way of starting \fBXvnc\fP is via the \fBvncserver\fP script. This
+sets up the environment appropriately and runs some X applications to get you
+going. See the manual page for \fBvncserver\fP(1) for more information.
+
+.SH OPTIONS
+.B Xvnc
+takes lots of options - running \fBXvnc -help\fP gives a list. Many of these
+are standard X server options, which are described in the \fBXserver\fP(1)
+manual page. In addition to options which can only be set via the
+command-line, there are also "parameters" which can be set both via the
+command-line and through the \fBvncconfig\fP(1) program.
+
+.TP
+.B \-geometry \fIwidth\fPx\fIheight\fP
+Specify the size of the desktop to be created. Default is 1024x768.
+
+.TP
+.B \-depth \fIdepth\fP
+Specify the pixel depth in bits of the desktop to be created. Default is 16,
+other possible values are 8, 15, and 24 - anything else is likely to cause
+strange behaviour by applications.
+
+.TP
+.B \-pixelformat \fIformat\fP
+Specify pixel format for server to use (BGRnnn or RGBnnn). The default for
+depth 8 is BGR233 (meaning the most significant two bits represent blue, the
+next three green, and the least significant three represent red), the default
+for depth 16 is RGB565 and for depth 24 is RGB888.
+
+.TP
+.B \-cc 3
+As an alternative to the default TrueColor visual, this allows you to run an
+Xvnc server with a PseudoColor visual (i.e. one which uses a colour map or
+palette), which can be useful for running some old X applications which only
+work on such a display. Values other than 3 (PseudoColor) and 4 (TrueColor)
+for the \-cc option may result in strange behaviour, and PseudoColor desktops
+must be 8 bits deep (i.e. \fB-depth 8\fP).
+
+.TP
+.B \-inetd
+This significantly changes Xvnc's behaviour so that it can be launched from
+inetd. See the section below on usage with inetd.
+
+.TP
+.B \-help
+List all the options and parameters
+
+.SH PARAMETERS
+VNC parameters can be set both via the command-line and through the
+\fBvncconfig\fP(1) program, and with a VNC-enabled XFree86 server via Options
+entries in the XF86Config file.
+
+Parameters can be turned on with -\fIparam\fP or off with
+-\fIparam\fP=0. Parameters which take a value can be specified as
+-\fIparam\fP \fIvalue\fP. Other valid forms are \fIparam\fP\fB=\fP\fIvalue\fP
+-\fIparam\fP=\fIvalue\fP --\fIparam\fP=\fIvalue\fP. Parameter names are
+case-insensitive.
+
+.TP
+.B \-desktop \fIdesktop-name\fP
+Each desktop has a name which may be displayed by the viewer. It defaults to
+"x11".
+
+.TP
+.B \-rfbport \fIport\fP
+Specifies the TCP port on which Xvnc listens for connections from viewers (the
+protocol used in VNC is called RFB - "remote framebuffer"). The default is
+5900 plus the display number.
+
+.TP
+.B \-rfbwait \fItime\fP, \-ClientWaitTimeMillis \fItime\fP
+
+Time in milliseconds to wait for a viewer which is blocking Xvnc. This is
+necessary because Xvnc is single-threaded and sometimes blocks until the viewer
+has finished sending or receiving a message - note that this does not mean an
+update will be aborted after this time. Default is 20000 (20 seconds).
+
+.TP
+.B \-httpd \fIdirectory\fP
+Run a mini-HTTP server which serves files from the given directory. Normally
+the directory will contain the classes for the Java viewer. In addition, files
+with a .vnc extension will have certain substitutions made so that a single
+installation of the Java VNC viewer can be served by separate instances of
+Xvnc.
+
+.TP
+.B \-httpPort \fIport\fP
+Specifies the port on which the mini-HTTP server runs. Default is 5800 plus
+the display number.
+
+.TP
+.B \-rfbauth \fIpasswd-file\fP, \-PasswordFile \fIpasswd-file\fP
+Specifies the file containing the password used to authenticate viewers. The
+file is accessed each time a connection comes in, so it can be changed on the
+fly via \fBvncpasswd\fP(1).
+
+.TP
+.B \-deferUpdate \fItime\fP
+Xvnc uses a "deferred update" mechanism which enhances performance in many
+cases. After any change to the framebuffer, Xvnc waits for this number of
+milliseconds (default 40) before sending an update to any waiting clients. This
+means that more changes tend to get coalesced together in a single
+update. Setting it to 0 results in the same behaviour as earlier versions of
+Xvnc, where the first change to the framebuffer causes an immediate update to
+any waiting clients.
+
+.TP
+.B \-SendCutText
+Send clipboard changes to clients (default is on). Note that you must also run
+\fBvncconfig\fP(1) to get the clipboard to work.
+
+.TP
+.B \-AcceptCutText
+Accept clipboard updates from clients (default is on). Note that you must also
+run \fBvncconfig\fP(1) to get the clipboard to work.
+
+.TP
+.B \-AcceptPointerEvents
+Accept pointer press and release events from clients (default is on).
+
+.TP
+.B \-AcceptKeyEvents
+Accept key press and release events from clients (default is on).
+
+.TP
+.B \-DisconnectClients
+Disconnect existing clients if an incoming connection is non-shared (default is
+on). If \fBDisconnectClients\fP is false, then a new non-shared connection will
+be refused while there is a client active. When combined with
+\fBNeverShared\fP this means only one client is allowed at a time.
+
+.TP
+.B \-NeverShared
+Never treat incoming connections as shared, regardless of the client-specified
+setting (default is off).
+
+.TP
+.B \-AlwaysShared
+Always treat incoming connections as shared, regardless of the client-specified
+setting (default is off).
+
+.TP
+.B \-Protocol3.3
+Always use protocol version 3.3 for backwards compatibility with badly-behaved
+clients (default is off).
+
+.TP
+.B \-CompareFB
+Perform pixel comparison on framebuffer to reduce unnecessary updates (default
+is on).
+
+.TP
+.B \-SecurityTypes \fIsec-types\fP
+Specify which security schemes to use separated by commas. At present only
+"None" and "VncAuth" are supported. The default is "VncAuth" - note that if
+you want a server which does not require a password, you must set this
+parameter to "None".
+
+.TP
+.B \-IdleTimeout \fIseconds\fP
+The number of seconds after which an idle VNC connection will be dropped
+(default is 3600 i.e. an hour).
+
+.TP
+.B \-localhost
+Only allow connections from the same machine. Useful if you use SSH and want to
+stop non-SSH connections from any other hosts. See the guide to using VNC with
+SSH on the web site.
+
+.TP
+.B \-log \fIlogname\fP:\fIdest\fP:\fIlevel\fP
+Configures the debug log settings. \fIdest\fP can currently be \fBstderr\fP or
+\fBstdout\fP, and \fIlevel\fP is between 0 and 100, 100 meaning most verbose
+output. \fIlogname\fP is usually \fB*\fP meaning all, but you can target a
+specific source file if you know the name of its "LogWriter". Default is
+\fB*:stderr:30\fP.
+
+.SH USAGE WITH INETD
+By configuring the \fBinetd\fP(1) service appropriately, Xvnc can be launched
+on demand when a connection comes in, rather than having to be started
+manually. When given the \fB-inetd\fP option, instead of listening for TCP
+connections on a given port it uses its standard input and standard output.
+There are two modes controlled by the wait/nowait entry in the inetd.conf file.
+
+In the nowait mode, Xvnc uses its standard input and output directly as the
+connection to a viewer. It never has a listening socket, so cannot accept
+further connections from viewers (it can however connect out to listening
+viewers by use of the vncconfig program). Further viewer connections to the
+same TCP port result in inetd spawning off a new Xvnc to deal with each
+connection. When the connection to the viewer dies, the Xvnc and any
+associated X clients die. This behaviour is most useful when combined with the
+XDMCP options -query and -once. An typical example in inetd.conf might be (all
+on one line):
+
+5950 stream tcp nowait nobody /usr/local/bin/Xvnc Xvnc -inetd -query
+localhost -once securitytypes=none
+
+In this example a viewer connection to :50 will result in a new Xvnc for that
+connection which should display the standard XDM login screen on that machine.
+Because the user needs to login via XDM, it is usually OK to accept connections
+without a VNC password in this case.
+
+In the wait mode, when the first connection comes in, inetd gives the listening
+socket to Xvnc. This means that for a given TCP port, there is only ever one
+Xvnc at a time. Further viewer connections to the same port are accepted by
+the same Xvnc in the normal way. Even when the original connection is broken,
+the Xvnc will continue to run. If this is used with the XDMCP options -query
+and -once, the Xvnc and associated X clients will die when the user logs out of
+the X session in the normal way. It is important to use a VNC password in this
+case. A typical entry in inetd.conf might be:
+
+5951 stream tcp wait james /usr/local/bin/Xvnc Xvnc -inetd -query localhost -once passwordFile=/home/james/.vnc/passwd
+
+In fact typically, you would have one entry for each user who uses VNC
+regularly, each of whom has their own dedicated TCP port which they use. In
+this example, when user "james" connects to :51, he enters his VNC password,
+then gets the XDM login screen where he logs in in the normal way. However,
+unlike the previous example, if he disconnects, the session remains persistent,
+and when he reconnects he will get the same session back again. When he logs
+out of the X session, the Xvnc will die, but of course a new one will be
+created automatically the next time he connects.
+
+.SH SEE ALSO
+.BR vncconfig (1),
+.BR vncpasswd (1),
+.BR vncserver (1),
+.BR vncviewer (1),
+.BR Xserver (1),
+.BR inetd (1)
+.br
+http://www.realvnc.com
+
+.SH AUTHOR
+Tristan Richardson, RealVNC Ltd.
+
+VNC was originally developed by the RealVNC team while at Olivetti Research Ltd
+/ AT&T Laboratories Cambridge. It is now being maintained by RealVNC Ltd. See
+http://www.realvnc.com for details.
diff --git a/xc/programs/Xserver/vnc/Imakefile b/xc/programs/Xserver/vnc/Imakefile
new file mode 100644
index 00000000..982233a5
--- /dev/null
+++ b/xc/programs/Xserver/vnc/Imakefile
@@ -0,0 +1,44 @@
+XCOMM CDEBUGFLAGS = -g
+XCOMM CXXDEBUGFLAGS = -g
+
+ VNCTOP = $(TOP)/..
+ VNCINCLUDE = -I$(VNCTOP) -I$(VNCTOP)/vncconfig
+
+#define CplusplusSource
+
+#if DoLoadableServer
+#define IHaveSubdirs
+#endif
+
+#include <Server.tmpl>
+
+#if DoLoadableServer
+ MODULE_SUBDIRS = module
+#endif
+ SRCS = vncExtInit.cc vncHooks.cc XserverDesktop.cc
+ OBJS = vncExtInit.o vncHooks.o XserverDesktop.o
+ INCLUDES = -I../include -I$(EXTINCSRC) -I$(XINCLUDESRC) -I$(FONTINCSRC) \
+ -I../mfb -I../mi $(VNCINCLUDE)
+#if defined(XFree86Version) && XFree86Version >= 4000
+ VNCDEFINES = -DGC_HAS_COMPOSITE_CLIP
+#endif
+#if defined(ProjectX) && (ProjectX >= 604)
+ VNCDEFINES = -DGC_HAS_COMPOSITE_CLIP
+#endif
+ DEFINES = $(STD_DEFINES) $(VNCDEFINES) -UXFree86LOADER
+
+#define IHaveSubdirs
+SUBDIRS = Xvnc $(MODULE_SUBDIRS)
+
+NormalLibraryTarget(vnc,$(OBJS))
+LintLibraryTarget(vnc,$(SRCS))
+NormalLintTarget($(SRCS))
+
+NormalLibraryObjectRule()
+NormalCplusplusObjectRule()
+
+
+MakeSubdirs($(SUBDIRS))
+DependSubdirs($(SUBDIRS))
+
+DependTarget()
diff --git a/xc/programs/Xserver/vnc/RegionHelper.h b/xc/programs/Xserver/vnc/RegionHelper.h
new file mode 100644
index 00000000..640d5589
--- /dev/null
+++ b/xc/programs/Xserver/vnc/RegionHelper.h
@@ -0,0 +1,77 @@
+/* Copyright (C) 2002-2003 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 __REGIONHELPER_H__
+#define __REGIONHELPER_H__
+
+// RegionHelper is a class which helps in using X server regions by
+// automatically freeing them in the destructor. It also fixes a problem with
+// REGION_INIT when given an empty rectangle.
+
+class RegionHelper {
+public:
+
+ // constructor from a single rect
+ RegionHelper(ScreenPtr pScreen_, BoxPtr rect, int size)
+ : pScreen(pScreen_), reg(0)
+ {
+ init(rect, size);
+ }
+
+ // constructor from an existing X server region
+ RegionHelper(ScreenPtr pScreen_, RegionPtr pRegion)
+ : pScreen(pScreen_), reg(&regRec)
+ {
+ REGION_INIT(pScreen, reg, NullBox, 0);
+ REGION_COPY(pScreen, reg, pRegion);
+ }
+
+ // constructor from an array of rectangles
+ RegionHelper(ScreenPtr pScreen_, int nrects, xRectanglePtr rects,
+ int ctype=CT_NONE)
+ : pScreen(pScreen_)
+ {
+ reg = RECTS_TO_REGION(pScreen, nrects, rects, ctype);
+ }
+
+ // constructor for calling init() later
+ RegionHelper(ScreenPtr pScreen_) : pScreen(pScreen_), reg(0) {
+ }
+
+ void init(BoxPtr rect, int size) {
+ reg = &regRec;
+ if (rect && (rect->x2 == rect->x1 || rect->y2 == rect->y1)) {
+ REGION_INIT(pScreen, reg, NullBox, 0);
+ } else {
+ REGION_INIT(pScreen, reg, rect, size);
+ }
+ }
+
+ // destructor frees as appropriate
+ ~RegionHelper() {
+ if (reg == &regRec) {
+ REGION_UNINIT(pScreen, reg);
+ } else if (reg) {
+ REGION_DESTROY(pScreen, reg);
+ }
+ }
+ ScreenPtr pScreen;
+ RegionRec regRec;
+ RegionPtr reg;
+};
+
+#endif
diff --git a/xc/programs/Xserver/vnc/XserverDesktop.cc b/xc/programs/Xserver/vnc/XserverDesktop.cc
new file mode 100644
index 00000000..9b5294ee
--- /dev/null
+++ b/xc/programs/Xserver/vnc/XserverDesktop.cc
@@ -0,0 +1,1079 @@
+/* Copyright (C) 2002-2004 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.
+ */
+//
+// XserverDesktop.cxx
+//
+
+#include <stdio.h>
+#include <strings.h>
+#include <unistd.h>
+#include <pwd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <sys/utsname.h>
+#include <network/TcpSocket.h>
+#include <rfb/Exception.h>
+#include <rfb/VNCServerST.h>
+#include <rfb/HTTPServer.h>
+#include <rfb/LogWriter.h>
+#include <rfb/Configuration.h>
+#include "XserverDesktop.h"
+#include "vncExtInit.h"
+
+extern "C" {
+#define public c_public
+#define class c_class
+
+ // windowTable is in globals.h in XFree 4, but not in XFree 3 unfortunately
+extern WindowPtr *WindowTable;
+extern char *display;
+
+#include "inputstr.h"
+#include "servermd.h"
+#include "colormapst.h"
+#include "resource.h"
+#include "cursorstr.h"
+#include "windowstr.h"
+#define XK_CYRILLIC
+#include "keysym.h"
+#undef public
+#undef class
+}
+
+using namespace rfb;
+using namespace network;
+
+static LogWriter vlog("XserverDesktop");
+
+rfb::IntParameter deferUpdateTime("DeferUpdate",
+ "Time in milliseconds to defer updates",40);
+
+rfb::BoolParameter alwaysSetDeferUpdateTimer("AlwaysSetDeferUpdateTimer",
+ "Always reset the defer update timer on every change",false);
+
+static KeyCode KeysymToKeycode(KeySymsPtr keymap, KeySym ks, int* col);
+
+static rdr::U8 reverseBits[] = {
+ 0x00, 0x80, 0x40, 0xc0, 0x20, 0xa0, 0x60, 0xe0, 0x10, 0x90, 0x50, 0xd0,
+ 0x30, 0xb0, 0x70, 0xf0, 0x08, 0x88, 0x48, 0xc8, 0x28, 0xa8, 0x68, 0xe8,
+ 0x18, 0x98, 0x58, 0xd8, 0x38, 0xb8, 0x78, 0xf8, 0x04, 0x84, 0x44, 0xc4,
+ 0x24, 0xa4, 0x64, 0xe4, 0x14, 0x94, 0x54, 0xd4, 0x34, 0xb4, 0x74, 0xf4,
+ 0x0c, 0x8c, 0x4c, 0xcc, 0x2c, 0xac, 0x6c, 0xec, 0x1c, 0x9c, 0x5c, 0xdc,
+ 0x3c, 0xbc, 0x7c, 0xfc, 0x02, 0x82, 0x42, 0xc2, 0x22, 0xa2, 0x62, 0xe2,
+ 0x12, 0x92, 0x52, 0xd2, 0x32, 0xb2, 0x72, 0xf2, 0x0a, 0x8a, 0x4a, 0xca,
+ 0x2a, 0xaa, 0x6a, 0xea, 0x1a, 0x9a, 0x5a, 0xda, 0x3a, 0xba, 0x7a, 0xfa,
+ 0x06, 0x86, 0x46, 0xc6, 0x26, 0xa6, 0x66, 0xe6, 0x16, 0x96, 0x56, 0xd6,
+ 0x36, 0xb6, 0x76, 0xf6, 0x0e, 0x8e, 0x4e, 0xce, 0x2e, 0xae, 0x6e, 0xee,
+ 0x1e, 0x9e, 0x5e, 0xde, 0x3e, 0xbe, 0x7e, 0xfe, 0x01, 0x81, 0x41, 0xc1,
+ 0x21, 0xa1, 0x61, 0xe1, 0x11, 0x91, 0x51, 0xd1, 0x31, 0xb1, 0x71, 0xf1,
+ 0x09, 0x89, 0x49, 0xc9, 0x29, 0xa9, 0x69, 0xe9, 0x19, 0x99, 0x59, 0xd9,
+ 0x39, 0xb9, 0x79, 0xf9, 0x05, 0x85, 0x45, 0xc5, 0x25, 0xa5, 0x65, 0xe5,
+ 0x15, 0x95, 0x55, 0xd5, 0x35, 0xb5, 0x75, 0xf5, 0x0d, 0x8d, 0x4d, 0xcd,
+ 0x2d, 0xad, 0x6d, 0xed, 0x1d, 0x9d, 0x5d, 0xdd, 0x3d, 0xbd, 0x7d, 0xfd,
+ 0x03, 0x83, 0x43, 0xc3, 0x23, 0xa3, 0x63, 0xe3, 0x13, 0x93, 0x53, 0xd3,
+ 0x33, 0xb3, 0x73, 0xf3, 0x0b, 0x8b, 0x4b, 0xcb, 0x2b, 0xab, 0x6b, 0xeb,
+ 0x1b, 0x9b, 0x5b, 0xdb, 0x3b, 0xbb, 0x7b, 0xfb, 0x07, 0x87, 0x47, 0xc7,
+ 0x27, 0xa7, 0x67, 0xe7, 0x17, 0x97, 0x57, 0xd7, 0x37, 0xb7, 0x77, 0xf7,
+ 0x0f, 0x8f, 0x4f, 0xcf, 0x2f, 0xaf, 0x6f, 0xef, 0x1f, 0x9f, 0x5f, 0xdf,
+ 0x3f, 0xbf, 0x7f, 0xff
+};
+
+
+class MyHTTPServer : public rfb::HTTPServer {
+public:
+ MyHTTPServer(XserverDesktop* d) : desktop(d) {}
+ virtual ~MyHTTPServer() {}
+
+ virtual rdr::InStream* getFile(const char* name, const char** contentType) {
+ if (name[0] != '/' || strstr(name, "..") != 0) {
+ vlog.info("http request was for invalid file name");
+ return 0;
+ }
+
+ if (strcmp(name, "/") == 0) name = "/index.vnc";
+
+ CharArray httpDirStr(httpDir.getData());
+ CharArray fname(strlen(httpDirStr.buf)+strlen(name)+1);
+ sprintf(fname.buf, "%s%s", httpDirStr.buf, name);
+ int fd = open(fname.buf, O_RDONLY);
+ if (fd < 0) return 0;
+
+ rdr::InStream* is = new rdr::FdInStream(fd, -1, 0, true);
+ *contentType = guessContentType(name, *contentType);
+ if (strlen(name) > 4 && strcasecmp(&name[strlen(name)-4], ".vnc") == 0) {
+ is = new rdr::SubstitutingInStream(is, desktop, 20);
+ *contentType = "text/html";
+ }
+ return is;
+ }
+
+ XserverDesktop* desktop;
+};
+
+
+XserverDesktop::XserverDesktop(ScreenPtr pScreen_,
+ network::TcpListener* listener_,
+ network::TcpListener* httpListener_,
+ const char* name, void* fbptr)
+ : pScreen(pScreen_), deferredUpdateTimer(0), dummyTimer(0),
+ server(0), httpServer(0),
+ listener(listener_), httpListener(httpListener_),
+ cmap(0), deferredUpdateTimerSet(false),
+ grabbing(false), ignoreHooks_(false), directFbptr(fbptr != 0),
+ oldButtonMask(0), cursorX(0), cursorY(0),
+ oldCursorX(0), oldCursorY(0)
+{
+ int i;
+ format.depth = pScreen->rootDepth;
+ for (i = 0; i < screenInfo.numPixmapFormats; i++) {
+ if (screenInfo.formats[i].depth == format.depth) {
+ format.bpp = screenInfo.formats[i].bitsPerPixel;
+ break;
+ }
+ }
+ if (i == screenInfo.numPixmapFormats) {
+ fprintf(stderr,"no pixmap format for root depth???\n");
+ abort();
+ }
+ format.bigEndian = (screenInfo.imageByteOrder == MSBFirst);
+
+ VisualPtr vis;
+ for (i = 0; i < pScreen->numVisuals; i++) {
+ if (pScreen->visuals[i].vid == pScreen->rootVisual) {
+ vis = &pScreen->visuals[i];
+ break;
+ }
+ }
+ if (i == pScreen->numVisuals) {
+ fprintf(stderr,"no visual rec for root visual???\n");
+ abort();
+ }
+ format.trueColour = (vis->c_class == TrueColor);
+ if (!format.trueColour && format.bpp != 8)
+ throw rfb::Exception("X server uses unsupported visual");
+ format.redShift = ffs(vis->redMask) - 1;
+ format.greenShift = ffs(vis->greenMask) - 1;
+ format.blueShift = ffs(vis->blueMask) - 1;
+ format.redMax = vis->redMask >> format.redShift;
+ format.greenMax = vis->greenMask >> format.greenShift;
+ format.blueMax = vis->blueMask >> format.blueShift;
+
+ width_ = pScreen->width;
+ height_ = pScreen->height;
+ if (fbptr)
+ data = (rdr::U8*)fbptr;
+ else
+ data = new rdr::U8[pScreen->width * pScreen->height * (format.bpp/8)];
+ colourmap = this;
+
+ serverReset(pScreen);
+
+ server = new VNCServerST(name, this);
+ server->setPixelBuffer(this);
+
+ if (httpListener)
+ httpServer = new MyHTTPServer(this);
+}
+
+XserverDesktop::~XserverDesktop()
+{
+ if (!directFbptr)
+ delete [] data;
+ TimerFree(deferredUpdateTimer);
+ TimerFree(dummyTimer);
+ delete httpServer;
+ delete server;
+}
+
+void XserverDesktop::serverReset(ScreenPtr pScreen_)
+{
+ pScreen = pScreen_;
+ XID* ids = new XID[pScreen->maxInstalledCmaps];
+ int nmaps = (*pScreen->ListInstalledColormaps)(pScreen, ids);
+ cmap = (ColormapPtr)LookupIDByType(ids[0], RT_COLORMAP);
+ delete [] ids;
+}
+
+char* XserverDesktop::substitute(const char* varName)
+{
+ if (strcmp(varName, "$$") == 0) {
+ return rfb::strDup("$");
+ }
+ if (strcmp(varName, "$PORT") == 0) {
+ char* str = new char[10];
+ sprintf(str, "%d", listener ? listener->getMyPort() : 0);
+ return str;
+ }
+ if (strcmp(varName, "$WIDTH") == 0) {
+ char* str = new char[10];
+ sprintf(str, "%d", width());
+ return str;
+ }
+ if (strcmp(varName, "$HEIGHT") == 0) {
+ char* str = new char[10];
+ sprintf(str, "%d", height());
+ return str;
+ }
+ if (strcmp(varName, "$APPLETWIDTH") == 0) {
+ char* str = new char[10];
+ sprintf(str, "%d", width());
+ return str;
+ }
+ if (strcmp(varName, "$APPLETHEIGHT") == 0) {
+ char* str = new char[10];
+ sprintf(str, "%d", height() + 32);
+ return str;
+ }
+ if (strcmp(varName, "$DESKTOP") == 0) {
+ return rfb::strDup(server->getName());
+ }
+ if (strcmp(varName, "$DISPLAY") == 0) {
+ struct utsname uts;
+ uname(&uts);
+ char* str = new char[256];
+ strncat(str, uts.nodename, 240);
+ strcat(str, ":");
+ strncat(str, display, 10);
+ return str;
+ }
+ if (strcmp(varName, "$USER") == 0) {
+ struct passwd* user = getpwuid(getuid());
+ return rfb::strDup(user ? user->pw_name : "?");
+ }
+ return 0;
+}
+
+void XserverDesktop::setColormap(ColormapPtr cmap_)
+{
+ if (cmap != cmap_) {
+ cmap = cmap_;
+ setColourMapEntries(0, 0);
+ }
+}
+
+void XserverDesktop::setColourMapEntries(ColormapPtr pColormap, int ndef,
+ xColorItem* pdef)
+{
+ if (cmap != pColormap || ndef <= 0) return;
+
+ int first = pdef[0].pixel;
+ int n = 1;
+
+ for (int i = 1; i < ndef; i++) {
+ if (first + n == pdef[i].pixel) {
+ n++;
+ } else {
+ setColourMapEntries(first, n);
+ first = pdef[i].pixel;
+ n = 1;
+ }
+ }
+ setColourMapEntries(first, n);
+}
+
+void XserverDesktop::setColourMapEntries(int firstColour, int nColours)
+{
+ try {
+ server->setColourMapEntries(firstColour, nColours);
+ } catch (rdr::Exception& e) {
+ vlog.error("XserverDesktop::setColourMapEntries: %s",e.str());
+ }
+}
+
+void XserverDesktop::bell()
+{
+ server->bell();
+}
+
+void XserverDesktop::serverCutText(const char* str, int len)
+{
+ try {
+ server->serverCutText(str, len);
+ } catch (rdr::Exception& e) {
+ vlog.error("XserverDesktop::serverCutText: %s",e.str());
+ }
+}
+
+void XserverDesktop::setCursor(CursorPtr cursor)
+{
+ try {
+ int w = cursor->bits->width;
+ int h = cursor->bits->height;
+ rdr::U8* cursorData = new rdr::U8[w * h * (getPF().bpp / 8)];
+
+ xColorItem fg, bg;
+ fg.red = cursor->foreRed;
+ fg.green = cursor->foreGreen;
+ fg.blue = cursor->foreBlue;
+ FakeAllocColor(cmap, &fg);
+ bg.red = cursor->backRed;
+ bg.green = cursor->backGreen;
+ bg.blue = cursor->backBlue;
+ FakeAllocColor(cmap, &bg);
+ FakeFreeColor(cmap, fg.pixel);
+ FakeFreeColor(cmap, bg.pixel);
+
+ int xMaskBytesPerRow = BitmapBytePad(w);
+
+ for (int y = 0; y < h; y++) {
+ for (int x = 0; x < w; x++) {
+ int byte = y * xMaskBytesPerRow + x / 8;
+#if (BITMAP_BIT_ORDER == MSBFirst)
+ int bit = 7 - x % 8;
+#else
+ int bit = x % 8;
+#endif
+ switch (getPF().bpp) {
+ case 8:
+ ((rdr::U8*)cursorData)[y * w + x]
+ = (cursor->bits->source[byte] & (1 << bit)) ? fg.pixel : bg.pixel;
+ break;
+ case 16:
+ ((rdr::U16*)cursorData)[y * w + x]
+ = (cursor->bits->source[byte] & (1 << bit)) ? fg.pixel : bg.pixel;
+ break;
+ case 32:
+ ((rdr::U32*)cursorData)[y * w + x]
+ = (cursor->bits->source[byte] & (1 << bit)) ? fg.pixel : bg.pixel;
+ break;
+ }
+ }
+ }
+
+ int rfbMaskBytesPerRow = (w + 7) / 8;
+
+ rdr::U8* cursorMask = new rdr::U8[rfbMaskBytesPerRow * h];
+
+ for (int j = 0; j < h; j++) {
+ for (int i = 0; i < rfbMaskBytesPerRow; i++)
+#if (BITMAP_BIT_ORDER == MSBFirst)
+ cursorMask[j * rfbMaskBytesPerRow + i]
+ = cursor->bits->mask[j * xMaskBytesPerRow + i];
+#else
+ cursorMask[j * rfbMaskBytesPerRow + i]
+ = reverseBits[cursor->bits->mask[j * xMaskBytesPerRow + i]];
+#endif
+ }
+
+ server->setCursor(cursor->bits->width, cursor->bits->height,
+ cursor->bits->xhot, cursor->bits->yhot,
+ cursorData, cursorMask);
+ server->tryUpdate();
+ delete [] cursorData;
+ delete [] cursorMask;
+ } catch (rdr::Exception& e) {
+ vlog.error("XserverDesktop::setCursor: %s",e.str());
+ }
+}
+
+static void printRegion(RegionPtr reg)
+{
+ int nrects = REGION_NUM_RECTS(reg);
+
+ fprintf(stderr,"Region num rects %2d extents %3d,%3d %3dx%3d\n",nrects,
+ (REGION_EXTENTS(pScreen,reg))->x1,
+ (REGION_EXTENTS(pScreen,reg))->y1,
+ (REGION_EXTENTS(pScreen,reg))->x2-(REGION_EXTENTS(pScreen,reg))->x1,
+ (REGION_EXTENTS(pScreen,reg))->y2-(REGION_EXTENTS(pScreen,reg))->y1);
+
+ for (int i = 0; i < nrects; i++) {
+ fprintf(stderr," rect %3d,%3d %3dx%3d\n",
+ REGION_RECTS(reg)[i].x1,
+ REGION_RECTS(reg)[i].y1,
+ REGION_RECTS(reg)[i].x2-REGION_RECTS(reg)[i].x1,
+ REGION_RECTS(reg)[i].y2-REGION_RECTS(reg)[i].y1);
+ }
+}
+
+CARD32 XserverDesktop::deferredUpdateTimerCallback(OsTimerPtr timer,
+ CARD32 now, pointer arg)
+{
+ XserverDesktop* desktop = (XserverDesktop*)arg;
+ desktop->deferredUpdateTimerSet = false;
+ try {
+ desktop->server->tryUpdate();
+ } catch (rdr::Exception& e) {
+ vlog.error("XserverDesktop::deferredUpdateTimerCallback: %s",e.str());
+ }
+ return 0;
+}
+
+void XserverDesktop::add_changed(RegionPtr reg)
+{
+ if (ignoreHooks_) return;
+ if (grabbing) return;
+ try {
+ rfb::Region rfbReg;
+ rfbReg.setExtentsAndOrderedRects((ShortRect*)REGION_EXTENTS(pScreen, reg),
+ REGION_NUM_RECTS(reg),
+ (ShortRect*)REGION_RECTS(reg));
+ server->add_changed(rfbReg);
+ if (!deferredUpdateTimerSet || alwaysSetDeferUpdateTimer) {
+ deferredUpdateTimer = TimerSet(deferredUpdateTimer, 0,
+ deferUpdateTime,
+ deferredUpdateTimerCallback, this);
+ deferredUpdateTimerSet = true;
+ }
+ } catch (rdr::Exception& e) {
+ vlog.error("XserverDesktop::add_changed: %s",e.str());
+ }
+}
+
+void XserverDesktop::add_copied(RegionPtr dst, int dx, int dy)
+{
+ if (ignoreHooks_) return;
+ if (grabbing) return;
+ try {
+ rfb::Region rfbReg;
+ rfbReg.setExtentsAndOrderedRects((ShortRect*)REGION_EXTENTS(pScreen, dst),
+ REGION_NUM_RECTS(dst),
+ (ShortRect*)REGION_RECTS(dst));
+ server->add_copied(rfbReg, rfb::Point(dx, dy));
+ if (!deferredUpdateTimerSet || alwaysSetDeferUpdateTimer) {
+ deferredUpdateTimer = TimerSet(deferredUpdateTimer, 0,
+ deferUpdateTime,
+ deferredUpdateTimerCallback, this);
+ deferredUpdateTimerSet = true;
+ }
+ } catch (rdr::Exception& e) {
+ vlog.error("XserverDesktop::add_copied: %s",e.str());
+ }
+}
+
+void XserverDesktop::positionCursor()
+{
+ if (cursorX != oldCursorX || cursorY != oldCursorY) {
+ oldCursorX = cursorX;
+ oldCursorY = cursorY;
+ (*pScreen->SetCursorPosition) (pScreen, cursorX, cursorY, FALSE);
+ server->setCursorPos(cursorX, cursorY);
+ server->tryUpdate();
+ }
+}
+
+void XserverDesktop::blockHandler(fd_set* fds)
+{
+ try {
+ ScreenPtr screenWithCursor = GetCurrentRootWindow()->drawable.pScreen;
+ if (screenWithCursor == pScreen) {
+ int x, y;
+ GetSpritePosition(&x, &y);
+ if (x != cursorX || y != cursorY) {
+ cursorX = oldCursorX = x;
+ cursorY = oldCursorY = y;
+ server->setCursorPos(x, y);
+ server->tryUpdate();
+ }
+ }
+
+ if (listener)
+ FD_SET(listener->getFd(), fds);
+ if (httpListener)
+ FD_SET(httpListener->getFd(), fds);
+
+ std::list<Socket*> sockets;
+ server->getSockets(&sockets);
+ std::list<Socket*>::iterator i;
+ for (i = sockets.begin(); i != sockets.end(); i++) {
+ FD_SET((*i)->getFd(), fds);
+ }
+ if (httpServer) {
+ httpServer->getSockets(&sockets);
+ for (i = sockets.begin(); i != sockets.end(); i++) {
+ FD_SET((*i)->getFd(), fds);
+ }
+ }
+ } catch (rdr::Exception& e) {
+ vlog.error("XserverDesktop::blockHandler: %s",e.str());
+ }
+}
+
+static CARD32 dummyTimerCallback(OsTimerPtr timer, CARD32 now, pointer arg) {
+ return 0;
+}
+
+void XserverDesktop::wakeupHandler(fd_set* fds, int nfds)
+{
+ try {
+ if (nfds >= 1) {
+
+ if (listener) {
+ if (FD_ISSET(listener->getFd(), fds)) {
+ FD_CLR(listener->getFd(), fds);
+ Socket* sock = listener->accept();
+ server->addClient(sock);
+ vlog.debug("new client, sock %d",sock->getFd());
+ }
+ }
+
+ if (httpListener) {
+ if (FD_ISSET(httpListener->getFd(), fds)) {
+ FD_CLR(httpListener->getFd(), fds);
+ Socket* sock = httpListener->accept();
+ httpServer->addClient(sock);
+ vlog.debug("new http client, sock %d",sock->getFd());
+ }
+ }
+
+ std::list<Socket*> sockets;
+ server->getSockets(&sockets);
+ std::list<Socket*>::iterator i;
+ for (i = sockets.begin(); i != sockets.end(); i++) {
+ int fd = (*i)->getFd();
+ if (FD_ISSET(fd, fds)) {
+ FD_CLR(fd, fds);
+ if (!server->processSocketEvent(*i)) {
+ vlog.debug("client gone, sock %d",fd);
+ vncClientGone(fd);
+ }
+ }
+ }
+
+ if (httpServer) {
+ httpServer->getSockets(&sockets);
+ for (i = sockets.begin(); i != sockets.end(); i++) {
+ int fd = (*i)->getFd();
+ if (FD_ISSET(fd, fds)) {
+ FD_CLR(fd, fds);
+ if (!httpServer->processSocketEvent(*i)) {
+ vlog.debug("http client gone, sock %d",fd);
+ }
+ }
+ }
+ }
+
+ positionCursor();
+ }
+
+ int timeout = server->checkTimeouts();
+ if (timeout > 0) {
+ // set a dummy timer just so we are guaranteed be called again next time.
+ dummyTimer = TimerSet(dummyTimer, 0, timeout,
+ dummyTimerCallback, 0);
+ }
+
+ } catch (rdr::Exception& e) {
+ vlog.error("XserverDesktop::wakeupHandler: %s",e.str());
+ }
+}
+
+void XserverDesktop::addClient(Socket* sock, bool reverse)
+{
+ vlog.debug("new client, sock %d reverse %d",sock->getFd(),reverse);
+ server->addClient(sock, reverse);
+}
+
+void XserverDesktop::disconnectClients()
+{
+ vlog.debug("disconnecting all clients");
+ return server->closeClients("Disconnection from server end");
+}
+
+
+///////////////////////////////////////////////////////////////////////////
+//
+// SDesktop callbacks
+
+
+void XserverDesktop::pointerEvent(const Point& pos, rdr::U8 buttonMask)
+{
+ xEvent ev;
+ DevicePtr dev = LookupPointerDevice();
+
+ // SetCursorPosition seems to be very expensive (at least on XFree86 3.3.6
+ // for S3), so we delay calling it until positionCursor() is called at the
+ // end of processing a load of RFB.
+ //(*pScreen->SetCursorPosition) (pScreen, pos.x, pos.y, FALSE);
+
+ NewCurrentScreen(pScreen, pos.x, pos.y);
+
+ ev.u.u.type = MotionNotify;
+ ev.u.u.detail = 0;
+ ev.u.keyButtonPointer.rootX = pos.x;
+ ev.u.keyButtonPointer.rootY = pos.y;
+ ev.u.keyButtonPointer.time = GetTimeInMillis();
+
+ if (pos.x != cursorX || pos.y != cursorY)
+ (*dev->processInputProc)(&ev, (DeviceIntPtr)dev, 1);
+
+ for (int i = 0; i < 5; i++) {
+ if ((buttonMask ^ oldButtonMask) & (1<<i)) {
+#ifdef XINPUT
+ // God knows why but some idiot decided to conditionally move the pointer
+ // mapping out of DIX, so we guess here that if XINPUT is defined we have
+ // to do it ourselves...
+ ev.u.u.detail = ((DeviceIntPtr)dev)->button->map[i + 1];
+#else
+ ev.u.u.detail = i + 1;
+#endif
+ ev.u.u.type = (buttonMask & (1<<i)) ? ButtonPress : ButtonRelease;
+ (*dev->processInputProc)(&ev, (DeviceIntPtr)dev, 1);
+ }
+ }
+
+ cursorX = pos.x;
+ cursorY = pos.y;
+ oldButtonMask = buttonMask;
+}
+
+void XserverDesktop::clientCutText(const char* str, int len)
+{
+ vncClientCutText(str, len);
+}
+
+void XserverDesktop::grabRegion(const rfb::Region& region)
+{
+ if (directFbptr) return;
+ if (!pScreen->GetImage) {
+ vlog.error("VNC error: pScreen->GetImage == 0");
+ return;
+ }
+
+ grabbing = true;
+
+ int bytesPerPixel = format.bpp/8;
+ int bytesPerRow = pScreen->width * bytesPerPixel;
+
+ std::vector<rfb::Rect> rects;
+ std::vector<rfb::Rect>::iterator i;
+ region.get_rects(&rects);
+ for (i = rects.begin(); i != rects.end(); i++) {
+ for (int y = i->tl.y; y < i->br.y; y++) {
+ (*pScreen->GetImage) ((DrawablePtr)WindowTable[pScreen->myNum],
+ i->tl.x, y, i->width(), 1,
+ ZPixmap, (unsigned long)~0L,
+ ((char*)data
+ + y * bytesPerRow + i->tl.x * bytesPerPixel));
+ }
+ }
+ grabbing = false;
+}
+
+void XserverDesktop::lookup(int index, int* r, int* g, int* b)
+{
+ EntryPtr pent;
+ pent = (EntryPtr)&cmap->red[index];
+ if (pent->fShared) {
+ *r = pent->co.shco.red->color;
+ *g = pent->co.shco.green->color;
+ *b = pent->co.shco.blue->color;
+ } else {
+ *r = pent->co.local.red;
+ *g = pent->co.local.green;
+ *b = pent->co.local.blue;
+ }
+}
+
+//
+// Keyboard handling
+//
+
+#define IS_PRESSED(keyc, keycode) \
+ ((keyc)->down[(keycode) >> 3] & (1 << ((keycode) & 7)))
+
+// ModifierState is a class which helps simplify generating a "fake" press
+// or release of shift, ctrl, alt, etc. An instance of the class is created
+// for every modifier which may need to be pressed or released. Then either
+// press() or release() may be called to make sure that the corresponding keys
+// are in the right state. The destructor of the class automatically reverts
+// to the previous state. Each modifier may have multiple keys associated with
+// it, so in the case of a fake release, this may involve releasing more than
+// one key.
+
+class ModifierState {
+public:
+ ModifierState(DeviceIntPtr dev_, int modIndex_)
+ : dev(dev_), modIndex(modIndex_), nKeys(0), keys(0), pressed(false)
+ {
+ }
+ ~ModifierState() {
+ for (int i = 0; i < nKeys; i++)
+ generateXKeyEvent(keys[i], !pressed);
+ delete [] keys;
+ }
+ void press() {
+ KeyClassPtr keyc = dev->key;
+ if (!(keyc->state & (1<<modIndex))) {
+ tempKeyEvent(keyc->modifierKeyMap[modIndex * keyc->maxKeysPerModifier],
+ true);
+ pressed = true;
+ }
+ }
+ void release() {
+ KeyClassPtr keyc = dev->key;
+ if (keyc->state & (1<<modIndex)) {
+ for (int k = 0; k < keyc->maxKeysPerModifier; k++) {
+ int keycode
+ = keyc->modifierKeyMap[modIndex * keyc->maxKeysPerModifier + k];
+ if (keycode && IS_PRESSED(keyc, keycode))
+ tempKeyEvent(keycode, false);
+ }
+ }
+ }
+private:
+ void tempKeyEvent(int keycode, bool down) {
+ if (keycode) {
+ if (!keys) keys = new int[dev->key->maxKeysPerModifier];
+ keys[nKeys++] = keycode;
+ generateXKeyEvent(keycode, down);
+ }
+ }
+ void generateXKeyEvent(int keycode, bool down) {
+ xEvent ev;
+ ev.u.u.type = down ? KeyPress : KeyRelease;
+ ev.u.u.detail = keycode;
+ ev.u.keyButtonPointer.time = GetTimeInMillis();
+ (*dev->c_public.processInputProc)(&ev, dev, 1);
+ vlog.debug("fake keycode %d %s", keycode, down ? "down" : "up");
+ }
+ DeviceIntPtr dev;
+ int modIndex;
+ int nKeys;
+ int* keys;
+ bool pressed;
+};
+
+
+// altKeysym is a table of alternative keysyms which have the same meaning.
+
+struct altKeysym_t {
+ KeySym a, b;
+};
+
+altKeysym_t altKeysym[] = {
+ { XK_Shift_L, XK_Shift_R },
+ { XK_Control_L, XK_Control_R },
+ { XK_Meta_L, XK_Meta_R },
+ { XK_Alt_L, XK_Alt_R },
+ { XK_Super_L, XK_Super_R },
+ { XK_Hyper_L, XK_Hyper_R },
+ { XK_KP_Space, XK_space },
+ { XK_KP_Tab, XK_Tab },
+ { XK_KP_Enter, XK_Return },
+ { XK_KP_F1, XK_F1 },
+ { XK_KP_F2, XK_F2 },
+ { XK_KP_F3, XK_F3 },
+ { XK_KP_F4, XK_F4 },
+ { XK_KP_Home, XK_Home },
+ { XK_KP_Left, XK_Left },
+ { XK_KP_Up, XK_Up },
+ { XK_KP_Right, XK_Right },
+ { XK_KP_Down, XK_Down },
+ { XK_KP_Page_Up, XK_Page_Up },
+ { XK_KP_Page_Down, XK_Page_Down },
+ { XK_KP_End, XK_End },
+ { XK_KP_Begin, XK_Begin },
+ { XK_KP_Insert, XK_Insert },
+ { XK_KP_Delete, XK_Delete },
+ { XK_KP_Equal, XK_equal },
+ { XK_KP_Multiply, XK_asterisk },
+ { XK_KP_Add, XK_plus },
+ { XK_KP_Separator, XK_comma },
+ { XK_KP_Subtract, XK_minus },
+ { XK_KP_Decimal, XK_period },
+ { XK_KP_Divide, XK_slash },
+ { XK_KP_0, XK_0 },
+ { XK_KP_1, XK_1 },
+ { XK_KP_2, XK_2 },
+ { XK_KP_3, XK_3 },
+ { XK_KP_4, XK_4 },
+ { XK_KP_5, XK_5 },
+ { XK_KP_6, XK_6 },
+ { XK_KP_7, XK_7 },
+ { XK_KP_8, XK_8 },
+ { XK_KP_9, XK_9 },
+};
+
+// keyEvent() - work out the best keycode corresponding to the keysym sent by
+// the viewer. This is non-trivial because we can't assume much about the
+// local keyboard layout. We must also find out which column of the keyboard
+// mapping the keysym is in, and alter the shift state appropriately. Column 0
+// means both shift and "mode_switch" (AltGr) must be released, column 1 means
+// shift must be pressed and mode_switch released, column 2 means shift must be
+// released and mode_switch pressed, and column 3 means both shift and
+// mode_switch must be pressed.
+
+void XserverDesktop::keyEvent(rdr::U32 keysym, bool down)
+{
+ if (keysym == XK_Caps_Lock) {
+ vlog.debug("Ignoring caps lock");
+ return;
+ }
+ DeviceIntPtr dev = (DeviceIntPtr)LookupKeyboardDevice();
+ KeyClassPtr keyc = dev->key;
+ KeySymsPtr keymap = &keyc->curKeySyms;
+
+ // find which modifier Mode_switch is on.
+ int modeSwitchMapIndex = 0;
+ for (int i = 3; i < 8; i++) {
+ for (int k = 0; k < keyc->maxKeysPerModifier; k++) {
+ int keycode = keyc->modifierKeyMap[i * keyc->maxKeysPerModifier + k];
+ for (int j = 0; j < keymap->mapWidth; j++) {
+ if (keycode != 0 &&
+ keymap->map[(keycode - keymap->minKeyCode)
+ * keymap->mapWidth + j] == XK_Mode_switch)
+ {
+ modeSwitchMapIndex = i;
+ break;
+ }
+ }
+ }
+ }
+
+ int col = 0;
+ if (keyc->state & (1<<ShiftMapIndex)) col |= 1;
+ if (modeSwitchMapIndex && (keyc->state & (1<<modeSwitchMapIndex))) col |= 2;
+
+ int kc = KeysymToKeycode(keymap, keysym, &col);
+
+ // Sort out the "shifted Tab" mess. If we are sent a shifted Tab, generate a
+ // local shifted Tab regardless of what the "shifted Tab" keysym is on the
+ // local keyboard (it might be Tab, ISO_Left_Tab or HP's private BackTab
+ // keysym, and quite possibly some others too). We never get ISO_Left_Tab
+ // here because it's already been translated in VNCSConnectionST.
+ if (keysym == XK_Tab && (keyc->state & (1<<ShiftMapIndex)))
+ col |= 1;
+
+ if (kc == 0) {
+ // Not a direct match in the local keyboard mapping. Check for alternative
+ // keysyms with the same meaning.
+ for (int i = 0; i < sizeof(altKeysym) / sizeof(altKeysym_t); i++) {
+ if (keysym == altKeysym[i].a)
+ kc = KeysymToKeycode(keymap, altKeysym[i].b, &col);
+ else if (keysym == altKeysym[i].b)
+ kc = KeysymToKeycode(keymap, altKeysym[i].a, &col);
+ if (kc) break;
+ }
+ }
+
+ if (kc == 0) {
+ // Last resort - dynamically add a new key to the keyboard mapping.
+ for (kc = keymap->maxKeyCode; kc >= keymap->minKeyCode; kc--) {
+ if (!keymap->map[(kc - keymap->minKeyCode) * keymap->mapWidth]) {
+ keymap->map[(kc - keymap->minKeyCode) * keymap->mapWidth] = keysym;
+ col = 0;
+ SendMappingNotify(MappingKeyboard, kc, 1, serverClient);
+ vlog.info("Added unknown keysym 0x%x to keycode %d",keysym,kc);
+ break;
+ }
+ }
+ if (kc < keymap->minKeyCode) {
+ vlog.info("Keyboard mapping full - ignoring unknown keysym 0x%x",keysym);
+ return;
+ }
+ }
+
+ // See if it's a modifier key. If so, then don't do any auto-repeat, because
+ // the X server will translate each press into a release followed by a press.
+ for (int i = 0; i < 8; i++) {
+ for (int k = 0; k < keyc->maxKeysPerModifier; k++) {
+ if (kc == keyc->modifierKeyMap[i * keyc->maxKeysPerModifier + k] &&
+ IS_PRESSED(keyc,kc) && down)
+ return;
+ }
+ }
+
+ ModifierState shift(dev, ShiftMapIndex);
+ ModifierState modeSwitch(dev, modeSwitchMapIndex);
+ if (down) {
+ if (col & 1)
+ shift.press();
+ else
+ shift.release();
+ if (modeSwitchMapIndex) {
+ if (col & 2)
+ modeSwitch.press();
+ else
+ modeSwitch.release();
+ }
+ }
+ vlog.debug("keycode %d %s", kc, down ? "down" : "up");
+ xEvent ev;
+ ev.u.u.type = down ? KeyPress : KeyRelease;
+ ev.u.u.detail = kc;
+ ev.u.keyButtonPointer.time = GetTimeInMillis();
+ (*dev->c_public.processInputProc)(&ev, dev, 1);
+}
+
+
+void XConvertCase(KeySym sym, KeySym *lower, KeySym *upper)
+{
+ *lower = sym;
+ *upper = sym;
+ switch(sym >> 8) {
+ case 0: /* Latin 1 */
+ if ((sym >= XK_A) && (sym <= XK_Z))
+ *lower += (XK_a - XK_A);
+ else if ((sym >= XK_a) && (sym <= XK_z))
+ *upper -= (XK_a - XK_A);
+ else if ((sym >= XK_Agrave) && (sym <= XK_Odiaeresis))
+ *lower += (XK_agrave - XK_Agrave);
+ else if ((sym >= XK_agrave) && (sym <= XK_odiaeresis))
+ *upper -= (XK_agrave - XK_Agrave);
+ else if ((sym >= XK_Ooblique) && (sym <= XK_Thorn))
+ *lower += (XK_oslash - XK_Ooblique);
+ else if ((sym >= XK_oslash) && (sym <= XK_thorn))
+ *upper -= (XK_oslash - XK_Ooblique);
+ break;
+ case 1: /* Latin 2 */
+ /* Assume the KeySym is a legal value (ignore discontinuities) */
+ if (sym == XK_Aogonek)
+ *lower = XK_aogonek;
+ else if (sym >= XK_Lstroke && sym <= XK_Sacute)
+ *lower += (XK_lstroke - XK_Lstroke);
+ else if (sym >= XK_Scaron && sym <= XK_Zacute)
+ *lower += (XK_scaron - XK_Scaron);
+ else if (sym >= XK_Zcaron && sym <= XK_Zabovedot)
+ *lower += (XK_zcaron - XK_Zcaron);
+ else if (sym == XK_aogonek)
+ *upper = XK_Aogonek;
+ else if (sym >= XK_lstroke && sym <= XK_sacute)
+ *upper -= (XK_lstroke - XK_Lstroke);
+ else if (sym >= XK_scaron && sym <= XK_zacute)
+ *upper -= (XK_scaron - XK_Scaron);
+ else if (sym >= XK_zcaron && sym <= XK_zabovedot)
+ *upper -= (XK_zcaron - XK_Zcaron);
+ else if (sym >= XK_Racute && sym <= XK_Tcedilla)
+ *lower += (XK_racute - XK_Racute);
+ else if (sym >= XK_racute && sym <= XK_tcedilla)
+ *upper -= (XK_racute - XK_Racute);
+ break;
+ case 2: /* Latin 3 */
+ /* Assume the KeySym is a legal value (ignore discontinuities) */
+ if (sym >= XK_Hstroke && sym <= XK_Hcircumflex)
+ *lower += (XK_hstroke - XK_Hstroke);
+ else if (sym >= XK_Gbreve && sym <= XK_Jcircumflex)
+ *lower += (XK_gbreve - XK_Gbreve);
+ else if (sym >= XK_hstroke && sym <= XK_hcircumflex)
+ *upper -= (XK_hstroke - XK_Hstroke);
+ else if (sym >= XK_gbreve && sym <= XK_jcircumflex)
+ *upper -= (XK_gbreve - XK_Gbreve);
+ else if (sym >= XK_Cabovedot && sym <= XK_Scircumflex)
+ *lower += (XK_cabovedot - XK_Cabovedot);
+ else if (sym >= XK_cabovedot && sym <= XK_scircumflex)
+ *upper -= (XK_cabovedot - XK_Cabovedot);
+ break;
+ case 3: /* Latin 4 */
+ /* Assume the KeySym is a legal value (ignore discontinuities) */
+ if (sym >= XK_Rcedilla && sym <= XK_Tslash)
+ *lower += (XK_rcedilla - XK_Rcedilla);
+ else if (sym >= XK_rcedilla && sym <= XK_tslash)
+ *upper -= (XK_rcedilla - XK_Rcedilla);
+ else if (sym == XK_ENG)
+ *lower = XK_eng;
+ else if (sym == XK_eng)
+ *upper = XK_ENG;
+ else if (sym >= XK_Amacron && sym <= XK_Umacron)
+ *lower += (XK_amacron - XK_Amacron);
+ else if (sym >= XK_amacron && sym <= XK_umacron)
+ *upper -= (XK_amacron - XK_Amacron);
+ break;
+ case 6: /* Cyrillic */
+ /* Assume the KeySym is a legal value (ignore discontinuities) */
+ if (sym >= XK_Serbian_DJE && sym <= XK_Serbian_DZE)
+ *lower -= (XK_Serbian_DJE - XK_Serbian_dje);
+ else if (sym >= XK_Serbian_dje && sym <= XK_Serbian_dze)
+ *upper += (XK_Serbian_DJE - XK_Serbian_dje);
+ else if (sym >= XK_Cyrillic_YU && sym <= XK_Cyrillic_HARDSIGN)
+ *lower -= (XK_Cyrillic_YU - XK_Cyrillic_yu);
+ else if (sym >= XK_Cyrillic_yu && sym <= XK_Cyrillic_hardsign)
+ *upper += (XK_Cyrillic_YU - XK_Cyrillic_yu);
+ break;
+ case 7: /* Greek */
+ /* Assume the KeySym is a legal value (ignore discontinuities) */
+ if (sym >= XK_Greek_ALPHAaccent && sym <= XK_Greek_OMEGAaccent)
+ *lower += (XK_Greek_alphaaccent - XK_Greek_ALPHAaccent);
+ else if (sym >= XK_Greek_alphaaccent && sym <= XK_Greek_omegaaccent &&
+ sym != XK_Greek_iotaaccentdieresis &&
+ sym != XK_Greek_upsilonaccentdieresis)
+ *upper -= (XK_Greek_alphaaccent - XK_Greek_ALPHAaccent);
+ else if (sym >= XK_Greek_ALPHA && sym <= XK_Greek_OMEGA)
+ *lower += (XK_Greek_alpha - XK_Greek_ALPHA);
+ else if (sym >= XK_Greek_alpha && sym <= XK_Greek_omega &&
+ sym != XK_Greek_finalsmallsigma)
+ *upper -= (XK_Greek_alpha - XK_Greek_ALPHA);
+ break;
+ }
+}
+
+static KeySym KeyCodetoKeySym(KeySymsPtr keymap, int keycode, int col)
+{
+ register int per = keymap->mapWidth;
+ register KeySym *syms;
+ KeySym lsym, usym;
+
+ if ((col < 0) || ((col >= per) && (col > 3)) ||
+ (keycode < keymap->minKeyCode) || (keycode > keymap->maxKeyCode))
+ return NoSymbol;
+
+ syms = &keymap->map[(keycode - keymap->minKeyCode) * per];
+ if (col < 4) {
+ if (col > 1) {
+ while ((per > 2) && (syms[per - 1] == NoSymbol))
+ per--;
+ if (per < 3)
+ col -= 2;
+ }
+ if ((per <= (col|1)) || (syms[col|1] == NoSymbol)) {
+ XConvertCase(syms[col&~1], &lsym, &usym);
+ if (!(col & 1))
+ return lsym;
+ // I'm commenting out this logic because it's incorrect even though it
+ // was copied from the Xlib sources. The X protocol book quite clearly
+ // states that where a group consists of element 1 being a non-alphabetic
+ // keysym and element 2 being NoSymbol that you treat the second element
+ // as being the same as the first. This also tallies with the behaviour
+ // produced by the installed Xlib on my linux box (I believe this is
+ // because it uses some XKB code rather than the original Xlib code -
+ // compare XKBBind.c with KeyBind.c in lib/X11).
+ // else if (usym == lsym)
+ // return NoSymbol;
+ else
+ return usym;
+ }
+ }
+ return syms[col];
+}
+
+// KeysymToKeycode() - find the keycode and column corresponding to the given
+// keysym. The value of col passed in should be the column determined from the
+// current shift state. If the keysym can be found in that column we prefer
+// that to finding it in a different column (which would require fake events to
+// alter the shift state).
+
+static KeyCode KeysymToKeycode(KeySymsPtr keymap, KeySym ks, int* col)
+{
+ register int i, j;
+
+ j = *col;
+ for (i = keymap->minKeyCode; i <= keymap->maxKeyCode; i++) {
+ if (KeyCodetoKeySym(keymap, i, j) == ks)
+ return i;
+ }
+
+ for (j = 0; j < keymap->mapWidth; j++) {
+ for (i = keymap->minKeyCode; i <= keymap->maxKeyCode; i++) {
+ if (KeyCodetoKeySym(keymap, i, j) == ks) {
+ *col = j;
+ return i;
+ }
+ }
+ }
+ return 0;
+}
diff --git a/xc/programs/Xserver/vnc/XserverDesktop.h b/xc/programs/Xserver/vnc/XserverDesktop.h
new file mode 100644
index 00000000..c983ecee
--- /dev/null
+++ b/xc/programs/Xserver/vnc/XserverDesktop.h
@@ -0,0 +1,102 @@
+/* Copyright (C) 2002-2004 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.
+ */
+//
+// XserverDesktop.h
+//
+
+#ifndef __XSERVERDESKTOP_H__
+#define __XSERVERDESKTOP_H__
+
+#include <rfb/SDesktop.h>
+#include <rfb/PixelBuffer.h>
+#include <rfb/Configuration.h>
+#include <rdr/SubstitutingInStream.h>
+
+extern "C" {
+#define class c_class;
+#include <scrnintstr.h>
+#include <os.h>
+#undef class
+}
+
+namespace rfb {
+ class VNCServerST;
+}
+
+namespace network { class TcpListener; class Socket; }
+class MyHTTPServer;
+
+class XserverDesktop : public rfb::SDesktop, public rfb::FullFramePixelBuffer,
+ public rfb::ColourMap, public rdr::Substitutor {
+public:
+
+ XserverDesktop(ScreenPtr pScreen, network::TcpListener* listener,
+ network::TcpListener* httpListener_,
+ const char* name, void* fbptr);
+ virtual ~XserverDesktop();
+
+ // methods called from X server code
+ void serverReset(ScreenPtr pScreen);
+ void setColormap(ColormapPtr cmap);
+ void setColourMapEntries(ColormapPtr pColormap, int ndef, xColorItem* pdef);
+ void bell();
+ void serverCutText(const char* str, int len);
+ void setCursor(CursorPtr cursor);
+ void add_changed(RegionPtr reg);
+ void add_copied(RegionPtr dst, int dx, int dy);
+ void positionCursor();
+ void ignoreHooks(bool b) { ignoreHooks_ = b; }
+ void blockHandler(fd_set* fds);
+ void wakeupHandler(fd_set* fds, int nfds);
+ void addClient(network::Socket* sock, bool reverse);
+ void disconnectClients();
+
+ // rfb::SDesktop callbacks
+ virtual void pointerEvent(const rfb::Point& pos, rdr::U8 buttonMask);
+ virtual void keyEvent(rdr::U32 key, bool down);
+ virtual void clientCutText(const char* str, int len);
+ virtual rfb::Point getFbSize() { return rfb::Point(width(), height()); }
+
+ // rfb::PixelBuffer callbacks
+ virtual void grabRegion(const rfb::Region& r);
+
+ // rfb::ColourMap callbacks
+ virtual void lookup(int index, int* r, int* g, int* b);
+
+ // rdr::Substitutor callback
+ virtual char* substitute(const char* varName);
+
+private:
+ void setColourMapEntries(int firstColour, int nColours);
+ static CARD32 deferredUpdateTimerCallback(OsTimerPtr timer, CARD32 now,
+ pointer arg);
+ ScreenPtr pScreen;
+ OsTimerPtr deferredUpdateTimer, dummyTimer;
+ rfb::VNCServerST* server;
+ MyHTTPServer* httpServer;
+ network::TcpListener* listener;
+ network::TcpListener* httpListener;
+ ColormapPtr cmap;
+ bool deferredUpdateTimerSet;
+ bool grabbing;
+ bool ignoreHooks_;
+ bool directFbptr;
+ int oldButtonMask;
+ int cursorX, cursorY, oldCursorX, oldCursorY;
+};
+#endif
diff --git a/xc/programs/Xserver/vnc/Xvnc/Imakefile b/xc/programs/Xserver/vnc/Xvnc/Imakefile
new file mode 100644
index 00000000..d948887e
--- /dev/null
+++ b/xc/programs/Xserver/vnc/Xvnc/Imakefile
@@ -0,0 +1,74 @@
+
+ VNCTOP = $(TOP)/..
+ VNCLIBS = VncExtLibs
+ VNCINCLUDE = -I$(VNCTOP) -I$(VNCTOP)/vncconfig
+
+#define CplusplusSource
+
+#include <Server.tmpl>
+
+#ifdef XVendorString
+VENDORSTRING = XVendorString
+#else
+VENDORSTRING = "unknown"
+#endif
+
+#ifdef XVendorRelease
+VENDORRELEASE = XVendorRelease
+#else
+VENDORRELEASE = 0
+#endif
+
+ VENDOR_STRING = -DVENDOR_STRING=\"$(VENDORSTRING)\"
+ VENDOR_RELEASE = -DVENDOR_RELEASE="$(VENDORRELEASE)"
+
+#ifdef OS2Architecture
+SRCS1 = os2_stubs.c
+OBJS1 = os2_stubs.o
+#endif
+
+SRCSA = xvnc.cc stubs.c $(SRCS1) miinitext.c $(SRCS2)
+
+OBJSA = xvnc.o stubs.o $(OBJS1) miinitext.o $(OBJS2)
+
+INCLUDES = -I. -I.. -I$(XBUILDINCDIR) -I$(FONTINCSRC) \
+ -I../../cfb -I../../mfb -I../../mi -I../../include -I../../os \
+ -I$(EXTINCSRC) -I$(XINCLUDESRC) -I$(SERVERSRC)/render $(VNCINCLUDE)
+
+DEFINES = $(OS_DEFINES) $(SHMDEF) $(MMAPDEF) \
+ $(VENDOR_STRING) $(VENDOR_RELEASE) $(STD_DEFINES) ServerOSDefines \
+ -UXFree86LOADER
+
+#ifdef XFree86Version
+/*
+ * Make sure XINPUT, XF86VidTune, etc arent defined for the miinitext.o
+ * used by Xvnc
+ */
+EXT_DEFINES = ExtensionDefines -UXINPUT -UXF86VIDMODE -UXFreeXDGA -UXF86MISC
+#endif
+
+
+SRCS = $(SRCSA) $(SRCSB) $(SRCSC)
+OBJS = $(OBJSA) $(OBJSB) $(OBJSC)
+
+NormalLibraryObjectRule()
+NormalLibraryTarget(xvnc,$(OBJS) buildtime.o)
+
+#ifdef OS2Architecture
+LinkSourceFile(os2_stubs.c,../xfree86/os-support/os2)
+SpecialCObjectRule(os2_stubs,$(ICONFIGFILES),-DOS2NULLSELECT)
+#endif
+
+#ifdef HasGcc
+NO_OPERATOR_NAMES = -fno-operator-names
+#endif
+LinkSourceFile(stubs.c,../../Xi)
+SpecialCplusplusObjectRule(xvnc,$(ICONFIGFILES) xvnc,$(EXT_DEFINES) $(NO_OPERATOR_NAMES))
+
+LinkSourceFile(miinitext.c,$(SERVERSRC)/mi)
+SpecialCObjectRule(miinitext,$(ICONFIGFILES),$(EXT_DEFINES) $(PAN_DEFINES) -DNO_HW_ONLY_EXTS -DNO_MODULE_EXTS $(EXT_MODULE_DEFINES) -UXFree86LOADER)
+
+/* InstallManPage(Xvfb,$(MANDIR)) */
+DependTarget()
+
+buildtime.o: $(OBJS) ../LibraryTargetName(vnc) $(VNCLIBS)
diff --git a/xc/programs/Xserver/vnc/Xvnc/buildtime.c b/xc/programs/Xserver/vnc/Xvnc/buildtime.c
new file mode 100644
index 00000000..a96031cc
--- /dev/null
+++ b/xc/programs/Xserver/vnc/Xvnc/buildtime.c
@@ -0,0 +1,18 @@
+/* Copyright (C) 2002-2003 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.
+ */
+char buildtime[] = __DATE__ " " __TIME__;
diff --git a/xc/programs/Xserver/vnc/Xvnc/xvnc.cc b/xc/programs/Xserver/vnc/Xvnc/xvnc.cc
new file mode 100644
index 00000000..cf8fdf92
--- /dev/null
+++ b/xc/programs/Xserver/vnc/Xvnc/xvnc.cc
@@ -0,0 +1,1221 @@
+/* Copyright (C) 2002-2004 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.
+ */
+
+/*
+
+Copyright (c) 1993 X Consortium
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be included
+in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE X CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR
+OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+OTHER DEALINGS IN THE SOFTWARE.
+
+Except as contained in this notice, the name of the X Consortium shall
+not be used in advertising or otherwise to promote the sale, use or
+other dealings in this Software without prior written authorization
+from the X Consortium.
+
+*/
+
+#include <rfb/Configuration.h>
+#include <rfb/Logger_stdio.h>
+#include <rfb/LogWriter.h>
+#include <network/TcpSocket.h>
+#include "vncExtInit.h"
+
+extern "C" {
+#define class c_class
+#define public c_public
+#define xor c_xor
+#define and c_and
+#ifdef WIN32
+#include <X11/Xwinsock.h>
+#endif
+#include <stdio.h>
+#include "X11/X.h"
+#define NEED_EVENTS
+#include "X11/Xproto.h"
+#include "X11/Xos.h"
+#include "scrnintstr.h"
+#include "servermd.h"
+#define PSZ 8
+#include "cfb.h"
+#include "mi.h"
+#include "mibstore.h"
+#include "colormapst.h"
+#include "gcstruct.h"
+#include "input.h"
+#include "mipointer.h"
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <errno.h>
+#ifndef WIN32
+#include <sys/param.h>
+#endif
+#include <X11/XWDFile.h>
+#include "dix.h"
+#include "miline.h"
+#include "inputstr.h"
+#include "keysym.h"
+ extern int defaultColorVisualClass;
+ extern char buildtime[];
+#undef class
+#undef public
+#undef xor
+#undef and
+ extern Bool cfb16ScreenInit(ScreenPtr, pointer, int, int, int, int, int);
+ extern Bool cfb32ScreenInit(ScreenPtr, pointer, int, int, int, int, int);
+ extern Bool cfb16CreateGC(GCPtr);
+ extern Bool cfb32CreateGC(GCPtr);
+ extern void cfb16GetSpans(DrawablePtr, int, DDXPointPtr, int*, int, char*);
+ extern void cfb32GetSpans(DrawablePtr, int, DDXPointPtr, int*, int, char*);
+ extern void cfb16GetImage(DrawablePtr, int, int, int, int, unsigned int,
+ unsigned long, char*);
+ extern void cfb32GetImage(DrawablePtr, int, int, int, int, unsigned int,
+ unsigned long, char*);
+}
+
+#define XVNCVERSION "4.0"
+
+extern char *display;
+extern int monitorResolution;
+
+#define VFB_DEFAULT_WIDTH 1024
+#define VFB_DEFAULT_HEIGHT 768
+#define VFB_DEFAULT_DEPTH 16
+#define VFB_DEFAULT_WHITEPIXEL 0xffff
+#define VFB_DEFAULT_BLACKPIXEL 0
+#define VFB_DEFAULT_LINEBIAS 0
+#define XWD_WINDOW_NAME_LEN 60
+
+typedef struct
+{
+ int scrnum;
+ int width;
+ int paddedWidth;
+ int paddedWidthInBytes;
+ int height;
+ int depth;
+ int bitsPerPixel;
+ int sizeInBytes;
+ int ncolors;
+ char *pfbMemory;
+ XWDColor *pXWDCmap;
+ XWDFileHeader *pXWDHeader;
+ Pixel blackPixel;
+ Pixel whitePixel;
+ unsigned int lineBias;
+ Bool pixelFormatDefined;
+ Bool rgbNotBgr;
+ int redBits, greenBits, blueBits;
+
+} vfbScreenInfo, *vfbScreenInfoPtr;
+
+static int vfbNumScreens;
+static vfbScreenInfo vfbScreens[MAXSCREENS];
+static Bool vfbPixmapDepths[33];
+static char needswap = 0;
+static int lastScreen = -1;
+
+static bool displaySpecified = false;
+static bool wellKnownSocketsCreated = false;
+static char displayNumStr[16];
+
+#define swapcopy16(_dst, _src) \
+ if (needswap) { CARD16 _s = _src; cpswaps(_s, _dst); } \
+ else _dst = _src;
+
+#define swapcopy32(_dst, _src) \
+ if (needswap) { CARD32 _s = _src; cpswapl(_s, _dst); } \
+ else _dst = _src;
+
+
+static void vfbInitializePixmapDepths()
+{
+ int i;
+ vfbPixmapDepths[1] = TRUE; /* always need bitmaps */
+ for (i = 2; i <= 32; i++)
+ vfbPixmapDepths[i] = FALSE;
+}
+
+static void vfbInitializeDefaultScreens()
+{
+ int i;
+
+ for (i = 0; i < MAXSCREENS; i++)
+ {
+ vfbScreens[i].scrnum = i;
+ vfbScreens[i].width = VFB_DEFAULT_WIDTH;
+ vfbScreens[i].height = VFB_DEFAULT_HEIGHT;
+ vfbScreens[i].depth = VFB_DEFAULT_DEPTH;
+ vfbScreens[i].blackPixel = VFB_DEFAULT_BLACKPIXEL;
+ vfbScreens[i].whitePixel = VFB_DEFAULT_WHITEPIXEL;
+ vfbScreens[i].lineBias = VFB_DEFAULT_LINEBIAS;
+ vfbScreens[i].pixelFormatDefined = FALSE;
+ vfbScreens[i].pfbMemory = NULL;
+ }
+ vfbNumScreens = 1;
+}
+
+static int vfbBitsPerPixel(int depth)
+{
+ if (depth == 1) return 1;
+ else if (depth <= 8) return 8;
+ else if (depth <= 16) return 16;
+ else return 32;
+}
+
+extern "C" {
+ void ddxGiveUp()
+ {
+ int i;
+
+ /* clean up the framebuffers */
+
+ for (i = 0; i < vfbNumScreens; i++)
+ {
+ Xfree(vfbScreens[i].pXWDHeader);
+ }
+
+ // Remove any unix domain sockets left behind. I think these should
+ // already have been cleaned up but it doesn't hurt to try again.
+ if (wellKnownSocketsCreated) {
+ char sockName[64];
+ sprintf(sockName,"/tmp/.X11-unix/X%s",display);
+ unlink(sockName);
+ sprintf(sockName,"/usr/spool/sockets/X11/%s",display);
+ unlink(sockName);
+ }
+ }
+
+ void AbortDDX() { ddxGiveUp(); }
+ void OsVendorInit() {}
+ void OsVendorFatalError() {}
+
+ void ddxUseMsg()
+ {
+ ErrorF("\nXvnc version %s - built %s\n", XVNCVERSION, buildtime);
+ ErrorF("Underlying X server release %d, %s\n\n", VENDOR_RELEASE,
+ VENDOR_STRING);
+ ErrorF("-screen scrn WxHxD set screen's width, height, depth\n");
+ ErrorF("-pixdepths list-of-int support given pixmap depths\n");
+ ErrorF("-linebias n adjust thin line pixelization\n");
+ ErrorF("-blackpixel n pixel value for black\n");
+ ErrorF("-whitepixel n pixel value for white\n");
+ ErrorF("-geometry WxH set screen 0's width, height\n");
+ ErrorF("-depth D set screen 0's depth\n");
+ ErrorF("-pixelformat fmt set pixel format (rgbNNN or bgrNNN)\n");
+ ErrorF("-inetd has been launched from inetd\n");
+ ErrorF("\nVNC parameters:\n");
+
+ fprintf(stderr,"\n"
+ "Parameters can be turned on with -<param> or off with -<param>=0\n"
+ "Parameters which take a value can be specified as "
+ "-<param> <value>\n"
+ "Other valid forms are <param>=<value> -<param>=<value> "
+ "--<param>=<value>\n"
+ "Parameter names are case-insensitive. The parameters are:\n\n");
+ rfb::Configuration::listParams(79, 14);
+ }
+}
+
+static bool displayNumFree(int num)
+{
+ try {
+ network::TcpListener l(6000+num);
+ } catch (rdr::Exception& e) {
+ return false;
+ }
+ char file[256];
+ sprintf(file, "/tmp/.X%d-lock", num);
+ if (access(file, F_OK) == 0) return false;
+ sprintf(file, "/tmp/.X11-unix/X%d", num);
+ if (access(file, F_OK) == 0) return false;
+ sprintf(file, "/usr/spool/sockets/X11/%d", num);
+ if (access(file, F_OK) == 0) return false;
+ return true;
+}
+
+int ddxProcessArgument(int argc, char *argv[], int i)
+{
+ static Bool firstTime = TRUE;
+
+ if (firstTime)
+ {
+ vfbInitializeDefaultScreens();
+ vfbInitializePixmapDepths();
+ firstTime = FALSE;
+ rfb::initStdIOLoggers();
+ rfb::LogWriter::setLogParams("*:stderr:30");
+ }
+
+ if (argv[i][0] == ':')
+ displaySpecified = true;
+
+ if (strcmp (argv[i], "-screen") == 0) /* -screen n WxHxD */
+ {
+ int screenNum;
+ if (i + 2 >= argc) UseMsg();
+ screenNum = atoi(argv[i+1]);
+ if (screenNum < 0 || screenNum >= MAXSCREENS)
+ {
+ ErrorF("Invalid screen number %d\n", screenNum);
+ UseMsg();
+ }
+ if (3 != sscanf(argv[i+2], "%dx%dx%d",
+ &vfbScreens[screenNum].width,
+ &vfbScreens[screenNum].height,
+ &vfbScreens[screenNum].depth))
+ {
+ ErrorF("Invalid screen configuration %s\n", argv[i+2]);
+ UseMsg();
+ }
+
+ if (screenNum >= vfbNumScreens)
+ vfbNumScreens = screenNum + 1;
+ lastScreen = screenNum;
+ return 3;
+ }
+
+ if (strcmp (argv[i], "-pixdepths") == 0) /* -pixdepths list-of-depth */
+ {
+ int depth, ret = 1;
+
+ if (++i >= argc) UseMsg();
+ while ((i < argc) && (depth = atoi(argv[i++])) != 0)
+ {
+ if (depth < 0 || depth > 32)
+ {
+ ErrorF("Invalid pixmap depth %d\n", depth);
+ UseMsg();
+ }
+ vfbPixmapDepths[depth] = TRUE;
+ ret++;
+ }
+ return ret;
+ }
+
+ if (strcmp (argv[i], "-blackpixel") == 0) /* -blackpixel n */
+ {
+ Pixel pix;
+ if (++i >= argc) UseMsg();
+ pix = atoi(argv[i]);
+ if (-1 == lastScreen)
+ {
+ int i;
+ for (i = 0; i < MAXSCREENS; i++)
+ {
+ vfbScreens[i].blackPixel = pix;
+ }
+ }
+ else
+ {
+ vfbScreens[lastScreen].blackPixel = pix;
+ }
+ return 2;
+ }
+
+ if (strcmp (argv[i], "-whitepixel") == 0) /* -whitepixel n */
+ {
+ Pixel pix;
+ if (++i >= argc) UseMsg();
+ pix = atoi(argv[i]);
+ if (-1 == lastScreen)
+ {
+ int i;
+ for (i = 0; i < MAXSCREENS; i++)
+ {
+ vfbScreens[i].whitePixel = pix;
+ }
+ }
+ else
+ {
+ vfbScreens[lastScreen].whitePixel = pix;
+ }
+ return 2;
+ }
+
+ if (strcmp (argv[i], "-linebias") == 0) /* -linebias n */
+ {
+ unsigned int linebias;
+ if (++i >= argc) UseMsg();
+ linebias = atoi(argv[i]);
+ if (-1 == lastScreen)
+ {
+ int i;
+ for (i = 0; i < MAXSCREENS; i++)
+ {
+ vfbScreens[i].lineBias = linebias;
+ }
+ }
+ else
+ {
+ vfbScreens[lastScreen].lineBias = linebias;
+ }
+ return 2;
+ }
+
+ if (strcmp(argv[i], "-geometry") == 0)
+ {
+ if (++i >= argc) UseMsg();
+ if (sscanf(argv[i],"%dx%d",&vfbScreens[0].width,
+ &vfbScreens[0].height) != 2) {
+ ErrorF("Invalid geometry %s\n", argv[i]);
+ UseMsg();
+ }
+ return 2;
+ }
+
+ if (strcmp(argv[i], "-depth") == 0)
+ {
+ if (++i >= argc) UseMsg();
+ vfbScreens[0].depth = atoi(argv[i]);
+ return 2;
+ }
+
+ if (strcmp(argv[i], "-pixelformat") == 0)
+ {
+ char rgbbgr[4];
+ int bits1, bits2, bits3;
+ if (++i >= argc) UseMsg();
+ if (sscanf(argv[i], "%3s%1d%1d%1d", rgbbgr,&bits1,&bits2,&bits3) < 4) {
+ ErrorF("Invalid pixel format %s\n", argv[i]);
+ UseMsg();
+ }
+
+#define SET_PIXEL_FORMAT(vfbScreen) \
+ (vfbScreen).pixelFormatDefined = TRUE; \
+ (vfbScreen).depth = bits1 + bits2 + bits3; \
+ (vfbScreen).greenBits = bits2; \
+ if (strcasecmp(rgbbgr, "bgr") == 0) { \
+ (vfbScreen).rgbNotBgr = FALSE; \
+ (vfbScreen).redBits = bits3; \
+ (vfbScreen).blueBits = bits1; \
+ } else if (strcasecmp(rgbbgr, "rgb") == 0) { \
+ (vfbScreen).rgbNotBgr = TRUE; \
+ (vfbScreen).redBits = bits1; \
+ (vfbScreen).blueBits = bits3; \
+ } else { \
+ ErrorF("Invalid pixel format %s\n", argv[i]); \
+ UseMsg(); \
+ }
+
+ if (-1 == lastScreen)
+ {
+ int i;
+ for (i = 0; i < MAXSCREENS; i++)
+ {
+ SET_PIXEL_FORMAT(vfbScreens[i]);
+ }
+ }
+ else
+ {
+ SET_PIXEL_FORMAT(vfbScreens[lastScreen]);
+ }
+
+ return 2;
+ }
+
+ if (strcmp(argv[i], "-inetd") == 0)
+ {
+ dup2(0,3);
+ vncInetdSock = 3;
+ close(2);
+
+ if (!displaySpecified) {
+ int port = network::TcpSocket::getSockPort(vncInetdSock);
+ int displayNum = port - 5900;
+ if (displayNum < 0 || displayNum > 99 || !displayNumFree(displayNum)) {
+ for (displayNum = 1; displayNum < 100; displayNum++)
+ if (displayNumFree(displayNum)) break;
+
+ if (displayNum == 100)
+ FatalError("Xvnc error: no free display number for -inetd");
+ }
+
+ display = displayNumStr;
+ sprintf(displayNumStr, "%d", displayNum);
+ }
+
+ return 1;
+ }
+
+ if (rfb::Configuration::setParam(argv[i]))
+ return 1;
+
+ if (argv[i][0] == '-' && i+1 < argc) {
+ if (rfb::Configuration::setParam(&argv[i][1], argv[i+1]))
+ return 2;
+ }
+
+ return 0;
+}
+
+#ifdef DDXTIME /* from ServerOSDefines */
+CARD32 GetTimeInMillis()
+{
+ struct timeval tp;
+
+ X_GETTIMEOFDAY(&tp);
+ return(tp.tv_sec * 1000) + (tp.tv_usec / 1000);
+}
+#endif
+
+
+static Bool vfbMultiDepthCreateGC(GCPtr pGC)
+{
+ switch (vfbBitsPerPixel(pGC->depth))
+ {
+ case 1: return mfbCreateGC (pGC);
+ case 8: return cfbCreateGC (pGC);
+ case 16: return cfb16CreateGC (pGC);
+ case 32: return cfb32CreateGC (pGC);
+ default: return FALSE;
+ }
+}
+
+static void vfbMultiDepthGetSpans(
+ DrawablePtr pDrawable, /* drawable from which to get bits */
+ int wMax, /* largest value of all *pwidths */
+ register DDXPointPtr ppt, /* points to start copying from */
+ int *pwidth, /* list of number of bits to copy */
+ int nspans, /* number of scanlines to copy */
+ char *pdstStart) /* where to put the bits */
+{
+ switch (pDrawable->bitsPerPixel) {
+ case 1:
+ mfbGetSpans(pDrawable, wMax, ppt, pwidth, nspans, pdstStart);
+ break;
+ case 8:
+ cfbGetSpans(pDrawable, wMax, ppt, pwidth, nspans, pdstStart);
+ break;
+ case 16:
+ cfb16GetSpans(pDrawable, wMax, ppt, pwidth, nspans, pdstStart);
+ break;
+ case 32:
+ cfb32GetSpans(pDrawable, wMax, ppt, pwidth, nspans, pdstStart);
+ break;
+ }
+ return;
+}
+
+static void
+vfbMultiDepthGetImage(DrawablePtr pDrawable, int sx, int sy, int w, int h,
+ unsigned int format, unsigned long planeMask,
+ char *pdstLine)
+{
+ switch (pDrawable->bitsPerPixel)
+ {
+ case 1:
+ mfbGetImage(pDrawable, sx, sy, w, h, format, planeMask, pdstLine);
+ break;
+ case 8:
+ cfbGetImage(pDrawable, sx, sy, w, h, format, planeMask, pdstLine);
+ break;
+ case 16:
+ cfb16GetImage(pDrawable, sx, sy, w, h, format, planeMask, pdstLine);
+ break;
+ case 32:
+ cfb32GetImage(pDrawable, sx, sy, w, h, format, planeMask, pdstLine);
+ break;
+ }
+}
+
+static ColormapPtr InstalledMaps[MAXSCREENS];
+
+static int vfbListInstalledColormaps(ScreenPtr pScreen, Colormap *pmaps)
+{
+ /* By the time we are processing requests, we can guarantee that there
+ * is always a colormap installed */
+ *pmaps = InstalledMaps[pScreen->myNum]->mid;
+ return (1);
+}
+
+
+static void vfbInstallColormap(ColormapPtr pmap)
+{
+ int index = pmap->pScreen->myNum;
+ ColormapPtr oldpmap = InstalledMaps[index];
+
+ if (pmap != oldpmap)
+ {
+ int entries;
+ XWDFileHeader *pXWDHeader;
+ XWDColor *pXWDCmap;
+ VisualPtr pVisual;
+ Pixel * ppix;
+ xrgb * prgb;
+ xColorItem *defs;
+ int i;
+
+ if(oldpmap != (ColormapPtr)None)
+ WalkTree(pmap->pScreen, TellLostMap, (char *)&oldpmap->mid);
+ /* Install pmap */
+ InstalledMaps[index] = pmap;
+ WalkTree(pmap->pScreen, TellGainedMap, (char *)&pmap->mid);
+
+ entries = pmap->pVisual->ColormapEntries;
+ pXWDHeader = vfbScreens[pmap->pScreen->myNum].pXWDHeader;
+ pXWDCmap = vfbScreens[pmap->pScreen->myNum].pXWDCmap;
+ pVisual = pmap->pVisual;
+
+ swapcopy32(pXWDHeader->visual_class, pVisual->c_class);
+ swapcopy32(pXWDHeader->red_mask, pVisual->redMask);
+ swapcopy32(pXWDHeader->green_mask, pVisual->greenMask);
+ swapcopy32(pXWDHeader->blue_mask, pVisual->blueMask);
+ swapcopy32(pXWDHeader->bits_per_rgb, pVisual->bitsPerRGBValue);
+ swapcopy32(pXWDHeader->colormap_entries, pVisual->ColormapEntries);
+
+ ppix = (Pixel *)ALLOCATE_LOCAL(entries * sizeof(Pixel));
+ prgb = (xrgb *)ALLOCATE_LOCAL(entries * sizeof(xrgb));
+ defs = (xColorItem *)ALLOCATE_LOCAL(entries * sizeof(xColorItem));
+
+ for (i = 0; i < entries; i++) ppix[i] = i;
+ /* XXX truecolor */
+ QueryColors(pmap, entries, ppix, prgb);
+
+ for (i = 0; i < entries; i++) { /* convert xrgbs to xColorItems */
+ defs[i].pixel = ppix[i] & 0xff; /* change pixel to index */
+ defs[i].red = prgb[i].red;
+ defs[i].green = prgb[i].green;
+ defs[i].blue = prgb[i].blue;
+ defs[i].flags = DoRed|DoGreen|DoBlue;
+ }
+ (*pmap->pScreen->StoreColors)(pmap, entries, defs);
+
+ DEALLOCATE_LOCAL(ppix);
+ DEALLOCATE_LOCAL(prgb);
+ DEALLOCATE_LOCAL(defs);
+ }
+}
+
+static void vfbUninstallColormap(ColormapPtr pmap)
+{
+ ColormapPtr curpmap = InstalledMaps[pmap->pScreen->myNum];
+
+ if(pmap == curpmap)
+ {
+ if (pmap->mid != pmap->pScreen->defColormap)
+ {
+ curpmap = (ColormapPtr) LookupIDByType(pmap->pScreen->defColormap,
+ RT_COLORMAP);
+ (*pmap->pScreen->InstallColormap)(curpmap);
+ }
+ }
+}
+
+static void vfbStoreColors(ColormapPtr pmap, int ndef, xColorItem *pdefs)
+{
+ XWDColor *pXWDCmap;
+ int i;
+
+ if (pmap != InstalledMaps[pmap->pScreen->myNum]) return;
+
+ pXWDCmap = vfbScreens[pmap->pScreen->myNum].pXWDCmap;
+
+ if ((pmap->pVisual->c_class | DynamicClass) == DirectColor)
+ return;
+
+ for (i = 0; i < ndef; i++)
+ {
+ if (pdefs[i].flags & DoRed) {
+ swapcopy16(pXWDCmap[pdefs[i].pixel].red, pdefs[i].red);
+ }
+ if (pdefs[i].flags & DoGreen) {
+ swapcopy16(pXWDCmap[pdefs[i].pixel].green, pdefs[i].green);
+ }
+ if (pdefs[i].flags & DoBlue) {
+ swapcopy16(pXWDCmap[pdefs[i].pixel].blue, pdefs[i].blue);
+ }
+ }
+}
+
+static Bool vfbSaveScreen(ScreenPtr pScreen, int on)
+{
+ return TRUE;
+}
+
+static char* vfbAllocateFramebufferMemory(vfbScreenInfoPtr pvfb)
+{
+ if (pvfb->pfbMemory) return pvfb->pfbMemory; /* already done */
+
+ pvfb->sizeInBytes = pvfb->paddedWidthInBytes * pvfb->height;
+
+ /* Calculate how many entries in colormap. This is rather bogus, because
+ * the visuals haven't even been set up yet, but we need to know because we
+ * have to allocate space in the file for the colormap. The number 10
+ * below comes from the MAX_PSEUDO_DEPTH define in cfbcmap.c.
+ */
+
+ if (pvfb->depth <= 10)
+ { /* single index colormaps */
+ pvfb->ncolors = 1 << pvfb->depth;
+ }
+ else
+ { /* decomposed colormaps */
+ int nplanes_per_color_component = pvfb->depth / 3;
+ if (pvfb->depth % 3) nplanes_per_color_component++;
+ pvfb->ncolors = 1 << nplanes_per_color_component;
+ }
+
+ /* add extra bytes for XWDFileHeader, window name, and colormap */
+
+ pvfb->sizeInBytes += SIZEOF(XWDheader) + XWD_WINDOW_NAME_LEN +
+ pvfb->ncolors * SIZEOF(XWDColor);
+
+ pvfb->pXWDHeader = NULL;
+ pvfb->pXWDHeader = (XWDFileHeader *)Xalloc(pvfb->sizeInBytes);
+
+ if (pvfb->pXWDHeader)
+ {
+ pvfb->pXWDCmap = (XWDColor *)((char *)pvfb->pXWDHeader
+ + SIZEOF(XWDheader) + XWD_WINDOW_NAME_LEN);
+ pvfb->pfbMemory = (char *)(pvfb->pXWDCmap + pvfb->ncolors);
+ memset(pvfb->pfbMemory, 0, pvfb->paddedWidthInBytes * pvfb->height);
+ return pvfb->pfbMemory;
+ }
+ else
+ return NULL;
+}
+
+
+static void vfbWriteXWDFileHeader(ScreenPtr pScreen)
+{
+ vfbScreenInfoPtr pvfb = &vfbScreens[pScreen->myNum];
+ XWDFileHeader *pXWDHeader = pvfb->pXWDHeader;
+ char hostname[XWD_WINDOW_NAME_LEN];
+ VisualPtr pVisual;
+ unsigned long swaptest = 1;
+ int i;
+
+ needswap = *(char *) &swaptest;
+
+ pXWDHeader->header_size = (char *)pvfb->pXWDCmap - (char *)pvfb->pXWDHeader;
+ pXWDHeader->file_version = XWD_FILE_VERSION;
+
+ pXWDHeader->pixmap_format = ZPixmap;
+ pXWDHeader->pixmap_depth = pvfb->depth;
+ pXWDHeader->pixmap_height = pXWDHeader->window_height = pvfb->height;
+ pXWDHeader->xoffset = 0;
+ pXWDHeader->byte_order = IMAGE_BYTE_ORDER;
+ pXWDHeader->bitmap_bit_order = BITMAP_BIT_ORDER;
+#ifndef INTERNAL_VS_EXTERNAL_PADDING
+ pXWDHeader->pixmap_width = pXWDHeader->window_width = pvfb->width;
+ pXWDHeader->bitmap_unit = BITMAP_SCANLINE_UNIT;
+ pXWDHeader->bitmap_pad = BITMAP_SCANLINE_PAD;
+#else
+ pXWDHeader->pixmap_width = pXWDHeader->window_width = pvfb->paddedWidth;
+ pXWDHeader->bitmap_unit = BITMAP_SCANLINE_UNIT_PROTO;
+ pXWDHeader->bitmap_pad = BITMAP_SCANLINE_PAD_PROTO;
+#endif
+ pXWDHeader->bits_per_pixel = pvfb->bitsPerPixel;
+ pXWDHeader->bytes_per_line = pvfb->paddedWidthInBytes;
+ pXWDHeader->ncolors = pvfb->ncolors;
+
+ /* visual related fields are written when colormap is installed */
+
+ pXWDHeader->window_x = pXWDHeader->window_y = 0;
+ pXWDHeader->window_bdrwidth = 0;
+
+ /* write xwd "window" name: Xvfb hostname:server.screen */
+
+ hostname[0] = 0;
+ sprintf((char *)(pXWDHeader+1), "Xvfb %s:%s.%d", hostname, display,
+ pScreen->myNum);
+
+ /* write colormap pixel slot values */
+
+ for (i = 0; i < pvfb->ncolors; i++)
+ {
+ pvfb->pXWDCmap[i].pixel = i;
+ }
+
+ /* byte swap to most significant byte first */
+
+ if (needswap)
+ {
+ SwapLongs((CARD32 *)pXWDHeader, SIZEOF(XWDheader)/4);
+ for (i = 0; i < pvfb->ncolors; i++)
+ {
+ register char n;
+ swapl(&pvfb->pXWDCmap[i].pixel, n);
+ }
+ }
+}
+
+
+static Bool vfbCursorOffScreen(ScreenPtr *ppScreen, int *x, int *y) {
+ return FALSE;
+}
+static void vfbCrossScreen (ScreenPtr pScreen, Bool entering) {}
+static Bool vfbRealizeCursor(ScreenPtr pScreen, CursorPtr pCursor) {
+ return TRUE;
+}
+static Bool vfbUnrealizeCursor(ScreenPtr pScreen, CursorPtr pCursor) {
+ return TRUE;
+}
+static void vfbSetCursor(ScreenPtr pScreen, CursorPtr pCursor,
+ int x, int y) {}
+static void vfbMoveCursor(ScreenPtr pScreen, int x, int y) {}
+
+static miPointerSpriteFuncRec vfbPointerSpriteFuncs = {
+ vfbRealizeCursor,
+ vfbUnrealizeCursor,
+ vfbSetCursor,
+ vfbMoveCursor
+};
+
+static miPointerScreenFuncRec vfbPointerScreenFuncs = {
+ vfbCursorOffScreen,
+ vfbCrossScreen,
+ miPointerWarpCursor
+};
+
+static Bool vfbScreenInit(int index, ScreenPtr pScreen, int argc, char** argv)
+{
+ vfbScreenInfoPtr pvfb = &vfbScreens[index];
+ int dpi = 100;
+ int ret;
+ char *pbits;
+
+ if (monitorResolution) dpi = monitorResolution;
+
+ pvfb->paddedWidthInBytes = PixmapBytePad(pvfb->width, pvfb->depth);
+ pvfb->bitsPerPixel = vfbBitsPerPixel(pvfb->depth);
+ pvfb->paddedWidth = pvfb->paddedWidthInBytes * 8 / pvfb->bitsPerPixel;
+ pbits = vfbAllocateFramebufferMemory(pvfb);
+ if (!pbits) return FALSE;
+ vncFbptr[index] = pbits;
+
+ defaultColorVisualClass
+ = (pvfb->bitsPerPixel > 8) ? TrueColor : PseudoColor;
+
+ switch (pvfb->bitsPerPixel)
+ {
+ case 1:
+ ret = mfbScreenInit(pScreen, pbits, pvfb->width, pvfb->height,
+ dpi, dpi, pvfb->paddedWidth);
+ break;
+ case 8:
+ ret = cfbScreenInit(pScreen, pbits, pvfb->width, pvfb->height,
+ dpi, dpi, pvfb->paddedWidth);
+ break;
+ case 16:
+ ret = cfb16ScreenInit(pScreen, pbits, pvfb->width, pvfb->height,
+ dpi, dpi, pvfb->paddedWidth);
+ break;
+ case 32:
+ ret = cfb32ScreenInit(pScreen, pbits, pvfb->width, pvfb->height,
+ dpi, dpi, pvfb->paddedWidth);
+ break;
+ default:
+ return FALSE;
+ }
+
+ if (!ret) return FALSE;
+
+ pScreen->CreateGC = vfbMultiDepthCreateGC;
+ pScreen->GetImage = vfbMultiDepthGetImage;
+ pScreen->GetSpans = vfbMultiDepthGetSpans;
+
+ pScreen->InstallColormap = vfbInstallColormap;
+ pScreen->UninstallColormap = vfbUninstallColormap;
+ pScreen->ListInstalledColormaps = vfbListInstalledColormaps;
+
+ pScreen->SaveScreen = vfbSaveScreen;
+ pScreen->StoreColors = vfbStoreColors;
+
+ miPointerInitialize(pScreen, &vfbPointerSpriteFuncs, &vfbPointerScreenFuncs,
+ FALSE);
+
+ vfbWriteXWDFileHeader(pScreen);
+
+ pScreen->blackPixel = pvfb->blackPixel;
+ pScreen->whitePixel = pvfb->whitePixel;
+
+ if (!pvfb->pixelFormatDefined && pvfb->depth == 16) {
+ pvfb->pixelFormatDefined = TRUE;
+ pvfb->rgbNotBgr = TRUE;
+ pvfb->blueBits = pvfb->redBits = 5;
+ pvfb->greenBits = 6;
+ }
+
+ if (pvfb->pixelFormatDefined) {
+ VisualPtr vis;
+ for (vis = pScreen->visuals; vis->vid != pScreen->rootVisual; vis++)
+ ;
+
+ if (pvfb->rgbNotBgr) {
+ vis->offsetBlue = 0;
+ vis->blueMask = (1 << pvfb->blueBits) - 1;
+ vis->offsetGreen = pvfb->blueBits;
+ vis->greenMask = ((1 << pvfb->greenBits) - 1) << vis->offsetGreen;
+ vis->offsetRed = vis->offsetGreen + pvfb->greenBits;
+ vis->redMask = ((1 << pvfb->redBits) - 1) << vis->offsetRed;
+ } else {
+ vis->offsetRed = 0;
+ vis->redMask = (1 << pvfb->redBits) - 1;
+ vis->offsetGreen = pvfb->redBits;
+ vis->greenMask = ((1 << pvfb->greenBits) - 1) << vis->offsetGreen;
+ vis->offsetBlue = vis->offsetGreen + pvfb->greenBits;
+ vis->blueMask = ((1 << pvfb->blueBits) - 1) << vis->offsetBlue;
+ }
+ }
+
+ if (pvfb->bitsPerPixel == 1)
+ {
+ ret = mfbCreateDefColormap(pScreen);
+ }
+ else
+ {
+ ret = cfbCreateDefColormap(pScreen);
+ }
+
+ miSetZeroLineBias(pScreen, pvfb->lineBias);
+
+ return ret;
+
+} /* end vfbScreenInit */
+
+
+static void vfbClientStateChange(CallbackListPtr*, pointer, pointer) {
+ dispatchException &= ~DE_RESET;
+}
+
+void InitOutput(ScreenInfo *screenInfo, int argc, char **argv)
+{
+ ErrorF("\nXvnc version %s - built %s\n", XVNCVERSION, buildtime);
+ ErrorF("Underlying X server release %d, %s\n\n", VENDOR_RELEASE,
+ VENDOR_STRING);
+ wellKnownSocketsCreated = true;
+
+ int i;
+ int NumFormats = 0;
+
+ /* initialize pixmap formats */
+
+ /* must have a pixmap depth to match every screen depth */
+ for (i = 0; i < vfbNumScreens; i++)
+ {
+ vfbPixmapDepths[vfbScreens[i].depth] = TRUE;
+ }
+
+ for (i = 1; i <= 32; i++)
+ {
+ if (vfbPixmapDepths[i])
+ {
+ if (NumFormats >= MAXFORMATS)
+ FatalError ("MAXFORMATS is too small for this server\n");
+ screenInfo->formats[NumFormats].depth = i;
+ screenInfo->formats[NumFormats].bitsPerPixel = vfbBitsPerPixel(i);
+ screenInfo->formats[NumFormats].scanlinePad = BITMAP_SCANLINE_PAD;
+ NumFormats++;
+ }
+ }
+
+ screenInfo->imageByteOrder = IMAGE_BYTE_ORDER;
+ screenInfo->bitmapScanlineUnit = BITMAP_SCANLINE_UNIT;
+ screenInfo->bitmapScanlinePad = BITMAP_SCANLINE_PAD;
+ screenInfo->bitmapBitOrder = BITMAP_BIT_ORDER;
+ screenInfo->numPixmapFormats = NumFormats;
+
+ /* initialize screens */
+
+ for (i = 0; i < vfbNumScreens; i++)
+ {
+ if (-1 == AddScreen(vfbScreenInit, argc, argv))
+ {
+ FatalError("Couldn't add screen %d", i);
+ }
+ }
+
+ if (!AddCallback(&ClientStateCallback, vfbClientStateChange, 0)) {
+ FatalError("AddCallback failed\n");
+ }
+
+} /* end InitOutput */
+
+#ifdef DPMSExtension
+extern "C" {
+#if NeedFunctionPrototypes
+ void DPMSSet(CARD16 level)
+#else
+ void DPMSSet(level)
+ CARD16 level;
+#endif
+ {
+ return;
+ }
+
+ Bool DPMSSupported()
+ {
+ return FALSE;
+ }
+}
+#endif
+
+/* this is just to get the server to link on AIX */
+#ifdef AIXV3
+int SelectWaitTime = 10000; /* usec */
+#endif
+
+Bool LegalModifier(unsigned int key, DevicePtr pDev)
+{
+ return TRUE;
+}
+
+void ProcessInputEvents()
+{
+ mieqProcessInputEvents();
+ miPointerUpdate();
+}
+
+/* Fairly standard US PC Keyboard */
+
+#define VFB_MIN_KEY 8
+#define VFB_MAX_KEY 255
+#define VFB_MAP_LEN (VFB_MAX_KEY - VFB_MIN_KEY + 1)
+#define KEYSYMS_PER_KEY 2
+KeySym keyboardMap[VFB_MAP_LEN * KEYSYMS_PER_KEY] = {
+ NoSymbol, NoSymbol,
+ XK_Escape, NoSymbol,
+ XK_1, XK_exclam,
+ XK_2, XK_at,
+ XK_3, XK_numbersign,
+ XK_4, XK_dollar,
+ XK_5, XK_percent,
+ XK_6, XK_asciicircum,
+ XK_7, XK_ampersand,
+ XK_8, XK_asterisk,
+ XK_9, XK_parenleft,
+ XK_0, XK_parenright,
+ XK_minus, XK_underscore,
+ XK_equal, XK_plus,
+ XK_BackSpace, NoSymbol,
+ XK_Tab, NoSymbol,
+ XK_q, XK_Q,
+ XK_w, XK_W,
+ XK_e, XK_E,
+ XK_r, XK_R,
+ XK_t, XK_T,
+ XK_y, XK_Y,
+ XK_u, XK_U,
+ XK_i, XK_I,
+ XK_o, XK_O,
+ XK_p, XK_P,
+ XK_bracketleft, XK_braceleft,
+ XK_bracketright, XK_braceright,
+ XK_Return, NoSymbol,
+ XK_Control_L, NoSymbol,
+ XK_a, XK_A,
+ XK_s, XK_S,
+ XK_d, XK_D,
+ XK_f, XK_F,
+ XK_g, XK_G,
+ XK_h, XK_H,
+ XK_j, XK_J,
+ XK_k, XK_K,
+ XK_l, XK_L,
+ XK_semicolon, XK_colon,
+ XK_apostrophe, XK_quotedbl,
+ XK_grave, XK_asciitilde,
+ XK_Shift_L, NoSymbol,
+ XK_backslash, XK_bar,
+ XK_z, XK_Z,
+ XK_x, XK_X,
+ XK_c, XK_C,
+ XK_v, XK_V,
+ XK_b, XK_B,
+ XK_n, XK_N,
+ XK_m, XK_M,
+ XK_comma, XK_less,
+ XK_period, XK_greater,
+ XK_slash, XK_question,
+ XK_Shift_R, NoSymbol,
+ XK_KP_Multiply, NoSymbol,
+ XK_Alt_L, XK_Meta_L,
+ XK_space, NoSymbol,
+ /*XK_Caps_Lock*/ NoSymbol, NoSymbol,
+ XK_F1, NoSymbol,
+ XK_F2, NoSymbol,
+ XK_F3, NoSymbol,
+ XK_F4, NoSymbol,
+ XK_F5, NoSymbol,
+ XK_F6, NoSymbol,
+ XK_F7, NoSymbol,
+ XK_F8, NoSymbol,
+ XK_F9, NoSymbol,
+ XK_F10, NoSymbol,
+ XK_Num_Lock, XK_Pointer_EnableKeys,
+ XK_Scroll_Lock, NoSymbol,
+ XK_KP_Home, XK_KP_7,
+ XK_KP_Up, XK_KP_8,
+ XK_KP_Prior, XK_KP_9,
+ XK_KP_Subtract, NoSymbol,
+ XK_KP_Left, XK_KP_4,
+ XK_KP_Begin, XK_KP_5,
+ XK_KP_Right, XK_KP_6,
+ XK_KP_Add, NoSymbol,
+ XK_KP_End, XK_KP_1,
+ XK_KP_Down, XK_KP_2,
+ XK_KP_Next, XK_KP_3,
+ XK_KP_Insert, XK_KP_0,
+ XK_KP_Delete, XK_KP_Decimal,
+ NoSymbol, NoSymbol,
+ NoSymbol, NoSymbol,
+ NoSymbol, NoSymbol,
+ XK_F11, NoSymbol,
+ XK_F12, NoSymbol,
+ XK_Home, NoSymbol,
+ XK_Up, NoSymbol,
+ XK_Prior, NoSymbol,
+ XK_Left, NoSymbol,
+ NoSymbol, NoSymbol,
+ XK_Right, NoSymbol,
+ XK_End, NoSymbol,
+ XK_Down, NoSymbol,
+ XK_Next, NoSymbol,
+ XK_Insert, NoSymbol,
+ XK_Delete, NoSymbol,
+ XK_KP_Enter, NoSymbol,
+ XK_Control_R, NoSymbol,
+ XK_Pause, XK_Break,
+ XK_Print, XK_Execute,
+ XK_KP_Divide, NoSymbol,
+ XK_Alt_R, XK_Meta_R,
+};
+
+static Bool GetMappings(KeySymsPtr pKeySyms, CARD8 *pModMap)
+{
+ int i;
+
+ for (i = 0; i < MAP_LENGTH; i++)
+ pModMap[i] = NoSymbol;
+
+ for (i = 0; i < VFB_MAP_LEN; i++) {
+ if (keyboardMap[i * KEYSYMS_PER_KEY] == XK_Caps_Lock)
+ pModMap[i + VFB_MIN_KEY] = LockMask;
+ else if (keyboardMap[i * KEYSYMS_PER_KEY] == XK_Shift_L ||
+ keyboardMap[i * KEYSYMS_PER_KEY] == XK_Shift_R)
+ pModMap[i + VFB_MIN_KEY] = ShiftMask;
+ else if (keyboardMap[i * KEYSYMS_PER_KEY] == XK_Control_L ||
+ keyboardMap[i * KEYSYMS_PER_KEY] == XK_Control_R) {
+ pModMap[i + VFB_MIN_KEY] = ControlMask;
+ }
+ else if (keyboardMap[i * KEYSYMS_PER_KEY] == XK_Alt_L ||
+ keyboardMap[i * KEYSYMS_PER_KEY] == XK_Alt_R)
+ pModMap[i + VFB_MIN_KEY] = Mod1Mask;
+ }
+
+ pKeySyms->minKeyCode = VFB_MIN_KEY;
+ pKeySyms->maxKeyCode = VFB_MAX_KEY;
+ pKeySyms->mapWidth = KEYSYMS_PER_KEY;
+ pKeySyms->map = keyboardMap;
+
+ return TRUE;
+}
+
+static void vfbBell(int percent, DeviceIntPtr device, pointer ctrl, int class_)
+{
+ if (percent > 0)
+ vncBell();
+}
+
+static int vfbKeybdProc(DeviceIntPtr pDevice, int onoff)
+{
+ KeySymsRec keySyms;
+ CARD8 modMap[MAP_LENGTH];
+ DevicePtr pDev = (DevicePtr)pDevice;
+
+ switch (onoff)
+ {
+ case DEVICE_INIT:
+ GetMappings(&keySyms, modMap);
+ InitKeyboardDeviceStruct(pDev, &keySyms, modMap,
+ (BellProcPtr)vfbBell, (KbdCtrlProcPtr)NoopDDA);
+ break;
+ case DEVICE_ON:
+ pDev->on = TRUE;
+ break;
+ case DEVICE_OFF:
+ pDev->on = FALSE;
+ break;
+ case DEVICE_CLOSE:
+ break;
+ }
+ return Success;
+}
+
+static int vfbMouseProc(DeviceIntPtr pDevice, int onoff)
+{
+ BYTE map[6];
+ DevicePtr pDev = (DevicePtr)pDevice;
+
+ switch (onoff)
+ {
+ case DEVICE_INIT:
+ map[1] = 1;
+ map[2] = 2;
+ map[3] = 3;
+ map[4] = 4;
+ map[5] = 5;
+ InitPointerDeviceStruct(pDev, map, 5, miPointerGetMotionEvents,
+ (PtrCtrlProcPtr)NoopDDA, miPointerGetMotionBufferSize());
+ break;
+
+ case DEVICE_ON:
+ pDev->on = TRUE;
+ break;
+
+ case DEVICE_OFF:
+ pDev->on = FALSE;
+ break;
+
+ case DEVICE_CLOSE:
+ break;
+ }
+ return Success;
+}
+
+// InitInput is called after InitExtensions, so we're guaranteed that
+// vncExtensionInit() has already been called.
+
+void InitInput(int argc, char *argv[])
+{
+ DeviceIntPtr p, k;
+ p = AddInputDevice(vfbMouseProc, TRUE);
+ k = AddInputDevice(vfbKeybdProc, TRUE);
+ RegisterPointerDevice(p);
+ RegisterKeyboardDevice(k);
+ miRegisterPointerDevice(screenInfo.screens[0], p);
+ (void)mieqInit ((DevicePtr)k, (DevicePtr)p);
+}
diff --git a/xc/programs/Xserver/vnc/module/Imakefile b/xc/programs/Xserver/vnc/module/Imakefile
new file mode 100644
index 00000000..46e548f3
--- /dev/null
+++ b/xc/programs/Xserver/vnc/module/Imakefile
@@ -0,0 +1,54 @@
+
+ VNCTOP = $(TOP)/..
+ VNCLIBS = VncExtLibs
+ VNCINCLUDE = -I$(VNCTOP) -I$(VNCTOP)/vncconfig
+
+#define CplusplusSource
+
+#define IHaveModules
+#include <Server.tmpl>
+
+ SRCS = vncExtInit.cc vncHooks.cc xf86vncModule.cc XserverDesktop.cc
+ OBJS = vncExtInit.o vncHooks.o xf86vncModule.o XserverDesktop.o
+INCLUDES = -I.. -I../../include -I$(EXTINCSRC) -I$(XINCLUDESRC) \
+ -I$(FONTINCSRC) -I$(XF86COMSRC) \
+ $(VNCINCLUDE)
+ DEFINES = $(STD_DEFINES) -DGC_HAS_COMPOSITE_CLIP -DXFree86LOADER
+
+LinkSourceFile(vncExtInit.cc,..)
+LinkSourceFile(vncHooks.cc,..)
+LinkSourceFile(xf86vncModule.cc,..)
+LinkSourceFile(XserverDesktop.cc,..)
+
+ModuleObjectRule()
+/*
+ LibraryModuleTarget(vnc,$(OBJS) $(VNCLIBS))
+ InstallLibraryModule(vnc,$(MODULEDIR),extensions)
+*/
+
+
+/*
+ * CplusplusDynamicModuleTarget - build a module to be dynamically loaded
+ */
+#ifndef CplusplusDynamicModuleTarget
+#define CplusplusDynamicModuleTarget(module,modlist) @@\
+AllTarget(module) @@\
+ @@\
+module: modlist @@\
+ RemoveFile($@) @@\
+ $(CXX) -o $@ $(SHLIBLDFLAGS) modlist @@\
+ @@\
+clean:: @@\
+ RemoveFile(module)
+#endif /* CplusplusDynamicModuleTarget */
+
+
+
+CplusplusDynamicModuleTarget(vnc.so,$(OBJS) $(VNCLIBS))
+InstallDynamicModule(vnc.so,$(MODULEDIR),extensions)
+
+DependTarget()
+
+/*
+ InstallDriverSDKLibraryModule(vnc,$(DRIVERSDKMODULEDIR),extensions)
+*/
diff --git a/xc/programs/Xserver/vnc/vncExtInit.cc b/xc/programs/Xserver/vnc/vncExtInit.cc
new file mode 100644
index 00000000..ccaf5b85
--- /dev/null
+++ b/xc/programs/Xserver/vnc/vncExtInit.cc
@@ -0,0 +1,714 @@
+/* Copyright (C) 2002-2004 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 <stdio.h>
+
+extern "C" {
+#define class c_class
+#define NEED_EVENTS
+#include "X.h"
+#include "Xproto.h"
+#include "misc.h"
+#include "os.h"
+#include "dixstruct.h"
+#include "extnsionst.h"
+#include "scrnintstr.h"
+#include "selection.h"
+#define _VNCEXT_SERVER_
+#define _VNCEXT_PROTO_
+#include "vncExt.h"
+#undef class
+#undef xalloc
+}
+
+#include <rfb/Configuration.h>
+#include <rfb/Logger_stdio.h>
+#include <rfb/LogWriter.h>
+#include <rfb/util.h>
+#include <rfb/ServerCore.h>
+#include <rfb/SSecurityFactoryStandard.h>
+#include <rdr/HexOutStream.h>
+#include <rfb/LogWriter.h>
+#undef max
+#undef min
+#include <network/TcpSocket.h>
+
+#include "XserverDesktop.h"
+#include "vncHooks.h"
+#include "vncExtInit.h"
+
+extern "C" {
+
+ extern void vncExtensionInit();
+ static void vncResetProc(ExtensionEntry* extEntry);
+ static void vncBlockHandler(pointer data, OSTimePtr t, pointer readmask);
+ static void vncWakeupHandler(pointer data, int nfds, pointer readmask);
+ static void vncClientStateChange(CallbackListPtr*, pointer, pointer);
+ static void SendSelectionChangeEvent(Atom selection);
+ static int ProcVncExtDispatch(ClientPtr client);
+ static int SProcVncExtDispatch(ClientPtr client);
+
+ extern char *display;
+
+ extern Selection *CurrentSelections;
+ extern int NumCurrentSelections;
+}
+
+using namespace rfb;
+
+static rfb::LogWriter vlog("vncext");
+
+static unsigned long vncExtGeneration = 0;
+static bool initialised = false;
+static XserverDesktop* desktop[MAXSCREENS] = { 0, };
+void* vncFbptr[MAXSCREENS] = { 0, };
+
+static char* clientCutText = 0;
+static int clientCutTextLen = 0;
+
+static struct VncInputSelect* vncInputSelectHead = 0;
+struct VncInputSelect {
+ VncInputSelect(ClientPtr c, Window w, int m) : client(c), window(w), mask(m)
+ {
+ next = vncInputSelectHead;
+ vncInputSelectHead = this;
+ }
+ ClientPtr client;
+ Window window;
+ int mask;
+ VncInputSelect* next;
+};
+
+static int nPrevSelections = 0;
+static TimeStamp* prevSelectionTimes = 0;
+
+static int vncErrorBase = 0;
+static int vncEventBase = 0;
+static char* vncPasswdFile = 0;
+int vncInetdSock = -1;
+
+rfb::VncAuthPasswdFileParameter vncAuthPasswdFile;
+rfb::AliasParameter rfbauth("rfbauth", "Alias for PasswordFile",
+ &vncAuthPasswdFile.param);
+rfb::StringParameter httpDir("httpd",
+ "Directory containing files to serve via HTTP",
+ "");
+rfb::IntParameter httpPort("httpPort", "TCP port to listen for HTTP",0);
+rfb::AliasParameter rfbwait("rfbwait", "Alias for ClientWaitTimeMillis",
+ &rfb::Server::clientWaitTimeMillis);
+rfb::IntParameter rfbport("rfbport", "TCP port to listen for RFB protocol",0);
+rfb::StringParameter desktopName("desktop", "Name of VNC desktop","x11");
+rfb::BoolParameter localhostOnly("localhost",
+ "Only allow connections from localhost",
+ false);
+
+void vncExtensionInit()
+{
+ if (vncExtGeneration == serverGeneration) {
+ vlog.error("vncExtensionInit: called twice in same generation?");
+ return;
+ }
+ vncExtGeneration = serverGeneration;
+
+ ExtensionEntry* extEntry
+ = AddExtension(VNCEXTNAME, VncExtNumberEvents, VncExtNumberErrors,
+ ProcVncExtDispatch, SProcVncExtDispatch, vncResetProc,
+ StandardMinorOpcode);
+ if (!extEntry) {
+ ErrorF("vncExtInit: AddExtension failed\n");
+ return;
+ }
+
+ vncErrorBase = extEntry->errorBase;
+ vncEventBase = extEntry->eventBase;
+
+ vlog.info("VNC extension running!");
+
+ if (!AddCallback(&ClientStateCallback, vncClientStateChange, 0)) {
+ FatalError("AddCallback failed\n");
+ }
+
+ try {
+ if (!initialised) {
+ rfb::initStdIOLoggers();
+ initialised = true;
+ }
+
+ for (int scr = 0; scr < screenInfo.numScreens; scr++) {
+
+ if (!desktop[scr]) {
+ network::TcpListener* listener = 0;
+ network::TcpListener* httpListener = 0;
+ if (scr == 0 && vncInetdSock != -1) {
+ if (network::TcpSocket::isSocket(vncInetdSock) &&
+ !network::TcpSocket::isConnected(vncInetdSock))
+ {
+ listener = new network::TcpListener(0, 0, vncInetdSock, true);
+ vlog.info("inetd wait");
+ }
+ } else {
+ int port = rfbport;
+ if (port == 0) port = 5900 + atoi(display);
+ port += 1000 * scr;
+ listener = new network::TcpListener(port, localhostOnly);
+ vlog.info("Listening for VNC connections on port %d",port);
+ CharArray httpDirStr(httpDir.getData());
+ if (httpDirStr.buf[0]) {
+ port = httpPort;
+ if (port == 0) port = 5800 + atoi(display);
+ port += 1000 * scr;
+ httpListener = new network::TcpListener(port, localhostOnly);
+ vlog.info("Listening for HTTP connections on port %d",port);
+ }
+ }
+
+ CharArray desktopNameStr(desktopName.getData());
+ desktop[scr] = new XserverDesktop(screenInfo.screens[scr], listener,
+ httpListener,
+ desktopNameStr.buf,
+ vncFbptr[scr]);
+ vlog.info("created VNC server for screen %d", scr);
+
+ if (scr == 0 && vncInetdSock != -1 && !listener) {
+ network::Socket* sock = new network::TcpSocket(vncInetdSock);
+ desktop[scr]->addClient(sock, false);
+ vlog.info("added inetd sock");
+ }
+
+ } else {
+ desktop[scr]->serverReset(screenInfo.screens[scr]);
+ }
+
+ vncHooksInit(screenInfo.screens[scr], desktop[scr]);
+ }
+
+ RegisterBlockAndWakeupHandlers(vncBlockHandler, vncWakeupHandler, 0);
+
+ } catch (rdr::Exception& e) {
+ vlog.error("vncExtInit: %s",e.str());
+ }
+}
+
+static void vncResetProc(ExtensionEntry* extEntry)
+{
+}
+
+//
+// vncBlockHandler - called just before the X server goes into select(). Call
+// on to the block handler for each desktop. Then check whether any of the
+// selections have changed, and if so, notify any interested X clients.
+//
+
+static void vncBlockHandler(pointer data, OSTimePtr timeout, pointer readmask)
+{
+ fd_set* fds = (fd_set*)readmask;
+
+ for (int scr = 0; scr < screenInfo.numScreens; scr++) {
+ if (desktop[scr]) {
+ desktop[scr]->blockHandler(fds);
+ }
+ }
+
+ if (nPrevSelections != NumCurrentSelections) {
+ prevSelectionTimes
+ = (TimeStamp*)xnfrealloc(prevSelectionTimes,
+ NumCurrentSelections * sizeof(TimeStamp));
+ for (int i = nPrevSelections; i < NumCurrentSelections; i++) {
+ prevSelectionTimes[i].months = 0;
+ prevSelectionTimes[i].milliseconds = 0;
+ }
+ nPrevSelections = NumCurrentSelections;
+ }
+ for (int i = 0; i < NumCurrentSelections; i++) {
+ if (CurrentSelections[i].lastTimeChanged.months
+ != prevSelectionTimes[i].months ||
+ CurrentSelections[i].lastTimeChanged.milliseconds
+ != prevSelectionTimes[i].milliseconds)
+ {
+ SendSelectionChangeEvent(CurrentSelections[i].selection);
+ prevSelectionTimes[i] = CurrentSelections[i].lastTimeChanged;
+ }
+ }
+}
+
+static void vncWakeupHandler(pointer data, int nfds, pointer readmask)
+{
+ fd_set* fds = (fd_set*)readmask;
+
+ for (int scr = 0; scr < screenInfo.numScreens; scr++) {
+ if (desktop[scr]) {
+ desktop[scr]->wakeupHandler(fds, nfds);
+ }
+ }
+}
+
+static void vncClientStateChange(CallbackListPtr*, pointer, pointer p)
+{
+ ClientPtr client = ((NewClientInfoRec*)p)->client;
+ if (client->clientState == ClientStateGone) {
+ VncInputSelect** nextPtr = &vncInputSelectHead;
+ for (VncInputSelect* cur = vncInputSelectHead; cur; cur = *nextPtr) {
+ if (cur->client == client) {
+ *nextPtr = cur->next;
+ delete cur;
+ continue;
+ }
+ nextPtr = &cur->next;
+ }
+ }
+}
+
+void vncBell()
+{
+ for (int scr = 0; scr < screenInfo.numScreens; scr++) {
+ if (desktop[scr]) {
+ desktop[scr]->bell();
+ }
+ }
+}
+
+void vncClientGone(int fd)
+{
+ if (fd == vncInetdSock) {
+ fprintf(stderr,"inetdSock client gone\n");
+ GiveUp(0);
+ }
+}
+
+void vncClientCutText(const char* str, int len)
+{
+ delete [] clientCutText;
+ clientCutText = new char[len];
+ memcpy(clientCutText, str, len);
+ clientCutTextLen = len;
+ xVncExtClientCutTextNotifyEvent ev;
+ ev.type = vncEventBase + VncExtClientCutTextNotify;
+ for (VncInputSelect* cur = vncInputSelectHead; cur; cur = cur->next) {
+ if (cur->mask & VncExtClientCutTextMask) {
+ ev.sequenceNumber = cur->client->sequence;
+ ev.window = cur->window;
+ ev.time = GetTimeInMillis();
+ if (cur->client->swapped) {
+ int n;
+ swaps(&ev.sequenceNumber, n);
+ swapl(&ev.window, n);
+ swapl(&ev.time, n);
+ }
+ WriteToClient(cur->client, sizeof(xVncExtClientCutTextNotifyEvent),
+ (char *)&ev);
+ }
+ }
+}
+
+static void SendSelectionChangeEvent(Atom selection)
+{
+ xVncExtSelectionChangeNotifyEvent ev;
+ ev.type = vncEventBase + VncExtSelectionChangeNotify;
+ for (VncInputSelect* cur = vncInputSelectHead; cur; cur = cur->next) {
+ if (cur->mask & VncExtSelectionChangeMask) {
+ ev.sequenceNumber = cur->client->sequence;
+ ev.window = cur->window;
+ ev.selection = selection;
+ if (cur->client->swapped) {
+ int n;
+ swaps(&ev.sequenceNumber, n);
+ swapl(&ev.window, n);
+ swapl(&ev.selection, n);
+ }
+ WriteToClient(cur->client, sizeof(xVncExtSelectionChangeNotifyEvent),
+ (char *)&ev);
+ }
+ }
+}
+
+static int ProcVncExtSetParam(ClientPtr client)
+{
+ REQUEST(xVncExtSetParamReq);
+ REQUEST_FIXED_SIZE(xVncExtSetParamReq, stuff->paramLen);
+ CharArray param(stuff->paramLen+1);
+ strncpy(param.buf, (char*)&stuff[1], stuff->paramLen);
+ param.buf[stuff->paramLen] = 0;
+
+ xVncExtSetParamReply rep;
+ int n;
+ rep.type = X_Reply;
+ rep.length = 0;
+ rep.sequenceNumber = client->sequence;
+ rep.success = rfb::Configuration::setParam(param.buf);
+ if (client->swapped) {
+ swaps(&rep.sequenceNumber, n);
+ swapl(&rep.length, n);
+ }
+ WriteToClient(client, sizeof(xVncExtSetParamReply), (char *)&rep);
+ return (client->noClientException);
+}
+
+static int SProcVncExtSetParam(ClientPtr client)
+{
+ register char n;
+ REQUEST(xVncExtSetParamReq);
+ swaps(&stuff->length, n);
+ REQUEST_AT_LEAST_SIZE(xVncExtSetParamReq);
+ return ProcVncExtSetParam(client);
+}
+
+static int ProcVncExtGetParam(ClientPtr client)
+{
+ REQUEST(xVncExtGetParamReq);
+ REQUEST_FIXED_SIZE(xVncExtGetParamReq, stuff->paramLen);
+ CharArray param(stuff->paramLen+1);
+ strncpy(param.buf, (char*)&stuff[1], stuff->paramLen);
+ param.buf[stuff->paramLen] = 0;
+
+ xVncExtGetParamReply rep;
+ int n;
+ rep.type = X_Reply;
+ rep.sequenceNumber = client->sequence;
+ rep.success = 0;
+ int len = 0;
+ char* value = 0;
+ rfb::VoidParameter* p = rfb::Configuration::getParam(param.buf);
+ // Hack to avoid exposing password!
+ if (strcasecmp(param.buf, "Password") == 0)
+ p = 0;
+ if (p) {
+ value = p->getValueStr();
+ rep.success = 1;
+ len = value ? strlen(value) : 0;
+ }
+ rep.length = (len + 3) >> 2;
+ rep.valueLen = len;
+ if (client->swapped) {
+ swaps(&rep.sequenceNumber, n);
+ swapl(&rep.length, n);
+ swaps(&rep.valueLen, n);
+ }
+ WriteToClient(client, sizeof(xVncExtGetParamReply), (char *)&rep);
+ if (value)
+ WriteToClient(client, len, value);
+ delete [] value;
+ return (client->noClientException);
+}
+
+static int SProcVncExtGetParam(ClientPtr client)
+{
+ register char n;
+ REQUEST(xVncExtGetParamReq);
+ swaps(&stuff->length, n);
+ REQUEST_AT_LEAST_SIZE(xVncExtGetParamReq);
+ return ProcVncExtGetParam(client);
+}
+
+static int ProcVncExtGetParamDesc(ClientPtr client)
+{
+ REQUEST(xVncExtGetParamDescReq);
+ REQUEST_FIXED_SIZE(xVncExtGetParamDescReq, stuff->paramLen);
+ CharArray param(stuff->paramLen+1);
+ strncpy(param.buf, (char*)&stuff[1], stuff->paramLen);
+ param.buf[stuff->paramLen] = 0;
+
+ xVncExtGetParamDescReply rep;
+ int n;
+ rep.type = X_Reply;
+ rep.sequenceNumber = client->sequence;
+ rep.success = 0;
+ int len = 0;
+ const char* desc = 0;
+ rfb::VoidParameter* p = rfb::Configuration::getParam(param.buf);
+ if (p) {
+ desc = p->getDescription();
+ rep.success = 1;
+ len = desc ? strlen(desc) : 0;
+ }
+ rep.length = (len + 3) >> 2;
+ rep.descLen = len;
+ if (client->swapped) {
+ swaps(&rep.sequenceNumber, n);
+ swapl(&rep.length, n);
+ swaps(&rep.descLen, n);
+ }
+ WriteToClient(client, sizeof(xVncExtGetParamDescReply), (char *)&rep);
+ if (desc)
+ WriteToClient(client, len, (char*)desc);
+ return (client->noClientException);
+}
+
+static int SProcVncExtGetParamDesc(ClientPtr client)
+{
+ register char n;
+ REQUEST(xVncExtGetParamDescReq);
+ swaps(&stuff->length, n);
+ REQUEST_AT_LEAST_SIZE(xVncExtGetParamDescReq);
+ return ProcVncExtGetParamDesc(client);
+}
+
+static int ProcVncExtListParams(ClientPtr client)
+{
+ REQUEST(xVncExtListParamsReq);
+ REQUEST_SIZE_MATCH(xVncExtListParamsReq);
+
+ xVncExtListParamsReply rep;
+ int n;
+ rep.type = X_Reply;
+ rep.sequenceNumber = client->sequence;
+
+ int nParams = 0;
+ int len = 0;
+ rfb::VoidParameter* current = rfb::Configuration::head;
+ while (current) {
+ int l = strlen(current->getName());
+ if (l <= 255) {
+ nParams++;
+ len += l + 1;
+ }
+ current = current->_next;
+ }
+ rep.length = (len + 3) >> 2;
+ rep.nParams = nParams;
+ if (client->swapped) {
+ swaps(&rep.sequenceNumber, n);
+ swapl(&rep.length, n);
+ swaps(&rep.nParams, n);
+ }
+ WriteToClient(client, sizeof(xVncExtListParamsReply), (char *)&rep);
+ rdr::U8* data = new rdr::U8[len];
+ rdr::U8* ptr = data;
+ current = rfb::Configuration::head;
+ while (current) {
+ int l = strlen(current->getName());
+ if (l <= 255) {
+ *ptr++ = l;
+ memcpy(ptr, current->getName(), l);
+ ptr += l;
+ }
+ current = current->_next;
+ }
+ WriteToClient(client, len, (char*)data);
+ delete [] data;
+ return (client->noClientException);
+}
+
+static int SProcVncExtListParams(ClientPtr client)
+{
+ register char n;
+ REQUEST(xVncExtListParamsReq);
+ swaps(&stuff->length, n);
+ REQUEST_SIZE_MATCH(xVncExtListParamsReq);
+ return ProcVncExtListParams(client);
+}
+
+static int ProcVncExtSetServerCutText(ClientPtr client)
+{
+ REQUEST(xVncExtSetServerCutTextReq);
+ REQUEST_FIXED_SIZE(xVncExtSetServerCutTextReq, stuff->textLen);
+ char* str = new char[stuff->textLen+1];
+ strncpy(str, (char*)&stuff[1], stuff->textLen);
+ str[stuff->textLen] = 0;
+ for (int scr = 0; scr < screenInfo.numScreens; scr++) {
+ if (desktop[scr]) {
+ desktop[scr]->serverCutText(str, stuff->textLen);
+ }
+ }
+ delete [] str;
+ return (client->noClientException);
+}
+
+static int SProcVncExtSetServerCutText(ClientPtr client)
+{
+ register char n;
+ REQUEST(xVncExtSetServerCutTextReq);
+ swaps(&stuff->length, n);
+ REQUEST_AT_LEAST_SIZE(xVncExtSetServerCutTextReq);
+ swapl(&stuff->textLen, n);
+ return ProcVncExtSetServerCutText(client);
+}
+
+static int ProcVncExtGetClientCutText(ClientPtr client)
+{
+ REQUEST(xVncExtGetClientCutTextReq);
+ REQUEST_SIZE_MATCH(xVncExtGetClientCutTextReq);
+
+ xVncExtGetClientCutTextReply rep;
+ int n;
+ rep.type = X_Reply;
+ rep.length = (clientCutTextLen + 3) >> 2;
+ rep.sequenceNumber = client->sequence;
+ rep.textLen = clientCutTextLen;
+ if (client->swapped) {
+ swaps(&rep.sequenceNumber, n);
+ swapl(&rep.length, n);
+ swapl(&rep.textLen, n);
+ }
+ WriteToClient(client, sizeof(xVncExtGetClientCutTextReply), (char *)&rep);
+ if (clientCutText)
+ WriteToClient(client, clientCutTextLen, clientCutText);
+ return (client->noClientException);
+}
+
+static int SProcVncExtGetClientCutText(ClientPtr client)
+{
+ register char n;
+ REQUEST(xVncExtGetClientCutTextReq);
+ swaps(&stuff->length, n);
+ REQUEST_SIZE_MATCH(xVncExtGetClientCutTextReq);
+ return ProcVncExtGetClientCutText(client);
+}
+
+static int ProcVncExtSelectInput(ClientPtr client)
+{
+ REQUEST(xVncExtSelectInputReq);
+ REQUEST_SIZE_MATCH(xVncExtSelectInputReq);
+ VncInputSelect** nextPtr = &vncInputSelectHead;
+ VncInputSelect* cur;
+ for (cur = vncInputSelectHead; cur; cur = *nextPtr) {
+ if (cur->client == client && cur->window == stuff->window) {
+ cur->mask = stuff->mask;
+ if (!cur->mask) {
+ *nextPtr = cur->next;
+ delete cur;
+ }
+ break;
+ }
+ nextPtr = &cur->next;
+ }
+ if (!cur) {
+ cur = new VncInputSelect(client, stuff->window, stuff->mask);
+ }
+ return (client->noClientException);
+}
+
+static int SProcVncExtSelectInput(ClientPtr client)
+{
+ register char n;
+ REQUEST(xVncExtSelectInputReq);
+ swaps(&stuff->length, n);
+ REQUEST_SIZE_MATCH(xVncExtSelectInputReq);
+ swapl(&stuff->window, n);
+ swapl(&stuff->mask, n);
+ return ProcVncExtSelectInput(client);
+}
+
+static int ProcVncExtConnect(ClientPtr client)
+{
+ REQUEST(xVncExtConnectReq);
+ REQUEST_FIXED_SIZE(xVncExtConnectReq, stuff->strLen);
+ CharArray str(stuff->strLen+1);
+ strncpy(str.buf, (char*)&stuff[1], stuff->strLen);
+ str.buf[stuff->strLen] = 0;
+
+ xVncExtConnectReply rep;
+ rep.success = 0;
+ if (desktop[0]) {
+ if (stuff->strLen == 0) {
+ try {
+ desktop[0]->disconnectClients();
+ rep.success = 1;
+ } catch (rdr::Exception& e) {
+ vlog.error("Disconnecting all clients: %s",e.str());
+ }
+ } else {
+ int port = 5500;
+ for (int i = 0; i < stuff->strLen; i++) {
+ if (str.buf[i] == ':') {
+ port = atoi(&str.buf[i+1]);
+ str.buf[i] = 0;
+ break;
+ }
+ }
+
+ try {
+ network::Socket* sock = new network::TcpSocket(str.buf, port);
+ desktop[0]->addClient(sock, true);
+ rep.success = 1;
+ } catch (rdr::Exception& e) {
+ vlog.error("Reverse connection: %s",e.str());
+ }
+ }
+ }
+
+ rep.type = X_Reply;
+ rep.length = 0;
+ rep.sequenceNumber = client->sequence;
+ if (client->swapped) {
+ int n;
+ swaps(&rep.sequenceNumber, n);
+ swapl(&rep.length, n);
+ }
+ WriteToClient(client, sizeof(xVncExtConnectReply), (char *)&rep);
+ return (client->noClientException);
+}
+
+static int SProcVncExtConnect(ClientPtr client)
+{
+ register char n;
+ REQUEST(xVncExtConnectReq);
+ swaps(&stuff->length, n);
+ REQUEST_AT_LEAST_SIZE(xVncExtConnectReq);
+ return ProcVncExtConnect(client);
+}
+
+static int ProcVncExtDispatch(ClientPtr client)
+{
+ REQUEST(xReq);
+ switch (stuff->data) {
+ case X_VncExtSetParam:
+ return ProcVncExtSetParam(client);
+ case X_VncExtGetParam:
+ return ProcVncExtGetParam(client);
+ case X_VncExtGetParamDesc:
+ return ProcVncExtGetParamDesc(client);
+ case X_VncExtListParams:
+ return ProcVncExtListParams(client);
+ case X_VncExtSetServerCutText:
+ return ProcVncExtSetServerCutText(client);
+ case X_VncExtGetClientCutText:
+ return ProcVncExtGetClientCutText(client);
+ case X_VncExtSelectInput:
+ return ProcVncExtSelectInput(client);
+ case X_VncExtConnect:
+ return ProcVncExtConnect(client);
+ default:
+ return BadRequest;
+ }
+}
+
+static int SProcVncExtDispatch(ClientPtr client)
+{
+ REQUEST(xReq);
+ switch (stuff->data) {
+ case X_VncExtSetParam:
+ return SProcVncExtSetParam(client);
+ case X_VncExtGetParam:
+ return SProcVncExtGetParam(client);
+ case X_VncExtGetParamDesc:
+ return SProcVncExtGetParamDesc(client);
+ case X_VncExtListParams:
+ return SProcVncExtListParams(client);
+ case X_VncExtSetServerCutText:
+ return SProcVncExtSetServerCutText(client);
+ case X_VncExtGetClientCutText:
+ return SProcVncExtGetClientCutText(client);
+ case X_VncExtSelectInput:
+ return SProcVncExtSelectInput(client);
+ case X_VncExtConnect:
+ return SProcVncExtConnect(client);
+ default:
+ return BadRequest;
+ }
+}
+
diff --git a/xc/programs/Xserver/vnc/vncExtInit.h b/xc/programs/Xserver/vnc/vncExtInit.h
new file mode 100644
index 00000000..947f34dc
--- /dev/null
+++ b/xc/programs/Xserver/vnc/vncExtInit.h
@@ -0,0 +1,30 @@
+/* Copyright (C) 2002-2003 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 __VNCEXTINIT_H__
+#define __VNCEXTINIT_H__
+
+#include <rfb/Configuration.h>
+
+extern void vncClientCutText(const char* str, int len);
+extern void vncClientGone(int fd);
+extern void vncBell();
+extern void* vncFbptr[];
+extern int vncInetdSock;
+extern rfb::StringParameter httpDir;
+
+#endif
diff --git a/xc/programs/Xserver/vnc/vncHooks.cc b/xc/programs/Xserver/vnc/vncHooks.cc
new file mode 100644
index 00000000..d52a4971
--- /dev/null
+++ b/xc/programs/Xserver/vnc/vncHooks.cc
@@ -0,0 +1,1475 @@
+/* Copyright (C) 2002-2003 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 <stdio.h>
+#include "XserverDesktop.h"
+#include "vncHooks.h"
+
+extern "C" {
+#define class c_class
+#define private c_private
+#include "scrnintstr.h"
+#include "windowstr.h"
+#include "gcstruct.h"
+#include "regionstr.h"
+#include "dixfontstr.h"
+#include "colormapst.h"
+
+#ifdef GC_HAS_COMPOSITE_CLIP
+#define COMPOSITE_CLIP(gc) ((gc)->pCompositeClip)
+#else
+#include "mfb.h"
+#define COMPOSITE_CLIP(gc) \
+ (((mfbPrivGCPtr)((gc)->devPrivates[mfbGCPrivateIndex].ptr))->pCompositeClip)
+#endif
+
+#undef class
+#undef private
+}
+
+#include "RegionHelper.h"
+
+#define DBGPRINT(x) //(fprintf x)
+
+// MAX_RECTS_PER_OP is the maximum number of rectangles we generate from
+// operations like Polylines and PolySegment. If the operation is more complex
+// than this, we simply use the bounding box. Ideally it would be a
+// command-line option, but that would involve an extra malloc each time, so we
+// fix it here.
+#define MAX_RECTS_PER_OP 5
+
+static unsigned long vncHooksGeneration = 0;
+
+// vncHooksScreenRec and vncHooksGCRec contain pointers to the original
+// functions which we "wrap" in order to hook the screen changes. The screen
+// functions are each wrapped individually, while the GC "funcs" and "ops" are
+// wrapped as a unit.
+
+typedef struct {
+ XserverDesktop* desktop;
+
+ CloseScreenProcPtr CloseScreen;
+ CreateGCProcPtr CreateGC;
+ PaintWindowBackgroundProcPtr PaintWindowBackground;
+ PaintWindowBorderProcPtr PaintWindowBorder;
+ CopyWindowProcPtr CopyWindow;
+ ClearToBackgroundProcPtr ClearToBackground;
+ RestoreAreasProcPtr RestoreAreas;
+ InstallColormapProcPtr InstallColormap;
+ StoreColorsProcPtr StoreColors;
+ DisplayCursorProcPtr DisplayCursor;
+ ScreenBlockHandlerProcPtr BlockHandler;
+} vncHooksScreenRec, *vncHooksScreenPtr;
+
+typedef struct {
+ GCFuncs *wrappedFuncs;
+ GCOps *wrappedOps;
+} vncHooksGCRec, *vncHooksGCPtr;
+
+static int vncHooksScreenIndex;
+static int vncHooksGCIndex;
+
+
+// screen functions
+
+static Bool vncHooksCloseScreen(int i, ScreenPtr pScreen);
+static Bool vncHooksCreateGC(GCPtr pGC);
+static void vncHooksPaintWindowBackground(WindowPtr pWin, RegionPtr pRegion,
+ int what);
+static void vncHooksPaintWindowBorder(WindowPtr pWin, RegionPtr pRegion,
+ int what);
+static void vncHooksCopyWindow(WindowPtr pWin, DDXPointRec ptOldOrg,
+ RegionPtr pOldRegion);
+static void vncHooksClearToBackground(WindowPtr pWin, int x, int y, int w,
+ int h, Bool generateExposures);
+static RegionPtr vncHooksRestoreAreas(WindowPtr pWin, RegionPtr prgnExposed);
+static void vncHooksInstallColormap(ColormapPtr pColormap);
+static void vncHooksStoreColors(ColormapPtr pColormap, int ndef,
+ xColorItem* pdef);
+static Bool vncHooksDisplayCursor(ScreenPtr pScreen, CursorPtr cursor);
+static void vncHooksBlockHandler(int i, pointer blockData, pointer pTimeout,
+ pointer pReadmask);
+
+// GC "funcs"
+
+static void vncHooksValidateGC(GCPtr pGC, unsigned long changes,
+ DrawablePtr pDrawable);
+static void vncHooksChangeGC(GCPtr pGC, unsigned long mask);
+static void vncHooksCopyGC(GCPtr src, unsigned long mask, GCPtr dst);
+static void vncHooksDestroyGC(GCPtr pGC);
+static void vncHooksChangeClip(GCPtr pGC, int type, pointer pValue,int nrects);
+static void vncHooksDestroyClip(GCPtr pGC);
+static void vncHooksCopyClip(GCPtr dst, GCPtr src);
+
+static GCFuncs vncHooksGCFuncs = {
+ vncHooksValidateGC, vncHooksChangeGC, vncHooksCopyGC, vncHooksDestroyGC,
+ vncHooksChangeClip, vncHooksDestroyClip, vncHooksCopyClip,
+};
+
+// GC "ops"
+
+static void vncHooksFillSpans(DrawablePtr pDrawable, GCPtr pGC, int nInit,
+ DDXPointPtr pptInit, int *pwidthInit,
+ int fSorted);
+static void vncHooksSetSpans(DrawablePtr pDrawable, GCPtr pGC, char *psrc,
+ DDXPointPtr ppt, int *pwidth, int nspans,
+ int fSorted);
+static void vncHooksPutImage(DrawablePtr pDrawable, GCPtr pGC, int depth,
+ int x, int y, int w, int h, int leftPad,
+ int format, char *pBits);
+static RegionPtr vncHooksCopyArea(DrawablePtr pSrc, DrawablePtr pDst,
+ GCPtr pGC, int srcx, int srcy, int w, int h,
+ int dstx, int dsty);
+static RegionPtr vncHooksCopyPlane(DrawablePtr pSrc, DrawablePtr pDst,
+ GCPtr pGC, int srcx, int srcy, int w, int h,
+ int dstx, int dsty, unsigned long plane);
+static void vncHooksPolyPoint(DrawablePtr pDrawable, GCPtr pGC, int mode,
+ int npt, xPoint *pts);
+static void vncHooksPolylines(DrawablePtr pDrawable, GCPtr pGC, int mode,
+ int npt, DDXPointPtr ppts);
+static void vncHooksPolySegment(DrawablePtr pDrawable, GCPtr pGC, int nseg,
+ xSegment *segs);
+static void vncHooksPolyRectangle(DrawablePtr pDrawable, GCPtr pGC, int nrects,
+ xRectangle *rects);
+static void vncHooksPolyArc(DrawablePtr pDrawable, GCPtr pGC, int narcs,
+ xArc *arcs);
+static void vncHooksFillPolygon(DrawablePtr pDrawable, GCPtr pGC, int shape,
+ int mode, int count, DDXPointPtr pts);
+static void vncHooksPolyFillRect(DrawablePtr pDrawable, GCPtr pGC, int nrects,
+ xRectangle *rects);
+static void vncHooksPolyFillArc(DrawablePtr pDrawable, GCPtr pGC, int narcs,
+ xArc *arcs);
+static int vncHooksPolyText8(DrawablePtr pDrawable, GCPtr pGC, int x, int y,
+ int count, char *chars);
+static int vncHooksPolyText16(DrawablePtr pDrawable, GCPtr pGC, int x, int y,
+ int count, unsigned short *chars);
+static void vncHooksImageText8(DrawablePtr pDrawable, GCPtr pGC, int x, int y,
+ int count, char *chars);
+static void vncHooksImageText16(DrawablePtr pDrawable, GCPtr pGC, int x, int y,
+ int count, unsigned short *chars);
+static void vncHooksImageGlyphBlt(DrawablePtr pDrawable, GCPtr pGC, int x,
+ int y, unsigned int nglyph,
+ CharInfoPtr *ppci, pointer pglyphBase);
+static void vncHooksPolyGlyphBlt(DrawablePtr pDrawable, GCPtr pGC, int x,
+ int y, unsigned int nglyph,
+ CharInfoPtr *ppci, pointer pglyphBase);
+static void vncHooksPushPixels(GCPtr pGC, PixmapPtr pBitMap,
+ DrawablePtr pDrawable, int w, int h, int x,
+ int y);
+
+static GCOps vncHooksGCOps = {
+ vncHooksFillSpans, vncHooksSetSpans, vncHooksPutImage, vncHooksCopyArea,
+ vncHooksCopyPlane, vncHooksPolyPoint, vncHooksPolylines, vncHooksPolySegment,
+ vncHooksPolyRectangle, vncHooksPolyArc, vncHooksFillPolygon,
+ vncHooksPolyFillRect, vncHooksPolyFillArc, vncHooksPolyText8,
+ vncHooksPolyText16, vncHooksImageText8, vncHooksImageText16,
+ vncHooksImageGlyphBlt, vncHooksPolyGlyphBlt, vncHooksPushPixels
+};
+
+
+
+/////////////////////////////////////////////////////////////////////////////
+// vncHooksInit() is called at initialisation time and every time the server
+// resets. It is called once for each screen, but the indexes are only
+// allocated once for each server generation.
+
+Bool vncHooksInit(ScreenPtr pScreen, XserverDesktop* desktop)
+{
+ vncHooksScreenPtr vncHooksScreen;
+
+ if (vncHooksGeneration != serverGeneration) {
+ vncHooksGeneration = serverGeneration;
+
+ vncHooksScreenIndex = AllocateScreenPrivateIndex();
+ if (vncHooksScreenIndex < 0) {
+ ErrorF("vncHooksInit: AllocateScreenPrivateIndex failed\n");
+ return FALSE;
+ }
+
+ vncHooksGCIndex = AllocateGCPrivateIndex();
+ if (vncHooksGCIndex < 0) {
+ ErrorF("vncHooksInit: AllocateGCPrivateIndex failed\n");
+ return FALSE;
+ }
+ }
+
+ if (!AllocateGCPrivate(pScreen, vncHooksGCIndex, sizeof(vncHooksGCRec))) {
+ ErrorF("vncHooksInit: AllocateGCPrivate failed\n");
+ return FALSE;
+ }
+
+ vncHooksScreen = (vncHooksScreenPtr)xnfalloc(sizeof(vncHooksScreenRec));
+ pScreen->devPrivates[vncHooksScreenIndex].ptr = (pointer)vncHooksScreen;
+
+ vncHooksScreen->desktop = desktop;
+
+ vncHooksScreen->CloseScreen = pScreen->CloseScreen;
+ vncHooksScreen->CreateGC = pScreen->CreateGC;
+ vncHooksScreen->PaintWindowBackground = pScreen->PaintWindowBackground;
+ vncHooksScreen->PaintWindowBorder = pScreen->PaintWindowBorder;
+ vncHooksScreen->CopyWindow = pScreen->CopyWindow;
+ vncHooksScreen->ClearToBackground = pScreen->ClearToBackground;
+ vncHooksScreen->RestoreAreas = pScreen->RestoreAreas;
+ vncHooksScreen->InstallColormap = pScreen->InstallColormap;
+ vncHooksScreen->StoreColors = pScreen->StoreColors;
+ vncHooksScreen->DisplayCursor = pScreen->DisplayCursor;
+ vncHooksScreen->BlockHandler = pScreen->BlockHandler;
+
+ pScreen->CloseScreen = vncHooksCloseScreen;
+ pScreen->CreateGC = vncHooksCreateGC;
+ pScreen->PaintWindowBackground = vncHooksPaintWindowBackground;
+ pScreen->PaintWindowBorder = vncHooksPaintWindowBorder;
+ pScreen->CopyWindow = vncHooksCopyWindow;
+ pScreen->ClearToBackground = vncHooksClearToBackground;
+ pScreen->RestoreAreas = vncHooksRestoreAreas;
+ pScreen->InstallColormap = vncHooksInstallColormap;
+ pScreen->StoreColors = vncHooksStoreColors;
+ pScreen->DisplayCursor = vncHooksDisplayCursor;
+ pScreen->BlockHandler = vncHooksBlockHandler;
+
+ return TRUE;
+}
+
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// screen functions
+//
+
+// SCREEN_UNWRAP and SCREEN_REWRAP unwrap and rewrap the given screen function.
+// It would be nice to do this with a C++ class, but each function is of a
+// distinct type, so it would have to use templates, and it's not worth that
+// much pain.
+
+#define SCREEN_UNWRAP(scrn,field) \
+ ScreenPtr pScreen = scrn; \
+ vncHooksScreenPtr vncHooksScreen \
+ = ((vncHooksScreenPtr)pScreen->devPrivates[vncHooksScreenIndex].ptr); \
+ pScreen->field = vncHooksScreen->field; \
+ DBGPRINT((stderr,"vncHooks" #field " called\n"));
+
+#define SCREEN_REWRAP(field) pScreen->field = vncHooks##field;
+
+
+// CloseScreen - unwrap the screen functions and call the original CloseScreen
+// function
+
+static Bool vncHooksCloseScreen(int i, ScreenPtr pScreen_)
+{
+ SCREEN_UNWRAP(pScreen_, CloseScreen);
+
+ pScreen->CreateGC = vncHooksScreen->CreateGC;
+ pScreen->PaintWindowBackground = vncHooksScreen->PaintWindowBackground;
+ pScreen->PaintWindowBorder = vncHooksScreen->PaintWindowBorder;
+ pScreen->CopyWindow = vncHooksScreen->CopyWindow;
+ pScreen->ClearToBackground = vncHooksScreen->ClearToBackground;
+ pScreen->RestoreAreas = vncHooksScreen->RestoreAreas;
+ pScreen->InstallColormap = vncHooksScreen->InstallColormap;
+ pScreen->StoreColors = vncHooksScreen->StoreColors;
+ pScreen->DisplayCursor = vncHooksScreen->DisplayCursor;
+ pScreen->BlockHandler = vncHooksScreen->BlockHandler;
+
+ xfree((pointer)vncHooksScreen);
+
+ DBGPRINT((stderr,"vncHooksCloseScreen: unwrapped screen functions\n"));
+
+ return (*pScreen->CloseScreen)(i, pScreen);
+}
+
+// CreateGC - wrap the "GC funcs"
+
+static Bool vncHooksCreateGC(GCPtr pGC)
+{
+ SCREEN_UNWRAP(pGC->pScreen, CreateGC);
+
+ vncHooksGCPtr vncHooksGC
+ = (vncHooksGCPtr)pGC->devPrivates[vncHooksGCIndex].ptr;
+
+ Bool ret = (*pScreen->CreateGC) (pGC);
+
+ vncHooksGC->wrappedOps = 0;
+ vncHooksGC->wrappedFuncs = pGC->funcs;
+ pGC->funcs = &vncHooksGCFuncs;
+
+ SCREEN_REWRAP(CreateGC);
+
+ return ret;
+}
+
+// PaintWindowBackground - changed region is the given region
+
+static void vncHooksPaintWindowBackground(WindowPtr pWin, RegionPtr pRegion,
+ int what)
+{
+ SCREEN_UNWRAP(pWin->drawable.pScreen, PaintWindowBackground);
+
+ RegionHelper changed(pScreen, pRegion);
+
+ (*pScreen->PaintWindowBackground) (pWin, pRegion, what);
+
+ vncHooksScreen->desktop->add_changed(changed.reg);
+
+ SCREEN_REWRAP(PaintWindowBackground);
+}
+
+// PaintWindowBorder - changed region is the given region
+
+static void vncHooksPaintWindowBorder(WindowPtr pWin, RegionPtr pRegion,
+ int what)
+{
+ SCREEN_UNWRAP(pWin->drawable.pScreen, PaintWindowBorder);
+
+ RegionHelper changed(pScreen, pRegion);
+
+ (*pScreen->PaintWindowBorder) (pWin, pRegion, what);
+
+ vncHooksScreen->desktop->add_changed(changed.reg);
+
+ SCREEN_REWRAP(PaintWindowBorder);
+}
+
+// CopyWindow - destination of the copy is the old region, clipped by
+// borderClip, translated by the delta. This call only does the copy - it
+// doesn't affect any other bits.
+
+static void vncHooksCopyWindow(WindowPtr pWin, DDXPointRec ptOldOrg,
+ RegionPtr pOldRegion)
+{
+ SCREEN_UNWRAP(pWin->drawable.pScreen, CopyWindow);
+
+ RegionHelper copied(pScreen, pOldRegion);
+ int dx = pWin->drawable.x - ptOldOrg.x;
+ int dy = pWin->drawable.y - ptOldOrg.y;
+ REGION_TRANSLATE(pScreen, copied.reg, dx, dy);
+ REGION_INTERSECT(pWin->drawable.pScreen, copied.reg, copied.reg,
+ &pWin->borderClip);
+
+ (*pScreen->CopyWindow) (pWin, ptOldOrg, pOldRegion);
+
+ vncHooksScreen->desktop->add_copied(copied.reg, dx, dy);
+
+ SCREEN_REWRAP(CopyWindow);
+}
+
+// ClearToBackground - changed region is the given rectangle, clipped by
+// clipList, but only if generateExposures is false.
+
+static void vncHooksClearToBackground(WindowPtr pWin, int x, int y, int w,
+ int h, Bool generateExposures)
+{
+ SCREEN_UNWRAP(pWin->drawable.pScreen, ClearToBackground);
+
+ BoxRec box;
+ box.x1 = x + pWin->drawable.x;
+ box.y1 = y + pWin->drawable.y;
+ box.x2 = w ? (box.x1 + w) : (pWin->drawable.x + pWin->drawable.width);
+ box.y2 = h ? (box.y1 + h) : (pWin->drawable.y + pWin->drawable.height);
+
+ RegionHelper changed(pScreen, &box, 0);
+
+ REGION_INTERSECT(pScreen, changed.reg, changed.reg, &pWin->clipList);
+
+ (*pScreen->ClearToBackground) (pWin, x, y, w, h, generateExposures);
+
+ if (!generateExposures) {
+ vncHooksScreen->desktop->add_changed(changed.reg);
+ }
+
+ SCREEN_REWRAP(ClearToBackground);
+}
+
+// RestoreAreas - changed region is the given region
+
+static RegionPtr vncHooksRestoreAreas(WindowPtr pWin, RegionPtr pRegion)
+{
+ SCREEN_UNWRAP(pWin->drawable.pScreen, RestoreAreas);
+
+ RegionHelper changed(pScreen, pRegion);
+
+ RegionPtr result = (*pScreen->RestoreAreas) (pWin, pRegion);
+
+ vncHooksScreen->desktop->add_changed(changed.reg);
+
+ SCREEN_REWRAP(RestoreAreas);
+
+ return result;
+}
+
+// InstallColormap - get the new colormap
+
+static void vncHooksInstallColormap(ColormapPtr pColormap)
+{
+ SCREEN_UNWRAP(pColormap->pScreen, InstallColormap);
+
+ (*pScreen->InstallColormap) (pColormap);
+
+ vncHooksScreen->desktop->setColormap(pColormap);
+
+ SCREEN_REWRAP(InstallColormap);
+}
+
+// StoreColors - get the colormap changes
+
+static void vncHooksStoreColors(ColormapPtr pColormap, int ndef,
+ xColorItem* pdef)
+{
+ SCREEN_UNWRAP(pColormap->pScreen, StoreColors);
+
+ (*pScreen->StoreColors) (pColormap, ndef, pdef);
+
+ vncHooksScreen->desktop->setColourMapEntries(pColormap, ndef, pdef);
+
+ SCREEN_REWRAP(StoreColors);
+}
+
+// DisplayCursor - get the cursor shape
+
+static Bool vncHooksDisplayCursor(ScreenPtr pScreen_, CursorPtr cursor)
+{
+ SCREEN_UNWRAP(pScreen_, DisplayCursor);
+
+ Bool ret = (*pScreen->DisplayCursor) (pScreen, cursor);
+
+ vncHooksScreen->desktop->setCursor(cursor);
+
+ SCREEN_REWRAP(DisplayCursor);
+
+ return ret;
+}
+
+// BlockHandler - ignore any changes during the block handler - it's likely
+// these are just drawing the cursor.
+
+static void vncHooksBlockHandler(int i, pointer blockData, pointer pTimeout,
+ pointer pReadmask)
+{
+ SCREEN_UNWRAP(screenInfo.screens[i], BlockHandler);
+
+ vncHooksScreen->desktop->ignoreHooks(true);
+
+ (*pScreen->BlockHandler) (i, blockData, pTimeout, pReadmask);
+
+ vncHooksScreen->desktop->ignoreHooks(false);
+
+ SCREEN_REWRAP(BlockHandler);
+}
+
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// GC "funcs"
+//
+
+// GCFuncUnwrapper is a helper class which unwraps the GC funcs and ops in its
+// constructor and rewraps them in its destructor.
+
+class GCFuncUnwrapper {
+public:
+ GCFuncUnwrapper(GCPtr pGC_) : pGC(pGC_) {
+ vncHooksGC = (vncHooksGCPtr)pGC->devPrivates[vncHooksGCIndex].ptr;
+ pGC->funcs = vncHooksGC->wrappedFuncs;
+ if (vncHooksGC->wrappedOps)
+ pGC->ops = vncHooksGC->wrappedOps;
+ }
+ ~GCFuncUnwrapper() {
+ vncHooksGC->wrappedFuncs = pGC->funcs;
+ pGC->funcs = &vncHooksGCFuncs;
+ if (vncHooksGC->wrappedOps) {
+ vncHooksGC->wrappedOps = pGC->ops;
+ pGC->ops = &vncHooksGCOps;
+ }
+ }
+ GCPtr pGC;
+ vncHooksGCPtr vncHooksGC;
+};
+
+
+// ValidateGC - wrap the "ops" if a viewable window
+
+static void vncHooksValidateGC(GCPtr pGC, unsigned long changes,
+ DrawablePtr pDrawable)
+{
+ GCFuncUnwrapper u(pGC);
+
+ DBGPRINT((stderr,"vncHooksValidateGC called\n"));
+
+ (*pGC->funcs->ValidateGC) (pGC, changes, pDrawable);
+
+ u.vncHooksGC->wrappedOps = 0;
+ if (pDrawable->type == DRAWABLE_WINDOW && ((WindowPtr)pDrawable)->viewable) {
+ WindowPtr pWin = (WindowPtr)pDrawable;
+ RegionPtr pRegion = &pWin->clipList;
+
+ if (pGC->subWindowMode == IncludeInferiors)
+ pRegion = &pWin->borderClip;
+ if (REGION_NOTEMPTY(pDrawable->pScreen, pRegion)) {
+ u.vncHooksGC->wrappedOps = pGC->ops;
+ DBGPRINT((stderr,"vncHooksValidateGC: wrapped GC ops\n"));
+ }
+ }
+}
+
+// Other GC funcs - just unwrap and call on
+
+static void vncHooksChangeGC(GCPtr pGC, unsigned long mask) {
+ GCFuncUnwrapper u(pGC);
+ (*pGC->funcs->ChangeGC) (pGC, mask);
+}
+static void vncHooksCopyGC(GCPtr src, unsigned long mask, GCPtr dst) {
+ GCFuncUnwrapper u(dst);
+ (*dst->funcs->CopyGC) (src, mask, dst);
+}
+static void vncHooksDestroyGC(GCPtr pGC) {
+ GCFuncUnwrapper u(pGC);
+ (*pGC->funcs->DestroyGC) (pGC);
+}
+static void vncHooksChangeClip(GCPtr pGC, int type, pointer pValue, int nrects)
+{
+ GCFuncUnwrapper u(pGC);
+ (*pGC->funcs->ChangeClip) (pGC, type, pValue, nrects);
+}
+static void vncHooksDestroyClip(GCPtr pGC) {
+ GCFuncUnwrapper u(pGC);
+ (*pGC->funcs->DestroyClip) (pGC);
+}
+static void vncHooksCopyClip(GCPtr dst, GCPtr src) {
+ GCFuncUnwrapper u(dst);
+ (*dst->funcs->CopyClip) (dst, src);
+}
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// GC "ops"
+//
+
+// GCOpUnwrapper is a helper class which unwraps the GC funcs and ops in its
+// constructor and rewraps them in its destructor.
+
+class GCOpUnwrapper {
+public:
+ GCOpUnwrapper(DrawablePtr pDrawable, GCPtr pGC_)
+ : pGC(pGC_), pScreen(pDrawable->pScreen)
+ {
+ vncHooksGC = (vncHooksGCPtr)pGC->devPrivates[vncHooksGCIndex].ptr;
+ oldFuncs = pGC->funcs;
+ pGC->funcs = vncHooksGC->wrappedFuncs;
+ pGC->ops = vncHooksGC->wrappedOps;
+ }
+ ~GCOpUnwrapper() {
+ vncHooksGC->wrappedOps = pGC->ops;
+ pGC->funcs = oldFuncs;
+ pGC->ops = &vncHooksGCOps;
+ }
+ GCPtr pGC;
+ vncHooksGCPtr vncHooksGC;
+ GCFuncs* oldFuncs;
+ ScreenPtr pScreen;
+};
+
+#define GC_OP_UNWRAPPER(pDrawable, pGC, name) \
+ GCOpUnwrapper u(pDrawable, pGC); \
+ ScreenPtr pScreen = (pDrawable)->pScreen; \
+ vncHooksScreenPtr vncHooksScreen \
+ = ((vncHooksScreenPtr)pScreen->devPrivates[vncHooksScreenIndex].ptr); \
+ DBGPRINT((stderr,"vncHooks" #name " called\n"));
+
+
+// FillSpans - changed region is the whole of borderClip. This is pessimistic,
+// but I believe this function is rarely used so it doesn't matter.
+
+static void vncHooksFillSpans(DrawablePtr pDrawable, GCPtr pGC, int nInit,
+ DDXPointPtr pptInit, int *pwidthInit,
+ int fSorted)
+{
+ GC_OP_UNWRAPPER(pDrawable, pGC, FillSpans);
+
+ RegionHelper changed(pScreen, &((WindowPtr)pDrawable)->borderClip);
+
+ (*pGC->ops->FillSpans) (pDrawable, pGC, nInit, pptInit, pwidthInit, fSorted);
+
+ vncHooksScreen->desktop->add_changed(changed.reg);
+}
+
+// SetSpans - changed region is the whole of borderClip. This is pessimistic,
+// but I believe this function is rarely used so it doesn't matter.
+
+static void vncHooksSetSpans(DrawablePtr pDrawable, GCPtr pGC, char *psrc,
+ DDXPointPtr ppt, int *pwidth, int nspans,
+ int fSorted)
+{
+ GC_OP_UNWRAPPER(pDrawable, pGC, SetSpans);
+
+ RegionHelper changed(pScreen, &((WindowPtr)pDrawable)->borderClip);
+
+ (*pGC->ops->SetSpans) (pDrawable, pGC, psrc, ppt, pwidth, nspans, fSorted);
+
+ vncHooksScreen->desktop->add_changed(changed.reg);
+}
+
+// PutImage - changed region is the given rectangle, clipped by pCompositeClip
+
+static void vncHooksPutImage(DrawablePtr pDrawable, GCPtr pGC, int depth,
+ int x, int y, int w, int h, int leftPad,
+ int format, char *pBits)
+{
+ GC_OP_UNWRAPPER(pDrawable, pGC, PutImage);
+
+ BoxRec box;
+ box.x1 = x + pDrawable->x;
+ box.y1 = y + pDrawable->y;
+ box.x2 = box.x1 + w;
+ box.y2 = box.y1 + h;
+
+ RegionHelper changed(pScreen, &box, 0);
+
+ REGION_INTERSECT(pScreen, changed.reg, changed.reg, COMPOSITE_CLIP(pGC));
+
+ (*pGC->ops->PutImage) (pDrawable, pGC, depth, x, y, w, h, leftPad, format,
+ pBits);
+
+ vncHooksScreen->desktop->add_changed(changed.reg);
+}
+
+// CopyArea - destination of the copy is the dest rectangle, clipped by
+// pCompositeClip. Any parts of the destination which cannot be copied from
+// the source (could be all of it) go into the changed region.
+
+static RegionPtr vncHooksCopyArea(DrawablePtr pSrc, DrawablePtr pDst,
+ GCPtr pGC, int srcx, int srcy, int w, int h,
+ int dstx, int dsty)
+{
+ GC_OP_UNWRAPPER(pDst, pGC, CopyArea);
+
+ BoxRec box;
+ box.x1 = dstx + pDst->x;
+ box.y1 = dsty + pDst->y;
+ box.x2 = box.x1 + w;
+ box.y2 = box.y1 + h;
+
+ RegionHelper dst(pScreen, &box, 0);
+ REGION_INTERSECT(pScreen, dst.reg, dst.reg, COMPOSITE_CLIP(pGC));
+
+ RegionHelper src(pScreen);
+
+ if ((pSrc->type == DRAWABLE_WINDOW) && (pSrc->pScreen == pScreen)) {
+ box.x1 = srcx + pSrc->x;
+ box.y1 = srcy + pSrc->y;
+ box.x2 = box.x1 + w;
+ box.y2 = box.y1 + h;
+
+ src.init(&box, 0);
+ REGION_INTERSECT(pScreen, src.reg, src.reg, &((WindowPtr)pSrc)->clipList);
+ REGION_TRANSLATE(pScreen, src.reg,
+ dstx + pDst->x - srcx - pSrc->x,
+ dsty + pDst->y - srcy - pSrc->y);
+ } else {
+ src.init(NullBox, 0);
+ }
+
+ RegionHelper changed(pScreen, NullBox, 0);
+ REGION_SUBTRACT(pScreen, changed.reg, dst.reg, src.reg);
+ REGION_INTERSECT(pScreen, dst.reg, dst.reg, src.reg);
+
+ RegionPtr rgn = (*pGC->ops->CopyArea) (pSrc, pDst, pGC, srcx, srcy, w, h,
+ dstx, dsty);
+
+ if (REGION_NOTEMPTY(pScreen, dst.reg))
+ vncHooksScreen->desktop->add_copied(dst.reg,
+ dstx + pDst->x - srcx - pSrc->x,
+ dsty + pDst->y - srcy - pSrc->y);
+
+ if (REGION_NOTEMPTY(pScreen, changed.reg))
+ vncHooksScreen->desktop->add_changed(changed.reg);
+
+ return rgn;
+}
+
+
+// CopyPlane - changed region is the destination rectangle, clipped by
+// pCompositeClip
+
+static RegionPtr vncHooksCopyPlane(DrawablePtr pSrc, DrawablePtr pDst,
+ GCPtr pGC, int srcx, int srcy, int w, int h,
+ int dstx, int dsty, unsigned long plane)
+{
+ GC_OP_UNWRAPPER(pDst, pGC, CopyPlane);
+
+ BoxRec box;
+ box.x1 = dstx + pDst->x;
+ box.y1 = dsty + pDst->y;
+ box.x2 = box.x1 + w;
+ box.y2 = box.y1 + h;
+
+ RegionHelper changed(pScreen, &box, 0);
+
+ REGION_INTERSECT(pScreen, changed.reg, changed.reg, COMPOSITE_CLIP(pGC));
+
+ RegionPtr rgn = (*pGC->ops->CopyPlane) (pSrc, pDst, pGC, srcx, srcy, w, h,
+ dstx, dsty, plane);
+ vncHooksScreen->desktop->add_changed(changed.reg);
+
+ return rgn;
+}
+
+// PolyPoint - changed region is the bounding rect, clipped by pCompositeClip
+
+static void vncHooksPolyPoint(DrawablePtr pDrawable, GCPtr pGC, int mode,
+ int npt, xPoint *pts)
+{
+ GC_OP_UNWRAPPER(pDrawable, pGC, PolyPoint);
+
+ if (npt == 0) {
+ (*pGC->ops->PolyPoint) (pDrawable, pGC, mode, npt, pts);
+ return;
+ }
+
+ int minX = pts[0].x;
+ int maxX = pts[0].x;
+ int minY = pts[0].y;
+ int maxY = pts[0].y;
+
+ if (mode == CoordModePrevious) {
+ int x = pts[0].x;
+ int y = pts[0].y;
+
+ for (int i = 1; i < npt; i++) {
+ x += pts[i].x;
+ y += pts[i].y;
+ if (x < minX) minX = x;
+ if (x > maxX) maxX = x;
+ if (y < minY) minY = y;
+ if (y > maxY) maxY = y;
+ }
+ } else {
+ for (int i = 1; i < npt; i++) {
+ if (pts[i].x < minX) minX = pts[i].x;
+ if (pts[i].x > maxX) maxX = pts[i].x;
+ if (pts[i].y < minY) minY = pts[i].y;
+ if (pts[i].y > maxY) maxY = pts[i].y;
+ }
+ }
+
+ BoxRec box;
+ box.x1 = minX + pDrawable->x;
+ box.y1 = minY + pDrawable->y;
+ box.x2 = maxX + 1 + pDrawable->x;
+ box.y2 = maxY + 1 + pDrawable->y;
+
+ RegionHelper changed(pScreen, &box, 0);
+
+ REGION_INTERSECT(pScreen, changed.reg, changed.reg, COMPOSITE_CLIP(pGC));
+
+ (*pGC->ops->PolyPoint) (pDrawable, pGC, mode, npt, pts);
+
+ vncHooksScreen->desktop->add_changed(changed.reg);
+}
+
+// Polylines - changed region is the union of the bounding rects of each line,
+// clipped by pCompositeClip. If there are more than MAX_RECTS_PER_OP lines,
+// just use the bounding rect of all the lines.
+
+static void vncHooksPolylines(DrawablePtr pDrawable, GCPtr pGC, int mode,
+ int npt, DDXPointPtr ppts)
+{
+ GC_OP_UNWRAPPER(pDrawable, pGC, Polylines);
+
+ if (npt == 0) {
+ (*pGC->ops->Polylines) (pDrawable, pGC, mode, npt, ppts);
+ return;
+ }
+
+ int nRegRects = npt - 1;
+ xRectangle regRects[MAX_RECTS_PER_OP];
+
+ int lw = pGC->lineWidth;
+ if (lw == 0) lw = 1;
+
+ if (npt == 1)
+ {
+ // a single point
+ nRegRects = 1;
+ regRects[0].x = pDrawable->x + ppts[0].x - lw;
+ regRects[0].y = pDrawable->y + ppts[0].y - lw;
+ regRects[0].width = 2*lw;
+ regRects[0].height = 2*lw;
+ }
+ else
+ {
+ /*
+ * mitered joins can project quite a way from
+ * the line end; the 11 degree miter limit limits
+ * this extension to lw / (2 * tan(11/2)), rounded up
+ * and converted to int yields 6 * lw
+ */
+
+ int extra = lw / 2;
+ if (pGC->joinStyle == JoinMiter) {
+ extra = 6 * lw;
+ }
+
+ int prevX, prevY, curX, curY;
+ int rectX1, rectY1, rectX2, rectY2;
+ int minX, minY, maxX, maxY;
+
+ prevX = ppts[0].x + pDrawable->x;
+ prevY = ppts[0].y + pDrawable->y;
+ minX = maxX = prevX;
+ minY = maxY = prevY;
+
+ for (int i = 0; i < nRegRects; i++) {
+ if (mode == CoordModeOrigin) {
+ curX = pDrawable->x + ppts[i+1].x;
+ curY = pDrawable->y + ppts[i+1].y;
+ } else {
+ curX = prevX + ppts[i+1].x;
+ curY = prevY + ppts[i+1].y;
+ }
+
+ if (prevX > curX) {
+ rectX1 = curX - extra;
+ rectX2 = prevX + extra + 1;
+ } else {
+ rectX1 = prevX - extra;
+ rectX2 = curX + extra + 1;
+ }
+
+ if (prevY > curY) {
+ rectY1 = curY - extra;
+ rectY2 = prevY + extra + 1;
+ } else {
+ rectY1 = prevY - extra;
+ rectY2 = curY + extra + 1;
+ }
+
+ if (nRegRects <= MAX_RECTS_PER_OP) {
+ regRects[i].x = rectX1;
+ regRects[i].y = rectY1;
+ regRects[i].width = rectX2 - rectX1;
+ regRects[i].height = rectY2 - rectY1;
+ } else {
+ if (rectX1 < minX) minX = rectX1;
+ if (rectY1 < minY) minY = rectY1;
+ if (rectX2 > maxX) maxX = rectX2;
+ if (rectY2 > maxY) maxY = rectY2;
+ }
+
+ prevX = curX;
+ prevY = curY;
+ }
+
+ if (nRegRects > MAX_RECTS_PER_OP) {
+ regRects[0].x = minX;
+ regRects[0].y = minY;
+ regRects[0].width = maxX - minX;
+ regRects[0].height = maxY - minY;
+ nRegRects = 1;
+ }
+ }
+
+ RegionHelper changed(pScreen, nRegRects, regRects);
+
+ REGION_INTERSECT(pScreen, changed.reg, changed.reg, COMPOSITE_CLIP(pGC));
+
+ (*pGC->ops->Polylines) (pDrawable, pGC, mode, npt, ppts);
+
+ vncHooksScreen->desktop->add_changed(changed.reg);
+}
+
+// PolySegment - changed region is the union of the bounding rects of each
+// segment, clipped by pCompositeClip. If there are more than MAX_RECTS_PER_OP
+// segments, just use the bounding rect of all the segments.
+
+static void vncHooksPolySegment(DrawablePtr pDrawable, GCPtr pGC, int nseg,
+ xSegment *segs)
+{
+ GC_OP_UNWRAPPER(pDrawable, pGC, PolySegment);
+
+ if (nseg == 0) {
+ (*pGC->ops->PolySegment) (pDrawable, pGC, nseg, segs);
+ return;
+ }
+
+ xRectangle regRects[MAX_RECTS_PER_OP];
+ int nRegRects = nseg;
+
+ int lw = pGC->lineWidth;
+ int extra = lw / 2;
+
+ int rectX1, rectY1, rectX2, rectY2;
+ int minX, minY, maxX, maxY;
+
+ minX = maxX = segs[0].x1;
+ minY = maxY = segs[0].y1;
+
+ for (int i = 0; i < nseg; i++) {
+ if (segs[i].x1 > segs[i].x2) {
+ rectX1 = pDrawable->x + segs[i].x2 - extra;
+ rectX2 = pDrawable->x + segs[i].x1 + extra + 1;
+ } else {
+ rectX1 = pDrawable->x + segs[i].x1 - extra;
+ rectX2 = pDrawable->x + segs[i].x2 + extra + 1;
+ }
+
+ if (segs[i].y1 > segs[i].y2) {
+ rectY1 = pDrawable->y + segs[i].y2 - extra;
+ rectY2 = pDrawable->y + segs[i].y1 + extra + 1;
+ } else {
+ rectY1 = pDrawable->y + segs[i].y1 - extra;
+ rectY2 = pDrawable->y + segs[i].y2 + extra + 1;
+ }
+
+ if (nseg <= MAX_RECTS_PER_OP) {
+ regRects[i].x = rectX1;
+ regRects[i].y = rectY1;
+ regRects[i].width = rectX2 - rectX1;
+ regRects[i].height = rectY2 - rectY1;
+ } else {
+ if (rectX1 < minX) minX = rectX1;
+ if (rectY1 < minY) minY = rectY1;
+ if (rectX2 > maxX) maxX = rectX2;
+ if (rectY2 > maxY) maxY = rectY2;
+ }
+ }
+
+ if (nseg > MAX_RECTS_PER_OP) {
+ regRects[0].x = minX;
+ regRects[0].y = minY;
+ regRects[0].width = maxX - minX;
+ regRects[0].height = maxY - minY;
+ nRegRects = 1;
+ }
+
+ RegionHelper changed(pScreen, nRegRects, regRects);
+
+ REGION_INTERSECT(pScreen, changed.reg, changed.reg, COMPOSITE_CLIP(pGC));
+
+ (*pGC->ops->PolySegment) (pDrawable, pGC, nseg, segs);
+
+ vncHooksScreen->desktop->add_changed(changed.reg);
+}
+
+// PolyRectangle - changed region is the union of the bounding rects around
+// each side of the outline rectangles, clipped by pCompositeClip. If there
+// are more than MAX_RECTS_PER_OP rectangles, just use the bounding rect of all
+// the rectangles.
+
+static void vncHooksPolyRectangle(DrawablePtr pDrawable, GCPtr pGC, int nrects,
+ xRectangle *rects)
+{
+ GC_OP_UNWRAPPER(pDrawable, pGC, PolyRectangle);
+
+ if (nrects == 0) {
+ (*pGC->ops->PolyRectangle) (pDrawable, pGC, nrects, rects);
+ return;
+ }
+
+ xRectangle regRects[MAX_RECTS_PER_OP*4];
+ int nRegRects = nrects * 4;
+
+ int lw = pGC->lineWidth;
+ int extra = lw / 2;
+
+ int rectX1, rectY1, rectX2, rectY2;
+ int minX, minY, maxX, maxY;
+
+ minX = maxX = rects[0].x;
+ minY = maxY = rects[0].y;
+
+ for (int i = 0; i < nrects; i++) {
+ if (nrects <= MAX_RECTS_PER_OP) {
+ regRects[i*4].x = rects[i].x - extra + pDrawable->x;
+ regRects[i*4].y = rects[i].y - extra + pDrawable->y;
+ regRects[i*4].width = rects[i].width + 1 + 2 * extra;
+ regRects[i*4].height = 1 + 2 * extra;
+
+ regRects[i*4+1].x = rects[i].x - extra + pDrawable->x;
+ regRects[i*4+1].y = rects[i].y - extra + pDrawable->y;
+ regRects[i*4+1].width = 1 + 2 * extra;
+ regRects[i*4+1].height = rects[i].height + 1 + 2 * extra;
+
+ regRects[i*4+2].x = rects[i].x + rects[i].width - extra + pDrawable->x;
+ regRects[i*4+2].y = rects[i].y - extra + pDrawable->y;
+ regRects[i*4+2].width = 1 + 2 * extra;
+ regRects[i*4+2].height = rects[i].height + 1 + 2 * extra;
+
+ regRects[i*4+3].x = rects[i].x - extra + pDrawable->x;
+ regRects[i*4+3].y = rects[i].y + rects[i].height - extra + pDrawable->y;
+ regRects[i*4+3].width = rects[i].width + 1 + 2 * extra;
+ regRects[i*4+3].height = 1 + 2 * extra;
+ } else {
+ rectX1 = pDrawable->x + rects[i].x - extra;
+ rectY1 = pDrawable->y + rects[i].y - extra;
+ rectX2 = pDrawable->x + rects[i].x + rects[i].width + extra+1;
+ rectY2 = pDrawable->y + rects[i].y + rects[i].height + extra+1;
+ if (rectX1 < minX) minX = rectX1;
+ if (rectY1 < minY) minY = rectY1;
+ if (rectX2 > maxX) maxX = rectX2;
+ if (rectY2 > maxY) maxY = rectY2;
+ }
+ }
+
+ if (nrects > MAX_RECTS_PER_OP) {
+ regRects[0].x = minX;
+ regRects[0].y = minY;
+ regRects[0].width = maxX - minX;
+ regRects[0].height = maxY - minY;
+ nRegRects = 1;
+ }
+
+ RegionHelper changed(pScreen, nRegRects, regRects);
+
+ REGION_INTERSECT(pScreen, changed.reg, changed.reg, COMPOSITE_CLIP(pGC));
+
+ (*pGC->ops->PolyRectangle) (pDrawable, pGC, nrects, rects);
+
+ vncHooksScreen->desktop->add_changed(changed.reg);
+}
+
+// PolyArc - changed region is the union of bounding rects around each arc,
+// clipped by pCompositeClip. If there are more than MAX_RECTS_PER_OP
+// arcs, just use the bounding rect of all the arcs.
+
+static void vncHooksPolyArc(DrawablePtr pDrawable, GCPtr pGC, int narcs,
+ xArc *arcs)
+{
+ GC_OP_UNWRAPPER(pDrawable, pGC, PolyArc);
+
+ if (narcs == 0) {
+ (*pGC->ops->PolyArc) (pDrawable, pGC, narcs, arcs);
+ return;
+ }
+
+ xRectangle regRects[MAX_RECTS_PER_OP];
+ int nRegRects = narcs;
+
+ int lw = pGC->lineWidth;
+ if (lw == 0) lw = 1;
+ int extra = lw / 2;
+
+ int rectX1, rectY1, rectX2, rectY2;
+ int minX, minY, maxX, maxY;
+
+ minX = maxX = arcs[0].x;
+ minY = maxY = arcs[0].y;
+
+ for (int i = 0; i < narcs; i++) {
+ if (narcs <= MAX_RECTS_PER_OP) {
+ regRects[i].x = arcs[i].x - extra + pDrawable->x;
+ regRects[i].y = arcs[i].y - extra + pDrawable->y;
+ regRects[i].width = arcs[i].width + lw;
+ regRects[i].height = arcs[i].height + lw;
+ } else {
+ rectX1 = pDrawable->x + arcs[i].x - extra;
+ rectY1 = pDrawable->y + arcs[i].y - extra;
+ rectX2 = pDrawable->x + arcs[i].x + arcs[i].width + lw;
+ rectY2 = pDrawable->y + arcs[i].y + arcs[i].height + lw;
+ if (rectX1 < minX) minX = rectX1;
+ if (rectY1 < minY) minY = rectY1;
+ if (rectX2 > maxX) maxX = rectX2;
+ if (rectY2 > maxY) maxY = rectY2;
+ }
+ }
+
+ if (narcs > MAX_RECTS_PER_OP) {
+ regRects[0].x = minX;
+ regRects[0].y = minY;
+ regRects[0].width = maxX - minX;
+ regRects[0].height = maxY - minY;
+ nRegRects = 1;
+ }
+
+ RegionHelper changed(pScreen, nRegRects, regRects);
+
+ REGION_INTERSECT(pScreen, changed.reg, changed.reg, COMPOSITE_CLIP(pGC));
+
+ (*pGC->ops->PolyArc) (pDrawable, pGC, narcs, arcs);
+
+ vncHooksScreen->desktop->add_changed(changed.reg);
+}
+
+
+// FillPolygon - changed region is the bounding rect around the polygon,
+// clipped by pCompositeClip
+
+static void vncHooksFillPolygon(DrawablePtr pDrawable, GCPtr pGC, int shape,
+ int mode, int count, DDXPointPtr pts)
+{
+ GC_OP_UNWRAPPER(pDrawable, pGC, FillPolygon);
+
+ if (count == 0) {
+ (*pGC->ops->FillPolygon) (pDrawable, pGC, shape, mode, count, pts);
+ return;
+ }
+
+ int minX = pts[0].x;
+ int maxX = pts[0].x;
+ int minY = pts[0].y;
+ int maxY = pts[0].y;
+
+ if (mode == CoordModePrevious) {
+ int x = pts[0].x;
+ int y = pts[0].y;
+
+ for (int i = 1; i < count; i++) {
+ x += pts[i].x;
+ y += pts[i].y;
+ if (x < minX) minX = x;
+ if (x > maxX) maxX = x;
+ if (y < minY) minY = y;
+ if (y > maxY) maxY = y;
+ }
+ } else {
+ for (int i = 1; i < count; i++) {
+ if (pts[i].x < minX) minX = pts[i].x;
+ if (pts[i].x > maxX) maxX = pts[i].x;
+ if (pts[i].y < minY) minY = pts[i].y;
+ if (pts[i].y > maxY) maxY = pts[i].y;
+ }
+ }
+
+ BoxRec box;
+ box.x1 = minX + pDrawable->x;
+ box.y1 = minY + pDrawable->y;
+ box.x2 = maxX + 1 + pDrawable->x;
+ box.y2 = maxY + 1 + pDrawable->y;
+
+ RegionHelper changed(pScreen, &box, 0);
+
+ REGION_INTERSECT(pScreen, changed.reg, changed.reg, COMPOSITE_CLIP(pGC));
+
+ (*pGC->ops->FillPolygon) (pDrawable, pGC, shape, mode, count, pts);
+
+ vncHooksScreen->desktop->add_changed(changed.reg);
+}
+
+// PolyFillRect - changed region is the union of the rectangles, clipped by
+// pCompositeClip. If there are more than MAX_RECTS_PER_OP rectangles, just
+// use the bounding rect of all the rectangles.
+
+static void vncHooksPolyFillRect(DrawablePtr pDrawable, GCPtr pGC, int nrects,
+ xRectangle *rects)
+{
+ GC_OP_UNWRAPPER(pDrawable, pGC, PolyFillRect);
+
+ if (nrects == 0) {
+ (*pGC->ops->PolyFillRect) (pDrawable, pGC, nrects, rects);
+ return;
+ }
+
+ xRectangle regRects[MAX_RECTS_PER_OP];
+ int nRegRects = nrects;
+ int rectX1, rectY1, rectX2, rectY2;
+ int minX, minY, maxX, maxY;
+ minX = maxX = rects[0].x;
+ minY = maxY = rects[0].y;
+
+ for (int i = 0; i < nrects; i++) {
+ if (nrects <= MAX_RECTS_PER_OP) {
+ regRects[i].x = rects[i].x + pDrawable->x;
+ regRects[i].y = rects[i].y + pDrawable->y;
+ regRects[i].width = rects[i].width;
+ regRects[i].height = rects[i].height;
+ } else {
+ rectX1 = pDrawable->x + rects[i].x;
+ rectY1 = pDrawable->y + rects[i].y;
+ rectX2 = pDrawable->x + rects[i].x + rects[i].width;
+ rectY2 = pDrawable->y + rects[i].y + rects[i].height;
+ if (rectX1 < minX) minX = rectX1;
+ if (rectY1 < minY) minY = rectY1;
+ if (rectX2 > maxX) maxX = rectX2;
+ if (rectY2 > maxY) maxY = rectY2;
+ }
+ }
+
+ if (nrects > MAX_RECTS_PER_OP) {
+ regRects[0].x = minX;
+ regRects[0].y = minY;
+ regRects[0].width = maxX - minX;
+ regRects[0].height = maxY - minY;
+ nRegRects = 1;
+ }
+
+ RegionHelper changed(pScreen, nRegRects, regRects);
+
+ REGION_INTERSECT(pScreen, changed.reg, changed.reg, COMPOSITE_CLIP(pGC));
+
+ (*pGC->ops->PolyFillRect) (pDrawable, pGC, nrects, rects);
+
+ vncHooksScreen->desktop->add_changed(changed.reg);
+}
+
+// PolyFillArc - changed region is the union of bounding rects around each arc,
+// clipped by pCompositeClip. If there are more than MAX_RECTS_PER_OP arcs,
+// just use the bounding rect of all the arcs.
+
+static void vncHooksPolyFillArc(DrawablePtr pDrawable, GCPtr pGC, int narcs,
+ xArc *arcs)
+{
+ GC_OP_UNWRAPPER(pDrawable, pGC, PolyFillArc);
+
+ if (narcs == 0) {
+ (*pGC->ops->PolyFillArc) (pDrawable, pGC, narcs, arcs);
+ return;
+ }
+
+ xRectangle regRects[MAX_RECTS_PER_OP];
+ int nRegRects = narcs;
+
+ int lw = pGC->lineWidth;
+ if (lw == 0) lw = 1;
+ int extra = lw / 2;
+
+ int rectX1, rectY1, rectX2, rectY2;
+ int minX, minY, maxX, maxY;
+
+ minX = maxX = arcs[0].x;
+ minY = maxY = arcs[0].y;
+
+ for (int i = 0; i < narcs; i++) {
+ if (narcs <= MAX_RECTS_PER_OP) {
+ regRects[i].x = arcs[i].x - extra + pDrawable->x;
+ regRects[i].y = arcs[i].y - extra + pDrawable->y;
+ regRects[i].width = arcs[i].width + lw;
+ regRects[i].height = arcs[i].height + lw;
+ } else {
+ rectX1 = pDrawable->x + arcs[i].x - extra;
+ rectY1 = pDrawable->y + arcs[i].y - extra;
+ rectX2 = pDrawable->x + arcs[i].x + arcs[i].width + lw;
+ rectY2 = pDrawable->y + arcs[i].y + arcs[i].height + lw;
+ if (rectX1 < minX) minX = rectX1;
+ if (rectY1 < minY) minY = rectY1;
+ if (rectX2 > maxX) maxX = rectX2;
+ if (rectY2 > maxY) maxY = rectY2;
+ }
+ }
+
+ if (narcs > MAX_RECTS_PER_OP) {
+ regRects[0].x = minX;
+ regRects[0].y = minY;
+ regRects[0].width = maxX - minX;
+ regRects[0].height = maxY - minY;
+ nRegRects = 1;
+ }
+
+ RegionHelper changed(pScreen, nRegRects, regRects);
+
+ REGION_INTERSECT(pScreen, changed.reg, changed.reg, COMPOSITE_CLIP(pGC));
+
+ (*pGC->ops->PolyFillArc) (pDrawable, pGC, narcs, arcs);
+
+ vncHooksScreen->desktop->add_changed(changed.reg);
+}
+
+// GetTextBoundingRect - calculate a bounding rectangle around n chars of a
+// font. Not particularly accurate, but good enough.
+
+static void GetTextBoundingRect(DrawablePtr pDrawable, FontPtr font, int x,
+ int y, int nchars, BoxPtr box)
+{
+ int ascent = max(FONTASCENT(font), FONTMAXBOUNDS(font, ascent));
+ int descent = max(FONTDESCENT(font), FONTMAXBOUNDS(font, descent));
+ int charWidth = max(FONTMAXBOUNDS(font,rightSideBearing),
+ FONTMAXBOUNDS(font,characterWidth));
+
+ box->x1 = pDrawable->x + x;
+ box->y1 = pDrawable->y + y - ascent;
+ box->x2 = box->x1 + charWidth * nchars;
+ box->y2 = box->y1 + ascent + descent;
+
+ if (FONTMINBOUNDS(font,leftSideBearing) < 0)
+ box->x1 += FONTMINBOUNDS(font,leftSideBearing);
+}
+
+// PolyText8 - changed region is bounding rect around count chars, clipped by
+// pCompositeClip
+
+static int vncHooksPolyText8(DrawablePtr pDrawable, GCPtr pGC, int x, int y,
+ int count, char *chars)
+{
+ GC_OP_UNWRAPPER(pDrawable, pGC, PolyText8);
+
+ if (count == 0)
+ return (*pGC->ops->PolyText8) (pDrawable, pGC, x, y, count, chars);
+
+ BoxRec box;
+ GetTextBoundingRect(pDrawable, pGC->font, x, y, count, &box);
+
+ RegionHelper changed(pScreen, &box, 0);
+
+ REGION_INTERSECT(pScreen, changed.reg, changed.reg, COMPOSITE_CLIP(pGC));
+
+ int ret = (*pGC->ops->PolyText8) (pDrawable, pGC, x, y, count, chars);
+
+ vncHooksScreen->desktop->add_changed(changed.reg);
+
+ return ret;
+}
+
+// PolyText16 - changed region is bounding rect around count chars, clipped by
+// pCompositeClip
+
+static int vncHooksPolyText16(DrawablePtr pDrawable, GCPtr pGC, int x, int y,
+ int count, unsigned short *chars)
+{
+ GC_OP_UNWRAPPER(pDrawable, pGC, PolyText16);
+
+ if (count == 0)
+ return (*pGC->ops->PolyText16) (pDrawable, pGC, x, y, count, chars);
+
+ BoxRec box;
+ GetTextBoundingRect(pDrawable, pGC->font, x, y, count, &box);
+
+ RegionHelper changed(pScreen, &box, 0);
+
+ REGION_INTERSECT(pScreen, changed.reg, changed.reg, COMPOSITE_CLIP(pGC));
+
+ int ret = (*pGC->ops->PolyText16) (pDrawable, pGC, x, y, count, chars);
+
+ vncHooksScreen->desktop->add_changed(changed.reg);
+
+ return ret;
+}
+
+// ImageText8 - changed region is bounding rect around count chars, clipped by
+// pCompositeClip
+
+static void vncHooksImageText8(DrawablePtr pDrawable, GCPtr pGC, int x, int y,
+ int count, char *chars)
+{
+ GC_OP_UNWRAPPER(pDrawable, pGC, ImageText8);
+
+ if (count == 0) {
+ (*pGC->ops->ImageText8) (pDrawable, pGC, x, y, count, chars);
+ return;
+ }
+
+ BoxRec box;
+ GetTextBoundingRect(pDrawable, pGC->font, x, y, count, &box);
+
+ RegionHelper changed(pScreen, &box, 0);
+
+ REGION_INTERSECT(pScreen, changed.reg, changed.reg, COMPOSITE_CLIP(pGC));
+
+ (*pGC->ops->ImageText8) (pDrawable, pGC, x, y, count, chars);
+
+ vncHooksScreen->desktop->add_changed(changed.reg);
+}
+
+// ImageText16 - changed region is bounding rect around count chars, clipped by
+// pCompositeClip
+
+static void vncHooksImageText16(DrawablePtr pDrawable, GCPtr pGC, int x, int y,
+ int count, unsigned short *chars)
+{
+ GC_OP_UNWRAPPER(pDrawable, pGC, ImageText16);
+
+ if (count == 0) {
+ (*pGC->ops->ImageText16) (pDrawable, pGC, x, y, count, chars);
+ return;
+ }
+
+ BoxRec box;
+ GetTextBoundingRect(pDrawable, pGC->font, x, y, count, &box);
+
+ RegionHelper changed(pScreen, &box, 0);
+
+ REGION_INTERSECT(pScreen, changed.reg, changed.reg, COMPOSITE_CLIP(pGC));
+
+ (*pGC->ops->ImageText16) (pDrawable, pGC, x, y, count, chars);
+
+ vncHooksScreen->desktop->add_changed(changed.reg);
+}
+
+// ImageGlyphBlt - changed region is bounding rect around nglyph chars, clipped
+// by pCompositeClip
+
+static void vncHooksImageGlyphBlt(DrawablePtr pDrawable, GCPtr pGC, int x,
+ int y, unsigned int nglyph,
+ CharInfoPtr *ppci, pointer pglyphBase)
+{
+ GC_OP_UNWRAPPER(pDrawable, pGC, ImageGlyphBlt);
+
+ if (nglyph == 0) {
+ (*pGC->ops->ImageGlyphBlt) (pDrawable, pGC, x, y, nglyph, ppci,pglyphBase);
+ return;
+ }
+
+ BoxRec box;
+ GetTextBoundingRect(pDrawable, pGC->font, x, y, nglyph, &box);
+
+ RegionHelper changed(pScreen, &box, 0);
+
+ REGION_INTERSECT(pScreen, changed.reg, changed.reg, COMPOSITE_CLIP(pGC));
+
+ (*pGC->ops->ImageGlyphBlt) (pDrawable, pGC, x, y, nglyph, ppci, pglyphBase);
+
+ vncHooksScreen->desktop->add_changed(changed.reg);
+}
+
+// PolyGlyphBlt - changed region is bounding rect around nglyph chars, clipped
+// by pCompositeClip
+
+static void vncHooksPolyGlyphBlt(DrawablePtr pDrawable, GCPtr pGC, int x,
+ int y, unsigned int nglyph,
+ CharInfoPtr *ppci, pointer pglyphBase)
+{
+ GC_OP_UNWRAPPER(pDrawable, pGC, PolyGlyphBlt);
+
+ if (nglyph == 0) {
+ (*pGC->ops->PolyGlyphBlt) (pDrawable, pGC, x, y, nglyph, ppci,pglyphBase);
+ return;
+ }
+
+ BoxRec box;
+ GetTextBoundingRect(pDrawable, pGC->font, x, y, nglyph, &box);
+
+ RegionHelper changed(pScreen, &box, 0);
+
+ REGION_INTERSECT(pScreen, changed.reg, changed.reg, COMPOSITE_CLIP(pGC));
+
+ (*pGC->ops->PolyGlyphBlt) (pDrawable, pGC, x, y, nglyph, ppci, pglyphBase);
+
+ vncHooksScreen->desktop->add_changed(changed.reg);
+}
+
+// PushPixels - changed region is the given rectangle, clipped by
+// pCompositeClip
+
+static void vncHooksPushPixels(GCPtr pGC, PixmapPtr pBitMap,
+ DrawablePtr pDrawable, int w, int h, int x,
+ int y)
+{
+ GC_OP_UNWRAPPER(pDrawable, pGC, PushPixels);
+
+ BoxRec box;
+ box.x1 = x + pDrawable->x;
+ box.y1 = y + pDrawable->y;
+ box.x2 = box.x1 + w;
+ box.y2 = box.y1 + h;
+
+ RegionHelper changed(pScreen, &box, 0);
+
+ REGION_INTERSECT(pScreen, changed.reg, changed.reg, COMPOSITE_CLIP(pGC));
+
+ (*pGC->ops->PushPixels) (pGC, pBitMap, pDrawable, w, h, x, y);
+
+ vncHooksScreen->desktop->add_changed(changed.reg);
+}
diff --git a/xc/programs/Xserver/vnc/vncHooks.h b/xc/programs/Xserver/vnc/vncHooks.h
new file mode 100644
index 00000000..c2ca8255
--- /dev/null
+++ b/xc/programs/Xserver/vnc/vncHooks.h
@@ -0,0 +1,26 @@
+/* Copyright (C) 2002-2003 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 __VNCHOOKS_H__
+#define __VNCHOOKS_H__
+
+extern "C" {
+#include <screenint.h>
+ extern Bool vncHooksInit(ScreenPtr pScreen, XserverDesktop* desktop);
+}
+
+#endif
diff --git a/xc/programs/Xserver/vnc/xf86vncModule.cc b/xc/programs/Xserver/vnc/xf86vncModule.cc
new file mode 100644
index 00000000..3504c6a6
--- /dev/null
+++ b/xc/programs/Xserver/vnc/xf86vncModule.cc
@@ -0,0 +1,96 @@
+/* Copyright (C) 2002-2003 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.
+ */
+/* This is the xf86 module code for the vnc extension.
+ */
+
+#include <rfb/Configuration.h>
+#include <rfb/Logger_stdio.h>
+#include <rfb/LogWriter.h>
+
+extern "C" {
+#define class c_class
+#define private c_private
+#define bool c_bool
+#define new c_new
+#include "xf86.h"
+#include "xf86Module.h"
+#undef class
+#undef private
+#undef bool
+#undef new
+
+extern void vncExtensionInit();
+static void vncExtensionInitWithParams(INITARGS);
+
+#ifdef XFree86LOADER
+
+static MODULESETUPPROTO(vncSetup);
+
+ExtensionModule vncExt =
+{
+ vncExtensionInitWithParams,
+ "VNC",
+ NULL,
+ NULL,
+ NULL
+};
+
+static XF86ModuleVersionInfo vncVersRec =
+{
+ "vnc",
+ "RealVNC Ltd",
+ MODINFOSTRING1,
+ MODINFOSTRING2,
+ XF86_VERSION_CURRENT,
+ 1, 0, 0,
+ ABI_CLASS_EXTENSION, /* needs the server extension ABI */
+ ABI_EXTENSION_VERSION,
+ MOD_CLASS_EXTENSION,
+ {0,0,0,0}
+};
+
+XF86ModuleData vncModuleData = { &vncVersRec, vncSetup, NULL };
+
+static pointer
+vncSetup(pointer module, pointer opts, int *errmaj, int *errmin) {
+ LoadExtension(&vncExt, FALSE);
+ /* Need a non-NULL return value to indicate success */
+ return (pointer)1;
+}
+
+static void vncExtensionInitWithParams(INITARGS)
+{
+ rfb::initStdIOLoggers();
+ rfb::LogWriter::setLogParams("*:stderr:30");
+
+ for (int scr = 0; scr < screenInfo.numScreens; scr++) {
+ ScrnInfoPtr pScrn = xf86Screens[scr];
+
+ rfb::VoidParameter* p;
+ for (p = rfb::Configuration::head; p; p = p->_next) {
+ char* val = xf86FindOptionValue(pScrn->options, p->getName());
+ if (val)
+ p->setParam(val);
+ }
+ }
+
+ vncExtensionInit();
+}
+
+#endif /* XFree86LOADER */
+}
diff --git a/zlib/ChangeLog b/zlib/ChangeLog
new file mode 100644
index 00000000..eded33d4
--- /dev/null
+++ b/zlib/ChangeLog
@@ -0,0 +1,481 @@
+
+ ChangeLog file for zlib
+
+Changes in 1.1.4 (11 March 2002)
+- ZFREE was repeated on same allocation on some error conditions.
+ This creates a security problem described in
+ http://www.zlib.org/advisory-2002-03-11.txt
+- Returned incorrect error (Z_MEM_ERROR) on some invalid data
+- Avoid accesses before window for invalid distances with inflate window
+ less than 32K.
+- force windowBits > 8 to avoid a bug in the encoder for a window size
+ of 256 bytes. (A complete fix will be available in 1.1.5).
+
+Changes in 1.1.3 (9 July 1998)
+- fix "an inflate input buffer bug that shows up on rare but persistent
+ occasions" (Mark)
+- fix gzread and gztell for concatenated .gz files (Didier Le Botlan)
+- fix gzseek(..., SEEK_SET) in write mode
+- fix crc check after a gzeek (Frank Faubert)
+- fix miniunzip when the last entry in a zip file is itself a zip file
+ (J Lillge)
+- add contrib/asm586 and contrib/asm686 (Brian Raiter)
+ See http://www.muppetlabs.com/~breadbox/software/assembly.html
+- add support for Delphi 3 in contrib/delphi (Bob Dellaca)
+- add support for C++Builder 3 and Delphi 3 in contrib/delphi2 (Davide Moretti)
+- do not exit prematurely in untgz if 0 at start of block (Magnus Holmgren)
+- use macro EXTERN instead of extern to support DLL for BeOS (Sander Stoks)
+- added a FAQ file
+
+- Support gzdopen on Mac with Metrowerks (Jason Linhart)
+- Do not redefine Byte on Mac (Brad Pettit & Jason Linhart)
+- define SEEK_END too if SEEK_SET is not defined (Albert Chin-A-Young)
+- avoid some warnings with Borland C (Tom Tanner)
+- fix a problem in contrib/minizip/zip.c for 16-bit MSDOS (Gilles Vollant)
+- emulate utime() for WIN32 in contrib/untgz (Gilles Vollant)
+- allow several arguments to configure (Tim Mooney, Frodo Looijaard)
+- use libdir and includedir in Makefile.in (Tim Mooney)
+- support shared libraries on OSF1 V4 (Tim Mooney)
+- remove so_locations in "make clean" (Tim Mooney)
+- fix maketree.c compilation error (Glenn, Mark)
+- Python interface to zlib now in Python 1.5 (Jeremy Hylton)
+- new Makefile.riscos (Rich Walker)
+- initialize static descriptors in trees.c for embedded targets (Nick Smith)
+- use "foo-gz" in example.c for RISCOS and VMS (Nick Smith)
+- add the OS/2 files in Makefile.in too (Andrew Zabolotny)
+- fix fdopen and halloc macros for Microsoft C 6.0 (Tom Lane)
+- fix maketree.c to allow clean compilation of inffixed.h (Mark)
+- fix parameter check in deflateCopy (Gunther Nikl)
+- cleanup trees.c, use compressed_len only in debug mode (Christian Spieler)
+- Many portability patches by Christian Spieler:
+ . zutil.c, zutil.h: added "const" for zmem*
+ . Make_vms.com: fixed some typos
+ . Make_vms.com: msdos/Makefile.*: removed zutil.h from some dependency lists
+ . msdos/Makefile.msc: remove "default rtl link library" info from obj files
+ . msdos/Makefile.*: use model-dependent name for the built zlib library
+ . msdos/Makefile.emx, nt/Makefile.emx, nt/Makefile.gcc:
+ new makefiles, for emx (DOS/OS2), emx&rsxnt and mingw32 (Windows 9x / NT)
+- use define instead of typedef for Bytef also for MSC small/medium (Tom Lane)
+- replace __far with _far for better portability (Christian Spieler, Tom Lane)
+- fix test for errno.h in configure (Tim Newsham)
+
+Changes in 1.1.2 (19 March 98)
+- added contrib/minzip, mini zip and unzip based on zlib (Gilles Vollant)
+ See http://www.winimage.com/zLibDll/unzip.html
+- preinitialize the inflate tables for fixed codes, to make the code
+ completely thread safe (Mark)
+- some simplifications and slight speed-up to the inflate code (Mark)
+- fix gzeof on non-compressed files (Allan Schrum)
+- add -std1 option in configure for OSF1 to fix gzprintf (Martin Mokrejs)
+- use default value of 4K for Z_BUFSIZE for 16-bit MSDOS (Tim Wegner + Glenn)
+- added os2/Makefile.def and os2/zlib.def (Andrew Zabolotny)
+- add shared lib support for UNIX_SV4.2MP (MATSUURA Takanori)
+- do not wrap extern "C" around system includes (Tom Lane)
+- mention zlib binding for TCL in README (Andreas Kupries)
+- added amiga/Makefile.pup for Amiga powerUP SAS/C PPC (Andreas Kleinert)
+- allow "make install prefix=..." even after configure (Glenn Randers-Pehrson)
+- allow "configure --prefix $HOME" (Tim Mooney)
+- remove warnings in example.c and gzio.c (Glenn Randers-Pehrson)
+- move Makefile.sas to amiga/Makefile.sas
+
+Changes in 1.1.1 (27 Feb 98)
+- fix macros _tr_tally_* in deflate.h for debug mode (Glenn Randers-Pehrson)
+- remove block truncation heuristic which had very marginal effect for zlib
+ (smaller lit_bufsize than in gzip 1.2.4) and degraded a little the
+ compression ratio on some files. This also allows inlining _tr_tally for
+ matches in deflate_slow.
+- added msdos/Makefile.w32 for WIN32 Microsoft Visual C++ (Bob Frazier)
+
+Changes in 1.1.0 (24 Feb 98)
+- do not return STREAM_END prematurely in inflate (John Bowler)
+- revert to the zlib 1.0.8 inflate to avoid the gcc 2.8.0 bug (Jeremy Buhler)
+- compile with -DFASTEST to get compression code optimized for speed only
+- in minigzip, try mmap'ing the input file first (Miguel Albrecht)
+- increase size of I/O buffers in minigzip.c and gzio.c (not a big gain
+ on Sun but significant on HP)
+
+- add a pointer to experimental unzip library in README (Gilles Vollant)
+- initialize variable gcc in configure (Chris Herborth)
+
+Changes in 1.0.9 (17 Feb 1998)
+- added gzputs and gzgets functions
+- do not clear eof flag in gzseek (Mark Diekhans)
+- fix gzseek for files in transparent mode (Mark Diekhans)
+- do not assume that vsprintf returns the number of bytes written (Jens Krinke)
+- replace EXPORT with ZEXPORT to avoid conflict with other programs
+- added compress2 in zconf.h, zlib.def, zlib.dnt
+- new asm code from Gilles Vollant in contrib/asm386
+- simplify the inflate code (Mark):
+ . Replace ZALLOC's in huft_build() with single ZALLOC in inflate_blocks_new()
+ . ZALLOC the length list in inflate_trees_fixed() instead of using stack
+ . ZALLOC the value area for huft_build() instead of using stack
+ . Simplify Z_FINISH check in inflate()
+
+- Avoid gcc 2.8.0 comparison bug a little differently than zlib 1.0.8
+- in inftrees.c, avoid cc -O bug on HP (Farshid Elahi)
+- in zconf.h move the ZLIB_DLL stuff earlier to avoid problems with
+ the declaration of FAR (Gilles VOllant)
+- install libz.so* with mode 755 (executable) instead of 644 (Marc Lehmann)
+- read_buf buf parameter of type Bytef* instead of charf*
+- zmemcpy parameters are of type Bytef*, not charf* (Joseph Strout)
+- do not redeclare unlink in minigzip.c for WIN32 (John Bowler)
+- fix check for presence of directories in "make install" (Ian Willis)
+
+Changes in 1.0.8 (27 Jan 1998)
+- fixed offsets in contrib/asm386/gvmat32.asm (Gilles Vollant)
+- fix gzgetc and gzputc for big endian systems (Markus Oberhumer)
+- added compress2() to allow setting the compression level
+- include sys/types.h to get off_t on some systems (Marc Lehmann & QingLong)
+- use constant arrays for the static trees in trees.c instead of computing
+ them at run time (thanks to Ken Raeburn for this suggestion). To create
+ trees.h, compile with GEN_TREES_H and run "make test".
+- check return code of example in "make test" and display result
+- pass minigzip command line options to file_compress
+- simplifying code of inflateSync to avoid gcc 2.8 bug
+
+- support CC="gcc -Wall" in configure -s (QingLong)
+- avoid a flush caused by ftell in gzopen for write mode (Ken Raeburn)
+- fix test for shared library support to avoid compiler warnings
+- zlib.lib -> zlib.dll in msdos/zlib.rc (Gilles Vollant)
+- check for TARGET_OS_MAC in addition to MACOS (Brad Pettit)
+- do not use fdopen for Metrowerks on Mac (Brad Pettit))
+- add checks for gzputc and gzputc in example.c
+- avoid warnings in gzio.c and deflate.c (Andreas Kleinert)
+- use const for the CRC table (Ken Raeburn)
+- fixed "make uninstall" for shared libraries
+- use Tracev instead of Trace in infblock.c
+- in example.c use correct compressed length for test_sync
+- suppress +vnocompatwarnings in configure for HPUX (not always supported)
+
+Changes in 1.0.7 (20 Jan 1998)
+- fix gzseek which was broken in write mode
+- return error for gzseek to negative absolute position
+- fix configure for Linux (Chun-Chung Chen)
+- increase stack space for MSC (Tim Wegner)
+- get_crc_table and inflateSyncPoint are EXPORTed (Gilles Vollant)
+- define EXPORTVA for gzprintf (Gilles Vollant)
+- added man page zlib.3 (Rick Rodgers)
+- for contrib/untgz, fix makedir() and improve Makefile
+
+- check gzseek in write mode in example.c
+- allocate extra buffer for seeks only if gzseek is actually called
+- avoid signed/unsigned comparisons (Tim Wegner, Gilles Vollant)
+- add inflateSyncPoint in zconf.h
+- fix list of exported functions in nt/zlib.dnt and mdsos/zlib.def
+
+Changes in 1.0.6 (19 Jan 1998)
+- add functions gzprintf, gzputc, gzgetc, gztell, gzeof, gzseek, gzrewind and
+ gzsetparams (thanks to Roland Giersig and Kevin Ruland for some of this code)
+- Fix a deflate bug occuring only with compression level 0 (thanks to
+ Andy Buckler for finding this one).
+- In minigzip, pass transparently also the first byte for .Z files.
+- return Z_BUF_ERROR instead of Z_OK if output buffer full in uncompress()
+- check Z_FINISH in inflate (thanks to Marc Schluper)
+- Implement deflateCopy (thanks to Adam Costello)
+- make static libraries by default in configure, add --shared option.
+- move MSDOS or Windows specific files to directory msdos
+- suppress the notion of partial flush to simplify the interface
+ (but the symbol Z_PARTIAL_FLUSH is kept for compatibility with 1.0.4)
+- suppress history buffer provided by application to simplify the interface
+ (this feature was not implemented anyway in 1.0.4)
+- next_in and avail_in must be initialized before calling inflateInit or
+ inflateInit2
+- add EXPORT in all exported functions (for Windows DLL)
+- added Makefile.nt (thanks to Stephen Williams)
+- added the unsupported "contrib" directory:
+ contrib/asm386/ by Gilles Vollant <info@winimage.com>
+ 386 asm code replacing longest_match().
+ contrib/iostream/ by Kevin Ruland <kevin@rodin.wustl.edu>
+ A C++ I/O streams interface to the zlib gz* functions
+ contrib/iostream2/ by Tyge Løvset <Tyge.Lovset@cmr.no>
+ Another C++ I/O streams interface
+ contrib/untgz/ by "Pedro A. Aranda Guti\irrez" <paag@tid.es>
+ A very simple tar.gz file extractor using zlib
+ contrib/visual-basic.txt by Carlos Rios <c_rios@sonda.cl>
+ How to use compress(), uncompress() and the gz* functions from VB.
+- pass params -f (filtered data), -h (huffman only), -1 to -9 (compression
+ level) in minigzip (thanks to Tom Lane)
+
+- use const for rommable constants in deflate
+- added test for gzseek and gztell in example.c
+- add undocumented function inflateSyncPoint() (hack for Paul Mackerras)
+- add undocumented function zError to convert error code to string
+ (for Tim Smithers)
+- Allow compilation of gzio with -DNO_DEFLATE to avoid the compression code.
+- Use default memcpy for Symantec MSDOS compiler.
+- Add EXPORT keyword for check_func (needed for Windows DLL)
+- add current directory to LD_LIBRARY_PATH for "make test"
+- create also a link for libz.so.1
+- added support for FUJITSU UXP/DS (thanks to Toshiaki Nomura)
+- use $(SHAREDLIB) instead of libz.so in Makefile.in (for HPUX)
+- added -soname for Linux in configure (Chun-Chung Chen,
+- assign numbers to the exported functions in zlib.def (for Windows DLL)
+- add advice in zlib.h for best usage of deflateSetDictionary
+- work around compiler bug on Atari (cast Z_NULL in call of s->checkfn)
+- allow compilation with ANSI keywords only enabled for TurboC in large model
+- avoid "versionString"[0] (Borland bug)
+- add NEED_DUMMY_RETURN for Borland
+- use variable z_verbose for tracing in debug mode (L. Peter Deutsch).
+- allow compilation with CC
+- defined STDC for OS/2 (David Charlap)
+- limit external names to 8 chars for MVS (Thomas Lund)
+- in minigzip.c, use static buffers only for 16-bit systems
+- fix suffix check for "minigzip -d foo.gz"
+- do not return an error for the 2nd of two consecutive gzflush() (Felix Lee)
+- use _fdopen instead of fdopen for MSC >= 6.0 (Thomas Fanslau)
+- added makelcc.bat for lcc-win32 (Tom St Denis)
+- in Makefile.dj2, use copy and del instead of install and rm (Frank Donahoe)
+- Avoid expanded $Id: ChangeLog,v 1.1 2004/10/08 09:44:20 const_k Exp $. Use "rcs -kb" or "cvs admin -kb" to avoid Id expansion.
+- check for unistd.h in configure (for off_t)
+- remove useless check parameter in inflate_blocks_free
+- avoid useless assignment of s->check to itself in inflate_blocks_new
+- do not flush twice in gzclose (thanks to Ken Raeburn)
+- rename FOPEN as F_OPEN to avoid clash with /usr/include/sys/file.h
+- use NO_ERRNO_H instead of enumeration of operating systems with errno.h
+- work around buggy fclose on pipes for HP/UX
+- support zlib DLL with BORLAND C++ 5.0 (thanks to Glenn Randers-Pehrson)
+- fix configure if CC is already equal to gcc
+
+Changes in 1.0.5 (3 Jan 98)
+- Fix inflate to terminate gracefully when fed corrupted or invalid data
+- Use const for rommable constants in inflate
+- Eliminate memory leaks on error conditions in inflate
+- Removed some vestigial code in inflate
+- Update web address in README
+
+Changes in 1.0.4 (24 Jul 96)
+- In very rare conditions, deflate(s, Z_FINISH) could fail to produce an EOF
+ bit, so the decompressor could decompress all the correct data but went
+ on to attempt decompressing extra garbage data. This affected minigzip too.
+- zlibVersion and gzerror return const char* (needed for DLL)
+- port to RISCOS (no fdopen, no multiple dots, no unlink, no fileno)
+- use z_error only for DEBUG (avoid problem with DLLs)
+
+Changes in 1.0.3 (2 Jul 96)
+- use z_streamp instead of z_stream *, which is now a far pointer in MSDOS
+ small and medium models; this makes the library incompatible with previous
+ versions for these models. (No effect in large model or on other systems.)
+- return OK instead of BUF_ERROR if previous deflate call returned with
+ avail_out as zero but there is nothing to do
+- added memcmp for non STDC compilers
+- define NO_DUMMY_DECL for more Mac compilers (.h files merged incorrectly)
+- define __32BIT__ if __386__ or i386 is defined (pb. with Watcom and SCO)
+- better check for 16-bit mode MSC (avoids problem with Symantec)
+
+Changes in 1.0.2 (23 May 96)
+- added Windows DLL support
+- added a function zlibVersion (for the DLL support)
+- fixed declarations using Bytef in infutil.c (pb with MSDOS medium model)
+- Bytef is define's instead of typedef'd only for Borland C
+- avoid reading uninitialized memory in example.c
+- mention in README that the zlib format is now RFC1950
+- updated Makefile.dj2
+- added algorithm.doc
+
+Changes in 1.0.1 (20 May 96) [1.0 skipped to avoid confusion]
+- fix array overlay in deflate.c which sometimes caused bad compressed data
+- fix inflate bug with empty stored block
+- fix MSDOS medium model which was broken in 0.99
+- fix deflateParams() which could generated bad compressed data.
+- Bytef is define'd instead of typedef'ed (work around Borland bug)
+- added an INDEX file
+- new makefiles for DJGPP (Makefile.dj2), 32-bit Borland (Makefile.b32),
+ Watcom (Makefile.wat), Amiga SAS/C (Makefile.sas)
+- speed up adler32 for modern machines without auto-increment
+- added -ansi for IRIX in configure
+- static_init_done in trees.c is an int
+- define unlink as delete for VMS
+- fix configure for QNX
+- add configure branch for SCO and HPUX
+- avoid many warnings (unused variables, dead assignments, etc...)
+- no fdopen for BeOS
+- fix the Watcom fix for 32 bit mode (define FAR as empty)
+- removed redefinition of Byte for MKWERKS
+- work around an MWKERKS bug (incorrect merge of all .h files)
+
+Changes in 0.99 (27 Jan 96)
+- allow preset dictionary shared between compressor and decompressor
+- allow compression level 0 (no compression)
+- add deflateParams in zlib.h: allow dynamic change of compression level
+ and compression strategy.
+- test large buffers and deflateParams in example.c
+- add optional "configure" to build zlib as a shared library
+- suppress Makefile.qnx, use configure instead
+- fixed deflate for 64-bit systems (detected on Cray)
+- fixed inflate_blocks for 64-bit systems (detected on Alpha)
+- declare Z_DEFLATED in zlib.h (possible parameter for deflateInit2)
+- always return Z_BUF_ERROR when deflate() has nothing to do
+- deflateInit and inflateInit are now macros to allow version checking
+- prefix all global functions and types with z_ with -DZ_PREFIX
+- make falloc completely reentrant (inftrees.c)
+- fixed very unlikely race condition in ct_static_init
+- free in reverse order of allocation to help memory manager
+- use zlib-1.0/* instead of zlib/* inside the tar.gz
+- make zlib warning-free with "gcc -O3 -Wall -Wwrite-strings -Wpointer-arith
+ -Wconversion -Wstrict-prototypes -Wmissing-prototypes"
+- allow gzread on concatenated .gz files
+- deflateEnd now returns Z_DATA_ERROR if it was premature
+- deflate is finally (?) fully deterministic (no matches beyond end of input)
+- Document Z_SYNC_FLUSH
+- add uninstall in Makefile
+- Check for __cpluplus in zlib.h
+- Better test in ct_align for partial flush
+- avoid harmless warnings for Borland C++
+- initialize hash_head in deflate.c
+- avoid warning on fdopen (gzio.c) for HP cc -Aa
+- include stdlib.h for STDC compilers
+- include errno.h for Cray
+- ignore error if ranlib doesn't exist
+- call ranlib twice for NeXTSTEP
+- use exec_prefix instead of prefix for libz.a
+- renamed ct_* as _tr_* to avoid conflict with applications
+- clear z->msg in inflateInit2 before any error return
+- initialize opaque in example.c, gzio.c, deflate.c and inflate.c
+- fixed typo in zconf.h (_GNUC__ => __GNUC__)
+- check for WIN32 in zconf.h and zutil.c (avoid farmalloc in 32-bit mode)
+- fix typo in Make_vms.com (f$trnlnm -> f$getsyi)
+- in fcalloc, normalize pointer if size > 65520 bytes
+- don't use special fcalloc for 32 bit Borland C++
+- use STDC instead of __GO32__ to avoid redeclaring exit, calloc, etc...
+- use Z_BINARY instead of BINARY
+- document that gzclose after gzdopen will close the file
+- allow "a" as mode in gzopen.
+- fix error checking in gzread
+- allow skipping .gz extra-field on pipes
+- added reference to Perl interface in README
+- put the crc table in FAR data (I dislike more and more the medium model :)
+- added get_crc_table
+- added a dimension to all arrays (Borland C can't count).
+- workaround Borland C bug in declaration of inflate_codes_new & inflate_fast
+- guard against multiple inclusion of *.h (for precompiled header on Mac)
+- Watcom C pretends to be Microsoft C small model even in 32 bit mode.
+- don't use unsized arrays to avoid silly warnings by Visual C++:
+ warning C4746: 'inflate_mask' : unsized array treated as '__far'
+ (what's wrong with far data in far model?).
+- define enum out of inflate_blocks_state to allow compilation with C++
+
+Changes in 0.95 (16 Aug 95)
+- fix MSDOS small and medium model (now easier to adapt to any compiler)
+- inlined send_bits
+- fix the final (:-) bug for deflate with flush (output was correct but
+ not completely flushed in rare occasions).
+- default window size is same for compression and decompression
+ (it's now sufficient to set MAX_WBITS in zconf.h).
+- voidp -> voidpf and voidnp -> voidp (for consistency with other
+ typedefs and because voidnp was not near in large model).
+
+Changes in 0.94 (13 Aug 95)
+- support MSDOS medium model
+- fix deflate with flush (could sometimes generate bad output)
+- fix deflateReset (zlib header was incorrectly suppressed)
+- added support for VMS
+- allow a compression level in gzopen()
+- gzflush now calls fflush
+- For deflate with flush, flush even if no more input is provided.
+- rename libgz.a as libz.a
+- avoid complex expression in infcodes.c triggering Turbo C bug
+- work around a problem with gcc on Alpha (in INSERT_STRING)
+- don't use inline functions (problem with some gcc versions)
+- allow renaming of Byte, uInt, etc... with #define.
+- avoid warning about (unused) pointer before start of array in deflate.c
+- avoid various warnings in gzio.c, example.c, infblock.c, adler32.c, zutil.c
+- avoid reserved word 'new' in trees.c
+
+Changes in 0.93 (25 June 95)
+- temporarily disable inline functions
+- make deflate deterministic
+- give enough lookahead for PARTIAL_FLUSH
+- Set binary mode for stdin/stdout in minigzip.c for OS/2
+- don't even use signed char in inflate (not portable enough)
+- fix inflate memory leak for segmented architectures
+
+Changes in 0.92 (3 May 95)
+- don't assume that char is signed (problem on SGI)
+- Clear bit buffer when starting a stored block
+- no memcpy on Pyramid
+- suppressed inftest.c
+- optimized fill_window, put longest_match inline for gcc
+- optimized inflate on stored blocks.
+- untabify all sources to simplify patches
+
+Changes in 0.91 (2 May 95)
+- Default MEM_LEVEL is 8 (not 9 for Unix) as documented in zlib.h
+- Document the memory requirements in zconf.h
+- added "make install"
+- fix sync search logic in inflateSync
+- deflate(Z_FULL_FLUSH) now works even if output buffer too short
+- after inflateSync, don't scare people with just "lo world"
+- added support for DJGPP
+
+Changes in 0.9 (1 May 95)
+- don't assume that zalloc clears the allocated memory (the TurboC bug
+ was Mark's bug after all :)
+- let again gzread copy uncompressed data unchanged (was working in 0.71)
+- deflate(Z_FULL_FLUSH), inflateReset and inflateSync are now fully implemented
+- added a test of inflateSync in example.c
+- moved MAX_WBITS to zconf.h because users might want to change that.
+- document explicitly that zalloc(64K) on MSDOS must return a normalized
+ pointer (zero offset)
+- added Makefiles for Microsoft C, Turbo C, Borland C++
+- faster crc32()
+
+Changes in 0.8 (29 April 95)
+- added fast inflate (inffast.c)
+- deflate(Z_FINISH) now returns Z_STREAM_END when done. Warning: this
+ is incompatible with previous versions of zlib which returned Z_OK.
+- work around a TurboC compiler bug (bad code for b << 0, see infutil.h)
+ (actually that was not a compiler bug, see 0.81 above)
+- gzread no longer reads one extra byte in certain cases
+- In gzio destroy(), don't reference a freed structure
+- avoid many warnings for MSDOS
+- avoid the ERROR symbol which is used by MS Windows
+
+Changes in 0.71 (14 April 95)
+- Fixed more MSDOS compilation problems :( There is still a bug with
+ TurboC large model.
+
+Changes in 0.7 (14 April 95)
+- Added full inflate support.
+- Simplified the crc32() interface. The pre- and post-conditioning
+ (one's complement) is now done inside crc32(). WARNING: this is
+ incompatible with previous versions; see zlib.h for the new usage.
+
+Changes in 0.61 (12 April 95)
+- workaround for a bug in TurboC. example and minigzip now work on MSDOS.
+
+Changes in 0.6 (11 April 95)
+- added minigzip.c
+- added gzdopen to reopen a file descriptor as gzFile
+- added transparent reading of non-gziped files in gzread.
+- fixed bug in gzread (don't read crc as data)
+- fixed bug in destroy (gzio.c) (don't return Z_STREAM_END for gzclose).
+- don't allocate big arrays in the stack (for MSDOS)
+- fix some MSDOS compilation problems
+
+Changes in 0.5:
+- do real compression in deflate.c. Z_PARTIAL_FLUSH is supported but
+ not yet Z_FULL_FLUSH.
+- support decompression but only in a single step (forced Z_FINISH)
+- added opaque object for zalloc and zfree.
+- added deflateReset and inflateReset
+- added a variable zlib_version for consistency checking.
+- renamed the 'filter' parameter of deflateInit2 as 'strategy'.
+ Added Z_FILTERED and Z_HUFFMAN_ONLY constants.
+
+Changes in 0.4:
+- avoid "zip" everywhere, use zlib instead of ziplib.
+- suppress Z_BLOCK_FLUSH, interpret Z_PARTIAL_FLUSH as block flush
+ if compression method == 8.
+- added adler32 and crc32
+- renamed deflateOptions as deflateInit2, call one or the other but not both
+- added the method parameter for deflateInit2.
+- added inflateInit2
+- simplied considerably deflateInit and inflateInit by not supporting
+ user-provided history buffer. This is supported only in deflateInit2
+ and inflateInit2.
+
+Changes in 0.3:
+- prefix all macro names with Z_
+- use Z_FINISH instead of deflateEnd to finish compression.
+- added Z_HUFFMAN_ONLY
+- added gzerror()
diff --git a/zlib/FAQ b/zlib/FAQ
new file mode 100644
index 00000000..47a7d60c
--- /dev/null
+++ b/zlib/FAQ
@@ -0,0 +1,100 @@
+
+ Frequently Asked Questions about zlib
+
+
+If your question is not there, please check the zlib home page
+http://www.zlib.org which may have more recent information.
+The lastest zlib FAQ is at http://www.gzip.org/zlib/zlib_faq.html
+
+
+ 1. Is zlib Y2K-compliant?
+
+ Yes. zlib doesn't handle dates.
+
+ 2. Where can I get a Windows DLL version?
+
+ The zlib sources can be compiled without change to produce a DLL. If you
+ want a precompiled DLL, see http://www.winimage.com/zLibDll/ . Questions
+ about the zlib DLL should be sent to Gilles Vollant (info@winimage.com).
+
+ 3. Where can I get a Visual Basic interface to zlib?
+
+ See
+ * http://www.winimage.com/zLibDll/cmp-z-it.zip
+ * http://www.dogma.net/markn/articles/zlibtool/zlibtool.htm
+ * contrib/visual-basic.txt in the zlib distribution
+
+ 4. compress() returns Z_BUF_ERROR
+
+ Make sure that before the call of compress, the length of the compressed
+ buffer is equal to the total size of the compressed buffer and not
+ zero. For Visual Basic, check that this parameter is passed by reference
+ ("as any"), not by value ("as long").
+
+ 5. deflate() or inflate() returns Z_BUF_ERROR
+
+ Before making the call, make sure that avail_in and avail_out are not
+ zero. When setting the parameter flush equal to Z_FINISH, also make sure
+ that avail_out is big enough to allow processing all pending input.
+
+ 6. Where's the zlib documentation (man pages, etc.)?
+
+ It's in zlib.h for the moment, and Francis S. Lin has converted it to a
+ web page zlib.html. Volunteers to transform this to Unix-style man pages,
+ please contact Jean-loup Gailly (jloup@gzip.org). Examples of zlib usage
+ are in the files example.c and minigzip.c.
+
+ 7. Why don't you use GNU autoconf or libtool or ...?
+
+ Because we would like to keep zlib as a very small and simple
+ package. zlib is rather portable and doesn't need much configuration.
+
+ 8. I found a bug in zlib.
+
+ Most of the time, such problems are due to an incorrect usage of
+ zlib. Please try to reproduce the problem with a small program and send
+ the corresponding source to us at zlib@gzip.org . Do not send
+ multi-megabyte data files without prior agreement.
+
+ 9. Why do I get "undefined reference to gzputc"?
+
+ If "make test" produces something like
+
+ example.o(.text+0x154): undefined reference to `gzputc'
+
+ check that you don't have old files libz.* in /usr/lib, /usr/local/lib or
+ /usr/X11R6/lib. Remove any old versions, then do "make install".
+
+10. I need a Delphi interface to zlib.
+
+ See the directories contrib/delphi and contrib/delphi2 in the zlib
+ distribution.
+
+11. Can zlib handle .zip archives?
+
+ See the directory contrib/minizip in the zlib distribution.
+
+12. Can zlib handle .Z files?
+
+ No, sorry. You have to spawn an uncompress or gunzip subprocess, or adapt
+ the code of uncompress on your own.
+
+13. How can I make a Unix shared library?
+
+ make clean
+ ./configure -s
+ make
+
+14. Why does "make test" fail on Mac OS X?
+
+ Mac OS X already includes zlib as a shared library, and so -lz links the
+ shared library instead of the one that the "make" compiled. For zlib
+ 1.1.3, the two are incompatible due to different compile-time
+ options. Simply change the -lz in the Makefile to libz.a, and it will use
+ the compiled library instead of the shared one and the "make test" will
+ succeed.
+
+15. I have a question about OttoPDF
+
+ We are not the authors of OttoPDF. The real author is on the OttoPDF web
+ site Joel Hainley jhainley@myndkryme.com.
diff --git a/zlib/INDEX b/zlib/INDEX
new file mode 100644
index 00000000..8a245766
--- /dev/null
+++ b/zlib/INDEX
@@ -0,0 +1,86 @@
+ChangeLog history of changes
+INDEX this file
+FAQ Frequently Asked Questions about zlib
+Make_vms.com script for Vax/VMS
+Makefile makefile for Unix (generated by configure)
+Makefile.in makefile for Unix (template for configure)
+Makefile.riscos makefile for RISCOS
+README guess what
+algorithm.txt description of the (de)compression algorithm
+configure configure script for Unix
+descrip.mms makefile for Vax/VMS
+zlib.3 mini man page for zlib (volunteers to write full
+ man pages from zlib.h welcome. write to jloup@gzip.org)
+
+amiga/Makefile.sas makefile for Amiga SAS/C
+amiga/Makefile.pup makefile for Amiga powerUP SAS/C PPC
+
+msdos/Makefile.w32 makefile for Microsoft Visual C++ 32-bit
+msdos/Makefile.b32 makefile for Borland C++ 32-bit
+msdos/Makefile.bor makefile for Borland C/C++ 16-bit
+msdos/Makefile.dj2 makefile for DJGPP 2.x
+msdos/Makefile.emx makefile for EMX 0.9c (32-bit DOS/OS2)
+msdos/Makefile.msc makefile for Microsoft C 16-bit
+msdos/Makefile.tc makefile for Turbo C
+msdos/Makefile.wat makefile for Watcom C
+msdos/zlib.def definition file for Windows DLL
+msdos/zlib.rc definition file for Windows DLL
+
+nt/Makefile.nt makefile for Windows NT
+nt/zlib.dnt definition file for Windows NT DLL
+nt/Makefile.emx makefile for EMX 0.9c/RSXNT 1.41 (Win32 Intel)
+nt/Makefile.gcc makefile for Windows NT using GCC (mingw32)
+
+
+ zlib public header files (must be kept):
+zconf.h
+zlib.h
+
+ private source files used to build the zlib library:
+adler32.c
+compress.c
+crc32.c
+deflate.c
+deflate.h
+gzio.c
+infblock.c
+infblock.h
+infcodes.c
+infcodes.h
+inffast.c
+inffast.h
+inflate.c
+inftrees.c
+inftrees.h
+infutil.c
+infutil.h
+maketree.c
+trees.c
+uncompr.c
+zutil.c
+zutil.h
+
+ source files for sample programs:
+example.c
+minigzip.c
+
+ unsupported contribution by third parties
+
+contrib/asm386/ by Gilles Vollant <info@winimage.com>
+ 386 asm code replacing longest_match().
+
+contrib/minizip/ by Gilles Vollant <info@winimage.com>
+ Mini zip and unzip based on zlib
+ See http://www.winimage.com/zLibDll/unzip.html
+
+contrib/iostream/ by Kevin Ruland <kevin@rodin.wustl.edu>
+ A C++ I/O streams interface to the zlib gz* functions
+
+contrib/iostream2/ by Tyge Løvset <Tyge.Lovset@cmr.no>
+ Another C++ I/O streams interface
+
+contrib/untgz/ by "Pedro A. Aranda Guti\irrez" <paag@tid.es>
+ A very simple tar.gz extractor using zlib
+
+contrib/visual-basic.txt by Carlos Rios <c_rios@sonda.cl>
+ How to use compress(), uncompress() and the gz* functions from VB.
diff --git a/zlib/Make_vms.com b/zlib/Make_vms.com
new file mode 100644
index 00000000..1c57e8f0
--- /dev/null
+++ b/zlib/Make_vms.com
@@ -0,0 +1,115 @@
+$! make libz under VMS
+$! written by Martin P.J. Zinser <m.zinser@gsi.de>
+$!
+$! Look for the compiler used
+$!
+$ ccopt = ""
+$ if f$getsyi("HW_MODEL").ge.1024
+$ then
+$ ccopt = "/prefix=all"+ccopt
+$ comp = "__decc__=1"
+$ if f$trnlnm("SYS").eqs."" then define sys sys$library:
+$ else
+$ if f$search("SYS$SYSTEM:DECC$COMPILER.EXE").eqs.""
+$ then
+$ comp = "__vaxc__=1"
+$ if f$trnlnm("SYS").eqs."" then define sys sys$library:
+$ else
+$ if f$trnlnm("SYS").eqs."" then define sys decc$library_include:
+$ ccopt = "/decc/prefix=all"+ccopt
+$ comp = "__decc__=1"
+$ endif
+$ endif
+$!
+$! Build the thing plain or with mms
+$!
+$ write sys$output "Compiling Zlib sources ..."
+$ if f$search("SYS$SYSTEM:MMS.EXE").eqs.""
+$ then
+$ dele example.obj;*,minigzip.obj;*
+$ CALL MAKE adler32.OBJ "CC ''CCOPT' adler32" -
+ adler32.c zlib.h zconf.h
+$ CALL MAKE compress.OBJ "CC ''CCOPT' compress" -
+ compress.c zlib.h zconf.h
+$ CALL MAKE crc32.OBJ "CC ''CCOPT' crc32" -
+ crc32.c zlib.h zconf.h
+$ CALL MAKE deflate.OBJ "CC ''CCOPT' deflate" -
+ deflate.c deflate.h zutil.h zlib.h zconf.h
+$ CALL MAKE gzio.OBJ "CC ''CCOPT' gzio" -
+ gzio.c zutil.h zlib.h zconf.h
+$ CALL MAKE infblock.OBJ "CC ''CCOPT' infblock" -
+ infblock.c zutil.h zlib.h zconf.h infblock.h
+$ CALL MAKE infcodes.OBJ "CC ''CCOPT' infcodes" -
+ infcodes.c zutil.h zlib.h zconf.h inftrees.h
+$ CALL MAKE inffast.OBJ "CC ''CCOPT' inffast" -
+ inffast.c zutil.h zlib.h zconf.h inffast.h
+$ CALL MAKE inflate.OBJ "CC ''CCOPT' inflate" -
+ inflate.c zutil.h zlib.h zconf.h infblock.h
+$ CALL MAKE inftrees.OBJ "CC ''CCOPT' inftrees" -
+ inftrees.c zutil.h zlib.h zconf.h inftrees.h
+$ CALL MAKE infutil.OBJ "CC ''CCOPT' infutil" -
+ infutil.c zutil.h zlib.h zconf.h inftrees.h infutil.h
+$ CALL MAKE trees.OBJ "CC ''CCOPT' trees" -
+ trees.c deflate.h zutil.h zlib.h zconf.h
+$ CALL MAKE uncompr.OBJ "CC ''CCOPT' uncompr" -
+ uncompr.c zlib.h zconf.h
+$ CALL MAKE zutil.OBJ "CC ''CCOPT' zutil" -
+ zutil.c zutil.h zlib.h zconf.h
+$ write sys$output "Building Zlib ..."
+$ CALL MAKE libz.OLB "lib/crea libz.olb *.obj" *.OBJ
+$ write sys$output "Building example..."
+$ CALL MAKE example.OBJ "CC ''CCOPT' example" -
+ example.c zlib.h zconf.h
+$ call make example.exe "LINK example,libz.olb/lib" example.obj libz.olb
+$ write sys$output "Building minigzip..."
+$ CALL MAKE minigzip.OBJ "CC ''CCOPT' minigzip" -
+ minigzip.c zlib.h zconf.h
+$ call make minigzip.exe -
+ "LINK minigzip,libz.olb/lib,x11vms:xvmsutils.olb/lib" -
+ minigzip.obj libz.olb
+$ else
+$ mms/macro=('comp')
+$ endif
+$ write sys$output "Zlib build completed"
+$ exit
+$!
+$!
+$MAKE: SUBROUTINE !SUBROUTINE TO CHECK DEPENDENCIES
+$ V = 'F$Verify(0)
+$! P1 = What we are trying to make
+$! P2 = Command to make it
+$! P3 - P8 What it depends on
+$
+$ If F$Search(P1) .Eqs. "" Then Goto Makeit
+$ Time = F$CvTime(F$File(P1,"RDT"))
+$arg=3
+$Loop:
+$ Argument = P'arg
+$ If Argument .Eqs. "" Then Goto Exit
+$ El=0
+$Loop2:
+$ File = F$Element(El," ",Argument)
+$ If File .Eqs. " " Then Goto Endl
+$ AFile = ""
+$Loop3:
+$ OFile = AFile
+$ AFile = F$Search(File)
+$ If AFile .Eqs. "" .Or. AFile .Eqs. OFile Then Goto NextEl
+$ If F$CvTime(F$File(AFile,"RDT")) .Ges. Time Then Goto Makeit
+$ Goto Loop3
+$NextEL:
+$ El = El + 1
+$ Goto Loop2
+$EndL:
+$ arg=arg+1
+$ If arg .Le. 8 Then Goto Loop
+$ Goto Exit
+$
+$Makeit:
+$ VV=F$VERIFY(0)
+$ write sys$output P2
+$ 'P2
+$ VV='F$Verify(VV)
+$Exit:
+$ If V Then Set Verify
+$ENDSUBROUTINE
diff --git a/zlib/Makefile.in b/zlib/Makefile.in
new file mode 100644
index 00000000..531562b2
--- /dev/null
+++ b/zlib/Makefile.in
@@ -0,0 +1,175 @@
+# Makefile for zlib
+# Copyright (C) 1995-2002 Jean-loup Gailly.
+# For conditions of distribution and use, see copyright notice in zlib.h
+
+# To compile and test, type:
+# ./configure; make test
+# The call of configure is optional if you don't have special requirements
+# If you wish to build zlib as a shared library, use: ./configure -s
+
+# To install /usr/local/lib/libz.* and /usr/local/include/zlib.h, type:
+# make install
+# To install in $HOME instead of /usr/local, use:
+# make install prefix=$HOME
+
+CC=cc
+
+CFLAGS=-O
+#CFLAGS=-O -DMAX_WBITS=14 -DMAX_MEM_LEVEL=7
+#CFLAGS=-g -DDEBUG
+#CFLAGS=-O3 -Wall -Wwrite-strings -Wpointer-arith -Wconversion \
+# -Wstrict-prototypes -Wmissing-prototypes
+
+LDFLAGS=-L. -lz
+LDSHARED=$(CC)
+CPP=$(CC) -E
+
+VER=1.1.4
+LIBS=libz.a
+SHAREDLIB=libz.so
+
+AR=ar rc
+RANLIB=ranlib
+TAR=tar
+SHELL=/bin/sh
+
+prefix = /usr/local
+exec_prefix = ${prefix}
+libdir = ${exec_prefix}/lib
+includedir = ${prefix}/include
+
+OBJS = adler32.o compress.o crc32.o gzio.o uncompr.o deflate.o trees.o \
+ zutil.o inflate.o infblock.o inftrees.o infcodes.o infutil.o inffast.o
+
+OBJA =
+# to use the asm code: make OBJA=match.o
+
+TEST_OBJS = example.o minigzip.o
+
+DISTFILES = README FAQ INDEX ChangeLog configure Make*[a-z0-9] *.[ch] *.mms \
+ algorithm.txt zlib.3 zlib.html \
+ msdos/Make*[a-z0-9] msdos/zlib.def msdos/zlib.rc \
+ nt/Make*[a-z0-9] nt/zlib.dnt amiga/Make*.??? os2/M*.os2 os2/zlib.def \
+ contrib/RE*.contrib contrib/*.txt contrib/asm386/*.asm contrib/asm386/*.c \
+ contrib/asm386/*.bat contrib/asm386/zlibvc.d?? contrib/asm[56]86/*.?86 \
+ contrib/asm[56]86/*.S contrib/iostream/*.cpp \
+ contrib/iostream/*.h contrib/iostream2/*.h contrib/iostream2/*.cpp \
+ contrib/untgz/Makefile contrib/untgz/*.c contrib/untgz/*.w32 \
+ contrib/minizip/[CM]*[pe] contrib/minizip/*.[ch] contrib/minizip/*.[td]?? \
+ contrib/delphi*/*.???
+
+all: example minigzip
+
+test: all
+ @LD_LIBRARY_PATH=.:$(LD_LIBRARY_PATH) ; export LD_LIBRARY_PATH; \
+ echo hello world | ./minigzip | ./minigzip -d || \
+ echo ' *** minigzip test FAILED ***' ; \
+ if ./example; then \
+ echo ' *** zlib test OK ***'; \
+ else \
+ echo ' *** zlib test FAILED ***'; \
+ fi
+
+libz.a: $(OBJS) $(OBJA)
+ $(AR) $@ $(OBJS) $(OBJA)
+ -@ ($(RANLIB) $@ || true) >/dev/null 2>&1
+
+match.o: match.S
+ $(CPP) match.S > _match.s
+ $(CC) -c _match.s
+ mv _match.o match.o
+ rm -f _match.s
+
+$(SHAREDLIB).$(VER): $(OBJS)
+ $(LDSHARED) -o $@ $(OBJS)
+ rm -f $(SHAREDLIB) $(SHAREDLIB).1
+ ln -s $@ $(SHAREDLIB)
+ ln -s $@ $(SHAREDLIB).1
+
+example: example.o $(LIBS)
+ $(CC) $(CFLAGS) -o $@ example.o $(LDFLAGS)
+
+minigzip: minigzip.o $(LIBS)
+ $(CC) $(CFLAGS) -o $@ minigzip.o $(LDFLAGS)
+
+install: $(LIBS)
+ -@if [ ! -d $(includedir) ]; then mkdir $(includedir); fi
+ -@if [ ! -d $(libdir) ]; then mkdir $(libdir); fi
+ cp zlib.h zconf.h $(includedir)
+ chmod 644 $(includedir)/zlib.h $(includedir)/zconf.h
+ cp $(LIBS) $(libdir)
+ cd $(libdir); chmod 755 $(LIBS)
+ -@(cd $(libdir); $(RANLIB) libz.a || true) >/dev/null 2>&1
+ cd $(libdir); if test -f $(SHAREDLIB).$(VER); then \
+ rm -f $(SHAREDLIB) $(SHAREDLIB).1; \
+ ln -s $(SHAREDLIB).$(VER) $(SHAREDLIB); \
+ ln -s $(SHAREDLIB).$(VER) $(SHAREDLIB).1; \
+ (ldconfig || true) >/dev/null 2>&1; \
+ fi
+# The ranlib in install is needed on NeXTSTEP which checks file times
+# ldconfig is for Linux
+
+uninstall:
+ cd $(includedir); \
+ v=$(VER); \
+ if test -f zlib.h; then \
+ v=`sed -n '/VERSION "/s/.*"\(.*\)".*/\1/p' < zlib.h`; \
+ rm -f zlib.h zconf.h; \
+ fi; \
+ cd $(libdir); rm -f libz.a; \
+ if test -f $(SHAREDLIB).$$v; then \
+ rm -f $(SHAREDLIB).$$v $(SHAREDLIB) $(SHAREDLIB).1; \
+ fi
+
+clean:
+ rm -f *.o *~ example minigzip libz.a libz.so* foo.gz so_locations \
+ _match.s maketree
+
+distclean: clean
+
+zip:
+ mv Makefile Makefile~; cp -p Makefile.in Makefile
+ rm -f test.c ztest*.c contrib/minizip/test.zip
+ v=`sed -n -e 's/\.//g' -e '/VERSION "/s/.*"\(.*\)".*/\1/p' < zlib.h`;\
+ zip -ul9 zlib$$v $(DISTFILES)
+ mv Makefile~ Makefile
+
+dist:
+ mv Makefile Makefile~; cp -p Makefile.in Makefile
+ rm -f test.c ztest*.c contrib/minizip/test.zip
+ d=zlib-`sed -n '/VERSION "/s/.*"\(.*\)".*/\1/p' < zlib.h`;\
+ rm -f $$d.tar.gz; \
+ if test ! -d ../$$d; then rm -f ../$$d; ln -s `pwd` ../$$d; fi; \
+ files=""; \
+ for f in $(DISTFILES); do files="$$files $$d/$$f"; done; \
+ cd ..; \
+ GZIP=-9 $(TAR) chofz $$d/$$d.tar.gz $$files; \
+ if test ! -d $$d; then rm -f $$d; fi
+ mv Makefile~ Makefile
+
+tags:
+ etags *.[ch]
+
+depend:
+ makedepend -- $(CFLAGS) -- *.[ch]
+
+# DO NOT DELETE THIS LINE -- make depend depends on it.
+
+adler32.o: zlib.h zconf.h
+compress.o: zlib.h zconf.h
+crc32.o: zlib.h zconf.h
+deflate.o: deflate.h zutil.h zlib.h zconf.h
+example.o: zlib.h zconf.h
+gzio.o: zutil.h zlib.h zconf.h
+infblock.o: infblock.h inftrees.h infcodes.h infutil.h zutil.h zlib.h zconf.h
+infcodes.o: zutil.h zlib.h zconf.h
+infcodes.o: inftrees.h infblock.h infcodes.h infutil.h inffast.h
+inffast.o: zutil.h zlib.h zconf.h inftrees.h
+inffast.o: infblock.h infcodes.h infutil.h inffast.h
+inflate.o: zutil.h zlib.h zconf.h infblock.h
+inftrees.o: zutil.h zlib.h zconf.h inftrees.h
+infutil.o: zutil.h zlib.h zconf.h infblock.h inftrees.h infcodes.h infutil.h
+minigzip.o: zlib.h zconf.h
+trees.o: deflate.h zutil.h zlib.h zconf.h trees.h
+uncompr.o: zlib.h zconf.h
+zutil.o: zutil.h zlib.h zconf.h
diff --git a/zlib/Makefile.riscos b/zlib/Makefile.riscos
new file mode 100644
index 00000000..d97f4492
--- /dev/null
+++ b/zlib/Makefile.riscos
@@ -0,0 +1,151 @@
+# Project: zlib_1_03
+# Patched for zlib 1.1.2 rw@shadow.org.uk 19980430
+# test works out-of-the-box, installs `somewhere' on demand
+
+# Toolflags:
+CCflags = -c -depend !Depend -IC: -g -throwback -DRISCOS -fah
+C++flags = -c -depend !Depend -IC: -throwback
+Linkflags = -aif -c++ -o $@
+ObjAsmflags = -throwback -NoCache -depend !Depend
+CMHGflags =
+LibFileflags = -c -l -o $@
+Squeezeflags = -o $@
+
+# change the line below to where _you_ want the library installed.
+libdest = lib:zlib
+
+# Final targets:
+@.lib: @.o.adler32 @.o.compress @.o.crc32 @.o.deflate @.o.gzio \
+ @.o.infblock @.o.infcodes @.o.inffast @.o.inflate @.o.inftrees @.o.infutil @.o.trees \
+ @.o.uncompr @.o.zutil
+ LibFile $(LibFileflags) @.o.adler32 @.o.compress @.o.crc32 @.o.deflate \
+ @.o.gzio @.o.infblock @.o.infcodes @.o.inffast @.o.inflate @.o.inftrees @.o.infutil \
+ @.o.trees @.o.uncompr @.o.zutil
+test: @.minigzip @.example @.lib
+ @copy @.lib @.libc A~C~DF~L~N~P~Q~RS~TV
+ @echo running tests: hang on.
+ @/@.minigzip -f -9 libc
+ @/@.minigzip -d libc-gz
+ @/@.minigzip -f -1 libc
+ @/@.minigzip -d libc-gz
+ @/@.minigzip -h -9 libc
+ @/@.minigzip -d libc-gz
+ @/@.minigzip -h -1 libc
+ @/@.minigzip -d libc-gz
+ @/@.minigzip -9 libc
+ @/@.minigzip -d libc-gz
+ @/@.minigzip -1 libc
+ @/@.minigzip -d libc-gz
+ @diff @.lib @.libc
+ @echo that should have reported '@.lib and @.libc identical' if you have diff.
+ @/@.example @.fred @.fred
+ @echo that will have given lots of hello!'s.
+
+@.minigzip: @.o.minigzip @.lib C:o.Stubs
+ Link $(Linkflags) @.o.minigzip @.lib C:o.Stubs
+@.example: @.o.example @.lib C:o.Stubs
+ Link $(Linkflags) @.o.example @.lib C:o.Stubs
+
+install: @.lib
+ cdir $(libdest)
+ cdir $(libdest).h
+ @copy @.h.zlib $(libdest).h.zlib A~C~DF~L~N~P~Q~RS~TV
+ @copy @.h.zconf $(libdest).h.zconf A~C~DF~L~N~P~Q~RS~TV
+ @copy @.lib $(libdest).lib A~C~DF~L~N~P~Q~RS~TV
+ @echo okay, installed zlib in $(libdest)
+
+clean:; remove @.minigzip
+ remove @.example
+ remove @.libc
+ -wipe @.o.* F~r~cV
+ remove @.fred
+
+# User-editable dependencies:
+.c.o:
+ cc $(ccflags) -o $@ $<
+
+# Static dependencies:
+
+# Dynamic dependencies:
+o.example: c.example
+o.example: h.zlib
+o.example: h.zconf
+o.minigzip: c.minigzip
+o.minigzip: h.zlib
+o.minigzip: h.zconf
+o.adler32: c.adler32
+o.adler32: h.zlib
+o.adler32: h.zconf
+o.compress: c.compress
+o.compress: h.zlib
+o.compress: h.zconf
+o.crc32: c.crc32
+o.crc32: h.zlib
+o.crc32: h.zconf
+o.deflate: c.deflate
+o.deflate: h.deflate
+o.deflate: h.zutil
+o.deflate: h.zlib
+o.deflate: h.zconf
+o.gzio: c.gzio
+o.gzio: h.zutil
+o.gzio: h.zlib
+o.gzio: h.zconf
+o.infblock: c.infblock
+o.infblock: h.zutil
+o.infblock: h.zlib
+o.infblock: h.zconf
+o.infblock: h.infblock
+o.infblock: h.inftrees
+o.infblock: h.infcodes
+o.infblock: h.infutil
+o.infcodes: c.infcodes
+o.infcodes: h.zutil
+o.infcodes: h.zlib
+o.infcodes: h.zconf
+o.infcodes: h.inftrees
+o.infcodes: h.infblock
+o.infcodes: h.infcodes
+o.infcodes: h.infutil
+o.infcodes: h.inffast
+o.inffast: c.inffast
+o.inffast: h.zutil
+o.inffast: h.zlib
+o.inffast: h.zconf
+o.inffast: h.inftrees
+o.inffast: h.infblock
+o.inffast: h.infcodes
+o.inffast: h.infutil
+o.inffast: h.inffast
+o.inflate: c.inflate
+o.inflate: h.zutil
+o.inflate: h.zlib
+o.inflate: h.zconf
+o.inflate: h.infblock
+o.inftrees: c.inftrees
+o.inftrees: h.zutil
+o.inftrees: h.zlib
+o.inftrees: h.zconf
+o.inftrees: h.inftrees
+o.inftrees: h.inffixed
+o.infutil: c.infutil
+o.infutil: h.zutil
+o.infutil: h.zlib
+o.infutil: h.zconf
+o.infutil: h.infblock
+o.infutil: h.inftrees
+o.infutil: h.infcodes
+o.infutil: h.infutil
+o.trees: c.trees
+o.trees: h.deflate
+o.trees: h.zutil
+o.trees: h.zlib
+o.trees: h.zconf
+o.trees: h.trees
+o.uncompr: c.uncompr
+o.uncompr: h.zlib
+o.uncompr: h.zconf
+o.zutil: c.zutil
+o.zutil: h.zutil
+o.zutil: h.zlib
+o.zutil: h.zconf
diff --git a/zlib/README b/zlib/README
new file mode 100644
index 00000000..29d67146
--- /dev/null
+++ b/zlib/README
@@ -0,0 +1,147 @@
+zlib 1.1.4 is a general purpose data compression library. All the code
+is thread safe. The data format used by the zlib library
+is described by RFCs (Request for Comments) 1950 to 1952 in the files
+http://www.ietf.org/rfc/rfc1950.txt (zlib format), rfc1951.txt (deflate
+format) and rfc1952.txt (gzip format). These documents are also available in
+other formats from ftp://ftp.uu.net/graphics/png/documents/zlib/zdoc-index.html
+
+All functions of the compression library are documented in the file zlib.h
+(volunteer to write man pages welcome, contact jloup@gzip.org). A usage
+example of the library is given in the file example.c which also tests that
+the library is working correctly. Another example is given in the file
+minigzip.c. The compression library itself is composed of all source files
+except example.c and minigzip.c.
+
+To compile all files and run the test program, follow the instructions
+given at the top of Makefile. In short "make test; make install"
+should work for most machines. For Unix: "./configure; make test; make install"
+For MSDOS, use one of the special makefiles such as Makefile.msc.
+For VMS, use Make_vms.com or descrip.mms.
+
+Questions about zlib should be sent to <zlib@gzip.org>, or to
+Gilles Vollant <info@winimage.com> for the Windows DLL version.
+The zlib home page is http://www.zlib.org or http://www.gzip.org/zlib/
+Before reporting a problem, please check this site to verify that
+you have the latest version of zlib; otherwise get the latest version and
+check whether the problem still exists or not.
+
+PLEASE read the zlib FAQ http://www.gzip.org/zlib/zlib_faq.html
+before asking for help.
+
+Mark Nelson <markn@ieee.org> wrote an article about zlib for the Jan. 1997
+issue of Dr. Dobb's Journal; a copy of the article is available in
+http://dogma.net/markn/articles/zlibtool/zlibtool.htm
+
+The changes made in version 1.1.4 are documented in the file ChangeLog.
+The only changes made since 1.1.3 are bug corrections:
+
+- ZFREE was repeated on same allocation on some error conditions.
+ This creates a security problem described in
+ http://www.zlib.org/advisory-2002-03-11.txt
+- Returned incorrect error (Z_MEM_ERROR) on some invalid data
+- Avoid accesses before window for invalid distances with inflate window
+ less than 32K.
+- force windowBits > 8 to avoid a bug in the encoder for a window size
+ of 256 bytes. (A complete fix will be available in 1.1.5).
+
+The beta version 1.1.5beta includes many more changes. A new official
+version 1.1.5 will be released as soon as extensive testing has been
+completed on it.
+
+
+Unsupported third party contributions are provided in directory "contrib".
+
+A Java implementation of zlib is available in the Java Development Kit
+http://www.javasoft.com/products/JDK/1.1/docs/api/Package-java.util.zip.html
+See the zlib home page http://www.zlib.org for details.
+
+A Perl interface to zlib written by Paul Marquess <pmarquess@bfsec.bt.co.uk>
+is in the CPAN (Comprehensive Perl Archive Network) sites
+http://www.cpan.org/modules/by-module/Compress/
+
+A Python interface to zlib written by A.M. Kuchling <amk@magnet.com>
+is available in Python 1.5 and later versions, see
+http://www.python.org/doc/lib/module-zlib.html
+
+A zlib binding for TCL written by Andreas Kupries <a.kupries@westend.com>
+is availlable at http://www.westend.com/~kupries/doc/trf/man/man.html
+
+An experimental package to read and write files in .zip format,
+written on top of zlib by Gilles Vollant <info@winimage.com>, is
+available at http://www.winimage.com/zLibDll/unzip.html
+and also in the contrib/minizip directory of zlib.
+
+
+Notes for some targets:
+
+- To build a Windows DLL version, include in a DLL project zlib.def, zlib.rc
+ and all .c files except example.c and minigzip.c; compile with -DZLIB_DLL
+ The zlib DLL support was initially done by Alessandro Iacopetti and is
+ now maintained by Gilles Vollant <info@winimage.com>. Check the zlib DLL
+ home page at http://www.winimage.com/zLibDll
+
+ From Visual Basic, you can call the DLL functions which do not take
+ a structure as argument: compress, uncompress and all gz* functions.
+ See contrib/visual-basic.txt for more information, or get
+ http://www.tcfb.com/dowseware/cmp-z-it.zip
+
+- For 64-bit Irix, deflate.c must be compiled without any optimization.
+ With -O, one libpng test fails. The test works in 32 bit mode (with
+ the -n32 compiler flag). The compiler bug has been reported to SGI.
+
+- zlib doesn't work with gcc 2.6.3 on a DEC 3000/300LX under OSF/1 2.1
+ it works when compiled with cc.
+
+- on Digital Unix 4.0D (formely OSF/1) on AlphaServer, the cc option -std1
+ is necessary to get gzprintf working correctly. This is done by configure.
+
+- zlib doesn't work on HP-UX 9.05 with some versions of /bin/cc. It works
+ with other compilers. Use "make test" to check your compiler.
+
+- gzdopen is not supported on RISCOS, BEOS and by some Mac compilers.
+
+- For Turbo C the small model is supported only with reduced performance to
+ avoid any far allocation; it was tested with -DMAX_WBITS=11 -DMAX_MEM_LEVEL=3
+
+- For PalmOs, see http://www.cs.uit.no/~perm/PASTA/pilot/software.html
+ Per Harald Myrvang <perm@stud.cs.uit.no>
+
+
+Acknowledgments:
+
+ The deflate format used by zlib was defined by Phil Katz. The deflate
+ and zlib specifications were written by L. Peter Deutsch. Thanks to all the
+ people who reported problems and suggested various improvements in zlib;
+ they are too numerous to cite here.
+
+Copyright notice:
+
+ (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
+
+If you use the zlib library in a product, we would appreciate *not*
+receiving lengthy legal documents to sign. The sources are provided
+for free but without warranty of any kind. The library has been
+entirely written by Jean-loup Gailly and Mark Adler; it does not
+include third-party code.
+
+If you redistribute modified sources, we would appreciate that you include
+in the file ChangeLog history information documenting your changes.
diff --git a/zlib/adler32.c b/zlib/adler32.c
new file mode 100644
index 00000000..828a3ff5
--- /dev/null
+++ b/zlib/adler32.c
@@ -0,0 +1,48 @@
+/* adler32.c -- compute the Adler-32 checksum of a data stream
+ * Copyright (C) 1995-2002 Mark Adler
+ * For conditions of distribution and use, see copyright notice in zlib.h
+ */
+
+/* @(#) $Id: adler32.c,v 1.1 2004/10/08 09:44:21 const_k Exp $ */
+
+#include "zlib.h"
+
+#define BASE 65521L /* largest prime smaller than 65536 */
+#define NMAX 5552
+/* NMAX is the largest n such that 255n(n+1)/2 + (n+1)(BASE-1) <= 2^32-1 */
+
+#define DO1(buf,i) {s1 += buf[i]; s2 += s1;}
+#define DO2(buf,i) DO1(buf,i); DO1(buf,i+1);
+#define DO4(buf,i) DO2(buf,i); DO2(buf,i+2);
+#define DO8(buf,i) DO4(buf,i); DO4(buf,i+4);
+#define DO16(buf) DO8(buf,0); DO8(buf,8);
+
+/* ========================================================================= */
+uLong ZEXPORT adler32(adler, buf, len)
+ uLong adler;
+ const Bytef *buf;
+ uInt len;
+{
+ unsigned long s1 = adler & 0xffff;
+ unsigned long s2 = (adler >> 16) & 0xffff;
+ int k;
+
+ if (buf == Z_NULL) return 1L;
+
+ while (len > 0) {
+ k = len < NMAX ? len : NMAX;
+ len -= k;
+ while (k >= 16) {
+ DO16(buf);
+ buf += 16;
+ k -= 16;
+ }
+ if (k != 0) do {
+ s1 += *buf++;
+ s2 += s1;
+ } while (--k);
+ s1 %= BASE;
+ s2 %= BASE;
+ }
+ return (s2 << 16) | s1;
+}
diff --git a/zlib/algorithm.txt b/zlib/algorithm.txt
new file mode 100644
index 00000000..cdc830b5
--- /dev/null
+++ b/zlib/algorithm.txt
@@ -0,0 +1,213 @@
+1. Compression algorithm (deflate)
+
+The deflation algorithm used by gzip (also zip and zlib) is a variation of
+LZ77 (Lempel-Ziv 1977, see reference below). It finds duplicated strings in
+the input data. The second occurrence of a string is replaced by a
+pointer to the previous string, in the form of a pair (distance,
+length). Distances are limited to 32K bytes, and lengths are limited
+to 258 bytes. When a string does not occur anywhere in the previous
+32K bytes, it is emitted as a sequence of literal bytes. (In this
+description, `string' must be taken as an arbitrary sequence of bytes,
+and is not restricted to printable characters.)
+
+Literals or match lengths are compressed with one Huffman tree, and
+match distances are compressed with another tree. The trees are stored
+in a compact form at the start of each block. The blocks can have any
+size (except that the compressed data for one block must fit in
+available memory). A block is terminated when deflate() determines that
+it would be useful to start another block with fresh trees. (This is
+somewhat similar to the behavior of LZW-based _compress_.)
+
+Duplicated strings are found using a hash table. All input strings of
+length 3 are inserted in the hash table. A hash index is computed for
+the next 3 bytes. If the hash chain for this index is not empty, all
+strings in the chain are compared with the current input string, and
+the longest match is selected.
+
+The hash chains are searched starting with the most recent strings, to
+favor small distances and thus take advantage of the Huffman encoding.
+The hash chains are singly linked. There are no deletions from the
+hash chains, the algorithm simply discards matches that are too old.
+
+To avoid a worst-case situation, very long hash chains are arbitrarily
+truncated at a certain length, determined by a runtime option (level
+parameter of deflateInit). So deflate() does not always find the longest
+possible match but generally finds a match which is long enough.
+
+deflate() also defers the selection of matches with a lazy evaluation
+mechanism. After a match of length N has been found, deflate() searches for
+a longer match at the next input byte. If a longer match is found, the
+previous match is truncated to a length of one (thus producing a single
+literal byte) and the process of lazy evaluation begins again. Otherwise,
+the original match is kept, and the next match search is attempted only N
+steps later.
+
+The lazy match evaluation is also subject to a runtime parameter. If
+the current match is long enough, deflate() reduces the search for a longer
+match, thus speeding up the whole process. If compression ratio is more
+important than speed, deflate() attempts a complete second search even if
+the first match is already long enough.
+
+The lazy match evaluation is not performed for the fastest compression
+modes (level parameter 1 to 3). For these fast modes, new strings
+are inserted in the hash table only when no match was found, or
+when the match is not too long. This degrades the compression ratio
+but saves time since there are both fewer insertions and fewer searches.
+
+
+2. Decompression algorithm (inflate)
+
+2.1 Introduction
+
+The real question is, given a Huffman tree, how to decode fast. The most
+important realization is that shorter codes are much more common than
+longer codes, so pay attention to decoding the short codes fast, and let
+the long codes take longer to decode.
+
+inflate() sets up a first level table that covers some number of bits of
+input less than the length of longest code. It gets that many bits from the
+stream, and looks it up in the table. The table will tell if the next
+code is that many bits or less and how many, and if it is, it will tell
+the value, else it will point to the next level table for which inflate()
+grabs more bits and tries to decode a longer code.
+
+How many bits to make the first lookup is a tradeoff between the time it
+takes to decode and the time it takes to build the table. If building the
+table took no time (and if you had infinite memory), then there would only
+be a first level table to cover all the way to the longest code. However,
+building the table ends up taking a lot longer for more bits since short
+codes are replicated many times in such a table. What inflate() does is
+simply to make the number of bits in the first table a variable, and set it
+for the maximum speed.
+
+inflate() sends new trees relatively often, so it is possibly set for a
+smaller first level table than an application that has only one tree for
+all the data. For inflate, which has 286 possible codes for the
+literal/length tree, the size of the first table is nine bits. Also the
+distance trees have 30 possible values, and the size of the first table is
+six bits. Note that for each of those cases, the table ended up one bit
+longer than the ``average'' code length, i.e. the code length of an
+approximately flat code which would be a little more than eight bits for
+286 symbols and a little less than five bits for 30 symbols. It would be
+interesting to see if optimizing the first level table for other
+applications gave values within a bit or two of the flat code size.
+
+
+2.2 More details on the inflate table lookup
+
+Ok, you want to know what this cleverly obfuscated inflate tree actually
+looks like. You are correct that it's not a Huffman tree. It is simply a
+lookup table for the first, let's say, nine bits of a Huffman symbol. The
+symbol could be as short as one bit or as long as 15 bits. If a particular
+symbol is shorter than nine bits, then that symbol's translation is duplicated
+in all those entries that start with that symbol's bits. For example, if the
+symbol is four bits, then it's duplicated 32 times in a nine-bit table. If a
+symbol is nine bits long, it appears in the table once.
+
+If the symbol is longer than nine bits, then that entry in the table points
+to another similar table for the remaining bits. Again, there are duplicated
+entries as needed. The idea is that most of the time the symbol will be short
+and there will only be one table look up. (That's whole idea behind data
+compression in the first place.) For the less frequent long symbols, there
+will be two lookups. If you had a compression method with really long
+symbols, you could have as many levels of lookups as is efficient. For
+inflate, two is enough.
+
+So a table entry either points to another table (in which case nine bits in
+the above example are gobbled), or it contains the translation for the symbol
+and the number of bits to gobble. Then you start again with the next
+ungobbled bit.
+
+You may wonder: why not just have one lookup table for how ever many bits the
+longest symbol is? The reason is that if you do that, you end up spending
+more time filling in duplicate symbol entries than you do actually decoding.
+At least for deflate's output that generates new trees every several 10's of
+kbytes. You can imagine that filling in a 2^15 entry table for a 15-bit code
+would take too long if you're only decoding several thousand symbols. At the
+other extreme, you could make a new table for every bit in the code. In fact,
+that's essentially a Huffman tree. But then you spend two much time
+traversing the tree while decoding, even for short symbols.
+
+So the number of bits for the first lookup table is a trade of the time to
+fill out the table vs. the time spent looking at the second level and above of
+the table.
+
+Here is an example, scaled down:
+
+The code being decoded, with 10 symbols, from 1 to 6 bits long:
+
+A: 0
+B: 10
+C: 1100
+D: 11010
+E: 11011
+F: 11100
+G: 11101
+H: 11110
+I: 111110
+J: 111111
+
+Let's make the first table three bits long (eight entries):
+
+000: A,1
+001: A,1
+010: A,1
+011: A,1
+100: B,2
+101: B,2
+110: -> table X (gobble 3 bits)
+111: -> table Y (gobble 3 bits)
+
+Each entry is what the bits decode to and how many bits that is, i.e. how
+many bits to gobble. Or the entry points to another table, with the number of
+bits to gobble implicit in the size of the table.
+
+Table X is two bits long since the longest code starting with 110 is five bits
+long:
+
+00: C,1
+01: C,1
+10: D,2
+11: E,2
+
+Table Y is three bits long since the longest code starting with 111 is six
+bits long:
+
+000: F,2
+001: F,2
+010: G,2
+011: G,2
+100: H,2
+101: H,2
+110: I,3
+111: J,3
+
+So what we have here are three tables with a total of 20 entries that had to
+be constructed. That's compared to 64 entries for a single table. Or
+compared to 16 entries for a Huffman tree (six two entry tables and one four
+entry table). Assuming that the code ideally represents the probability of
+the symbols, it takes on the average 1.25 lookups per symbol. That's compared
+to one lookup for the single table, or 1.66 lookups per symbol for the
+Huffman tree.
+
+There, I think that gives you a picture of what's going on. For inflate, the
+meaning of a particular symbol is often more than just a letter. It can be a
+byte (a "literal"), or it can be either a length or a distance which
+indicates a base value and a number of bits to fetch after the code that is
+added to the base value. Or it might be the special end-of-block code. The
+data structures created in inftrees.c try to encode all that information
+compactly in the tables.
+
+
+Jean-loup Gailly Mark Adler
+jloup@gzip.org madler@alumni.caltech.edu
+
+
+References:
+
+[LZ77] Ziv J., Lempel A., ``A Universal Algorithm for Sequential Data
+Compression,'' IEEE Transactions on Information Theory, Vol. 23, No. 3,
+pp. 337-343.
+
+``DEFLATE Compressed Data Format Specification'' available in
+ftp://ds.internic.net/rfc/rfc1951.txt
diff --git a/zlib/compress.c b/zlib/compress.c
new file mode 100644
index 00000000..5a977490
--- /dev/null
+++ b/zlib/compress.c
@@ -0,0 +1,68 @@
+/* compress.c -- compress a memory buffer
+ * Copyright (C) 1995-2002 Jean-loup Gailly.
+ * For conditions of distribution and use, see copyright notice in zlib.h
+ */
+
+/* @(#) $Id: compress.c,v 1.1 2004/10/08 09:44:22 const_k Exp $ */
+
+#include "zlib.h"
+
+/* ===========================================================================
+ Compresses the source buffer into the destination buffer. The level
+ parameter has the same meaning as in deflateInit. sourceLen is the byte
+ length of the source buffer. Upon entry, destLen is the total size of the
+ destination buffer, which must be at least 0.1% larger than sourceLen plus
+ 12 bytes. Upon exit, destLen is the actual size of the compressed buffer.
+
+ compress2 returns Z_OK if success, Z_MEM_ERROR if there was not enough
+ memory, Z_BUF_ERROR if there was not enough room in the output buffer,
+ Z_STREAM_ERROR if the level parameter is invalid.
+*/
+int ZEXPORT compress2 (dest, destLen, source, sourceLen, level)
+ Bytef *dest;
+ uLongf *destLen;
+ const Bytef *source;
+ uLong sourceLen;
+ int level;
+{
+ z_stream stream;
+ int err;
+
+ stream.next_in = (Bytef*)source;
+ stream.avail_in = (uInt)sourceLen;
+#ifdef MAXSEG_64K
+ /* Check for source > 64K on 16-bit machine: */
+ if ((uLong)stream.avail_in != sourceLen) return Z_BUF_ERROR;
+#endif
+ stream.next_out = dest;
+ stream.avail_out = (uInt)*destLen;
+ if ((uLong)stream.avail_out != *destLen) return Z_BUF_ERROR;
+
+ stream.zalloc = (alloc_func)0;
+ stream.zfree = (free_func)0;
+ stream.opaque = (voidpf)0;
+
+ err = deflateInit(&stream, level);
+ if (err != Z_OK) return err;
+
+ err = deflate(&stream, Z_FINISH);
+ if (err != Z_STREAM_END) {
+ deflateEnd(&stream);
+ return err == Z_OK ? Z_BUF_ERROR : err;
+ }
+ *destLen = stream.total_out;
+
+ err = deflateEnd(&stream);
+ return err;
+}
+
+/* ===========================================================================
+ */
+int ZEXPORT compress (dest, destLen, source, sourceLen)
+ Bytef *dest;
+ uLongf *destLen;
+ const Bytef *source;
+ uLong sourceLen;
+{
+ return compress2(dest, destLen, source, sourceLen, Z_DEFAULT_COMPRESSION);
+}
diff --git a/zlib/configure b/zlib/configure
new file mode 100755
index 00000000..e8942359
--- /dev/null
+++ b/zlib/configure
@@ -0,0 +1,212 @@
+#!/bin/sh
+# configure script for zlib. This script is needed only if
+# you wish to build a shared library and your system supports them,
+# of if you need special compiler, flags or install directory.
+# Otherwise, you can just use directly "make test; make install"
+#
+# To create a shared library, use "configure --shared"; by default a static
+# library is created. If the primitive shared library support provided here
+# does not work, use ftp://prep.ai.mit.edu/pub/gnu/libtool-*.tar.gz
+#
+# To impose specific compiler or flags or install directory, use for example:
+# prefix=$HOME CC=cc CFLAGS="-O4" ./configure
+# or for csh/tcsh users:
+# (setenv prefix $HOME; setenv CC cc; setenv CFLAGS "-O4"; ./configure)
+# LDSHARED is the command to be used to create a shared library
+
+# Incorrect settings of CC or CFLAGS may prevent creating a shared library.
+# If you have problems, try without defining CC and CFLAGS before reporting
+# an error.
+
+LIBS=libz.a
+SHAREDLIB=libz.so
+VER=`sed -n -e '/VERSION "/s/.*"\(.*\)".*/\1/p' < zlib.h`
+AR=${AR-"ar rc"}
+RANLIB=${RANLIB-"ranlib"}
+prefix=${prefix-/usr/local}
+exec_prefix=${exec_prefix-'${prefix}'}
+libdir=${libdir-'${exec_prefix}/lib'}
+includedir=${includedir-'${prefix}/include'}
+shared_ext='.so'
+shared=0
+gcc=0
+old_cc="$CC"
+old_cflags="$CFLAGS"
+
+while test $# -ge 1
+do
+case "$1" in
+ -h* | --h*)
+ echo 'usage:'
+ echo ' configure [--shared] [--prefix=PREFIX] [--exec_prefix=EXPREFIX]'
+ echo ' [--libdir=LIBDIR] [--includedir=INCLUDEDIR]'
+ exit 0;;
+ -p*=* | --p*=*) prefix=`echo $1 | sed 's/[-a-z_]*=//'`; shift;;
+ -e*=* | --e*=*) exec_prefix=`echo $1 | sed 's/[-a-z_]*=//'`; shift;;
+ -l*=* | --libdir=*) libdir=`echo $1 | sed 's/[-a-z_]*=//'`; shift;;
+ -i*=* | --includedir=*) includedir=`echo $1 | sed 's/[-a-z_]*=//'`;shift;;
+ -p* | --p*) prefix="$2"; shift; shift;;
+ -e* | --e*) exec_prefix="$2"; shift; shift;;
+ -l* | --l*) libdir="$2"; shift; shift;;
+ -i* | --i*) includedir="$2"; shift; shift;;
+ -s* | --s*) shared=1; shift;;
+ esac
+done
+
+test=ztest$$
+cat > $test.c <<EOF
+extern int getchar();
+int hello() {return getchar();}
+EOF
+
+test -z "$CC" && echo Checking for gcc...
+cc=${CC-gcc}
+cflags=${CFLAGS-"-O3"}
+# to force the asm version use: CFLAGS="-O3 -DASMV" ./configure
+case "$cc" in
+ *gcc*) gcc=1;;
+esac
+
+if test "$gcc" -eq 1 && ($cc -c $cflags $test.c) 2>/dev/null; then
+ CC="$cc"
+ SFLAGS=${CFLAGS-"-fPIC -O3"}
+ CFLAGS="$cflags"
+ case `(uname -s || echo unknown) 2>/dev/null` in
+ Linux | linux) LDSHARED=${LDSHARED-"gcc -shared -Wl,-soname,libz.so.1"};;
+ *) LDSHARED=${LDSHARED-"gcc -shared"};;
+ esac
+else
+ # find system name and corresponding cc options
+ CC=${CC-cc}
+ case `(uname -sr || echo unknown) 2>/dev/null` in
+ HP-UX*) SFLAGS=${CFLAGS-"-O +z"}
+ CFLAGS=${CFLAGS-"-O"}
+# LDSHARED=${LDSHARED-"ld -b +vnocompatwarnings"}
+ LDSHARED=${LDSHARED-"ld -b"}
+ shared_ext='.sl'
+ SHAREDLIB='libz.sl';;
+ IRIX*) SFLAGS=${CFLAGS-"-ansi -O2 -rpath ."}
+ CFLAGS=${CFLAGS-"-ansi -O2"}
+ LDSHARED=${LDSHARED-"cc -shared"};;
+ OSF1\ V4*) SFLAGS=${CFLAGS-"-O -std1"}
+ CFLAGS=${CFLAGS-"-O -std1"}
+ LDSHARED=${LDSHARED-"cc -shared -Wl,-soname,$SHAREDLIB -Wl,-msym -Wl,-rpath,$(libdir) -Wl,-set_version,${VER}:1.0"};;
+ OSF1*) SFLAGS=${CFLAGS-"-O -std1"}
+ CFLAGS=${CFLAGS-"-O -std1"}
+ LDSHARED=${LDSHARED-"cc -shared"};;
+ QNX*) SFLAGS=${CFLAGS-"-4 -O"}
+ CFLAGS=${CFLAGS-"-4 -O"}
+ LDSHARED=${LDSHARED-"cc"}
+ RANLIB=${RANLIB-"true"}
+ AR="cc -A";;
+ SCO_SV\ 3.2*) SFLAGS=${CFLAGS-"-O3 -dy -KPIC "}
+ CFLAGS=${CFLAGS-"-O3"}
+ LDSHARED=${LDSHARED-"cc -dy -KPIC -G"};;
+ SunOS\ 5*) SFLAGS=${CFLAGS-"-fast -xcg89 -KPIC -R."}
+ CFLAGS=${CFLAGS-"-fast -xcg89"}
+ LDSHARED=${LDSHARED-"cc -G"};;
+ SunOS\ 4*) SFLAGS=${CFLAGS-"-O2 -PIC"}
+ CFLAGS=${CFLAGS-"-O2"}
+ LDSHARED=${LDSHARED-"ld"};;
+ UNIX_System_V\ 4.2.0)
+ SFLAGS=${CFLAGS-"-KPIC -O"}
+ CFLAGS=${CFLAGS-"-O"}
+ LDSHARED=${LDSHARED-"cc -G"};;
+ UNIX_SV\ 4.2MP)
+ SFLAGS=${CFLAGS-"-Kconform_pic -O"}
+ CFLAGS=${CFLAGS-"-O"}
+ LDSHARED=${LDSHARED-"cc -G"};;
+ # send working options for other systems to support@gzip.org
+ *) SFLAGS=${CFLAGS-"-O"}
+ CFLAGS=${CFLAGS-"-O"}
+ LDSHARED=${LDSHARED-"cc -shared"};;
+ esac
+fi
+
+if test $shared -eq 1; then
+ echo Checking for shared library support...
+ # we must test in two steps (cc then ld), required at least on SunOS 4.x
+ if test "`($CC -c $SFLAGS $test.c) 2>&1`" = "" &&
+ test "`($LDSHARED -o $test$shared_ext $test.o) 2>&1`" = ""; then
+ CFLAGS="$SFLAGS"
+ LIBS="$SHAREDLIB.$VER"
+ echo Building shared library $SHAREDLIB.$VER with $CC.
+ elif test -z "$old_cc" -a -z "$old_cflags"; then
+ echo No shared library suppport.
+ shared=0;
+ else
+ echo 'No shared library suppport; try without defining CC and CFLAGS'
+ shared=0;
+ fi
+fi
+if test $shared -eq 0; then
+ LDSHARED="$CC"
+ echo Building static library $LIBS version $VER with $CC.
+fi
+
+cat > $test.c <<EOF
+#include <unistd.h>
+int main() { return 0; }
+EOF
+if test "`($CC -c $CFLAGS $test.c) 2>&1`" = ""; then
+ CFLAGS="$CFLAGS -DHAVE_UNISTD_H"
+ echo "Checking for unistd.h... Yes."
+else
+ echo "Checking for unistd.h... No."
+fi
+
+cat > $test.c <<EOF
+#include <errno.h>
+int main() { return 0; }
+EOF
+if test "`($CC -c $CFLAGS $test.c) 2>&1`" = ""; then
+ echo "Checking for errno.h... Yes."
+else
+ echo "Checking for errno.h... No."
+ CFLAGS="$CFLAGS -DNO_ERRNO_H"
+fi
+
+cat > $test.c <<EOF
+#include <sys/types.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+caddr_t hello() {
+ return mmap((caddr_t)0, (off_t)0, PROT_READ, MAP_SHARED, 0, (off_t)0);
+}
+EOF
+if test "`($CC -c $CFLAGS $test.c) 2>&1`" = ""; then
+ CFLAGS="$CFLAGS -DUSE_MMAP"
+ echo Checking for mmap support... Yes.
+else
+ echo Checking for mmap support... No.
+fi
+
+CPP=${CPP-"$CC -E"}
+case $CFLAGS in
+ *ASMV*)
+ if test "`nm $test.o | grep _hello`" = ""; then
+ CPP="$CPP -DNO_UNDERLINE"
+ echo Checking for underline in external names... No.
+ else
+ echo Checking for underline in external names... Yes.
+ fi;;
+esac
+
+rm -f $test.[co] $test$shared_ext
+
+# udpate Makefile
+sed < Makefile.in "
+/^CC *=/s%=.*%=$CC%
+/^CFLAGS *=/s%=.*%=$CFLAGS%
+/^CPP *=/s%=.*%=$CPP%
+/^LDSHARED *=/s%=.*%=$LDSHARED%
+/^LIBS *=/s%=.*%=$LIBS%
+/^SHAREDLIB *=/s%=.*%=$SHAREDLIB%
+/^AR *=/s%=.*%=$AR%
+/^RANLIB *=/s%=.*%=$RANLIB%
+/^VER *=/s%=.*%=$VER%
+/^prefix *=/s%=.*%=$prefix%
+/^exec_prefix *=/s%=.*%=$exec_prefix%
+/^libdir *=/s%=.*%=$libdir%
+/^includedir *=/s%=.*%=$includedir%
+" > Makefile
diff --git a/zlib/crc32.c b/zlib/crc32.c
new file mode 100644
index 00000000..b9a8a923
--- /dev/null
+++ b/zlib/crc32.c
@@ -0,0 +1,162 @@
+/* crc32.c -- compute the CRC-32 of a data stream
+ * Copyright (C) 1995-2002 Mark Adler
+ * For conditions of distribution and use, see copyright notice in zlib.h
+ */
+
+/* @(#) $Id: crc32.c,v 1.1 2004/10/08 09:44:23 const_k Exp $ */
+
+#include "zlib.h"
+
+#define local static
+
+#ifdef DYNAMIC_CRC_TABLE
+
+local int crc_table_empty = 1;
+local uLongf crc_table[256];
+local void make_crc_table OF((void));
+
+/*
+ Generate a table for a byte-wise 32-bit CRC calculation on the polynomial:
+ x^32+x^26+x^23+x^22+x^16+x^12+x^11+x^10+x^8+x^7+x^5+x^4+x^2+x+1.
+
+ Polynomials over GF(2) are represented in binary, one bit per coefficient,
+ with the lowest powers in the most significant bit. Then adding polynomials
+ is just exclusive-or, and multiplying a polynomial by x is a right shift by
+ one. If we call the above polynomial p, and represent a byte as the
+ polynomial q, also with the lowest power in the most significant bit (so the
+ byte 0xb1 is the polynomial x^7+x^3+x+1), then the CRC is (q*x^32) mod p,
+ where a mod b means the remainder after dividing a by b.
+
+ This calculation is done using the shift-register method of multiplying and
+ taking the remainder. The register is initialized to zero, and for each
+ incoming bit, x^32 is added mod p to the register if the bit is a one (where
+ x^32 mod p is p+x^32 = x^26+...+1), and the register is multiplied mod p by
+ x (which is shifting right by one and adding x^32 mod p if the bit shifted
+ out is a one). We start with the highest power (least significant bit) of
+ q and repeat for all eight bits of q.
+
+ The table is simply the CRC of all possible eight bit values. This is all
+ the information needed to generate CRC's on data a byte at a time for all
+ combinations of CRC register values and incoming bytes.
+*/
+local void make_crc_table()
+{
+ uLong c;
+ int n, k;
+ uLong poly; /* polynomial exclusive-or pattern */
+ /* terms of polynomial defining this crc (except x^32): */
+ static const Byte p[] = {0,1,2,4,5,7,8,10,11,12,16,22,23,26};
+
+ /* make exclusive-or pattern from polynomial (0xedb88320L) */
+ poly = 0L;
+ for (n = 0; n < sizeof(p)/sizeof(Byte); n++)
+ poly |= 1L << (31 - p[n]);
+
+ for (n = 0; n < 256; n++)
+ {
+ c = (uLong)n;
+ for (k = 0; k < 8; k++)
+ c = c & 1 ? poly ^ (c >> 1) : c >> 1;
+ crc_table[n] = c;
+ }
+ crc_table_empty = 0;
+}
+#else
+/* ========================================================================
+ * Table of CRC-32's of all single-byte values (made by make_crc_table)
+ */
+local const uLongf crc_table[256] = {
+ 0x00000000L, 0x77073096L, 0xee0e612cL, 0x990951baL, 0x076dc419L,
+ 0x706af48fL, 0xe963a535L, 0x9e6495a3L, 0x0edb8832L, 0x79dcb8a4L,
+ 0xe0d5e91eL, 0x97d2d988L, 0x09b64c2bL, 0x7eb17cbdL, 0xe7b82d07L,
+ 0x90bf1d91L, 0x1db71064L, 0x6ab020f2L, 0xf3b97148L, 0x84be41deL,
+ 0x1adad47dL, 0x6ddde4ebL, 0xf4d4b551L, 0x83d385c7L, 0x136c9856L,
+ 0x646ba8c0L, 0xfd62f97aL, 0x8a65c9ecL, 0x14015c4fL, 0x63066cd9L,
+ 0xfa0f3d63L, 0x8d080df5L, 0x3b6e20c8L, 0x4c69105eL, 0xd56041e4L,
+ 0xa2677172L, 0x3c03e4d1L, 0x4b04d447L, 0xd20d85fdL, 0xa50ab56bL,
+ 0x35b5a8faL, 0x42b2986cL, 0xdbbbc9d6L, 0xacbcf940L, 0x32d86ce3L,
+ 0x45df5c75L, 0xdcd60dcfL, 0xabd13d59L, 0x26d930acL, 0x51de003aL,
+ 0xc8d75180L, 0xbfd06116L, 0x21b4f4b5L, 0x56b3c423L, 0xcfba9599L,
+ 0xb8bda50fL, 0x2802b89eL, 0x5f058808L, 0xc60cd9b2L, 0xb10be924L,
+ 0x2f6f7c87L, 0x58684c11L, 0xc1611dabL, 0xb6662d3dL, 0x76dc4190L,
+ 0x01db7106L, 0x98d220bcL, 0xefd5102aL, 0x71b18589L, 0x06b6b51fL,
+ 0x9fbfe4a5L, 0xe8b8d433L, 0x7807c9a2L, 0x0f00f934L, 0x9609a88eL,
+ 0xe10e9818L, 0x7f6a0dbbL, 0x086d3d2dL, 0x91646c97L, 0xe6635c01L,
+ 0x6b6b51f4L, 0x1c6c6162L, 0x856530d8L, 0xf262004eL, 0x6c0695edL,
+ 0x1b01a57bL, 0x8208f4c1L, 0xf50fc457L, 0x65b0d9c6L, 0x12b7e950L,
+ 0x8bbeb8eaL, 0xfcb9887cL, 0x62dd1ddfL, 0x15da2d49L, 0x8cd37cf3L,
+ 0xfbd44c65L, 0x4db26158L, 0x3ab551ceL, 0xa3bc0074L, 0xd4bb30e2L,
+ 0x4adfa541L, 0x3dd895d7L, 0xa4d1c46dL, 0xd3d6f4fbL, 0x4369e96aL,
+ 0x346ed9fcL, 0xad678846L, 0xda60b8d0L, 0x44042d73L, 0x33031de5L,
+ 0xaa0a4c5fL, 0xdd0d7cc9L, 0x5005713cL, 0x270241aaL, 0xbe0b1010L,
+ 0xc90c2086L, 0x5768b525L, 0x206f85b3L, 0xb966d409L, 0xce61e49fL,
+ 0x5edef90eL, 0x29d9c998L, 0xb0d09822L, 0xc7d7a8b4L, 0x59b33d17L,
+ 0x2eb40d81L, 0xb7bd5c3bL, 0xc0ba6cadL, 0xedb88320L, 0x9abfb3b6L,
+ 0x03b6e20cL, 0x74b1d29aL, 0xead54739L, 0x9dd277afL, 0x04db2615L,
+ 0x73dc1683L, 0xe3630b12L, 0x94643b84L, 0x0d6d6a3eL, 0x7a6a5aa8L,
+ 0xe40ecf0bL, 0x9309ff9dL, 0x0a00ae27L, 0x7d079eb1L, 0xf00f9344L,
+ 0x8708a3d2L, 0x1e01f268L, 0x6906c2feL, 0xf762575dL, 0x806567cbL,
+ 0x196c3671L, 0x6e6b06e7L, 0xfed41b76L, 0x89d32be0L, 0x10da7a5aL,
+ 0x67dd4accL, 0xf9b9df6fL, 0x8ebeeff9L, 0x17b7be43L, 0x60b08ed5L,
+ 0xd6d6a3e8L, 0xa1d1937eL, 0x38d8c2c4L, 0x4fdff252L, 0xd1bb67f1L,
+ 0xa6bc5767L, 0x3fb506ddL, 0x48b2364bL, 0xd80d2bdaL, 0xaf0a1b4cL,
+ 0x36034af6L, 0x41047a60L, 0xdf60efc3L, 0xa867df55L, 0x316e8eefL,
+ 0x4669be79L, 0xcb61b38cL, 0xbc66831aL, 0x256fd2a0L, 0x5268e236L,
+ 0xcc0c7795L, 0xbb0b4703L, 0x220216b9L, 0x5505262fL, 0xc5ba3bbeL,
+ 0xb2bd0b28L, 0x2bb45a92L, 0x5cb36a04L, 0xc2d7ffa7L, 0xb5d0cf31L,
+ 0x2cd99e8bL, 0x5bdeae1dL, 0x9b64c2b0L, 0xec63f226L, 0x756aa39cL,
+ 0x026d930aL, 0x9c0906a9L, 0xeb0e363fL, 0x72076785L, 0x05005713L,
+ 0x95bf4a82L, 0xe2b87a14L, 0x7bb12baeL, 0x0cb61b38L, 0x92d28e9bL,
+ 0xe5d5be0dL, 0x7cdcefb7L, 0x0bdbdf21L, 0x86d3d2d4L, 0xf1d4e242L,
+ 0x68ddb3f8L, 0x1fda836eL, 0x81be16cdL, 0xf6b9265bL, 0x6fb077e1L,
+ 0x18b74777L, 0x88085ae6L, 0xff0f6a70L, 0x66063bcaL, 0x11010b5cL,
+ 0x8f659effL, 0xf862ae69L, 0x616bffd3L, 0x166ccf45L, 0xa00ae278L,
+ 0xd70dd2eeL, 0x4e048354L, 0x3903b3c2L, 0xa7672661L, 0xd06016f7L,
+ 0x4969474dL, 0x3e6e77dbL, 0xaed16a4aL, 0xd9d65adcL, 0x40df0b66L,
+ 0x37d83bf0L, 0xa9bcae53L, 0xdebb9ec5L, 0x47b2cf7fL, 0x30b5ffe9L,
+ 0xbdbdf21cL, 0xcabac28aL, 0x53b39330L, 0x24b4a3a6L, 0xbad03605L,
+ 0xcdd70693L, 0x54de5729L, 0x23d967bfL, 0xb3667a2eL, 0xc4614ab8L,
+ 0x5d681b02L, 0x2a6f2b94L, 0xb40bbe37L, 0xc30c8ea1L, 0x5a05df1bL,
+ 0x2d02ef8dL
+};
+#endif
+
+/* =========================================================================
+ * This function can be used by asm versions of crc32()
+ */
+const uLongf * ZEXPORT get_crc_table()
+{
+#ifdef DYNAMIC_CRC_TABLE
+ if (crc_table_empty) make_crc_table();
+#endif
+ return (const uLongf *)crc_table;
+}
+
+/* ========================================================================= */
+#define DO1(buf) crc = crc_table[((int)crc ^ (*buf++)) & 0xff] ^ (crc >> 8);
+#define DO2(buf) DO1(buf); DO1(buf);
+#define DO4(buf) DO2(buf); DO2(buf);
+#define DO8(buf) DO4(buf); DO4(buf);
+
+/* ========================================================================= */
+uLong ZEXPORT crc32(crc, buf, len)
+ uLong crc;
+ const Bytef *buf;
+ uInt len;
+{
+ if (buf == Z_NULL) return 0L;
+#ifdef DYNAMIC_CRC_TABLE
+ if (crc_table_empty)
+ make_crc_table();
+#endif
+ crc = crc ^ 0xffffffffL;
+ while (len >= 8)
+ {
+ DO8(buf);
+ len -= 8;
+ }
+ if (len) do {
+ DO1(buf);
+ } while (--len);
+ return crc ^ 0xffffffffL;
+}
diff --git a/zlib/deflate.c b/zlib/deflate.c
new file mode 100644
index 00000000..57a590c2
--- /dev/null
+++ b/zlib/deflate.c
@@ -0,0 +1,1350 @@
+/* deflate.c -- compress data using the deflation algorithm
+ * Copyright (C) 1995-2002 Jean-loup Gailly.
+ * For conditions of distribution and use, see copyright notice in zlib.h
+ */
+
+/*
+ * ALGORITHM
+ *
+ * The "deflation" process depends on being able to identify portions
+ * of the input text which are identical to earlier input (within a
+ * sliding window trailing behind the input currently being processed).
+ *
+ * The most straightforward technique turns out to be the fastest for
+ * most input files: try all possible matches and select the longest.
+ * The key feature of this algorithm is that insertions into the string
+ * dictionary are very simple and thus fast, and deletions are avoided
+ * completely. Insertions are performed at each input character, whereas
+ * string matches are performed only when the previous match ends. So it
+ * is preferable to spend more time in matches to allow very fast string
+ * insertions and avoid deletions. The matching algorithm for small
+ * strings is inspired from that of Rabin & Karp. A brute force approach
+ * is used to find longer strings when a small match has been found.
+ * A similar algorithm is used in comic (by Jan-Mark Wams) and freeze
+ * (by Leonid Broukhis).
+ * A previous version of this file used a more sophisticated algorithm
+ * (by Fiala and Greene) which is guaranteed to run in linear amortized
+ * time, but has a larger average cost, uses more memory and is patented.
+ * However the F&G algorithm may be faster for some highly redundant
+ * files if the parameter max_chain_length (described below) is too large.
+ *
+ * ACKNOWLEDGEMENTS
+ *
+ * The idea of lazy evaluation of matches is due to Jan-Mark Wams, and
+ * I found it in 'freeze' written by Leonid Broukhis.
+ * Thanks to many people for bug reports and testing.
+ *
+ * REFERENCES
+ *
+ * Deutsch, L.P.,"DEFLATE Compressed Data Format Specification".
+ * Available in ftp://ds.internic.net/rfc/rfc1951.txt
+ *
+ * A description of the Rabin and Karp algorithm is given in the book
+ * "Algorithms" by R. Sedgewick, Addison-Wesley, p252.
+ *
+ * Fiala,E.R., and Greene,D.H.
+ * Data Compression with Finite Windows, Comm.ACM, 32,4 (1989) 490-595
+ *
+ */
+
+/* @(#) $Id: deflate.c,v 1.1 2004/10/08 09:44:24 const_k Exp $ */
+
+#include "deflate.h"
+
+const char deflate_copyright[] =
+ " deflate 1.1.4 Copyright 1995-2002 Jean-loup Gailly ";
+/*
+ If you use the zlib library in a product, an acknowledgment is welcome
+ in the documentation of your product. If for some reason you cannot
+ include such an acknowledgment, I would appreciate that you keep this
+ copyright string in the executable of your product.
+ */
+
+/* ===========================================================================
+ * Function prototypes.
+ */
+typedef enum {
+ need_more, /* block not completed, need more input or more output */
+ block_done, /* block flush performed */
+ finish_started, /* finish started, need only more output at next deflate */
+ finish_done /* finish done, accept no more input or output */
+} block_state;
+
+typedef block_state (*compress_func) OF((deflate_state *s, int flush));
+/* Compression function. Returns the block state after the call. */
+
+local void fill_window OF((deflate_state *s));
+local block_state deflate_stored OF((deflate_state *s, int flush));
+local block_state deflate_fast OF((deflate_state *s, int flush));
+local block_state deflate_slow OF((deflate_state *s, int flush));
+local void lm_init OF((deflate_state *s));
+local void putShortMSB OF((deflate_state *s, uInt b));
+local void flush_pending OF((z_streamp strm));
+local int read_buf OF((z_streamp strm, Bytef *buf, unsigned size));
+#ifdef ASMV
+ void match_init OF((void)); /* asm code initialization */
+ uInt longest_match OF((deflate_state *s, IPos cur_match));
+#else
+local uInt longest_match OF((deflate_state *s, IPos cur_match));
+#endif
+
+#ifdef DEBUG
+local void check_match OF((deflate_state *s, IPos start, IPos match,
+ int length));
+#endif
+
+/* ===========================================================================
+ * Local data
+ */
+
+#define NIL 0
+/* Tail of hash chains */
+
+#ifndef TOO_FAR
+# define TOO_FAR 4096
+#endif
+/* Matches of length 3 are discarded if their distance exceeds TOO_FAR */
+
+#define MIN_LOOKAHEAD (MAX_MATCH+MIN_MATCH+1)
+/* Minimum amount of lookahead, except at the end of the input file.
+ * See deflate.c for comments about the MIN_MATCH+1.
+ */
+
+/* Values for max_lazy_match, good_match and max_chain_length, depending on
+ * the desired pack level (0..9). The values given below have been tuned to
+ * exclude worst case performance for pathological files. Better values may be
+ * found for specific files.
+ */
+typedef struct config_s {
+ ush good_length; /* reduce lazy search above this match length */
+ ush max_lazy; /* do not perform lazy search above this match length */
+ ush nice_length; /* quit search above this match length */
+ ush max_chain;
+ compress_func func;
+} config;
+
+local const config configuration_table[10] = {
+/* good lazy nice chain */
+/* 0 */ {0, 0, 0, 0, deflate_stored}, /* store only */
+/* 1 */ {4, 4, 8, 4, deflate_fast}, /* maximum speed, no lazy matches */
+/* 2 */ {4, 5, 16, 8, deflate_fast},
+/* 3 */ {4, 6, 32, 32, deflate_fast},
+
+/* 4 */ {4, 4, 16, 16, deflate_slow}, /* lazy matches */
+/* 5 */ {8, 16, 32, 32, deflate_slow},
+/* 6 */ {8, 16, 128, 128, deflate_slow},
+/* 7 */ {8, 32, 128, 256, deflate_slow},
+/* 8 */ {32, 128, 258, 1024, deflate_slow},
+/* 9 */ {32, 258, 258, 4096, deflate_slow}}; /* maximum compression */
+
+/* Note: the deflate() code requires max_lazy >= MIN_MATCH and max_chain >= 4
+ * For deflate_fast() (levels <= 3) good is ignored and lazy has a different
+ * meaning.
+ */
+
+#define EQUAL 0
+/* result of memcmp for equal strings */
+
+struct static_tree_desc_s {int dummy;}; /* for buggy compilers */
+
+/* ===========================================================================
+ * Update a hash value with the given input byte
+ * IN assertion: all calls to to UPDATE_HASH are made with consecutive
+ * input characters, so that a running hash key can be computed from the
+ * previous key instead of complete recalculation each time.
+ */
+#define UPDATE_HASH(s,h,c) (h = (((h)<<s->hash_shift) ^ (c)) & s->hash_mask)
+
+
+/* ===========================================================================
+ * Insert string str in the dictionary and set match_head to the previous head
+ * of the hash chain (the most recent string with same hash key). Return
+ * the previous length of the hash chain.
+ * If this file is compiled with -DFASTEST, the compression level is forced
+ * to 1, and no hash chains are maintained.
+ * IN assertion: all calls to to INSERT_STRING are made with consecutive
+ * input characters and the first MIN_MATCH bytes of str are valid
+ * (except for the last MIN_MATCH-1 bytes of the input file).
+ */
+#ifdef FASTEST
+#define INSERT_STRING(s, str, match_head) \
+ (UPDATE_HASH(s, s->ins_h, s->window[(str) + (MIN_MATCH-1)]), \
+ match_head = s->head[s->ins_h], \
+ s->head[s->ins_h] = (Pos)(str))
+#else
+#define INSERT_STRING(s, str, match_head) \
+ (UPDATE_HASH(s, s->ins_h, s->window[(str) + (MIN_MATCH-1)]), \
+ s->prev[(str) & s->w_mask] = match_head = s->head[s->ins_h], \
+ s->head[s->ins_h] = (Pos)(str))
+#endif
+
+/* ===========================================================================
+ * Initialize the hash table (avoiding 64K overflow for 16 bit systems).
+ * prev[] will be initialized on the fly.
+ */
+#define CLEAR_HASH(s) \
+ s->head[s->hash_size-1] = NIL; \
+ zmemzero((Bytef *)s->head, (unsigned)(s->hash_size-1)*sizeof(*s->head));
+
+/* ========================================================================= */
+int ZEXPORT deflateInit_(strm, level, version, stream_size)
+ z_streamp strm;
+ int level;
+ const char *version;
+ int stream_size;
+{
+ return deflateInit2_(strm, level, Z_DEFLATED, MAX_WBITS, DEF_MEM_LEVEL,
+ Z_DEFAULT_STRATEGY, version, stream_size);
+ /* To do: ignore strm->next_in if we use it as window */
+}
+
+/* ========================================================================= */
+int ZEXPORT deflateInit2_(strm, level, method, windowBits, memLevel, strategy,
+ version, stream_size)
+ z_streamp strm;
+ int level;
+ int method;
+ int windowBits;
+ int memLevel;
+ int strategy;
+ const char *version;
+ int stream_size;
+{
+ deflate_state *s;
+ int noheader = 0;
+ static const char* my_version = ZLIB_VERSION;
+
+ ushf *overlay;
+ /* We overlay pending_buf and d_buf+l_buf. This works since the average
+ * output size for (length,distance) codes is <= 24 bits.
+ */
+
+ if (version == Z_NULL || version[0] != my_version[0] ||
+ stream_size != sizeof(z_stream)) {
+ return Z_VERSION_ERROR;
+ }
+ if (strm == Z_NULL) return Z_STREAM_ERROR;
+
+ strm->msg = Z_NULL;
+ if (strm->zalloc == Z_NULL) {
+ strm->zalloc = zcalloc;
+ strm->opaque = (voidpf)0;
+ }
+ if (strm->zfree == Z_NULL) strm->zfree = zcfree;
+
+ if (level == Z_DEFAULT_COMPRESSION) level = 6;
+#ifdef FASTEST
+ level = 1;
+#endif
+
+ if (windowBits < 0) { /* undocumented feature: suppress zlib header */
+ noheader = 1;
+ windowBits = -windowBits;
+ }
+ if (memLevel < 1 || memLevel > MAX_MEM_LEVEL || method != Z_DEFLATED ||
+ windowBits < 9 || windowBits > 15 || level < 0 || level > 9 ||
+ strategy < 0 || strategy > Z_HUFFMAN_ONLY) {
+ return Z_STREAM_ERROR;
+ }
+ s = (deflate_state *) ZALLOC(strm, 1, sizeof(deflate_state));
+ if (s == Z_NULL) return Z_MEM_ERROR;
+ strm->state = (struct internal_state FAR *)s;
+ s->strm = strm;
+
+ s->noheader = noheader;
+ s->w_bits = windowBits;
+ s->w_size = 1 << s->w_bits;
+ s->w_mask = s->w_size - 1;
+
+ s->hash_bits = memLevel + 7;
+ s->hash_size = 1 << s->hash_bits;
+ s->hash_mask = s->hash_size - 1;
+ s->hash_shift = ((s->hash_bits+MIN_MATCH-1)/MIN_MATCH);
+
+ s->window = (Bytef *) ZALLOC(strm, s->w_size, 2*sizeof(Byte));
+ s->prev = (Posf *) ZALLOC(strm, s->w_size, sizeof(Pos));
+ s->head = (Posf *) ZALLOC(strm, s->hash_size, sizeof(Pos));
+
+ s->lit_bufsize = 1 << (memLevel + 6); /* 16K elements by default */
+
+ overlay = (ushf *) ZALLOC(strm, s->lit_bufsize, sizeof(ush)+2);
+ s->pending_buf = (uchf *) overlay;
+ s->pending_buf_size = (ulg)s->lit_bufsize * (sizeof(ush)+2L);
+
+ if (s->window == Z_NULL || s->prev == Z_NULL || s->head == Z_NULL ||
+ s->pending_buf == Z_NULL) {
+ strm->msg = (char*)ERR_MSG(Z_MEM_ERROR);
+ deflateEnd (strm);
+ return Z_MEM_ERROR;
+ }
+ s->d_buf = overlay + s->lit_bufsize/sizeof(ush);
+ s->l_buf = s->pending_buf + (1+sizeof(ush))*s->lit_bufsize;
+
+ s->level = level;
+ s->strategy = strategy;
+ s->method = (Byte)method;
+
+ return deflateReset(strm);
+}
+
+/* ========================================================================= */
+int ZEXPORT deflateSetDictionary (strm, dictionary, dictLength)
+ z_streamp strm;
+ const Bytef *dictionary;
+ uInt dictLength;
+{
+ deflate_state *s;
+ uInt length = dictLength;
+ uInt n;
+ IPos hash_head = 0;
+
+ if (strm == Z_NULL || strm->state == Z_NULL || dictionary == Z_NULL ||
+ strm->state->status != INIT_STATE) return Z_STREAM_ERROR;
+
+ s = strm->state;
+ strm->adler = adler32(strm->adler, dictionary, dictLength);
+
+ if (length < MIN_MATCH) return Z_OK;
+ if (length > MAX_DIST(s)) {
+ length = MAX_DIST(s);
+#ifndef USE_DICT_HEAD
+ dictionary += dictLength - length; /* use the tail of the dictionary */
+#endif
+ }
+ zmemcpy(s->window, dictionary, length);
+ s->strstart = length;
+ s->block_start = (long)length;
+
+ /* Insert all strings in the hash table (except for the last two bytes).
+ * s->lookahead stays null, so s->ins_h will be recomputed at the next
+ * call of fill_window.
+ */
+ s->ins_h = s->window[0];
+ UPDATE_HASH(s, s->ins_h, s->window[1]);
+ for (n = 0; n <= length - MIN_MATCH; n++) {
+ INSERT_STRING(s, n, hash_head);
+ }
+ if (hash_head) hash_head = 0; /* to make compiler happy */
+ return Z_OK;
+}
+
+/* ========================================================================= */
+int ZEXPORT deflateReset (strm)
+ z_streamp strm;
+{
+ deflate_state *s;
+
+ if (strm == Z_NULL || strm->state == Z_NULL ||
+ strm->zalloc == Z_NULL || strm->zfree == Z_NULL) return Z_STREAM_ERROR;
+
+ strm->total_in = strm->total_out = 0;
+ strm->msg = Z_NULL; /* use zfree if we ever allocate msg dynamically */
+ strm->data_type = Z_UNKNOWN;
+
+ s = (deflate_state *)strm->state;
+ s->pending = 0;
+ s->pending_out = s->pending_buf;
+
+ if (s->noheader < 0) {
+ s->noheader = 0; /* was set to -1 by deflate(..., Z_FINISH); */
+ }
+ s->status = s->noheader ? BUSY_STATE : INIT_STATE;
+ strm->adler = 1;
+ s->last_flush = Z_NO_FLUSH;
+
+ _tr_init(s);
+ lm_init(s);
+
+ return Z_OK;
+}
+
+/* ========================================================================= */
+int ZEXPORT deflateParams(strm, level, strategy)
+ z_streamp strm;
+ int level;
+ int strategy;
+{
+ deflate_state *s;
+ compress_func func;
+ int err = Z_OK;
+
+ if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR;
+ s = strm->state;
+
+ if (level == Z_DEFAULT_COMPRESSION) {
+ level = 6;
+ }
+ if (level < 0 || level > 9 || strategy < 0 || strategy > Z_HUFFMAN_ONLY) {
+ return Z_STREAM_ERROR;
+ }
+ func = configuration_table[s->level].func;
+
+ if (func != configuration_table[level].func && strm->total_in != 0) {
+ /* Flush the last buffer: */
+ err = deflate(strm, Z_PARTIAL_FLUSH);
+ }
+ if (s->level != level) {
+ s->level = level;
+ s->max_lazy_match = configuration_table[level].max_lazy;
+ s->good_match = configuration_table[level].good_length;
+ s->nice_match = configuration_table[level].nice_length;
+ s->max_chain_length = configuration_table[level].max_chain;
+ }
+ s->strategy = strategy;
+ return err;
+}
+
+/* =========================================================================
+ * Put a short in the pending buffer. The 16-bit value is put in MSB order.
+ * IN assertion: the stream state is correct and there is enough room in
+ * pending_buf.
+ */
+local void putShortMSB (s, b)
+ deflate_state *s;
+ uInt b;
+{
+ put_byte(s, (Byte)(b >> 8));
+ put_byte(s, (Byte)(b & 0xff));
+}
+
+/* =========================================================================
+ * Flush as much pending output as possible. All deflate() output goes
+ * through this function so some applications may wish to modify it
+ * to avoid allocating a large strm->next_out buffer and copying into it.
+ * (See also read_buf()).
+ */
+local void flush_pending(strm)
+ z_streamp strm;
+{
+ unsigned len = strm->state->pending;
+
+ if (len > strm->avail_out) len = strm->avail_out;
+ if (len == 0) return;
+
+ zmemcpy(strm->next_out, strm->state->pending_out, len);
+ strm->next_out += len;
+ strm->state->pending_out += len;
+ strm->total_out += len;
+ strm->avail_out -= len;
+ strm->state->pending -= len;
+ if (strm->state->pending == 0) {
+ strm->state->pending_out = strm->state->pending_buf;
+ }
+}
+
+/* ========================================================================= */
+int ZEXPORT deflate (strm, flush)
+ z_streamp strm;
+ int flush;
+{
+ int old_flush; /* value of flush param for previous deflate call */
+ deflate_state *s;
+
+ if (strm == Z_NULL || strm->state == Z_NULL ||
+ flush > Z_FINISH || flush < 0) {
+ return Z_STREAM_ERROR;
+ }
+ s = strm->state;
+
+ if (strm->next_out == Z_NULL ||
+ (strm->next_in == Z_NULL && strm->avail_in != 0) ||
+ (s->status == FINISH_STATE && flush != Z_FINISH)) {
+ ERR_RETURN(strm, Z_STREAM_ERROR);
+ }
+ if (strm->avail_out == 0) ERR_RETURN(strm, Z_BUF_ERROR);
+
+ s->strm = strm; /* just in case */
+ old_flush = s->last_flush;
+ s->last_flush = flush;
+
+ /* Write the zlib header */
+ if (s->status == INIT_STATE) {
+
+ uInt header = (Z_DEFLATED + ((s->w_bits-8)<<4)) << 8;
+ uInt level_flags = (s->level-1) >> 1;
+
+ if (level_flags > 3) level_flags = 3;
+ header |= (level_flags << 6);
+ if (s->strstart != 0) header |= PRESET_DICT;
+ header += 31 - (header % 31);
+
+ s->status = BUSY_STATE;
+ putShortMSB(s, header);
+
+ /* Save the adler32 of the preset dictionary: */
+ if (s->strstart != 0) {
+ putShortMSB(s, (uInt)(strm->adler >> 16));
+ putShortMSB(s, (uInt)(strm->adler & 0xffff));
+ }
+ strm->adler = 1L;
+ }
+
+ /* Flush as much pending output as possible */
+ if (s->pending != 0) {
+ flush_pending(strm);
+ if (strm->avail_out == 0) {
+ /* Since avail_out is 0, deflate will be called again with
+ * more output space, but possibly with both pending and
+ * avail_in equal to zero. There won't be anything to do,
+ * but this is not an error situation so make sure we
+ * return OK instead of BUF_ERROR at next call of deflate:
+ */
+ s->last_flush = -1;
+ return Z_OK;
+ }
+
+ /* Make sure there is something to do and avoid duplicate consecutive
+ * flushes. For repeated and useless calls with Z_FINISH, we keep
+ * returning Z_STREAM_END instead of Z_BUFF_ERROR.
+ */
+ } else if (strm->avail_in == 0 && flush <= old_flush &&
+ flush != Z_FINISH) {
+ ERR_RETURN(strm, Z_BUF_ERROR);
+ }
+
+ /* User must not provide more input after the first FINISH: */
+ if (s->status == FINISH_STATE && strm->avail_in != 0) {
+ ERR_RETURN(strm, Z_BUF_ERROR);
+ }
+
+ /* Start a new block or continue the current one.
+ */
+ if (strm->avail_in != 0 || s->lookahead != 0 ||
+ (flush != Z_NO_FLUSH && s->status != FINISH_STATE)) {
+ block_state bstate;
+
+ bstate = (*(configuration_table[s->level].func))(s, flush);
+
+ if (bstate == finish_started || bstate == finish_done) {
+ s->status = FINISH_STATE;
+ }
+ if (bstate == need_more || bstate == finish_started) {
+ if (strm->avail_out == 0) {
+ s->last_flush = -1; /* avoid BUF_ERROR next call, see above */
+ }
+ return Z_OK;
+ /* If flush != Z_NO_FLUSH && avail_out == 0, the next call
+ * of deflate should use the same flush parameter to make sure
+ * that the flush is complete. So we don't have to output an
+ * empty block here, this will be done at next call. This also
+ * ensures that for a very small output buffer, we emit at most
+ * one empty block.
+ */
+ }
+ if (bstate == block_done) {
+ if (flush == Z_PARTIAL_FLUSH) {
+ _tr_align(s);
+ } else { /* FULL_FLUSH or SYNC_FLUSH */
+ _tr_stored_block(s, (char*)0, 0L, 0);
+ /* For a full flush, this empty block will be recognized
+ * as a special marker by inflate_sync().
+ */
+ if (flush == Z_FULL_FLUSH) {
+ CLEAR_HASH(s); /* forget history */
+ }
+ }
+ flush_pending(strm);
+ if (strm->avail_out == 0) {
+ s->last_flush = -1; /* avoid BUF_ERROR at next call, see above */
+ return Z_OK;
+ }
+ }
+ }
+ Assert(strm->avail_out > 0, "bug2");
+
+ if (flush != Z_FINISH) return Z_OK;
+ if (s->noheader) return Z_STREAM_END;
+
+ /* Write the zlib trailer (adler32) */
+ putShortMSB(s, (uInt)(strm->adler >> 16));
+ putShortMSB(s, (uInt)(strm->adler & 0xffff));
+ flush_pending(strm);
+ /* If avail_out is zero, the application will call deflate again
+ * to flush the rest.
+ */
+ s->noheader = -1; /* write the trailer only once! */
+ return s->pending != 0 ? Z_OK : Z_STREAM_END;
+}
+
+/* ========================================================================= */
+int ZEXPORT deflateEnd (strm)
+ z_streamp strm;
+{
+ int status;
+
+ if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR;
+
+ status = strm->state->status;
+ if (status != INIT_STATE && status != BUSY_STATE &&
+ status != FINISH_STATE) {
+ return Z_STREAM_ERROR;
+ }
+
+ /* Deallocate in reverse order of allocations: */
+ TRY_FREE(strm, strm->state->pending_buf);
+ TRY_FREE(strm, strm->state->head);
+ TRY_FREE(strm, strm->state->prev);
+ TRY_FREE(strm, strm->state->window);
+
+ ZFREE(strm, strm->state);
+ strm->state = Z_NULL;
+
+ return status == BUSY_STATE ? Z_DATA_ERROR : Z_OK;
+}
+
+/* =========================================================================
+ * Copy the source state to the destination state.
+ * To simplify the source, this is not supported for 16-bit MSDOS (which
+ * doesn't have enough memory anyway to duplicate compression states).
+ */
+int ZEXPORT deflateCopy (dest, source)
+ z_streamp dest;
+ z_streamp source;
+{
+#ifdef MAXSEG_64K
+ return Z_STREAM_ERROR;
+#else
+ deflate_state *ds;
+ deflate_state *ss;
+ ushf *overlay;
+
+
+ if (source == Z_NULL || dest == Z_NULL || source->state == Z_NULL) {
+ return Z_STREAM_ERROR;
+ }
+
+ ss = source->state;
+
+ *dest = *source;
+
+ ds = (deflate_state *) ZALLOC(dest, 1, sizeof(deflate_state));
+ if (ds == Z_NULL) return Z_MEM_ERROR;
+ dest->state = (struct internal_state FAR *) ds;
+ *ds = *ss;
+ ds->strm = dest;
+
+ ds->window = (Bytef *) ZALLOC(dest, ds->w_size, 2*sizeof(Byte));
+ ds->prev = (Posf *) ZALLOC(dest, ds->w_size, sizeof(Pos));
+ ds->head = (Posf *) ZALLOC(dest, ds->hash_size, sizeof(Pos));
+ overlay = (ushf *) ZALLOC(dest, ds->lit_bufsize, sizeof(ush)+2);
+ ds->pending_buf = (uchf *) overlay;
+
+ if (ds->window == Z_NULL || ds->prev == Z_NULL || ds->head == Z_NULL ||
+ ds->pending_buf == Z_NULL) {
+ deflateEnd (dest);
+ return Z_MEM_ERROR;
+ }
+ /* following zmemcpy do not work for 16-bit MSDOS */
+ zmemcpy(ds->window, ss->window, ds->w_size * 2 * sizeof(Byte));
+ zmemcpy(ds->prev, ss->prev, ds->w_size * sizeof(Pos));
+ zmemcpy(ds->head, ss->head, ds->hash_size * sizeof(Pos));
+ zmemcpy(ds->pending_buf, ss->pending_buf, (uInt)ds->pending_buf_size);
+
+ ds->pending_out = ds->pending_buf + (ss->pending_out - ss->pending_buf);
+ ds->d_buf = overlay + ds->lit_bufsize/sizeof(ush);
+ ds->l_buf = ds->pending_buf + (1+sizeof(ush))*ds->lit_bufsize;
+
+ ds->l_desc.dyn_tree = ds->dyn_ltree;
+ ds->d_desc.dyn_tree = ds->dyn_dtree;
+ ds->bl_desc.dyn_tree = ds->bl_tree;
+
+ return Z_OK;
+#endif
+}
+
+/* ===========================================================================
+ * Read a new buffer from the current input stream, update the adler32
+ * and total number of bytes read. All deflate() input goes through
+ * this function so some applications may wish to modify it to avoid
+ * allocating a large strm->next_in buffer and copying from it.
+ * (See also flush_pending()).
+ */
+local int read_buf(strm, buf, size)
+ z_streamp strm;
+ Bytef *buf;
+ unsigned size;
+{
+ unsigned len = strm->avail_in;
+
+ if (len > size) len = size;
+ if (len == 0) return 0;
+
+ strm->avail_in -= len;
+
+ if (!strm->state->noheader) {
+ strm->adler = adler32(strm->adler, strm->next_in, len);
+ }
+ zmemcpy(buf, strm->next_in, len);
+ strm->next_in += len;
+ strm->total_in += len;
+
+ return (int)len;
+}
+
+/* ===========================================================================
+ * Initialize the "longest match" routines for a new zlib stream
+ */
+local void lm_init (s)
+ deflate_state *s;
+{
+ s->window_size = (ulg)2L*s->w_size;
+
+ CLEAR_HASH(s);
+
+ /* Set the default configuration parameters:
+ */
+ s->max_lazy_match = configuration_table[s->level].max_lazy;
+ s->good_match = configuration_table[s->level].good_length;
+ s->nice_match = configuration_table[s->level].nice_length;
+ s->max_chain_length = configuration_table[s->level].max_chain;
+
+ s->strstart = 0;
+ s->block_start = 0L;
+ s->lookahead = 0;
+ s->match_length = s->prev_length = MIN_MATCH-1;
+ s->match_available = 0;
+ s->ins_h = 0;
+#ifdef ASMV
+ match_init(); /* initialize the asm code */
+#endif
+}
+
+/* ===========================================================================
+ * Set match_start to the longest match starting at the given string and
+ * return its length. Matches shorter or equal to prev_length are discarded,
+ * in which case the result is equal to prev_length and match_start is
+ * garbage.
+ * IN assertions: cur_match is the head of the hash chain for the current
+ * string (strstart) and its distance is <= MAX_DIST, and prev_length >= 1
+ * OUT assertion: the match length is not greater than s->lookahead.
+ */
+#ifndef ASMV
+/* For 80x86 and 680x0, an optimized version will be provided in match.asm or
+ * match.S. The code will be functionally equivalent.
+ */
+#ifndef FASTEST
+local uInt longest_match(s, cur_match)
+ deflate_state *s;
+ IPos cur_match; /* current match */
+{
+ unsigned chain_length = s->max_chain_length;/* max hash chain length */
+ register Bytef *scan = s->window + s->strstart; /* current string */
+ register Bytef *match; /* matched string */
+ register int len; /* length of current match */
+ int best_len = s->prev_length; /* best match length so far */
+ int nice_match = s->nice_match; /* stop if match long enough */
+ IPos limit = s->strstart > (IPos)MAX_DIST(s) ?
+ s->strstart - (IPos)MAX_DIST(s) : NIL;
+ /* Stop when cur_match becomes <= limit. To simplify the code,
+ * we prevent matches with the string of window index 0.
+ */
+ Posf *prev = s->prev;
+ uInt wmask = s->w_mask;
+
+#ifdef UNALIGNED_OK
+ /* Compare two bytes at a time. Note: this is not always beneficial.
+ * Try with and without -DUNALIGNED_OK to check.
+ */
+ register Bytef *strend = s->window + s->strstart + MAX_MATCH - 1;
+ register ush scan_start = *(ushf*)scan;
+ register ush scan_end = *(ushf*)(scan+best_len-1);
+#else
+ register Bytef *strend = s->window + s->strstart + MAX_MATCH;
+ register Byte scan_end1 = scan[best_len-1];
+ register Byte scan_end = scan[best_len];
+#endif
+
+ /* The code is optimized for HASH_BITS >= 8 and MAX_MATCH-2 multiple of 16.
+ * It is easy to get rid of this optimization if necessary.
+ */
+ Assert(s->hash_bits >= 8 && MAX_MATCH == 258, "Code too clever");
+
+ /* Do not waste too much time if we already have a good match: */
+ if (s->prev_length >= s->good_match) {
+ chain_length >>= 2;
+ }
+ /* Do not look for matches beyond the end of the input. This is necessary
+ * to make deflate deterministic.
+ */
+ if ((uInt)nice_match > s->lookahead) nice_match = s->lookahead;
+
+ Assert((ulg)s->strstart <= s->window_size-MIN_LOOKAHEAD, "need lookahead");
+
+ do {
+ Assert(cur_match < s->strstart, "no future");
+ match = s->window + cur_match;
+
+ /* Skip to next match if the match length cannot increase
+ * or if the match length is less than 2:
+ */
+#if (defined(UNALIGNED_OK) && MAX_MATCH == 258)
+ /* This code assumes sizeof(unsigned short) == 2. Do not use
+ * UNALIGNED_OK if your compiler uses a different size.
+ */
+ if (*(ushf*)(match+best_len-1) != scan_end ||
+ *(ushf*)match != scan_start) continue;
+
+ /* It is not necessary to compare scan[2] and match[2] since they are
+ * always equal when the other bytes match, given that the hash keys
+ * are equal and that HASH_BITS >= 8. Compare 2 bytes at a time at
+ * strstart+3, +5, ... up to strstart+257. We check for insufficient
+ * lookahead only every 4th comparison; the 128th check will be made
+ * at strstart+257. If MAX_MATCH-2 is not a multiple of 8, it is
+ * necessary to put more guard bytes at the end of the window, or
+ * to check more often for insufficient lookahead.
+ */
+ Assert(scan[2] == match[2], "scan[2]?");
+ scan++, match++;
+ do {
+ } while (*(ushf*)(scan+=2) == *(ushf*)(match+=2) &&
+ *(ushf*)(scan+=2) == *(ushf*)(match+=2) &&
+ *(ushf*)(scan+=2) == *(ushf*)(match+=2) &&
+ *(ushf*)(scan+=2) == *(ushf*)(match+=2) &&
+ scan < strend);
+ /* The funny "do {}" generates better code on most compilers */
+
+ /* Here, scan <= window+strstart+257 */
+ Assert(scan <= s->window+(unsigned)(s->window_size-1), "wild scan");
+ if (*scan == *match) scan++;
+
+ len = (MAX_MATCH - 1) - (int)(strend-scan);
+ scan = strend - (MAX_MATCH-1);
+
+#else /* UNALIGNED_OK */
+
+ if (match[best_len] != scan_end ||
+ match[best_len-1] != scan_end1 ||
+ *match != *scan ||
+ *++match != scan[1]) continue;
+
+ /* The check at best_len-1 can be removed because it will be made
+ * again later. (This heuristic is not always a win.)
+ * It is not necessary to compare scan[2] and match[2] since they
+ * are always equal when the other bytes match, given that
+ * the hash keys are equal and that HASH_BITS >= 8.
+ */
+ scan += 2, match++;
+ Assert(*scan == *match, "match[2]?");
+
+ /* We check for insufficient lookahead only every 8th comparison;
+ * the 256th check will be made at strstart+258.
+ */
+ do {
+ } while (*++scan == *++match && *++scan == *++match &&
+ *++scan == *++match && *++scan == *++match &&
+ *++scan == *++match && *++scan == *++match &&
+ *++scan == *++match && *++scan == *++match &&
+ scan < strend);
+
+ Assert(scan <= s->window+(unsigned)(s->window_size-1), "wild scan");
+
+ len = MAX_MATCH - (int)(strend - scan);
+ scan = strend - MAX_MATCH;
+
+#endif /* UNALIGNED_OK */
+
+ if (len > best_len) {
+ s->match_start = cur_match;
+ best_len = len;
+ if (len >= nice_match) break;
+#ifdef UNALIGNED_OK
+ scan_end = *(ushf*)(scan+best_len-1);
+#else
+ scan_end1 = scan[best_len-1];
+ scan_end = scan[best_len];
+#endif
+ }
+ } while ((cur_match = prev[cur_match & wmask]) > limit
+ && --chain_length != 0);
+
+ if ((uInt)best_len <= s->lookahead) return (uInt)best_len;
+ return s->lookahead;
+}
+
+#else /* FASTEST */
+/* ---------------------------------------------------------------------------
+ * Optimized version for level == 1 only
+ */
+local uInt longest_match(s, cur_match)
+ deflate_state *s;
+ IPos cur_match; /* current match */
+{
+ register Bytef *scan = s->window + s->strstart; /* current string */
+ register Bytef *match; /* matched string */
+ register int len; /* length of current match */
+ register Bytef *strend = s->window + s->strstart + MAX_MATCH;
+
+ /* The code is optimized for HASH_BITS >= 8 and MAX_MATCH-2 multiple of 16.
+ * It is easy to get rid of this optimization if necessary.
+ */
+ Assert(s->hash_bits >= 8 && MAX_MATCH == 258, "Code too clever");
+
+ Assert((ulg)s->strstart <= s->window_size-MIN_LOOKAHEAD, "need lookahead");
+
+ Assert(cur_match < s->strstart, "no future");
+
+ match = s->window + cur_match;
+
+ /* Return failure if the match length is less than 2:
+ */
+ if (match[0] != scan[0] || match[1] != scan[1]) return MIN_MATCH-1;
+
+ /* The check at best_len-1 can be removed because it will be made
+ * again later. (This heuristic is not always a win.)
+ * It is not necessary to compare scan[2] and match[2] since they
+ * are always equal when the other bytes match, given that
+ * the hash keys are equal and that HASH_BITS >= 8.
+ */
+ scan += 2, match += 2;
+ Assert(*scan == *match, "match[2]?");
+
+ /* We check for insufficient lookahead only every 8th comparison;
+ * the 256th check will be made at strstart+258.
+ */
+ do {
+ } while (*++scan == *++match && *++scan == *++match &&
+ *++scan == *++match && *++scan == *++match &&
+ *++scan == *++match && *++scan == *++match &&
+ *++scan == *++match && *++scan == *++match &&
+ scan < strend);
+
+ Assert(scan <= s->window+(unsigned)(s->window_size-1), "wild scan");
+
+ len = MAX_MATCH - (int)(strend - scan);
+
+ if (len < MIN_MATCH) return MIN_MATCH - 1;
+
+ s->match_start = cur_match;
+ return len <= s->lookahead ? len : s->lookahead;
+}
+#endif /* FASTEST */
+#endif /* ASMV */
+
+#ifdef DEBUG
+/* ===========================================================================
+ * Check that the match at match_start is indeed a match.
+ */
+local void check_match(s, start, match, length)
+ deflate_state *s;
+ IPos start, match;
+ int length;
+{
+ /* check that the match is indeed a match */
+ if (zmemcmp(s->window + match,
+ s->window + start, length) != EQUAL) {
+ fprintf(stderr, " start %u, match %u, length %d\n",
+ start, match, length);
+ do {
+ fprintf(stderr, "%c%c", s->window[match++], s->window[start++]);
+ } while (--length != 0);
+ z_error("invalid match");
+ }
+ if (z_verbose > 1) {
+ fprintf(stderr,"\\[%d,%d]", start-match, length);
+ do { putc(s->window[start++], stderr); } while (--length != 0);
+ }
+}
+#else
+# define check_match(s, start, match, length)
+#endif
+
+/* ===========================================================================
+ * Fill the window when the lookahead becomes insufficient.
+ * Updates strstart and lookahead.
+ *
+ * IN assertion: lookahead < MIN_LOOKAHEAD
+ * OUT assertions: strstart <= window_size-MIN_LOOKAHEAD
+ * At least one byte has been read, or avail_in == 0; reads are
+ * performed for at least two bytes (required for the zip translate_eol
+ * option -- not supported here).
+ */
+local void fill_window(s)
+ deflate_state *s;
+{
+ register unsigned n, m;
+ register Posf *p;
+ unsigned more; /* Amount of free space at the end of the window. */
+ uInt wsize = s->w_size;
+
+ do {
+ more = (unsigned)(s->window_size -(ulg)s->lookahead -(ulg)s->strstart);
+
+ /* Deal with !@#$% 64K limit: */
+ if (more == 0 && s->strstart == 0 && s->lookahead == 0) {
+ more = wsize;
+
+ } else if (more == (unsigned)(-1)) {
+ /* Very unlikely, but possible on 16 bit machine if strstart == 0
+ * and lookahead == 1 (input done one byte at time)
+ */
+ more--;
+
+ /* If the window is almost full and there is insufficient lookahead,
+ * move the upper half to the lower one to make room in the upper half.
+ */
+ } else if (s->strstart >= wsize+MAX_DIST(s)) {
+
+ zmemcpy(s->window, s->window+wsize, (unsigned)wsize);
+ s->match_start -= wsize;
+ s->strstart -= wsize; /* we now have strstart >= MAX_DIST */
+ s->block_start -= (long) wsize;
+
+ /* Slide the hash table (could be avoided with 32 bit values
+ at the expense of memory usage). We slide even when level == 0
+ to keep the hash table consistent if we switch back to level > 0
+ later. (Using level 0 permanently is not an optimal usage of
+ zlib, so we don't care about this pathological case.)
+ */
+ n = s->hash_size;
+ p = &s->head[n];
+ do {
+ m = *--p;
+ *p = (Pos)(m >= wsize ? m-wsize : NIL);
+ } while (--n);
+
+ n = wsize;
+#ifndef FASTEST
+ p = &s->prev[n];
+ do {
+ m = *--p;
+ *p = (Pos)(m >= wsize ? m-wsize : NIL);
+ /* If n is not on any hash chain, prev[n] is garbage but
+ * its value will never be used.
+ */
+ } while (--n);
+#endif
+ more += wsize;
+ }
+ if (s->strm->avail_in == 0) return;
+
+ /* If there was no sliding:
+ * strstart <= WSIZE+MAX_DIST-1 && lookahead <= MIN_LOOKAHEAD - 1 &&
+ * more == window_size - lookahead - strstart
+ * => more >= window_size - (MIN_LOOKAHEAD-1 + WSIZE + MAX_DIST-1)
+ * => more >= window_size - 2*WSIZE + 2
+ * In the BIG_MEM or MMAP case (not yet supported),
+ * window_size == input_size + MIN_LOOKAHEAD &&
+ * strstart + s->lookahead <= input_size => more >= MIN_LOOKAHEAD.
+ * Otherwise, window_size == 2*WSIZE so more >= 2.
+ * If there was sliding, more >= WSIZE. So in all cases, more >= 2.
+ */
+ Assert(more >= 2, "more < 2");
+
+ n = read_buf(s->strm, s->window + s->strstart + s->lookahead, more);
+ s->lookahead += n;
+
+ /* Initialize the hash value now that we have some input: */
+ if (s->lookahead >= MIN_MATCH) {
+ s->ins_h = s->window[s->strstart];
+ UPDATE_HASH(s, s->ins_h, s->window[s->strstart+1]);
+#if MIN_MATCH != 3
+ Call UPDATE_HASH() MIN_MATCH-3 more times
+#endif
+ }
+ /* If the whole input has less than MIN_MATCH bytes, ins_h is garbage,
+ * but this is not important since only literal bytes will be emitted.
+ */
+
+ } while (s->lookahead < MIN_LOOKAHEAD && s->strm->avail_in != 0);
+}
+
+/* ===========================================================================
+ * Flush the current block, with given end-of-file flag.
+ * IN assertion: strstart is set to the end of the current match.
+ */
+#define FLUSH_BLOCK_ONLY(s, eof) { \
+ _tr_flush_block(s, (s->block_start >= 0L ? \
+ (charf *)&s->window[(unsigned)s->block_start] : \
+ (charf *)Z_NULL), \
+ (ulg)((long)s->strstart - s->block_start), \
+ (eof)); \
+ s->block_start = s->strstart; \
+ flush_pending(s->strm); \
+ Tracev((stderr,"[FLUSH]")); \
+}
+
+/* Same but force premature exit if necessary. */
+#define FLUSH_BLOCK(s, eof) { \
+ FLUSH_BLOCK_ONLY(s, eof); \
+ if (s->strm->avail_out == 0) return (eof) ? finish_started : need_more; \
+}
+
+/* ===========================================================================
+ * Copy without compression as much as possible from the input stream, return
+ * the current block state.
+ * This function does not insert new strings in the dictionary since
+ * uncompressible data is probably not useful. This function is used
+ * only for the level=0 compression option.
+ * NOTE: this function should be optimized to avoid extra copying from
+ * window to pending_buf.
+ */
+local block_state deflate_stored(s, flush)
+ deflate_state *s;
+ int flush;
+{
+ /* Stored blocks are limited to 0xffff bytes, pending_buf is limited
+ * to pending_buf_size, and each stored block has a 5 byte header:
+ */
+ ulg max_block_size = 0xffff;
+ ulg max_start;
+
+ if (max_block_size > s->pending_buf_size - 5) {
+ max_block_size = s->pending_buf_size - 5;
+ }
+
+ /* Copy as much as possible from input to output: */
+ for (;;) {
+ /* Fill the window as much as possible: */
+ if (s->lookahead <= 1) {
+
+ Assert(s->strstart < s->w_size+MAX_DIST(s) ||
+ s->block_start >= (long)s->w_size, "slide too late");
+
+ fill_window(s);
+ if (s->lookahead == 0 && flush == Z_NO_FLUSH) return need_more;
+
+ if (s->lookahead == 0) break; /* flush the current block */
+ }
+ Assert(s->block_start >= 0L, "block gone");
+
+ s->strstart += s->lookahead;
+ s->lookahead = 0;
+
+ /* Emit a stored block if pending_buf will be full: */
+ max_start = s->block_start + max_block_size;
+ if (s->strstart == 0 || (ulg)s->strstart >= max_start) {
+ /* strstart == 0 is possible when wraparound on 16-bit machine */
+ s->lookahead = (uInt)(s->strstart - max_start);
+ s->strstart = (uInt)max_start;
+ FLUSH_BLOCK(s, 0);
+ }
+ /* Flush if we may have to slide, otherwise block_start may become
+ * negative and the data will be gone:
+ */
+ if (s->strstart - (uInt)s->block_start >= MAX_DIST(s)) {
+ FLUSH_BLOCK(s, 0);
+ }
+ }
+ FLUSH_BLOCK(s, flush == Z_FINISH);
+ return flush == Z_FINISH ? finish_done : block_done;
+}
+
+/* ===========================================================================
+ * Compress as much as possible from the input stream, return the current
+ * block state.
+ * This function does not perform lazy evaluation of matches and inserts
+ * new strings in the dictionary only for unmatched strings or for short
+ * matches. It is used only for the fast compression options.
+ */
+local block_state deflate_fast(s, flush)
+ deflate_state *s;
+ int flush;
+{
+ IPos hash_head = NIL; /* head of the hash chain */
+ int bflush; /* set if current block must be flushed */
+
+ for (;;) {
+ /* Make sure that we always have enough lookahead, except
+ * at the end of the input file. We need MAX_MATCH bytes
+ * for the next match, plus MIN_MATCH bytes to insert the
+ * string following the next match.
+ */
+ if (s->lookahead < MIN_LOOKAHEAD) {
+ fill_window(s);
+ if (s->lookahead < MIN_LOOKAHEAD && flush == Z_NO_FLUSH) {
+ return need_more;
+ }
+ if (s->lookahead == 0) break; /* flush the current block */
+ }
+
+ /* Insert the string window[strstart .. strstart+2] in the
+ * dictionary, and set hash_head to the head of the hash chain:
+ */
+ if (s->lookahead >= MIN_MATCH) {
+ INSERT_STRING(s, s->strstart, hash_head);
+ }
+
+ /* Find the longest match, discarding those <= prev_length.
+ * At this point we have always match_length < MIN_MATCH
+ */
+ if (hash_head != NIL && s->strstart - hash_head <= MAX_DIST(s)) {
+ /* To simplify the code, we prevent matches with the string
+ * of window index 0 (in particular we have to avoid a match
+ * of the string with itself at the start of the input file).
+ */
+ if (s->strategy != Z_HUFFMAN_ONLY) {
+ s->match_length = longest_match (s, hash_head);
+ }
+ /* longest_match() sets match_start */
+ }
+ if (s->match_length >= MIN_MATCH) {
+ check_match(s, s->strstart, s->match_start, s->match_length);
+
+ _tr_tally_dist(s, s->strstart - s->match_start,
+ s->match_length - MIN_MATCH, bflush);
+
+ s->lookahead -= s->match_length;
+
+ /* Insert new strings in the hash table only if the match length
+ * is not too large. This saves time but degrades compression.
+ */
+#ifndef FASTEST
+ if (s->match_length <= s->max_insert_length &&
+ s->lookahead >= MIN_MATCH) {
+ s->match_length--; /* string at strstart already in hash table */
+ do {
+ s->strstart++;
+ INSERT_STRING(s, s->strstart, hash_head);
+ /* strstart never exceeds WSIZE-MAX_MATCH, so there are
+ * always MIN_MATCH bytes ahead.
+ */
+ } while (--s->match_length != 0);
+ s->strstart++;
+ } else
+#endif
+ {
+ s->strstart += s->match_length;
+ s->match_length = 0;
+ s->ins_h = s->window[s->strstart];
+ UPDATE_HASH(s, s->ins_h, s->window[s->strstart+1]);
+#if MIN_MATCH != 3
+ Call UPDATE_HASH() MIN_MATCH-3 more times
+#endif
+ /* If lookahead < MIN_MATCH, ins_h is garbage, but it does not
+ * matter since it will be recomputed at next deflate call.
+ */
+ }
+ } else {
+ /* No match, output a literal byte */
+ Tracevv((stderr,"%c", s->window[s->strstart]));
+ _tr_tally_lit (s, s->window[s->strstart], bflush);
+ s->lookahead--;
+ s->strstart++;
+ }
+ if (bflush) FLUSH_BLOCK(s, 0);
+ }
+ FLUSH_BLOCK(s, flush == Z_FINISH);
+ return flush == Z_FINISH ? finish_done : block_done;
+}
+
+/* ===========================================================================
+ * Same as above, but achieves better compression. We use a lazy
+ * evaluation for matches: a match is finally adopted only if there is
+ * no better match at the next window position.
+ */
+local block_state deflate_slow(s, flush)
+ deflate_state *s;
+ int flush;
+{
+ IPos hash_head = NIL; /* head of hash chain */
+ int bflush; /* set if current block must be flushed */
+
+ /* Process the input block. */
+ for (;;) {
+ /* Make sure that we always have enough lookahead, except
+ * at the end of the input file. We need MAX_MATCH bytes
+ * for the next match, plus MIN_MATCH bytes to insert the
+ * string following the next match.
+ */
+ if (s->lookahead < MIN_LOOKAHEAD) {
+ fill_window(s);
+ if (s->lookahead < MIN_LOOKAHEAD && flush == Z_NO_FLUSH) {
+ return need_more;
+ }
+ if (s->lookahead == 0) break; /* flush the current block */
+ }
+
+ /* Insert the string window[strstart .. strstart+2] in the
+ * dictionary, and set hash_head to the head of the hash chain:
+ */
+ if (s->lookahead >= MIN_MATCH) {
+ INSERT_STRING(s, s->strstart, hash_head);
+ }
+
+ /* Find the longest match, discarding those <= prev_length.
+ */
+ s->prev_length = s->match_length, s->prev_match = s->match_start;
+ s->match_length = MIN_MATCH-1;
+
+ if (hash_head != NIL && s->prev_length < s->max_lazy_match &&
+ s->strstart - hash_head <= MAX_DIST(s)) {
+ /* To simplify the code, we prevent matches with the string
+ * of window index 0 (in particular we have to avoid a match
+ * of the string with itself at the start of the input file).
+ */
+ if (s->strategy != Z_HUFFMAN_ONLY) {
+ s->match_length = longest_match (s, hash_head);
+ }
+ /* longest_match() sets match_start */
+
+ if (s->match_length <= 5 && (s->strategy == Z_FILTERED ||
+ (s->match_length == MIN_MATCH &&
+ s->strstart - s->match_start > TOO_FAR))) {
+
+ /* If prev_match is also MIN_MATCH, match_start is garbage
+ * but we will ignore the current match anyway.
+ */
+ s->match_length = MIN_MATCH-1;
+ }
+ }
+ /* If there was a match at the previous step and the current
+ * match is not better, output the previous match:
+ */
+ if (s->prev_length >= MIN_MATCH && s->match_length <= s->prev_length) {
+ uInt max_insert = s->strstart + s->lookahead - MIN_MATCH;
+ /* Do not insert strings in hash table beyond this. */
+
+ check_match(s, s->strstart-1, s->prev_match, s->prev_length);
+
+ _tr_tally_dist(s, s->strstart -1 - s->prev_match,
+ s->prev_length - MIN_MATCH, bflush);
+
+ /* Insert in hash table all strings up to the end of the match.
+ * strstart-1 and strstart are already inserted. If there is not
+ * enough lookahead, the last two strings are not inserted in
+ * the hash table.
+ */
+ s->lookahead -= s->prev_length-1;
+ s->prev_length -= 2;
+ do {
+ if (++s->strstart <= max_insert) {
+ INSERT_STRING(s, s->strstart, hash_head);
+ }
+ } while (--s->prev_length != 0);
+ s->match_available = 0;
+ s->match_length = MIN_MATCH-1;
+ s->strstart++;
+
+ if (bflush) FLUSH_BLOCK(s, 0);
+
+ } else if (s->match_available) {
+ /* If there was no match at the previous position, output a
+ * single literal. If there was a match but the current match
+ * is longer, truncate the previous match to a single literal.
+ */
+ Tracevv((stderr,"%c", s->window[s->strstart-1]));
+ _tr_tally_lit(s, s->window[s->strstart-1], bflush);
+ if (bflush) {
+ FLUSH_BLOCK_ONLY(s, 0);
+ }
+ s->strstart++;
+ s->lookahead--;
+ if (s->strm->avail_out == 0) return need_more;
+ } else {
+ /* There is no previous match to compare with, wait for
+ * the next step to decide.
+ */
+ s->match_available = 1;
+ s->strstart++;
+ s->lookahead--;
+ }
+ }
+ Assert (flush != Z_NO_FLUSH, "no flush?");
+ if (s->match_available) {
+ Tracevv((stderr,"%c", s->window[s->strstart-1]));
+ _tr_tally_lit(s, s->window[s->strstart-1], bflush);
+ s->match_available = 0;
+ }
+ FLUSH_BLOCK(s, flush == Z_FINISH);
+ return flush == Z_FINISH ? finish_done : block_done;
+}
diff --git a/zlib/deflate.h b/zlib/deflate.h
new file mode 100644
index 00000000..39cfaeb1
--- /dev/null
+++ b/zlib/deflate.h
@@ -0,0 +1,318 @@
+/* deflate.h -- internal compression state
+ * Copyright (C) 1995-2002 Jean-loup Gailly
+ * For conditions of distribution and use, see copyright notice in zlib.h
+ */
+
+/* WARNING: this file should *not* be used by applications. It is
+ part of the implementation of the compression library and is
+ subject to change. Applications should only use zlib.h.
+ */
+
+/* @(#) $Id: deflate.h,v 1.1 2004/10/08 09:44:24 const_k Exp $ */
+
+#ifndef _DEFLATE_H
+#define _DEFLATE_H
+
+#include "zutil.h"
+
+/* ===========================================================================
+ * Internal compression state.
+ */
+
+#define LENGTH_CODES 29
+/* number of length codes, not counting the special END_BLOCK code */
+
+#define LITERALS 256
+/* number of literal bytes 0..255 */
+
+#define L_CODES (LITERALS+1+LENGTH_CODES)
+/* number of Literal or Length codes, including the END_BLOCK code */
+
+#define D_CODES 30
+/* number of distance codes */
+
+#define BL_CODES 19
+/* number of codes used to transfer the bit lengths */
+
+#define HEAP_SIZE (2*L_CODES+1)
+/* maximum heap size */
+
+#define MAX_BITS 15
+/* All codes must not exceed MAX_BITS bits */
+
+#define INIT_STATE 42
+#define BUSY_STATE 113
+#define FINISH_STATE 666
+/* Stream status */
+
+
+/* Data structure describing a single value and its code string. */
+typedef struct ct_data_s {
+ union {
+ ush freq; /* frequency count */
+ ush code; /* bit string */
+ } fc;
+ union {
+ ush dad; /* father node in Huffman tree */
+ ush len; /* length of bit string */
+ } dl;
+} FAR ct_data;
+
+#define Freq fc.freq
+#define Code fc.code
+#define Dad dl.dad
+#define Len dl.len
+
+typedef struct static_tree_desc_s static_tree_desc;
+
+typedef struct tree_desc_s {
+ ct_data *dyn_tree; /* the dynamic tree */
+ int max_code; /* largest code with non zero frequency */
+ static_tree_desc *stat_desc; /* the corresponding static tree */
+} FAR tree_desc;
+
+typedef ush Pos;
+typedef Pos FAR Posf;
+typedef unsigned IPos;
+
+/* A Pos is an index in the character window. We use short instead of int to
+ * save space in the various tables. IPos is used only for parameter passing.
+ */
+
+typedef struct internal_state {
+ z_streamp strm; /* pointer back to this zlib stream */
+ int status; /* as the name implies */
+ Bytef *pending_buf; /* output still pending */
+ ulg pending_buf_size; /* size of pending_buf */
+ Bytef *pending_out; /* next pending byte to output to the stream */
+ int pending; /* nb of bytes in the pending buffer */
+ int noheader; /* suppress zlib header and adler32 */
+ Byte data_type; /* UNKNOWN, BINARY or ASCII */
+ Byte method; /* STORED (for zip only) or DEFLATED */
+ int last_flush; /* value of flush param for previous deflate call */
+
+ /* used by deflate.c: */
+
+ uInt w_size; /* LZ77 window size (32K by default) */
+ uInt w_bits; /* log2(w_size) (8..16) */
+ uInt w_mask; /* w_size - 1 */
+
+ Bytef *window;
+ /* Sliding window. Input bytes are read into the second half of the window,
+ * and move to the first half later to keep a dictionary of at least wSize
+ * bytes. With this organization, matches are limited to a distance of
+ * wSize-MAX_MATCH bytes, but this ensures that IO is always
+ * performed with a length multiple of the block size. Also, it limits
+ * the window size to 64K, which is quite useful on MSDOS.
+ * To do: use the user input buffer as sliding window.
+ */
+
+ ulg window_size;
+ /* Actual size of window: 2*wSize, except when the user input buffer
+ * is directly used as sliding window.
+ */
+
+ Posf *prev;
+ /* Link to older string with same hash index. To limit the size of this
+ * array to 64K, this link is maintained only for the last 32K strings.
+ * An index in this array is thus a window index modulo 32K.
+ */
+
+ Posf *head; /* Heads of the hash chains or NIL. */
+
+ uInt ins_h; /* hash index of string to be inserted */
+ uInt hash_size; /* number of elements in hash table */
+ uInt hash_bits; /* log2(hash_size) */
+ uInt hash_mask; /* hash_size-1 */
+
+ uInt hash_shift;
+ /* Number of bits by which ins_h must be shifted at each input
+ * step. It must be such that after MIN_MATCH steps, the oldest
+ * byte no longer takes part in the hash key, that is:
+ * hash_shift * MIN_MATCH >= hash_bits
+ */
+
+ long block_start;
+ /* Window position at the beginning of the current output block. Gets
+ * negative when the window is moved backwards.
+ */
+
+ uInt match_length; /* length of best match */
+ IPos prev_match; /* previous match */
+ int match_available; /* set if previous match exists */
+ uInt strstart; /* start of string to insert */
+ uInt match_start; /* start of matching string */
+ uInt lookahead; /* number of valid bytes ahead in window */
+
+ uInt prev_length;
+ /* Length of the best match at previous step. Matches not greater than this
+ * are discarded. This is used in the lazy match evaluation.
+ */
+
+ uInt max_chain_length;
+ /* To speed up deflation, hash chains are never searched beyond this
+ * length. A higher limit improves compression ratio but degrades the
+ * speed.
+ */
+
+ uInt max_lazy_match;
+ /* Attempt to find a better match only when the current match is strictly
+ * smaller than this value. This mechanism is used only for compression
+ * levels >= 4.
+ */
+# define max_insert_length max_lazy_match
+ /* Insert new strings in the hash table only if the match length is not
+ * greater than this length. This saves time but degrades compression.
+ * max_insert_length is used only for compression levels <= 3.
+ */
+
+ int level; /* compression level (1..9) */
+ int strategy; /* favor or force Huffman coding*/
+
+ uInt good_match;
+ /* Use a faster search when the previous match is longer than this */
+
+ int nice_match; /* Stop searching when current match exceeds this */
+
+ /* used by trees.c: */
+ /* Didn't use ct_data typedef below to supress compiler warning */
+ struct ct_data_s dyn_ltree[HEAP_SIZE]; /* literal and length tree */
+ struct ct_data_s dyn_dtree[2*D_CODES+1]; /* distance tree */
+ struct ct_data_s bl_tree[2*BL_CODES+1]; /* Huffman tree for bit lengths */
+
+ struct tree_desc_s l_desc; /* desc. for literal tree */
+ struct tree_desc_s d_desc; /* desc. for distance tree */
+ struct tree_desc_s bl_desc; /* desc. for bit length tree */
+
+ ush bl_count[MAX_BITS+1];
+ /* number of codes at each bit length for an optimal tree */
+
+ int heap[2*L_CODES+1]; /* heap used to build the Huffman trees */
+ int heap_len; /* number of elements in the heap */
+ int heap_max; /* element of largest frequency */
+ /* The sons of heap[n] are heap[2*n] and heap[2*n+1]. heap[0] is not used.
+ * The same heap array is used to build all trees.
+ */
+
+ uch depth[2*L_CODES+1];
+ /* Depth of each subtree used as tie breaker for trees of equal frequency
+ */
+
+ uchf *l_buf; /* buffer for literals or lengths */
+
+ uInt lit_bufsize;
+ /* Size of match buffer for literals/lengths. There are 4 reasons for
+ * limiting lit_bufsize to 64K:
+ * - frequencies can be kept in 16 bit counters
+ * - if compression is not successful for the first block, all input
+ * data is still in the window so we can still emit a stored block even
+ * when input comes from standard input. (This can also be done for
+ * all blocks if lit_bufsize is not greater than 32K.)
+ * - if compression is not successful for a file smaller than 64K, we can
+ * even emit a stored file instead of a stored block (saving 5 bytes).
+ * This is applicable only for zip (not gzip or zlib).
+ * - creating new Huffman trees less frequently may not provide fast
+ * adaptation to changes in the input data statistics. (Take for
+ * example a binary file with poorly compressible code followed by
+ * a highly compressible string table.) Smaller buffer sizes give
+ * fast adaptation but have of course the overhead of transmitting
+ * trees more frequently.
+ * - I can't count above 4
+ */
+
+ uInt last_lit; /* running index in l_buf */
+
+ ushf *d_buf;
+ /* Buffer for distances. To simplify the code, d_buf and l_buf have
+ * the same number of elements. To use different lengths, an extra flag
+ * array would be necessary.
+ */
+
+ ulg opt_len; /* bit length of current block with optimal trees */
+ ulg static_len; /* bit length of current block with static trees */
+ uInt matches; /* number of string matches in current block */
+ int last_eob_len; /* bit length of EOB code for last block */
+
+#ifdef DEBUG
+ ulg compressed_len; /* total bit length of compressed file mod 2^32 */
+ ulg bits_sent; /* bit length of compressed data sent mod 2^32 */
+#endif
+
+ ush bi_buf;
+ /* Output buffer. bits are inserted starting at the bottom (least
+ * significant bits).
+ */
+ int bi_valid;
+ /* Number of valid bits in bi_buf. All bits above the last valid bit
+ * are always zero.
+ */
+
+} FAR deflate_state;
+
+/* Output a byte on the stream.
+ * IN assertion: there is enough room in pending_buf.
+ */
+#define put_byte(s, c) {s->pending_buf[s->pending++] = (c);}
+
+
+#define MIN_LOOKAHEAD (MAX_MATCH+MIN_MATCH+1)
+/* Minimum amount of lookahead, except at the end of the input file.
+ * See deflate.c for comments about the MIN_MATCH+1.
+ */
+
+#define MAX_DIST(s) ((s)->w_size-MIN_LOOKAHEAD)
+/* In order to simplify the code, particularly on 16 bit machines, match
+ * distances are limited to MAX_DIST instead of WSIZE.
+ */
+
+ /* in trees.c */
+void _tr_init OF((deflate_state *s));
+int _tr_tally OF((deflate_state *s, unsigned dist, unsigned lc));
+void _tr_flush_block OF((deflate_state *s, charf *buf, ulg stored_len,
+ int eof));
+void _tr_align OF((deflate_state *s));
+void _tr_stored_block OF((deflate_state *s, charf *buf, ulg stored_len,
+ int eof));
+
+#define d_code(dist) \
+ ((dist) < 256 ? _dist_code[dist] : _dist_code[256+((dist)>>7)])
+/* Mapping from a distance to a distance code. dist is the distance - 1 and
+ * must not have side effects. _dist_code[256] and _dist_code[257] are never
+ * used.
+ */
+
+#ifndef DEBUG
+/* Inline versions of _tr_tally for speed: */
+
+#if defined(GEN_TREES_H) || !defined(STDC)
+ extern uch _length_code[];
+ extern uch _dist_code[];
+#else
+ extern const uch _length_code[];
+ extern const uch _dist_code[];
+#endif
+
+# define _tr_tally_lit(s, c, flush) \
+ { uch cc = (c); \
+ s->d_buf[s->last_lit] = 0; \
+ s->l_buf[s->last_lit++] = cc; \
+ s->dyn_ltree[cc].Freq++; \
+ flush = (s->last_lit == s->lit_bufsize-1); \
+ }
+# define _tr_tally_dist(s, distance, length, flush) \
+ { uch len = (length); \
+ ush dist = (distance); \
+ s->d_buf[s->last_lit] = dist; \
+ s->l_buf[s->last_lit++] = len; \
+ dist--; \
+ s->dyn_ltree[_length_code[len]+LITERALS+1].Freq++; \
+ s->dyn_dtree[d_code(dist)].Freq++; \
+ flush = (s->last_lit == s->lit_bufsize-1); \
+ }
+#else
+# define _tr_tally_lit(s, c, flush) flush = _tr_tally(s, 0, c)
+# define _tr_tally_dist(s, distance, length, flush) \
+ flush = _tr_tally(s, distance, length)
+#endif
+
+#endif
diff --git a/zlib/descrip.mms b/zlib/descrip.mms
new file mode 100644
index 00000000..9d364598
--- /dev/null
+++ b/zlib/descrip.mms
@@ -0,0 +1,48 @@
+# descrip.mms: MMS description file for building zlib on VMS
+# written by Martin P.J. Zinser <m.zinser@gsi.de>
+
+cc_defs =
+c_deb =
+
+.ifdef __DECC__
+pref = /prefix=all
+.endif
+
+OBJS = adler32.obj, compress.obj, crc32.obj, gzio.obj, uncompr.obj,\
+ deflate.obj, trees.obj, zutil.obj, inflate.obj, infblock.obj,\
+ inftrees.obj, infcodes.obj, infutil.obj, inffast.obj
+
+CFLAGS= $(C_DEB) $(CC_DEFS) $(PREF)
+
+all : example.exe minigzip.exe
+ @ write sys$output " Example applications available"
+libz.olb : libz.olb($(OBJS))
+ @ write sys$output " libz available"
+
+example.exe : example.obj libz.olb
+ link example,libz.olb/lib
+
+minigzip.exe : minigzip.obj libz.olb
+ link minigzip,libz.olb/lib,x11vms:xvmsutils.olb/lib
+
+clean :
+ delete *.obj;*,libz.olb;*
+
+
+# Other dependencies.
+adler32.obj : zutil.h zlib.h zconf.h
+compress.obj : zlib.h zconf.h
+crc32.obj : zutil.h zlib.h zconf.h
+deflate.obj : deflate.h zutil.h zlib.h zconf.h
+example.obj : zlib.h zconf.h
+gzio.obj : zutil.h zlib.h zconf.h
+infblock.obj : zutil.h zlib.h zconf.h infblock.h inftrees.h infcodes.h infutil.h
+infcodes.obj : zutil.h zlib.h zconf.h inftrees.h infutil.h infcodes.h inffast.h
+inffast.obj : zutil.h zlib.h zconf.h inftrees.h infutil.h inffast.h
+inflate.obj : zutil.h zlib.h zconf.h infblock.h
+inftrees.obj : zutil.h zlib.h zconf.h inftrees.h
+infutil.obj : zutil.h zlib.h zconf.h inftrees.h infutil.h
+minigzip.obj : zlib.h zconf.h
+trees.obj : deflate.h zutil.h zlib.h zconf.h
+uncompr.obj : zlib.h zconf.h
+zutil.obj : zutil.h zlib.h zconf.h
diff --git a/zlib/example.c b/zlib/example.c
new file mode 100644
index 00000000..255662f2
--- /dev/null
+++ b/zlib/example.c
@@ -0,0 +1,556 @@
+/* example.c -- usage example of the zlib compression library
+ * Copyright (C) 1995-2002 Jean-loup Gailly.
+ * For conditions of distribution and use, see copyright notice in zlib.h
+ */
+
+/* @(#) $Id: example.c,v 1.1 2004/10/08 09:44:25 const_k Exp $ */
+
+#include <stdio.h>
+#include "zlib.h"
+
+#ifdef STDC
+# include <string.h>
+# include <stdlib.h>
+#else
+ extern void exit OF((int));
+#endif
+
+#if defined(VMS) || defined(RISCOS)
+# define TESTFILE "foo-gz"
+#else
+# define TESTFILE "foo.gz"
+#endif
+
+#define CHECK_ERR(err, msg) { \
+ if (err != Z_OK) { \
+ fprintf(stderr, "%s error: %d\n", msg, err); \
+ exit(1); \
+ } \
+}
+
+const char hello[] = "hello, hello!";
+/* "hello world" would be more standard, but the repeated "hello"
+ * stresses the compression code better, sorry...
+ */
+
+const char dictionary[] = "hello";
+uLong dictId; /* Adler32 value of the dictionary */
+
+void test_compress OF((Byte *compr, uLong comprLen,
+ Byte *uncompr, uLong uncomprLen));
+void test_gzio OF((const char *out, const char *in,
+ Byte *uncompr, int uncomprLen));
+void test_deflate OF((Byte *compr, uLong comprLen));
+void test_inflate OF((Byte *compr, uLong comprLen,
+ Byte *uncompr, uLong uncomprLen));
+void test_large_deflate OF((Byte *compr, uLong comprLen,
+ Byte *uncompr, uLong uncomprLen));
+void test_large_inflate OF((Byte *compr, uLong comprLen,
+ Byte *uncompr, uLong uncomprLen));
+void test_flush OF((Byte *compr, uLong *comprLen));
+void test_sync OF((Byte *compr, uLong comprLen,
+ Byte *uncompr, uLong uncomprLen));
+void test_dict_deflate OF((Byte *compr, uLong comprLen));
+void test_dict_inflate OF((Byte *compr, uLong comprLen,
+ Byte *uncompr, uLong uncomprLen));
+int main OF((int argc, char *argv[]));
+
+/* ===========================================================================
+ * Test compress() and uncompress()
+ */
+void test_compress(compr, comprLen, uncompr, uncomprLen)
+ Byte *compr, *uncompr;
+ uLong comprLen, uncomprLen;
+{
+ int err;
+ uLong len = strlen(hello)+1;
+
+ err = compress(compr, &comprLen, (const Bytef*)hello, len);
+ CHECK_ERR(err, "compress");
+
+ strcpy((char*)uncompr, "garbage");
+
+ err = uncompress(uncompr, &uncomprLen, compr, comprLen);
+ CHECK_ERR(err, "uncompress");
+
+ if (strcmp((char*)uncompr, hello)) {
+ fprintf(stderr, "bad uncompress\n");
+ exit(1);
+ } else {
+ printf("uncompress(): %s\n", (char *)uncompr);
+ }
+}
+
+/* ===========================================================================
+ * Test read/write of .gz files
+ */
+void test_gzio(out, in, uncompr, uncomprLen)
+ const char *out; /* compressed output file */
+ const char *in; /* compressed input file */
+ Byte *uncompr;
+ int uncomprLen;
+{
+ int err;
+ int len = strlen(hello)+1;
+ gzFile file;
+ z_off_t pos;
+
+ file = gzopen(out, "wb");
+ if (file == NULL) {
+ fprintf(stderr, "gzopen error\n");
+ exit(1);
+ }
+ gzputc(file, 'h');
+ if (gzputs(file, "ello") != 4) {
+ fprintf(stderr, "gzputs err: %s\n", gzerror(file, &err));
+ exit(1);
+ }
+ if (gzprintf(file, ", %s!", "hello") != 8) {
+ fprintf(stderr, "gzprintf err: %s\n", gzerror(file, &err));
+ exit(1);
+ }
+ gzseek(file, 1L, SEEK_CUR); /* add one zero byte */
+ gzclose(file);
+
+ file = gzopen(in, "rb");
+ if (file == NULL) {
+ fprintf(stderr, "gzopen error\n");
+ }
+ strcpy((char*)uncompr, "garbage");
+
+ uncomprLen = gzread(file, uncompr, (unsigned)uncomprLen);
+ if (uncomprLen != len) {
+ fprintf(stderr, "gzread err: %s\n", gzerror(file, &err));
+ exit(1);
+ }
+ if (strcmp((char*)uncompr, hello)) {
+ fprintf(stderr, "bad gzread: %s\n", (char*)uncompr);
+ exit(1);
+ } else {
+ printf("gzread(): %s\n", (char *)uncompr);
+ }
+
+ pos = gzseek(file, -8L, SEEK_CUR);
+ if (pos != 6 || gztell(file) != pos) {
+ fprintf(stderr, "gzseek error, pos=%ld, gztell=%ld\n",
+ (long)pos, (long)gztell(file));
+ exit(1);
+ }
+
+ if (gzgetc(file) != ' ') {
+ fprintf(stderr, "gzgetc error\n");
+ exit(1);
+ }
+
+ gzgets(file, (char*)uncompr, uncomprLen);
+ uncomprLen = strlen((char*)uncompr);
+ if (uncomprLen != 6) { /* "hello!" */
+ fprintf(stderr, "gzgets err after gzseek: %s\n", gzerror(file, &err));
+ exit(1);
+ }
+ if (strcmp((char*)uncompr, hello+7)) {
+ fprintf(stderr, "bad gzgets after gzseek\n");
+ exit(1);
+ } else {
+ printf("gzgets() after gzseek: %s\n", (char *)uncompr);
+ }
+
+ gzclose(file);
+}
+
+/* ===========================================================================
+ * Test deflate() with small buffers
+ */
+void test_deflate(compr, comprLen)
+ Byte *compr;
+ uLong comprLen;
+{
+ z_stream c_stream; /* compression stream */
+ int err;
+ int len = strlen(hello)+1;
+
+ c_stream.zalloc = (alloc_func)0;
+ c_stream.zfree = (free_func)0;
+ c_stream.opaque = (voidpf)0;
+
+ err = deflateInit(&c_stream, Z_DEFAULT_COMPRESSION);
+ CHECK_ERR(err, "deflateInit");
+
+ c_stream.next_in = (Bytef*)hello;
+ c_stream.next_out = compr;
+
+ while (c_stream.total_in != (uLong)len && c_stream.total_out < comprLen) {
+ c_stream.avail_in = c_stream.avail_out = 1; /* force small buffers */
+ err = deflate(&c_stream, Z_NO_FLUSH);
+ CHECK_ERR(err, "deflate");
+ }
+ /* Finish the stream, still forcing small buffers: */
+ for (;;) {
+ c_stream.avail_out = 1;
+ err = deflate(&c_stream, Z_FINISH);
+ if (err == Z_STREAM_END) break;
+ CHECK_ERR(err, "deflate");
+ }
+
+ err = deflateEnd(&c_stream);
+ CHECK_ERR(err, "deflateEnd");
+}
+
+/* ===========================================================================
+ * Test inflate() with small buffers
+ */
+void test_inflate(compr, comprLen, uncompr, uncomprLen)
+ Byte *compr, *uncompr;
+ uLong comprLen, uncomprLen;
+{
+ int err;
+ z_stream d_stream; /* decompression stream */
+
+ strcpy((char*)uncompr, "garbage");
+
+ d_stream.zalloc = (alloc_func)0;
+ d_stream.zfree = (free_func)0;
+ d_stream.opaque = (voidpf)0;
+
+ d_stream.next_in = compr;
+ d_stream.avail_in = 0;
+ d_stream.next_out = uncompr;
+
+ err = inflateInit(&d_stream);
+ CHECK_ERR(err, "inflateInit");
+
+ while (d_stream.total_out < uncomprLen && d_stream.total_in < comprLen) {
+ d_stream.avail_in = d_stream.avail_out = 1; /* force small buffers */
+ err = inflate(&d_stream, Z_NO_FLUSH);
+ if (err == Z_STREAM_END) break;
+ CHECK_ERR(err, "inflate");
+ }
+
+ err = inflateEnd(&d_stream);
+ CHECK_ERR(err, "inflateEnd");
+
+ if (strcmp((char*)uncompr, hello)) {
+ fprintf(stderr, "bad inflate\n");
+ exit(1);
+ } else {
+ printf("inflate(): %s\n", (char *)uncompr);
+ }
+}
+
+/* ===========================================================================
+ * Test deflate() with large buffers and dynamic change of compression level
+ */
+void test_large_deflate(compr, comprLen, uncompr, uncomprLen)
+ Byte *compr, *uncompr;
+ uLong comprLen, uncomprLen;
+{
+ z_stream c_stream; /* compression stream */
+ int err;
+
+ c_stream.zalloc = (alloc_func)0;
+ c_stream.zfree = (free_func)0;
+ c_stream.opaque = (voidpf)0;
+
+ err = deflateInit(&c_stream, Z_BEST_SPEED);
+ CHECK_ERR(err, "deflateInit");
+
+ c_stream.next_out = compr;
+ c_stream.avail_out = (uInt)comprLen;
+
+ /* At this point, uncompr is still mostly zeroes, so it should compress
+ * very well:
+ */
+ c_stream.next_in = uncompr;
+ c_stream.avail_in = (uInt)uncomprLen;
+ err = deflate(&c_stream, Z_NO_FLUSH);
+ CHECK_ERR(err, "deflate");
+ if (c_stream.avail_in != 0) {
+ fprintf(stderr, "deflate not greedy\n");
+ exit(1);
+ }
+
+ /* Feed in already compressed data and switch to no compression: */
+ deflateParams(&c_stream, Z_NO_COMPRESSION, Z_DEFAULT_STRATEGY);
+ c_stream.next_in = compr;
+ c_stream.avail_in = (uInt)comprLen/2;
+ err = deflate(&c_stream, Z_NO_FLUSH);
+ CHECK_ERR(err, "deflate");
+
+ /* Switch back to compressing mode: */
+ deflateParams(&c_stream, Z_BEST_COMPRESSION, Z_FILTERED);
+ c_stream.next_in = uncompr;
+ c_stream.avail_in = (uInt)uncomprLen;
+ err = deflate(&c_stream, Z_NO_FLUSH);
+ CHECK_ERR(err, "deflate");
+
+ err = deflate(&c_stream, Z_FINISH);
+ if (err != Z_STREAM_END) {
+ fprintf(stderr, "deflate should report Z_STREAM_END\n");
+ exit(1);
+ }
+ err = deflateEnd(&c_stream);
+ CHECK_ERR(err, "deflateEnd");
+}
+
+/* ===========================================================================
+ * Test inflate() with large buffers
+ */
+void test_large_inflate(compr, comprLen, uncompr, uncomprLen)
+ Byte *compr, *uncompr;
+ uLong comprLen, uncomprLen;
+{
+ int err;
+ z_stream d_stream; /* decompression stream */
+
+ strcpy((char*)uncompr, "garbage");
+
+ d_stream.zalloc = (alloc_func)0;
+ d_stream.zfree = (free_func)0;
+ d_stream.opaque = (voidpf)0;
+
+ d_stream.next_in = compr;
+ d_stream.avail_in = (uInt)comprLen;
+
+ err = inflateInit(&d_stream);
+ CHECK_ERR(err, "inflateInit");
+
+ for (;;) {
+ d_stream.next_out = uncompr; /* discard the output */
+ d_stream.avail_out = (uInt)uncomprLen;
+ err = inflate(&d_stream, Z_NO_FLUSH);
+ if (err == Z_STREAM_END) break;
+ CHECK_ERR(err, "large inflate");
+ }
+
+ err = inflateEnd(&d_stream);
+ CHECK_ERR(err, "inflateEnd");
+
+ if (d_stream.total_out != 2*uncomprLen + comprLen/2) {
+ fprintf(stderr, "bad large inflate: %ld\n", d_stream.total_out);
+ exit(1);
+ } else {
+ printf("large_inflate(): OK\n");
+ }
+}
+
+/* ===========================================================================
+ * Test deflate() with full flush
+ */
+void test_flush(compr, comprLen)
+ Byte *compr;
+ uLong *comprLen;
+{
+ z_stream c_stream; /* compression stream */
+ int err;
+ int len = strlen(hello)+1;
+
+ c_stream.zalloc = (alloc_func)0;
+ c_stream.zfree = (free_func)0;
+ c_stream.opaque = (voidpf)0;
+
+ err = deflateInit(&c_stream, Z_DEFAULT_COMPRESSION);
+ CHECK_ERR(err, "deflateInit");
+
+ c_stream.next_in = (Bytef*)hello;
+ c_stream.next_out = compr;
+ c_stream.avail_in = 3;
+ c_stream.avail_out = (uInt)*comprLen;
+ err = deflate(&c_stream, Z_FULL_FLUSH);
+ CHECK_ERR(err, "deflate");
+
+ compr[3]++; /* force an error in first compressed block */
+ c_stream.avail_in = len - 3;
+
+ err = deflate(&c_stream, Z_FINISH);
+ if (err != Z_STREAM_END) {
+ CHECK_ERR(err, "deflate");
+ }
+ err = deflateEnd(&c_stream);
+ CHECK_ERR(err, "deflateEnd");
+
+ *comprLen = c_stream.total_out;
+}
+
+/* ===========================================================================
+ * Test inflateSync()
+ */
+void test_sync(compr, comprLen, uncompr, uncomprLen)
+ Byte *compr, *uncompr;
+ uLong comprLen, uncomprLen;
+{
+ int err;
+ z_stream d_stream; /* decompression stream */
+
+ strcpy((char*)uncompr, "garbage");
+
+ d_stream.zalloc = (alloc_func)0;
+ d_stream.zfree = (free_func)0;
+ d_stream.opaque = (voidpf)0;
+
+ d_stream.next_in = compr;
+ d_stream.avail_in = 2; /* just read the zlib header */
+
+ err = inflateInit(&d_stream);
+ CHECK_ERR(err, "inflateInit");
+
+ d_stream.next_out = uncompr;
+ d_stream.avail_out = (uInt)uncomprLen;
+
+ inflate(&d_stream, Z_NO_FLUSH);
+ CHECK_ERR(err, "inflate");
+
+ d_stream.avail_in = (uInt)comprLen-2; /* read all compressed data */
+ err = inflateSync(&d_stream); /* but skip the damaged part */
+ CHECK_ERR(err, "inflateSync");
+
+ err = inflate(&d_stream, Z_FINISH);
+ if (err != Z_DATA_ERROR) {
+ fprintf(stderr, "inflate should report DATA_ERROR\n");
+ /* Because of incorrect adler32 */
+ exit(1);
+ }
+ err = inflateEnd(&d_stream);
+ CHECK_ERR(err, "inflateEnd");
+
+ printf("after inflateSync(): hel%s\n", (char *)uncompr);
+}
+
+/* ===========================================================================
+ * Test deflate() with preset dictionary
+ */
+void test_dict_deflate(compr, comprLen)
+ Byte *compr;
+ uLong comprLen;
+{
+ z_stream c_stream; /* compression stream */
+ int err;
+
+ c_stream.zalloc = (alloc_func)0;
+ c_stream.zfree = (free_func)0;
+ c_stream.opaque = (voidpf)0;
+
+ err = deflateInit(&c_stream, Z_BEST_COMPRESSION);
+ CHECK_ERR(err, "deflateInit");
+
+ err = deflateSetDictionary(&c_stream,
+ (const Bytef*)dictionary, sizeof(dictionary));
+ CHECK_ERR(err, "deflateSetDictionary");
+
+ dictId = c_stream.adler;
+ c_stream.next_out = compr;
+ c_stream.avail_out = (uInt)comprLen;
+
+ c_stream.next_in = (Bytef*)hello;
+ c_stream.avail_in = (uInt)strlen(hello)+1;
+
+ err = deflate(&c_stream, Z_FINISH);
+ if (err != Z_STREAM_END) {
+ fprintf(stderr, "deflate should report Z_STREAM_END\n");
+ exit(1);
+ }
+ err = deflateEnd(&c_stream);
+ CHECK_ERR(err, "deflateEnd");
+}
+
+/* ===========================================================================
+ * Test inflate() with a preset dictionary
+ */
+void test_dict_inflate(compr, comprLen, uncompr, uncomprLen)
+ Byte *compr, *uncompr;
+ uLong comprLen, uncomprLen;
+{
+ int err;
+ z_stream d_stream; /* decompression stream */
+
+ strcpy((char*)uncompr, "garbage");
+
+ d_stream.zalloc = (alloc_func)0;
+ d_stream.zfree = (free_func)0;
+ d_stream.opaque = (voidpf)0;
+
+ d_stream.next_in = compr;
+ d_stream.avail_in = (uInt)comprLen;
+
+ err = inflateInit(&d_stream);
+ CHECK_ERR(err, "inflateInit");
+
+ d_stream.next_out = uncompr;
+ d_stream.avail_out = (uInt)uncomprLen;
+
+ for (;;) {
+ err = inflate(&d_stream, Z_NO_FLUSH);
+ if (err == Z_STREAM_END) break;
+ if (err == Z_NEED_DICT) {
+ if (d_stream.adler != dictId) {
+ fprintf(stderr, "unexpected dictionary");
+ exit(1);
+ }
+ err = inflateSetDictionary(&d_stream, (const Bytef*)dictionary,
+ sizeof(dictionary));
+ }
+ CHECK_ERR(err, "inflate with dict");
+ }
+
+ err = inflateEnd(&d_stream);
+ CHECK_ERR(err, "inflateEnd");
+
+ if (strcmp((char*)uncompr, hello)) {
+ fprintf(stderr, "bad inflate with dict\n");
+ exit(1);
+ } else {
+ printf("inflate with dictionary: %s\n", (char *)uncompr);
+ }
+}
+
+/* ===========================================================================
+ * Usage: example [output.gz [input.gz]]
+ */
+
+int main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ Byte *compr, *uncompr;
+ uLong comprLen = 10000*sizeof(int); /* don't overflow on MSDOS */
+ uLong uncomprLen = comprLen;
+ static const char* myVersion = ZLIB_VERSION;
+
+ if (zlibVersion()[0] != myVersion[0]) {
+ fprintf(stderr, "incompatible zlib version\n");
+ exit(1);
+
+ } else if (strcmp(zlibVersion(), ZLIB_VERSION) != 0) {
+ fprintf(stderr, "warning: different zlib version\n");
+ }
+
+ compr = (Byte*)calloc((uInt)comprLen, 1);
+ uncompr = (Byte*)calloc((uInt)uncomprLen, 1);
+ /* compr and uncompr are cleared to avoid reading uninitialized
+ * data and to ensure that uncompr compresses well.
+ */
+ if (compr == Z_NULL || uncompr == Z_NULL) {
+ printf("out of memory\n");
+ exit(1);
+ }
+ test_compress(compr, comprLen, uncompr, uncomprLen);
+
+ test_gzio((argc > 1 ? argv[1] : TESTFILE),
+ (argc > 2 ? argv[2] : TESTFILE),
+ uncompr, (int)uncomprLen);
+
+ test_deflate(compr, comprLen);
+ test_inflate(compr, comprLen, uncompr, uncomprLen);
+
+ test_large_deflate(compr, comprLen, uncompr, uncomprLen);
+ test_large_inflate(compr, comprLen, uncompr, uncomprLen);
+
+ test_flush(compr, &comprLen);
+ test_sync(compr, comprLen, uncompr, uncomprLen);
+ comprLen = uncomprLen;
+
+ test_dict_deflate(compr, comprLen);
+ test_dict_inflate(compr, comprLen, uncompr, uncomprLen);
+
+ exit(0);
+ return 0; /* to avoid warning */
+}
diff --git a/zlib/gzio.c b/zlib/gzio.c
new file mode 100644
index 00000000..518b573b
--- /dev/null
+++ b/zlib/gzio.c
@@ -0,0 +1,875 @@
+/* gzio.c -- IO on .gz files
+ * Copyright (C) 1995-2002 Jean-loup Gailly.
+ * For conditions of distribution and use, see copyright notice in zlib.h
+ *
+ * Compile this file with -DNO_DEFLATE to avoid the compression code.
+ */
+
+/* @(#) $Id: gzio.c,v 1.1 2004/10/08 09:44:25 const_k Exp $ */
+
+#include <stdio.h>
+
+#include "zutil.h"
+
+struct internal_state {int dummy;}; /* for buggy compilers */
+
+#ifndef Z_BUFSIZE
+# ifdef MAXSEG_64K
+# define Z_BUFSIZE 4096 /* minimize memory usage for 16-bit DOS */
+# else
+# define Z_BUFSIZE 16384
+# endif
+#endif
+#ifndef Z_PRINTF_BUFSIZE
+# define Z_PRINTF_BUFSIZE 4096
+#endif
+
+#define ALLOC(size) malloc(size)
+#define TRYFREE(p) {if (p) free(p);}
+
+static int gz_magic[2] = {0x1f, 0x8b}; /* gzip magic header */
+
+/* gzip flag byte */
+#define ASCII_FLAG 0x01 /* bit 0 set: file probably ascii text */
+#define HEAD_CRC 0x02 /* bit 1 set: header CRC present */
+#define EXTRA_FIELD 0x04 /* bit 2 set: extra field present */
+#define ORIG_NAME 0x08 /* bit 3 set: original file name present */
+#define COMMENT 0x10 /* bit 4 set: file comment present */
+#define RESERVED 0xE0 /* bits 5..7: reserved */
+
+typedef struct gz_stream {
+ z_stream stream;
+ int z_err; /* error code for last stream operation */
+ int z_eof; /* set if end of input file */
+ FILE *file; /* .gz file */
+ Byte *inbuf; /* input buffer */
+ Byte *outbuf; /* output buffer */
+ uLong crc; /* crc32 of uncompressed data */
+ char *msg; /* error message */
+ char *path; /* path name for debugging only */
+ int transparent; /* 1 if input file is not a .gz file */
+ char mode; /* 'w' or 'r' */
+ long startpos; /* start of compressed data in file (header skipped) */
+} gz_stream;
+
+
+local gzFile gz_open OF((const char *path, const char *mode, int fd));
+local int do_flush OF((gzFile file, int flush));
+local int get_byte OF((gz_stream *s));
+local void check_header OF((gz_stream *s));
+local int destroy OF((gz_stream *s));
+local void putLong OF((FILE *file, uLong x));
+local uLong getLong OF((gz_stream *s));
+
+/* ===========================================================================
+ Opens a gzip (.gz) file for reading or writing. The mode parameter
+ is as in fopen ("rb" or "wb"). The file is given either by file descriptor
+ or path name (if fd == -1).
+ gz_open return NULL if the file could not be opened or if there was
+ insufficient memory to allocate the (de)compression state; errno
+ can be checked to distinguish the two cases (if errno is zero, the
+ zlib error is Z_MEM_ERROR).
+*/
+local gzFile gz_open (path, mode, fd)
+ const char *path;
+ const char *mode;
+ int fd;
+{
+ int err;
+ int level = Z_DEFAULT_COMPRESSION; /* compression level */
+ int strategy = Z_DEFAULT_STRATEGY; /* compression strategy */
+ char *p = (char*)mode;
+ gz_stream *s;
+ char fmode[80]; /* copy of mode, without the compression level */
+ char *m = fmode;
+
+ if (!path || !mode) return Z_NULL;
+
+ s = (gz_stream *)ALLOC(sizeof(gz_stream));
+ if (!s) return Z_NULL;
+
+ s->stream.zalloc = (alloc_func)0;
+ s->stream.zfree = (free_func)0;
+ s->stream.opaque = (voidpf)0;
+ s->stream.next_in = s->inbuf = Z_NULL;
+ s->stream.next_out = s->outbuf = Z_NULL;
+ s->stream.avail_in = s->stream.avail_out = 0;
+ s->file = NULL;
+ s->z_err = Z_OK;
+ s->z_eof = 0;
+ s->crc = crc32(0L, Z_NULL, 0);
+ s->msg = NULL;
+ s->transparent = 0;
+
+ s->path = (char*)ALLOC(strlen(path)+1);
+ if (s->path == NULL) {
+ return destroy(s), (gzFile)Z_NULL;
+ }
+ strcpy(s->path, path); /* do this early for debugging */
+
+ s->mode = '\0';
+ do {
+ if (*p == 'r') s->mode = 'r';
+ if (*p == 'w' || *p == 'a') s->mode = 'w';
+ if (*p >= '0' && *p <= '9') {
+ level = *p - '0';
+ } else if (*p == 'f') {
+ strategy = Z_FILTERED;
+ } else if (*p == 'h') {
+ strategy = Z_HUFFMAN_ONLY;
+ } else {
+ *m++ = *p; /* copy the mode */
+ }
+ } while (*p++ && m != fmode + sizeof(fmode));
+ if (s->mode == '\0') return destroy(s), (gzFile)Z_NULL;
+
+ if (s->mode == 'w') {
+#ifdef NO_DEFLATE
+ err = Z_STREAM_ERROR;
+#else
+ err = deflateInit2(&(s->stream), level,
+ Z_DEFLATED, -MAX_WBITS, DEF_MEM_LEVEL, strategy);
+ /* windowBits is passed < 0 to suppress zlib header */
+
+ s->stream.next_out = s->outbuf = (Byte*)ALLOC(Z_BUFSIZE);
+#endif
+ if (err != Z_OK || s->outbuf == Z_NULL) {
+ return destroy(s), (gzFile)Z_NULL;
+ }
+ } else {
+ s->stream.next_in = s->inbuf = (Byte*)ALLOC(Z_BUFSIZE);
+
+ err = inflateInit2(&(s->stream), -MAX_WBITS);
+ /* windowBits is passed < 0 to tell that there is no zlib header.
+ * Note that in this case inflate *requires* an extra "dummy" byte
+ * after the compressed stream in order to complete decompression and
+ * return Z_STREAM_END. Here the gzip CRC32 ensures that 4 bytes are
+ * present after the compressed stream.
+ */
+ if (err != Z_OK || s->inbuf == Z_NULL) {
+ return destroy(s), (gzFile)Z_NULL;
+ }
+ }
+ s->stream.avail_out = Z_BUFSIZE;
+
+ errno = 0;
+ s->file = fd < 0 ? F_OPEN(path, fmode) : (FILE*)fdopen(fd, fmode);
+
+ if (s->file == NULL) {
+ return destroy(s), (gzFile)Z_NULL;
+ }
+ if (s->mode == 'w') {
+ /* Write a very simple .gz header:
+ */
+ fprintf(s->file, "%c%c%c%c%c%c%c%c%c%c", gz_magic[0], gz_magic[1],
+ Z_DEFLATED, 0 /*flags*/, 0,0,0,0 /*time*/, 0 /*xflags*/, OS_CODE);
+ s->startpos = 10L;
+ /* We use 10L instead of ftell(s->file) to because ftell causes an
+ * fflush on some systems. This version of the library doesn't use
+ * startpos anyway in write mode, so this initialization is not
+ * necessary.
+ */
+ } else {
+ check_header(s); /* skip the .gz header */
+ s->startpos = (ftell(s->file) - s->stream.avail_in);
+ }
+
+ return (gzFile)s;
+}
+
+/* ===========================================================================
+ Opens a gzip (.gz) file for reading or writing.
+*/
+gzFile ZEXPORT gzopen (path, mode)
+ const char *path;
+ const char *mode;
+{
+ return gz_open (path, mode, -1);
+}
+
+/* ===========================================================================
+ Associate a gzFile with the file descriptor fd. fd is not dup'ed here
+ to mimic the behavio(u)r of fdopen.
+*/
+gzFile ZEXPORT gzdopen (fd, mode)
+ int fd;
+ const char *mode;
+{
+ char name[20];
+
+ if (fd < 0) return (gzFile)Z_NULL;
+ sprintf(name, "<fd:%d>", fd); /* for debugging */
+
+ return gz_open (name, mode, fd);
+}
+
+/* ===========================================================================
+ * Update the compression level and strategy
+ */
+int ZEXPORT gzsetparams (file, level, strategy)
+ gzFile file;
+ int level;
+ int strategy;
+{
+ gz_stream *s = (gz_stream*)file;
+
+ if (s == NULL || s->mode != 'w') return Z_STREAM_ERROR;
+
+ /* Make room to allow flushing */
+ if (s->stream.avail_out == 0) {
+
+ s->stream.next_out = s->outbuf;
+ if (fwrite(s->outbuf, 1, Z_BUFSIZE, s->file) != Z_BUFSIZE) {
+ s->z_err = Z_ERRNO;
+ }
+ s->stream.avail_out = Z_BUFSIZE;
+ }
+
+ return deflateParams (&(s->stream), level, strategy);
+}
+
+/* ===========================================================================
+ Read a byte from a gz_stream; update next_in and avail_in. Return EOF
+ for end of file.
+ IN assertion: the stream s has been sucessfully opened for reading.
+*/
+local int get_byte(s)
+ gz_stream *s;
+{
+ if (s->z_eof) return EOF;
+ if (s->stream.avail_in == 0) {
+ errno = 0;
+ s->stream.avail_in = fread(s->inbuf, 1, Z_BUFSIZE, s->file);
+ if (s->stream.avail_in == 0) {
+ s->z_eof = 1;
+ if (ferror(s->file)) s->z_err = Z_ERRNO;
+ return EOF;
+ }
+ s->stream.next_in = s->inbuf;
+ }
+ s->stream.avail_in--;
+ return *(s->stream.next_in)++;
+}
+
+/* ===========================================================================
+ Check the gzip header of a gz_stream opened for reading. Set the stream
+ mode to transparent if the gzip magic header is not present; set s->err
+ to Z_DATA_ERROR if the magic header is present but the rest of the header
+ is incorrect.
+ IN assertion: the stream s has already been created sucessfully;
+ s->stream.avail_in is zero for the first time, but may be non-zero
+ for concatenated .gz files.
+*/
+local void check_header(s)
+ gz_stream *s;
+{
+ int method; /* method byte */
+ int flags; /* flags byte */
+ uInt len;
+ int c;
+
+ /* Check the gzip magic header */
+ for (len = 0; len < 2; len++) {
+ c = get_byte(s);
+ if (c != gz_magic[len]) {
+ if (len != 0) s->stream.avail_in++, s->stream.next_in--;
+ if (c != EOF) {
+ s->stream.avail_in++, s->stream.next_in--;
+ s->transparent = 1;
+ }
+ s->z_err = s->stream.avail_in != 0 ? Z_OK : Z_STREAM_END;
+ return;
+ }
+ }
+ method = get_byte(s);
+ flags = get_byte(s);
+ if (method != Z_DEFLATED || (flags & RESERVED) != 0) {
+ s->z_err = Z_DATA_ERROR;
+ return;
+ }
+
+ /* Discard time, xflags and OS code: */
+ for (len = 0; len < 6; len++) (void)get_byte(s);
+
+ if ((flags & EXTRA_FIELD) != 0) { /* skip the extra field */
+ len = (uInt)get_byte(s);
+ len += ((uInt)get_byte(s))<<8;
+ /* len is garbage if EOF but the loop below will quit anyway */
+ while (len-- != 0 && get_byte(s) != EOF) ;
+ }
+ if ((flags & ORIG_NAME) != 0) { /* skip the original file name */
+ while ((c = get_byte(s)) != 0 && c != EOF) ;
+ }
+ if ((flags & COMMENT) != 0) { /* skip the .gz file comment */
+ while ((c = get_byte(s)) != 0 && c != EOF) ;
+ }
+ if ((flags & HEAD_CRC) != 0) { /* skip the header crc */
+ for (len = 0; len < 2; len++) (void)get_byte(s);
+ }
+ s->z_err = s->z_eof ? Z_DATA_ERROR : Z_OK;
+}
+
+ /* ===========================================================================
+ * Cleanup then free the given gz_stream. Return a zlib error code.
+ Try freeing in the reverse order of allocations.
+ */
+local int destroy (s)
+ gz_stream *s;
+{
+ int err = Z_OK;
+
+ if (!s) return Z_STREAM_ERROR;
+
+ TRYFREE(s->msg);
+
+ if (s->stream.state != NULL) {
+ if (s->mode == 'w') {
+#ifdef NO_DEFLATE
+ err = Z_STREAM_ERROR;
+#else
+ err = deflateEnd(&(s->stream));
+#endif
+ } else if (s->mode == 'r') {
+ err = inflateEnd(&(s->stream));
+ }
+ }
+ if (s->file != NULL && fclose(s->file)) {
+#ifdef ESPIPE
+ if (errno != ESPIPE) /* fclose is broken for pipes in HP/UX */
+#endif
+ err = Z_ERRNO;
+ }
+ if (s->z_err < 0) err = s->z_err;
+
+ TRYFREE(s->inbuf);
+ TRYFREE(s->outbuf);
+ TRYFREE(s->path);
+ TRYFREE(s);
+ return err;
+}
+
+/* ===========================================================================
+ Reads the given number of uncompressed bytes from the compressed file.
+ gzread returns the number of bytes actually read (0 for end of file).
+*/
+int ZEXPORT gzread (file, buf, len)
+ gzFile file;
+ voidp buf;
+ unsigned len;
+{
+ gz_stream *s = (gz_stream*)file;
+ Bytef *start = (Bytef*)buf; /* starting point for crc computation */
+ Byte *next_out; /* == stream.next_out but not forced far (for MSDOS) */
+
+ if (s == NULL || s->mode != 'r') return Z_STREAM_ERROR;
+
+ if (s->z_err == Z_DATA_ERROR || s->z_err == Z_ERRNO) return -1;
+ if (s->z_err == Z_STREAM_END) return 0; /* EOF */
+
+ next_out = (Byte*)buf;
+ s->stream.next_out = (Bytef*)buf;
+ s->stream.avail_out = len;
+
+ while (s->stream.avail_out != 0) {
+
+ if (s->transparent) {
+ /* Copy first the lookahead bytes: */
+ uInt n = s->stream.avail_in;
+ if (n > s->stream.avail_out) n = s->stream.avail_out;
+ if (n > 0) {
+ zmemcpy(s->stream.next_out, s->stream.next_in, n);
+ next_out += n;
+ s->stream.next_out = next_out;
+ s->stream.next_in += n;
+ s->stream.avail_out -= n;
+ s->stream.avail_in -= n;
+ }
+ if (s->stream.avail_out > 0) {
+ s->stream.avail_out -= fread(next_out, 1, s->stream.avail_out,
+ s->file);
+ }
+ len -= s->stream.avail_out;
+ s->stream.total_in += (uLong)len;
+ s->stream.total_out += (uLong)len;
+ if (len == 0) s->z_eof = 1;
+ return (int)len;
+ }
+ if (s->stream.avail_in == 0 && !s->z_eof) {
+
+ errno = 0;
+ s->stream.avail_in = fread(s->inbuf, 1, Z_BUFSIZE, s->file);
+ if (s->stream.avail_in == 0) {
+ s->z_eof = 1;
+ if (ferror(s->file)) {
+ s->z_err = Z_ERRNO;
+ break;
+ }
+ }
+ s->stream.next_in = s->inbuf;
+ }
+ s->z_err = inflate(&(s->stream), Z_NO_FLUSH);
+
+ if (s->z_err == Z_STREAM_END) {
+ /* Check CRC and original size */
+ s->crc = crc32(s->crc, start, (uInt)(s->stream.next_out - start));
+ start = s->stream.next_out;
+
+ if (getLong(s) != s->crc) {
+ s->z_err = Z_DATA_ERROR;
+ } else {
+ (void)getLong(s);
+ /* The uncompressed length returned by above getlong() may
+ * be different from s->stream.total_out) in case of
+ * concatenated .gz files. Check for such files:
+ */
+ check_header(s);
+ if (s->z_err == Z_OK) {
+ uLong total_in = s->stream.total_in;
+ uLong total_out = s->stream.total_out;
+
+ inflateReset(&(s->stream));
+ s->stream.total_in = total_in;
+ s->stream.total_out = total_out;
+ s->crc = crc32(0L, Z_NULL, 0);
+ }
+ }
+ }
+ if (s->z_err != Z_OK || s->z_eof) break;
+ }
+ s->crc = crc32(s->crc, start, (uInt)(s->stream.next_out - start));
+
+ return (int)(len - s->stream.avail_out);
+}
+
+
+/* ===========================================================================
+ Reads one byte from the compressed file. gzgetc returns this byte
+ or -1 in case of end of file or error.
+*/
+int ZEXPORT gzgetc(file)
+ gzFile file;
+{
+ unsigned char c;
+
+ return gzread(file, &c, 1) == 1 ? c : -1;
+}
+
+
+/* ===========================================================================
+ Reads bytes from the compressed file until len-1 characters are
+ read, or a newline character is read and transferred to buf, or an
+ end-of-file condition is encountered. The string is then terminated
+ with a null character.
+ gzgets returns buf, or Z_NULL in case of error.
+
+ The current implementation is not optimized at all.
+*/
+char * ZEXPORT gzgets(file, buf, len)
+ gzFile file;
+ char *buf;
+ int len;
+{
+ char *b = buf;
+ if (buf == Z_NULL || len <= 0) return Z_NULL;
+
+ while (--len > 0 && gzread(file, buf, 1) == 1 && *buf++ != '\n') ;
+ *buf = '\0';
+ return b == buf && len > 0 ? Z_NULL : b;
+}
+
+
+#ifndef NO_DEFLATE
+/* ===========================================================================
+ Writes the given number of uncompressed bytes into the compressed file.
+ gzwrite returns the number of bytes actually written (0 in case of error).
+*/
+int ZEXPORT gzwrite (file, buf, len)
+ gzFile file;
+ const voidp buf;
+ unsigned len;
+{
+ gz_stream *s = (gz_stream*)file;
+
+ if (s == NULL || s->mode != 'w') return Z_STREAM_ERROR;
+
+ s->stream.next_in = (Bytef*)buf;
+ s->stream.avail_in = len;
+
+ while (s->stream.avail_in != 0) {
+
+ if (s->stream.avail_out == 0) {
+
+ s->stream.next_out = s->outbuf;
+ if (fwrite(s->outbuf, 1, Z_BUFSIZE, s->file) != Z_BUFSIZE) {
+ s->z_err = Z_ERRNO;
+ break;
+ }
+ s->stream.avail_out = Z_BUFSIZE;
+ }
+ s->z_err = deflate(&(s->stream), Z_NO_FLUSH);
+ if (s->z_err != Z_OK) break;
+ }
+ s->crc = crc32(s->crc, (const Bytef *)buf, len);
+
+ return (int)(len - s->stream.avail_in);
+}
+
+/* ===========================================================================
+ Converts, formats, and writes the args to the compressed file under
+ control of the format string, as in fprintf. gzprintf returns the number of
+ uncompressed bytes actually written (0 in case of error).
+*/
+#ifdef STDC
+#include <stdarg.h>
+
+int ZEXPORTVA gzprintf (gzFile file, const char *format, /* args */ ...)
+{
+ char buf[Z_PRINTF_BUFSIZE];
+ va_list va;
+ int len;
+
+ va_start(va, format);
+#ifdef HAS_vsnprintf
+ (void)vsnprintf(buf, sizeof(buf), format, va);
+#else
+ (void)vsprintf(buf, format, va);
+#endif
+ va_end(va);
+ len = strlen(buf); /* some *sprintf don't return the nb of bytes written */
+ if (len <= 0) return 0;
+
+ return gzwrite(file, buf, (unsigned)len);
+}
+#else /* not ANSI C */
+
+int ZEXPORTVA gzprintf (file, format, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10,
+ a11, a12, a13, a14, a15, a16, a17, a18, a19, a20)
+ gzFile file;
+ const char *format;
+ int a1, a2, a3, a4, a5, a6, a7, a8, a9, a10,
+ a11, a12, a13, a14, a15, a16, a17, a18, a19, a20;
+{
+ char buf[Z_PRINTF_BUFSIZE];
+ int len;
+
+#ifdef HAS_snprintf
+ snprintf(buf, sizeof(buf), format, a1, a2, a3, a4, a5, a6, a7, a8,
+ a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20);
+#else
+ sprintf(buf, format, a1, a2, a3, a4, a5, a6, a7, a8,
+ a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20);
+#endif
+ len = strlen(buf); /* old sprintf doesn't return the nb of bytes written */
+ if (len <= 0) return 0;
+
+ return gzwrite(file, buf, len);
+}
+#endif
+
+/* ===========================================================================
+ Writes c, converted to an unsigned char, into the compressed file.
+ gzputc returns the value that was written, or -1 in case of error.
+*/
+int ZEXPORT gzputc(file, c)
+ gzFile file;
+ int c;
+{
+ unsigned char cc = (unsigned char) c; /* required for big endian systems */
+
+ return gzwrite(file, &cc, 1) == 1 ? (int)cc : -1;
+}
+
+
+/* ===========================================================================
+ Writes the given null-terminated string to the compressed file, excluding
+ the terminating null character.
+ gzputs returns the number of characters written, or -1 in case of error.
+*/
+int ZEXPORT gzputs(file, s)
+ gzFile file;
+ const char *s;
+{
+ return gzwrite(file, (char*)s, (unsigned)strlen(s));
+}
+
+
+/* ===========================================================================
+ Flushes all pending output into the compressed file. The parameter
+ flush is as in the deflate() function.
+*/
+local int do_flush (file, flush)
+ gzFile file;
+ int flush;
+{
+ uInt len;
+ int done = 0;
+ gz_stream *s = (gz_stream*)file;
+
+ if (s == NULL || s->mode != 'w') return Z_STREAM_ERROR;
+
+ s->stream.avail_in = 0; /* should be zero already anyway */
+
+ for (;;) {
+ len = Z_BUFSIZE - s->stream.avail_out;
+
+ if (len != 0) {
+ if ((uInt)fwrite(s->outbuf, 1, len, s->file) != len) {
+ s->z_err = Z_ERRNO;
+ return Z_ERRNO;
+ }
+ s->stream.next_out = s->outbuf;
+ s->stream.avail_out = Z_BUFSIZE;
+ }
+ if (done) break;
+ s->z_err = deflate(&(s->stream), flush);
+
+ /* Ignore the second of two consecutive flushes: */
+ if (len == 0 && s->z_err == Z_BUF_ERROR) s->z_err = Z_OK;
+
+ /* deflate has finished flushing only when it hasn't used up
+ * all the available space in the output buffer:
+ */
+ done = (s->stream.avail_out != 0 || s->z_err == Z_STREAM_END);
+
+ if (s->z_err != Z_OK && s->z_err != Z_STREAM_END) break;
+ }
+ return s->z_err == Z_STREAM_END ? Z_OK : s->z_err;
+}
+
+int ZEXPORT gzflush (file, flush)
+ gzFile file;
+ int flush;
+{
+ gz_stream *s = (gz_stream*)file;
+ int err = do_flush (file, flush);
+
+ if (err) return err;
+ fflush(s->file);
+ return s->z_err == Z_STREAM_END ? Z_OK : s->z_err;
+}
+#endif /* NO_DEFLATE */
+
+/* ===========================================================================
+ Sets the starting position for the next gzread or gzwrite on the given
+ compressed file. The offset represents a number of bytes in the
+ gzseek returns the resulting offset location as measured in bytes from
+ the beginning of the uncompressed stream, or -1 in case of error.
+ SEEK_END is not implemented, returns error.
+ In this version of the library, gzseek can be extremely slow.
+*/
+z_off_t ZEXPORT gzseek (file, offset, whence)
+ gzFile file;
+ z_off_t offset;
+ int whence;
+{
+ gz_stream *s = (gz_stream*)file;
+
+ if (s == NULL || whence == SEEK_END ||
+ s->z_err == Z_ERRNO || s->z_err == Z_DATA_ERROR) {
+ return -1L;
+ }
+
+ if (s->mode == 'w') {
+#ifdef NO_DEFLATE
+ return -1L;
+#else
+ if (whence == SEEK_SET) {
+ offset -= s->stream.total_in;
+ }
+ if (offset < 0) return -1L;
+
+ /* At this point, offset is the number of zero bytes to write. */
+ if (s->inbuf == Z_NULL) {
+ s->inbuf = (Byte*)ALLOC(Z_BUFSIZE); /* for seeking */
+ zmemzero(s->inbuf, Z_BUFSIZE);
+ }
+ while (offset > 0) {
+ uInt size = Z_BUFSIZE;
+ if (offset < Z_BUFSIZE) size = (uInt)offset;
+
+ size = gzwrite(file, s->inbuf, size);
+ if (size == 0) return -1L;
+
+ offset -= size;
+ }
+ return (z_off_t)s->stream.total_in;
+#endif
+ }
+ /* Rest of function is for reading only */
+
+ /* compute absolute position */
+ if (whence == SEEK_CUR) {
+ offset += s->stream.total_out;
+ }
+ if (offset < 0) return -1L;
+
+ if (s->transparent) {
+ /* map to fseek */
+ s->stream.avail_in = 0;
+ s->stream.next_in = s->inbuf;
+ if (fseek(s->file, offset, SEEK_SET) < 0) return -1L;
+
+ s->stream.total_in = s->stream.total_out = (uLong)offset;
+ return offset;
+ }
+
+ /* For a negative seek, rewind and use positive seek */
+ if ((uLong)offset >= s->stream.total_out) {
+ offset -= s->stream.total_out;
+ } else if (gzrewind(file) < 0) {
+ return -1L;
+ }
+ /* offset is now the number of bytes to skip. */
+
+ if (offset != 0 && s->outbuf == Z_NULL) {
+ s->outbuf = (Byte*)ALLOC(Z_BUFSIZE);
+ }
+ while (offset > 0) {
+ int size = Z_BUFSIZE;
+ if (offset < Z_BUFSIZE) size = (int)offset;
+
+ size = gzread(file, s->outbuf, (uInt)size);
+ if (size <= 0) return -1L;
+ offset -= size;
+ }
+ return (z_off_t)s->stream.total_out;
+}
+
+/* ===========================================================================
+ Rewinds input file.
+*/
+int ZEXPORT gzrewind (file)
+ gzFile file;
+{
+ gz_stream *s = (gz_stream*)file;
+
+ if (s == NULL || s->mode != 'r') return -1;
+
+ s->z_err = Z_OK;
+ s->z_eof = 0;
+ s->stream.avail_in = 0;
+ s->stream.next_in = s->inbuf;
+ s->crc = crc32(0L, Z_NULL, 0);
+
+ if (s->startpos == 0) { /* not a compressed file */
+ rewind(s->file);
+ return 0;
+ }
+
+ (void) inflateReset(&s->stream);
+ return fseek(s->file, s->startpos, SEEK_SET);
+}
+
+/* ===========================================================================
+ Returns the starting position for the next gzread or gzwrite on the
+ given compressed file. This position represents a number of bytes in the
+ uncompressed data stream.
+*/
+z_off_t ZEXPORT gztell (file)
+ gzFile file;
+{
+ return gzseek(file, 0L, SEEK_CUR);
+}
+
+/* ===========================================================================
+ Returns 1 when EOF has previously been detected reading the given
+ input stream, otherwise zero.
+*/
+int ZEXPORT gzeof (file)
+ gzFile file;
+{
+ gz_stream *s = (gz_stream*)file;
+
+ return (s == NULL || s->mode != 'r') ? 0 : s->z_eof;
+}
+
+/* ===========================================================================
+ Outputs a long in LSB order to the given file
+*/
+local void putLong (file, x)
+ FILE *file;
+ uLong x;
+{
+ int n;
+ for (n = 0; n < 4; n++) {
+ fputc((int)(x & 0xff), file);
+ x >>= 8;
+ }
+}
+
+/* ===========================================================================
+ Reads a long in LSB order from the given gz_stream. Sets z_err in case
+ of error.
+*/
+local uLong getLong (s)
+ gz_stream *s;
+{
+ uLong x = (uLong)get_byte(s);
+ int c;
+
+ x += ((uLong)get_byte(s))<<8;
+ x += ((uLong)get_byte(s))<<16;
+ c = get_byte(s);
+ if (c == EOF) s->z_err = Z_DATA_ERROR;
+ x += ((uLong)c)<<24;
+ return x;
+}
+
+/* ===========================================================================
+ Flushes all pending output if necessary, closes the compressed file
+ and deallocates all the (de)compression state.
+*/
+int ZEXPORT gzclose (file)
+ gzFile file;
+{
+ int err;
+ gz_stream *s = (gz_stream*)file;
+
+ if (s == NULL) return Z_STREAM_ERROR;
+
+ if (s->mode == 'w') {
+#ifdef NO_DEFLATE
+ return Z_STREAM_ERROR;
+#else
+ err = do_flush (file, Z_FINISH);
+ if (err != Z_OK) return destroy((gz_stream*)file);
+
+ putLong (s->file, s->crc);
+ putLong (s->file, s->stream.total_in);
+#endif
+ }
+ return destroy((gz_stream*)file);
+}
+
+/* ===========================================================================
+ Returns the error message for the last error which occured on the
+ given compressed file. errnum is set to zlib error number. If an
+ error occured in the file system and not in the compression library,
+ errnum is set to Z_ERRNO and the application may consult errno
+ to get the exact error code.
+*/
+const char* ZEXPORT gzerror (file, errnum)
+ gzFile file;
+ int *errnum;
+{
+ char *m;
+ gz_stream *s = (gz_stream*)file;
+
+ if (s == NULL) {
+ *errnum = Z_STREAM_ERROR;
+ return (const char*)ERR_MSG(Z_STREAM_ERROR);
+ }
+ *errnum = s->z_err;
+ if (*errnum == Z_OK) return (const char*)"";
+
+ m = (char*)(*errnum == Z_ERRNO ? zstrerror(errno) : s->stream.msg);
+
+ if (m == NULL || *m == '\0') m = (char*)ERR_MSG(s->z_err);
+
+ TRYFREE(s->msg);
+ s->msg = (char*)ALLOC(strlen(s->path) + strlen(m) + 3);
+ strcpy(s->msg, s->path);
+ strcat(s->msg, ": ");
+ strcat(s->msg, m);
+ return (const char*)s->msg;
+}
diff --git a/zlib/infblock.c b/zlib/infblock.c
new file mode 100644
index 00000000..dd7a6d40
--- /dev/null
+++ b/zlib/infblock.c
@@ -0,0 +1,403 @@
+/* infblock.c -- interpret and process block types to last block
+ * Copyright (C) 1995-2002 Mark Adler
+ * For conditions of distribution and use, see copyright notice in zlib.h
+ */
+
+#include "zutil.h"
+#include "infblock.h"
+#include "inftrees.h"
+#include "infcodes.h"
+#include "infutil.h"
+
+struct inflate_codes_state {int dummy;}; /* for buggy compilers */
+
+/* simplify the use of the inflate_huft type with some defines */
+#define exop word.what.Exop
+#define bits word.what.Bits
+
+/* Table for deflate from PKZIP's appnote.txt. */
+local const uInt border[] = { /* Order of the bit length code lengths */
+ 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15};
+
+/*
+ Notes beyond the 1.93a appnote.txt:
+
+ 1. Distance pointers never point before the beginning of the output
+ stream.
+ 2. Distance pointers can point back across blocks, up to 32k away.
+ 3. There is an implied maximum of 7 bits for the bit length table and
+ 15 bits for the actual data.
+ 4. If only one code exists, then it is encoded using one bit. (Zero
+ would be more efficient, but perhaps a little confusing.) If two
+ codes exist, they are coded using one bit each (0 and 1).
+ 5. There is no way of sending zero distance codes--a dummy must be
+ sent if there are none. (History: a pre 2.0 version of PKZIP would
+ store blocks with no distance codes, but this was discovered to be
+ too harsh a criterion.) Valid only for 1.93a. 2.04c does allow
+ zero distance codes, which is sent as one code of zero bits in
+ length.
+ 6. There are up to 286 literal/length codes. Code 256 represents the
+ end-of-block. Note however that the static length tree defines
+ 288 codes just to fill out the Huffman codes. Codes 286 and 287
+ cannot be used though, since there is no length base or extra bits
+ defined for them. Similarily, there are up to 30 distance codes.
+ However, static trees define 32 codes (all 5 bits) to fill out the
+ Huffman codes, but the last two had better not show up in the data.
+ 7. Unzip can check dynamic Huffman blocks for complete code sets.
+ The exception is that a single code would not be complete (see #4).
+ 8. The five bits following the block type is really the number of
+ literal codes sent minus 257.
+ 9. Length codes 8,16,16 are interpreted as 13 length codes of 8 bits
+ (1+6+6). Therefore, to output three times the length, you output
+ three codes (1+1+1), whereas to output four times the same length,
+ you only need two codes (1+3). Hmm.
+ 10. In the tree reconstruction algorithm, Code = Code + Increment
+ only if BitLength(i) is not zero. (Pretty obvious.)
+ 11. Correction: 4 Bits: # of Bit Length codes - 4 (4 - 19)
+ 12. Note: length code 284 can represent 227-258, but length code 285
+ really is 258. The last length deserves its own, short code
+ since it gets used a lot in very redundant files. The length
+ 258 is special since 258 - 3 (the min match length) is 255.
+ 13. The literal/length and distance code bit lengths are read as a
+ single stream of lengths. It is possible (and advantageous) for
+ a repeat code (16, 17, or 18) to go across the boundary between
+ the two sets of lengths.
+ */
+
+
+void inflate_blocks_reset(s, z, c)
+inflate_blocks_statef *s;
+z_streamp z;
+uLongf *c;
+{
+ if (c != Z_NULL)
+ *c = s->check;
+ if (s->mode == BTREE || s->mode == DTREE)
+ ZFREE(z, s->sub.trees.blens);
+ if (s->mode == CODES)
+ inflate_codes_free(s->sub.decode.codes, z);
+ s->mode = TYPE;
+ s->bitk = 0;
+ s->bitb = 0;
+ s->read = s->write = s->window;
+ if (s->checkfn != Z_NULL)
+ z->adler = s->check = (*s->checkfn)(0L, (const Bytef *)Z_NULL, 0);
+ Tracev((stderr, "inflate: blocks reset\n"));
+}
+
+
+inflate_blocks_statef *inflate_blocks_new(z, c, w)
+z_streamp z;
+check_func c;
+uInt w;
+{
+ inflate_blocks_statef *s;
+
+ if ((s = (inflate_blocks_statef *)ZALLOC
+ (z,1,sizeof(struct inflate_blocks_state))) == Z_NULL)
+ return s;
+ if ((s->hufts =
+ (inflate_huft *)ZALLOC(z, sizeof(inflate_huft), MANY)) == Z_NULL)
+ {
+ ZFREE(z, s);
+ return Z_NULL;
+ }
+ if ((s->window = (Bytef *)ZALLOC(z, 1, w)) == Z_NULL)
+ {
+ ZFREE(z, s->hufts);
+ ZFREE(z, s);
+ return Z_NULL;
+ }
+ s->end = s->window + w;
+ s->checkfn = c;
+ s->mode = TYPE;
+ Tracev((stderr, "inflate: blocks allocated\n"));
+ inflate_blocks_reset(s, z, Z_NULL);
+ return s;
+}
+
+
+int inflate_blocks(s, z, r)
+inflate_blocks_statef *s;
+z_streamp z;
+int r;
+{
+ uInt t; /* temporary storage */
+ uLong b; /* bit buffer */
+ uInt k; /* bits in bit buffer */
+ Bytef *p; /* input data pointer */
+ uInt n; /* bytes available there */
+ Bytef *q; /* output window write pointer */
+ uInt m; /* bytes to end of window or read pointer */
+
+ /* copy input/output information to locals (UPDATE macro restores) */
+ LOAD
+
+ /* process input based on current state */
+ while (1) switch (s->mode)
+ {
+ case TYPE:
+ NEEDBITS(3)
+ t = (uInt)b & 7;
+ s->last = t & 1;
+ switch (t >> 1)
+ {
+ case 0: /* stored */
+ Tracev((stderr, "inflate: stored block%s\n",
+ s->last ? " (last)" : ""));
+ DUMPBITS(3)
+ t = k & 7; /* go to byte boundary */
+ DUMPBITS(t)
+ s->mode = LENS; /* get length of stored block */
+ break;
+ case 1: /* fixed */
+ Tracev((stderr, "inflate: fixed codes block%s\n",
+ s->last ? " (last)" : ""));
+ {
+ uInt bl, bd;
+ inflate_huft *tl, *td;
+
+ inflate_trees_fixed(&bl, &bd, &tl, &td, z);
+ s->sub.decode.codes = inflate_codes_new(bl, bd, tl, td, z);
+ if (s->sub.decode.codes == Z_NULL)
+ {
+ r = Z_MEM_ERROR;
+ LEAVE
+ }
+ }
+ DUMPBITS(3)
+ s->mode = CODES;
+ break;
+ case 2: /* dynamic */
+ Tracev((stderr, "inflate: dynamic codes block%s\n",
+ s->last ? " (last)" : ""));
+ DUMPBITS(3)
+ s->mode = TABLE;
+ break;
+ case 3: /* illegal */
+ DUMPBITS(3)
+ s->mode = BAD;
+ z->msg = (char*)"invalid block type";
+ r = Z_DATA_ERROR;
+ LEAVE
+ }
+ break;
+ case LENS:
+ NEEDBITS(32)
+ if ((((~b) >> 16) & 0xffff) != (b & 0xffff))
+ {
+ s->mode = BAD;
+ z->msg = (char*)"invalid stored block lengths";
+ r = Z_DATA_ERROR;
+ LEAVE
+ }
+ s->sub.left = (uInt)b & 0xffff;
+ b = k = 0; /* dump bits */
+ Tracev((stderr, "inflate: stored length %u\n", s->sub.left));
+ s->mode = s->sub.left ? STORED : (s->last ? DRY : TYPE);
+ break;
+ case STORED:
+ if (n == 0)
+ LEAVE
+ NEEDOUT
+ t = s->sub.left;
+ if (t > n) t = n;
+ if (t > m) t = m;
+ zmemcpy(q, p, t);
+ p += t; n -= t;
+ q += t; m -= t;
+ if ((s->sub.left -= t) != 0)
+ break;
+ Tracev((stderr, "inflate: stored end, %lu total out\n",
+ z->total_out + (q >= s->read ? q - s->read :
+ (s->end - s->read) + (q - s->window))));
+ s->mode = s->last ? DRY : TYPE;
+ break;
+ case TABLE:
+ NEEDBITS(14)
+ s->sub.trees.table = t = (uInt)b & 0x3fff;
+#ifndef PKZIP_BUG_WORKAROUND
+ if ((t & 0x1f) > 29 || ((t >> 5) & 0x1f) > 29)
+ {
+ s->mode = BAD;
+ z->msg = (char*)"too many length or distance symbols";
+ r = Z_DATA_ERROR;
+ LEAVE
+ }
+#endif
+ t = 258 + (t & 0x1f) + ((t >> 5) & 0x1f);
+ if ((s->sub.trees.blens = (uIntf*)ZALLOC(z, t, sizeof(uInt))) == Z_NULL)
+ {
+ r = Z_MEM_ERROR;
+ LEAVE
+ }
+ DUMPBITS(14)
+ s->sub.trees.index = 0;
+ Tracev((stderr, "inflate: table sizes ok\n"));
+ s->mode = BTREE;
+ case BTREE:
+ while (s->sub.trees.index < 4 + (s->sub.trees.table >> 10))
+ {
+ NEEDBITS(3)
+ s->sub.trees.blens[border[s->sub.trees.index++]] = (uInt)b & 7;
+ DUMPBITS(3)
+ }
+ while (s->sub.trees.index < 19)
+ s->sub.trees.blens[border[s->sub.trees.index++]] = 0;
+ s->sub.trees.bb = 7;
+ t = inflate_trees_bits(s->sub.trees.blens, &s->sub.trees.bb,
+ &s->sub.trees.tb, s->hufts, z);
+ if (t != Z_OK)
+ {
+ r = t;
+ if (r == Z_DATA_ERROR)
+ {
+ ZFREE(z, s->sub.trees.blens);
+ s->mode = BAD;
+ }
+ LEAVE
+ }
+ s->sub.trees.index = 0;
+ Tracev((stderr, "inflate: bits tree ok\n"));
+ s->mode = DTREE;
+ case DTREE:
+ while (t = s->sub.trees.table,
+ s->sub.trees.index < 258 + (t & 0x1f) + ((t >> 5) & 0x1f))
+ {
+ inflate_huft *h;
+ uInt i, j, c;
+
+ t = s->sub.trees.bb;
+ NEEDBITS(t)
+ h = s->sub.trees.tb + ((uInt)b & inflate_mask[t]);
+ t = h->bits;
+ c = h->base;
+ if (c < 16)
+ {
+ DUMPBITS(t)
+ s->sub.trees.blens[s->sub.trees.index++] = c;
+ }
+ else /* c == 16..18 */
+ {
+ i = c == 18 ? 7 : c - 14;
+ j = c == 18 ? 11 : 3;
+ NEEDBITS(t + i)
+ DUMPBITS(t)
+ j += (uInt)b & inflate_mask[i];
+ DUMPBITS(i)
+ i = s->sub.trees.index;
+ t = s->sub.trees.table;
+ if (i + j > 258 + (t & 0x1f) + ((t >> 5) & 0x1f) ||
+ (c == 16 && i < 1))
+ {
+ ZFREE(z, s->sub.trees.blens);
+ s->mode = BAD;
+ z->msg = (char*)"invalid bit length repeat";
+ r = Z_DATA_ERROR;
+ LEAVE
+ }
+ c = c == 16 ? s->sub.trees.blens[i - 1] : 0;
+ do {
+ s->sub.trees.blens[i++] = c;
+ } while (--j);
+ s->sub.trees.index = i;
+ }
+ }
+ s->sub.trees.tb = Z_NULL;
+ {
+ uInt bl, bd;
+ inflate_huft *tl, *td;
+ inflate_codes_statef *c;
+
+ bl = 9; /* must be <= 9 for lookahead assumptions */
+ bd = 6; /* must be <= 9 for lookahead assumptions */
+ t = s->sub.trees.table;
+ t = inflate_trees_dynamic(257 + (t & 0x1f), 1 + ((t >> 5) & 0x1f),
+ s->sub.trees.blens, &bl, &bd, &tl, &td,
+ s->hufts, z);
+ if (t != Z_OK)
+ {
+ if (t == (uInt)Z_DATA_ERROR)
+ {
+ ZFREE(z, s->sub.trees.blens);
+ s->mode = BAD;
+ }
+ r = t;
+ LEAVE
+ }
+ Tracev((stderr, "inflate: trees ok\n"));
+ if ((c = inflate_codes_new(bl, bd, tl, td, z)) == Z_NULL)
+ {
+ r = Z_MEM_ERROR;
+ LEAVE
+ }
+ s->sub.decode.codes = c;
+ }
+ ZFREE(z, s->sub.trees.blens);
+ s->mode = CODES;
+ case CODES:
+ UPDATE
+ if ((r = inflate_codes(s, z, r)) != Z_STREAM_END)
+ return inflate_flush(s, z, r);
+ r = Z_OK;
+ inflate_codes_free(s->sub.decode.codes, z);
+ LOAD
+ Tracev((stderr, "inflate: codes end, %lu total out\n",
+ z->total_out + (q >= s->read ? q - s->read :
+ (s->end - s->read) + (q - s->window))));
+ if (!s->last)
+ {
+ s->mode = TYPE;
+ break;
+ }
+ s->mode = DRY;
+ case DRY:
+ FLUSH
+ if (s->read != s->write)
+ LEAVE
+ s->mode = DONE;
+ case DONE:
+ r = Z_STREAM_END;
+ LEAVE
+ case BAD:
+ r = Z_DATA_ERROR;
+ LEAVE
+ default:
+ r = Z_STREAM_ERROR;
+ LEAVE
+ }
+}
+
+
+int inflate_blocks_free(s, z)
+inflate_blocks_statef *s;
+z_streamp z;
+{
+ inflate_blocks_reset(s, z, Z_NULL);
+ ZFREE(z, s->window);
+ ZFREE(z, s->hufts);
+ ZFREE(z, s);
+ Tracev((stderr, "inflate: blocks freed\n"));
+ return Z_OK;
+}
+
+
+void inflate_set_dictionary(s, d, n)
+inflate_blocks_statef *s;
+const Bytef *d;
+uInt n;
+{
+ zmemcpy(s->window, d, n);
+ s->read = s->write = s->window + n;
+}
+
+
+/* Returns true if inflate is currently at the end of a block generated
+ * by Z_SYNC_FLUSH or Z_FULL_FLUSH.
+ * IN assertion: s != Z_NULL
+ */
+int inflate_blocks_sync_point(s)
+inflate_blocks_statef *s;
+{
+ return s->mode == LENS;
+}
diff --git a/zlib/infblock.h b/zlib/infblock.h
new file mode 100644
index 00000000..173b2267
--- /dev/null
+++ b/zlib/infblock.h
@@ -0,0 +1,39 @@
+/* infblock.h -- header to use infblock.c
+ * Copyright (C) 1995-2002 Mark Adler
+ * For conditions of distribution and use, see copyright notice in zlib.h
+ */
+
+/* WARNING: this file should *not* be used by applications. It is
+ part of the implementation of the compression library and is
+ subject to change. Applications should only use zlib.h.
+ */
+
+struct inflate_blocks_state;
+typedef struct inflate_blocks_state FAR inflate_blocks_statef;
+
+extern inflate_blocks_statef * inflate_blocks_new OF((
+ z_streamp z,
+ check_func c, /* check function */
+ uInt w)); /* window size */
+
+extern int inflate_blocks OF((
+ inflate_blocks_statef *,
+ z_streamp ,
+ int)); /* initial return code */
+
+extern void inflate_blocks_reset OF((
+ inflate_blocks_statef *,
+ z_streamp ,
+ uLongf *)); /* check value on output */
+
+extern int inflate_blocks_free OF((
+ inflate_blocks_statef *,
+ z_streamp));
+
+extern void inflate_set_dictionary OF((
+ inflate_blocks_statef *s,
+ const Bytef *d, /* dictionary */
+ uInt n)); /* dictionary length */
+
+extern int inflate_blocks_sync_point OF((
+ inflate_blocks_statef *s));
diff --git a/zlib/infcodes.c b/zlib/infcodes.c
new file mode 100644
index 00000000..9abe5412
--- /dev/null
+++ b/zlib/infcodes.c
@@ -0,0 +1,251 @@
+/* infcodes.c -- process literals and length/distance pairs
+ * Copyright (C) 1995-2002 Mark Adler
+ * For conditions of distribution and use, see copyright notice in zlib.h
+ */
+
+#include "zutil.h"
+#include "inftrees.h"
+#include "infblock.h"
+#include "infcodes.h"
+#include "infutil.h"
+#include "inffast.h"
+
+/* simplify the use of the inflate_huft type with some defines */
+#define exop word.what.Exop
+#define bits word.what.Bits
+
+typedef enum { /* waiting for "i:"=input, "o:"=output, "x:"=nothing */
+ START, /* x: set up for LEN */
+ LEN, /* i: get length/literal/eob next */
+ LENEXT, /* i: getting length extra (have base) */
+ DIST, /* i: get distance next */
+ DISTEXT, /* i: getting distance extra */
+ COPY, /* o: copying bytes in window, waiting for space */
+ LIT, /* o: got literal, waiting for output space */
+ WASH, /* o: got eob, possibly still output waiting */
+ END, /* x: got eob and all data flushed */
+ BADCODE} /* x: got error */
+inflate_codes_mode;
+
+/* inflate codes private state */
+struct inflate_codes_state {
+
+ /* mode */
+ inflate_codes_mode mode; /* current inflate_codes mode */
+
+ /* mode dependent information */
+ uInt len;
+ union {
+ struct {
+ inflate_huft *tree; /* pointer into tree */
+ uInt need; /* bits needed */
+ } code; /* if LEN or DIST, where in tree */
+ uInt lit; /* if LIT, literal */
+ struct {
+ uInt get; /* bits to get for extra */
+ uInt dist; /* distance back to copy from */
+ } copy; /* if EXT or COPY, where and how much */
+ } sub; /* submode */
+
+ /* mode independent information */
+ Byte lbits; /* ltree bits decoded per branch */
+ Byte dbits; /* dtree bits decoder per branch */
+ inflate_huft *ltree; /* literal/length/eob tree */
+ inflate_huft *dtree; /* distance tree */
+
+};
+
+
+inflate_codes_statef *inflate_codes_new(bl, bd, tl, td, z)
+uInt bl, bd;
+inflate_huft *tl;
+inflate_huft *td; /* need separate declaration for Borland C++ */
+z_streamp z;
+{
+ inflate_codes_statef *c;
+
+ if ((c = (inflate_codes_statef *)
+ ZALLOC(z,1,sizeof(struct inflate_codes_state))) != Z_NULL)
+ {
+ c->mode = START;
+ c->lbits = (Byte)bl;
+ c->dbits = (Byte)bd;
+ c->ltree = tl;
+ c->dtree = td;
+ Tracev((stderr, "inflate: codes new\n"));
+ }
+ return c;
+}
+
+
+int inflate_codes(s, z, r)
+inflate_blocks_statef *s;
+z_streamp z;
+int r;
+{
+ uInt j; /* temporary storage */
+ inflate_huft *t; /* temporary pointer */
+ uInt e; /* extra bits or operation */
+ uLong b; /* bit buffer */
+ uInt k; /* bits in bit buffer */
+ Bytef *p; /* input data pointer */
+ uInt n; /* bytes available there */
+ Bytef *q; /* output window write pointer */
+ uInt m; /* bytes to end of window or read pointer */
+ Bytef *f; /* pointer to copy strings from */
+ inflate_codes_statef *c = s->sub.decode.codes; /* codes state */
+
+ /* copy input/output information to locals (UPDATE macro restores) */
+ LOAD
+
+ /* process input and output based on current state */
+ while (1) switch (c->mode)
+ { /* waiting for "i:"=input, "o:"=output, "x:"=nothing */
+ case START: /* x: set up for LEN */
+#ifndef SLOW
+ if (m >= 258 && n >= 10)
+ {
+ UPDATE
+ r = inflate_fast(c->lbits, c->dbits, c->ltree, c->dtree, s, z);
+ LOAD
+ if (r != Z_OK)
+ {
+ c->mode = r == Z_STREAM_END ? WASH : BADCODE;
+ break;
+ }
+ }
+#endif /* !SLOW */
+ c->sub.code.need = c->lbits;
+ c->sub.code.tree = c->ltree;
+ c->mode = LEN;
+ case LEN: /* i: get length/literal/eob next */
+ j = c->sub.code.need;
+ NEEDBITS(j)
+ t = c->sub.code.tree + ((uInt)b & inflate_mask[j]);
+ DUMPBITS(t->bits)
+ e = (uInt)(t->exop);
+ if (e == 0) /* literal */
+ {
+ c->sub.lit = t->base;
+ Tracevv((stderr, t->base >= 0x20 && t->base < 0x7f ?
+ "inflate: literal '%c'\n" :
+ "inflate: literal 0x%02x\n", t->base));
+ c->mode = LIT;
+ break;
+ }
+ if (e & 16) /* length */
+ {
+ c->sub.copy.get = e & 15;
+ c->len = t->base;
+ c->mode = LENEXT;
+ break;
+ }
+ if ((e & 64) == 0) /* next table */
+ {
+ c->sub.code.need = e;
+ c->sub.code.tree = t + t->base;
+ break;
+ }
+ if (e & 32) /* end of block */
+ {
+ Tracevv((stderr, "inflate: end of block\n"));
+ c->mode = WASH;
+ break;
+ }
+ c->mode = BADCODE; /* invalid code */
+ z->msg = (char*)"invalid literal/length code";
+ r = Z_DATA_ERROR;
+ LEAVE
+ case LENEXT: /* i: getting length extra (have base) */
+ j = c->sub.copy.get;
+ NEEDBITS(j)
+ c->len += (uInt)b & inflate_mask[j];
+ DUMPBITS(j)
+ c->sub.code.need = c->dbits;
+ c->sub.code.tree = c->dtree;
+ Tracevv((stderr, "inflate: length %u\n", c->len));
+ c->mode = DIST;
+ case DIST: /* i: get distance next */
+ j = c->sub.code.need;
+ NEEDBITS(j)
+ t = c->sub.code.tree + ((uInt)b & inflate_mask[j]);
+ DUMPBITS(t->bits)
+ e = (uInt)(t->exop);
+ if (e & 16) /* distance */
+ {
+ c->sub.copy.get = e & 15;
+ c->sub.copy.dist = t->base;
+ c->mode = DISTEXT;
+ break;
+ }
+ if ((e & 64) == 0) /* next table */
+ {
+ c->sub.code.need = e;
+ c->sub.code.tree = t + t->base;
+ break;
+ }
+ c->mode = BADCODE; /* invalid code */
+ z->msg = (char*)"invalid distance code";
+ r = Z_DATA_ERROR;
+ LEAVE
+ case DISTEXT: /* i: getting distance extra */
+ j = c->sub.copy.get;
+ NEEDBITS(j)
+ c->sub.copy.dist += (uInt)b & inflate_mask[j];
+ DUMPBITS(j)
+ Tracevv((stderr, "inflate: distance %u\n", c->sub.copy.dist));
+ c->mode = COPY;
+ case COPY: /* o: copying bytes in window, waiting for space */
+ f = q - c->sub.copy.dist;
+ while (f < s->window) /* modulo window size-"while" instead */
+ f += s->end - s->window; /* of "if" handles invalid distances */
+ while (c->len)
+ {
+ NEEDOUT
+ OUTBYTE(*f++)
+ if (f == s->end)
+ f = s->window;
+ c->len--;
+ }
+ c->mode = START;
+ break;
+ case LIT: /* o: got literal, waiting for output space */
+ NEEDOUT
+ OUTBYTE(c->sub.lit)
+ c->mode = START;
+ break;
+ case WASH: /* o: got eob, possibly more output */
+ if (k > 7) /* return unused byte, if any */
+ {
+ Assert(k < 16, "inflate_codes grabbed too many bytes")
+ k -= 8;
+ n++;
+ p--; /* can always return one */
+ }
+ FLUSH
+ if (s->read != s->write)
+ LEAVE
+ c->mode = END;
+ case END:
+ r = Z_STREAM_END;
+ LEAVE
+ case BADCODE: /* x: got error */
+ r = Z_DATA_ERROR;
+ LEAVE
+ default:
+ r = Z_STREAM_ERROR;
+ LEAVE
+ }
+#ifdef NEED_DUMMY_RETURN
+ return Z_STREAM_ERROR; /* Some dumb compilers complain without this */
+#endif
+}
+
+
+void inflate_codes_free(c, z)
+inflate_codes_statef *c;
+z_streamp z;
+{
+ ZFREE(z, c);
+ Tracev((stderr, "inflate: codes free\n"));
+}
diff --git a/zlib/infcodes.h b/zlib/infcodes.h
new file mode 100644
index 00000000..46821a02
--- /dev/null
+++ b/zlib/infcodes.h
@@ -0,0 +1,27 @@
+/* infcodes.h -- header to use infcodes.c
+ * Copyright (C) 1995-2002 Mark Adler
+ * For conditions of distribution and use, see copyright notice in zlib.h
+ */
+
+/* WARNING: this file should *not* be used by applications. It is
+ part of the implementation of the compression library and is
+ subject to change. Applications should only use zlib.h.
+ */
+
+struct inflate_codes_state;
+typedef struct inflate_codes_state FAR inflate_codes_statef;
+
+extern inflate_codes_statef *inflate_codes_new OF((
+ uInt, uInt,
+ inflate_huft *, inflate_huft *,
+ z_streamp ));
+
+extern int inflate_codes OF((
+ inflate_blocks_statef *,
+ z_streamp ,
+ int));
+
+extern void inflate_codes_free OF((
+ inflate_codes_statef *,
+ z_streamp ));
+
diff --git a/zlib/inffast.c b/zlib/inffast.c
new file mode 100644
index 00000000..aa7f1d4d
--- /dev/null
+++ b/zlib/inffast.c
@@ -0,0 +1,183 @@
+/* inffast.c -- process literals and length/distance pairs fast
+ * Copyright (C) 1995-2002 Mark Adler
+ * For conditions of distribution and use, see copyright notice in zlib.h
+ */
+
+#include "zutil.h"
+#include "inftrees.h"
+#include "infblock.h"
+#include "infcodes.h"
+#include "infutil.h"
+#include "inffast.h"
+
+struct inflate_codes_state {int dummy;}; /* for buggy compilers */
+
+/* simplify the use of the inflate_huft type with some defines */
+#define exop word.what.Exop
+#define bits word.what.Bits
+
+/* macros for bit input with no checking and for returning unused bytes */
+#define GRABBITS(j) {while(k<(j)){b|=((uLong)NEXTBYTE)<<k;k+=8;}}
+#define UNGRAB {c=z->avail_in-n;c=(k>>3)<c?k>>3:c;n+=c;p-=c;k-=c<<3;}
+
+/* Called with number of bytes left to write in window at least 258
+ (the maximum string length) and number of input bytes available
+ at least ten. The ten bytes are six bytes for the longest length/
+ distance pair plus four bytes for overloading the bit buffer. */
+
+int inflate_fast(bl, bd, tl, td, s, z)
+uInt bl, bd;
+inflate_huft *tl;
+inflate_huft *td; /* need separate declaration for Borland C++ */
+inflate_blocks_statef *s;
+z_streamp z;
+{
+ inflate_huft *t; /* temporary pointer */
+ uInt e; /* extra bits or operation */
+ uLong b; /* bit buffer */
+ uInt k; /* bits in bit buffer */
+ Bytef *p; /* input data pointer */
+ uInt n; /* bytes available there */
+ Bytef *q; /* output window write pointer */
+ uInt m; /* bytes to end of window or read pointer */
+ uInt ml; /* mask for literal/length tree */
+ uInt md; /* mask for distance tree */
+ uInt c; /* bytes to copy */
+ uInt d; /* distance back to copy from */
+ Bytef *r; /* copy source pointer */
+
+ /* load input, output, bit values */
+ LOAD
+
+ /* initialize masks */
+ ml = inflate_mask[bl];
+ md = inflate_mask[bd];
+
+ /* do until not enough input or output space for fast loop */
+ do { /* assume called with m >= 258 && n >= 10 */
+ /* get literal/length code */
+ GRABBITS(20) /* max bits for literal/length code */
+ if ((e = (t = tl + ((uInt)b & ml))->exop) == 0)
+ {
+ DUMPBITS(t->bits)
+ Tracevv((stderr, t->base >= 0x20 && t->base < 0x7f ?
+ "inflate: * literal '%c'\n" :
+ "inflate: * literal 0x%02x\n", t->base));
+ *q++ = (Byte)t->base;
+ m--;
+ continue;
+ }
+ do {
+ DUMPBITS(t->bits)
+ if (e & 16)
+ {
+ /* get extra bits for length */
+ e &= 15;
+ c = t->base + ((uInt)b & inflate_mask[e]);
+ DUMPBITS(e)
+ Tracevv((stderr, "inflate: * length %u\n", c));
+
+ /* decode distance base of block to copy */
+ GRABBITS(15); /* max bits for distance code */
+ e = (t = td + ((uInt)b & md))->exop;
+ do {
+ DUMPBITS(t->bits)
+ if (e & 16)
+ {
+ /* get extra bits to add to distance base */
+ e &= 15;
+ GRABBITS(e) /* get extra bits (up to 13) */
+ d = t->base + ((uInt)b & inflate_mask[e]);
+ DUMPBITS(e)
+ Tracevv((stderr, "inflate: * distance %u\n", d));
+
+ /* do the copy */
+ m -= c;
+ r = q - d;
+ if (r < s->window) /* wrap if needed */
+ {
+ do {
+ r += s->end - s->window; /* force pointer in window */
+ } while (r < s->window); /* covers invalid distances */
+ e = s->end - r;
+ if (c > e)
+ {
+ c -= e; /* wrapped copy */
+ do {
+ *q++ = *r++;
+ } while (--e);
+ r = s->window;
+ do {
+ *q++ = *r++;
+ } while (--c);
+ }
+ else /* normal copy */
+ {
+ *q++ = *r++; c--;
+ *q++ = *r++; c--;
+ do {
+ *q++ = *r++;
+ } while (--c);
+ }
+ }
+ else /* normal copy */
+ {
+ *q++ = *r++; c--;
+ *q++ = *r++; c--;
+ do {
+ *q++ = *r++;
+ } while (--c);
+ }
+ break;
+ }
+ else if ((e & 64) == 0)
+ {
+ t += t->base;
+ e = (t += ((uInt)b & inflate_mask[e]))->exop;
+ }
+ else
+ {
+ z->msg = (char*)"invalid distance code";
+ UNGRAB
+ UPDATE
+ return Z_DATA_ERROR;
+ }
+ } while (1);
+ break;
+ }
+ if ((e & 64) == 0)
+ {
+ t += t->base;
+ if ((e = (t += ((uInt)b & inflate_mask[e]))->exop) == 0)
+ {
+ DUMPBITS(t->bits)
+ Tracevv((stderr, t->base >= 0x20 && t->base < 0x7f ?
+ "inflate: * literal '%c'\n" :
+ "inflate: * literal 0x%02x\n", t->base));
+ *q++ = (Byte)t->base;
+ m--;
+ break;
+ }
+ }
+ else if (e & 32)
+ {
+ Tracevv((stderr, "inflate: * end of block\n"));
+ UNGRAB
+ UPDATE
+ return Z_STREAM_END;
+ }
+ else
+ {
+ z->msg = (char*)"invalid literal/length code";
+ UNGRAB
+ UPDATE
+ return Z_DATA_ERROR;
+ }
+ } while (1);
+ } while (m >= 258 && n >= 10);
+
+ /* not enough input or output--restore pointers and return */
+ UNGRAB
+ UPDATE
+ return Z_OK;
+}
diff --git a/zlib/inffast.h b/zlib/inffast.h
new file mode 100644
index 00000000..a31a4bbb
--- /dev/null
+++ b/zlib/inffast.h
@@ -0,0 +1,17 @@
+/* inffast.h -- header to use inffast.c
+ * Copyright (C) 1995-2002 Mark Adler
+ * For conditions of distribution and use, see copyright notice in zlib.h
+ */
+
+/* WARNING: this file should *not* be used by applications. It is
+ part of the implementation of the compression library and is
+ subject to change. Applications should only use zlib.h.
+ */
+
+extern int inflate_fast OF((
+ uInt,
+ uInt,
+ inflate_huft *,
+ inflate_huft *,
+ inflate_blocks_statef *,
+ z_streamp ));
diff --git a/zlib/inffixed.h b/zlib/inffixed.h
new file mode 100644
index 00000000..77f7e763
--- /dev/null
+++ b/zlib/inffixed.h
@@ -0,0 +1,151 @@
+/* inffixed.h -- table for decoding fixed codes
+ * Generated automatically by the maketree.c program
+ */
+
+/* WARNING: this file should *not* be used by applications. It is
+ part of the implementation of the compression library and is
+ subject to change. Applications should only use zlib.h.
+ */
+
+local uInt fixed_bl = 9;
+local uInt fixed_bd = 5;
+local inflate_huft fixed_tl[] = {
+ {{{96,7}},256}, {{{0,8}},80}, {{{0,8}},16}, {{{84,8}},115},
+ {{{82,7}},31}, {{{0,8}},112}, {{{0,8}},48}, {{{0,9}},192},
+ {{{80,7}},10}, {{{0,8}},96}, {{{0,8}},32}, {{{0,9}},160},
+ {{{0,8}},0}, {{{0,8}},128}, {{{0,8}},64}, {{{0,9}},224},
+ {{{80,7}},6}, {{{0,8}},88}, {{{0,8}},24}, {{{0,9}},144},
+ {{{83,7}},59}, {{{0,8}},120}, {{{0,8}},56}, {{{0,9}},208},
+ {{{81,7}},17}, {{{0,8}},104}, {{{0,8}},40}, {{{0,9}},176},
+ {{{0,8}},8}, {{{0,8}},136}, {{{0,8}},72}, {{{0,9}},240},
+ {{{80,7}},4}, {{{0,8}},84}, {{{0,8}},20}, {{{85,8}},227},
+ {{{83,7}},43}, {{{0,8}},116}, {{{0,8}},52}, {{{0,9}},200},
+ {{{81,7}},13}, {{{0,8}},100}, {{{0,8}},36}, {{{0,9}},168},
+ {{{0,8}},4}, {{{0,8}},132}, {{{0,8}},68}, {{{0,9}},232},
+ {{{80,7}},8}, {{{0,8}},92}, {{{0,8}},28}, {{{0,9}},152},
+ {{{84,7}},83}, {{{0,8}},124}, {{{0,8}},60}, {{{0,9}},216},
+ {{{82,7}},23}, {{{0,8}},108}, {{{0,8}},44}, {{{0,9}},184},
+ {{{0,8}},12}, {{{0,8}},140}, {{{0,8}},76}, {{{0,9}},248},
+ {{{80,7}},3}, {{{0,8}},82}, {{{0,8}},18}, {{{85,8}},163},
+ {{{83,7}},35}, {{{0,8}},114}, {{{0,8}},50}, {{{0,9}},196},
+ {{{81,7}},11}, {{{0,8}},98}, {{{0,8}},34}, {{{0,9}},164},
+ {{{0,8}},2}, {{{0,8}},130}, {{{0,8}},66}, {{{0,9}},228},
+ {{{80,7}},7}, {{{0,8}},90}, {{{0,8}},26}, {{{0,9}},148},
+ {{{84,7}},67}, {{{0,8}},122}, {{{0,8}},58}, {{{0,9}},212},
+ {{{82,7}},19}, {{{0,8}},106}, {{{0,8}},42}, {{{0,9}},180},
+ {{{0,8}},10}, {{{0,8}},138}, {{{0,8}},74}, {{{0,9}},244},
+ {{{80,7}},5}, {{{0,8}},86}, {{{0,8}},22}, {{{192,8}},0},
+ {{{83,7}},51}, {{{0,8}},118}, {{{0,8}},54}, {{{0,9}},204},
+ {{{81,7}},15}, {{{0,8}},102}, {{{0,8}},38}, {{{0,9}},172},
+ {{{0,8}},6}, {{{0,8}},134}, {{{0,8}},70}, {{{0,9}},236},
+ {{{80,7}},9}, {{{0,8}},94}, {{{0,8}},30}, {{{0,9}},156},
+ {{{84,7}},99}, {{{0,8}},126}, {{{0,8}},62}, {{{0,9}},220},
+ {{{82,7}},27}, {{{0,8}},110}, {{{0,8}},46}, {{{0,9}},188},
+ {{{0,8}},14}, {{{0,8}},142}, {{{0,8}},78}, {{{0,9}},252},
+ {{{96,7}},256}, {{{0,8}},81}, {{{0,8}},17}, {{{85,8}},131},
+ {{{82,7}},31}, {{{0,8}},113}, {{{0,8}},49}, {{{0,9}},194},
+ {{{80,7}},10}, {{{0,8}},97}, {{{0,8}},33}, {{{0,9}},162},
+ {{{0,8}},1}, {{{0,8}},129}, {{{0,8}},65}, {{{0,9}},226},
+ {{{80,7}},6}, {{{0,8}},89}, {{{0,8}},25}, {{{0,9}},146},
+ {{{83,7}},59}, {{{0,8}},121}, {{{0,8}},57}, {{{0,9}},210},
+ {{{81,7}},17}, {{{0,8}},105}, {{{0,8}},41}, {{{0,9}},178},
+ {{{0,8}},9}, {{{0,8}},137}, {{{0,8}},73}, {{{0,9}},242},
+ {{{80,7}},4}, {{{0,8}},85}, {{{0,8}},21}, {{{80,8}},258},
+ {{{83,7}},43}, {{{0,8}},117}, {{{0,8}},53}, {{{0,9}},202},
+ {{{81,7}},13}, {{{0,8}},101}, {{{0,8}},37}, {{{0,9}},170},
+ {{{0,8}},5}, {{{0,8}},133}, {{{0,8}},69}, {{{0,9}},234},
+ {{{80,7}},8}, {{{0,8}},93}, {{{0,8}},29}, {{{0,9}},154},
+ {{{84,7}},83}, {{{0,8}},125}, {{{0,8}},61}, {{{0,9}},218},
+ {{{82,7}},23}, {{{0,8}},109}, {{{0,8}},45}, {{{0,9}},186},
+ {{{0,8}},13}, {{{0,8}},141}, {{{0,8}},77}, {{{0,9}},250},
+ {{{80,7}},3}, {{{0,8}},83}, {{{0,8}},19}, {{{85,8}},195},
+ {{{83,7}},35}, {{{0,8}},115}, {{{0,8}},51}, {{{0,9}},198},
+ {{{81,7}},11}, {{{0,8}},99}, {{{0,8}},35}, {{{0,9}},166},
+ {{{0,8}},3}, {{{0,8}},131}, {{{0,8}},67}, {{{0,9}},230},
+ {{{80,7}},7}, {{{0,8}},91}, {{{0,8}},27}, {{{0,9}},150},
+ {{{84,7}},67}, {{{0,8}},123}, {{{0,8}},59}, {{{0,9}},214},
+ {{{82,7}},19}, {{{0,8}},107}, {{{0,8}},43}, {{{0,9}},182},
+ {{{0,8}},11}, {{{0,8}},139}, {{{0,8}},75}, {{{0,9}},246},
+ {{{80,7}},5}, {{{0,8}},87}, {{{0,8}},23}, {{{192,8}},0},
+ {{{83,7}},51}, {{{0,8}},119}, {{{0,8}},55}, {{{0,9}},206},
+ {{{81,7}},15}, {{{0,8}},103}, {{{0,8}},39}, {{{0,9}},174},
+ {{{0,8}},7}, {{{0,8}},135}, {{{0,8}},71}, {{{0,9}},238},
+ {{{80,7}},9}, {{{0,8}},95}, {{{0,8}},31}, {{{0,9}},158},
+ {{{84,7}},99}, {{{0,8}},127}, {{{0,8}},63}, {{{0,9}},222},
+ {{{82,7}},27}, {{{0,8}},111}, {{{0,8}},47}, {{{0,9}},190},
+ {{{0,8}},15}, {{{0,8}},143}, {{{0,8}},79}, {{{0,9}},254},
+ {{{96,7}},256}, {{{0,8}},80}, {{{0,8}},16}, {{{84,8}},115},
+ {{{82,7}},31}, {{{0,8}},112}, {{{0,8}},48}, {{{0,9}},193},
+ {{{80,7}},10}, {{{0,8}},96}, {{{0,8}},32}, {{{0,9}},161},
+ {{{0,8}},0}, {{{0,8}},128}, {{{0,8}},64}, {{{0,9}},225},
+ {{{80,7}},6}, {{{0,8}},88}, {{{0,8}},24}, {{{0,9}},145},
+ {{{83,7}},59}, {{{0,8}},120}, {{{0,8}},56}, {{{0,9}},209},
+ {{{81,7}},17}, {{{0,8}},104}, {{{0,8}},40}, {{{0,9}},177},
+ {{{0,8}},8}, {{{0,8}},136}, {{{0,8}},72}, {{{0,9}},241},
+ {{{80,7}},4}, {{{0,8}},84}, {{{0,8}},20}, {{{85,8}},227},
+ {{{83,7}},43}, {{{0,8}},116}, {{{0,8}},52}, {{{0,9}},201},
+ {{{81,7}},13}, {{{0,8}},100}, {{{0,8}},36}, {{{0,9}},169},
+ {{{0,8}},4}, {{{0,8}},132}, {{{0,8}},68}, {{{0,9}},233},
+ {{{80,7}},8}, {{{0,8}},92}, {{{0,8}},28}, {{{0,9}},153},
+ {{{84,7}},83}, {{{0,8}},124}, {{{0,8}},60}, {{{0,9}},217},
+ {{{82,7}},23}, {{{0,8}},108}, {{{0,8}},44}, {{{0,9}},185},
+ {{{0,8}},12}, {{{0,8}},140}, {{{0,8}},76}, {{{0,9}},249},
+ {{{80,7}},3}, {{{0,8}},82}, {{{0,8}},18}, {{{85,8}},163},
+ {{{83,7}},35}, {{{0,8}},114}, {{{0,8}},50}, {{{0,9}},197},
+ {{{81,7}},11}, {{{0,8}},98}, {{{0,8}},34}, {{{0,9}},165},
+ {{{0,8}},2}, {{{0,8}},130}, {{{0,8}},66}, {{{0,9}},229},
+ {{{80,7}},7}, {{{0,8}},90}, {{{0,8}},26}, {{{0,9}},149},
+ {{{84,7}},67}, {{{0,8}},122}, {{{0,8}},58}, {{{0,9}},213},
+ {{{82,7}},19}, {{{0,8}},106}, {{{0,8}},42}, {{{0,9}},181},
+ {{{0,8}},10}, {{{0,8}},138}, {{{0,8}},74}, {{{0,9}},245},
+ {{{80,7}},5}, {{{0,8}},86}, {{{0,8}},22}, {{{192,8}},0},
+ {{{83,7}},51}, {{{0,8}},118}, {{{0,8}},54}, {{{0,9}},205},
+ {{{81,7}},15}, {{{0,8}},102}, {{{0,8}},38}, {{{0,9}},173},
+ {{{0,8}},6}, {{{0,8}},134}, {{{0,8}},70}, {{{0,9}},237},
+ {{{80,7}},9}, {{{0,8}},94}, {{{0,8}},30}, {{{0,9}},157},
+ {{{84,7}},99}, {{{0,8}},126}, {{{0,8}},62}, {{{0,9}},221},
+ {{{82,7}},27}, {{{0,8}},110}, {{{0,8}},46}, {{{0,9}},189},
+ {{{0,8}},14}, {{{0,8}},142}, {{{0,8}},78}, {{{0,9}},253},
+ {{{96,7}},256}, {{{0,8}},81}, {{{0,8}},17}, {{{85,8}},131},
+ {{{82,7}},31}, {{{0,8}},113}, {{{0,8}},49}, {{{0,9}},195},
+ {{{80,7}},10}, {{{0,8}},97}, {{{0,8}},33}, {{{0,9}},163},
+ {{{0,8}},1}, {{{0,8}},129}, {{{0,8}},65}, {{{0,9}},227},
+ {{{80,7}},6}, {{{0,8}},89}, {{{0,8}},25}, {{{0,9}},147},
+ {{{83,7}},59}, {{{0,8}},121}, {{{0,8}},57}, {{{0,9}},211},
+ {{{81,7}},17}, {{{0,8}},105}, {{{0,8}},41}, {{{0,9}},179},
+ {{{0,8}},9}, {{{0,8}},137}, {{{0,8}},73}, {{{0,9}},243},
+ {{{80,7}},4}, {{{0,8}},85}, {{{0,8}},21}, {{{80,8}},258},
+ {{{83,7}},43}, {{{0,8}},117}, {{{0,8}},53}, {{{0,9}},203},
+ {{{81,7}},13}, {{{0,8}},101}, {{{0,8}},37}, {{{0,9}},171},
+ {{{0,8}},5}, {{{0,8}},133}, {{{0,8}},69}, {{{0,9}},235},
+ {{{80,7}},8}, {{{0,8}},93}, {{{0,8}},29}, {{{0,9}},155},
+ {{{84,7}},83}, {{{0,8}},125}, {{{0,8}},61}, {{{0,9}},219},
+ {{{82,7}},23}, {{{0,8}},109}, {{{0,8}},45}, {{{0,9}},187},
+ {{{0,8}},13}, {{{0,8}},141}, {{{0,8}},77}, {{{0,9}},251},
+ {{{80,7}},3}, {{{0,8}},83}, {{{0,8}},19}, {{{85,8}},195},
+ {{{83,7}},35}, {{{0,8}},115}, {{{0,8}},51}, {{{0,9}},199},
+ {{{81,7}},11}, {{{0,8}},99}, {{{0,8}},35}, {{{0,9}},167},
+ {{{0,8}},3}, {{{0,8}},131}, {{{0,8}},67}, {{{0,9}},231},
+ {{{80,7}},7}, {{{0,8}},91}, {{{0,8}},27}, {{{0,9}},151},
+ {{{84,7}},67}, {{{0,8}},123}, {{{0,8}},59}, {{{0,9}},215},
+ {{{82,7}},19}, {{{0,8}},107}, {{{0,8}},43}, {{{0,9}},183},
+ {{{0,8}},11}, {{{0,8}},139}, {{{0,8}},75}, {{{0,9}},247},
+ {{{80,7}},5}, {{{0,8}},87}, {{{0,8}},23}, {{{192,8}},0},
+ {{{83,7}},51}, {{{0,8}},119}, {{{0,8}},55}, {{{0,9}},207},
+ {{{81,7}},15}, {{{0,8}},103}, {{{0,8}},39}, {{{0,9}},175},
+ {{{0,8}},7}, {{{0,8}},135}, {{{0,8}},71}, {{{0,9}},239},
+ {{{80,7}},9}, {{{0,8}},95}, {{{0,8}},31}, {{{0,9}},159},
+ {{{84,7}},99}, {{{0,8}},127}, {{{0,8}},63}, {{{0,9}},223},
+ {{{82,7}},27}, {{{0,8}},111}, {{{0,8}},47}, {{{0,9}},191},
+ {{{0,8}},15}, {{{0,8}},143}, {{{0,8}},79}, {{{0,9}},255}
+ };
+local inflate_huft fixed_td[] = {
+ {{{80,5}},1}, {{{87,5}},257}, {{{83,5}},17}, {{{91,5}},4097},
+ {{{81,5}},5}, {{{89,5}},1025}, {{{85,5}},65}, {{{93,5}},16385},
+ {{{80,5}},3}, {{{88,5}},513}, {{{84,5}},33}, {{{92,5}},8193},
+ {{{82,5}},9}, {{{90,5}},2049}, {{{86,5}},129}, {{{192,5}},24577},
+ {{{80,5}},2}, {{{87,5}},385}, {{{83,5}},25}, {{{91,5}},6145},
+ {{{81,5}},7}, {{{89,5}},1537}, {{{85,5}},97}, {{{93,5}},24577},
+ {{{80,5}},4}, {{{88,5}},769}, {{{84,5}},49}, {{{92,5}},12289},
+ {{{82,5}},13}, {{{90,5}},3073}, {{{86,5}},193}, {{{192,5}},24577}
+ };
diff --git a/zlib/inflate.c b/zlib/inflate.c
new file mode 100644
index 00000000..dfb2e867
--- /dev/null
+++ b/zlib/inflate.c
@@ -0,0 +1,366 @@
+/* inflate.c -- zlib interface to inflate modules
+ * Copyright (C) 1995-2002 Mark Adler
+ * For conditions of distribution and use, see copyright notice in zlib.h
+ */
+
+#include "zutil.h"
+#include "infblock.h"
+
+struct inflate_blocks_state {int dummy;}; /* for buggy compilers */
+
+typedef enum {
+ METHOD, /* waiting for method byte */
+ FLAG, /* waiting for flag byte */
+ DICT4, /* four dictionary check bytes to go */
+ DICT3, /* three dictionary check bytes to go */
+ DICT2, /* two dictionary check bytes to go */
+ DICT1, /* one dictionary check byte to go */
+ DICT0, /* waiting for inflateSetDictionary */
+ BLOCKS, /* decompressing blocks */
+ CHECK4, /* four check bytes to go */
+ CHECK3, /* three check bytes to go */
+ CHECK2, /* two check bytes to go */
+ CHECK1, /* one check byte to go */
+ DONE, /* finished check, done */
+ BAD} /* got an error--stay here */
+inflate_mode;
+
+/* inflate private state */
+struct internal_state {
+
+ /* mode */
+ inflate_mode mode; /* current inflate mode */
+
+ /* mode dependent information */
+ union {
+ uInt method; /* if FLAGS, method byte */
+ struct {
+ uLong was; /* computed check value */
+ uLong need; /* stream check value */
+ } check; /* if CHECK, check values to compare */
+ uInt marker; /* if BAD, inflateSync's marker bytes count */
+ } sub; /* submode */
+
+ /* mode independent information */
+ int nowrap; /* flag for no wrapper */
+ uInt wbits; /* log2(window size) (8..15, defaults to 15) */
+ inflate_blocks_statef
+ *blocks; /* current inflate_blocks state */
+
+};
+
+
+int ZEXPORT inflateReset(z)
+z_streamp z;
+{
+ if (z == Z_NULL || z->state == Z_NULL)
+ return Z_STREAM_ERROR;
+ z->total_in = z->total_out = 0;
+ z->msg = Z_NULL;
+ z->state->mode = z->state->nowrap ? BLOCKS : METHOD;
+ inflate_blocks_reset(z->state->blocks, z, Z_NULL);
+ Tracev((stderr, "inflate: reset\n"));
+ return Z_OK;
+}
+
+
+int ZEXPORT inflateEnd(z)
+z_streamp z;
+{
+ if (z == Z_NULL || z->state == Z_NULL || z->zfree == Z_NULL)
+ return Z_STREAM_ERROR;
+ if (z->state->blocks != Z_NULL)
+ inflate_blocks_free(z->state->blocks, z);
+ ZFREE(z, z->state);
+ z->state = Z_NULL;
+ Tracev((stderr, "inflate: end\n"));
+ return Z_OK;
+}
+
+
+int ZEXPORT inflateInit2_(z, w, version, stream_size)
+z_streamp z;
+int w;
+const char *version;
+int stream_size;
+{
+ if (version == Z_NULL || version[0] != ZLIB_VERSION[0] ||
+ stream_size != sizeof(z_stream))
+ return Z_VERSION_ERROR;
+
+ /* initialize state */
+ if (z == Z_NULL)
+ return Z_STREAM_ERROR;
+ z->msg = Z_NULL;
+ if (z->zalloc == Z_NULL)
+ {
+ z->zalloc = zcalloc;
+ z->opaque = (voidpf)0;
+ }
+ if (z->zfree == Z_NULL) z->zfree = zcfree;
+ if ((z->state = (struct internal_state FAR *)
+ ZALLOC(z,1,sizeof(struct internal_state))) == Z_NULL)
+ return Z_MEM_ERROR;
+ z->state->blocks = Z_NULL;
+
+ /* handle undocumented nowrap option (no zlib header or check) */
+ z->state->nowrap = 0;
+ if (w < 0)
+ {
+ w = - w;
+ z->state->nowrap = 1;
+ }
+
+ /* set window size */
+ if (w < 8 || w > 15)
+ {
+ inflateEnd(z);
+ return Z_STREAM_ERROR;
+ }
+ z->state->wbits = (uInt)w;
+
+ /* create inflate_blocks state */
+ if ((z->state->blocks =
+ inflate_blocks_new(z, z->state->nowrap ? Z_NULL : adler32, (uInt)1 << w))
+ == Z_NULL)
+ {
+ inflateEnd(z);
+ return Z_MEM_ERROR;
+ }
+ Tracev((stderr, "inflate: allocated\n"));
+
+ /* reset state */
+ inflateReset(z);
+ return Z_OK;
+}
+
+
+int ZEXPORT inflateInit_(z, version, stream_size)
+z_streamp z;
+const char *version;
+int stream_size;
+{
+ return inflateInit2_(z, DEF_WBITS, version, stream_size);
+}
+
+
+#define NEEDBYTE {if(z->avail_in==0)return r;r=f;}
+#define NEXTBYTE (z->avail_in--,z->total_in++,*z->next_in++)
+
+int ZEXPORT inflate(z, f)
+z_streamp z;
+int f;
+{
+ int r;
+ uInt b;
+
+ if (z == Z_NULL || z->state == Z_NULL || z->next_in == Z_NULL)
+ return Z_STREAM_ERROR;
+ f = f == Z_FINISH ? Z_BUF_ERROR : Z_OK;
+ r = Z_BUF_ERROR;
+ while (1) switch (z->state->mode)
+ {
+ case METHOD:
+ NEEDBYTE
+ if (((z->state->sub.method = NEXTBYTE) & 0xf) != Z_DEFLATED)
+ {
+ z->state->mode = BAD;
+ z->msg = (char*)"unknown compression method";
+ z->state->sub.marker = 5; /* can't try inflateSync */
+ break;
+ }
+ if ((z->state->sub.method >> 4) + 8 > z->state->wbits)
+ {
+ z->state->mode = BAD;
+ z->msg = (char*)"invalid window size";
+ z->state->sub.marker = 5; /* can't try inflateSync */
+ break;
+ }
+ z->state->mode = FLAG;
+ case FLAG:
+ NEEDBYTE
+ b = NEXTBYTE;
+ if (((z->state->sub.method << 8) + b) % 31)
+ {
+ z->state->mode = BAD;
+ z->msg = (char*)"incorrect header check";
+ z->state->sub.marker = 5; /* can't try inflateSync */
+ break;
+ }
+ Tracev((stderr, "inflate: zlib header ok\n"));
+ if (!(b & PRESET_DICT))
+ {
+ z->state->mode = BLOCKS;
+ break;
+ }
+ z->state->mode = DICT4;
+ case DICT4:
+ NEEDBYTE
+ z->state->sub.check.need = (uLong)NEXTBYTE << 24;
+ z->state->mode = DICT3;
+ case DICT3:
+ NEEDBYTE
+ z->state->sub.check.need += (uLong)NEXTBYTE << 16;
+ z->state->mode = DICT2;
+ case DICT2:
+ NEEDBYTE
+ z->state->sub.check.need += (uLong)NEXTBYTE << 8;
+ z->state->mode = DICT1;
+ case DICT1:
+ NEEDBYTE
+ z->state->sub.check.need += (uLong)NEXTBYTE;
+ z->adler = z->state->sub.check.need;
+ z->state->mode = DICT0;
+ return Z_NEED_DICT;
+ case DICT0:
+ z->state->mode = BAD;
+ z->msg = (char*)"need dictionary";
+ z->state->sub.marker = 0; /* can try inflateSync */
+ return Z_STREAM_ERROR;
+ case BLOCKS:
+ r = inflate_blocks(z->state->blocks, z, r);
+ if (r == Z_DATA_ERROR)
+ {
+ z->state->mode = BAD;
+ z->state->sub.marker = 0; /* can try inflateSync */
+ break;
+ }
+ if (r == Z_OK)
+ r = f;
+ if (r != Z_STREAM_END)
+ return r;
+ r = f;
+ inflate_blocks_reset(z->state->blocks, z, &z->state->sub.check.was);
+ if (z->state->nowrap)
+ {
+ z->state->mode = DONE;
+ break;
+ }
+ z->state->mode = CHECK4;
+ case CHECK4:
+ NEEDBYTE
+ z->state->sub.check.need = (uLong)NEXTBYTE << 24;
+ z->state->mode = CHECK3;
+ case CHECK3:
+ NEEDBYTE
+ z->state->sub.check.need += (uLong)NEXTBYTE << 16;
+ z->state->mode = CHECK2;
+ case CHECK2:
+ NEEDBYTE
+ z->state->sub.check.need += (uLong)NEXTBYTE << 8;
+ z->state->mode = CHECK1;
+ case CHECK1:
+ NEEDBYTE
+ z->state->sub.check.need += (uLong)NEXTBYTE;
+
+ if (z->state->sub.check.was != z->state->sub.check.need)
+ {
+ z->state->mode = BAD;
+ z->msg = (char*)"incorrect data check";
+ z->state->sub.marker = 5; /* can't try inflateSync */
+ break;
+ }
+ Tracev((stderr, "inflate: zlib check ok\n"));
+ z->state->mode = DONE;
+ case DONE:
+ return Z_STREAM_END;
+ case BAD:
+ return Z_DATA_ERROR;
+ default:
+ return Z_STREAM_ERROR;
+ }
+#ifdef NEED_DUMMY_RETURN
+ return Z_STREAM_ERROR; /* Some dumb compilers complain without this */
+#endif
+}
+
+
+int ZEXPORT inflateSetDictionary(z, dictionary, dictLength)
+z_streamp z;
+const Bytef *dictionary;
+uInt dictLength;
+{
+ uInt length = dictLength;
+
+ if (z == Z_NULL || z->state == Z_NULL || z->state->mode != DICT0)
+ return Z_STREAM_ERROR;
+
+ if (adler32(1L, dictionary, dictLength) != z->adler) return Z_DATA_ERROR;
+ z->adler = 1L;
+
+ if (length >= ((uInt)1<<z->state->wbits))
+ {
+ length = (1<<z->state->wbits)-1;
+ dictionary += dictLength - length;
+ }
+ inflate_set_dictionary(z->state->blocks, dictionary, length);
+ z->state->mode = BLOCKS;
+ return Z_OK;
+}
+
+
+int ZEXPORT inflateSync(z)
+z_streamp z;
+{
+ uInt n; /* number of bytes to look at */
+ Bytef *p; /* pointer to bytes */
+ uInt m; /* number of marker bytes found in a row */
+ uLong r, w; /* temporaries to save total_in and total_out */
+
+ /* set up */
+ if (z == Z_NULL || z->state == Z_NULL)
+ return Z_STREAM_ERROR;
+ if (z->state->mode != BAD)
+ {
+ z->state->mode = BAD;
+ z->state->sub.marker = 0;
+ }
+ if ((n = z->avail_in) == 0)
+ return Z_BUF_ERROR;
+ p = z->next_in;
+ m = z->state->sub.marker;
+
+ /* search */
+ while (n && m < 4)
+ {
+ static const Byte mark[4] = {0, 0, 0xff, 0xff};
+ if (*p == mark[m])
+ m++;
+ else if (*p)
+ m = 0;
+ else
+ m = 4 - m;
+ p++, n--;
+ }
+
+ /* restore */
+ z->total_in += p - z->next_in;
+ z->next_in = p;
+ z->avail_in = n;
+ z->state->sub.marker = m;
+
+ /* return no joy or set up to restart on a new block */
+ if (m != 4)
+ return Z_DATA_ERROR;
+ r = z->total_in; w = z->total_out;
+ inflateReset(z);
+ z->total_in = r; z->total_out = w;
+ z->state->mode = BLOCKS;
+ return Z_OK;
+}
+
+
+/* Returns true if inflate is currently at the end of a block generated
+ * by Z_SYNC_FLUSH or Z_FULL_FLUSH. This function is used by one PPP
+ * implementation to provide an additional safety check. PPP uses Z_SYNC_FLUSH
+ * but removes the length bytes of the resulting empty stored block. When
+ * decompressing, PPP checks that at the end of input packet, inflate is
+ * waiting for these length bytes.
+ */
+int ZEXPORT inflateSyncPoint(z)
+z_streamp z;
+{
+ if (z == Z_NULL || z->state == Z_NULL || z->state->blocks == Z_NULL)
+ return Z_STREAM_ERROR;
+ return inflate_blocks_sync_point(z->state->blocks);
+}
diff --git a/zlib/inftrees.c b/zlib/inftrees.c
new file mode 100644
index 00000000..4c32ca30
--- /dev/null
+++ b/zlib/inftrees.c
@@ -0,0 +1,454 @@
+/* inftrees.c -- generate Huffman trees for efficient decoding
+ * Copyright (C) 1995-2002 Mark Adler
+ * For conditions of distribution and use, see copyright notice in zlib.h
+ */
+
+#include "zutil.h"
+#include "inftrees.h"
+
+#if !defined(BUILDFIXED) && !defined(STDC)
+# define BUILDFIXED /* non ANSI compilers may not accept inffixed.h */
+#endif
+
+const char inflate_copyright[] =
+ " inflate 1.1.4 Copyright 1995-2002 Mark Adler ";
+/*
+ If you use the zlib library in a product, an acknowledgment is welcome
+ in the documentation of your product. If for some reason you cannot
+ include such an acknowledgment, I would appreciate that you keep this
+ copyright string in the executable of your product.
+ */
+struct internal_state {int dummy;}; /* for buggy compilers */
+
+/* simplify the use of the inflate_huft type with some defines */
+#define exop word.what.Exop
+#define bits word.what.Bits
+
+
+local int huft_build OF((
+ uIntf *, /* code lengths in bits */
+ uInt, /* number of codes */
+ uInt, /* number of "simple" codes */
+ const uIntf *, /* list of base values for non-simple codes */
+ const uIntf *, /* list of extra bits for non-simple codes */
+ inflate_huft * FAR*,/* result: starting table */
+ uIntf *, /* maximum lookup bits (returns actual) */
+ inflate_huft *, /* space for trees */
+ uInt *, /* hufts used in space */
+ uIntf * )); /* space for values */
+
+/* Tables for deflate from PKZIP's appnote.txt. */
+local const uInt cplens[31] = { /* Copy lengths for literal codes 257..285 */
+ 3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27, 31,
+ 35, 43, 51, 59, 67, 83, 99, 115, 131, 163, 195, 227, 258, 0, 0};
+ /* see note #13 above about 258 */
+local const uInt cplext[31] = { /* Extra bits for literal codes 257..285 */
+ 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2,
+ 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0, 112, 112}; /* 112==invalid */
+local const uInt cpdist[30] = { /* Copy offsets for distance codes 0..29 */
+ 1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193,
+ 257, 385, 513, 769, 1025, 1537, 2049, 3073, 4097, 6145,
+ 8193, 12289, 16385, 24577};
+local const uInt cpdext[30] = { /* Extra bits for distance codes */
+ 0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6,
+ 7, 7, 8, 8, 9, 9, 10, 10, 11, 11,
+ 12, 12, 13, 13};
+
+/*
+ Huffman code decoding is performed using a multi-level table lookup.
+ The fastest way to decode is to simply build a lookup table whose
+ size is determined by the longest code. However, the time it takes
+ to build this table can also be a factor if the data being decoded
+ is not very long. The most common codes are necessarily the
+ shortest codes, so those codes dominate the decoding time, and hence
+ the speed. The idea is you can have a shorter table that decodes the
+ shorter, more probable codes, and then point to subsidiary tables for
+ the longer codes. The time it costs to decode the longer codes is
+ then traded against the time it takes to make longer tables.
+
+ This results of this trade are in the variables lbits and dbits
+ below. lbits is the number of bits the first level table for literal/
+ length codes can decode in one step, and dbits is the same thing for
+ the distance codes. Subsequent tables are also less than or equal to
+ those sizes. These values may be adjusted either when all of the
+ codes are shorter than that, in which case the longest code length in
+ bits is used, or when the shortest code is *longer* than the requested
+ table size, in which case the length of the shortest code in bits is
+ used.
+
+ There are two different values for the two tables, since they code a
+ different number of possibilities each. The literal/length table
+ codes 286 possible values, or in a flat code, a little over eight
+ bits. The distance table codes 30 possible values, or a little less
+ than five bits, flat. The optimum values for speed end up being
+ about one bit more than those, so lbits is 8+1 and dbits is 5+1.
+ The optimum values may differ though from machine to machine, and
+ possibly even between compilers. Your mileage may vary.
+ */
+
+
+/* If BMAX needs to be larger than 16, then h and x[] should be uLong. */
+#define BMAX 15 /* maximum bit length of any code */
+
+local int huft_build(b, n, s, d, e, t, m, hp, hn, v)
+uIntf *b; /* code lengths in bits (all assumed <= BMAX) */
+uInt n; /* number of codes (assumed <= 288) */
+uInt s; /* number of simple-valued codes (0..s-1) */
+const uIntf *d; /* list of base values for non-simple codes */
+const uIntf *e; /* list of extra bits for non-simple codes */
+inflate_huft * FAR *t; /* result: starting table */
+uIntf *m; /* maximum lookup bits, returns actual */
+inflate_huft *hp; /* space for trees */
+uInt *hn; /* hufts used in space */
+uIntf *v; /* working area: values in order of bit length */
+/* Given a list of code lengths and a maximum table size, make a set of
+ tables to decode that set of codes. Return Z_OK on success, Z_BUF_ERROR
+ if the given code set is incomplete (the tables are still built in this
+ case), or Z_DATA_ERROR if the input is invalid. */
+{
+
+ uInt a; /* counter for codes of length k */
+ uInt c[BMAX+1]; /* bit length count table */
+ uInt f; /* i repeats in table every f entries */
+ int g; /* maximum code length */
+ int h; /* table level */
+ register uInt i; /* counter, current code */
+ register uInt j; /* counter */
+ register int k; /* number of bits in current code */
+ int l; /* bits per table (returned in m) */
+ uInt mask; /* (1 << w) - 1, to avoid cc -O bug on HP */
+ register uIntf *p; /* pointer into c[], b[], or v[] */
+ inflate_huft *q; /* points to current table */
+ struct inflate_huft_s r; /* table entry for structure assignment */
+ inflate_huft *u[BMAX]; /* table stack */
+ register int w; /* bits before this table == (l * h) */
+ uInt x[BMAX+1]; /* bit offsets, then code stack */
+ uIntf *xp; /* pointer into x */
+ int y; /* number of dummy codes added */
+ uInt z; /* number of entries in current table */
+
+
+ /* Generate counts for each bit length */
+ p = c;
+#define C0 *p++ = 0;
+#define C2 C0 C0 C0 C0
+#define C4 C2 C2 C2 C2
+ C4 /* clear c[]--assume BMAX+1 is 16 */
+ p = b; i = n;
+ do {
+ c[*p++]++; /* assume all entries <= BMAX */
+ } while (--i);
+ if (c[0] == n) /* null input--all zero length codes */
+ {
+ *t = (inflate_huft *)Z_NULL;
+ *m = 0;
+ return Z_OK;
+ }
+
+
+ /* Find minimum and maximum length, bound *m by those */
+ l = *m;
+ for (j = 1; j <= BMAX; j++)
+ if (c[j])
+ break;
+ k = j; /* minimum code length */
+ if ((uInt)l < j)
+ l = j;
+ for (i = BMAX; i; i--)
+ if (c[i])
+ break;
+ g = i; /* maximum code length */
+ if ((uInt)l > i)
+ l = i;
+ *m = l;
+
+
+ /* Adjust last length count to fill out codes, if needed */
+ for (y = 1 << j; j < i; j++, y <<= 1)
+ if ((y -= c[j]) < 0)
+ return Z_DATA_ERROR;
+ if ((y -= c[i]) < 0)
+ return Z_DATA_ERROR;
+ c[i] += y;
+
+
+ /* Generate starting offsets into the value table for each length */
+ x[1] = j = 0;
+ p = c + 1; xp = x + 2;
+ while (--i) { /* note that i == g from above */
+ *xp++ = (j += *p++);
+ }
+
+
+ /* Make a table of values in order of bit lengths */
+ p = b; i = 0;
+ do {
+ if ((j = *p++) != 0)
+ v[x[j]++] = i;
+ } while (++i < n);
+ n = x[g]; /* set n to length of v */
+
+
+ /* Generate the Huffman codes and for each, make the table entries */
+ x[0] = i = 0; /* first Huffman code is zero */
+ p = v; /* grab values in bit order */
+ h = -1; /* no tables yet--level -1 */
+ w = -l; /* bits decoded == (l * h) */
+ u[0] = (inflate_huft *)Z_NULL; /* just to keep compilers happy */
+ q = (inflate_huft *)Z_NULL; /* ditto */
+ z = 0; /* ditto */
+
+ /* go through the bit lengths (k already is bits in shortest code) */
+ for (; k <= g; k++)
+ {
+ a = c[k];
+ while (a--)
+ {
+ /* here i is the Huffman code of length k bits for value *p */
+ /* make tables up to required level */
+ while (k > w + l)
+ {
+ h++;
+ w += l; /* previous table always l bits */
+
+ /* compute minimum size table less than or equal to l bits */
+ z = g - w;
+ z = z > (uInt)l ? l : z; /* table size upper limit */
+ if ((f = 1 << (j = k - w)) > a + 1) /* try a k-w bit table */
+ { /* too few codes for k-w bit table */
+ f -= a + 1; /* deduct codes from patterns left */
+ xp = c + k;
+ if (j < z)
+ while (++j < z) /* try smaller tables up to z bits */
+ {
+ if ((f <<= 1) <= *++xp)
+ break; /* enough codes to use up j bits */
+ f -= *xp; /* else deduct codes from patterns */
+ }
+ }
+ z = 1 << j; /* table entries for j-bit table */
+
+ /* allocate new table */
+ if (*hn + z > MANY) /* (note: doesn't matter for fixed) */
+ return Z_DATA_ERROR; /* overflow of MANY */
+ u[h] = q = hp + *hn;
+ *hn += z;
+
+ /* connect to last table, if there is one */
+ if (h)
+ {
+ x[h] = i; /* save pattern for backing up */
+ r.bits = (Byte)l; /* bits to dump before this table */
+ r.exop = (Byte)j; /* bits in this table */
+ j = i >> (w - l);
+ r.base = (uInt)(q - u[h-1] - j); /* offset to this table */
+ u[h-1][j] = r; /* connect to last table */
+ }
+ else
+ *t = q; /* first table is returned result */
+ }
+
+ /* set up table entry in r */
+ r.bits = (Byte)(k - w);
+ if (p >= v + n)
+ r.exop = 128 + 64; /* out of values--invalid code */
+ else if (*p < s)
+ {
+ r.exop = (Byte)(*p < 256 ? 0 : 32 + 64); /* 256 is end-of-block */
+ r.base = *p++; /* simple code is just the value */
+ }
+ else
+ {
+ r.exop = (Byte)(e[*p - s] + 16 + 64);/* non-simple--look up in lists */
+ r.base = d[*p++ - s];
+ }
+
+ /* fill code-like entries with r */
+ f = 1 << (k - w);
+ for (j = i >> w; j < z; j += f)
+ q[j] = r;
+
+ /* backwards increment the k-bit code i */
+ for (j = 1 << (k - 1); i & j; j >>= 1)
+ i ^= j;
+ i ^= j;
+
+ /* backup over finished tables */
+ mask = (1 << w) - 1; /* needed on HP, cc -O bug */
+ while ((i & mask) != x[h])
+ {
+ h--; /* don't need to update q */
+ w -= l;
+ mask = (1 << w) - 1;
+ }
+ }
+ }
+
+
+ /* Return Z_BUF_ERROR if we were given an incomplete table */
+ return y != 0 && g != 1 ? Z_BUF_ERROR : Z_OK;
+}
+
+
+int inflate_trees_bits(c, bb, tb, hp, z)
+uIntf *c; /* 19 code lengths */
+uIntf *bb; /* bits tree desired/actual depth */
+inflate_huft * FAR *tb; /* bits tree result */
+inflate_huft *hp; /* space for trees */
+z_streamp z; /* for messages */
+{
+ int r;
+ uInt hn = 0; /* hufts used in space */
+ uIntf *v; /* work area for huft_build */
+
+ if ((v = (uIntf*)ZALLOC(z, 19, sizeof(uInt))) == Z_NULL)
+ return Z_MEM_ERROR;
+ r = huft_build(c, 19, 19, (uIntf*)Z_NULL, (uIntf*)Z_NULL,
+ tb, bb, hp, &hn, v);
+ if (r == Z_DATA_ERROR)
+ z->msg = (char*)"oversubscribed dynamic bit lengths tree";
+ else if (r == Z_BUF_ERROR || *bb == 0)
+ {
+ z->msg = (char*)"incomplete dynamic bit lengths tree";
+ r = Z_DATA_ERROR;
+ }
+ ZFREE(z, v);
+ return r;
+}
+
+
+int inflate_trees_dynamic(nl, nd, c, bl, bd, tl, td, hp, z)
+uInt nl; /* number of literal/length codes */
+uInt nd; /* number of distance codes */
+uIntf *c; /* that many (total) code lengths */
+uIntf *bl; /* literal desired/actual bit depth */
+uIntf *bd; /* distance desired/actual bit depth */
+inflate_huft * FAR *tl; /* literal/length tree result */
+inflate_huft * FAR *td; /* distance tree result */
+inflate_huft *hp; /* space for trees */
+z_streamp z; /* for messages */
+{
+ int r;
+ uInt hn = 0; /* hufts used in space */
+ uIntf *v; /* work area for huft_build */
+
+ /* allocate work area */
+ if ((v = (uIntf*)ZALLOC(z, 288, sizeof(uInt))) == Z_NULL)
+ return Z_MEM_ERROR;
+
+ /* build literal/length tree */
+ r = huft_build(c, nl, 257, cplens, cplext, tl, bl, hp, &hn, v);
+ if (r != Z_OK || *bl == 0)
+ {
+ if (r == Z_DATA_ERROR)
+ z->msg = (char*)"oversubscribed literal/length tree";
+ else if (r != Z_MEM_ERROR)
+ {
+ z->msg = (char*)"incomplete literal/length tree";
+ r = Z_DATA_ERROR;
+ }
+ ZFREE(z, v);
+ return r;
+ }
+
+ /* build distance tree */
+ r = huft_build(c + nl, nd, 0, cpdist, cpdext, td, bd, hp, &hn, v);
+ if (r != Z_OK || (*bd == 0 && nl > 257))
+ {
+ if (r == Z_DATA_ERROR)
+ z->msg = (char*)"oversubscribed distance tree";
+ else if (r == Z_BUF_ERROR) {
+#ifdef PKZIP_BUG_WORKAROUND
+ r = Z_OK;
+ }
+#else
+ z->msg = (char*)"incomplete distance tree";
+ r = Z_DATA_ERROR;
+ }
+ else if (r != Z_MEM_ERROR)
+ {
+ z->msg = (char*)"empty distance tree with lengths";
+ r = Z_DATA_ERROR;
+ }
+ ZFREE(z, v);
+ return r;
+#endif
+ }
+
+ /* done */
+ ZFREE(z, v);
+ return Z_OK;
+}
+
+
+/* build fixed tables only once--keep them here */
+#ifdef BUILDFIXED
+local int fixed_built = 0;
+#define FIXEDH 544 /* number of hufts used by fixed tables */
+local inflate_huft fixed_mem[FIXEDH];
+local uInt fixed_bl;
+local uInt fixed_bd;
+local inflate_huft *fixed_tl;
+local inflate_huft *fixed_td;
+#else
+#include "inffixed.h"
+#endif
+
+
+int inflate_trees_fixed(bl, bd, tl, td, z)
+uIntf *bl; /* literal desired/actual bit depth */
+uIntf *bd; /* distance desired/actual bit depth */
+inflate_huft * FAR *tl; /* literal/length tree result */
+inflate_huft * FAR *td; /* distance tree result */
+z_streamp z; /* for memory allocation */
+{
+#ifdef BUILDFIXED
+ /* build fixed tables if not already */
+ if (!fixed_built)
+ {
+ int k; /* temporary variable */
+ uInt f = 0; /* number of hufts used in fixed_mem */
+ uIntf *c; /* length list for huft_build */
+ uIntf *v; /* work area for huft_build */
+
+ /* allocate memory */
+ if ((c = (uIntf*)ZALLOC(z, 288, sizeof(uInt))) == Z_NULL)
+ return Z_MEM_ERROR;
+ if ((v = (uIntf*)ZALLOC(z, 288, sizeof(uInt))) == Z_NULL)
+ {
+ ZFREE(z, c);
+ return Z_MEM_ERROR;
+ }
+
+ /* literal table */
+ for (k = 0; k < 144; k++)
+ c[k] = 8;
+ for (; k < 256; k++)
+ c[k] = 9;
+ for (; k < 280; k++)
+ c[k] = 7;
+ for (; k < 288; k++)
+ c[k] = 8;
+ fixed_bl = 9;
+ huft_build(c, 288, 257, cplens, cplext, &fixed_tl, &fixed_bl,
+ fixed_mem, &f, v);
+
+ /* distance table */
+ for (k = 0; k < 30; k++)
+ c[k] = 5;
+ fixed_bd = 5;
+ huft_build(c, 30, 0, cpdist, cpdext, &fixed_td, &fixed_bd,
+ fixed_mem, &f, v);
+
+ /* done */
+ ZFREE(z, v);
+ ZFREE(z, c);
+ fixed_built = 1;
+ }
+#endif
+ *bl = fixed_bl;
+ *bd = fixed_bd;
+ *tl = fixed_tl;
+ *td = fixed_td;
+ return Z_OK;
+}
diff --git a/zlib/inftrees.h b/zlib/inftrees.h
new file mode 100644
index 00000000..04b73b72
--- /dev/null
+++ b/zlib/inftrees.h
@@ -0,0 +1,58 @@
+/* inftrees.h -- header to use inftrees.c
+ * Copyright (C) 1995-2002 Mark Adler
+ * For conditions of distribution and use, see copyright notice in zlib.h
+ */
+
+/* WARNING: this file should *not* be used by applications. It is
+ part of the implementation of the compression library and is
+ subject to change. Applications should only use zlib.h.
+ */
+
+/* Huffman code lookup table entry--this entry is four bytes for machines
+ that have 16-bit pointers (e.g. PC's in the small or medium model). */
+
+typedef struct inflate_huft_s FAR inflate_huft;
+
+struct inflate_huft_s {
+ union {
+ struct {
+ Byte Exop; /* number of extra bits or operation */
+ Byte Bits; /* number of bits in this code or subcode */
+ } what;
+ uInt pad; /* pad structure to a power of 2 (4 bytes for */
+ } word; /* 16-bit, 8 bytes for 32-bit int's) */
+ uInt base; /* literal, length base, distance base,
+ or table offset */
+};
+
+/* Maximum size of dynamic tree. The maximum found in a long but non-
+ exhaustive search was 1004 huft structures (850 for length/literals
+ and 154 for distances, the latter actually the result of an
+ exhaustive search). The actual maximum is not known, but the
+ value below is more than safe. */
+#define MANY 1440
+
+extern int inflate_trees_bits OF((
+ uIntf *, /* 19 code lengths */
+ uIntf *, /* bits tree desired/actual depth */
+ inflate_huft * FAR *, /* bits tree result */
+ inflate_huft *, /* space for trees */
+ z_streamp)); /* for messages */
+
+extern int inflate_trees_dynamic OF((
+ uInt, /* number of literal/length codes */
+ uInt, /* number of distance codes */
+ uIntf *, /* that many (total) code lengths */
+ uIntf *, /* literal desired/actual bit depth */
+ uIntf *, /* distance desired/actual bit depth */
+ inflate_huft * FAR *, /* literal/length tree result */
+ inflate_huft * FAR *, /* distance tree result */
+ inflate_huft *, /* space for trees */
+ z_streamp)); /* for messages */
+
+extern int inflate_trees_fixed OF((
+ uIntf *, /* literal desired/actual bit depth */
+ uIntf *, /* distance desired/actual bit depth */
+ inflate_huft * FAR *, /* literal/length tree result */
+ inflate_huft * FAR *, /* distance tree result */
+ z_streamp)); /* for memory allocation */
diff --git a/zlib/infutil.c b/zlib/infutil.c
new file mode 100644
index 00000000..9a076221
--- /dev/null
+++ b/zlib/infutil.c
@@ -0,0 +1,87 @@
+/* inflate_util.c -- data and routines common to blocks and codes
+ * Copyright (C) 1995-2002 Mark Adler
+ * For conditions of distribution and use, see copyright notice in zlib.h
+ */
+
+#include "zutil.h"
+#include "infblock.h"
+#include "inftrees.h"
+#include "infcodes.h"
+#include "infutil.h"
+
+struct inflate_codes_state {int dummy;}; /* for buggy compilers */
+
+/* And'ing with mask[n] masks the lower n bits */
+uInt inflate_mask[17] = {
+ 0x0000,
+ 0x0001, 0x0003, 0x0007, 0x000f, 0x001f, 0x003f, 0x007f, 0x00ff,
+ 0x01ff, 0x03ff, 0x07ff, 0x0fff, 0x1fff, 0x3fff, 0x7fff, 0xffff
+};
+
+
+/* copy as much as possible from the sliding window to the output area */
+int inflate_flush(s, z, r)
+inflate_blocks_statef *s;
+z_streamp z;
+int r;
+{
+ uInt n;
+ Bytef *p;
+ Bytef *q;
+
+ /* local copies of source and destination pointers */
+ p = z->next_out;
+ q = s->read;
+
+ /* compute number of bytes to copy as far as end of window */
+ n = (uInt)((q <= s->write ? s->write : s->end) - q);
+ if (n > z->avail_out) n = z->avail_out;
+ if (n && r == Z_BUF_ERROR) r = Z_OK;
+
+ /* update counters */
+ z->avail_out -= n;
+ z->total_out += n;
+
+ /* update check information */
+ if (s->checkfn != Z_NULL)
+ z->adler = s->check = (*s->checkfn)(s->check, q, n);
+
+ /* copy as far as end of window */
+ zmemcpy(p, q, n);
+ p += n;
+ q += n;
+
+ /* see if more to copy at beginning of window */
+ if (q == s->end)
+ {
+ /* wrap pointers */
+ q = s->window;
+ if (s->write == s->end)
+ s->write = s->window;
+
+ /* compute bytes to copy */
+ n = (uInt)(s->write - q);
+ if (n > z->avail_out) n = z->avail_out;
+ if (n && r == Z_BUF_ERROR) r = Z_OK;
+
+ /* update counters */
+ z->avail_out -= n;
+ z->total_out += n;
+
+ /* update check information */
+ if (s->checkfn != Z_NULL)
+ z->adler = s->check = (*s->checkfn)(s->check, q, n);
+
+ /* copy */
+ zmemcpy(p, q, n);
+ p += n;
+ q += n;
+ }
+
+ /* update pointers */
+ z->next_out = p;
+ s->read = q;
+
+ /* done */
+ return r;
+}
diff --git a/zlib/infutil.h b/zlib/infutil.h
new file mode 100644
index 00000000..4401df82
--- /dev/null
+++ b/zlib/infutil.h
@@ -0,0 +1,98 @@
+/* infutil.h -- types and macros common to blocks and codes
+ * Copyright (C) 1995-2002 Mark Adler
+ * For conditions of distribution and use, see copyright notice in zlib.h
+ */
+
+/* WARNING: this file should *not* be used by applications. It is
+ part of the implementation of the compression library and is
+ subject to change. Applications should only use zlib.h.
+ */
+
+#ifndef _INFUTIL_H
+#define _INFUTIL_H
+
+typedef enum {
+ TYPE, /* get type bits (3, including end bit) */
+ LENS, /* get lengths for stored */
+ STORED, /* processing stored block */
+ TABLE, /* get table lengths */
+ BTREE, /* get bit lengths tree for a dynamic block */
+ DTREE, /* get length, distance trees for a dynamic block */
+ CODES, /* processing fixed or dynamic block */
+ DRY, /* output remaining window bytes */
+ DONE, /* finished last block, done */
+ BAD} /* got a data error--stuck here */
+inflate_block_mode;
+
+/* inflate blocks semi-private state */
+struct inflate_blocks_state {
+
+ /* mode */
+ inflate_block_mode mode; /* current inflate_block mode */
+
+ /* mode dependent information */
+ union {
+ uInt left; /* if STORED, bytes left to copy */
+ struct {
+ uInt table; /* table lengths (14 bits) */
+ uInt index; /* index into blens (or border) */
+ uIntf *blens; /* bit lengths of codes */
+ uInt bb; /* bit length tree depth */
+ inflate_huft *tb; /* bit length decoding tree */
+ } trees; /* if DTREE, decoding info for trees */
+ struct {
+ inflate_codes_statef
+ *codes;
+ } decode; /* if CODES, current state */
+ } sub; /* submode */
+ uInt last; /* true if this block is the last block */
+
+ /* mode independent information */
+ uInt bitk; /* bits in bit buffer */
+ uLong bitb; /* bit buffer */
+ inflate_huft *hufts; /* single malloc for tree space */
+ Bytef *window; /* sliding window */
+ Bytef *end; /* one byte after sliding window */
+ Bytef *read; /* window read pointer */
+ Bytef *write; /* window write pointer */
+ check_func checkfn; /* check function */
+ uLong check; /* check on output */
+
+};
+
+
+/* defines for inflate input/output */
+/* update pointers and return */
+#define UPDBITS {s->bitb=b;s->bitk=k;}
+#define UPDIN {z->avail_in=n;z->total_in+=p-z->next_in;z->next_in=p;}
+#define UPDOUT {s->write=q;}
+#define UPDATE {UPDBITS UPDIN UPDOUT}
+#define LEAVE {UPDATE return inflate_flush(s,z,r);}
+/* get bytes and bits */
+#define LOADIN {p=z->next_in;n=z->avail_in;b=s->bitb;k=s->bitk;}
+#define NEEDBYTE {if(n)r=Z_OK;else LEAVE}
+#define NEXTBYTE (n--,*p++)
+#define NEEDBITS(j) {while(k<(j)){NEEDBYTE;b|=((uLong)NEXTBYTE)<<k;k+=8;}}
+#define DUMPBITS(j) {b>>=(j);k-=(j);}
+/* output bytes */
+#define WAVAIL (uInt)(q<s->read?s->read-q-1:s->end-q)
+#define LOADOUT {q=s->write;m=(uInt)WAVAIL;}
+#define WRAP {if(q==s->end&&s->read!=s->window){q=s->window;m=(uInt)WAVAIL;}}
+#define FLUSH {UPDOUT r=inflate_flush(s,z,r); LOADOUT}
+#define NEEDOUT {if(m==0){WRAP if(m==0){FLUSH WRAP if(m==0) LEAVE}}r=Z_OK;}
+#define OUTBYTE(a) {*q++=(Byte)(a);m--;}
+/* load local pointers */
+#define LOAD {LOADIN LOADOUT}
+
+/* masks for lower bits (size given to avoid silly warnings with Visual C++) */
+extern uInt inflate_mask[17];
+
+/* copy as much as possible from the sliding window to the output area */
+extern int inflate_flush OF((
+ inflate_blocks_statef *,
+ z_streamp ,
+ int));
+
+struct internal_state {int dummy;}; /* for buggy compilers */
+
+#endif
diff --git a/zlib/maketree.c b/zlib/maketree.c
new file mode 100644
index 00000000..a16d4b14
--- /dev/null
+++ b/zlib/maketree.c
@@ -0,0 +1,85 @@
+/* maketree.c -- make inffixed.h table for decoding fixed codes
+ * Copyright (C) 1995-2002 Mark Adler
+ * For conditions of distribution and use, see copyright notice in zlib.h
+ */
+
+/* WARNING: this file should *not* be used by applications. It is
+ part of the implementation of the compression library and is
+ subject to change. Applications should only use zlib.h.
+ */
+
+/* This program is included in the distribution for completeness.
+ You do not need to compile or run this program since inffixed.h
+ is already included in the distribution. To use this program
+ you need to compile zlib with BUILDFIXED defined and then compile
+ and link this program with the zlib library. Then the output of
+ this program can be piped to inffixed.h. */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include "zutil.h"
+#include "inftrees.h"
+
+/* simplify the use of the inflate_huft type with some defines */
+#define exop word.what.Exop
+#define bits word.what.Bits
+
+/* generate initialization table for an inflate_huft structure array */
+void maketree(uInt b, inflate_huft *t)
+{
+ int i, e;
+
+ i = 0;
+ while (1)
+ {
+ e = t[i].exop;
+ if (e && (e & (16+64)) == 0) /* table pointer */
+ {
+ fprintf(stderr, "maketree: cannot initialize sub-tables!\n");
+ exit(1);
+ }
+ if (i % 4 == 0)
+ printf("\n ");
+ printf(" {{{%u,%u}},%u}", t[i].exop, t[i].bits, t[i].base);
+ if (++i == (1<<b))
+ break;
+ putchar(',');
+ }
+ puts("");
+}
+
+/* create the fixed tables in C initialization syntax */
+void main(void)
+{
+ int r;
+ uInt bl, bd;
+ inflate_huft *tl, *td;
+ z_stream z;
+
+ z.zalloc = zcalloc;
+ z.opaque = (voidpf)0;
+ z.zfree = zcfree;
+ r = inflate_trees_fixed(&bl, &bd, &tl, &td, &z);
+ if (r)
+ {
+ fprintf(stderr, "inflate_trees_fixed error %d\n", r);
+ return;
+ }
+ puts("/* inffixed.h -- table for decoding fixed codes");
+ puts(" * Generated automatically by the maketree.c program");
+ puts(" */");
+ puts("");
+ puts("/* WARNING: this file should *not* be used by applications. It is");
+ puts(" part of the implementation of the compression library and is");
+ puts(" subject to change. Applications should only use zlib.h.");
+ puts(" */");
+ puts("");
+ printf("local uInt fixed_bl = %d;\n", bl);
+ printf("local uInt fixed_bd = %d;\n", bd);
+ printf("local inflate_huft fixed_tl[] = {");
+ maketree(bl, tl);
+ puts(" };");
+ printf("local inflate_huft fixed_td[] = {");
+ maketree(bd, td);
+ puts(" };");
+}
diff --git a/zlib/minigzip.c b/zlib/minigzip.c
new file mode 100644
index 00000000..8be02bd6
--- /dev/null
+++ b/zlib/minigzip.c
@@ -0,0 +1,320 @@
+/* minigzip.c -- simulate gzip using the zlib compression library
+ * Copyright (C) 1995-2002 Jean-loup Gailly.
+ * For conditions of distribution and use, see copyright notice in zlib.h
+ */
+
+/*
+ * minigzip is a minimal implementation of the gzip utility. This is
+ * only an example of using zlib and isn't meant to replace the
+ * full-featured gzip. No attempt is made to deal with file systems
+ * limiting names to 14 or 8+3 characters, etc... Error checking is
+ * very limited. So use minigzip only for testing; use gzip for the
+ * real thing. On MSDOS, use only on file names without extension
+ * or in pipe mode.
+ */
+
+/* @(#) $Id: minigzip.c,v 1.1 2004/10/08 09:44:26 const_k Exp $ */
+
+#include <stdio.h>
+#include "zlib.h"
+
+#ifdef STDC
+# include <string.h>
+# include <stdlib.h>
+#else
+ extern void exit OF((int));
+#endif
+
+#ifdef USE_MMAP
+# include <sys/types.h>
+# include <sys/mman.h>
+# include <sys/stat.h>
+#endif
+
+#if defined(MSDOS) || defined(OS2) || defined(WIN32)
+# include <fcntl.h>
+# include <io.h>
+# define SET_BINARY_MODE(file) setmode(fileno(file), O_BINARY)
+#else
+# define SET_BINARY_MODE(file)
+#endif
+
+#ifdef VMS
+# define unlink delete
+# define GZ_SUFFIX "-gz"
+#endif
+#ifdef RISCOS
+# define unlink remove
+# define GZ_SUFFIX "-gz"
+# define fileno(file) file->__file
+#endif
+#if defined(__MWERKS__) && __dest_os != __be_os && __dest_os != __win32_os
+# include <unix.h> /* for fileno */
+#endif
+
+#ifndef WIN32 /* unlink already in stdio.h for WIN32 */
+ extern int unlink OF((const char *));
+#endif
+
+#ifndef GZ_SUFFIX
+# define GZ_SUFFIX ".gz"
+#endif
+#define SUFFIX_LEN (sizeof(GZ_SUFFIX)-1)
+
+#define BUFLEN 16384
+#define MAX_NAME_LEN 1024
+
+#ifdef MAXSEG_64K
+# define local static
+ /* Needed for systems with limitation on stack size. */
+#else
+# define local
+#endif
+
+char *prog;
+
+void error OF((const char *msg));
+void gz_compress OF((FILE *in, gzFile out));
+#ifdef USE_MMAP
+int gz_compress_mmap OF((FILE *in, gzFile out));
+#endif
+void gz_uncompress OF((gzFile in, FILE *out));
+void file_compress OF((char *file, char *mode));
+void file_uncompress OF((char *file));
+int main OF((int argc, char *argv[]));
+
+/* ===========================================================================
+ * Display error message and exit
+ */
+void error(msg)
+ const char *msg;
+{
+ fprintf(stderr, "%s: %s\n", prog, msg);
+ exit(1);
+}
+
+/* ===========================================================================
+ * Compress input to output then close both files.
+ */
+
+void gz_compress(in, out)
+ FILE *in;
+ gzFile out;
+{
+ local char buf[BUFLEN];
+ int len;
+ int err;
+
+#ifdef USE_MMAP
+ /* Try first compressing with mmap. If mmap fails (minigzip used in a
+ * pipe), use the normal fread loop.
+ */
+ if (gz_compress_mmap(in, out) == Z_OK) return;
+#endif
+ for (;;) {
+ len = fread(buf, 1, sizeof(buf), in);
+ if (ferror(in)) {
+ perror("fread");
+ exit(1);
+ }
+ if (len == 0) break;
+
+ if (gzwrite(out, buf, (unsigned)len) != len) error(gzerror(out, &err));
+ }
+ fclose(in);
+ if (gzclose(out) != Z_OK) error("failed gzclose");
+}
+
+#ifdef USE_MMAP /* MMAP version, Miguel Albrecht <malbrech@eso.org> */
+
+/* Try compressing the input file at once using mmap. Return Z_OK if
+ * if success, Z_ERRNO otherwise.
+ */
+int gz_compress_mmap(in, out)
+ FILE *in;
+ gzFile out;
+{
+ int len;
+ int err;
+ int ifd = fileno(in);
+ caddr_t buf; /* mmap'ed buffer for the entire input file */
+ off_t buf_len; /* length of the input file */
+ struct stat sb;
+
+ /* Determine the size of the file, needed for mmap: */
+ if (fstat(ifd, &sb) < 0) return Z_ERRNO;
+ buf_len = sb.st_size;
+ if (buf_len <= 0) return Z_ERRNO;
+
+ /* Now do the actual mmap: */
+ buf = mmap((caddr_t) 0, buf_len, PROT_READ, MAP_SHARED, ifd, (off_t)0);
+ if (buf == (caddr_t)(-1)) return Z_ERRNO;
+
+ /* Compress the whole file at once: */
+ len = gzwrite(out, (char *)buf, (unsigned)buf_len);
+
+ if (len != (int)buf_len) error(gzerror(out, &err));
+
+ munmap(buf, buf_len);
+ fclose(in);
+ if (gzclose(out) != Z_OK) error("failed gzclose");
+ return Z_OK;
+}
+#endif /* USE_MMAP */
+
+/* ===========================================================================
+ * Uncompress input to output then close both files.
+ */
+void gz_uncompress(in, out)
+ gzFile in;
+ FILE *out;
+{
+ local char buf[BUFLEN];
+ int len;
+ int err;
+
+ for (;;) {
+ len = gzread(in, buf, sizeof(buf));
+ if (len < 0) error (gzerror(in, &err));
+ if (len == 0) break;
+
+ if ((int)fwrite(buf, 1, (unsigned)len, out) != len) {
+ error("failed fwrite");
+ }
+ }
+ if (fclose(out)) error("failed fclose");
+
+ if (gzclose(in) != Z_OK) error("failed gzclose");
+}
+
+
+/* ===========================================================================
+ * Compress the given file: create a corresponding .gz file and remove the
+ * original.
+ */
+void file_compress(file, mode)
+ char *file;
+ char *mode;
+{
+ local char outfile[MAX_NAME_LEN];
+ FILE *in;
+ gzFile out;
+
+ strcpy(outfile, file);
+ strcat(outfile, GZ_SUFFIX);
+
+ in = fopen(file, "rb");
+ if (in == NULL) {
+ perror(file);
+ exit(1);
+ }
+ out = gzopen(outfile, mode);
+ if (out == NULL) {
+ fprintf(stderr, "%s: can't gzopen %s\n", prog, outfile);
+ exit(1);
+ }
+ gz_compress(in, out);
+
+ unlink(file);
+}
+
+
+/* ===========================================================================
+ * Uncompress the given file and remove the original.
+ */
+void file_uncompress(file)
+ char *file;
+{
+ local char buf[MAX_NAME_LEN];
+ char *infile, *outfile;
+ FILE *out;
+ gzFile in;
+ int len = strlen(file);
+
+ strcpy(buf, file);
+
+ if (len > SUFFIX_LEN && strcmp(file+len-SUFFIX_LEN, GZ_SUFFIX) == 0) {
+ infile = file;
+ outfile = buf;
+ outfile[len-3] = '\0';
+ } else {
+ outfile = file;
+ infile = buf;
+ strcat(infile, GZ_SUFFIX);
+ }
+ in = gzopen(infile, "rb");
+ if (in == NULL) {
+ fprintf(stderr, "%s: can't gzopen %s\n", prog, infile);
+ exit(1);
+ }
+ out = fopen(outfile, "wb");
+ if (out == NULL) {
+ perror(file);
+ exit(1);
+ }
+
+ gz_uncompress(in, out);
+
+ unlink(infile);
+}
+
+
+/* ===========================================================================
+ * Usage: minigzip [-d] [-f] [-h] [-1 to -9] [files...]
+ * -d : decompress
+ * -f : compress with Z_FILTERED
+ * -h : compress with Z_HUFFMAN_ONLY
+ * -1 to -9 : compression level
+ */
+
+int main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ int uncompr = 0;
+ gzFile file;
+ char outmode[20];
+
+ strcpy(outmode, "wb6 ");
+
+ prog = argv[0];
+ argc--, argv++;
+
+ while (argc > 0) {
+ if (strcmp(*argv, "-d") == 0)
+ uncompr = 1;
+ else if (strcmp(*argv, "-f") == 0)
+ outmode[3] = 'f';
+ else if (strcmp(*argv, "-h") == 0)
+ outmode[3] = 'h';
+ else if ((*argv)[0] == '-' && (*argv)[1] >= '1' && (*argv)[1] <= '9' &&
+ (*argv)[2] == 0)
+ outmode[2] = (*argv)[1];
+ else
+ break;
+ argc--, argv++;
+ }
+ if (argc == 0) {
+ SET_BINARY_MODE(stdin);
+ SET_BINARY_MODE(stdout);
+ if (uncompr) {
+ file = gzdopen(fileno(stdin), "rb");
+ if (file == NULL) error("can't gzdopen stdin");
+ gz_uncompress(file, stdout);
+ } else {
+ file = gzdopen(fileno(stdout), outmode);
+ if (file == NULL) error("can't gzdopen stdout");
+ gz_compress(stdin, file);
+ }
+ } else {
+ do {
+ if (uncompr) {
+ file_uncompress(*argv);
+ } else {
+ file_compress(*argv, outmode);
+ }
+ } while (argv++, --argc);
+ }
+ exit(0);
+ return 0; /* to avoid warning */
+}
diff --git a/zlib/trees.c b/zlib/trees.c
new file mode 100644
index 00000000..123c2b32
--- /dev/null
+++ b/zlib/trees.c
@@ -0,0 +1,1214 @@
+/* trees.c -- output deflated data using Huffman coding
+ * Copyright (C) 1995-2002 Jean-loup Gailly
+ * For conditions of distribution and use, see copyright notice in zlib.h
+ */
+
+/*
+ * ALGORITHM
+ *
+ * The "deflation" process uses several Huffman trees. The more
+ * common source values are represented by shorter bit sequences.
+ *
+ * Each code tree is stored in a compressed form which is itself
+ * a Huffman encoding of the lengths of all the code strings (in
+ * ascending order by source values). The actual code strings are
+ * reconstructed from the lengths in the inflate process, as described
+ * in the deflate specification.
+ *
+ * REFERENCES
+ *
+ * Deutsch, L.P.,"'Deflate' Compressed Data Format Specification".
+ * Available in ftp.uu.net:/pub/archiving/zip/doc/deflate-1.1.doc
+ *
+ * Storer, James A.
+ * Data Compression: Methods and Theory, pp. 49-50.
+ * Computer Science Press, 1988. ISBN 0-7167-8156-5.
+ *
+ * Sedgewick, R.
+ * Algorithms, p290.
+ * Addison-Wesley, 1983. ISBN 0-201-06672-6.
+ */
+
+/* @(#) $Id: trees.c,v 1.1 2004/10/08 09:44:26 const_k Exp $ */
+
+/* #define GEN_TREES_H */
+
+#include "deflate.h"
+
+#ifdef DEBUG
+# include <ctype.h>
+#endif
+
+/* ===========================================================================
+ * Constants
+ */
+
+#define MAX_BL_BITS 7
+/* Bit length codes must not exceed MAX_BL_BITS bits */
+
+#define END_BLOCK 256
+/* end of block literal code */
+
+#define REP_3_6 16
+/* repeat previous bit length 3-6 times (2 bits of repeat count) */
+
+#define REPZ_3_10 17
+/* repeat a zero length 3-10 times (3 bits of repeat count) */
+
+#define REPZ_11_138 18
+/* repeat a zero length 11-138 times (7 bits of repeat count) */
+
+local const int extra_lbits[LENGTH_CODES] /* extra bits for each length code */
+ = {0,0,0,0,0,0,0,0,1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4,5,5,5,5,0};
+
+local const int extra_dbits[D_CODES] /* extra bits for each distance code */
+ = {0,0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10,11,11,12,12,13,13};
+
+local const int extra_blbits[BL_CODES]/* extra bits for each bit length code */
+ = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,3,7};
+
+local const uch bl_order[BL_CODES]
+ = {16,17,18,0,8,7,9,6,10,5,11,4,12,3,13,2,14,1,15};
+/* The lengths of the bit length codes are sent in order of decreasing
+ * probability, to avoid transmitting the lengths for unused bit length codes.
+ */
+
+#define Buf_size (8 * 2*sizeof(char))
+/* Number of bits used within bi_buf. (bi_buf might be implemented on
+ * more than 16 bits on some systems.)
+ */
+
+/* ===========================================================================
+ * Local data. These are initialized only once.
+ */
+
+#define DIST_CODE_LEN 512 /* see definition of array dist_code below */
+
+#if defined(GEN_TREES_H) || !defined(STDC)
+/* non ANSI compilers may not accept trees.h */
+
+local ct_data static_ltree[L_CODES+2];
+/* The static literal tree. Since the bit lengths are imposed, there is no
+ * need for the L_CODES extra codes used during heap construction. However
+ * The codes 286 and 287 are needed to build a canonical tree (see _tr_init
+ * below).
+ */
+
+local ct_data static_dtree[D_CODES];
+/* The static distance tree. (Actually a trivial tree since all codes use
+ * 5 bits.)
+ */
+
+uch _dist_code[DIST_CODE_LEN];
+/* Distance codes. The first 256 values correspond to the distances
+ * 3 .. 258, the last 256 values correspond to the top 8 bits of
+ * the 15 bit distances.
+ */
+
+uch _length_code[MAX_MATCH-MIN_MATCH+1];
+/* length code for each normalized match length (0 == MIN_MATCH) */
+
+local int base_length[LENGTH_CODES];
+/* First normalized length for each code (0 = MIN_MATCH) */
+
+local int base_dist[D_CODES];
+/* First normalized distance for each code (0 = distance of 1) */
+
+#else
+# include "trees.h"
+#endif /* GEN_TREES_H */
+
+struct static_tree_desc_s {
+ const ct_data *static_tree; /* static tree or NULL */
+ const intf *extra_bits; /* extra bits for each code or NULL */
+ int extra_base; /* base index for extra_bits */
+ int elems; /* max number of elements in the tree */
+ int max_length; /* max bit length for the codes */
+};
+
+local static_tree_desc static_l_desc =
+{static_ltree, extra_lbits, LITERALS+1, L_CODES, MAX_BITS};
+
+local static_tree_desc static_d_desc =
+{static_dtree, extra_dbits, 0, D_CODES, MAX_BITS};
+
+local static_tree_desc static_bl_desc =
+{(const ct_data *)0, extra_blbits, 0, BL_CODES, MAX_BL_BITS};
+
+/* ===========================================================================
+ * Local (static) routines in this file.
+ */
+
+local void tr_static_init OF((void));
+local void init_block OF((deflate_state *s));
+local void pqdownheap OF((deflate_state *s, ct_data *tree, int k));
+local void gen_bitlen OF((deflate_state *s, tree_desc *desc));
+local void gen_codes OF((ct_data *tree, int max_code, ushf *bl_count));
+local void build_tree OF((deflate_state *s, tree_desc *desc));
+local void scan_tree OF((deflate_state *s, ct_data *tree, int max_code));
+local void send_tree OF((deflate_state *s, ct_data *tree, int max_code));
+local int build_bl_tree OF((deflate_state *s));
+local void send_all_trees OF((deflate_state *s, int lcodes, int dcodes,
+ int blcodes));
+local void compress_block OF((deflate_state *s, ct_data *ltree,
+ ct_data *dtree));
+local void set_data_type OF((deflate_state *s));
+local unsigned bi_reverse OF((unsigned value, int length));
+local void bi_windup OF((deflate_state *s));
+local void bi_flush OF((deflate_state *s));
+local void copy_block OF((deflate_state *s, charf *buf, unsigned len,
+ int header));
+
+#ifdef GEN_TREES_H
+local void gen_trees_header OF((void));
+#endif
+
+#ifndef DEBUG
+# define send_code(s, c, tree) send_bits(s, tree[c].Code, tree[c].Len)
+ /* Send a code of the given tree. c and tree must not have side effects */
+
+#else /* DEBUG */
+# define send_code(s, c, tree) \
+ { if (z_verbose>2) fprintf(stderr,"\ncd %3d ",(c)); \
+ send_bits(s, tree[c].Code, tree[c].Len); }
+#endif
+
+/* ===========================================================================
+ * Output a short LSB first on the stream.
+ * IN assertion: there is enough room in pendingBuf.
+ */
+#define put_short(s, w) { \
+ put_byte(s, (uch)((w) & 0xff)); \
+ put_byte(s, (uch)((ush)(w) >> 8)); \
+}
+
+/* ===========================================================================
+ * Send a value on a given number of bits.
+ * IN assertion: length <= 16 and value fits in length bits.
+ */
+#ifdef DEBUG
+local void send_bits OF((deflate_state *s, int value, int length));
+
+local void send_bits(s, value, length)
+ deflate_state *s;
+ int value; /* value to send */
+ int length; /* number of bits */
+{
+ Tracevv((stderr," l %2d v %4x ", length, value));
+ Assert(length > 0 && length <= 15, "invalid length");
+ s->bits_sent += (ulg)length;
+
+ /* If not enough room in bi_buf, use (valid) bits from bi_buf and
+ * (16 - bi_valid) bits from value, leaving (width - (16-bi_valid))
+ * unused bits in value.
+ */
+ if (s->bi_valid > (int)Buf_size - length) {
+ s->bi_buf |= (value << s->bi_valid);
+ put_short(s, s->bi_buf);
+ s->bi_buf = (ush)value >> (Buf_size - s->bi_valid);
+ s->bi_valid += length - Buf_size;
+ } else {
+ s->bi_buf |= value << s->bi_valid;
+ s->bi_valid += length;
+ }
+}
+#else /* !DEBUG */
+
+#define send_bits(s, value, length) \
+{ int len = length;\
+ if (s->bi_valid > (int)Buf_size - len) {\
+ int val = value;\
+ s->bi_buf |= (val << s->bi_valid);\
+ put_short(s, s->bi_buf);\
+ s->bi_buf = (ush)val >> (Buf_size - s->bi_valid);\
+ s->bi_valid += len - Buf_size;\
+ } else {\
+ s->bi_buf |= (value) << s->bi_valid;\
+ s->bi_valid += len;\
+ }\
+}
+#endif /* DEBUG */
+
+
+#define MAX(a,b) (a >= b ? a : b)
+/* the arguments must not have side effects */
+
+/* ===========================================================================
+ * Initialize the various 'constant' tables.
+ */
+local void tr_static_init()
+{
+#if defined(GEN_TREES_H) || !defined(STDC)
+ static int static_init_done = 0;
+ int n; /* iterates over tree elements */
+ int bits; /* bit counter */
+ int length; /* length value */
+ int code; /* code value */
+ int dist; /* distance index */
+ ush bl_count[MAX_BITS+1];
+ /* number of codes at each bit length for an optimal tree */
+
+ if (static_init_done) return;
+
+ /* For some embedded targets, global variables are not initialized: */
+ static_l_desc.static_tree = static_ltree;
+ static_l_desc.extra_bits = extra_lbits;
+ static_d_desc.static_tree = static_dtree;
+ static_d_desc.extra_bits = extra_dbits;
+ static_bl_desc.extra_bits = extra_blbits;
+
+ /* Initialize the mapping length (0..255) -> length code (0..28) */
+ length = 0;
+ for (code = 0; code < LENGTH_CODES-1; code++) {
+ base_length[code] = length;
+ for (n = 0; n < (1<<extra_lbits[code]); n++) {
+ _length_code[length++] = (uch)code;
+ }
+ }
+ Assert (length == 256, "tr_static_init: length != 256");
+ /* Note that the length 255 (match length 258) can be represented
+ * in two different ways: code 284 + 5 bits or code 285, so we
+ * overwrite length_code[255] to use the best encoding:
+ */
+ _length_code[length-1] = (uch)code;
+
+ /* Initialize the mapping dist (0..32K) -> dist code (0..29) */
+ dist = 0;
+ for (code = 0 ; code < 16; code++) {
+ base_dist[code] = dist;
+ for (n = 0; n < (1<<extra_dbits[code]); n++) {
+ _dist_code[dist++] = (uch)code;
+ }
+ }
+ Assert (dist == 256, "tr_static_init: dist != 256");
+ dist >>= 7; /* from now on, all distances are divided by 128 */
+ for ( ; code < D_CODES; code++) {
+ base_dist[code] = dist << 7;
+ for (n = 0; n < (1<<(extra_dbits[code]-7)); n++) {
+ _dist_code[256 + dist++] = (uch)code;
+ }
+ }
+ Assert (dist == 256, "tr_static_init: 256+dist != 512");
+
+ /* Construct the codes of the static literal tree */
+ for (bits = 0; bits <= MAX_BITS; bits++) bl_count[bits] = 0;
+ n = 0;
+ while (n <= 143) static_ltree[n++].Len = 8, bl_count[8]++;
+ while (n <= 255) static_ltree[n++].Len = 9, bl_count[9]++;
+ while (n <= 279) static_ltree[n++].Len = 7, bl_count[7]++;
+ while (n <= 287) static_ltree[n++].Len = 8, bl_count[8]++;
+ /* Codes 286 and 287 do not exist, but we must include them in the
+ * tree construction to get a canonical Huffman tree (longest code
+ * all ones)
+ */
+ gen_codes((ct_data *)static_ltree, L_CODES+1, bl_count);
+
+ /* The static distance tree is trivial: */
+ for (n = 0; n < D_CODES; n++) {
+ static_dtree[n].Len = 5;
+ static_dtree[n].Code = bi_reverse((unsigned)n, 5);
+ }
+ static_init_done = 1;
+
+# ifdef GEN_TREES_H
+ gen_trees_header();
+# endif
+#endif /* defined(GEN_TREES_H) || !defined(STDC) */
+}
+
+/* ===========================================================================
+ * Genererate the file trees.h describing the static trees.
+ */
+#ifdef GEN_TREES_H
+# ifndef DEBUG
+# include <stdio.h>
+# endif
+
+# define SEPARATOR(i, last, width) \
+ ((i) == (last)? "\n};\n\n" : \
+ ((i) % (width) == (width)-1 ? ",\n" : ", "))
+
+void gen_trees_header()
+{
+ FILE *header = fopen("trees.h", "w");
+ int i;
+
+ Assert (header != NULL, "Can't open trees.h");
+ fprintf(header,
+ "/* header created automatically with -DGEN_TREES_H */\n\n");
+
+ fprintf(header, "local const ct_data static_ltree[L_CODES+2] = {\n");
+ for (i = 0; i < L_CODES+2; i++) {
+ fprintf(header, "{{%3u},{%3u}}%s", static_ltree[i].Code,
+ static_ltree[i].Len, SEPARATOR(i, L_CODES+1, 5));
+ }
+
+ fprintf(header, "local const ct_data static_dtree[D_CODES] = {\n");
+ for (i = 0; i < D_CODES; i++) {
+ fprintf(header, "{{%2u},{%2u}}%s", static_dtree[i].Code,
+ static_dtree[i].Len, SEPARATOR(i, D_CODES-1, 5));
+ }
+
+ fprintf(header, "const uch _dist_code[DIST_CODE_LEN] = {\n");
+ for (i = 0; i < DIST_CODE_LEN; i++) {
+ fprintf(header, "%2u%s", _dist_code[i],
+ SEPARATOR(i, DIST_CODE_LEN-1, 20));
+ }
+
+ fprintf(header, "const uch _length_code[MAX_MATCH-MIN_MATCH+1]= {\n");
+ for (i = 0; i < MAX_MATCH-MIN_MATCH+1; i++) {
+ fprintf(header, "%2u%s", _length_code[i],
+ SEPARATOR(i, MAX_MATCH-MIN_MATCH, 20));
+ }
+
+ fprintf(header, "local const int base_length[LENGTH_CODES] = {\n");
+ for (i = 0; i < LENGTH_CODES; i++) {
+ fprintf(header, "%1u%s", base_length[i],
+ SEPARATOR(i, LENGTH_CODES-1, 20));
+ }
+
+ fprintf(header, "local const int base_dist[D_CODES] = {\n");
+ for (i = 0; i < D_CODES; i++) {
+ fprintf(header, "%5u%s", base_dist[i],
+ SEPARATOR(i, D_CODES-1, 10));
+ }
+
+ fclose(header);
+}
+#endif /* GEN_TREES_H */
+
+/* ===========================================================================
+ * Initialize the tree data structures for a new zlib stream.
+ */
+void _tr_init(s)
+ deflate_state *s;
+{
+ tr_static_init();
+
+ s->l_desc.dyn_tree = s->dyn_ltree;
+ s->l_desc.stat_desc = &static_l_desc;
+
+ s->d_desc.dyn_tree = s->dyn_dtree;
+ s->d_desc.stat_desc = &static_d_desc;
+
+ s->bl_desc.dyn_tree = s->bl_tree;
+ s->bl_desc.stat_desc = &static_bl_desc;
+
+ s->bi_buf = 0;
+ s->bi_valid = 0;
+ s->last_eob_len = 8; /* enough lookahead for inflate */
+#ifdef DEBUG
+ s->compressed_len = 0L;
+ s->bits_sent = 0L;
+#endif
+
+ /* Initialize the first block of the first file: */
+ init_block(s);
+}
+
+/* ===========================================================================
+ * Initialize a new block.
+ */
+local void init_block(s)
+ deflate_state *s;
+{
+ int n; /* iterates over tree elements */
+
+ /* Initialize the trees. */
+ for (n = 0; n < L_CODES; n++) s->dyn_ltree[n].Freq = 0;
+ for (n = 0; n < D_CODES; n++) s->dyn_dtree[n].Freq = 0;
+ for (n = 0; n < BL_CODES; n++) s->bl_tree[n].Freq = 0;
+
+ s->dyn_ltree[END_BLOCK].Freq = 1;
+ s->opt_len = s->static_len = 0L;
+ s->last_lit = s->matches = 0;
+}
+
+#define SMALLEST 1
+/* Index within the heap array of least frequent node in the Huffman tree */
+
+
+/* ===========================================================================
+ * Remove the smallest element from the heap and recreate the heap with
+ * one less element. Updates heap and heap_len.
+ */
+#define pqremove(s, tree, top) \
+{\
+ top = s->heap[SMALLEST]; \
+ s->heap[SMALLEST] = s->heap[s->heap_len--]; \
+ pqdownheap(s, tree, SMALLEST); \
+}
+
+/* ===========================================================================
+ * Compares to subtrees, using the tree depth as tie breaker when
+ * the subtrees have equal frequency. This minimizes the worst case length.
+ */
+#define smaller(tree, n, m, depth) \
+ (tree[n].Freq < tree[m].Freq || \
+ (tree[n].Freq == tree[m].Freq && depth[n] <= depth[m]))
+
+/* ===========================================================================
+ * Restore the heap property by moving down the tree starting at node k,
+ * exchanging a node with the smallest of its two sons if necessary, stopping
+ * when the heap property is re-established (each father smaller than its
+ * two sons).
+ */
+local void pqdownheap(s, tree, k)
+ deflate_state *s;
+ ct_data *tree; /* the tree to restore */
+ int k; /* node to move down */
+{
+ int v = s->heap[k];
+ int j = k << 1; /* left son of k */
+ while (j <= s->heap_len) {
+ /* Set j to the smallest of the two sons: */
+ if (j < s->heap_len &&
+ smaller(tree, s->heap[j+1], s->heap[j], s->depth)) {
+ j++;
+ }
+ /* Exit if v is smaller than both sons */
+ if (smaller(tree, v, s->heap[j], s->depth)) break;
+
+ /* Exchange v with the smallest son */
+ s->heap[k] = s->heap[j]; k = j;
+
+ /* And continue down the tree, setting j to the left son of k */
+ j <<= 1;
+ }
+ s->heap[k] = v;
+}
+
+/* ===========================================================================
+ * Compute the optimal bit lengths for a tree and update the total bit length
+ * for the current block.
+ * IN assertion: the fields freq and dad are set, heap[heap_max] and
+ * above are the tree nodes sorted by increasing frequency.
+ * OUT assertions: the field len is set to the optimal bit length, the
+ * array bl_count contains the frequencies for each bit length.
+ * The length opt_len is updated; static_len is also updated if stree is
+ * not null.
+ */
+local void gen_bitlen(s, desc)
+ deflate_state *s;
+ tree_desc *desc; /* the tree descriptor */
+{
+ ct_data *tree = desc->dyn_tree;
+ int max_code = desc->max_code;
+ const ct_data *stree = desc->stat_desc->static_tree;
+ const intf *extra = desc->stat_desc->extra_bits;
+ int base = desc->stat_desc->extra_base;
+ int max_length = desc->stat_desc->max_length;
+ int h; /* heap index */
+ int n, m; /* iterate over the tree elements */
+ int bits; /* bit length */
+ int xbits; /* extra bits */
+ ush f; /* frequency */
+ int overflow = 0; /* number of elements with bit length too large */
+
+ for (bits = 0; bits <= MAX_BITS; bits++) s->bl_count[bits] = 0;
+
+ /* In a first pass, compute the optimal bit lengths (which may
+ * overflow in the case of the bit length tree).
+ */
+ tree[s->heap[s->heap_max]].Len = 0; /* root of the heap */
+
+ for (h = s->heap_max+1; h < HEAP_SIZE; h++) {
+ n = s->heap[h];
+ bits = tree[tree[n].Dad].Len + 1;
+ if (bits > max_length) bits = max_length, overflow++;
+ tree[n].Len = (ush)bits;
+ /* We overwrite tree[n].Dad which is no longer needed */
+
+ if (n > max_code) continue; /* not a leaf node */
+
+ s->bl_count[bits]++;
+ xbits = 0;
+ if (n >= base) xbits = extra[n-base];
+ f = tree[n].Freq;
+ s->opt_len += (ulg)f * (bits + xbits);
+ if (stree) s->static_len += (ulg)f * (stree[n].Len + xbits);
+ }
+ if (overflow == 0) return;
+
+ Trace((stderr,"\nbit length overflow\n"));
+ /* This happens for example on obj2 and pic of the Calgary corpus */
+
+ /* Find the first bit length which could increase: */
+ do {
+ bits = max_length-1;
+ while (s->bl_count[bits] == 0) bits--;
+ s->bl_count[bits]--; /* move one leaf down the tree */
+ s->bl_count[bits+1] += 2; /* move one overflow item as its brother */
+ s->bl_count[max_length]--;
+ /* The brother of the overflow item also moves one step up,
+ * but this does not affect bl_count[max_length]
+ */
+ overflow -= 2;
+ } while (overflow > 0);
+
+ /* Now recompute all bit lengths, scanning in increasing frequency.
+ * h is still equal to HEAP_SIZE. (It is simpler to reconstruct all
+ * lengths instead of fixing only the wrong ones. This idea is taken
+ * from 'ar' written by Haruhiko Okumura.)
+ */
+ for (bits = max_length; bits != 0; bits--) {
+ n = s->bl_count[bits];
+ while (n != 0) {
+ m = s->heap[--h];
+ if (m > max_code) continue;
+ if (tree[m].Len != (unsigned) bits) {
+ Trace((stderr,"code %d bits %d->%d\n", m, tree[m].Len, bits));
+ s->opt_len += ((long)bits - (long)tree[m].Len)
+ *(long)tree[m].Freq;
+ tree[m].Len = (ush)bits;
+ }
+ n--;
+ }
+ }
+}
+
+/* ===========================================================================
+ * Generate the codes for a given tree and bit counts (which need not be
+ * optimal).
+ * IN assertion: the array bl_count contains the bit length statistics for
+ * the given tree and the field len is set for all tree elements.
+ * OUT assertion: the field code is set for all tree elements of non
+ * zero code length.
+ */
+local void gen_codes (tree, max_code, bl_count)
+ ct_data *tree; /* the tree to decorate */
+ int max_code; /* largest code with non zero frequency */
+ ushf *bl_count; /* number of codes at each bit length */
+{
+ ush next_code[MAX_BITS+1]; /* next code value for each bit length */
+ ush code = 0; /* running code value */
+ int bits; /* bit index */
+ int n; /* code index */
+
+ /* The distribution counts are first used to generate the code values
+ * without bit reversal.
+ */
+ for (bits = 1; bits <= MAX_BITS; bits++) {
+ next_code[bits] = code = (code + bl_count[bits-1]) << 1;
+ }
+ /* Check that the bit counts in bl_count are consistent. The last code
+ * must be all ones.
+ */
+ Assert (code + bl_count[MAX_BITS]-1 == (1<<MAX_BITS)-1,
+ "inconsistent bit counts");
+ Tracev((stderr,"\ngen_codes: max_code %d ", max_code));
+
+ for (n = 0; n <= max_code; n++) {
+ int len = tree[n].Len;
+ if (len == 0) continue;
+ /* Now reverse the bits */
+ tree[n].Code = bi_reverse(next_code[len]++, len);
+
+ Tracecv(tree != static_ltree, (stderr,"\nn %3d %c l %2d c %4x (%x) ",
+ n, (isgraph(n) ? n : ' '), len, tree[n].Code, next_code[len]-1));
+ }
+}
+
+/* ===========================================================================
+ * Construct one Huffman tree and assigns the code bit strings and lengths.
+ * Update the total bit length for the current block.
+ * IN assertion: the field freq is set for all tree elements.
+ * OUT assertions: the fields len and code are set to the optimal bit length
+ * and corresponding code. The length opt_len is updated; static_len is
+ * also updated if stree is not null. The field max_code is set.
+ */
+local void build_tree(s, desc)
+ deflate_state *s;
+ tree_desc *desc; /* the tree descriptor */
+{
+ ct_data *tree = desc->dyn_tree;
+ const ct_data *stree = desc->stat_desc->static_tree;
+ int elems = desc->stat_desc->elems;
+ int n, m; /* iterate over heap elements */
+ int max_code = -1; /* largest code with non zero frequency */
+ int node; /* new node being created */
+
+ /* Construct the initial heap, with least frequent element in
+ * heap[SMALLEST]. The sons of heap[n] are heap[2*n] and heap[2*n+1].
+ * heap[0] is not used.
+ */
+ s->heap_len = 0, s->heap_max = HEAP_SIZE;
+
+ for (n = 0; n < elems; n++) {
+ if (tree[n].Freq != 0) {
+ s->heap[++(s->heap_len)] = max_code = n;
+ s->depth[n] = 0;
+ } else {
+ tree[n].Len = 0;
+ }
+ }
+
+ /* The pkzip format requires that at least one distance code exists,
+ * and that at least one bit should be sent even if there is only one
+ * possible code. So to avoid special checks later on we force at least
+ * two codes of non zero frequency.
+ */
+ while (s->heap_len < 2) {
+ node = s->heap[++(s->heap_len)] = (max_code < 2 ? ++max_code : 0);
+ tree[node].Freq = 1;
+ s->depth[node] = 0;
+ s->opt_len--; if (stree) s->static_len -= stree[node].Len;
+ /* node is 0 or 1 so it does not have extra bits */
+ }
+ desc->max_code = max_code;
+
+ /* The elements heap[heap_len/2+1 .. heap_len] are leaves of the tree,
+ * establish sub-heaps of increasing lengths:
+ */
+ for (n = s->heap_len/2; n >= 1; n--) pqdownheap(s, tree, n);
+
+ /* Construct the Huffman tree by repeatedly combining the least two
+ * frequent nodes.
+ */
+ node = elems; /* next internal node of the tree */
+ do {
+ pqremove(s, tree, n); /* n = node of least frequency */
+ m = s->heap[SMALLEST]; /* m = node of next least frequency */
+
+ s->heap[--(s->heap_max)] = n; /* keep the nodes sorted by frequency */
+ s->heap[--(s->heap_max)] = m;
+
+ /* Create a new node father of n and m */
+ tree[node].Freq = tree[n].Freq + tree[m].Freq;
+ s->depth[node] = (uch) (MAX(s->depth[n], s->depth[m]) + 1);
+ tree[n].Dad = tree[m].Dad = (ush)node;
+#ifdef DUMP_BL_TREE
+ if (tree == s->bl_tree) {
+ fprintf(stderr,"\nnode %d(%d), sons %d(%d) %d(%d)",
+ node, tree[node].Freq, n, tree[n].Freq, m, tree[m].Freq);
+ }
+#endif
+ /* and insert the new node in the heap */
+ s->heap[SMALLEST] = node++;
+ pqdownheap(s, tree, SMALLEST);
+
+ } while (s->heap_len >= 2);
+
+ s->heap[--(s->heap_max)] = s->heap[SMALLEST];
+
+ /* At this point, the fields freq and dad are set. We can now
+ * generate the bit lengths.
+ */
+ gen_bitlen(s, (tree_desc *)desc);
+
+ /* The field len is now set, we can generate the bit codes */
+ gen_codes ((ct_data *)tree, max_code, s->bl_count);
+}
+
+/* ===========================================================================
+ * Scan a literal or distance tree to determine the frequencies of the codes
+ * in the bit length tree.
+ */
+local void scan_tree (s, tree, max_code)
+ deflate_state *s;
+ ct_data *tree; /* the tree to be scanned */
+ int max_code; /* and its largest code of non zero frequency */
+{
+ int n; /* iterates over all tree elements */
+ int prevlen = -1; /* last emitted length */
+ int curlen; /* length of current code */
+ int nextlen = tree[0].Len; /* length of next code */
+ int count = 0; /* repeat count of the current code */
+ int max_count = 7; /* max repeat count */
+ int min_count = 4; /* min repeat count */
+
+ if (nextlen == 0) max_count = 138, min_count = 3;
+ tree[max_code+1].Len = (ush)0xffff; /* guard */
+
+ for (n = 0; n <= max_code; n++) {
+ curlen = nextlen; nextlen = tree[n+1].Len;
+ if (++count < max_count && curlen == nextlen) {
+ continue;
+ } else if (count < min_count) {
+ s->bl_tree[curlen].Freq += count;
+ } else if (curlen != 0) {
+ if (curlen != prevlen) s->bl_tree[curlen].Freq++;
+ s->bl_tree[REP_3_6].Freq++;
+ } else if (count <= 10) {
+ s->bl_tree[REPZ_3_10].Freq++;
+ } else {
+ s->bl_tree[REPZ_11_138].Freq++;
+ }
+ count = 0; prevlen = curlen;
+ if (nextlen == 0) {
+ max_count = 138, min_count = 3;
+ } else if (curlen == nextlen) {
+ max_count = 6, min_count = 3;
+ } else {
+ max_count = 7, min_count = 4;
+ }
+ }
+}
+
+/* ===========================================================================
+ * Send a literal or distance tree in compressed form, using the codes in
+ * bl_tree.
+ */
+local void send_tree (s, tree, max_code)
+ deflate_state *s;
+ ct_data *tree; /* the tree to be scanned */
+ int max_code; /* and its largest code of non zero frequency */
+{
+ int n; /* iterates over all tree elements */
+ int prevlen = -1; /* last emitted length */
+ int curlen; /* length of current code */
+ int nextlen = tree[0].Len; /* length of next code */
+ int count = 0; /* repeat count of the current code */
+ int max_count = 7; /* max repeat count */
+ int min_count = 4; /* min repeat count */
+
+ /* tree[max_code+1].Len = -1; */ /* guard already set */
+ if (nextlen == 0) max_count = 138, min_count = 3;
+
+ for (n = 0; n <= max_code; n++) {
+ curlen = nextlen; nextlen = tree[n+1].Len;
+ if (++count < max_count && curlen == nextlen) {
+ continue;
+ } else if (count < min_count) {
+ do { send_code(s, curlen, s->bl_tree); } while (--count != 0);
+
+ } else if (curlen != 0) {
+ if (curlen != prevlen) {
+ send_code(s, curlen, s->bl_tree); count--;
+ }
+ Assert(count >= 3 && count <= 6, " 3_6?");
+ send_code(s, REP_3_6, s->bl_tree); send_bits(s, count-3, 2);
+
+ } else if (count <= 10) {
+ send_code(s, REPZ_3_10, s->bl_tree); send_bits(s, count-3, 3);
+
+ } else {
+ send_code(s, REPZ_11_138, s->bl_tree); send_bits(s, count-11, 7);
+ }
+ count = 0; prevlen = curlen;
+ if (nextlen == 0) {
+ max_count = 138, min_count = 3;
+ } else if (curlen == nextlen) {
+ max_count = 6, min_count = 3;
+ } else {
+ max_count = 7, min_count = 4;
+ }
+ }
+}
+
+/* ===========================================================================
+ * Construct the Huffman tree for the bit lengths and return the index in
+ * bl_order of the last bit length code to send.
+ */
+local int build_bl_tree(s)
+ deflate_state *s;
+{
+ int max_blindex; /* index of last bit length code of non zero freq */
+
+ /* Determine the bit length frequencies for literal and distance trees */
+ scan_tree(s, (ct_data *)s->dyn_ltree, s->l_desc.max_code);
+ scan_tree(s, (ct_data *)s->dyn_dtree, s->d_desc.max_code);
+
+ /* Build the bit length tree: */
+ build_tree(s, (tree_desc *)(&(s->bl_desc)));
+ /* opt_len now includes the length of the tree representations, except
+ * the lengths of the bit lengths codes and the 5+5+4 bits for the counts.
+ */
+
+ /* Determine the number of bit length codes to send. The pkzip format
+ * requires that at least 4 bit length codes be sent. (appnote.txt says
+ * 3 but the actual value used is 4.)
+ */
+ for (max_blindex = BL_CODES-1; max_blindex >= 3; max_blindex--) {
+ if (s->bl_tree[bl_order[max_blindex]].Len != 0) break;
+ }
+ /* Update opt_len to include the bit length tree and counts */
+ s->opt_len += 3*(max_blindex+1) + 5+5+4;
+ Tracev((stderr, "\ndyn trees: dyn %ld, stat %ld",
+ s->opt_len, s->static_len));
+
+ return max_blindex;
+}
+
+/* ===========================================================================
+ * Send the header for a block using dynamic Huffman trees: the counts, the
+ * lengths of the bit length codes, the literal tree and the distance tree.
+ * IN assertion: lcodes >= 257, dcodes >= 1, blcodes >= 4.
+ */
+local void send_all_trees(s, lcodes, dcodes, blcodes)
+ deflate_state *s;
+ int lcodes, dcodes, blcodes; /* number of codes for each tree */
+{
+ int rank; /* index in bl_order */
+
+ Assert (lcodes >= 257 && dcodes >= 1 && blcodes >= 4, "not enough codes");
+ Assert (lcodes <= L_CODES && dcodes <= D_CODES && blcodes <= BL_CODES,
+ "too many codes");
+ Tracev((stderr, "\nbl counts: "));
+ send_bits(s, lcodes-257, 5); /* not +255 as stated in appnote.txt */
+ send_bits(s, dcodes-1, 5);
+ send_bits(s, blcodes-4, 4); /* not -3 as stated in appnote.txt */
+ for (rank = 0; rank < blcodes; rank++) {
+ Tracev((stderr, "\nbl code %2d ", bl_order[rank]));
+ send_bits(s, s->bl_tree[bl_order[rank]].Len, 3);
+ }
+ Tracev((stderr, "\nbl tree: sent %ld", s->bits_sent));
+
+ send_tree(s, (ct_data *)s->dyn_ltree, lcodes-1); /* literal tree */
+ Tracev((stderr, "\nlit tree: sent %ld", s->bits_sent));
+
+ send_tree(s, (ct_data *)s->dyn_dtree, dcodes-1); /* distance tree */
+ Tracev((stderr, "\ndist tree: sent %ld", s->bits_sent));
+}
+
+/* ===========================================================================
+ * Send a stored block
+ */
+void _tr_stored_block(s, buf, stored_len, eof)
+ deflate_state *s;
+ charf *buf; /* input block */
+ ulg stored_len; /* length of input block */
+ int eof; /* true if this is the last block for a file */
+{
+ send_bits(s, (STORED_BLOCK<<1)+eof, 3); /* send block type */
+#ifdef DEBUG
+ s->compressed_len = (s->compressed_len + 3 + 7) & (ulg)~7L;
+ s->compressed_len += (stored_len + 4) << 3;
+#endif
+ copy_block(s, buf, (unsigned)stored_len, 1); /* with header */
+}
+
+/* ===========================================================================
+ * Send one empty static block to give enough lookahead for inflate.
+ * This takes 10 bits, of which 7 may remain in the bit buffer.
+ * The current inflate code requires 9 bits of lookahead. If the
+ * last two codes for the previous block (real code plus EOB) were coded
+ * on 5 bits or less, inflate may have only 5+3 bits of lookahead to decode
+ * the last real code. In this case we send two empty static blocks instead
+ * of one. (There are no problems if the previous block is stored or fixed.)
+ * To simplify the code, we assume the worst case of last real code encoded
+ * on one bit only.
+ */
+void _tr_align(s)
+ deflate_state *s;
+{
+ send_bits(s, STATIC_TREES<<1, 3);
+ send_code(s, END_BLOCK, static_ltree);
+#ifdef DEBUG
+ s->compressed_len += 10L; /* 3 for block type, 7 for EOB */
+#endif
+ bi_flush(s);
+ /* Of the 10 bits for the empty block, we have already sent
+ * (10 - bi_valid) bits. The lookahead for the last real code (before
+ * the EOB of the previous block) was thus at least one plus the length
+ * of the EOB plus what we have just sent of the empty static block.
+ */
+ if (1 + s->last_eob_len + 10 - s->bi_valid < 9) {
+ send_bits(s, STATIC_TREES<<1, 3);
+ send_code(s, END_BLOCK, static_ltree);
+#ifdef DEBUG
+ s->compressed_len += 10L;
+#endif
+ bi_flush(s);
+ }
+ s->last_eob_len = 7;
+}
+
+/* ===========================================================================
+ * Determine the best encoding for the current block: dynamic trees, static
+ * trees or store, and output the encoded block to the zip file.
+ */
+void _tr_flush_block(s, buf, stored_len, eof)
+ deflate_state *s;
+ charf *buf; /* input block, or NULL if too old */
+ ulg stored_len; /* length of input block */
+ int eof; /* true if this is the last block for a file */
+{
+ ulg opt_lenb, static_lenb; /* opt_len and static_len in bytes */
+ int max_blindex = 0; /* index of last bit length code of non zero freq */
+
+ /* Build the Huffman trees unless a stored block is forced */
+ if (s->level > 0) {
+
+ /* Check if the file is ascii or binary */
+ if (s->data_type == Z_UNKNOWN) set_data_type(s);
+
+ /* Construct the literal and distance trees */
+ build_tree(s, (tree_desc *)(&(s->l_desc)));
+ Tracev((stderr, "\nlit data: dyn %ld, stat %ld", s->opt_len,
+ s->static_len));
+
+ build_tree(s, (tree_desc *)(&(s->d_desc)));
+ Tracev((stderr, "\ndist data: dyn %ld, stat %ld", s->opt_len,
+ s->static_len));
+ /* At this point, opt_len and static_len are the total bit lengths of
+ * the compressed block data, excluding the tree representations.
+ */
+
+ /* Build the bit length tree for the above two trees, and get the index
+ * in bl_order of the last bit length code to send.
+ */
+ max_blindex = build_bl_tree(s);
+
+ /* Determine the best encoding. Compute first the block length in bytes*/
+ opt_lenb = (s->opt_len+3+7)>>3;
+ static_lenb = (s->static_len+3+7)>>3;
+
+ Tracev((stderr, "\nopt %lu(%lu) stat %lu(%lu) stored %lu lit %u ",
+ opt_lenb, s->opt_len, static_lenb, s->static_len, stored_len,
+ s->last_lit));
+
+ if (static_lenb <= opt_lenb) opt_lenb = static_lenb;
+
+ } else {
+ Assert(buf != (char*)0, "lost buf");
+ opt_lenb = static_lenb = stored_len + 5; /* force a stored block */
+ }
+
+#ifdef FORCE_STORED
+ if (buf != (char*)0) { /* force stored block */
+#else
+ if (stored_len+4 <= opt_lenb && buf != (char*)0) {
+ /* 4: two words for the lengths */
+#endif
+ /* The test buf != NULL is only necessary if LIT_BUFSIZE > WSIZE.
+ * Otherwise we can't have processed more than WSIZE input bytes since
+ * the last block flush, because compression would have been
+ * successful. If LIT_BUFSIZE <= WSIZE, it is never too late to
+ * transform a block into a stored block.
+ */
+ _tr_stored_block(s, buf, stored_len, eof);
+
+#ifdef FORCE_STATIC
+ } else if (static_lenb >= 0) { /* force static trees */
+#else
+ } else if (static_lenb == opt_lenb) {
+#endif
+ send_bits(s, (STATIC_TREES<<1)+eof, 3);
+ compress_block(s, (ct_data *)static_ltree, (ct_data *)static_dtree);
+#ifdef DEBUG
+ s->compressed_len += 3 + s->static_len;
+#endif
+ } else {
+ send_bits(s, (DYN_TREES<<1)+eof, 3);
+ send_all_trees(s, s->l_desc.max_code+1, s->d_desc.max_code+1,
+ max_blindex+1);
+ compress_block(s, (ct_data *)s->dyn_ltree, (ct_data *)s->dyn_dtree);
+#ifdef DEBUG
+ s->compressed_len += 3 + s->opt_len;
+#endif
+ }
+ Assert (s->compressed_len == s->bits_sent, "bad compressed size");
+ /* The above check is made mod 2^32, for files larger than 512 MB
+ * and uLong implemented on 32 bits.
+ */
+ init_block(s);
+
+ if (eof) {
+ bi_windup(s);
+#ifdef DEBUG
+ s->compressed_len += 7; /* align on byte boundary */
+#endif
+ }
+ Tracev((stderr,"\ncomprlen %lu(%lu) ", s->compressed_len>>3,
+ s->compressed_len-7*eof));
+}
+
+/* ===========================================================================
+ * Save the match info and tally the frequency counts. Return true if
+ * the current block must be flushed.
+ */
+int _tr_tally (s, dist, lc)
+ deflate_state *s;
+ unsigned dist; /* distance of matched string */
+ unsigned lc; /* match length-MIN_MATCH or unmatched char (if dist==0) */
+{
+ s->d_buf[s->last_lit] = (ush)dist;
+ s->l_buf[s->last_lit++] = (uch)lc;
+ if (dist == 0) {
+ /* lc is the unmatched char */
+ s->dyn_ltree[lc].Freq++;
+ } else {
+ s->matches++;
+ /* Here, lc is the match length - MIN_MATCH */
+ dist--; /* dist = match distance - 1 */
+ Assert((ush)dist < (ush)MAX_DIST(s) &&
+ (ush)lc <= (ush)(MAX_MATCH-MIN_MATCH) &&
+ (ush)d_code(dist) < (ush)D_CODES, "_tr_tally: bad match");
+
+ s->dyn_ltree[_length_code[lc]+LITERALS+1].Freq++;
+ s->dyn_dtree[d_code(dist)].Freq++;
+ }
+
+#ifdef TRUNCATE_BLOCK
+ /* Try to guess if it is profitable to stop the current block here */
+ if ((s->last_lit & 0x1fff) == 0 && s->level > 2) {
+ /* Compute an upper bound for the compressed length */
+ ulg out_length = (ulg)s->last_lit*8L;
+ ulg in_length = (ulg)((long)s->strstart - s->block_start);
+ int dcode;
+ for (dcode = 0; dcode < D_CODES; dcode++) {
+ out_length += (ulg)s->dyn_dtree[dcode].Freq *
+ (5L+extra_dbits[dcode]);
+ }
+ out_length >>= 3;
+ Tracev((stderr,"\nlast_lit %u, in %ld, out ~%ld(%ld%%) ",
+ s->last_lit, in_length, out_length,
+ 100L - out_length*100L/in_length));
+ if (s->matches < s->last_lit/2 && out_length < in_length/2) return 1;
+ }
+#endif
+ return (s->last_lit == s->lit_bufsize-1);
+ /* We avoid equality with lit_bufsize because of wraparound at 64K
+ * on 16 bit machines and because stored blocks are restricted to
+ * 64K-1 bytes.
+ */
+}
+
+/* ===========================================================================
+ * Send the block data compressed using the given Huffman trees
+ */
+local void compress_block(s, ltree, dtree)
+ deflate_state *s;
+ ct_data *ltree; /* literal tree */
+ ct_data *dtree; /* distance tree */
+{
+ unsigned dist; /* distance of matched string */
+ int lc; /* match length or unmatched char (if dist == 0) */
+ unsigned lx = 0; /* running index in l_buf */
+ unsigned code; /* the code to send */
+ int extra; /* number of extra bits to send */
+
+ if (s->last_lit != 0) do {
+ dist = s->d_buf[lx];
+ lc = s->l_buf[lx++];
+ if (dist == 0) {
+ send_code(s, lc, ltree); /* send a literal byte */
+ Tracecv(isgraph(lc), (stderr," '%c' ", lc));
+ } else {
+ /* Here, lc is the match length - MIN_MATCH */
+ code = _length_code[lc];
+ send_code(s, code+LITERALS+1, ltree); /* send the length code */
+ extra = extra_lbits[code];
+ if (extra != 0) {
+ lc -= base_length[code];
+ send_bits(s, lc, extra); /* send the extra length bits */
+ }
+ dist--; /* dist is now the match distance - 1 */
+ code = d_code(dist);
+ Assert (code < D_CODES, "bad d_code");
+
+ send_code(s, code, dtree); /* send the distance code */
+ extra = extra_dbits[code];
+ if (extra != 0) {
+ dist -= base_dist[code];
+ send_bits(s, dist, extra); /* send the extra distance bits */
+ }
+ } /* literal or match pair ? */
+
+ /* Check that the overlay between pending_buf and d_buf+l_buf is ok: */
+ Assert(s->pending < s->lit_bufsize + 2*lx, "pendingBuf overflow");
+
+ } while (lx < s->last_lit);
+
+ send_code(s, END_BLOCK, ltree);
+ s->last_eob_len = ltree[END_BLOCK].Len;
+}
+
+/* ===========================================================================
+ * Set the data type to ASCII or BINARY, using a crude approximation:
+ * binary if more than 20% of the bytes are <= 6 or >= 128, ascii otherwise.
+ * IN assertion: the fields freq of dyn_ltree are set and the total of all
+ * frequencies does not exceed 64K (to fit in an int on 16 bit machines).
+ */
+local void set_data_type(s)
+ deflate_state *s;
+{
+ int n = 0;
+ unsigned ascii_freq = 0;
+ unsigned bin_freq = 0;
+ while (n < 7) bin_freq += s->dyn_ltree[n++].Freq;
+ while (n < 128) ascii_freq += s->dyn_ltree[n++].Freq;
+ while (n < LITERALS) bin_freq += s->dyn_ltree[n++].Freq;
+ s->data_type = (Byte)(bin_freq > (ascii_freq >> 2) ? Z_BINARY : Z_ASCII);
+}
+
+/* ===========================================================================
+ * Reverse the first len bits of a code, using straightforward code (a faster
+ * method would use a table)
+ * IN assertion: 1 <= len <= 15
+ */
+local unsigned bi_reverse(code, len)
+ unsigned code; /* the value to invert */
+ int len; /* its bit length */
+{
+ register unsigned res = 0;
+ do {
+ res |= code & 1;
+ code >>= 1, res <<= 1;
+ } while (--len > 0);
+ return res >> 1;
+}
+
+/* ===========================================================================
+ * Flush the bit buffer, keeping at most 7 bits in it.
+ */
+local void bi_flush(s)
+ deflate_state *s;
+{
+ if (s->bi_valid == 16) {
+ put_short(s, s->bi_buf);
+ s->bi_buf = 0;
+ s->bi_valid = 0;
+ } else if (s->bi_valid >= 8) {
+ put_byte(s, (Byte)s->bi_buf);
+ s->bi_buf >>= 8;
+ s->bi_valid -= 8;
+ }
+}
+
+/* ===========================================================================
+ * Flush the bit buffer and align the output on a byte boundary
+ */
+local void bi_windup(s)
+ deflate_state *s;
+{
+ if (s->bi_valid > 8) {
+ put_short(s, s->bi_buf);
+ } else if (s->bi_valid > 0) {
+ put_byte(s, (Byte)s->bi_buf);
+ }
+ s->bi_buf = 0;
+ s->bi_valid = 0;
+#ifdef DEBUG
+ s->bits_sent = (s->bits_sent+7) & ~7;
+#endif
+}
+
+/* ===========================================================================
+ * Copy a stored block, storing first the length and its
+ * one's complement if requested.
+ */
+local void copy_block(s, buf, len, header)
+ deflate_state *s;
+ charf *buf; /* the input data */
+ unsigned len; /* its length */
+ int header; /* true if block header must be written */
+{
+ bi_windup(s); /* align on byte boundary */
+ s->last_eob_len = 8; /* enough lookahead for inflate */
+
+ if (header) {
+ put_short(s, (ush)len);
+ put_short(s, (ush)~len);
+#ifdef DEBUG
+ s->bits_sent += 2*16;
+#endif
+ }
+#ifdef DEBUG
+ s->bits_sent += (ulg)len<<3;
+#endif
+ while (len--) {
+ put_byte(s, *buf++);
+ }
+}
diff --git a/zlib/trees.h b/zlib/trees.h
new file mode 100644
index 00000000..72facf90
--- /dev/null
+++ b/zlib/trees.h
@@ -0,0 +1,128 @@
+/* header created automatically with -DGEN_TREES_H */
+
+local const ct_data static_ltree[L_CODES+2] = {
+{{ 12},{ 8}}, {{140},{ 8}}, {{ 76},{ 8}}, {{204},{ 8}}, {{ 44},{ 8}},
+{{172},{ 8}}, {{108},{ 8}}, {{236},{ 8}}, {{ 28},{ 8}}, {{156},{ 8}},
+{{ 92},{ 8}}, {{220},{ 8}}, {{ 60},{ 8}}, {{188},{ 8}}, {{124},{ 8}},
+{{252},{ 8}}, {{ 2},{ 8}}, {{130},{ 8}}, {{ 66},{ 8}}, {{194},{ 8}},
+{{ 34},{ 8}}, {{162},{ 8}}, {{ 98},{ 8}}, {{226},{ 8}}, {{ 18},{ 8}},
+{{146},{ 8}}, {{ 82},{ 8}}, {{210},{ 8}}, {{ 50},{ 8}}, {{178},{ 8}},
+{{114},{ 8}}, {{242},{ 8}}, {{ 10},{ 8}}, {{138},{ 8}}, {{ 74},{ 8}},
+{{202},{ 8}}, {{ 42},{ 8}}, {{170},{ 8}}, {{106},{ 8}}, {{234},{ 8}},
+{{ 26},{ 8}}, {{154},{ 8}}, {{ 90},{ 8}}, {{218},{ 8}}, {{ 58},{ 8}},
+{{186},{ 8}}, {{122},{ 8}}, {{250},{ 8}}, {{ 6},{ 8}}, {{134},{ 8}},
+{{ 70},{ 8}}, {{198},{ 8}}, {{ 38},{ 8}}, {{166},{ 8}}, {{102},{ 8}},
+{{230},{ 8}}, {{ 22},{ 8}}, {{150},{ 8}}, {{ 86},{ 8}}, {{214},{ 8}},
+{{ 54},{ 8}}, {{182},{ 8}}, {{118},{ 8}}, {{246},{ 8}}, {{ 14},{ 8}},
+{{142},{ 8}}, {{ 78},{ 8}}, {{206},{ 8}}, {{ 46},{ 8}}, {{174},{ 8}},
+{{110},{ 8}}, {{238},{ 8}}, {{ 30},{ 8}}, {{158},{ 8}}, {{ 94},{ 8}},
+{{222},{ 8}}, {{ 62},{ 8}}, {{190},{ 8}}, {{126},{ 8}}, {{254},{ 8}},
+{{ 1},{ 8}}, {{129},{ 8}}, {{ 65},{ 8}}, {{193},{ 8}}, {{ 33},{ 8}},
+{{161},{ 8}}, {{ 97},{ 8}}, {{225},{ 8}}, {{ 17},{ 8}}, {{145},{ 8}},
+{{ 81},{ 8}}, {{209},{ 8}}, {{ 49},{ 8}}, {{177},{ 8}}, {{113},{ 8}},
+{{241},{ 8}}, {{ 9},{ 8}}, {{137},{ 8}}, {{ 73},{ 8}}, {{201},{ 8}},
+{{ 41},{ 8}}, {{169},{ 8}}, {{105},{ 8}}, {{233},{ 8}}, {{ 25},{ 8}},
+{{153},{ 8}}, {{ 89},{ 8}}, {{217},{ 8}}, {{ 57},{ 8}}, {{185},{ 8}},
+{{121},{ 8}}, {{249},{ 8}}, {{ 5},{ 8}}, {{133},{ 8}}, {{ 69},{ 8}},
+{{197},{ 8}}, {{ 37},{ 8}}, {{165},{ 8}}, {{101},{ 8}}, {{229},{ 8}},
+{{ 21},{ 8}}, {{149},{ 8}}, {{ 85},{ 8}}, {{213},{ 8}}, {{ 53},{ 8}},
+{{181},{ 8}}, {{117},{ 8}}, {{245},{ 8}}, {{ 13},{ 8}}, {{141},{ 8}},
+{{ 77},{ 8}}, {{205},{ 8}}, {{ 45},{ 8}}, {{173},{ 8}}, {{109},{ 8}},
+{{237},{ 8}}, {{ 29},{ 8}}, {{157},{ 8}}, {{ 93},{ 8}}, {{221},{ 8}},
+{{ 61},{ 8}}, {{189},{ 8}}, {{125},{ 8}}, {{253},{ 8}}, {{ 19},{ 9}},
+{{275},{ 9}}, {{147},{ 9}}, {{403},{ 9}}, {{ 83},{ 9}}, {{339},{ 9}},
+{{211},{ 9}}, {{467},{ 9}}, {{ 51},{ 9}}, {{307},{ 9}}, {{179},{ 9}},
+{{435},{ 9}}, {{115},{ 9}}, {{371},{ 9}}, {{243},{ 9}}, {{499},{ 9}},
+{{ 11},{ 9}}, {{267},{ 9}}, {{139},{ 9}}, {{395},{ 9}}, {{ 75},{ 9}},
+{{331},{ 9}}, {{203},{ 9}}, {{459},{ 9}}, {{ 43},{ 9}}, {{299},{ 9}},
+{{171},{ 9}}, {{427},{ 9}}, {{107},{ 9}}, {{363},{ 9}}, {{235},{ 9}},
+{{491},{ 9}}, {{ 27},{ 9}}, {{283},{ 9}}, {{155},{ 9}}, {{411},{ 9}},
+{{ 91},{ 9}}, {{347},{ 9}}, {{219},{ 9}}, {{475},{ 9}}, {{ 59},{ 9}},
+{{315},{ 9}}, {{187},{ 9}}, {{443},{ 9}}, {{123},{ 9}}, {{379},{ 9}},
+{{251},{ 9}}, {{507},{ 9}}, {{ 7},{ 9}}, {{263},{ 9}}, {{135},{ 9}},
+{{391},{ 9}}, {{ 71},{ 9}}, {{327},{ 9}}, {{199},{ 9}}, {{455},{ 9}},
+{{ 39},{ 9}}, {{295},{ 9}}, {{167},{ 9}}, {{423},{ 9}}, {{103},{ 9}},
+{{359},{ 9}}, {{231},{ 9}}, {{487},{ 9}}, {{ 23},{ 9}}, {{279},{ 9}},
+{{151},{ 9}}, {{407},{ 9}}, {{ 87},{ 9}}, {{343},{ 9}}, {{215},{ 9}},
+{{471},{ 9}}, {{ 55},{ 9}}, {{311},{ 9}}, {{183},{ 9}}, {{439},{ 9}},
+{{119},{ 9}}, {{375},{ 9}}, {{247},{ 9}}, {{503},{ 9}}, {{ 15},{ 9}},
+{{271},{ 9}}, {{143},{ 9}}, {{399},{ 9}}, {{ 79},{ 9}}, {{335},{ 9}},
+{{207},{ 9}}, {{463},{ 9}}, {{ 47},{ 9}}, {{303},{ 9}}, {{175},{ 9}},
+{{431},{ 9}}, {{111},{ 9}}, {{367},{ 9}}, {{239},{ 9}}, {{495},{ 9}},
+{{ 31},{ 9}}, {{287},{ 9}}, {{159},{ 9}}, {{415},{ 9}}, {{ 95},{ 9}},
+{{351},{ 9}}, {{223},{ 9}}, {{479},{ 9}}, {{ 63},{ 9}}, {{319},{ 9}},
+{{191},{ 9}}, {{447},{ 9}}, {{127},{ 9}}, {{383},{ 9}}, {{255},{ 9}},
+{{511},{ 9}}, {{ 0},{ 7}}, {{ 64},{ 7}}, {{ 32},{ 7}}, {{ 96},{ 7}},
+{{ 16},{ 7}}, {{ 80},{ 7}}, {{ 48},{ 7}}, {{112},{ 7}}, {{ 8},{ 7}},
+{{ 72},{ 7}}, {{ 40},{ 7}}, {{104},{ 7}}, {{ 24},{ 7}}, {{ 88},{ 7}},
+{{ 56},{ 7}}, {{120},{ 7}}, {{ 4},{ 7}}, {{ 68},{ 7}}, {{ 36},{ 7}},
+{{100},{ 7}}, {{ 20},{ 7}}, {{ 84},{ 7}}, {{ 52},{ 7}}, {{116},{ 7}},
+{{ 3},{ 8}}, {{131},{ 8}}, {{ 67},{ 8}}, {{195},{ 8}}, {{ 35},{ 8}},
+{{163},{ 8}}, {{ 99},{ 8}}, {{227},{ 8}}
+};
+
+local const ct_data static_dtree[D_CODES] = {
+{{ 0},{ 5}}, {{16},{ 5}}, {{ 8},{ 5}}, {{24},{ 5}}, {{ 4},{ 5}},
+{{20},{ 5}}, {{12},{ 5}}, {{28},{ 5}}, {{ 2},{ 5}}, {{18},{ 5}},
+{{10},{ 5}}, {{26},{ 5}}, {{ 6},{ 5}}, {{22},{ 5}}, {{14},{ 5}},
+{{30},{ 5}}, {{ 1},{ 5}}, {{17},{ 5}}, {{ 9},{ 5}}, {{25},{ 5}},
+{{ 5},{ 5}}, {{21},{ 5}}, {{13},{ 5}}, {{29},{ 5}}, {{ 3},{ 5}},
+{{19},{ 5}}, {{11},{ 5}}, {{27},{ 5}}, {{ 7},{ 5}}, {{23},{ 5}}
+};
+
+const uch _dist_code[DIST_CODE_LEN] = {
+ 0, 1, 2, 3, 4, 4, 5, 5, 6, 6, 6, 6, 7, 7, 7, 7, 8, 8, 8, 8,
+ 8, 8, 8, 8, 9, 9, 9, 9, 9, 9, 9, 9, 10, 10, 10, 10, 10, 10, 10, 10,
+10, 10, 10, 10, 10, 10, 10, 10, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11,
+11, 11, 11, 11, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12,
+12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 13, 13, 13, 13,
+13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13,
+13, 13, 13, 13, 13, 13, 13, 13, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14,
+14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14,
+14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14,
+14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 15, 15, 15, 15, 15, 15, 15, 15,
+15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,
+15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,
+15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 0, 0, 16, 17,
+18, 18, 19, 19, 20, 20, 20, 20, 21, 21, 21, 21, 22, 22, 22, 22, 22, 22, 22, 22,
+23, 23, 23, 23, 23, 23, 23, 23, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,
+24, 24, 24, 24, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25,
+26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26,
+26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 27, 27, 27, 27, 27, 27, 27, 27,
+27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27,
+27, 27, 27, 27, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28,
+28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28,
+28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28,
+28, 28, 28, 28, 28, 28, 28, 28, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29,
+29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29,
+29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29,
+29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29
+};
+
+const uch _length_code[MAX_MATCH-MIN_MATCH+1]= {
+ 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 12, 12,
+13, 13, 13, 13, 14, 14, 14, 14, 15, 15, 15, 15, 16, 16, 16, 16, 16, 16, 16, 16,
+17, 17, 17, 17, 17, 17, 17, 17, 18, 18, 18, 18, 18, 18, 18, 18, 19, 19, 19, 19,
+19, 19, 19, 19, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20,
+21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 22, 22, 22, 22,
+22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 23, 23, 23, 23, 23, 23, 23, 23,
+23, 23, 23, 23, 23, 23, 23, 23, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,
+24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,
+25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25,
+25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 26, 26, 26, 26, 26, 26, 26, 26,
+26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26,
+26, 26, 26, 26, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27,
+27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 28
+};
+
+local const int base_length[LENGTH_CODES] = {
+0, 1, 2, 3, 4, 5, 6, 7, 8, 10, 12, 14, 16, 20, 24, 28, 32, 40, 48, 56,
+64, 80, 96, 112, 128, 160, 192, 224, 0
+};
+
+local const int base_dist[D_CODES] = {
+ 0, 1, 2, 3, 4, 6, 8, 12, 16, 24,
+ 32, 48, 64, 96, 128, 192, 256, 384, 512, 768,
+ 1024, 1536, 2048, 3072, 4096, 6144, 8192, 12288, 16384, 24576
+};
+
diff --git a/zlib/uncompr.c b/zlib/uncompr.c
new file mode 100644
index 00000000..d8a4a699
--- /dev/null
+++ b/zlib/uncompr.c
@@ -0,0 +1,58 @@
+/* uncompr.c -- decompress a memory buffer
+ * Copyright (C) 1995-2002 Jean-loup Gailly.
+ * For conditions of distribution and use, see copyright notice in zlib.h
+ */
+
+/* @(#) $Id: uncompr.c,v 1.1 2004/10/08 09:44:26 const_k Exp $ */
+
+#include "zlib.h"
+
+/* ===========================================================================
+ Decompresses the source buffer into the destination buffer. sourceLen is
+ the byte length of the source buffer. Upon entry, destLen is the total
+ size of the destination buffer, which must be large enough to hold the
+ entire uncompressed data. (The size of the uncompressed data must have
+ been saved previously by the compressor and transmitted to the decompressor
+ by some mechanism outside the scope of this compression library.)
+ Upon exit, destLen is the actual size of the compressed buffer.
+ This function can be used to decompress a whole file at once if the
+ input file is mmap'ed.
+
+ uncompress returns Z_OK if success, Z_MEM_ERROR if there was not
+ enough memory, Z_BUF_ERROR if there was not enough room in the output
+ buffer, or Z_DATA_ERROR if the input data was corrupted.
+*/
+int ZEXPORT uncompress (dest, destLen, source, sourceLen)
+ Bytef *dest;
+ uLongf *destLen;
+ const Bytef *source;
+ uLong sourceLen;
+{
+ z_stream stream;
+ int err;
+
+ stream.next_in = (Bytef*)source;
+ stream.avail_in = (uInt)sourceLen;
+ /* Check for source > 64K on 16-bit machine: */
+ if ((uLong)stream.avail_in != sourceLen) return Z_BUF_ERROR;
+
+ stream.next_out = dest;
+ stream.avail_out = (uInt)*destLen;
+ if ((uLong)stream.avail_out != *destLen) return Z_BUF_ERROR;
+
+ stream.zalloc = (alloc_func)0;
+ stream.zfree = (free_func)0;
+
+ err = inflateInit(&stream);
+ if (err != Z_OK) return err;
+
+ err = inflate(&stream, Z_FINISH);
+ if (err != Z_STREAM_END) {
+ inflateEnd(&stream);
+ return err == Z_OK ? Z_BUF_ERROR : err;
+ }
+ *destLen = stream.total_out;
+
+ err = inflateEnd(&stream);
+ return err;
+}
diff --git a/zlib/zconf.h b/zlib/zconf.h
new file mode 100644
index 00000000..c8d6ce9a
--- /dev/null
+++ b/zlib/zconf.h
@@ -0,0 +1,279 @@
+/* zconf.h -- configuration of the zlib compression library
+ * Copyright (C) 1995-2002 Jean-loup Gailly.
+ * For conditions of distribution and use, see copyright notice in zlib.h
+ */
+
+/* @(#) $Id: zconf.h,v 1.1 2004/10/08 09:44:26 const_k Exp $ */
+
+#ifndef _ZCONF_H
+#define _ZCONF_H
+
+/*
+ * If you *really* need a unique prefix for all types and library functions,
+ * compile with -DZ_PREFIX. The "standard" zlib should be compiled without it.
+ */
+#ifdef Z_PREFIX
+# define deflateInit_ z_deflateInit_
+# define deflate z_deflate
+# define deflateEnd z_deflateEnd
+# define inflateInit_ z_inflateInit_
+# define inflate z_inflate
+# define inflateEnd z_inflateEnd
+# define deflateInit2_ z_deflateInit2_
+# define deflateSetDictionary z_deflateSetDictionary
+# define deflateCopy z_deflateCopy
+# define deflateReset z_deflateReset
+# define deflateParams z_deflateParams
+# define inflateInit2_ z_inflateInit2_
+# define inflateSetDictionary z_inflateSetDictionary
+# define inflateSync z_inflateSync
+# define inflateSyncPoint z_inflateSyncPoint
+# define inflateReset z_inflateReset
+# define compress z_compress
+# define compress2 z_compress2
+# define uncompress z_uncompress
+# define adler32 z_adler32
+# define crc32 z_crc32
+# define get_crc_table z_get_crc_table
+
+# define Byte z_Byte
+# define uInt z_uInt
+# define uLong z_uLong
+# define Bytef z_Bytef
+# define charf z_charf
+# define intf z_intf
+# define uIntf z_uIntf
+# define uLongf z_uLongf
+# define voidpf z_voidpf
+# define voidp z_voidp
+#endif
+
+#if (defined(_WIN32) || defined(__WIN32__)) && !defined(WIN32)
+# define WIN32
+#endif
+#if defined(__GNUC__) || defined(WIN32) || defined(__386__) || defined(i386)
+# ifndef __32BIT__
+# define __32BIT__
+# endif
+#endif
+#if defined(__MSDOS__) && !defined(MSDOS)
+# define MSDOS
+#endif
+
+/*
+ * Compile with -DMAXSEG_64K if the alloc function cannot allocate more
+ * than 64k bytes at a time (needed on systems with 16-bit int).
+ */
+#if defined(MSDOS) && !defined(__32BIT__)
+# define MAXSEG_64K
+#endif
+#ifdef MSDOS
+# define UNALIGNED_OK
+#endif
+
+#if (defined(MSDOS) || defined(_WINDOWS) || defined(WIN32)) && !defined(STDC)
+# define STDC
+#endif
+#if defined(__STDC__) || defined(__cplusplus) || defined(__OS2__)
+# ifndef STDC
+# define STDC
+# endif
+#endif
+
+#ifndef STDC
+# ifndef const /* cannot use !defined(STDC) && !defined(const) on Mac */
+# define const
+# endif
+#endif
+
+/* Some Mac compilers merge all .h files incorrectly: */
+#if defined(__MWERKS__) || defined(applec) ||defined(THINK_C) ||defined(__SC__)
+# define NO_DUMMY_DECL
+#endif
+
+/* Old Borland C incorrectly complains about missing returns: */
+#if defined(__BORLANDC__) && (__BORLANDC__ < 0x500)
+# define NEED_DUMMY_RETURN
+#endif
+
+
+/* Maximum value for memLevel in deflateInit2 */
+#ifndef MAX_MEM_LEVEL
+# ifdef MAXSEG_64K
+# define MAX_MEM_LEVEL 8
+# else
+# define MAX_MEM_LEVEL 9
+# endif
+#endif
+
+/* Maximum value for windowBits in deflateInit2 and inflateInit2.
+ * WARNING: reducing MAX_WBITS makes minigzip unable to extract .gz files
+ * created by gzip. (Files created by minigzip can still be extracted by
+ * gzip.)
+ */
+#ifndef MAX_WBITS
+# define MAX_WBITS 15 /* 32K LZ77 window */
+#endif
+
+/* The memory requirements for deflate are (in bytes):
+ (1 << (windowBits+2)) + (1 << (memLevel+9))
+ that is: 128K for windowBits=15 + 128K for memLevel = 8 (default values)
+ plus a few kilobytes for small objects. For example, if you want to reduce
+ the default memory requirements from 256K to 128K, compile with
+ make CFLAGS="-O -DMAX_WBITS=14 -DMAX_MEM_LEVEL=7"
+ Of course this will generally degrade compression (there's no free lunch).
+
+ The memory requirements for inflate are (in bytes) 1 << windowBits
+ that is, 32K for windowBits=15 (default value) plus a few kilobytes
+ for small objects.
+*/
+
+ /* Type declarations */
+
+#ifndef OF /* function prototypes */
+# ifdef STDC
+# define OF(args) args
+# else
+# define OF(args) ()
+# endif
+#endif
+
+/* The following definitions for FAR are needed only for MSDOS mixed
+ * model programming (small or medium model with some far allocations).
+ * This was tested only with MSC; for other MSDOS compilers you may have
+ * to define NO_MEMCPY in zutil.h. If you don't need the mixed model,
+ * just define FAR to be empty.
+ */
+#if (defined(M_I86SM) || defined(M_I86MM)) && !defined(__32BIT__)
+ /* MSC small or medium model */
+# define SMALL_MEDIUM
+# ifdef _MSC_VER
+# define FAR _far
+# else
+# define FAR far
+# endif
+#endif
+#if defined(__BORLANDC__) && (defined(__SMALL__) || defined(__MEDIUM__))
+# ifndef __32BIT__
+# define SMALL_MEDIUM
+# define FAR _far
+# endif
+#endif
+
+/* Compile with -DZLIB_DLL for Windows DLL support */
+#if defined(ZLIB_DLL)
+# if defined(_WINDOWS) || defined(WINDOWS)
+# ifdef FAR
+# undef FAR
+# endif
+# include <windows.h>
+# define ZEXPORT WINAPI
+# ifdef WIN32
+# define ZEXPORTVA WINAPIV
+# else
+# define ZEXPORTVA FAR _cdecl _export
+# endif
+# endif
+# if defined (__BORLANDC__)
+# if (__BORLANDC__ >= 0x0500) && defined (WIN32)
+# include <windows.h>
+# define ZEXPORT __declspec(dllexport) WINAPI
+# define ZEXPORTRVA __declspec(dllexport) WINAPIV
+# else
+# if defined (_Windows) && defined (__DLL__)
+# define ZEXPORT _export
+# define ZEXPORTVA _export
+# endif
+# endif
+# endif
+#endif
+
+#if defined (__BEOS__)
+# if defined (ZLIB_DLL)
+# define ZEXTERN extern __declspec(dllexport)
+# else
+# define ZEXTERN extern __declspec(dllimport)
+# endif
+#endif
+
+#ifndef ZEXPORT
+# define ZEXPORT
+#endif
+#ifndef ZEXPORTVA
+# define ZEXPORTVA
+#endif
+#ifndef ZEXTERN
+# define ZEXTERN extern
+#endif
+
+#ifndef FAR
+# define FAR
+#endif
+
+#if !defined(MACOS) && !defined(TARGET_OS_MAC)
+typedef unsigned char Byte; /* 8 bits */
+#endif
+typedef unsigned int uInt; /* 16 bits or more */
+typedef unsigned long uLong; /* 32 bits or more */
+
+#ifdef SMALL_MEDIUM
+ /* Borland C/C++ and some old MSC versions ignore FAR inside typedef */
+# define Bytef Byte FAR
+#else
+ typedef Byte FAR Bytef;
+#endif
+typedef char FAR charf;
+typedef int FAR intf;
+typedef uInt FAR uIntf;
+typedef uLong FAR uLongf;
+
+#ifdef STDC
+ typedef void FAR *voidpf;
+ typedef void *voidp;
+#else
+ typedef Byte FAR *voidpf;
+ typedef Byte *voidp;
+#endif
+
+#ifdef HAVE_UNISTD_H
+# include <sys/types.h> /* for off_t */
+# include <unistd.h> /* for SEEK_* and off_t */
+# define z_off_t off_t
+#endif
+#ifndef SEEK_SET
+# define SEEK_SET 0 /* Seek from beginning of file. */
+# define SEEK_CUR 1 /* Seek from current position. */
+# define SEEK_END 2 /* Set file pointer to EOF plus "offset" */
+#endif
+#ifndef z_off_t
+# define z_off_t long
+#endif
+
+/* MVS linker does not support external names larger than 8 bytes */
+#if defined(__MVS__)
+# pragma map(deflateInit_,"DEIN")
+# pragma map(deflateInit2_,"DEIN2")
+# pragma map(deflateEnd,"DEEND")
+# pragma map(inflateInit_,"ININ")
+# pragma map(inflateInit2_,"ININ2")
+# pragma map(inflateEnd,"INEND")
+# pragma map(inflateSync,"INSY")
+# pragma map(inflateSetDictionary,"INSEDI")
+# pragma map(inflate_blocks,"INBL")
+# pragma map(inflate_blocks_new,"INBLNE")
+# pragma map(inflate_blocks_free,"INBLFR")
+# pragma map(inflate_blocks_reset,"INBLRE")
+# pragma map(inflate_codes_free,"INCOFR")
+# pragma map(inflate_codes,"INCO")
+# pragma map(inflate_fast,"INFA")
+# pragma map(inflate_flush,"INFLU")
+# pragma map(inflate_mask,"INMA")
+# pragma map(inflate_set_dictionary,"INSEDI2")
+# pragma map(inflate_copyright,"INCOPY")
+# pragma map(inflate_trees_bits,"INTRBI")
+# pragma map(inflate_trees_dynamic,"INTRDY")
+# pragma map(inflate_trees_fixed,"INTRFI")
+# pragma map(inflate_trees_free,"INTRFR")
+#endif
+
+#endif /* _ZCONF_H */
diff --git a/zlib/zlib.3 b/zlib/zlib.3
new file mode 100644
index 00000000..3a6e4504
--- /dev/null
+++ b/zlib/zlib.3
@@ -0,0 +1,107 @@
+.TH ZLIB 3 "11 March 2002"
+.SH NAME
+zlib \- compression/decompression library
+.SH SYNOPSIS
+[see
+.I zlib.h
+for full description]
+.SH DESCRIPTION
+The
+.I zlib
+library is a general purpose data compression library.
+The code is thread safe.
+It provides in-memory compression and decompression functions,
+including integrity checks of the uncompressed data.
+This version of the library supports only one compression method (deflation)
+but other algorithms will be added later and will have the same stream interface.
+.LP
+Compression can be done in a single step if the buffers are large enough
+(for example if an input file is mmap'ed),
+or can be done by repeated calls of the compression function.
+In the latter case,
+the application must provide more input and/or consume the output
+(providing more output space) before each call.
+.LP
+The library also supports reading and writing files in
+.I gzip
+(.gz) format
+with an interface similar to that of stdio.
+.LP
+The library does not install any signal handler. The decoder checks
+the consistency of the compressed data, so the library should never
+crash even in case of corrupted input.
+.LP
+All functions of the compression library are documented in the file
+.IR zlib.h.
+The distribution source includes examples of use of the library
+the files
+.I example.c
+and
+.IR minigzip.c .
+.LP
+A Java implementation of
+.IR zlib
+is available in the Java Development Kit 1.1
+.IP
+http://www.javasoft.com/products/JDK/1.1/docs/api/Package-java.util.zip.html
+.LP
+A Perl interface to
+.IR zlib ,
+written by Paul Marquess (pmarquess@bfsec.bt.co.uk)
+is available at CPAN (Comprehensive Perl Archive Network) sites,
+such as:
+.IP
+ftp://ftp.cis.ufl.edu/pub/perl/CPAN/modules/by-module/Compress/Compress-Zlib*
+.LP
+A Python interface to
+.IR zlib
+written by A.M. Kuchling <amk@magnet.com>
+is available from the Python Software Association sites, such as:
+.IP
+ftp://ftp.python.org/pub/python/contrib/Encoding/zlib*.tar.gz
+.SH "SEE ALSO"
+Questions about zlib should be sent to:
+.IP
+zlib@quest.jpl.nasa.gov
+or, if this fails, to the author addresses given below.
+The zlib home page is:
+.IP
+http://www.cdrom.com/pub/infozip/zlib/
+.LP
+The data format used by the zlib library is described by RFC
+(Request for Comments) 1950 to 1952 in the files:
+.IP
+ftp://ds.internic.net/rfc/rfc1950.txt (zlib format)
+.br
+rfc1951.txt (deflate format)
+.br
+rfc1952.txt (gzip format)
+.LP
+These documents are also available in other formats from:
+.IP
+ftp://ftp.uu.net/graphics/png/documents/zlib/zdoc-index.html
+.SH AUTHORS
+Version 1.1.4
+Copyright (C) 1995-2002 Jean-loup Gailly (jloup@gzip.org)
+and Mark Adler (madler@alumni.caltech.edu).
+.LP
+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.
+See the distribution directory with respect to requirements
+governing redistribution.
+The deflate format used by
+.I zlib
+was defined by Phil Katz.
+The deflate and
+.I zlib
+specifications were written by L. Peter Deutsch.
+Thanks to all the people who reported problems and suggested various
+improvements in
+.IR zlib ;
+who are too numerous to cite here.
+.LP
+UNIX manual page by R. P. C. Rodgers,
+U.S. National Library of Medicine (rodgers@nlm.nih.gov).
+.\" end of man page
diff --git a/zlib/zlib.dsp b/zlib/zlib.dsp
new file mode 100644
index 00000000..18f00b41
--- /dev/null
+++ b/zlib/zlib.dsp
@@ -0,0 +1,217 @@
+# Microsoft Developer Studio Project File - Name="zlib" - Package Owner=<4>
+# Microsoft Developer Studio Generated Build File, Format Version 6.00
+# ** DO NOT EDIT **
+
+# TARGTYPE "Win32 (x86) Static Library" 0x0104
+
+CFG=zlib - 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 "zlib.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 "zlib.mak" CFG="zlib - Win32 Debug Unicode"
+!MESSAGE
+!MESSAGE Possible choices for configuration are:
+!MESSAGE
+!MESSAGE "zlib - Win32 Release" (based on "Win32 (x86) Static Library")
+!MESSAGE "zlib - Win32 Debug" (based on "Win32 (x86) Static Library")
+!MESSAGE "zlib - 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)" == "zlib - 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"
+# 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 /O1 /D "NDEBUG" /D "_LIB" /D "WIN32" /D "_MBCS" /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)" == "zlib - 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"
+# 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 /D "_DEBUG" /D "_LIB" /D "WIN32" /D "_MBCS" /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)" == "zlib - Win32 Debug Unicode"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 1
+# PROP BASE Output_Dir "zlib___Win32_Debug_Unicode"
+# PROP BASE Intermediate_Dir "zlib___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"
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /MTd /W3 /Gm /GX /Zi /Od /D "_DEBUG" /D "_LIB" /D "WIN32" /D "_MBCS" /YX /FD /GZ /c
+# ADD CPP /nologo /MTd /W3 /Gm /GX /Zi /Od /D "_LIB" /D "_DEBUG" /D "WIN32" /D "_UNICODE" /D "UNICODE" /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 "zlib - Win32 Release"
+# Name "zlib - Win32 Debug"
+# Name "zlib - Win32 Debug Unicode"
+# Begin Group "Source Files"
+
+# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat"
+# Begin Source File
+
+SOURCE=.\adler32.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\compress.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\crc32.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\deflate.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\gzio.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\infblock.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\infcodes.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\inffast.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\inflate.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\inftrees.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\infutil.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\trees.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\uncompr.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\zutil.c
+# End Source File
+# End Group
+# Begin Group "Header Files"
+
+# PROP Default_Filter "h;hpp;hxx;hm;inl"
+# Begin Source File
+
+SOURCE=.\deflate.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\infblock.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\infcodes.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\inffast.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\inffixed.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\inftrees.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\infutil.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\trees.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\zconf.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\zlib.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\zutil.h
+# End Source File
+# End Group
+# End Target
+# End Project
diff --git a/zlib/zlib.h b/zlib/zlib.h
new file mode 100644
index 00000000..52cb529f
--- /dev/null
+++ b/zlib/zlib.h
@@ -0,0 +1,893 @@
+/* zlib.h -- interface of the 'zlib' general purpose compression library
+ version 1.1.4, March 11th, 2002
+
+ 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).
+*/
+
+#ifndef _ZLIB_H
+#define _ZLIB_H
+
+#include "zconf.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define ZLIB_VERSION "1.1.4"
+
+/*
+ The 'zlib' compression library provides in-memory compression and
+ decompression functions, including integrity checks of the uncompressed
+ data. This version of the library supports only one compression method
+ (deflation) but other algorithms will be added later and will have the same
+ stream interface.
+
+ Compression can be done in a single step if the buffers are large
+ enough (for example if an input file is mmap'ed), or can be done by
+ repeated calls of the compression function. In the latter case, the
+ application must provide more input and/or consume the output
+ (providing more output space) before each call.
+
+ The library also supports reading and writing files in gzip (.gz) format
+ with an interface similar to that of stdio.
+
+ The library does not install any signal handler. The decoder checks
+ the consistency of the compressed data, so the library should never
+ crash even in case of corrupted input.
+*/
+
+typedef voidpf (*alloc_func) OF((voidpf opaque, uInt items, uInt size));
+typedef void (*free_func) OF((voidpf opaque, voidpf address));
+
+struct internal_state;
+
+typedef struct z_stream_s {
+ Bytef *next_in; /* next input byte */
+ uInt avail_in; /* number of bytes available at next_in */
+ uLong total_in; /* total nb of input bytes read so far */
+
+ Bytef *next_out; /* next output byte should be put there */
+ uInt avail_out; /* remaining free space at next_out */
+ uLong total_out; /* total nb of bytes output so far */
+
+ char *msg; /* last error message, NULL if no error */
+ struct internal_state FAR *state; /* not visible by applications */
+
+ alloc_func zalloc; /* used to allocate the internal state */
+ free_func zfree; /* used to free the internal state */
+ voidpf opaque; /* private data object passed to zalloc and zfree */
+
+ int data_type; /* best guess about the data type: ascii or binary */
+ uLong adler; /* adler32 value of the uncompressed data */
+ uLong reserved; /* reserved for future use */
+} z_stream;
+
+typedef z_stream FAR *z_streamp;
+
+/*
+ The application must update next_in and avail_in when avail_in has
+ dropped to zero. It must update next_out and avail_out when avail_out
+ has dropped to zero. The application must initialize zalloc, zfree and
+ opaque before calling the init function. All other fields are set by the
+ compression library and must not be updated by the application.
+
+ The opaque value provided by the application will be passed as the first
+ parameter for calls of zalloc and zfree. This can be useful for custom
+ memory management. The compression library attaches no meaning to the
+ opaque value.
+
+ zalloc must return Z_NULL if there is not enough memory for the object.
+ If zlib is used in a multi-threaded application, zalloc and zfree must be
+ thread safe.
+
+ On 16-bit systems, the functions zalloc and zfree must be able to allocate
+ exactly 65536 bytes, but will not be required to allocate more than this
+ if the symbol MAXSEG_64K is defined (see zconf.h). WARNING: On MSDOS,
+ pointers returned by zalloc for objects of exactly 65536 bytes *must*
+ have their offset normalized to zero. The default allocation function
+ provided by this library ensures this (see zutil.c). To reduce memory
+ requirements and avoid any allocation of 64K objects, at the expense of
+ compression ratio, compile the library with -DMAX_WBITS=14 (see zconf.h).
+
+ The fields total_in and total_out can be used for statistics or
+ progress reports. After compression, total_in holds the total size of
+ the uncompressed data and may be saved for use in the decompressor
+ (particularly if the decompressor wants to decompress everything in
+ a single step).
+*/
+
+ /* constants */
+
+#define Z_NO_FLUSH 0
+#define Z_PARTIAL_FLUSH 1 /* will be removed, use Z_SYNC_FLUSH instead */
+#define Z_SYNC_FLUSH 2
+#define Z_FULL_FLUSH 3
+#define Z_FINISH 4
+/* Allowed flush values; see deflate() below for details */
+
+#define Z_OK 0
+#define Z_STREAM_END 1
+#define Z_NEED_DICT 2
+#define Z_ERRNO (-1)
+#define Z_STREAM_ERROR (-2)
+#define Z_DATA_ERROR (-3)
+#define Z_MEM_ERROR (-4)
+#define Z_BUF_ERROR (-5)
+#define Z_VERSION_ERROR (-6)
+/* Return codes for the compression/decompression functions. Negative
+ * values are errors, positive values are used for special but normal events.
+ */
+
+#define Z_NO_COMPRESSION 0
+#define Z_BEST_SPEED 1
+#define Z_BEST_COMPRESSION 9
+#define Z_DEFAULT_COMPRESSION (-1)
+/* compression levels */
+
+#define Z_FILTERED 1
+#define Z_HUFFMAN_ONLY 2
+#define Z_DEFAULT_STRATEGY 0
+/* compression strategy; see deflateInit2() below for details */
+
+#define Z_BINARY 0
+#define Z_ASCII 1
+#define Z_UNKNOWN 2
+/* Possible values of the data_type field */
+
+#define Z_DEFLATED 8
+/* The deflate compression method (the only one supported in this version) */
+
+#define Z_NULL 0 /* for initializing zalloc, zfree, opaque */
+
+#define zlib_version zlibVersion()
+/* for compatibility with versions < 1.0.2 */
+
+ /* basic functions */
+
+ZEXTERN const char * ZEXPORT zlibVersion OF((void));
+/* The application can compare zlibVersion and ZLIB_VERSION for consistency.
+ If the first character differs, the library code actually used is
+ not compatible with the zlib.h header file used by the application.
+ This check is automatically made by deflateInit and inflateInit.
+ */
+
+/*
+ZEXTERN int ZEXPORT deflateInit OF((z_streamp strm, int level));
+
+ Initializes the internal stream state for compression. The fields
+ zalloc, zfree and opaque must be initialized before by the caller.
+ If zalloc and zfree are set to Z_NULL, deflateInit updates them to
+ use default allocation functions.
+
+ The compression level must be Z_DEFAULT_COMPRESSION, or between 0 and 9:
+ 1 gives best speed, 9 gives best compression, 0 gives no compression at
+ all (the input data is simply copied a block at a time).
+ Z_DEFAULT_COMPRESSION requests a default compromise between speed and
+ compression (currently equivalent to level 6).
+
+ deflateInit returns Z_OK if success, Z_MEM_ERROR if there was not
+ enough memory, Z_STREAM_ERROR if level is not a valid compression level,
+ Z_VERSION_ERROR if the zlib library version (zlib_version) is incompatible
+ with the version assumed by the caller (ZLIB_VERSION).
+ msg is set to null if there is no error message. deflateInit does not
+ perform any compression: this will be done by deflate().
+*/
+
+
+ZEXTERN int ZEXPORT deflate OF((z_streamp strm, int flush));
+/*
+ deflate compresses as much data as possible, and stops when the input
+ buffer becomes empty or the output buffer becomes full. It may introduce some
+ output latency (reading input without producing any output) except when
+ forced to flush.
+
+ The detailed semantics are as follows. deflate performs one or both of the
+ following actions:
+
+ - Compress more input starting at next_in and update next_in and avail_in
+ accordingly. If not all input can be processed (because there is not
+ enough room in the output buffer), next_in and avail_in are updated and
+ processing will resume at this point for the next call of deflate().
+
+ - Provide more output starting at next_out and update next_out and avail_out
+ accordingly. This action is forced if the parameter flush is non zero.
+ Forcing flush frequently degrades the compression ratio, so this parameter
+ should be set only when necessary (in interactive applications).
+ Some output may be provided even if flush is not set.
+
+ Before the call of deflate(), the application should ensure that at least
+ one of the actions is possible, by providing more input and/or consuming
+ more output, and updating avail_in or avail_out accordingly; avail_out
+ should never be zero before the call. The application can consume the
+ compressed output when it wants, for example when the output buffer is full
+ (avail_out == 0), or after each call of deflate(). If deflate returns Z_OK
+ and with zero avail_out, it must be called again after making room in the
+ output buffer because there might be more output pending.
+
+ If the parameter flush is set to Z_SYNC_FLUSH, all pending output is
+ flushed to the output buffer and the output is aligned on a byte boundary, so
+ that the decompressor can get all input data available so far. (In particular
+ avail_in is zero after the call if enough output space has been provided
+ before the call.) Flushing may degrade compression for some compression
+ algorithms and so it should be used only when necessary.
+
+ If flush is set to Z_FULL_FLUSH, all output is flushed as with
+ Z_SYNC_FLUSH, and the compression state is reset so that decompression can
+ restart from this point if previous compressed data has been damaged or if
+ random access is desired. Using Z_FULL_FLUSH too often can seriously degrade
+ the compression.
+
+ If deflate returns with avail_out == 0, this function must be called again
+ with the same value of the flush parameter and more output space (updated
+ avail_out), until the flush is complete (deflate returns with non-zero
+ avail_out).
+
+ If the parameter flush is set to Z_FINISH, pending input is processed,
+ pending output is flushed and deflate returns with Z_STREAM_END if there
+ was enough output space; if deflate returns with Z_OK, this function must be
+ called again with Z_FINISH and more output space (updated avail_out) but no
+ more input data, until it returns with Z_STREAM_END or an error. After
+ deflate has returned Z_STREAM_END, the only possible operations on the
+ stream are deflateReset or deflateEnd.
+
+ Z_FINISH can be used immediately after deflateInit if all the compression
+ is to be done in a single step. In this case, avail_out must be at least
+ 0.1% larger than avail_in plus 12 bytes. If deflate does not return
+ Z_STREAM_END, then it must be called again as described above.
+
+ deflate() sets strm->adler to the adler32 checksum of all input read
+ so far (that is, total_in bytes).
+
+ deflate() may update data_type if it can make a good guess about
+ the input data type (Z_ASCII or Z_BINARY). In doubt, the data is considered
+ binary. This field is only for information purposes and does not affect
+ the compression algorithm in any manner.
+
+ deflate() returns Z_OK if some progress has been made (more input
+ processed or more output produced), Z_STREAM_END if all input has been
+ consumed and all output has been produced (only when flush is set to
+ Z_FINISH), Z_STREAM_ERROR if the stream state was inconsistent (for example
+ if next_in or next_out was NULL), Z_BUF_ERROR if no progress is possible
+ (for example avail_in or avail_out was zero).
+*/
+
+
+ZEXTERN int ZEXPORT deflateEnd OF((z_streamp strm));
+/*
+ All dynamically allocated data structures for this stream are freed.
+ This function discards any unprocessed input and does not flush any
+ pending output.
+
+ deflateEnd returns Z_OK if success, Z_STREAM_ERROR if the
+ stream state was inconsistent, Z_DATA_ERROR if the stream was freed
+ prematurely (some input or output was discarded). In the error case,
+ msg may be set but then points to a static string (which must not be
+ deallocated).
+*/
+
+
+/*
+ZEXTERN int ZEXPORT inflateInit OF((z_streamp strm));
+
+ Initializes the internal stream state for decompression. The fields
+ next_in, avail_in, zalloc, zfree and opaque must be initialized before by
+ the caller. If next_in is not Z_NULL and avail_in is large enough (the exact
+ value depends on the compression method), inflateInit determines the
+ compression method from the zlib header and allocates all data structures
+ accordingly; otherwise the allocation will be deferred to the first call of
+ inflate. If zalloc and zfree are set to Z_NULL, inflateInit updates them to
+ use default allocation functions.
+
+ inflateInit returns Z_OK if success, Z_MEM_ERROR if there was not enough
+ memory, Z_VERSION_ERROR if the zlib library version is incompatible with the
+ version assumed by the caller. msg is set to null if there is no error
+ message. inflateInit does not perform any decompression apart from reading
+ the zlib header if present: this will be done by inflate(). (So next_in and
+ avail_in may be modified, but next_out and avail_out are unchanged.)
+*/
+
+
+ZEXTERN int ZEXPORT inflate OF((z_streamp strm, int flush));
+/*
+ inflate decompresses as much data as possible, and stops when the input
+ buffer becomes empty or the output buffer becomes full. It may some
+ introduce some output latency (reading input without producing any output)
+ except when forced to flush.
+
+ The detailed semantics are as follows. inflate performs one or both of the
+ following actions:
+
+ - Decompress more input starting at next_in and update next_in and avail_in
+ accordingly. If not all input can be processed (because there is not
+ enough room in the output buffer), next_in is updated and processing
+ will resume at this point for the next call of inflate().
+
+ - Provide more output starting at next_out and update next_out and avail_out
+ accordingly. inflate() provides as much output as possible, until there
+ is no more input data or no more space in the output buffer (see below
+ about the flush parameter).
+
+ Before the call of inflate(), the application should ensure that at least
+ one of the actions is possible, by providing more input and/or consuming
+ more output, and updating the next_* and avail_* values accordingly.
+ The application can consume the uncompressed output when it wants, for
+ example when the output buffer is full (avail_out == 0), or after each
+ call of inflate(). If inflate returns Z_OK and with zero avail_out, it
+ must be called again after making room in the output buffer because there
+ might be more output pending.
+
+ If the parameter flush is set to Z_SYNC_FLUSH, inflate flushes as much
+ output as possible to the output buffer. The flushing behavior of inflate is
+ not specified for values of the flush parameter other than Z_SYNC_FLUSH
+ and Z_FINISH, but the current implementation actually flushes as much output
+ as possible anyway.
+
+ inflate() should normally be called until it returns Z_STREAM_END or an
+ error. However if all decompression is to be performed in a single step
+ (a single call of inflate), the parameter flush should be set to
+ Z_FINISH. In this case all pending input is processed and all pending
+ output is flushed; avail_out must be large enough to hold all the
+ uncompressed data. (The size of the uncompressed data may have been saved
+ by the compressor for this purpose.) The next operation on this stream must
+ be inflateEnd to deallocate the decompression state. The use of Z_FINISH
+ is never required, but can be used to inform inflate that a faster routine
+ may be used for the single inflate() call.
+
+ If a preset dictionary is needed at this point (see inflateSetDictionary
+ below), inflate sets strm-adler to the adler32 checksum of the
+ dictionary chosen by the compressor and returns Z_NEED_DICT; otherwise
+ it sets strm->adler to the adler32 checksum of all output produced
+ so far (that is, total_out bytes) and returns Z_OK, Z_STREAM_END or
+ an error code as described below. At the end of the stream, inflate()
+ checks that its computed adler32 checksum is equal to that saved by the
+ compressor and returns Z_STREAM_END only if the checksum is correct.
+
+ inflate() returns Z_OK if some progress has been made (more input processed
+ or more output produced), Z_STREAM_END if the end of the compressed data has
+ been reached and all uncompressed output has been produced, Z_NEED_DICT if a
+ preset dictionary is needed at this point, Z_DATA_ERROR if the input data was
+ corrupted (input stream not conforming to the zlib format or incorrect
+ adler32 checksum), Z_STREAM_ERROR if the stream structure was inconsistent
+ (for example if next_in or next_out was NULL), Z_MEM_ERROR if there was not
+ enough memory, Z_BUF_ERROR if no progress is possible or if there was not
+ enough room in the output buffer when Z_FINISH is used. In the Z_DATA_ERROR
+ case, the application may then call inflateSync to look for a good
+ compression block.
+*/
+
+
+ZEXTERN int ZEXPORT inflateEnd OF((z_streamp strm));
+/*
+ All dynamically allocated data structures for this stream are freed.
+ This function discards any unprocessed input and does not flush any
+ pending output.
+
+ inflateEnd returns Z_OK if success, Z_STREAM_ERROR if the stream state
+ was inconsistent. In the error case, msg may be set but then points to a
+ static string (which must not be deallocated).
+*/
+
+ /* Advanced functions */
+
+/*
+ The following functions are needed only in some special applications.
+*/
+
+/*
+ZEXTERN int ZEXPORT deflateInit2 OF((z_streamp strm,
+ int level,
+ int method,
+ int windowBits,
+ int memLevel,
+ int strategy));
+
+ This is another version of deflateInit with more compression options. The
+ fields next_in, zalloc, zfree and opaque must be initialized before by
+ the caller.
+
+ The method parameter is the compression method. It must be Z_DEFLATED in
+ this version of the library.
+
+ The windowBits parameter is the base two logarithm of the window size
+ (the size of the history buffer). It should be in the range 8..15 for this
+ version of the library. Larger values of this parameter result in better
+ compression at the expense of memory usage. The default value is 15 if
+ deflateInit is used instead.
+
+ The memLevel parameter specifies how much memory should be allocated
+ for the internal compression state. memLevel=1 uses minimum memory but
+ is slow and reduces compression ratio; memLevel=9 uses maximum memory
+ for optimal speed. The default value is 8. See zconf.h for total memory
+ usage as a function of windowBits and memLevel.
+
+ The strategy parameter is used to tune the compression algorithm. Use the
+ value Z_DEFAULT_STRATEGY for normal data, Z_FILTERED for data produced by a
+ filter (or predictor), or Z_HUFFMAN_ONLY to force Huffman encoding only (no
+ string match). Filtered data consists mostly of small values with a
+ somewhat random distribution. In this case, the compression algorithm is
+ tuned to compress them better. The effect of Z_FILTERED is to force more
+ Huffman coding and less string matching; it is somewhat intermediate
+ between Z_DEFAULT and Z_HUFFMAN_ONLY. The strategy parameter only affects
+ the compression ratio but not the correctness of the compressed output even
+ if it is not set appropriately.
+
+ deflateInit2 returns Z_OK if success, Z_MEM_ERROR if there was not enough
+ memory, Z_STREAM_ERROR if a parameter is invalid (such as an invalid
+ method). msg is set to null if there is no error message. deflateInit2 does
+ not perform any compression: this will be done by deflate().
+*/
+
+ZEXTERN int ZEXPORT deflateSetDictionary OF((z_streamp strm,
+ const Bytef *dictionary,
+ uInt dictLength));
+/*
+ Initializes the compression dictionary from the given byte sequence
+ without producing any compressed output. This function must be called
+ immediately after deflateInit, deflateInit2 or deflateReset, before any
+ call of deflate. The compressor and decompressor must use exactly the same
+ dictionary (see inflateSetDictionary).
+
+ The dictionary should consist of strings (byte sequences) that are likely
+ to be encountered later in the data to be compressed, with the most commonly
+ used strings preferably put towards the end of the dictionary. Using a
+ dictionary is most useful when the data to be compressed is short and can be
+ predicted with good accuracy; the data can then be compressed better than
+ with the default empty dictionary.
+
+ Depending on the size of the compression data structures selected by
+ deflateInit or deflateInit2, a part of the dictionary may in effect be
+ discarded, for example if the dictionary is larger than the window size in
+ deflate or deflate2. Thus the strings most likely to be useful should be
+ put at the end of the dictionary, not at the front.
+
+ Upon return of this function, strm->adler is set to the Adler32 value
+ of the dictionary; the decompressor may later use this value to determine
+ which dictionary has been used by the compressor. (The Adler32 value
+ applies to the whole dictionary even if only a subset of the dictionary is
+ actually used by the compressor.)
+
+ deflateSetDictionary returns Z_OK if success, or Z_STREAM_ERROR if a
+ parameter is invalid (such as NULL dictionary) or the stream state is
+ inconsistent (for example if deflate has already been called for this stream
+ or if the compression method is bsort). deflateSetDictionary does not
+ perform any compression: this will be done by deflate().
+*/
+
+ZEXTERN int ZEXPORT deflateCopy OF((z_streamp dest,
+ z_streamp source));
+/*
+ Sets the destination stream as a complete copy of the source stream.
+
+ This function can be useful when several compression strategies will be
+ tried, for example when there are several ways of pre-processing the input
+ data with a filter. The streams that will be discarded should then be freed
+ by calling deflateEnd. Note that deflateCopy duplicates the internal
+ compression state which can be quite large, so this strategy is slow and
+ can consume lots of memory.
+
+ deflateCopy returns Z_OK if success, Z_MEM_ERROR if there was not
+ enough memory, Z_STREAM_ERROR if the source stream state was inconsistent
+ (such as zalloc being NULL). msg is left unchanged in both source and
+ destination.
+*/
+
+ZEXTERN int ZEXPORT deflateReset OF((z_streamp strm));
+/*
+ This function is equivalent to deflateEnd followed by deflateInit,
+ but does not free and reallocate all the internal compression state.
+ The stream will keep the same compression level and any other attributes
+ that may have been set by deflateInit2.
+
+ deflateReset returns Z_OK if success, or Z_STREAM_ERROR if the source
+ stream state was inconsistent (such as zalloc or state being NULL).
+*/
+
+ZEXTERN int ZEXPORT deflateParams OF((z_streamp strm,
+ int level,
+ int strategy));
+/*
+ Dynamically update the compression level and compression strategy. The
+ interpretation of level and strategy is as in deflateInit2. This can be
+ used to switch between compression and straight copy of the input data, or
+ to switch to a different kind of input data requiring a different
+ strategy. If the compression level is changed, the input available so far
+ is compressed with the old level (and may be flushed); the new level will
+ take effect only at the next call of deflate().
+
+ Before the call of deflateParams, the stream state must be set as for
+ a call of deflate(), since the currently available input may have to
+ be compressed and flushed. In particular, strm->avail_out must be non-zero.
+
+ deflateParams returns Z_OK if success, Z_STREAM_ERROR if the source
+ stream state was inconsistent or if a parameter was invalid, Z_BUF_ERROR
+ if strm->avail_out was zero.
+*/
+
+/*
+ZEXTERN int ZEXPORT inflateInit2 OF((z_streamp strm,
+ int windowBits));
+
+ This is another version of inflateInit with an extra parameter. The
+ fields next_in, avail_in, zalloc, zfree and opaque must be initialized
+ before by the caller.
+
+ The windowBits parameter is the base two logarithm of the maximum window
+ size (the size of the history buffer). It should be in the range 8..15 for
+ this version of the library. The default value is 15 if inflateInit is used
+ instead. If a compressed stream with a larger window size is given as
+ input, inflate() will return with the error code Z_DATA_ERROR instead of
+ trying to allocate a larger window.
+
+ inflateInit2 returns Z_OK if success, Z_MEM_ERROR if there was not enough
+ memory, Z_STREAM_ERROR if a parameter is invalid (such as a negative
+ memLevel). msg is set to null if there is no error message. inflateInit2
+ does not perform any decompression apart from reading the zlib header if
+ present: this will be done by inflate(). (So next_in and avail_in may be
+ modified, but next_out and avail_out are unchanged.)
+*/
+
+ZEXTERN int ZEXPORT inflateSetDictionary OF((z_streamp strm,
+ const Bytef *dictionary,
+ uInt dictLength));
+/*
+ Initializes the decompression dictionary from the given uncompressed byte
+ sequence. This function must be called immediately after a call of inflate
+ if this call returned Z_NEED_DICT. The dictionary chosen by the compressor
+ can be determined from the Adler32 value returned by this call of
+ inflate. The compressor and decompressor must use exactly the same
+ dictionary (see deflateSetDictionary).
+
+ inflateSetDictionary returns Z_OK if success, Z_STREAM_ERROR if a
+ parameter is invalid (such as NULL dictionary) or the stream state is
+ inconsistent, Z_DATA_ERROR if the given dictionary doesn't match the
+ expected one (incorrect Adler32 value). inflateSetDictionary does not
+ perform any decompression: this will be done by subsequent calls of
+ inflate().
+*/
+
+ZEXTERN int ZEXPORT inflateSync OF((z_streamp strm));
+/*
+ Skips invalid compressed data until a full flush point (see above the
+ description of deflate with Z_FULL_FLUSH) can be found, or until all
+ available input is skipped. No output is provided.
+
+ inflateSync returns Z_OK if a full flush point has been found, Z_BUF_ERROR
+ if no more input was provided, Z_DATA_ERROR if no flush point has been found,
+ or Z_STREAM_ERROR if the stream structure was inconsistent. In the success
+ case, the application may save the current current value of total_in which
+ indicates where valid compressed data was found. In the error case, the
+ application may repeatedly call inflateSync, providing more input each time,
+ until success or end of the input data.
+*/
+
+ZEXTERN int ZEXPORT inflateReset OF((z_streamp strm));
+/*
+ This function is equivalent to inflateEnd followed by inflateInit,
+ but does not free and reallocate all the internal decompression state.
+ The stream will keep attributes that may have been set by inflateInit2.
+
+ inflateReset returns Z_OK if success, or Z_STREAM_ERROR if the source
+ stream state was inconsistent (such as zalloc or state being NULL).
+*/
+
+
+ /* utility functions */
+
+/*
+ The following utility functions are implemented on top of the
+ basic stream-oriented functions. To simplify the interface, some
+ default options are assumed (compression level and memory usage,
+ standard memory allocation functions). The source code of these
+ utility functions can easily be modified if you need special options.
+*/
+
+ZEXTERN int ZEXPORT compress OF((Bytef *dest, uLongf *destLen,
+ const Bytef *source, uLong sourceLen));
+/*
+ Compresses the source buffer into the destination buffer. sourceLen is
+ the byte length of the source buffer. Upon entry, destLen is the total
+ size of the destination buffer, which must be at least 0.1% larger than
+ sourceLen plus 12 bytes. Upon exit, destLen is the actual size of the
+ compressed buffer.
+ This function can be used to compress a whole file at once if the
+ input file is mmap'ed.
+ compress returns Z_OK if success, Z_MEM_ERROR if there was not
+ enough memory, Z_BUF_ERROR if there was not enough room in the output
+ buffer.
+*/
+
+ZEXTERN int ZEXPORT compress2 OF((Bytef *dest, uLongf *destLen,
+ const Bytef *source, uLong sourceLen,
+ int level));
+/*
+ Compresses the source buffer into the destination buffer. The level
+ parameter has the same meaning as in deflateInit. sourceLen is the byte
+ length of the source buffer. Upon entry, destLen is the total size of the
+ destination buffer, which must be at least 0.1% larger than sourceLen plus
+ 12 bytes. Upon exit, destLen is the actual size of the compressed buffer.
+
+ compress2 returns Z_OK if success, Z_MEM_ERROR if there was not enough
+ memory, Z_BUF_ERROR if there was not enough room in the output buffer,
+ Z_STREAM_ERROR if the level parameter is invalid.
+*/
+
+ZEXTERN int ZEXPORT uncompress OF((Bytef *dest, uLongf *destLen,
+ const Bytef *source, uLong sourceLen));
+/*
+ Decompresses the source buffer into the destination buffer. sourceLen is
+ the byte length of the source buffer. Upon entry, destLen is the total
+ size of the destination buffer, which must be large enough to hold the
+ entire uncompressed data. (The size of the uncompressed data must have
+ been saved previously by the compressor and transmitted to the decompressor
+ by some mechanism outside the scope of this compression library.)
+ Upon exit, destLen is the actual size of the compressed buffer.
+ This function can be used to decompress a whole file at once if the
+ input file is mmap'ed.
+
+ uncompress returns Z_OK if success, Z_MEM_ERROR if there was not
+ enough memory, Z_BUF_ERROR if there was not enough room in the output
+ buffer, or Z_DATA_ERROR if the input data was corrupted.
+*/
+
+
+typedef voidp gzFile;
+
+ZEXTERN gzFile ZEXPORT gzopen OF((const char *path, const char *mode));
+/*
+ Opens a gzip (.gz) file for reading or writing. The mode parameter
+ is as in fopen ("rb" or "wb") but can also include a compression level
+ ("wb9") or a strategy: 'f' for filtered data as in "wb6f", 'h' for
+ Huffman only compression as in "wb1h". (See the description
+ of deflateInit2 for more information about the strategy parameter.)
+
+ gzopen can be used to read a file which is not in gzip format; in this
+ case gzread will directly read from the file without decompression.
+
+ gzopen returns NULL if the file could not be opened or if there was
+ insufficient memory to allocate the (de)compression state; errno
+ can be checked to distinguish the two cases (if errno is zero, the
+ zlib error is Z_MEM_ERROR). */
+
+ZEXTERN gzFile ZEXPORT gzdopen OF((int fd, const char *mode));
+/*
+ gzdopen() associates a gzFile with the file descriptor fd. File
+ descriptors are obtained from calls like open, dup, creat, pipe or
+ fileno (in the file has been previously opened with fopen).
+ The mode parameter is as in gzopen.
+ The next call of gzclose on the returned gzFile will also close the
+ file descriptor fd, just like fclose(fdopen(fd), mode) closes the file
+ descriptor fd. If you want to keep fd open, use gzdopen(dup(fd), mode).
+ gzdopen returns NULL if there was insufficient memory to allocate
+ the (de)compression state.
+*/
+
+ZEXTERN int ZEXPORT gzsetparams OF((gzFile file, int level, int strategy));
+/*
+ Dynamically update the compression level or strategy. See the description
+ of deflateInit2 for the meaning of these parameters.
+ gzsetparams returns Z_OK if success, or Z_STREAM_ERROR if the file was not
+ opened for writing.
+*/
+
+ZEXTERN int ZEXPORT gzread OF((gzFile file, voidp buf, unsigned len));
+/*
+ Reads the given number of uncompressed bytes from the compressed file.
+ If the input file was not in gzip format, gzread copies the given number
+ of bytes into the buffer.
+ gzread returns the number of uncompressed bytes actually read (0 for
+ end of file, -1 for error). */
+
+ZEXTERN int ZEXPORT gzwrite OF((gzFile file,
+ const voidp buf, unsigned len));
+/*
+ Writes the given number of uncompressed bytes into the compressed file.
+ gzwrite returns the number of uncompressed bytes actually written
+ (0 in case of error).
+*/
+
+ZEXTERN int ZEXPORTVA gzprintf OF((gzFile file, const char *format, ...));
+/*
+ Converts, formats, and writes the args to the compressed file under
+ control of the format string, as in fprintf. gzprintf returns the number of
+ uncompressed bytes actually written (0 in case of error).
+*/
+
+ZEXTERN int ZEXPORT gzputs OF((gzFile file, const char *s));
+/*
+ Writes the given null-terminated string to the compressed file, excluding
+ the terminating null character.
+ gzputs returns the number of characters written, or -1 in case of error.
+*/
+
+ZEXTERN char * ZEXPORT gzgets OF((gzFile file, char *buf, int len));
+/*
+ Reads bytes from the compressed file until len-1 characters are read, or
+ a newline character is read and transferred to buf, or an end-of-file
+ condition is encountered. The string is then terminated with a null
+ character.
+ gzgets returns buf, or Z_NULL in case of error.
+*/
+
+ZEXTERN int ZEXPORT gzputc OF((gzFile file, int c));
+/*
+ Writes c, converted to an unsigned char, into the compressed file.
+ gzputc returns the value that was written, or -1 in case of error.
+*/
+
+ZEXTERN int ZEXPORT gzgetc OF((gzFile file));
+/*
+ Reads one byte from the compressed file. gzgetc returns this byte
+ or -1 in case of end of file or error.
+*/
+
+ZEXTERN int ZEXPORT gzflush OF((gzFile file, int flush));
+/*
+ Flushes all pending output into the compressed file. The parameter
+ flush is as in the deflate() function. The return value is the zlib
+ error number (see function gzerror below). gzflush returns Z_OK if
+ the flush parameter is Z_FINISH and all output could be flushed.
+ gzflush should be called only when strictly necessary because it can
+ degrade compression.
+*/
+
+ZEXTERN z_off_t ZEXPORT gzseek OF((gzFile file,
+ z_off_t offset, int whence));
+/*
+ Sets the starting position for the next gzread or gzwrite on the
+ given compressed file. The offset represents a number of bytes in the
+ uncompressed data stream. The whence parameter is defined as in lseek(2);
+ the value SEEK_END is not supported.
+ If the file is opened for reading, this function is emulated but can be
+ extremely slow. If the file is opened for writing, only forward seeks are
+ supported; gzseek then compresses a sequence of zeroes up to the new
+ starting position.
+
+ gzseek returns the resulting offset location as measured in bytes from
+ the beginning of the uncompressed stream, or -1 in case of error, in
+ particular if the file is opened for writing and the new starting position
+ would be before the current position.
+*/
+
+ZEXTERN int ZEXPORT gzrewind OF((gzFile file));
+/*
+ Rewinds the given file. This function is supported only for reading.
+
+ gzrewind(file) is equivalent to (int)gzseek(file, 0L, SEEK_SET)
+*/
+
+ZEXTERN z_off_t ZEXPORT gztell OF((gzFile file));
+/*
+ Returns the starting position for the next gzread or gzwrite on the
+ given compressed file. This position represents a number of bytes in the
+ uncompressed data stream.
+
+ gztell(file) is equivalent to gzseek(file, 0L, SEEK_CUR)
+*/
+
+ZEXTERN int ZEXPORT gzeof OF((gzFile file));
+/*
+ Returns 1 when EOF has previously been detected reading the given
+ input stream, otherwise zero.
+*/
+
+ZEXTERN int ZEXPORT gzclose OF((gzFile file));
+/*
+ Flushes all pending output if necessary, closes the compressed file
+ and deallocates all the (de)compression state. The return value is the zlib
+ error number (see function gzerror below).
+*/
+
+ZEXTERN const char * ZEXPORT gzerror OF((gzFile file, int *errnum));
+/*
+ Returns the error message for the last error which occurred on the
+ given compressed file. errnum is set to zlib error number. If an
+ error occurred in the file system and not in the compression library,
+ errnum is set to Z_ERRNO and the application may consult errno
+ to get the exact error code.
+*/
+
+ /* checksum functions */
+
+/*
+ These functions are not related to compression but are exported
+ anyway because they might be useful in applications using the
+ compression library.
+*/
+
+ZEXTERN uLong ZEXPORT adler32 OF((uLong adler, const Bytef *buf, uInt len));
+
+/*
+ Update a running Adler-32 checksum with the bytes buf[0..len-1] and
+ return the updated checksum. If buf is NULL, this function returns
+ the required initial value for the checksum.
+ An Adler-32 checksum is almost as reliable as a CRC32 but can be computed
+ much faster. Usage example:
+
+ uLong adler = adler32(0L, Z_NULL, 0);
+
+ while (read_buffer(buffer, length) != EOF) {
+ adler = adler32(adler, buffer, length);
+ }
+ if (adler != original_adler) error();
+*/
+
+ZEXTERN uLong ZEXPORT crc32 OF((uLong crc, const Bytef *buf, uInt len));
+/*
+ Update a running crc with the bytes buf[0..len-1] and return the updated
+ crc. If buf is NULL, this function returns the required initial value
+ for the crc. Pre- and post-conditioning (one's complement) is performed
+ within this function so it shouldn't be done by the application.
+ Usage example:
+
+ uLong crc = crc32(0L, Z_NULL, 0);
+
+ while (read_buffer(buffer, length) != EOF) {
+ crc = crc32(crc, buffer, length);
+ }
+ if (crc != original_crc) error();
+*/
+
+
+ /* various hacks, don't look :) */
+
+/* deflateInit and inflateInit are macros to allow checking the zlib version
+ * and the compiler's view of z_stream:
+ */
+ZEXTERN int ZEXPORT deflateInit_ OF((z_streamp strm, int level,
+ const char *version, int stream_size));
+ZEXTERN int ZEXPORT inflateInit_ OF((z_streamp strm,
+ const char *version, int stream_size));
+ZEXTERN int ZEXPORT deflateInit2_ OF((z_streamp strm, int level, int method,
+ int windowBits, int memLevel,
+ int strategy, const char *version,
+ int stream_size));
+ZEXTERN int ZEXPORT inflateInit2_ OF((z_streamp strm, int windowBits,
+ const char *version, int stream_size));
+#define deflateInit(strm, level) \
+ deflateInit_((strm), (level), ZLIB_VERSION, sizeof(z_stream))
+#define inflateInit(strm) \
+ inflateInit_((strm), ZLIB_VERSION, sizeof(z_stream))
+#define deflateInit2(strm, level, method, windowBits, memLevel, strategy) \
+ deflateInit2_((strm),(level),(method),(windowBits),(memLevel),\
+ (strategy), ZLIB_VERSION, sizeof(z_stream))
+#define inflateInit2(strm, windowBits) \
+ inflateInit2_((strm), (windowBits), ZLIB_VERSION, sizeof(z_stream))
+
+
+#if !defined(_Z_UTIL_H) && !defined(NO_DUMMY_DECL)
+ struct internal_state {int dummy;}; /* hack for buggy compilers */
+#endif
+
+ZEXTERN const char * ZEXPORT zError OF((int err));
+ZEXTERN int ZEXPORT inflateSyncPoint OF((z_streamp z));
+ZEXTERN const uLongf * ZEXPORT get_crc_table OF((void));
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _ZLIB_H */
diff --git a/zlib/zlib.html b/zlib/zlib.html
new file mode 100644
index 00000000..c3437038
--- /dev/null
+++ b/zlib/zlib.html
@@ -0,0 +1,971 @@
+<html>
+<head>
+ <title>
+ zlib general purpose compression library version 1.1.4
+ </title>
+</head>
+<body bgcolor="White" text="Black" vlink="Red" alink="Navy" link="Red">
+<!-- background="zlibbg.gif" -->
+
+<h1> zlib 1.1.4 Manual </h1>
+<hr>
+<a name="Contents"><h2>Contents</h2>
+<ol type="I">
+<li> <a href="#Prologue">Prologue</a>
+<li> <a href="#Introduction">Introduction</a>
+<li> <a href="#Utility functions">Utility functions</a>
+<li> <a href="#Basic functions">Basic functions</a>
+<li> <a href="#Advanced functions">Advanced functions</a>
+<li> <a href="#Constants">Constants</a>
+<li> <a href="#struct z_stream_s">struct z_stream_s</a>
+<li> <a href="#Checksum functions">Checksum functions</a>
+<li> <a href="#Misc">Misc</a>
+</ol>
+<hr>
+<a name="Prologue"><h2> Prologue </h2>
+ 'zlib' general purpose compression library version 1.1.4, March 11th, 2002
+ <p>
+ Copyright (C) 1995-2002 Jean-loup Gailly and Mark Adler
+ <p>
+ 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.
+ <p>
+ 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:
+ <ol>
+ <li> 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.
+ <li> Altered source versions must be plainly marked as such, and must not be
+ misrepresented as being the original software.
+ <li> This notice may not be removed or altered from any source distribution.
+ </ol>
+
+ <dl>
+ <dt>Jean-loup Gailly
+ <dd><a href="mailto:jloup@gzip.org">jloup@gzip.org</a>
+ <dt>Mark Adler
+ <dd><a href="mailto:madler@alumni.caltech.edu">madler@alumni.caltech.edu</a>
+ </dl>
+
+ The data format used by the zlib library is described by RFCs (Request for
+ Comments) 1950 to 1952 in the files
+ <a href="ftp://ds.internic.net/rfc/rfc1950.txt">
+ ftp://ds.internic.net/rfc/rfc1950.txt </a>
+ (zlib format),
+ <a href="ftp://ds.internic.net/rfc/rfc1951.txt">
+ rfc1951.txt </a>
+ (<a href="#deflate">deflate</a> format) and
+ <a href="ftp://ds.internic.net/rfc/rfc1952.txt">
+ rfc1952.txt </a>
+ (gzip format).
+ <p>
+ This manual is converted from zlib.h by
+ <a href="mailto:piaip@csie.ntu.edu.tw"> piaip </a>
+ <p>
+ Visit <a href="http://ftp.cdrom.com/pub/infozip/zlib/">
+ http://ftp.cdrom.com/pub/infozip/zlib/</a>
+ for the official zlib web page.
+ <p>
+
+<hr>
+<a name="Introduction"><h2> Introduction </h2>
+ The 'zlib' compression library provides in-memory compression and
+ decompression functions, including integrity checks of the uncompressed
+ data. This version of the library supports only one compression method
+ (deflation) but other algorithms will be added later and will have the same
+ stream interface.
+ <p>
+
+ Compression can be done in a single step if the buffers are large
+ enough (for example if an input file is mmap'ed), or can be done by
+ repeated calls of the compression function. In the latter case, the
+ application must provide more input and/or consume the output
+ (providing more output space) before each call.
+ <p>
+
+ The library also supports reading and writing files in gzip (.gz) format
+ with an interface similar to that of stdio.
+ <p>
+
+ The library does not install any signal handler. The decoder checks
+ the consistency of the compressed data, so the library should never
+ crash even in case of corrupted input.
+ <p>
+
+<hr>
+<a name="Utility functions"><h2> Utility functions </h2>
+ The following utility functions are implemented on top of the
+ <a href="#Basic functions">basic stream-oriented functions</a>.
+ To simplify the interface, some
+ default options are assumed (compression level and memory usage,
+ standard memory allocation functions). The source code of these
+ utility functions can easily be modified if you need special options.
+<h3> Function list </h3>
+<ul>
+<li> int <a href="#compress">compress</a> (Bytef *dest, uLongf *destLen, const Bytef *source, uLong sourceLen);
+<li> int <a href="#compress2">compress2</a> (Bytef *dest, uLongf *destLen, const Bytef *source, uLong sourceLen, int level);
+<li> int <a href="#uncompress">uncompress</a> (Bytef *dest, uLongf *destLen, const Bytef *source, uLong sourceLen);
+<li> typedef voidp gzFile;
+<li> gzFile <a href="#gzopen">gzopen</a> (const char *path, const char *mode);
+<li> gzFile <a href="#gzdopen">gzdopen</a> (int fd, const char *mode);
+<li> int <a href="#gzsetparams">gzsetparams</a> (gzFile file, int level, int strategy);
+<li> int <a href="#gzread">gzread</a> (gzFile file, voidp buf, unsigned len);
+<li> int <a href="#gzwrite">gzwrite</a> (gzFile file, const voidp buf, unsigned len);
+<li> int VA <a href="#gzprintf">gzprintf</a> (gzFile file, const char *format, ...);
+<li> int <a href="#gzputs">gzputs</a> (gzFile file, const char *s);
+<li> char * <a href="#gzgets">gzgets</a> (gzFile file, char *buf, int len);
+<li> int <a href="#gzputc">gzputc</a> (gzFile file, int c);
+<li> int <a href="#gzgetc">gzgetc</a> (gzFile file);
+<li> int <a href="#gzflush">gzflush</a> (gzFile file, int flush);
+<li> z_off_t <a href="#gzseek">gzseek</a> (gzFile file, z_off_t offset, int whence);
+<li> z_off_t <a href="#gztell">gztell</a> (gzFile file);
+<li> int <a href="#gzrewind">gzrewind</a> (gzFile file);
+<li> int <a href="#gzeof">gzeof</a> (gzFile file);
+<li> int <a href="#gzclose">gzclose</a> (gzFile file);
+<li> const char * <a href="#gzerror">gzerror</a> (gzFile file, int *errnum);
+</ul>
+<h3> Function description </h3>
+<dl>
+<font color="Blue"><dt> int <a name="compress">compress</a> (Bytef *dest, uLongf *destLen, const Bytef *source, uLong sourceLen);</font>
+<dd>
+ Compresses the source buffer into the destination buffer. sourceLen is
+ the byte length of the source buffer. Upon entry, destLen is the total
+ size of the destination buffer, which must be at least 0.1% larger than
+ sourceLen plus 12 bytes. Upon exit, destLen is the actual size of the
+ compressed buffer.<p>
+ This function can be used to <a href="#compress">compress</a> a whole file at once if the
+ input file is mmap'ed.<p>
+ <a href="#compress">compress</a> returns <a href="#Z_OK">Z_OK</a> if success, <a href="#Z_MEM_ERROR">Z_MEM_ERROR</a> if there was not
+ enough memory, <a href="#Z_BUF_ERROR">Z_BUF_ERROR</a> if there was not enough room in the output
+ buffer.<p>
+
+<font color="Blue"><dt> int <a name="compress2">compress2</a> (Bytef *dest, uLongf *destLen, const Bytef *source, uLong sourceLen, int level);</font>
+<dd>
+ Compresses the source buffer into the destination buffer. The level
+ parameter has the same meaning as in <a href="#deflateInit">deflateInit</a>. sourceLen is the byte
+ length of the source buffer. Upon entry, destLen is the total size of the
+ destination buffer, which must be at least 0.1% larger than sourceLen plus
+ 12 bytes. Upon exit, destLen is the actual size of the compressed buffer.
+ <p>
+
+ <a href="#compress2">compress2</a> returns <a href="#Z_OK">Z_OK</a> if success, <a href="#Z_MEM_ERROR">Z_MEM_ERROR</a> if there was not enough
+ memory, <a href="#Z_BUF_ERROR">Z_BUF_ERROR</a> if there was not enough room in the output buffer,
+ <a href="#Z_STREAM_ERROR">Z_STREAM_ERROR</a> if the level parameter is invalid.
+ <p>
+
+<font color="Blue"><dt> int <a name="uncompress">uncompress</a> (Bytef *dest, uLongf *destLen, const Bytef *source, uLong sourceLen);</font>
+<dd>
+ Decompresses the source buffer into the destination buffer. sourceLen is
+ the byte length of the source buffer. Upon entry, destLen is the total
+ size of the destination buffer, which must be large enough to hold the
+ entire uncompressed data. (The size of the uncompressed data must have
+ been saved previously by the compressor and transmitted to the decompressor
+ by some mechanism outside the scope of this compression library.)
+ Upon exit, destLen is the actual size of the compressed buffer. <p>
+ This function can be used to decompress a whole file at once if the
+ input file is mmap'ed.
+ <p>
+
+ <a href="#uncompress">uncompress</a> returns <a href="#Z_OK">Z_OK</a> if success, <a href="#Z_MEM_ERROR">Z_MEM_ERROR</a> if there was not
+ enough memory, <a href="#Z_BUF_ERROR">Z_BUF_ERROR</a> if there was not enough room in the output
+ buffer, or <a href="#Z_DATA_ERROR">Z_DATA_ERROR</a> if the input data was corrupted.
+ <p>
+
+<dt> typedef voidp gzFile;
+<dd> <p>
+
+<font color="Blue"><dt> gzFile <a name="gzopen">gzopen</a> (const char *path, const char *mode);</font>
+<dd>
+ Opens a gzip (.gz) file for reading or writing. The mode parameter
+ is as in fopen ("rb" or "wb") but can also include a compression level
+ ("wb9") or a strategy: 'f' for filtered data as in "wb6f", 'h' for
+ Huffman only compression as in "wb1h". (See the description
+ of <a href="#deflateInit2">deflateInit2</a> for more information about the strategy parameter.)
+ <p>
+
+ <a href="#gzopen">gzopen</a> can be used to read a file which is not in gzip format ; in this
+ case <a href="#gzread">gzread</a> will directly read from the file without decompression.
+ <p>
+
+ <a href="#gzopen">gzopen</a> returns NULL if the file could not be opened or if there was
+ insufficient memory to allocate the (de)compression <a href="#state">state</a> ; errno
+ can be checked to distinguish the two cases (if errno is zero, the
+ zlib error is <a href="#Z_MEM_ERROR">Z_MEM_ERROR</a>).
+ <p>
+
+<font color="Blue"><dt> gzFile <a name="gzdopen">gzdopen</a> (int fd, const char *mode);</font>
+<dd>
+ <a href="#gzdopen">gzdopen</a>() associates a gzFile with the file descriptor fd. File
+ descriptors are obtained from calls like open, dup, creat, pipe or
+ fileno (in the file has been previously opened with fopen).
+ The mode parameter is as in <a href="#gzopen">gzopen</a>.
+ <p>
+ The next call of <a href="#gzclose">gzclose</a> on the returned gzFile will also close the
+ file descriptor fd, just like fclose(fdopen(fd), mode) closes the file
+ descriptor fd. If you want to keep fd open, use <a href="#gzdopen">gzdopen</a>(dup(fd), mode).
+ <p>
+ <a href="#gzdopen">gzdopen</a> returns NULL if there was insufficient memory to allocate
+ the (de)compression <a href="#state">state</a>.
+ <p>
+
+<font color="Blue"><dt> int <a name="gzsetparams">gzsetparams</a> (gzFile file, int level, int strategy);</font>
+<dd>
+ Dynamically update the compression level or strategy. See the description
+ of <a href="#deflateInit2">deflateInit2</a> for the meaning of these parameters.
+ <p>
+ <a href="#gzsetparams">gzsetparams</a> returns <a href="#Z_OK">Z_OK</a> if success, or <a href="#Z_STREAM_ERROR">Z_STREAM_ERROR</a> if the file was not
+ opened for writing.
+ <p>
+
+<font color="Blue"><dt> int <a name="gzread">gzread</a> (gzFile file, voidp buf, unsigned len);</font>
+<dd>
+ Reads the given number of uncompressed bytes from the compressed file.
+ If the input file was not in gzip format, <a href="#gzread">gzread</a> copies the given number
+ of bytes into the buffer.
+ <p>
+ <a href="#gzread">gzread</a> returns the number of uncompressed bytes actually read (0 for
+ end of file, -1 for error).
+ <p>
+
+<font color="Blue"><dt> int <a name="gzwrite">gzwrite</a> (gzFile file, const voidp buf, unsigned len);</font>
+<dd>
+ Writes the given number of uncompressed bytes into the compressed file.
+ <a href="#gzwrite">gzwrite</a> returns the number of uncompressed bytes actually written
+ (0 in case of error).
+ <p>
+
+<font color="Blue"><dt> int VA <a name="gzprintf">gzprintf</a> (gzFile file, const char *format, ...);</font>
+<dd>
+ Converts, formats, and writes the args to the compressed file under
+ control of the format string, as in fprintf. <a href="#gzprintf">gzprintf</a> returns the number of
+ uncompressed bytes actually written (0 in case of error).
+ <p>
+
+<font color="Blue"><dt> int <a name="gzputs">gzputs</a> (gzFile file, const char *s);</font>
+<dd>
+ Writes the given null-terminated string to the compressed file, excluding
+ the terminating null character.
+ <p>
+ <a href="#gzputs">gzputs</a> returns the number of characters written, or -1 in case of error.
+ <p>
+
+<font color="Blue"><dt> char * <a name="gzgets">gzgets</a> (gzFile file, char *buf, int len);</font>
+<dd>
+ Reads bytes from the compressed file until len-1 characters are read, or
+ a newline character is read and transferred to buf, or an end-of-file
+ condition is encountered. The string is then terminated with a null
+ character.
+ <p>
+ <a href="#gzgets">gzgets</a> returns buf, or <a href="#Z_NULL">Z_NULL</a> in case of error.
+ <p>
+
+<font color="Blue"><dt> int <a name="gzputc">gzputc</a> (gzFile file, int c);</font>
+<dd>
+ Writes c, converted to an unsigned char, into the compressed file.
+ <a href="#gzputc">gzputc</a> returns the value that was written, or -1 in case of error.
+ <p>
+
+<font color="Blue"><dt> int <a name="gzgetc">gzgetc</a> (gzFile file);</font>
+<dd>
+ Reads one byte from the compressed file. <a href="#gzgetc">gzgetc</a> returns this byte
+ or -1 in case of end of file or error.
+ <p>
+
+<font color="Blue"><dt> int <a name="gzflush">gzflush</a> (gzFile file, int flush);</font>
+<dd>
+ Flushes all pending output into the compressed file. The parameter
+ flush is as in the <a href="#deflate">deflate</a>() function. The return value is the zlib
+ error number (see function <a href="#gzerror">gzerror</a> below). <a href="#gzflush">gzflush</a> returns <a href="#Z_OK">Z_OK</a> if
+ the flush parameter is <a href="#Z_FINISH">Z_FINISH</a> and all output could be flushed.
+ <p>
+ <a href="#gzflush">gzflush</a> should be called only when strictly necessary because it can
+ degrade compression.
+ <p>
+
+<font color="Blue"><dt> z_off_t <a name="gzseek">gzseek</a> (gzFile file, z_off_t offset, int whence);</font>
+<dd>
+ Sets the starting position for the next <a href="#gzread">gzread</a> or <a href="#gzwrite">gzwrite</a> on the
+ given compressed file. The offset represents a number of bytes in the
+ uncompressed data stream. The whence parameter is defined as in lseek(2);
+ the value SEEK_END is not supported.
+ <p>
+ If the file is opened for reading, this function is emulated but can be
+ extremely slow. If the file is opened for writing, only forward seeks are
+ supported ; <a href="#gzseek">gzseek</a> then compresses a sequence of zeroes up to the new
+ starting position.
+ <p>
+ <a href="#gzseek">gzseek</a> returns the resulting offset location as measured in bytes from
+ the beginning of the uncompressed stream, or -1 in case of error, in
+ particular if the file is opened for writing and the new starting position
+ would be before the current position.
+ <p>
+
+<font color="Blue"><dt> int <a name="gzrewind">gzrewind</a> (gzFile file);</font>
+<dd>
+ Rewinds the given file. This function is supported only for reading.
+ <p>
+ <a href="#gzrewind">gzrewind</a>(file) is equivalent to (int)<a href="#gzseek">gzseek</a>(file, 0L, SEEK_SET)
+ <p>
+
+<font color="Blue"><dt> z_off_t <a name="gztell">gztell</a> (gzFile file);</font>
+<dd>
+ Returns the starting position for the next <a href="#gzread">gzread</a> or <a href="#gzwrite">gzwrite</a> on the
+ given compressed file. This position represents a number of bytes in the
+ uncompressed data stream.
+ <p>
+
+ <a href="#gztell">gztell</a>(file) is equivalent to <a href="#gzseek">gzseek</a>(file, 0L, SEEK_CUR)
+ <p>
+
+<font color="Blue"><dt> int <a name="gzeof">gzeof</a> (gzFile file);</font>
+<dd>
+ Returns 1 when EOF has previously been detected reading the given
+ input stream, otherwise zero.
+ <p>
+
+<font color="Blue"><dt> int <a name="gzclose">gzclose</a> (gzFile file);</font>
+<dd>
+ Flushes all pending output if necessary, closes the compressed file
+ and deallocates all the (de)compression <a href="#state">state</a>. The return value is the zlib
+ error number (see function <a href="#gzerror">gzerror</a> below).
+ <p>
+
+<font color="Blue"><dt> const char * <a name="gzerror">gzerror</a> (gzFile file, int *errnum);</font>
+<dd>
+ Returns the error message for the last error which occurred on the
+ given compressed file. errnum is set to zlib error number. If an
+ error occurred in the file system and not in the compression library,
+ errnum is set to <a href="#Z_ERRNO">Z_ERRNO</a> and the application may consult errno
+ to get the exact error code.
+ <p>
+</dl>
+<hr>
+<a name="Basic functions"><h2> Basic functions </h2>
+<h3> Function list </h3>
+<ul>
+<li> const char * <a href="#zlibVersion">zlibVersion</a> (void);
+<li> int <a href="#deflateInit">deflateInit</a> (<a href="#z_streamp">z_streamp</a> strm, int level);
+<li> int <a href="#deflate">deflate</a> (<a href="#z_streamp">z_streamp</a> strm, int flush);
+<li> int <a href="#deflateEnd">deflateEnd</a> (<a href="#z_streamp">z_streamp</a> strm);
+<li> int <a href="#inflateInit">inflateInit</a> (<a href="#z_streamp">z_streamp</a> strm);
+<li> int <a href="#inflate">inflate</a> (<a href="#z_streamp">z_streamp</a> strm, int flush);
+<li> int <a href="#inflateEnd">inflateEnd</a> (<a href="#z_streamp">z_streamp</a> strm);
+</ul>
+
+<h3> Function description </h3>
+<dl>
+<font color="Blue"><dt> const char * <a name="zlibVersion">zlibVersion</a> (void);</font>
+<dd> The application can compare <a href="#zlibVersion">zlibVersion</a> and ZLIB_VERSION for consistency.
+ If the first character differs, the library code actually used is
+ not compatible with the zlib.h header file used by the application.
+ This check is automatically made by <a href="#deflateInit">deflateInit</a> and <a href="#inflateInit">inflateInit</a>.
+ <p>
+
+<font color="Blue"><dt> int <a name="deflateInit">deflateInit</a> (<a href="#z_streamp">z_streamp</a> strm, int level);</font>
+<dd>
+ Initializes the internal stream <a href="#state">state</a> for compression. The fields
+ <a href="#zalloc">zalloc</a>, <a href="#zfree">zfree</a> and <a href="#opaque">opaque</a> must be initialized before by the caller.
+ If <a href="#zalloc">zalloc</a> and <a href="#zfree">zfree</a> are set to <a href="#Z_NULL">Z_NULL</a>, <a href="#deflateInit">deflateInit</a> updates them to
+ use default allocation functions.
+ <p>
+
+ The compression level must be <a href="#Z_DEFAULT_COMPRESSION">Z_DEFAULT_COMPRESSION</a>, or between 0 and 9:
+ 1 gives best speed, 9 gives best compression, 0 gives no compression at
+ all (the input data is simply copied a block at a time).
+ <p>
+
+ <a href="#Z_DEFAULT_COMPRESSION">Z_DEFAULT_COMPRESSION</a> requests a default compromise between speed and
+ compression (currently equivalent to level 6).
+ <p>
+
+ <a href="#deflateInit">deflateInit</a> returns <a href="#Z_OK">Z_OK</a> if success, <a href="#Z_MEM_ERROR">Z_MEM_ERROR</a> if there was not
+ enough memory, <a href="#Z_STREAM_ERROR">Z_STREAM_ERROR</a> if level is not a valid compression level,
+ <a href="#Z_VERSION_ERROR">Z_VERSION_ERROR</a> if the zlib library version (<a href="#zlib_version">zlib_version</a>) is incompatible
+ with the version assumed by the caller (ZLIB_VERSION).
+ <a href="#msg">msg</a> is set to null if there is no error message. <a href="#deflateInit">deflateInit</a> does not
+ perform any compression: this will be done by <a href="#deflate">deflate</a>().
+ <p>
+
+<font color="Blue"><dt> int <a name="deflate">deflate</a> (<a href="#z_streamp">z_streamp</a> strm, int flush);</font>
+<dd>
+ <a href="#deflate">deflate</a> compresses as much data as possible, and stops when the input
+ buffer becomes empty or the output buffer becomes full. It may introduce some
+ output latency (reading input without producing any output) except when
+ forced to flush.<p>
+
+ The detailed semantics are as follows. <a href="#deflate">deflate</a> performs one or both of the
+ following actions:
+
+ <ul>
+ <li> Compress more input starting at <a href="#next_in">next_in</a> and update <a href="#next_in">next_in</a> and <a href="#avail_in">avail_in</a>
+ accordingly. If not all input can be processed (because there is not
+ enough room in the output buffer), <a href="#next_in">next_in</a> and <a href="#avail_in">avail_in</a> are updated and
+ processing will resume at this point for the next call of <a href="#deflate">deflate</a>().
+
+ <li>
+ Provide more output starting at <a href="#next_out">next_out</a> and update <a href="#next_out">next_out</a> and <a href="#avail_out">avail_out</a>
+ accordingly. This action is forced if the parameter flush is non zero.
+ Forcing flush frequently degrades the compression ratio, so this parameter
+ should be set only when necessary (in interactive applications).
+ Some output may be provided even if flush is not set.
+ </ul> <p>
+
+ Before the call of <a href="#deflate">deflate</a>(), the application should ensure that at least
+ one of the actions is possible, by providing more input and/or consuming
+ more output, and updating <a href="#avail_in">avail_in</a> or <a href="#avail_out">avail_out</a> accordingly ; <a href="#avail_out">avail_out</a>
+ should never be zero before the call. The application can consume the
+ compressed output when it wants, for example when the output buffer is full
+ (<a href="#avail_out">avail_out</a> == 0), or after each call of <a href="#deflate">deflate</a>(). If <a href="#deflate">deflate</a> returns <a href="#Z_OK">Z_OK</a>
+ and with zero <a href="#avail_out">avail_out</a>, it must be called again after making room in the
+ output buffer because there might be more output pending.
+ <p>
+
+ If the parameter flush is set to <a href="#Z_SYNC_FLUSH">Z_SYNC_FLUSH</a>, all pending output is
+ flushed to the output buffer and the output is aligned on a byte boundary, so
+ that the decompressor can get all input data available so far. (In particular
+ <a href="#avail_in">avail_in</a> is zero after the call if enough output space has been provided
+ before the call.) Flushing may degrade compression for some compression
+ algorithms and so it should be used only when necessary.
+ <p>
+
+ If flush is set to <a href="#Z_FULL_FLUSH">Z_FULL_FLUSH</a>, all output is flushed as with
+ <a href="#Z_SYNC_FLUSH">Z_SYNC_FLUSH</a>, and the compression <a href="#state">state</a> is reset so that decompression can
+ restart from this point if previous compressed data has been damaged or if
+ random access is desired. Using <a href="#Z_FULL_FLUSH">Z_FULL_FLUSH</a> too often can seriously degrade
+ the compression.
+ <p>
+
+ If <a href="#deflate">deflate</a> returns with <a href="#avail_out">avail_out</a> == 0, this function must be called again
+ with the same value of the flush parameter and more output space (updated
+ <a href="#avail_out">avail_out</a>), until the flush is complete (<a href="#deflate">deflate</a> returns with non-zero
+ <a href="#avail_out">avail_out</a>).
+ <p>
+
+ If the parameter flush is set to <a href="#Z_FINISH">Z_FINISH</a>, pending input is processed,
+ pending output is flushed and <a href="#deflate">deflate</a> returns with <a href="#Z_STREAM_END">Z_STREAM_END</a> if there
+ was enough output space ; if <a href="#deflate">deflate</a> returns with <a href="#Z_OK">Z_OK</a>, this function must be
+ called again with <a href="#Z_FINISH">Z_FINISH</a> and more output space (updated <a href="#avail_out">avail_out</a>) but no
+ more input data, until it returns with <a href="#Z_STREAM_END">Z_STREAM_END</a> or an error. After
+ <a href="#deflate">deflate</a> has returned <a href="#Z_STREAM_END">Z_STREAM_END</a>, the only possible operations on the
+ stream are <a href="#deflateReset">deflateReset</a> or <a href="#deflateEnd">deflateEnd</a>.
+ <p>
+
+ <a href="#Z_FINISH">Z_FINISH</a> can be used immediately after <a href="#deflateInit">deflateInit</a> if all the compression
+ is to be done in a single step. In this case, <a href="#avail_out">avail_out</a> must be at least
+ 0.1% larger than <a href="#avail_in">avail_in</a> plus 12 bytes. If <a href="#deflate">deflate</a> does not return
+ <a href="#Z_STREAM_END">Z_STREAM_END</a>, then it must be called again as described above.
+ <p>
+
+ <a href="#deflate">deflate</a>() sets strm-&gt <a href="#adler">adler</a> to the <a href="#adler32">adler32</a> checksum of all input read
+ so far (that is, <a href="#total_in">total_in</a> bytes).
+ <p>
+
+ <a href="#deflate">deflate</a>() may update <a href="#data_type">data_type</a> if it can make a good guess about
+ the input data type (<a href="#Z_ASCII">Z_ASCII</a> or <a href="#Z_BINARY">Z_BINARY</a>). In doubt, the data is considered
+ binary. This field is only for information purposes and does not affect
+ the compression algorithm in any manner.
+ <p>
+
+ <a href="#deflate">deflate</a>() returns <a href="#Z_OK">Z_OK</a> if some progress has been made (more input
+ processed or more output produced), <a href="#Z_STREAM_END">Z_STREAM_END</a> if all input has been
+ consumed and all output has been produced (only when flush is set to
+ <a href="#Z_FINISH">Z_FINISH</a>), <a href="#Z_STREAM_ERROR">Z_STREAM_ERROR</a> if the stream <a href="#state">state</a> was inconsistent (for example
+ if <a href="#next_in">next_in</a> or <a href="#next_out">next_out</a> was NULL), <a href="#Z_BUF_ERROR">Z_BUF_ERROR</a> if no progress is possible
+ (for example <a href="#avail_in">avail_in</a> or <a href="#avail_out">avail_out</a> was zero).
+ <p>
+
+<font color="Blue"><dt> int <a name="deflateEnd">deflateEnd</a> (<a href="#z_streamp">z_streamp</a> strm);</font>
+<dd>
+ All dynamically allocated data structures for this stream are freed.
+ This function discards any unprocessed input and does not flush any
+ pending output.
+ <p>
+
+ <a href="#deflateEnd">deflateEnd</a> returns <a href="#Z_OK">Z_OK</a> if success, <a href="#Z_STREAM_ERROR">Z_STREAM_ERROR</a> if the
+ stream <a href="#state">state</a> was inconsistent, <a href="#Z_DATA_ERROR">Z_DATA_ERROR</a> if the stream was freed
+ prematurely (some input or output was discarded). In the error case,
+ <a href="#msg">msg</a> may be set but then points to a static string (which must not be
+ deallocated).
+ <p>
+
+<font color="Blue"><dt> int <a name="inflateInit">inflateInit</a> (<a href="#z_streamp">z_streamp</a> strm);</font>
+<dd>
+ Initializes the internal stream <a href="#state">state</a> for decompression. The fields
+ <a href="#next_in">next_in</a>, <a href="#avail_in">avail_in</a>, <a href="#zalloc">zalloc</a>, <a href="#zfree">zfree</a> and <a href="#opaque">opaque</a> must be initialized before by
+ the caller. If <a href="#next_in">next_in</a> is not <a href="#Z_NULL">Z_NULL</a> and <a href="#avail_in">avail_in</a> is large enough (the exact
+ value depends on the compression method), <a href="#inflateInit">inflateInit</a> determines the
+ compression method from the zlib header and allocates all data structures
+ accordingly ; otherwise the allocation will be deferred to the first call of
+ <a href="#inflate">inflate</a>. If <a href="#zalloc">zalloc</a> and <a href="#zfree">zfree</a> are set to <a href="#Z_NULL">Z_NULL</a>, <a href="#inflateInit">inflateInit</a> updates them to
+ use default allocation functions.
+ <p>
+
+ <a href="#inflateInit">inflateInit</a> returns <a href="#Z_OK">Z_OK</a> if success, <a href="#Z_MEM_ERROR">Z_MEM_ERROR</a> if there was not enough
+ memory, <a href="#Z_VERSION_ERROR">Z_VERSION_ERROR</a> if the zlib library version is incompatible with the
+ version assumed by the caller. <a href="#msg">msg</a> is set to null if there is no error
+ message. <a href="#inflateInit">inflateInit</a> does not perform any decompression apart from reading
+ the zlib header if present: this will be done by <a href="#inflate">inflate</a>(). (So <a href="#next_in">next_in</a> and
+ <a href="#avail_in">avail_in</a> may be modified, but <a href="#next_out">next_out</a> and <a href="#avail_out">avail_out</a> are unchanged.)
+ <p>
+
+<font color="Blue"><dt> int <a name="inflate">inflate</a> (<a href="#z_streamp">z_streamp</a> strm, int flush);</font>
+<dd>
+ <a href="#inflate">inflate</a> decompresses as much data as possible, and stops when the input
+ buffer becomes empty or the output buffer becomes full. It may some
+ introduce some output latency (reading input without producing any output)
+ except when forced to flush.
+ <p>
+
+ The detailed semantics are as follows. <a href="#inflate">inflate</a> performs one or both of the
+ following actions:
+
+ <ul>
+ <li> Decompress more input starting at <a href="#next_in">next_in</a> and update <a href="#next_in">next_in</a> and <a href="#avail_in">avail_in</a>
+ accordingly. If not all input can be processed (because there is not
+ enough room in the output buffer), <a href="#next_in">next_in</a> is updated and processing
+ will resume at this point for the next call of <a href="#inflate">inflate</a>().
+
+ <li> Provide more output starting at <a href="#next_out">next_out</a> and update <a href="#next_out">next_out</a> and
+ <a href="#avail_out">avail_out</a> accordingly. <a href="#inflate">inflate</a>() provides as much output as possible,
+ until there is no more input data or no more space in the output buffer
+ (see below about the flush parameter).
+ </ul> <p>
+
+ Before the call of <a href="#inflate">inflate</a>(), the application should ensure that at least
+ one of the actions is possible, by providing more input and/or consuming
+ more output, and updating the next_* and avail_* values accordingly.
+ The application can consume the uncompressed output when it wants, for
+ example when the output buffer is full (<a href="#avail_out">avail_out</a> == 0), or after each
+ call of <a href="#inflate">inflate</a>(). If <a href="#inflate">inflate</a> returns <a href="#Z_OK">Z_OK</a> and with zero <a href="#avail_out">avail_out</a>, it
+ must be called again after making room in the output buffer because there
+ might be more output pending.
+ <p>
+
+ If the parameter flush is set to <a href="#Z_SYNC_FLUSH">Z_SYNC_FLUSH</a>, <a href="#inflate">inflate</a> flushes as much
+ output as possible to the output buffer. The flushing behavior of <a href="#inflate">inflate</a> is
+ not specified for values of the flush parameter other than <a href="#Z_SYNC_FLUSH">Z_SYNC_FLUSH</a>
+ and <a href="#Z_FINISH">Z_FINISH</a>, but the current implementation actually flushes as much output
+ as possible anyway.
+ <p>
+
+ <a href="#inflate">inflate</a>() should normally be called until it returns <a href="#Z_STREAM_END">Z_STREAM_END</a> or an
+ error. However if all decompression is to be performed in a single step
+ (a single call of <a href="#inflate">inflate</a>), the parameter flush should be set to
+ <a href="#Z_FINISH">Z_FINISH</a>. In this case all pending input is processed and all pending
+ output is flushed ; <a href="#avail_out">avail_out</a> must be large enough to hold all the
+ uncompressed data. (The size of the uncompressed data may have been saved
+ by the compressor for this purpose.) The next operation on this stream must
+ be <a href="#inflateEnd">inflateEnd</a> to deallocate the decompression <a href="#state">state</a>. The use of <a href="#Z_FINISH">Z_FINISH</a>
+ is never required, but can be used to inform <a href="#inflate">inflate</a> that a faster routine
+ may be used for the single <a href="#inflate">inflate</a>() call.
+ <p>
+
+ If a preset dictionary is needed at this point (see <a href="#inflateSetDictionary">inflateSetDictionary</a>
+ below), <a href="#inflate">inflate</a> sets strm-<a href="#adler">adler</a> to the <a href="#adler32">adler32</a> checksum of the
+ dictionary chosen by the compressor and returns <a href="#Z_NEED_DICT">Z_NEED_DICT</a> ; otherwise
+ it sets strm-&gt <a href="#adler">adler</a> to the <a href="#adler32">adler32</a> checksum of all output produced
+ so far (that is, <a href="#total_out">total_out</a> bytes) and returns <a href="#Z_OK">Z_OK</a>, <a href="#Z_STREAM_END">Z_STREAM_END</a> or
+ an error code as described below. At the end of the stream, <a href="#inflate">inflate</a>()
+ checks that its computed <a href="#adler32">adler32</a> checksum is equal to that saved by the
+ compressor and returns <a href="#Z_STREAM_END">Z_STREAM_END</a> only if the checksum is correct.
+ <p>
+
+ <a href="#inflate">inflate</a>() returns <a href="#Z_OK">Z_OK</a> if some progress has been made (more input processed
+ or more output produced), <a href="#Z_STREAM_END">Z_STREAM_END</a> if the end of the compressed data has
+ been reached and all uncompressed output has been produced, <a href="#Z_NEED_DICT">Z_NEED_DICT</a> if a
+ preset dictionary is needed at this point, <a href="#Z_DATA_ERROR">Z_DATA_ERROR</a> if the input data was
+ corrupted (input stream not conforming to the zlib format or incorrect
+ <a href="#adler32">adler32</a> checksum), <a href="#Z_STREAM_ERROR">Z_STREAM_ERROR</a> if the stream structure was inconsistent
+ (for example if <a href="#next_in">next_in</a> or <a href="#next_out">next_out</a> was NULL), <a href="#Z_MEM_ERROR">Z_MEM_ERROR</a> if there was not
+ enough memory, <a href="#Z_BUF_ERROR">Z_BUF_ERROR</a> if no progress is possible or if there was not
+ enough room in the output buffer when <a href="#Z_FINISH">Z_FINISH</a> is used. In the <a href="#Z_DATA_ERROR">Z_DATA_ERROR</a>
+ case, the application may then call <a href="#inflateSync">inflateSync</a> to look for a good
+ compression block.
+ <p>
+
+<font color="Blue"><dt> int <a name="inflateEnd">inflateEnd</a> (<a href="#z_streamp">z_streamp</a> strm);</font>
+<dd>
+ All dynamically allocated data structures for this stream are freed.
+ This function discards any unprocessed input and does not flush any
+ pending output.
+ <p>
+
+ <a href="#inflateEnd">inflateEnd</a> returns <a href="#Z_OK">Z_OK</a> if success, <a href="#Z_STREAM_ERROR">Z_STREAM_ERROR</a> if the stream <a href="#state">state</a>
+ was inconsistent. In the error case, <a href="#msg">msg</a> may be set but then points to a
+ static string (which must not be deallocated).
+</dl>
+<hr>
+<a name="Advanced functions"><h2> Advanced functions </h2>
+ The following functions are needed only in some special applications.
+<h3> Function list </h3>
+<ul>
+<li> int <a href="#deflateInit2">deflateInit2</a> (<a href="#z_streamp">z_streamp</a> strm,
+<li> int <a href="#deflateSetDictionary">deflateSetDictionary</a> (<a href="#z_streamp">z_streamp</a> strm, const Bytef *dictionary, uInt dictLength);
+<li> int <a href="#deflateCopy">deflateCopy</a> (<a href="#z_streamp">z_streamp</a> dest, <a href="#z_streamp">z_streamp</a> source);
+<li> int <a href="#deflateReset">deflateReset</a> (<a href="#z_streamp">z_streamp</a> strm);
+<li> int <a href="#deflateParams">deflateParams</a> (<a href="#z_streamp">z_streamp</a> strm, int level, int strategy);
+<li> int <a href="#inflateInit2">inflateInit2</a> (<a href="#z_streamp">z_streamp</a> strm, int windowBits);
+<li> int <a href="#inflateSetDictionary">inflateSetDictionary</a> (<a href="#z_streamp">z_streamp</a> strm, const Bytef *dictionary, uInt dictLength);
+<li> int <a href="#inflateSync">inflateSync</a> (<a href="#z_streamp">z_streamp</a> strm);
+<li> int <a href="#inflateReset">inflateReset</a> (<a href="#z_streamp">z_streamp</a> strm);
+
+</ul>
+<h3> Function description </h3>
+<dl>
+<font color="Blue"><dt> int <a name="deflateInit2">deflateInit2</a> (<a href="#z_streamp">z_streamp</a> strm, int level, int method, int windowBits, int memLevel, int strategy);</font>
+
+<dd> This is another version of <a href="#deflateInit">deflateInit</a> with more compression options. The
+ fields <a href="#next_in">next_in</a>, <a href="#zalloc">zalloc</a>, <a href="#zfree">zfree</a> and <a href="#opaque">opaque</a> must be initialized before by
+ the caller.<p>
+
+ The method parameter is the compression method. It must be <a href="#Z_DEFLATED">Z_DEFLATED</a> in
+ this version of the library.<p>
+
+ The windowBits parameter is the base two logarithm of the window size
+ (the size of the history buffer). It should be in the range 8..15 for this
+ version of the library. Larger values of this parameter result in better
+ compression at the expense of memory usage. The default value is 15 if
+ <a href="#deflateInit">deflateInit</a> is used instead.<p>
+
+ The memLevel parameter specifies how much memory should be allocated
+ for the internal compression <a href="#state">state</a>. memLevel=1 uses minimum memory but
+ is slow and reduces compression ratio ; memLevel=9 uses maximum memory
+ for optimal speed. The default value is 8. See zconf.h for total memory
+ usage as a function of windowBits and memLevel.<p>
+
+ The strategy parameter is used to tune the compression algorithm. Use the
+ value <a href="#Z_DEFAULT_STRATEGY">Z_DEFAULT_STRATEGY</a> for normal data, <a href="#Z_FILTERED">Z_FILTERED</a> for data produced by a
+ filter (or predictor), or <a href="#Z_HUFFMAN_ONLY">Z_HUFFMAN_ONLY</a> to force Huffman encoding only (no
+ string match). Filtered data consists mostly of small values with a
+ somewhat random distribution. In this case, the compression algorithm is
+ tuned to <a href="#compress">compress</a> them better. The effect of <a href="#Z_FILTERED">Z_FILTERED</a> is to force more
+ Huffman coding and less string matching ; it is somewhat intermediate
+ between Z_DEFAULT and <a href="#Z_HUFFMAN_ONLY">Z_HUFFMAN_ONLY</a>. The strategy parameter only affects
+ the compression ratio but not the correctness of the compressed output even
+ if it is not set appropriately.<p>
+
+ <a href="#deflateInit2">deflateInit2</a> returns <a href="#Z_OK">Z_OK</a> if success, <a href="#Z_MEM_ERROR">Z_MEM_ERROR</a> if there was not enough
+ memory, <a href="#Z_STREAM_ERROR">Z_STREAM_ERROR</a> if a parameter is invalid (such as an invalid
+ method). <a href="#msg">msg</a> is set to null if there is no error message. <a href="#deflateInit2">deflateInit2</a> does
+ not perform any compression: this will be done by <a href="#deflate">deflate</a>().<p>
+
+<font color="Blue"><dt> int <a name="deflateSetDictionary">deflateSetDictionary</a> (<a href="#z_streamp">z_streamp</a> strm, const Bytef *dictionary, uInt dictLength);</font>
+<dd>
+ Initializes the compression dictionary from the given byte sequence
+ without producing any compressed output. This function must be called
+ immediately after <a href="#deflateInit">deflateInit</a>, <a href="#deflateInit2">deflateInit2</a> or <a href="#deflateReset">deflateReset</a>, before any
+ call of <a href="#deflate">deflate</a>. The compressor and decompressor must use exactly the same
+ dictionary (see <a href="#inflateSetDictionary">inflateSetDictionary</a>).<p>
+
+ The dictionary should consist of strings (byte sequences) that are likely
+ to be encountered later in the data to be compressed, with the most commonly
+ used strings preferably put towards the end of the dictionary. Using a
+ dictionary is most useful when the data to be compressed is short and can be
+ predicted with good accuracy ; the data can then be compressed better than
+ with the default empty dictionary.<p>
+
+ Depending on the size of the compression data structures selected by
+ <a href="#deflateInit">deflateInit</a> or <a href="#deflateInit2">deflateInit2</a>, a part of the dictionary may in effect be
+ discarded, for example if the dictionary is larger than the window size in
+ <a href="#deflate">deflate</a> or deflate2. Thus the strings most likely to be useful should be
+ put at the end of the dictionary, not at the front.<p>
+
+ Upon return of this function, strm-&gt <a href="#adler">adler</a> is set to the Adler32 value
+ of the dictionary ; the decompressor may later use this value to determine
+ which dictionary has been used by the compressor. (The Adler32 value
+ applies to the whole dictionary even if only a subset of the dictionary is
+ actually used by the compressor.)<p>
+
+ <a href="#deflateSetDictionary">deflateSetDictionary</a> returns <a href="#Z_OK">Z_OK</a> if success, or <a href="#Z_STREAM_ERROR">Z_STREAM_ERROR</a> if a
+ parameter is invalid (such as NULL dictionary) or the stream <a href="#state">state</a> is
+ inconsistent (for example if <a href="#deflate">deflate</a> has already been called for this stream
+ or if the compression method is bsort). <a href="#deflateSetDictionary">deflateSetDictionary</a> does not
+ perform any compression: this will be done by <a href="#deflate">deflate</a>().<p>
+
+<font color="Blue"><dt> int <a name="deflateCopy">deflateCopy</a> (<a href="#z_streamp">z_streamp</a> dest, <a href="#z_streamp">z_streamp</a> source);</font>
+<dd>
+ Sets the destination stream as a complete copy of the source stream.<p>
+
+ This function can be useful when several compression strategies will be
+ tried, for example when there are several ways of pre-processing the input
+ data with a filter. The streams that will be discarded should then be freed
+ by calling <a href="#deflateEnd">deflateEnd</a>. Note that <a href="#deflateCopy">deflateCopy</a> duplicates the internal
+ compression <a href="#state">state</a> which can be quite large, so this strategy is slow and
+ can consume lots of memory.<p>
+
+ <a href="#deflateCopy">deflateCopy</a> returns <a href="#Z_OK">Z_OK</a> if success, <a href="#Z_MEM_ERROR">Z_MEM_ERROR</a> if there was not
+ enough memory, <a href="#Z_STREAM_ERROR">Z_STREAM_ERROR</a> if the source stream <a href="#state">state</a> was inconsistent
+ (such as <a href="#zalloc">zalloc</a> being NULL). <a href="#msg">msg</a> is left unchanged in both source and
+ destination.<p>
+
+<font color="Blue"><dt> int <a name="deflateReset">deflateReset</a> (<a href="#z_streamp">z_streamp</a> strm);</font>
+<dd> This function is equivalent to <a href="#deflateEnd">deflateEnd</a> followed by <a href="#deflateInit">deflateInit</a>,
+ but does not free and reallocate all the internal compression <a href="#state">state</a>.
+ The stream will keep the same compression level and any other attributes
+ that may have been set by <a href="#deflateInit2">deflateInit2</a>.<p>
+
+ <a href="#deflateReset">deflateReset</a> returns <a href="#Z_OK">Z_OK</a> if success, or <a href="#Z_STREAM_ERROR">Z_STREAM_ERROR</a> if the source
+ stream <a href="#state">state</a> was inconsistent (such as <a href="#zalloc">zalloc</a> or <a href="#state">state</a> being NULL).<p>
+
+<font color="Blue"><dt> int <a name="deflateParams">deflateParams</a> (<a href="#z_streamp">z_streamp</a> strm, int level, int strategy);</font>
+<dd>
+ Dynamically update the compression level and compression strategy. The
+ interpretation of level and strategy is as in <a href="#deflateInit2">deflateInit2</a>. This can be
+ used to switch between compression and straight copy of the input data, or
+ to switch to a different kind of input data requiring a different
+ strategy. If the compression level is changed, the input available so far
+ is compressed with the old level (and may be flushed); the new level will
+ take effect only at the next call of <a href="#deflate">deflate</a>().<p>
+
+ Before the call of <a href="#deflateParams">deflateParams</a>, the stream <a href="#state">state</a> must be set as for
+ a call of <a href="#deflate">deflate</a>(), since the currently available input may have to
+ be compressed and flushed. In particular, strm-&gt <a href="#avail_out">avail_out</a> must be
+ non-zero.<p>
+
+ <a href="#deflateParams">deflateParams</a> returns <a href="#Z_OK">Z_OK</a> if success, <a href="#Z_STREAM_ERROR">Z_STREAM_ERROR</a> if the source
+ stream <a href="#state">state</a> was inconsistent or if a parameter was invalid, <a href="#Z_BUF_ERROR">Z_BUF_ERROR</a>
+ if strm-&gtavail_out was zero.<p>
+
+<font color="Blue"><dt> int <a name="inflateInit2">inflateInit2</a> (<a href="#z_streamp">z_streamp</a> strm, int windowBits);</font>
+
+<dd> This is another version of <a href="#inflateInit">inflateInit</a> with an extra parameter. The
+ fields <a href="#next_in">next_in</a>, <a href="#avail_in">avail_in</a>, <a href="#zalloc">zalloc</a>, <a href="#zfree">zfree</a> and <a href="#opaque">opaque</a> must be initialized
+ before by the caller.<p>
+
+ The windowBits parameter is the base two logarithm of the maximum window
+ size (the size of the history buffer). It should be in the range 8..15 for
+ this version of the library. The default value is 15 if <a href="#inflateInit">inflateInit</a> is used
+ instead. If a compressed stream with a larger window size is given as
+ input, <a href="#inflate">inflate</a>() will return with the error code <a href="#Z_DATA_ERROR">Z_DATA_ERROR</a> instead of
+ trying to allocate a larger window.<p>
+
+ <a href="#inflateInit2">inflateInit2</a> returns <a href="#Z_OK">Z_OK</a> if success, <a href="#Z_MEM_ERROR">Z_MEM_ERROR</a> if there was not enough
+ memory, <a href="#Z_STREAM_ERROR">Z_STREAM_ERROR</a> if a parameter is invalid (such as a negative
+ memLevel). <a href="#msg">msg</a> is set to null if there is no error message. <a href="#inflateInit2">inflateInit2</a>
+ does not perform any decompression apart from reading the zlib header if
+ present: this will be done by <a href="#inflate">inflate</a>(). (So <a href="#next_in">next_in</a> and <a href="#avail_in">avail_in</a> may be
+ modified, but <a href="#next_out">next_out</a> and <a href="#avail_out">avail_out</a> are unchanged.)<p>
+
+<font color="Blue"><dt> int <a name="inflateSetDictionary">inflateSetDictionary</a> (<a href="#z_streamp">z_streamp</a> strm, const Bytef *dictionary, uInt dictLength);</font>
+<dd>
+ Initializes the decompression dictionary from the given uncompressed byte
+ sequence. This function must be called immediately after a call of <a href="#inflate">inflate</a>
+ if this call returned <a href="#Z_NEED_DICT">Z_NEED_DICT</a>. The dictionary chosen by the compressor
+ can be determined from the Adler32 value returned by this call of
+ <a href="#inflate">inflate</a>. The compressor and decompressor must use exactly the same
+ dictionary (see <a href="#deflateSetDictionary">deflateSetDictionary</a>).<p>
+
+ <a href="#inflateSetDictionary">inflateSetDictionary</a> returns <a href="#Z_OK">Z_OK</a> if success, <a href="#Z_STREAM_ERROR">Z_STREAM_ERROR</a> if a
+ parameter is invalid (such as NULL dictionary) or the stream <a href="#state">state</a> is
+ inconsistent, <a href="#Z_DATA_ERROR">Z_DATA_ERROR</a> if the given dictionary doesn't match the
+ expected one (incorrect Adler32 value). <a href="#inflateSetDictionary">inflateSetDictionary</a> does not
+ perform any decompression: this will be done by subsequent calls of
+ <a href="#inflate">inflate</a>().<p>
+
+<font color="Blue"><dt> int <a name="inflateSync">inflateSync</a> (<a href="#z_streamp">z_streamp</a> strm);</font>
+
+<dd> Skips invalid compressed data until a full flush point (see above the
+ description of <a href="#deflate">deflate</a> with <a href="#Z_FULL_FLUSH">Z_FULL_FLUSH</a>) can be found, or until all
+ available input is skipped. No output is provided.<p>
+
+ <a href="#inflateSync">inflateSync</a> returns <a href="#Z_OK">Z_OK</a> if a full flush point has been found, <a href="#Z_BUF_ERROR">Z_BUF_ERROR</a>
+ if no more input was provided, <a href="#Z_DATA_ERROR">Z_DATA_ERROR</a> if no flush point has been found,
+ or <a href="#Z_STREAM_ERROR">Z_STREAM_ERROR</a> if the stream structure was inconsistent. In the success
+ case, the application may save the current current value of <a href="#total_in">total_in</a> which
+ indicates where valid compressed data was found. In the error case, the
+ application may repeatedly call <a href="#inflateSync">inflateSync</a>, providing more input each time,
+ until success or end of the input data.<p>
+
+<font color="Blue"><dt> int <a name="inflateReset">inflateReset</a> (<a href="#z_streamp">z_streamp</a> strm);</font>
+<dd>
+ This function is equivalent to <a href="#inflateEnd">inflateEnd</a> followed by <a href="#inflateInit">inflateInit</a>,
+ but does not free and reallocate all the internal decompression <a href="#state">state</a>.
+ The stream will keep attributes that may have been set by <a href="#inflateInit2">inflateInit2</a>.
+ <p>
+
+ <a href="#inflateReset">inflateReset</a> returns <a href="#Z_OK">Z_OK</a> if success, or <a href="#Z_STREAM_ERROR">Z_STREAM_ERROR</a> if the source
+ stream <a href="#state">state</a> was inconsistent (such as <a href="#zalloc">zalloc</a> or <a href="#state">state</a> being NULL).
+ <p>
+</dl>
+
+<hr>
+<a name="Checksum functions"><h2> Checksum functions </h2>
+ These functions are not related to compression but are exported
+ anyway because they might be useful in applications using the
+ compression library.
+<h3> Function list </h3>
+<ul>
+<li> uLong <a href="#adler32">adler32</a> (uLong <a href="#adler">adler</a>, const Bytef *buf, uInt len);
+<li> uLong <a href="#crc32">crc32</a> (uLong crc, const Bytef *buf, uInt len);
+</ul>
+<h3> Function description </h3>
+<dl>
+<font color="Blue"><dt> uLong <a name="adler32">adler32</a> (uLong <a href="#adler">adler</a>, const Bytef *buf, uInt len);</font>
+<dd>
+ Update a running Adler-32 checksum with the bytes buf[0..len-1] and
+ return the updated checksum. If buf is NULL, this function returns
+ the required initial value for the checksum.
+ <p>
+ An Adler-32 checksum is almost as reliable as a CRC32 but can be computed
+ much faster. Usage example:
+ <pre>
+
+ uLong <a href="#adler">adler</a> = <a href="#adler32">adler32</a>(0L, <a href="#Z_NULL">Z_NULL</a>, 0);
+
+ while (read_buffer(buffer, length) != EOF) {
+ <a href="#adler">adler</a> = <a href="#adler32">adler32</a>(<a href="#adler">adler</a>, buffer, length);
+ }
+ if (<a href="#adler">adler</a> != original_adler) error();
+ </pre>
+
+<font color="Blue"><dt> uLong <a name="crc32">crc32</a> (uLong crc, const Bytef *buf, uInt len);</font>
+<dd>
+ Update a running crc with the bytes buf[0..len-1] and return the updated
+ crc. If buf is NULL, this function returns the required initial value
+ for the crc. Pre- and post-conditioning (one's complement) is performed
+ within this function so it shouldn't be done by the application.
+ Usage example:
+ <pre>
+
+ uLong crc = <a href="#crc32">crc32</a>(0L, <a href="#Z_NULL">Z_NULL</a>, 0);
+
+ while (read_buffer(buffer, length) != EOF) {
+ crc = <a href="#crc32">crc32</a>(crc, buffer, length);
+ }
+ if (crc != original_crc) error();
+ </pre>
+</dl>
+<hr>
+<a name="struct z_stream_s"><h2> struct z_stream_s </h2>
+<font color="Blue">
+<a name="z_stream_s">
+<pre>
+typedef struct z_stream_s {
+ Bytef *<a name="next_in">next_in</a>; /* next input byte */
+ uInt <a name="avail_in">avail_in</a>; /* number of bytes available at <a href="#next_in">next_in</a> */
+ uLong <a name="total_in">total_in</a>; /* total nb of input bytes read so far */
+
+ Bytef *<a name="next_out">next_out</a>; /* next output byte should be put there */
+ uInt <a name="avail_out">avail_out</a>; /* remaining free space at <a href="#next_out">next_out</a> */
+ uLong <a name="total_out">total_out</a>; /* total nb of bytes output so far */
+
+ char *<a name="msg">msg</a>; /* last error message, NULL if no error */
+ struct internal_state FAR *<a name="state">state</a>; /* not visible by applications */
+
+ alloc_func <a name="zalloc">zalloc</a>; /* used to allocate the internal <a href="#state">state</a> */
+ free_func <a name="zfree">zfree</a>; /* used to free the internal <a href="#state">state</a> */
+ voidpf <a name="opaque">opaque</a>; /* private data object passed to <a href="#zalloc">zalloc</a> and <a href="#zfree">zfree</a> */
+
+ int <a name="data_type">data_type</a>; /* best guess about the data type: ascii or binary */
+ uLong <a name="adler">adler</a>; /* <a href="#adler32">adler32</a> value of the uncompressed data */
+ uLong <a name="reserved">reserved</a>; /* <a href="#reserved">reserved</a> for future use */
+} <a href="#z_stream_s">z_stream</a> ;
+
+typedef <a href="#z_stream_s">z_stream</a> FAR * <a name="z_streamp">z_streamp</a>; ÿ
+</pre>
+</font>
+ The application must update <a href="#next_in">next_in</a> and <a href="#avail_in">avail_in</a> when <a href="#avail_in">avail_in</a> has
+ dropped to zero. It must update <a href="#next_out">next_out</a> and <a href="#avail_out">avail_out</a> when <a href="#avail_out">avail_out</a>
+ has dropped to zero. The application must initialize <a href="#zalloc">zalloc</a>, <a href="#zfree">zfree</a> and
+ <a href="#opaque">opaque</a> before calling the init function. All other fields are set by the
+ compression library and must not be updated by the application. <p>
+
+ The <a href="#opaque">opaque</a> value provided by the application will be passed as the first
+ parameter for calls of <a href="#zalloc">zalloc</a> and <a href="#zfree">zfree</a>. This can be useful for custom
+ memory management. The compression library attaches no meaning to the
+ <a href="#opaque">opaque</a> value. <p>
+
+ <a href="#zalloc">zalloc</a> must return <a href="#Z_NULL">Z_NULL</a> if there is not enough memory for the object.
+ If zlib is used in a multi-threaded application, <a href="#zalloc">zalloc</a> and <a href="#zfree">zfree</a> must be
+ thread safe. <p>
+
+ On 16-bit systems, the functions <a href="#zalloc">zalloc</a> and <a href="#zfree">zfree</a> must be able to allocate
+ exactly 65536 bytes, but will not be required to allocate more than this
+ if the symbol MAXSEG_64K is defined (see zconf.h). WARNING: On MSDOS,
+ pointers returned by <a href="#zalloc">zalloc</a> for objects of exactly 65536 bytes *must*
+ have their offset normalized to zero. The default allocation function
+ provided by this library ensures this (see zutil.c). To reduce memory
+ requirements and avoid any allocation of 64K objects, at the expense of
+ compression ratio, compile the library with -DMAX_WBITS=14 (see zconf.h).
+ <p>
+
+ The fields <a href="#total_in">total_in</a> and <a href="#total_out">total_out</a> can be used for statistics or
+ progress reports. After compression, <a href="#total_in">total_in</a> holds the total size of
+ the uncompressed data and may be saved for use in the decompressor
+ (particularly if the decompressor wants to decompress everything in
+ a single step). <p>
+
+<hr>
+<a name="Constants"><h2> Constants </h2>
+<font color="Blue">
+<pre>
+#define <a name="Z_NO_FLUSH">Z_NO_FLUSH</a> 0
+#define <a name="Z_PARTIAL_FLUSH">Z_PARTIAL_FLUSH</a> 1
+ /* will be removed, use <a href="#Z_SYNC_FLUSH">Z_SYNC_FLUSH</a> instead */
+#define <a name="Z_SYNC_FLUSH">Z_SYNC_FLUSH</a> 2
+#define <a name="Z_FULL_FLUSH">Z_FULL_FLUSH</a> 3
+#define <a name="Z_FINISH">Z_FINISH</a> 4
+/* Allowed flush values ; see <a href="#deflate">deflate</a>() below for details */
+
+#define <a name="Z_OK">Z_OK</a> 0
+#define <a name="Z_STREAM_END">Z_STREAM_END</a> 1
+#define <a name="Z_NEED_DICT">Z_NEED_DICT</a> 2
+#define <a name="Z_ERRNO">Z_ERRNO</a> (-1)
+#define <a name="Z_STREAM_ERROR">Z_STREAM_ERROR</a> (-2)
+#define <a name="Z_DATA_ERROR">Z_DATA_ERROR</a> (-3)
+#define <a name="Z_MEM_ERROR">Z_MEM_ERROR</a> (-4)
+#define <a name="Z_BUF_ERROR">Z_BUF_ERROR</a> (-5)
+#define <a name="Z_VERSION_ERROR">Z_VERSION_ERROR</a> (-6)
+/* Return codes for the compression/decompression functions. Negative
+ * values are errors, positive values are used for special but normal events.
+ */
+
+#define <a name="Z_NO_COMPRESSION">Z_NO_COMPRESSION</a> 0
+#define <a name="Z_BEST_SPEED">Z_BEST_SPEED</a> 1
+#define <a name="Z_BEST_COMPRESSION">Z_BEST_COMPRESSION</a> 9
+#define <a name="Z_DEFAULT_COMPRESSION">Z_DEFAULT_COMPRESSION</a> (-1)
+/* compression levels */
+
+#define <a name="Z_FILTERED">Z_FILTERED</a> 1
+#define <a name="Z_HUFFMAN_ONLY">Z_HUFFMAN_ONLY</a> 2
+#define <a name="Z_DEFAULT_STRATEGY">Z_DEFAULT_STRATEGY</a> 0
+/* compression strategy ; see <a href="#deflateInit2">deflateInit2</a>() below for details */
+
+#define <a name="Z_BINARY">Z_BINARY</a> 0
+#define <a name="Z_ASCII">Z_ASCII</a> 1
+#define <a name="Z_UNKNOWN">Z_UNKNOWN</a> 2
+/* Possible values of the <a href="#data_type">data_type</a> field */
+
+#define <a name="Z_DEFLATED">Z_DEFLATED</a> 8
+/* The <a href="#deflate">deflate</a> compression method (the only one supported in this version) */
+
+#define <a name="Z_NULL">Z_NULL</a> 0 /* for initializing <a href="#zalloc">zalloc</a>, <a href="#zfree">zfree</a>, <a href="#opaque">opaque</a> */
+
+#define <a name="zlib_version">zlib_version</a> <a href="#zlibVersion">zlibVersion</a>()
+/* for compatibility with versions less than 1.0.2 */
+</pre>
+</font>
+
+<hr>
+<a name="Misc"><h2> Misc </h2>
+ <a href="#deflateInit">deflateInit</a> and <a href="#inflateInit">inflateInit</a> are macros to allow checking the zlib version
+ and the compiler's view of <a href="#z_stream_s">z_stream</a>.
+ <p>
+ Other functions:
+ <dl>
+ <font color="Blue"><dt> const char * <a name="zError">zError</a> (int err);</font>
+ <font color="Blue"><dt> int <a name="inflateSyncPoint">inflateSyncPoint</a> (<a href="#z_streamp">z_streamp</a> z);</font>
+ <font color="Blue"><dt> const uLongf * <a name="get_crc_table">get_crc_table</a> (void);</font>
+ </dl>
+ <hr>
+ <font size="-1">
+ Last update: Wed Oct 13 20:42:34 1999<br>
+ piapi@csie.ntu.edu.tw
+ </font>
+
+</body>
+</html>
diff --git a/zlib/zutil.c b/zlib/zutil.c
new file mode 100644
index 00000000..65beb591
--- /dev/null
+++ b/zlib/zutil.c
@@ -0,0 +1,225 @@
+/* zutil.c -- target dependent utility functions for the compression library
+ * Copyright (C) 1995-2002 Jean-loup Gailly.
+ * For conditions of distribution and use, see copyright notice in zlib.h
+ */
+
+/* @(#) $Id: zutil.c,v 1.1 2004/10/08 09:44:28 const_k Exp $ */
+
+#include "zutil.h"
+
+struct internal_state {int dummy;}; /* for buggy compilers */
+
+#ifndef STDC
+extern void exit OF((int));
+#endif
+
+const char *z_errmsg[10] = {
+"need dictionary", /* Z_NEED_DICT 2 */
+"stream end", /* Z_STREAM_END 1 */
+"", /* Z_OK 0 */
+"file error", /* Z_ERRNO (-1) */
+"stream error", /* Z_STREAM_ERROR (-2) */
+"data error", /* Z_DATA_ERROR (-3) */
+"insufficient memory", /* Z_MEM_ERROR (-4) */
+"buffer error", /* Z_BUF_ERROR (-5) */
+"incompatible version",/* Z_VERSION_ERROR (-6) */
+""};
+
+
+const char * ZEXPORT zlibVersion()
+{
+ return ZLIB_VERSION;
+}
+
+#ifdef DEBUG
+
+# ifndef verbose
+# define verbose 0
+# endif
+int z_verbose = verbose;
+
+void z_error (m)
+ char *m;
+{
+ fprintf(stderr, "%s\n", m);
+ exit(1);
+}
+#endif
+
+/* exported to allow conversion of error code to string for compress() and
+ * uncompress()
+ */
+const char * ZEXPORT zError(err)
+ int err;
+{
+ return ERR_MSG(err);
+}
+
+
+#ifndef HAVE_MEMCPY
+
+void zmemcpy(dest, source, len)
+ Bytef* dest;
+ const Bytef* source;
+ uInt len;
+{
+ if (len == 0) return;
+ do {
+ *dest++ = *source++; /* ??? to be unrolled */
+ } while (--len != 0);
+}
+
+int zmemcmp(s1, s2, len)
+ const Bytef* s1;
+ const Bytef* s2;
+ uInt len;
+{
+ uInt j;
+
+ for (j = 0; j < len; j++) {
+ if (s1[j] != s2[j]) return 2*(s1[j] > s2[j])-1;
+ }
+ return 0;
+}
+
+void zmemzero(dest, len)
+ Bytef* dest;
+ uInt len;
+{
+ if (len == 0) return;
+ do {
+ *dest++ = 0; /* ??? to be unrolled */
+ } while (--len != 0);
+}
+#endif
+
+#ifdef __TURBOC__
+#if (defined( __BORLANDC__) || !defined(SMALL_MEDIUM)) && !defined(__32BIT__)
+/* Small and medium model in Turbo C are for now limited to near allocation
+ * with reduced MAX_WBITS and MAX_MEM_LEVEL
+ */
+# define MY_ZCALLOC
+
+/* Turbo C malloc() does not allow dynamic allocation of 64K bytes
+ * and farmalloc(64K) returns a pointer with an offset of 8, so we
+ * must fix the pointer. Warning: the pointer must be put back to its
+ * original form in order to free it, use zcfree().
+ */
+
+#define MAX_PTR 10
+/* 10*64K = 640K */
+
+local int next_ptr = 0;
+
+typedef struct ptr_table_s {
+ voidpf org_ptr;
+ voidpf new_ptr;
+} ptr_table;
+
+local ptr_table table[MAX_PTR];
+/* This table is used to remember the original form of pointers
+ * to large buffers (64K). Such pointers are normalized with a zero offset.
+ * Since MSDOS is not a preemptive multitasking OS, this table is not
+ * protected from concurrent access. This hack doesn't work anyway on
+ * a protected system like OS/2. Use Microsoft C instead.
+ */
+
+voidpf zcalloc (voidpf opaque, unsigned items, unsigned size)
+{
+ voidpf buf = opaque; /* just to make some compilers happy */
+ ulg bsize = (ulg)items*size;
+
+ /* If we allocate less than 65520 bytes, we assume that farmalloc
+ * will return a usable pointer which doesn't have to be normalized.
+ */
+ if (bsize < 65520L) {
+ buf = farmalloc(bsize);
+ if (*(ush*)&buf != 0) return buf;
+ } else {
+ buf = farmalloc(bsize + 16L);
+ }
+ if (buf == NULL || next_ptr >= MAX_PTR) return NULL;
+ table[next_ptr].org_ptr = buf;
+
+ /* Normalize the pointer to seg:0 */
+ *((ush*)&buf+1) += ((ush)((uch*)buf-0) + 15) >> 4;
+ *(ush*)&buf = 0;
+ table[next_ptr++].new_ptr = buf;
+ return buf;
+}
+
+void zcfree (voidpf opaque, voidpf ptr)
+{
+ int n;
+ if (*(ush*)&ptr != 0) { /* object < 64K */
+ farfree(ptr);
+ return;
+ }
+ /* Find the original pointer */
+ for (n = 0; n < next_ptr; n++) {
+ if (ptr != table[n].new_ptr) continue;
+
+ farfree(table[n].org_ptr);
+ while (++n < next_ptr) {
+ table[n-1] = table[n];
+ }
+ next_ptr--;
+ return;
+ }
+ ptr = opaque; /* just to make some compilers happy */
+ Assert(0, "zcfree: ptr not found");
+}
+#endif
+#endif /* __TURBOC__ */
+
+
+#if defined(M_I86) && !defined(__32BIT__)
+/* Microsoft C in 16-bit mode */
+
+# define MY_ZCALLOC
+
+#if (!defined(_MSC_VER) || (_MSC_VER <= 600))
+# define _halloc halloc
+# define _hfree hfree
+#endif
+
+voidpf zcalloc (voidpf opaque, unsigned items, unsigned size)
+{
+ if (opaque) opaque = 0; /* to make compiler happy */
+ return _halloc((long)items, size);
+}
+
+void zcfree (voidpf opaque, voidpf ptr)
+{
+ if (opaque) opaque = 0; /* to make compiler happy */
+ _hfree(ptr);
+}
+
+#endif /* MSC */
+
+
+#ifndef MY_ZCALLOC /* Any system without a special alloc function */
+
+#ifndef STDC
+extern voidp calloc OF((uInt items, uInt size));
+extern void free OF((voidpf ptr));
+#endif
+
+voidpf zcalloc (opaque, items, size)
+ voidpf opaque;
+ unsigned items;
+ unsigned size;
+{
+ if (opaque) items += size - size; /* make compiler happy */
+ return (voidpf)calloc(items, size);
+}
+
+void zcfree (opaque, ptr)
+ voidpf opaque;
+ voidpf ptr;
+{
+ free(ptr);
+ if (opaque) return; /* make compiler happy */
+}
+
+#endif /* MY_ZCALLOC */
diff --git a/zlib/zutil.h b/zlib/zutil.h
new file mode 100644
index 00000000..17eb77f2
--- /dev/null
+++ b/zlib/zutil.h
@@ -0,0 +1,220 @@
+/* zutil.h -- internal interface and configuration of the compression library
+ * Copyright (C) 1995-2002 Jean-loup Gailly.
+ * For conditions of distribution and use, see copyright notice in zlib.h
+ */
+
+/* WARNING: this file should *not* be used by applications. It is
+ part of the implementation of the compression library and is
+ subject to change. Applications should only use zlib.h.
+ */
+
+/* @(#) $Id: zutil.h,v 1.1 2004/10/08 09:44:28 const_k Exp $ */
+
+#ifndef _Z_UTIL_H
+#define _Z_UTIL_H
+
+#include "zlib.h"
+
+#ifdef STDC
+# include <stddef.h>
+# include <string.h>
+# include <stdlib.h>
+#endif
+#ifdef NO_ERRNO_H
+ extern int errno;
+#else
+# include <errno.h>
+#endif
+
+#ifndef local
+# define local static
+#endif
+/* compile with -Dlocal if your debugger can't find static symbols */
+
+typedef unsigned char uch;
+typedef uch FAR uchf;
+typedef unsigned short ush;
+typedef ush FAR ushf;
+typedef unsigned long ulg;
+
+extern const char *z_errmsg[10]; /* indexed by 2-zlib_error */
+/* (size given to avoid silly warnings with Visual C++) */
+
+#define ERR_MSG(err) z_errmsg[Z_NEED_DICT-(err)]
+
+#define ERR_RETURN(strm,err) \
+ return (strm->msg = (char*)ERR_MSG(err), (err))
+/* To be used only when the state is known to be valid */
+
+ /* common constants */
+
+#ifndef DEF_WBITS
+# define DEF_WBITS MAX_WBITS
+#endif
+/* default windowBits for decompression. MAX_WBITS is for compression only */
+
+#if MAX_MEM_LEVEL >= 8
+# define DEF_MEM_LEVEL 8
+#else
+# define DEF_MEM_LEVEL MAX_MEM_LEVEL
+#endif
+/* default memLevel */
+
+#define STORED_BLOCK 0
+#define STATIC_TREES 1
+#define DYN_TREES 2
+/* The three kinds of block type */
+
+#define MIN_MATCH 3
+#define MAX_MATCH 258
+/* The minimum and maximum match lengths */
+
+#define PRESET_DICT 0x20 /* preset dictionary flag in zlib header */
+
+ /* target dependencies */
+
+#ifdef MSDOS
+# define OS_CODE 0x00
+# if defined(__TURBOC__) || defined(__BORLANDC__)
+# if(__STDC__ == 1) && (defined(__LARGE__) || defined(__COMPACT__))
+ /* Allow compilation with ANSI keywords only enabled */
+ void _Cdecl farfree( void *block );
+ void *_Cdecl farmalloc( unsigned long nbytes );
+# else
+# include <alloc.h>
+# endif
+# else /* MSC or DJGPP */
+# include <malloc.h>
+# endif
+#endif
+
+#ifdef OS2
+# define OS_CODE 0x06
+#endif
+
+#ifdef WIN32 /* Window 95 & Windows NT */
+# define OS_CODE 0x0b
+#endif
+
+#if defined(VAXC) || defined(VMS)
+# define OS_CODE 0x02
+# define F_OPEN(name, mode) \
+ fopen((name), (mode), "mbc=60", "ctx=stm", "rfm=fix", "mrs=512")
+#endif
+
+#ifdef AMIGA
+# define OS_CODE 0x01
+#endif
+
+#if defined(ATARI) || defined(atarist)
+# define OS_CODE 0x05
+#endif
+
+#if defined(MACOS) || defined(TARGET_OS_MAC)
+# define OS_CODE 0x07
+# if defined(__MWERKS__) && __dest_os != __be_os && __dest_os != __win32_os
+# include <unix.h> /* for fdopen */
+# else
+# ifndef fdopen
+# define fdopen(fd,mode) NULL /* No fdopen() */
+# endif
+# endif
+#endif
+
+#ifdef __50SERIES /* Prime/PRIMOS */
+# define OS_CODE 0x0F
+#endif
+
+#ifdef TOPS20
+# define OS_CODE 0x0a
+#endif
+
+#if defined(_BEOS_) || defined(RISCOS)
+# define fdopen(fd,mode) NULL /* No fdopen() */
+#endif
+
+#if (defined(_MSC_VER) && (_MSC_VER > 600))
+# define fdopen(fd,type) _fdopen(fd,type)
+#endif
+
+
+ /* Common defaults */
+
+#ifndef OS_CODE
+# define OS_CODE 0x03 /* assume Unix */
+#endif
+
+#ifndef F_OPEN
+# define F_OPEN(name, mode) fopen((name), (mode))
+#endif
+
+ /* functions */
+
+#ifdef HAVE_STRERROR
+ extern char *strerror OF((int));
+# define zstrerror(errnum) strerror(errnum)
+#else
+# define zstrerror(errnum) ""
+#endif
+
+#if defined(pyr)
+# define NO_MEMCPY
+#endif
+#if defined(SMALL_MEDIUM) && !defined(_MSC_VER) && !defined(__SC__)
+ /* Use our own functions for small and medium model with MSC <= 5.0.
+ * You may have to use the same strategy for Borland C (untested).
+ * The __SC__ check is for Symantec.
+ */
+# define NO_MEMCPY
+#endif
+#if defined(STDC) && !defined(HAVE_MEMCPY) && !defined(NO_MEMCPY)
+# define HAVE_MEMCPY
+#endif
+#ifdef HAVE_MEMCPY
+# ifdef SMALL_MEDIUM /* MSDOS small or medium model */
+# define zmemcpy _fmemcpy
+# define zmemcmp _fmemcmp
+# define zmemzero(dest, len) _fmemset(dest, 0, len)
+# else
+# define zmemcpy memcpy
+# define zmemcmp memcmp
+# define zmemzero(dest, len) memset(dest, 0, len)
+# endif
+#else
+ extern void zmemcpy OF((Bytef* dest, const Bytef* source, uInt len));
+ extern int zmemcmp OF((const Bytef* s1, const Bytef* s2, uInt len));
+ extern void zmemzero OF((Bytef* dest, uInt len));
+#endif
+
+/* Diagnostic functions */
+#ifdef DEBUG
+# include <stdio.h>
+ extern int z_verbose;
+ extern void z_error OF((char *m));
+# define Assert(cond,msg) {if(!(cond)) z_error(msg);}
+# define Trace(x) {if (z_verbose>=0) fprintf x ;}
+# define Tracev(x) {if (z_verbose>0) fprintf x ;}
+# define Tracevv(x) {if (z_verbose>1) fprintf x ;}
+# define Tracec(c,x) {if (z_verbose>0 && (c)) fprintf x ;}
+# define Tracecv(c,x) {if (z_verbose>1 && (c)) fprintf x ;}
+#else
+# define Assert(cond,msg)
+# define Trace(x)
+# define Tracev(x)
+# define Tracevv(x)
+# define Tracec(c,x)
+# define Tracecv(c,x)
+#endif
+
+
+typedef uLong (ZEXPORT *check_func) OF((uLong check, const Bytef *buf,
+ uInt len));
+voidpf zcalloc OF((voidpf opaque, unsigned items, unsigned size));
+void zcfree OF((voidpf opaque, voidpf ptr));
+
+#define ZALLOC(strm, items, size) \
+ (*((strm)->zalloc))((strm)->opaque, (items), (size))
+#define ZFREE(strm, addr) (*((strm)->zfree))((strm)->opaque, (voidpf)(addr))
+#define TRY_FREE(s, p) {if (p) ZFREE(s, p);}
+
+#endif /* _Z_UTIL_H */