diff options
author | Constantin Kaplinsky <const@tightvnc.com> | 2006-05-25 05:12:25 +0000 |
---|---|---|
committer | Constantin Kaplinsky <const@tightvnc.com> | 2006-05-25 05:12:25 +0000 |
commit | 729598cb00d791bbdfe23ebe0023d3a1c3962f83 (patch) | |
tree | ffe1b87705a0541998b8d7c44ea75dc4702dc515 /win | |
parent | b30ae7facbdf8273f34f5d67d3d2e9c81db75576 (diff) | |
download | tigervnc-729598cb00d791bbdfe23ebe0023d3a1c3962f83.tar.gz tigervnc-729598cb00d791bbdfe23ebe0023d3a1c3962f83.zip |
Migrating to new directory structure adopted from the RealVNC's source tree. More changes will follow.
git-svn-id: svn://svn.code.sf.net/p/tigervnc/code/trunk@591 3789f03b-4d11-0410-bbf8-ca57d06f2519
Diffstat (limited to 'win')
228 files changed, 32244 insertions, 0 deletions
diff --git a/win/README.txt b/win/README.txt new file mode 100644 index 00000000..a834b21e --- /dev/null +++ b/win/README.txt @@ -0,0 +1,128 @@ + +TightVNC Source Distribution for Windows platforms +================================================== + +Copyright (C) 2002-2004 RealVNC Ltd. All Rights Reserved. +Copyright (C) 2000-2004 Constantin Kaplinsky. +Copyright (C) 2004 Peter Astrand, Cendio AB + +This software is distributed under the GNU General Public Licence as +published by the Free Software Foundation. See the file LICENCE.TXT +for the conditions under which this software is made available. +TightVNC also contains code from other sources. See the +Acknowledgements section below, and the individual files for details +of the conditions under which they are made available. + +The source tree contains a number of directories, and is most easily +built by loading the VNC workspace file (vnc.dsw) into Microsoft +Visual Studio 6/7. This will preserve the required dependencies +between the sub-projects. + +There are three main executable projects: + + vncviewer - The VNC Viewer for Win32. + + winvnc - The VNC Server for Win32 (command-line operation + only). + + vncconfig - The configuration applet and GUI front-end for VNC + Server. + +These projects are designed to be built using Microsoft Visual C++ +6.0, and should also compile with Microsoft Visual Studio .NET +(version 7). Other compilers have not been tested but the code base +is extremely portable. + + +ACKNOWLEDGEMENTS +================ + +This distribution contains zlib software by Jean-loup Gailly and Mark Adler. +This is: + + Copyright (C) 1995-2002 Jean-loup Gailly and Mark Adler. + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. + + Jean-loup Gailly Mark Adler + jloup@gzip.org madler@alumni.caltech.edu + + + The data format used by the zlib library is described by RFCs (Request for + Comments) 1950 to 1952 in the files ftp://ds.internic.net/rfc/rfc1950.txt + (zlib format), rfc1951.txt (deflate format) and rfc1952.txt (gzip format). + + +This distribution contains public domain DES software by Richard Outerbridge. +This is: + + Copyright (c) 1988,1989,1990,1991,1992 by Richard Outerbridge. + (GEnie : OUTER; CIS : [71755,204]) Graven Imagery, 1992. + + +This distribution contains Java DES software by Dave Zimmerman +<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/win/README_BINARY.txt b/win/README_BINARY.txt new file mode 100644 index 00000000..4c77e027 --- /dev/null +++ b/win/README_BINARY.txt @@ -0,0 +1,126 @@ + +TightVNC Binary Distribution for Windows platforms +================================================== + +Copyright (C) 2002-2004 RealVNC Ltd. All Rights Reserved. +Copyright (C) 2000-2004 Constantin Kaplinsky. +Copyright (C) 2004-2005 Peter Astrand, Cendio AB + +This software is distributed under the GNU General Public Licence as +published by the Free Software Foundation. See the file LICENCE.TXT +for the conditions under which this software is made available. +TightVNC also contains code from other sources. See the +Acknowledgements section below, and the individual files for details +of the conditions under which they are made available. + +The installer package contains two VNC components: + + VNC Viewer - this is the VNC Viewer, or client, program for + Win32. + [Win9x, WinME, NT4, Win2000, WinXP, + Windows 2003 Server] + + VNC Server - this is the VNC Server for Win32. It allows a + Windows desktop to be accessed remotely using a + VNC Viewer. + [Win9x, WinME, NT4, Win2000, WinXP(*), + Windows 2003 Server] + +(*) May not work if the in-built Fast User Switching or Remote + Administration features are in use. + +Both components were built using Microsoft Visual C++ 6.0, and are +designed to operate upon the Win32 platforms listed above. + +ACKNOWLEDGEMENTS +================ + +This distribution contains zlib software by Jean-loup Gailly and Mark Adler. +This is: + + Copyright (C) 1995-2002 Jean-loup Gailly and Mark Adler. + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. + + Jean-loup Gailly Mark Adler + jloup@gzip.org madler@alumni.caltech.edu + + + The data format used by the zlib library is described by RFCs (Request for + Comments) 1950 to 1952 in the files ftp://ds.internic.net/rfc/rfc1950.txt + (zlib format), rfc1951.txt (deflate format) and rfc1952.txt (gzip format). + + +This distribution contains public domain DES software by Richard Outerbridge. +This is: + + Copyright (c) 1988,1989,1990,1991,1992 by Richard Outerbridge. + (GEnie : OUTER; CIS : [71755,204]) Graven Imagery, 1992. + + +This distribution contains Java DES software by Dave Zimmerman +<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/win/logmessages/logmessages.dsp b/win/logmessages/logmessages.dsp new file mode 100644 index 00000000..06ed8c4f --- /dev/null +++ b/win/logmessages/logmessages.dsp @@ -0,0 +1,202 @@ +# Microsoft Developer Studio Project File - Name="logmessages" - Package Owner=<4>
+# Microsoft Developer Studio Generated Build File, Format Version 6.00
+# ** DO NOT EDIT **
+
+# TARGTYPE "Win32 (x86) Dynamic-Link Library" 0x0102
+
+CFG=logmessages - Win32 Debug Unicode
+!MESSAGE This is not a valid makefile. To build this project using NMAKE,
+!MESSAGE use the Export Makefile command and run
+!MESSAGE
+!MESSAGE NMAKE /f "logmessages.mak".
+!MESSAGE
+!MESSAGE You can specify a configuration when running NMAKE
+!MESSAGE by defining the macro CFG on the command line. For example:
+!MESSAGE
+!MESSAGE NMAKE /f "logmessages.mak" CFG="logmessages - Win32 Debug Unicode"
+!MESSAGE
+!MESSAGE Possible choices for configuration are:
+!MESSAGE
+!MESSAGE "logmessages - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library")
+!MESSAGE "logmessages - Win32 Debug" (based on "Win32 (x86) Dynamic-Link Library")
+!MESSAGE "logmessages - Win32 Debug Unicode" (based on "Win32 (x86) Dynamic-Link Library")
+!MESSAGE
+
+# Begin Project
+# PROP AllowPerConfigDependencies 0
+# PROP Scc_ProjName ""
+# PROP Scc_LocalPath ""
+CPP=cl.exe
+MTL=midl.exe
+RSC=rc.exe
+
+!IF "$(CFG)" == "logmessages - Win32 Release"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 0
+# PROP BASE Output_Dir "Release"
+# PROP BASE Intermediate_Dir "Release"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 0
+# PROP Output_Dir "..\Release"
+# PROP Intermediate_Dir "..\Release\logmessages"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /MT /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "LOGMESSAGES_EXPORTS" /YX /FD /c
+# ADD CPP /nologo /MT /W3 /GX /O2 /D "NDEBUG" /D "_WINDOWS" /D "_USRDLL" /D "LOGMESSAGES_EXPORTS" /D "WIN32" /D "_MBCS" /YX /FD /c
+# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32
+# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32
+# ADD BASE RSC /l 0x809 /d "NDEBUG"
+# ADD RSC /l 0x809 /d "NDEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /machine:I386
+# ADD LINK32 /nologo /dll /machine:I386 /out:"messages.mc"
+# Begin Custom Build
+InputPath=.\messages.mc
+SOURCE="$(InputPath)"
+
+BuildCmds= \
+ mc messages.mc \
+ rc -r -fo messages.res messages.rc \
+ link -dll -noentry -out:..\Release\logmessages.dll messages.res \
+
+
+"messages.res" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)"
+ $(BuildCmds)
+
+"messages.rc" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)"
+ $(BuildCmds)
+
+"messages.h" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)"
+ $(BuildCmds)
+
+"..\Release\logmessages.dll" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)"
+ $(BuildCmds)
+# End Custom Build
+
+!ELSEIF "$(CFG)" == "logmessages - Win32 Debug"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 1
+# PROP BASE Output_Dir "Debug"
+# PROP BASE Intermediate_Dir "Debug"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 1
+# PROP Output_Dir "../Debug"
+# PROP Intermediate_Dir "..\Debug\logmessages"
+# PROP Ignore_Export_Lib 1
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "LOGMESSAGES_EXPORTS" /YX /FD /GZ /c
+# ADD CPP /nologo /MTd /W3 /Gm /GX /Zi /Od /D "_DEBUG" /D "_WINDOWS" /D "_USRDLL" /D "LOGMESSAGES_EXPORTS" /D "WIN32" /D "_MBCS" /YX /FD /GZ /c
+# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /win32
+# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32
+# ADD BASE RSC /l 0x809 /d "_DEBUG"
+# ADD RSC /l 0x809 /d "_DEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /debug /machine:I386 /pdbtype:sept
+# ADD LINK32 /nologo /dll /debug /machine:I386 /nodefaultlib /out:"messages.mc" /pdbtype:sept
+# Begin Custom Build
+InputPath=.\messages.mc
+SOURCE="$(InputPath)"
+
+BuildCmds= \
+ mc messages.mc \
+ rc -r -fo messages.res messages.rc \
+ link -dll -noentry -out:..\Debug\logmessages.dll messages.res \
+
+
+"messages.res" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)"
+ $(BuildCmds)
+
+"messages.rc" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)"
+ $(BuildCmds)
+
+"messages.h" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)"
+ $(BuildCmds)
+
+"..\Debug\logmessages.dll" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)"
+ $(BuildCmds)
+# End Custom Build
+
+!ELSEIF "$(CFG)" == "logmessages - Win32 Debug Unicode"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 1
+# PROP BASE Output_Dir "logmessages___Win32_Debug_Unicode"
+# PROP BASE Intermediate_Dir "logmessages___Win32_Debug_Unicode"
+# PROP BASE Ignore_Export_Lib 1
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 1
+# PROP Output_Dir "..\Debug_Unicode"
+# PROP Intermediate_Dir "..\Debug_Unicode\logmessages"
+# PROP Ignore_Export_Lib 1
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /MTd /W3 /Gm /GX /Zi /Od /D "_DEBUG" /D "_WINDOWS" /D "_USRDLL" /D "LOGMESSAGES_EXPORTS" /D "WIN32" /D "_MBCS" /YX /FD /GZ /c
+# ADD CPP /nologo /MTd /W3 /Gm /GX /Zi /Od /D "_WINDOWS" /D "_USRDLL" /D "LOGMESSAGES_EXPORTS" /D "_DEBUG" /D "WIN32" /D "_UNICODE" /D "UNICODE" /YX /FD /GZ /c
+# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /win32
+# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32
+# ADD BASE RSC /l 0x809 /d "_DEBUG"
+# ADD RSC /l 0x809 /d "_DEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 /nologo /dll /debug /machine:I386 /nodefaultlib /out:"messages.mc" /pdbtype:sept
+# ADD LINK32 /nologo /dll /debug /machine:I386 /nodefaultlib /out:"messages.mc" /pdbtype:sept
+# Begin Custom Build
+InputPath=.\messages.mc
+SOURCE="$(InputPath)"
+
+BuildCmds= \
+ mc messages.mc \
+ rc -r -fo messages.res messages.rc \
+ link -dll -noentry -out:..\Debug_Unicode\logmessages.dll messages.res \
+
+
+"messages.res" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)"
+ $(BuildCmds)
+
+"messages.rc" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)"
+ $(BuildCmds)
+
+"messages.h" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)"
+ $(BuildCmds)
+
+"..\Debug_Unicode\logmessages.dll" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)"
+ $(BuildCmds)
+# End Custom Build
+
+!ENDIF
+
+# Begin Target
+
+# Name "logmessages - Win32 Release"
+# Name "logmessages - Win32 Debug"
+# Name "logmessages - Win32 Debug Unicode"
+# Begin Group "Source Files"
+
+# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat"
+# End Group
+# Begin Group "Header Files"
+
+# PROP Default_Filter "h;hpp;hxx;hm;inl"
+# End Group
+# Begin Group "Resource Files"
+
+# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe"
+# End Group
+# Begin Source File
+
+SOURCE=.\messages.mc
+# End Source File
+# End Target
+# End Project
diff --git a/win/logmessages/messages.h b/win/logmessages/messages.h new file mode 100644 index 00000000..bfb8c56d --- /dev/null +++ b/win/logmessages/messages.h @@ -0,0 +1,47 @@ +// +// Values are 32 bit values layed out as follows: +// +// 3 3 2 2 2 2 2 2 2 2 2 2 1 1 1 1 1 1 1 1 1 1 +// 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 +// +---+-+-+-----------------------+-------------------------------+ +// |Sev|C|R| Facility | Code | +// +---+-+-+-----------------------+-------------------------------+ +// +// where +// +// Sev - is the severity code +// +// 00 - Success +// 01 - Informational +// 10 - Warning +// 11 - Error +// +// C - is the Customer code flag +// +// R - is a reserved bit +// +// Facility - is the facility code +// +// Code - is the facility's status code +// +// +// Define the facility codes +// + + +// +// Define the severity codes +// + + +// +// MessageId: VNC4LogMessage +// +// MessageText: +// +// %1: %2 +// +// +// +#define VNC4LogMessage 0x00000001L + diff --git a/win/logmessages/messages.mc b/win/logmessages/messages.mc new file mode 100644 index 00000000..0bc8329e --- /dev/null +++ b/win/logmessages/messages.mc @@ -0,0 +1,7 @@ +MessageId=0x1 +Severity=Success +SymbolicName=VNC4LogMessage +Language=English +%1: %2 + + diff --git a/win/logmessages/messages.rc b/win/logmessages/messages.rc new file mode 100644 index 00000000..0885a897 --- /dev/null +++ b/win/logmessages/messages.rc @@ -0,0 +1,2 @@ +LANGUAGE 0x9,0x1 +1 11 MSG00001.bin diff --git a/win/rfb_win32/AboutDialog.cxx b/win/rfb_win32/AboutDialog.cxx new file mode 100644 index 00000000..030be1b3 --- /dev/null +++ b/win/rfb_win32/AboutDialog.cxx @@ -0,0 +1,49 @@ +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +#include <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("ProductVersion"))); + SetWindowText(GetDlgItem(handle, Copyright), verInfo.getVerString(_T("LegalCopyright"))); + SetWindowText(GetDlgItem(handle, Description), verInfo.getVerString(_T("ProductName"))); +} diff --git a/win/rfb_win32/AboutDialog.h b/win/rfb_win32/AboutDialog.h new file mode 100644 index 00000000..0dd9d494 --- /dev/null +++ b/win/rfb_win32/AboutDialog.h @@ -0,0 +1,55 @@ +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +// -=- AboutDialog.h + +#ifndef __RFB_WIN32_ABOUT_DIALOG_H__ +#define __RFB_WIN32_ABOUT_DIALOG_H__ + +#include <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/win/rfb_win32/BitmapInfo.h b/win/rfb_win32/BitmapInfo.h new file mode 100644 index 00000000..6a6f0d24 --- /dev/null +++ b/win/rfb_win32/BitmapInfo.h @@ -0,0 +1,48 @@ +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +#ifndef __RFB_WIN32_BITMAP_INFO_H__ +#define __RFB_WIN32_BITMAP_INFO_H__ + +#include <windows.h> +#include <rdr/types.h> + +namespace rfb { + namespace win32 { + + struct BitmapInfo { + BITMAPINFOHEADER bmiHeader; + union { + struct { + DWORD red; + DWORD green; + DWORD blue; + } mask; + RGBQUAD color[256]; + }; + }; + + inline void initMaxAndShift(DWORD mask, int* max, int* shift) { + for ((*shift) = 0; (mask & 1) == 0; (*shift)++) mask >>= 1; + (*max) = (rdr::U16)mask; + } + + }; +}; + +#endif diff --git a/win/rfb_win32/CKeyboard.cxx b/win/rfb_win32/CKeyboard.cxx new file mode 100644 index 00000000..28aceab7 --- /dev/null +++ b/win/rfb_win32/CKeyboard.cxx @@ -0,0 +1,258 @@ +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +#include <map> + +#define XK_MISCELLANY +#define XK_LATIN1 +#define XK_CURRENCY +#include <rfb/keysymdef.h> + +#include <rfb_win32/CKeyboard.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(InputHandler* 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->keyEvent(keysym, false); + } + } + ~ModifierKeyReleaser() { + if (keysym) { + vlog.debug("fake press extendedVkey 0x%x, keysym 0x%x", + extendedVkey, keysym); + writer->keyEvent(keysym, true); + } + } + InputHandler* writer; + int extendedVkey; + rdr::U32 keysym; +}; + +// IS_PRINTABLE_LATIN1 tests if a character is either a printable latin1 +// character, or 128, which is the Euro symbol on Windows. +#define IS_PRINTABLE_LATIN1(c) (((c) >= 32 && (c) <= 126) || (c) == 128 || \ + ((c) >= 160 && (c) <= 255)) + +void win32::CKeyboard::keyEvent(InputHandler* writer, rdr::U8 vkey, + rdr::U32 flags, bool down) +{ + bool extended = (flags & 0x1000000); + int extendedVkey = vkey + (extended ? 256 : 0); + + // If it's a release, just release whichever keysym corresponded to the same + // key being pressed, regardless of how it would be interpreted in the + // current keyboard state. + if (!down) { + releaseKey(writer, extendedVkey); + return; + } + + // We should always pass every down event to ToAscii() otherwise it can get + // out of sync. + + // XXX should we pass CapsLock, ScrollLock or NumLock to ToAscii - they + // actually alter the lock state on the keyboard? + + BYTE keystate[256]; + GetKeyboardState(keystate); + rdr::U8 chars[2]; + + int nchars = ToAscii(vkey, 0, keystate, (WORD*)&chars, 0); + + // See if it's in the Windows VK code -> X keysym map. We do this before + // looking at the result of ToAscii so that e.g. we recognise that it's + // XK_KP_Add rather than '+'. + + rdr::U32 keysym = ckeymapper.lookup(extendedVkey); + if (keysym) { + vlog.debug("mapped key: extendedVkey 0x%x", extendedVkey); + pressKey(writer, extendedVkey, keysym); + return; + } + + if (nchars < 0) { + // Dead key - the next call to ToAscii() will give us either the accented + // character or two characters. + vlog.debug("ToAscii dead key (1): extendedVkey 0x%x", extendedVkey); + return; + } + + if (nchars > 0 && IS_PRINTABLE_LATIN1(chars[0])) { + // Got a printable latin1 character. We must release Control and Alt + // (AltGr) if they were both pressed, so that the latin1 character is seen + // without them by the VNC server. + ModifierKeyReleaser lctrl(writer, VK_CONTROL, 0); + ModifierKeyReleaser rctrl(writer, VK_CONTROL, 1); + ModifierKeyReleaser lalt(writer, VK_MENU, 0); + ModifierKeyReleaser ralt(writer, VK_MENU, 1); + + if ((keystate[VK_CONTROL] & 0x80) && (keystate[VK_MENU] & 0x80)) { + lctrl.release(&downKeysym); + rctrl.release(&downKeysym); + lalt.release(&downKeysym); + ralt.release(&downKeysym); + } + + for (int i = 0; i < nchars; i++) { + vlog.debug("ToAscii key (1): extendedVkey 0x%x", extendedVkey); + if (chars[i] == 128) { // special hack for euro! + pressKey(writer, extendedVkey, XK_EuroSign); + } else { + pressKey(writer, extendedVkey, chars[i]); + } + } + return; + } + + // Either no chars were generated, or something outside the printable + // character range. Try ToAscii() without the Control and Alt keys down to + // see if that yields an ordinary character. + + keystate[VK_CONTROL] = keystate[VK_LCONTROL] = keystate[VK_RCONTROL] = 0; + keystate[VK_MENU] = keystate[VK_LMENU] = keystate[VK_RMENU] = 0; + + nchars = ToAscii(vkey, 0, keystate, (WORD*)&chars, 0); + + if (nchars < 0) { + // So it would be a dead key if neither control nor alt were pressed. + // However, we know that at least one of control and alt must be pressed. + // We can't leave it at this stage otherwise the next call to ToAscii() + // with a valid character will get wrongly interpreted in the context of + // this bogus dead key. Working on the assumption that a dead key followed + // by space usually returns the dead character itself, try calling ToAscii + // with VK_SPACE. + vlog.debug("ToAscii dead key (2): extendedVkey 0x%x", extendedVkey); + nchars = ToAscii(VK_SPACE, 0, keystate, (WORD*)&chars, 0); + if (nchars < 0) { + vlog.debug("ToAscii dead key (3): extendedVkey 0x%x - giving up!", + extendedVkey); + return; + } + } + + if (nchars > 0 && IS_PRINTABLE_LATIN1(chars[0])) { + for (int i = 0; i < nchars; i++) { + vlog.debug("ToAscii key (2) (no ctrl/alt): extendedVkey 0x%x", + extendedVkey); + if (chars[i] == 128) { // special hack for euro! + pressKey(writer, extendedVkey, XK_EuroSign); + } else { + pressKey(writer, extendedVkey, chars[i]); + } + } + return; + } + + vlog.debug("no chars regardless of control and alt: extendedVkey 0x%x", + extendedVkey); +} + +// releaseAllKeys() - write key release events to the server for all keys +// that are currently regarded as being down. +void win32::CKeyboard::releaseAllKeys(InputHandler* writer) { + std::map<int,rdr::U32>::iterator i, next_i; + for (i=downKeysym.begin(); i!=downKeysym.end(); i=next_i) { + next_i = i; next_i++; + writer->keyEvent((*i).second, false); + downKeysym.erase(i); + } +} + +// releaseKey() - write a key up event to the server, but only if we've +// actually sent a key down event for the given key. The key up event always +// contains the same keysym we used in the key down event, regardless of what +// it would look up as using the current keyboard state. +void win32::CKeyboard::releaseKey(InputHandler* writer, int extendedVkey) +{ + if (downKeysym.find(extendedVkey) != downKeysym.end()) { + vlog.debug("release extendedVkey 0x%x, keysym 0x%x", + extendedVkey, downKeysym[extendedVkey]); + writer->keyEvent(downKeysym[extendedVkey], false); + downKeysym.erase(extendedVkey); + } +} + +// pressKey() - write a key down event to the server, and record which keysym +// was sent as corresponding to the given extendedVkey. The only tricky bit is +// that if we are trying to press an extendedVkey which is already marked as +// down but with a different keysym, then we need to release the old keysym +// first. This can happen in two cases: (a) when a single key press results in +// more than one character, and (b) when shift is released while another key is +// autorepeating. +void win32::CKeyboard::pressKey(InputHandler* writer, int extendedVkey, + rdr::U32 keysym) +{ + if (downKeysym.find(extendedVkey) != downKeysym.end()) { + if (downKeysym[extendedVkey] != keysym) { + vlog.debug("release extendedVkey 0x%x, keysym 0x%x", + extendedVkey, downKeysym[extendedVkey]); + writer->keyEvent(downKeysym[extendedVkey], false); + } + } + vlog.debug("press extendedVkey 0x%x, keysym 0x%x", + extendedVkey, keysym); + writer->keyEvent(keysym, true); + downKeysym[extendedVkey] = keysym; +} diff --git a/win/rfb_win32/CKeyboard.h b/win/rfb_win32/CKeyboard.h new file mode 100644 index 00000000..666ebce5 --- /dev/null +++ b/win/rfb_win32/CKeyboard.h @@ -0,0 +1,51 @@ +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +// -=- CKeyboard.h +// +// Client-side keyboard handling for Win32 + +#ifndef __RFB_WIN32_CKEYBOARD_H__ +#define __RFB_WIN32_CKEYBOARD_H__ + +#include <rfb/InputHandler.h> +#include <map> + +namespace rfb { + + namespace win32 { + + class CKeyboard { + public: + void keyEvent(InputHandler* writer, rdr::U8 vkey, rdr::U32 flags, + bool down); + void releaseAllKeys(InputHandler* writer); + const std::map<int,rdr::U32>& pressedKeys() const {return downKeysym;}; + bool keyPressed(int k) const {return downKeysym.find(k)!=downKeysym.end();} + private: + void win32::CKeyboard::releaseKey(InputHandler* writer, int extendedVkey); + void win32::CKeyboard::pressKey(InputHandler* writer, int extendedVkey, + rdr::U32 keysym); + std::map<int,rdr::U32> downKeysym; + }; + + }; // win32 + +}; // rfb + +#endif // __RFB_WIN32_CKEYBOARD_H__ diff --git a/win/rfb_win32/CPointer.cxx b/win/rfb_win32/CPointer.cxx new file mode 100644 index 00000000..3d0d9342 --- /dev/null +++ b/win/rfb_win32/CPointer.cxx @@ -0,0 +1,186 @@ +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +#include <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(InputHandler* writer, const Point& pos, int buttonMask) { + // + // - Duplicate Event Filtering + // + + bool maskChanged = buttonMask != currButtonMask; + bool posChanged = !pos.equals(currPos); + if (!(posChanged || maskChanged)) + return; + + // Pass on the event to the event-interval handler + threePointerEvent(writer, pos, buttonMask); + + // Save the position and mask + currPos = pos; + currButtonMask = buttonMask; +} + + +inline abs(int x) {return x>0 ? x : 0;} + +int emulate3Mask(int buttonMask) { + // - Release left & right and press middle + vlog.debug("emulate3: active"); + buttonMask &= ~5; + buttonMask |= 2; + return buttonMask; +} + +void CPointer::threePointerEvent(InputHandler* writer, const Point& pos, int buttonMask) { + // + // - 3-Button Mouse Emulation + // + + if (emulate3) { + + bool leftChanged = (buttonMask & 1) != (currButtonMask & 1); + bool rightChanged = (buttonMask & 4) != (currButtonMask & 4); + + if (leftChanged || rightChanged) { + // - One of left or right have changed + + if ((buttonMask & 5) == 1 || (buttonMask & 5) == 4) { + // - One is up, one is down. Start a timer, so that if it + // expires then we know we should actually send this event + vlog.debug("emulate3: start timer"); + threeTimer.start(100); + threePos = pos; + threeMask = buttonMask; + return; + + } else if (threeTimer.isActive()) { + // - Both are up or both are down, and we were timing for an emulation event + // Stop the timer and flush the stored event + vlog.debug("emulate3: stop timer (state)"); + threeTimer.stop(); + if (threeEmulating == ((buttonMask & 5) == 5)) + intervalPointerEvent(writer, threePos, threeMask); + else + threeEmulating = ((buttonMask & 5) == 5); + } + + } else { + + if (threeTimer.isActive()) { + // - We are timing for an emulation event + + if (abs(threePos.x - pos.x) <= 4 || abs(threePos.y - pos.y) <= 4) { + // If the mouse has moved too far since the button-change event then flush + vlog.debug("emulate3: stop timer (moved)"); + threeTimer.stop(); + intervalPointerEvent(writer, threePos, threeMask); + + } else { + // Otherwise, we ignore the new event + return; + } + } + + } + + // - If neither left nor right are down, stop emulating + if ((buttonMask & 5) == 0) + threeEmulating = false; + + // - If emulating, release left & right and press middle + if (threeEmulating) + buttonMask = emulate3Mask(buttonMask); + + } + + // - Let the event pass through to the next stage of processing + intervalPointerEvent(writer, pos, buttonMask); +} + +void CPointer::intervalPointerEvent(InputHandler* writer, const Point& pos, int buttonMask) { + // + // - Pointer Event Interval + // + vlog.write(101, "ptrEvent: %d,%d (%lx)", pos.x, pos.y, buttonMask); + + // Send the event immediately if we haven't sent one for a while + bool sendNow = !intervalTimer.isActive(); + + if (intervalMask != buttonMask) { + // If the buttons have changed then flush queued events and send now + sendNow = true; + if (intervalQueued) + writer->pointerEvent(intervalPos, intervalMask); + intervalQueued = false; + } + + if (!sendNow) { + // If we're not sending now then just queue the event + intervalQueued = true; + intervalPos = pos; + intervalMask = buttonMask; + } else { + // Start the interval timer if required, and send the event + intervalQueued = false; + intervalMask = buttonMask; + if (pointerEventInterval) + intervalTimer.start(pointerEventInterval); + writer->pointerEvent(pos, buttonMask); + } +} + +void CPointer::handleTimer(InputHandler* writer, int timerId) { + if (timerId == intervalTimer.getId()) { + // Pointer interval has expired - send any queued events + if (intervalQueued) { + writer->pointerEvent(intervalPos, intervalMask); + intervalQueued = false; + } else { + intervalTimer.stop(); + } + + } else if (timerId = threeTimer.getId()) { + // 3-Button emulation timer has expired - send what we've got + vlog.debug("emulate3: timeout"); + threeTimer.stop(); + + // If emulating, release left & right and press middle + if (threeEmulating) + threeMask = emulate3Mask(threeMask); + + intervalPointerEvent(writer, threePos, threeMask); + } +} diff --git a/win/rfb_win32/CPointer.h b/win/rfb_win32/CPointer.h new file mode 100644 index 00000000..b5916010 --- /dev/null +++ b/win/rfb_win32/CPointer.h @@ -0,0 +1,74 @@ +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +// -=- CPointer.h +// +// Client-side pointer event handling for Win32 + +#ifndef __RFB_WIN32_CPOINTER_H__ +#define __RFB_WIN32_CPOINTER_H__ + +#include <rdr/Exception.h> +#include <rfb/Configuration.h> +#include <rfb/InputHandler.h> +#include <rfb/Rect.h> +#include <rfb_win32/IntervalTimer.h> + +namespace rfb { + + namespace win32 { + + class CPointer { + public: + CPointer(); + ~CPointer(); + + void pointerEvent(InputHandler* writer, const Point& pos, int buttonMask); + void handleTimer(InputHandler* writer, int timerId); + + void setHWND(HWND w) {intervalTimer.setHWND(w); threeTimer.setHWND(w);} + void setIntervalTimerId(int id) {intervalTimer.setId(id);} + void set3ButtonTimerId(int id) {threeTimer.setId(id);} + + void enableEmulate3(bool enable) {emulate3 = enable;} + void enableInterval(int millis) {pointerEventInterval = millis;} + private: + Point currPos; + int currButtonMask; + + bool emulate3; + int pointerEventInterval; + + void intervalPointerEvent(InputHandler* writer, const Point& pos, int buttonMask); + IntervalTimer intervalTimer; + bool intervalQueued; + Point intervalPos; + int intervalMask; + + void threePointerEvent(InputHandler* writer, const Point& pos, int buttonMask); + IntervalTimer threeTimer; + Point threePos; + int threeMask; + bool threeEmulating; + }; + + }; // win32 + +}; // rfb + +#endif // __RFB_WIN32_CPOINTER_H__ diff --git a/win/rfb_win32/CleanDesktop.cxx b/win/rfb_win32/CleanDesktop.cxx new file mode 100644 index 00000000..39cca119 --- /dev/null +++ b/win/rfb_win32/CleanDesktop.cxx @@ -0,0 +1,321 @@ +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +// -=- CleanDesktop.cxx + +#include <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_win32/OSVersion.h> +#include <rfb/LogWriter.h> +#include <rdr/Exception.h> +#include <set> + +#ifdef SPI_GETUIEFFECTS +#define RFB_HAVE_SPI_UIEFFECTS +#else +#pragma message(" NOTE: Not building Get/Set UI Effects support.") +#endif + +using namespace rfb; +using namespace rfb::win32; + +static LogWriter vlog("CleanDesktop"); + + +struct ActiveDesktop { + ActiveDesktop() : handle(0) { + // - Contact Active Desktop + HRESULT result = CoCreateInstance(CLSID_ActiveDesktop, NULL, CLSCTX_INPROC_SERVER, + IID_IActiveDesktop, (PVOID*)&handle); + if (result != S_OK) + throw rdr::SystemException("failed to contact Active Desktop", result); + } + ~ActiveDesktop() { + if (handle) + handle->Release(); + } + + // enableItem + // enables or disables the Nth Active Desktop item + bool enableItem(int i, bool enable_) { + COMPONENT item; + memset(&item, 0, sizeof(item)); + item.dwSize = sizeof(item); + + HRESULT hr = handle->GetDesktopItem(i, &item, 0); + if (hr != S_OK) { + vlog.error("unable to GetDesktopItem %d: %ld", i, hr); + return false; + } + item.fChecked = enable_; + vlog.debug("%sbling %d: \"%s\"", enable_ ? "ena" : "disa", i, (const char*)CStr(item.wszFriendlyName)); + + hr = handle->ModifyDesktopItem(&item, COMP_ELEM_CHECKED); + return hr == S_OK; + } + + // enable + // Attempts to enable/disable Active Desktop, returns true if the setting changed, + // false otherwise. + // If Active Desktop *can* be enabled/disabled then that is done. + // If Active Desktop is always on (XP/2K3) then instead the individual items are + // disabled, and true is returned to indicate that they need to be restored later. + bool enable(bool enable_) { + bool modifyComponents = false; + + vlog.debug("ActiveDesktop::enable"); + + // - Firstly, try to disable Active Desktop entirely + HRESULT hr; + COMPONENTSOPT adOptions; + memset(&adOptions, 0, sizeof(adOptions)); + adOptions.dwSize = sizeof(adOptions); + + // Attempt to actually disable/enable AD + hr = handle->GetDesktopItemOptions(&adOptions, 0); + if (hr == S_OK) { + // If Active Desktop is already in the desired state then return false (no change) + // NB: If AD is enabled AND restoreItems is set then we regard it as disabled... + if (((adOptions.fActiveDesktop==0) && restoreItems.empty()) == (enable_==false)) + return false; + adOptions.fActiveDesktop = enable_; + hr = handle->SetDesktopItemOptions(&adOptions, 0); + } + // Apply the change, then test whether it actually took effect + if (hr == S_OK) + hr = handle->ApplyChanges(AD_APPLY_REFRESH); + if (hr == S_OK) + hr = handle->GetDesktopItemOptions(&adOptions, 0); + if (hr == S_OK) + modifyComponents = (adOptions.fActiveDesktop==0) != (enable_==false); + if (hr != S_OK) { + vlog.error("failed to get/set Active Desktop options: %ld", hr); + return false; + } + + if (enable_) { + // - We are re-enabling Active Desktop. If there are components in restoreItems + // then restore them! + std::set<int>::const_iterator i; + for (i=restoreItems.begin(); i!=restoreItems.end(); i++) { + enableItem(*i, true); + } + restoreItems.clear(); + } else if (modifyComponents) { + // - Disable all currently enabled items, and add the disabled ones to restoreItems + int itemCount = 0; + hr = handle->GetDesktopItemCount(&itemCount, 0); + if (hr != S_OK) { + vlog.error("failed to get desktop item count: %ld", hr); + return false; + } + for (unsigned int i=0; i<itemCount; i++) { + if (enableItem(i, false)) + restoreItems.insert(i); + } + } + + // - Apply whatever changes we have made, but DON'T save them! + hr = handle->ApplyChanges(AD_APPLY_REFRESH); + return hr == S_OK; + } + IActiveDesktop* handle; + std::set<int> restoreItems; +}; + + +DWORD SysParamsInfo(UINT action, UINT param, PVOID ptr, UINT ini) { + DWORD r = ERROR_SUCCESS; + if (!SystemParametersInfo(action, param, ptr, ini)) { + r = GetLastError(); + vlog.info("SPI error: %d", r); + } + return r; +} + + +CleanDesktop::CleanDesktop() : restoreActiveDesktop(false), restoreWallpaper(false), + restorePattern(false), restoreEffects(false) { + CoInitialize(0); +} + +CleanDesktop::~CleanDesktop() { + enableEffects(); + enablePattern(); + enableWallpaper(); + CoUninitialize(); +} + +void CleanDesktop::disableWallpaper() { + try { + ImpersonateCurrentUser icu; + + vlog.debug("disable desktop wallpaper/Active Desktop"); + + // -=- First attempt to remove the wallpaper using Active Desktop + try { + ActiveDesktop ad; + if (ad.enable(false)) + restoreActiveDesktop = true; + } catch (rdr::Exception& e) { + vlog.error(e.str()); + } + + // -=- Switch of normal wallpaper and notify apps + SysParamsInfo(SPI_SETDESKWALLPAPER, 0, "", SPIF_SENDCHANGE); + restoreWallpaper = true; + + } catch (rdr::Exception& e) { + vlog.info(e.str()); + } +} + +void CleanDesktop::enableWallpaper() { + try { + ImpersonateCurrentUser icu; + + if (restoreActiveDesktop) { + vlog.debug("restore Active Desktop"); + + // -=- First attempt to re-enable Active Desktop + try { + ActiveDesktop ad; + ad.enable(true); + restoreActiveDesktop = false; + } catch (rdr::Exception& e) { + vlog.error(e.str()); + } + } + + if (restoreWallpaper) { + vlog.debug("restore desktop wallpaper"); + + // -=- Then restore the standard wallpaper if required + SysParamsInfo(SPI_SETDESKWALLPAPER, 0, NULL, SPIF_SENDCHANGE); + restoreWallpaper = false; + } + + } catch (rdr::Exception& e) { + vlog.info(e.str()); + } +} + + +void CleanDesktop::disablePattern() { + try { + ImpersonateCurrentUser icu; + + vlog.debug("disable desktop pattern"); + SysParamsInfo(SPI_SETDESKPATTERN, 0, "", SPIF_SENDCHANGE); + restorePattern = true; + + } catch (rdr::Exception& e) { + vlog.info(e.str()); + } +} + +void CleanDesktop::enablePattern() { + try { + if (restorePattern) { + ImpersonateCurrentUser icu; + + vlog.debug("restoring pattern..."); + + TCharArray pattern; + if (osVersion.isPlatformWindows) { + RegKey cfgKey; + cfgKey.openKey(HKEY_CURRENT_USER, _T("Control Panel\\Desktop")); + pattern.buf = cfgKey.getString(_T("Pattern")); + } + SysParamsInfo(SPI_SETDESKPATTERN, 0, pattern.buf, SPIF_SENDCHANGE); + restorePattern = false; + } + + } catch (rdr::Exception& e) { + vlog.info(e.str()); + } +} + + +void CleanDesktop::disableEffects() { + try { + ImpersonateCurrentUser icu; + + vlog.debug("disable desktop effects"); + + SysParamsInfo(SPI_SETFONTSMOOTHING, FALSE, 0, SPIF_SENDCHANGE); +#ifdef RFB_HAVE_SPI_UIEFFECTS + if (SysParamsInfo(SPI_GETUIEFFECTS, 0, &uiEffects, 0) == ERROR_CALL_NOT_IMPLEMENTED) { + SysParamsInfo(SPI_GETCOMBOBOXANIMATION, 0, &comboBoxAnim, 0); + SysParamsInfo(SPI_GETGRADIENTCAPTIONS, 0, &gradientCaptions, 0); + SysParamsInfo(SPI_GETHOTTRACKING, 0, &hotTracking, 0); + SysParamsInfo(SPI_GETLISTBOXSMOOTHSCROLLING, 0, &listBoxSmoothScroll, 0); + SysParamsInfo(SPI_GETMENUANIMATION, 0, &menuAnim, 0); + SysParamsInfo(SPI_SETCOMBOBOXANIMATION, 0, FALSE, SPIF_SENDCHANGE); + SysParamsInfo(SPI_SETGRADIENTCAPTIONS, 0, FALSE, SPIF_SENDCHANGE); + SysParamsInfo(SPI_SETHOTTRACKING, 0, FALSE, SPIF_SENDCHANGE); + SysParamsInfo(SPI_SETLISTBOXSMOOTHSCROLLING, 0, FALSE, SPIF_SENDCHANGE); + SysParamsInfo(SPI_SETMENUANIMATION, 0, FALSE, SPIF_SENDCHANGE); + } else { + SysParamsInfo(SPI_SETUIEFFECTS, 0, FALSE, SPIF_SENDCHANGE); + + // We *always* restore UI effects overall, since there is no Windows GUI to do it + uiEffects = TRUE; + } +#else + vlog.debug(" not supported"); +#endif + restoreEffects = true; + + } catch (rdr::Exception& e) { + vlog.info(e.str()); + } +} + +void CleanDesktop::enableEffects() { + try { + if (restoreEffects) { + ImpersonateCurrentUser icu; + + vlog.debug("restore desktop effects"); + + RegKey desktopCfg; + desktopCfg.openKey(HKEY_CURRENT_USER, _T("Control Panel\\Desktop")); + SysParamsInfo(SPI_SETFONTSMOOTHING, desktopCfg.getInt(_T("FontSmoothing"), 0) != 0, 0, SPIF_SENDCHANGE); +#ifdef RFB_HAVE_SPI_UIEFFECTS + if (SysParamsInfo(SPI_SETUIEFFECTS, 0, (void*)uiEffects, SPIF_SENDCHANGE) == ERROR_CALL_NOT_IMPLEMENTED) { + SysParamsInfo(SPI_SETCOMBOBOXANIMATION, 0, (void*)comboBoxAnim, SPIF_SENDCHANGE); + SysParamsInfo(SPI_SETGRADIENTCAPTIONS, 0, (void*)gradientCaptions, SPIF_SENDCHANGE); + SysParamsInfo(SPI_SETHOTTRACKING, 0, (void*)hotTracking, SPIF_SENDCHANGE); + SysParamsInfo(SPI_SETLISTBOXSMOOTHSCROLLING, 0, (void*)listBoxSmoothScroll, SPIF_SENDCHANGE); + SysParamsInfo(SPI_SETMENUANIMATION, 0, (void*)menuAnim, SPIF_SENDCHANGE); + } + restoreEffects = false; +#else + vlog.info(" not supported"); +#endif + } + + } catch (rdr::Exception& e) { + vlog.info(e.str()); + } +} diff --git a/win/rfb_win32/CleanDesktop.h b/win/rfb_win32/CleanDesktop.h new file mode 100644 index 00000000..22e246fa --- /dev/null +++ b/win/rfb_win32/CleanDesktop.h @@ -0,0 +1,57 @@ +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +// -=- CleanDesktop.h + +#ifndef __RFB_WIN32_CLEANDESKTOP_H__ +#define __RFB_WIN32_CLEANDESKTOP_H__ + +#include <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/win/rfb_win32/Clipboard.cxx b/win/rfb_win32/Clipboard.cxx new file mode 100644 index 00000000..a4c43f04 --- /dev/null +++ b/win/rfb_win32/Clipboard.cxx @@ -0,0 +1,200 @@ +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +// -=- Clipboard.cxx + +#include <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; +} + + +// +// -=- ISO-8859-1 (Latin 1) filter (in-place) +// + +void +removeNonISOLatin1Chars(char* text) { + int len = strlen(text); + int i=0, j=0; + for (; i<len; i++) { + if (((text[i] >= 1) && (text[i] <= 127)) || + ((text[i] >= 160) && (text[i] <= 255))) + text[j++] = text[i]; + } + text[j] = 0; +} + +// +// -=- Clipboard object +// + +Clipboard::Clipboard() + : MsgWindow(_T("Clipboard")), notifier(0), next_window(0) { + next_window = SetClipboardViewer(getHandle()); + vlog.debug("registered clipboard handler"); +} + +Clipboard::~Clipboard() { + vlog.debug("removing %x from chain (next is %x)", getHandle(), next_window); + ChangeClipboardChain(getHandle(), next_window); +} + +LRESULT +Clipboard::processMessage(UINT msg, WPARAM wParam, LPARAM lParam) { + switch (msg) { + + case WM_CHANGECBCHAIN: + vlog.debug("change clipboard chain (%x, %x)", wParam, lParam); + if ((HWND) wParam == next_window) + next_window = (HWND) lParam; + else if (next_window != 0) + SendMessage(next_window, msg, wParam, lParam); + else + vlog.error("bad clipboard chain change!"); + break; + + case WM_DRAWCLIPBOARD: + { + HWND owner = GetClipboardOwner(); + if (owner == getHandle()) { + vlog.debug("local clipboard changed by me"); + } else { + vlog.debug("local clipboard changed by %x", owner); + + // Open the clipboard + if (OpenClipboard(getHandle())) { + // Get the clipboard data + HGLOBAL cliphandle = GetClipboardData(CF_TEXT); + if (cliphandle) { + char* clipdata = (char*) GlobalLock(cliphandle); + + // Notify clients + if (notifier) { + if (!clipdata) { + notifier->notifyClipboardChanged(0, 0); + } else { + CharArray unix_text; + unix_text.buf = dos2unix(clipdata); + removeNonISOLatin1Chars(unix_text.buf); + notifier->notifyClipboardChanged(unix_text.buf, strlen(unix_text.buf)); + } + } else { + vlog.debug("no clipboard notifier registered"); + } + + // Release the buffer and close the clipboard + GlobalUnlock(cliphandle); + } + + CloseClipboard(); + } + } + } + if (next_window) + SendMessage(next_window, msg, wParam, lParam); + return 0; + + }; + return MsgWindow::processMessage(msg, wParam, lParam); +}; + +void +Clipboard::setClipText(const char* text) { + HANDLE clip_handle = 0; + + try { + + // - Firstly, we must open the clipboard + if (!OpenClipboard(getHandle())) + throw rdr::SystemException("unable to open Win32 clipboard", GetLastError()); + + // - Pre-process the supplied clipboard text into DOS format + CharArray dos_text; + dos_text.buf = unix2dos(text); + removeNonISOLatin1Chars(dos_text.buf); + int dos_text_len = strlen(dos_text.buf); + + // - Allocate global memory for the data + clip_handle = ::GlobalAlloc(GMEM_MOVEABLE, dos_text_len+1); + + char* data = (char*) GlobalLock(clip_handle); + memcpy(data, dos_text.buf, dos_text_len+1); + data[dos_text_len] = 0; + GlobalUnlock(clip_handle); + + // - Next, we must clear out any existing data + if (!EmptyClipboard()) + throw rdr::SystemException("unable to empty Win32 clipboard", GetLastError()); + + // - Set the new clipboard data + if (!SetClipboardData(CF_TEXT, clip_handle)) + throw rdr::SystemException("unable to set Win32 clipboard", GetLastError()); + clip_handle = 0; + + vlog.debug("set clipboard"); + } catch (rdr::Exception& e) { + vlog.debug(e.str()); + } + + // - Close the clipboard + if (!CloseClipboard()) + vlog.debug("unable to close Win32 clipboard: %u", GetLastError()); + else + vlog.debug("closed clipboard"); + if (clip_handle) { + vlog.debug("freeing clipboard handle"); + GlobalFree(clip_handle); + } +} diff --git a/win/rfb_win32/Clipboard.h b/win/rfb_win32/Clipboard.h new file mode 100644 index 00000000..b79768f6 --- /dev/null +++ b/win/rfb_win32/Clipboard.h @@ -0,0 +1,66 @@ +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +// -=- Clipboard.h +// +// The Clipboard is used to set the system clipboard, and to get callbacks +// when the system clipboard has changed. + +#ifndef __RFB_WIN32_CLIPBOARD_H__ +#define __RFB_WIN32_CLIPBOARD_H__ + +#include <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/win/rfb_win32/CompatibleBitmap.h b/win/rfb_win32/CompatibleBitmap.h new file mode 100644 index 00000000..4beed8dc --- /dev/null +++ b/win/rfb_win32/CompatibleBitmap.h @@ -0,0 +1,46 @@ +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +#ifndef __RFB_WIN32_COMPAT_BITMAP_H__ +#define __RFB_WIN32_COMPAT_BITMAP_H__ + +#include <windows.h> +#include <rdr/Exception.h> + +namespace rfb { + namespace win32 { + + class CompatibleBitmap { + public: + CompatibleBitmap(HDC hdc, int width, int height) { + hbmp = CreateCompatibleBitmap(hdc, width, height); + if (!hbmp) + throw rdr::SystemException("CreateCompatibleBitmap() failed", GetLastError()); + } + virtual ~CompatibleBitmap() { + if (hbmp) DeleteObject(hbmp); + } + operator HBITMAP() const {return hbmp;} + protected: + HBITMAP hbmp; + }; + + }; +}; + +#endif diff --git a/win/rfb_win32/ComputerName.h b/win/rfb_win32/ComputerName.h new file mode 100644 index 00000000..110caa59 --- /dev/null +++ b/win/rfb_win32/ComputerName.h @@ -0,0 +1,40 @@ +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +#ifndef __RFB_WIN32_COMPUTERNAME_H__ +#define __RFB_WIN32_COMPUTERNAME_H__ + +#include <windows.h> +#include <rfb_win32/TCharArray.h> + +namespace rfb { + namespace win32 { + + // Get the computer name + struct ComputerName : TCharArray { + ComputerName() : TCharArray(MAX_COMPUTERNAME_LENGTH+1) { + ULONG namelength = MAX_COMPUTERNAME_LENGTH+1; + if (!GetComputerName(buf, &namelength)) + _tcscpy(buf, _T("")); + } + }; + + }; +}; + +#endif diff --git a/win/rfb_win32/CurrentUser.cxx b/win/rfb_win32/CurrentUser.cxx new file mode 100644 index 00000000..7562d29b --- /dev/null +++ b/win/rfb_win32/CurrentUser.cxx @@ -0,0 +1,152 @@ +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +// -=- Currentuser.cxx + +#include <stdlib.h> +#include <rfb/LogWriter.h> +#include <rfb_win32/CurrentUser.h> +#include <rfb_win32/DynamicFn.h> +#include <rfb_win32/Service.h> +#include <rfb_win32/OSVersion.h> +#include <lmcons.h> + +using namespace rfb; +using namespace win32; + +static LogWriter vlog("CurrentUser"); + + +const TCHAR* shellIconClass = _T("Shell_TrayWnd"); + +BOOL CALLBACK enumWindows(HWND hwnd, LPARAM lParam) { + TCHAR className[16]; + if (GetClassName(hwnd, className, sizeof(className)) && + (_tcscmp(className, shellIconClass) == 0)) { + vlog.debug("located tray icon window (%s)", (const char*)CStr(className)); + DWORD processId = 0; + GetWindowThreadProcessId(hwnd, &processId); + if (!processId) + return TRUE; + Handle process = OpenProcess(MAXIMUM_ALLOWED, FALSE, processId); + if (!process.h) + return TRUE; + if (!OpenProcessToken(process, MAXIMUM_ALLOWED, (HANDLE*)lParam)) + return TRUE; + vlog.debug("obtained user token"); + return FALSE; + } + return TRUE; +} + +BOOL CALLBACK enumDesktops(LPTSTR lpszDesktop, LPARAM lParam) { + HDESK desktop = OpenDesktop(lpszDesktop, 0, FALSE, DESKTOP_ENUMERATE); + vlog.debug("opening \"%s\"", lpszDesktop); + if (!desktop) { + vlog.info("desktop \"%s\" inaccessible", (const char*)CStr(lpszDesktop)); + return TRUE; + } + BOOL result = EnumDesktopWindows(desktop, enumWindows, lParam); + if (!CloseDesktop(desktop)) + vlog.info("unable to close desktop: %ld", GetLastError()); + return result; +} + + +CurrentUserToken::CurrentUserToken() : isSafe_(false) { + if (isServiceProcess()) { + // If the platform is Windows 95/98/Me then we must fake the token's presence + if (osVersion.isPlatformWindows) { + try { + UserName un; + h = INVALID_HANDLE_VALUE; + } catch (rdr::SystemException& e) { + if (e.err != ERROR_NOT_LOGGED_ON) + throw; + if (FindWindow(shellIconClass, 0)) + h = INVALID_HANDLE_VALUE; + } + isSafe_ = (h != 0); + return; + } + + // Try to get the user token using the Terminal Services APIs + // NB: This will only work under XP/2003 and later + typedef BOOL (WINAPI *WTSQueryUserToken_proto)(ULONG, PHANDLE); + DynamicFn<WTSQueryUserToken_proto> _WTSQueryUserToken(_T("wtsapi32.dll"), "WTSQueryUserToken"); + if (_WTSQueryUserToken.isValid()) { + (*_WTSQueryUserToken)(-1, &h); + isSafe_ = true; + return; + } + + // Try to find the Shell Tray Icon window and take its token + // NB: This will only work under NT/2K (and later, but they're dealt with above) + // NB: If the shell is not running then this will return an Unsafe Null token. + EnumDesktops(GetProcessWindowStation(), enumDesktops, (LONG)&h); + isSafe_ = (h != 0); + } else { + // Try to open the security token for the User-Mode process + if (!OpenProcessToken(GetCurrentProcess(), GENERIC_ALL, &h)) { + DWORD err = GetLastError(); + if (err != ERROR_CALL_NOT_IMPLEMENTED) + throw rdr::SystemException("OpenProcessToken failed", err); + // Under Windows 95/98/Me, we fake the handle value... + h = INVALID_HANDLE_VALUE; + } + isSafe_ = true; + } +} + + +ImpersonateCurrentUser::ImpersonateCurrentUser() { + RegCloseKey(HKEY_CURRENT_USER); + if (!isServiceProcess()) + return; + if (!token.canImpersonate()) + throw rdr::Exception("Cannot impersonate unsafe or null token"); + if (!ImpersonateLoggedOnUser(token)) { + DWORD err = GetLastError(); + if (err != ERROR_CALL_NOT_IMPLEMENTED) + throw rdr::SystemException("Failed to impersonate user", GetLastError()); + } +} + +ImpersonateCurrentUser::~ImpersonateCurrentUser() { + if (!RevertToSelf()) { + DWORD err = GetLastError(); + if (err != ERROR_CALL_NOT_IMPLEMENTED) + exit(err); + } + RegCloseKey(HKEY_CURRENT_USER); +} + + +UserName::UserName() : TCharArray(UNLEN+1) { + DWORD len = UNLEN+1; + if (!GetUserName(buf, &len)) + throw rdr::SystemException("GetUserName failed", GetLastError()); +} + + +UserSID::UserSID() { + CurrentUserToken token; + if (!token.canImpersonate()) + return; + setSID(Sid::FromToken(token.h)); +} diff --git a/win/rfb_win32/CurrentUser.h b/win/rfb_win32/CurrentUser.h new file mode 100644 index 00000000..794f27c6 --- /dev/null +++ b/win/rfb_win32/CurrentUser.h @@ -0,0 +1,100 @@ +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +// CurrentUser.h + +// Helper class providing the session's logged on username, if +// a user is logged on. Also allows processes running under +// XP/2K3 etc to masquerade as the logged on user for security +// purposes + +#ifndef __RFB_WIN32_CURRENT_USER_H__ +#define __RFB_WIN32_CURRENT_USER_H__ + +#include <rfb_win32/Handle.h> +#include <rfb_win32/Security.h> + +namespace rfb { + + namespace win32 { + + // CurrentUserToken + // CurrentUserToken is a Handle containing the security token + // for the currently logged-on user, or null if no user is + // logged on. + // + // Under Windows 95/98/Me, which don't support security tokens, + // the token will be INVALID_HANDLE_VALUE if a user is logged on. + // + // Under Windows NT/2K, it may be the case that the token is + // null even when a user *is* logged on, because we use some hacks + // to detect the user's token and sometimes they fail. On these + // platforms, isSafe() will return False if the token is null. + // + // Under Windows XP, etc, isSafe() will always be True, and the token + // will always be set to the currently logged on user's token. + // + // canImpersonate() tests whether there is a user token that is safe + // to impersonate. + // + // noUserLoggedOn() tests whether there is *definitely* no user logged on. + + struct CurrentUserToken : public Handle { + CurrentUserToken(); + bool isSafe() const { return isSafe_; }; + bool canImpersonate() const { return h && isSafe(); } + bool noUserLoggedOn() const { return !h && isSafe(); } + private: + bool isSafe_; + }; + + // ImpersonateCurrentUser + // Throws an exception on failure. + // Succeeds (trivially) if process is not running as service. + // Fails if CurrentUserToken is not valid. + // Fails if platform is NT AND cannot impersonate token. + // Succeeds otherwise. + + struct ImpersonateCurrentUser { + ImpersonateCurrentUser(); + ~ImpersonateCurrentUser(); + CurrentUserToken token; + }; + + // UserName + // Returns the name of the user the thread is currently running as. + // Raises a SystemException in case of error. + // NB: Raises a SystemException with err == ERROR_NOT_LOGGED_ON if + // running under Windows 9x/95/Me and no user is logged on. + + struct UserName : public TCharArray { + UserName(); + }; + + // UserSID + // Returns the SID of the currently logged-on user (i.e. the session user) + + struct UserSID : public Sid { + UserSID(); + }; + + } + +} + +#endif diff --git a/win/rfb_win32/DIBSectionBuffer.cxx b/win/rfb_win32/DIBSectionBuffer.cxx new file mode 100644 index 00000000..18ce3ea4 --- /dev/null +++ b/win/rfb_win32/DIBSectionBuffer.cxx @@ -0,0 +1,222 @@ +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +#include <rfb_win32/DIBSectionBuffer.h> +#include <rfb_win32/DeviceContext.h> +#include <rfb_win32/BitmapInfo.h> +#include <rfb/LogWriter.h> + +using namespace rfb; +using namespace win32; + +static LogWriter vlog("DIBSectionBuffer"); + + +DIBSectionBuffer::DIBSectionBuffer(HWND window_) + : bitmap(0), device(0), window(window_) { + memset(&format, 0, sizeof(format)); + memset(palette, 0, sizeof(palette)); +} + +DIBSectionBuffer::DIBSectionBuffer(HDC device_) + : bitmap(0), window(0), device(device_) { + memset(&format, 0, sizeof(format)); + memset(palette, 0, sizeof(palette)); +} + +DIBSectionBuffer::~DIBSectionBuffer() { + if (bitmap) + DeleteObject(bitmap); +} + + +void DIBSectionBuffer::setPF(const PixelFormat& pf) { + if (memcmp(&getPF(), &pf, sizeof(pf)) == 0) { + vlog.debug("pixel format unchanged by setPF()"); + return; + } + format = pf; + recreateBuffer(); + if ((pf.bpp <= 8) && pf.trueColour) { + vlog.info("creating %d-bit TrueColour palette", pf.depth); + for (int i=0; i < (1<<(pf.depth)); i++) { + palette[i].b = ((((i >> pf.blueShift) & pf.blueMax) * 65535) + pf.blueMax/2) / pf.blueMax; + palette[i].g = ((((i >> pf.greenShift) & pf.greenMax) * 65535) + pf.greenMax/2) / pf.greenMax; + palette[i].r = ((((i >> pf.redShift) & pf.redMax) * 65535) + pf.redMax/2) / pf.redMax; + } + refreshPalette(); + } +} + +void DIBSectionBuffer::setSize(int w, int h) { + if (width_ == w && height_ == h) { + vlog.debug("size unchanged by setSize()"); + return; + } + width_ = w; + height_ = h; + recreateBuffer(); +} + + +// * copyPaletteToDIB MUST NEVER be called on a truecolour DIB! * + +void copyPaletteToDIB(Colour palette[256], HDC wndDC, HBITMAP dib) { + BitmapDC dibDC(wndDC, dib); + RGBQUAD rgb[256]; + for (unsigned int i=0;i<256;i++) { + rgb[i].rgbRed = palette[i].r >> 8; + rgb[i].rgbGreen = palette[i].g >> 8; + rgb[i].rgbBlue = palette[i].b >> 8; + } + if (!SetDIBColorTable(dibDC, 0, 256, (RGBQUAD*) rgb)) + throw rdr::SystemException("unable to SetDIBColorTable", GetLastError()); +} + +inline void initMaxAndShift(DWORD mask, int* max, int* shift) { + for ((*shift) = 0; (mask & 1) == 0; (*shift)++) mask >>= 1; + (*max) = (rdr::U16)mask; +} + +void DIBSectionBuffer::recreateBuffer() { + HBITMAP new_bitmap = 0; + rdr::U8* new_data = 0; + + if (width_ && height_ && (format.depth != 0)) { + BitmapInfo bi; + memset(&bi, 0, sizeof(bi)); + // *** wrong? + UINT iUsage = format.trueColour ? DIB_RGB_COLORS : DIB_PAL_COLORS; + // *** + bi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER); + bi.bmiHeader.biBitCount = format.bpp; + bi.bmiHeader.biSizeImage = (format.bpp / 8) * width_ * height_; + bi.bmiHeader.biPlanes = 1; + bi.bmiHeader.biWidth = width_; + bi.bmiHeader.biHeight = -height_; + bi.bmiHeader.biCompression = (format.bpp > 8) ? BI_BITFIELDS : BI_RGB; + bi.mask.red = format.redMax << format.redShift; + bi.mask.green = format.greenMax << format.greenShift; + bi.mask.blue = format.blueMax << format.blueShift; + + // Create a DIBSection to draw into + if (device) + new_bitmap = ::CreateDIBSection(device, (BITMAPINFO*)&bi.bmiHeader, iUsage, + (void**)&new_data, NULL, 0); + else + new_bitmap = ::CreateDIBSection(WindowDC(window), (BITMAPINFO*)&bi.bmiHeader, iUsage, + (void**)&new_data, NULL, 0); + + if (!new_bitmap) { + int err = GetLastError(); + throw rdr::SystemException("unable to create DIB section", err); + } + + vlog.debug("recreateBuffer()"); + } else { + vlog.debug("one of area or format not set"); + } + + if (new_bitmap && bitmap) { + vlog.debug("preserving bitmap contents"); + + // Copy the contents across + if (device) { + if (format.bpp <= 8) + copyPaletteToDIB(palette, device, new_bitmap); + BitmapDC src_dev(device, bitmap); + BitmapDC dest_dev(device, new_bitmap); + BitBlt(dest_dev, 0, 0, width_, height_, src_dev, 0, 0, SRCCOPY); + } else { + WindowDC wndDC(window); + if (format.bpp <= 8) + copyPaletteToDIB(palette, wndDC, new_bitmap); + BitmapDC src_dev(wndDC, bitmap); + BitmapDC dest_dev(wndDC, new_bitmap); + BitBlt(dest_dev, 0, 0, width_, height_, src_dev, 0, 0, SRCCOPY); + } + } + + if (bitmap) { + // Delete the old bitmap + DeleteObject(bitmap); + bitmap = 0; + data = 0; + } + + if (new_bitmap) { + // Set up the new bitmap + bitmap = new_bitmap; + data = new_data; + + // Determine the *actual* DIBSection format + DIBSECTION ds; + if (!GetObject(bitmap, sizeof(ds), &ds)) + throw rdr::SystemException("GetObject", GetLastError()); + + // Correct the "stride" of the DIB + // *** This code DWORD aligns each row - is that right??? + stride = width_; + int bytesPerRow = stride * format.bpp/8; + if (bytesPerRow % 4) { + bytesPerRow += 4 - (bytesPerRow % 4); + stride = (bytesPerRow * 8) / format.bpp; + vlog.info("adjusting DIB stride: %d to %d", width_, stride); + } + + // Calculate the PixelFormat for the DIB + format.bigEndian = 0; + format.bpp = format.depth = ds.dsBm.bmBitsPixel; + format.trueColour = format.trueColour || format.bpp > 8; + if (format.bpp > 8) { + + // Get the truecolour format used by the DIBSection + initMaxAndShift(ds.dsBitfields[0], &format.redMax, &format.redShift); + initMaxAndShift(ds.dsBitfields[1], &format.greenMax, &format.greenShift); + initMaxAndShift(ds.dsBitfields[2], &format.blueMax, &format.blueShift); + + // Calculate the effective depth + format.depth = 0; + Pixel bits = ds.dsBitfields[0] | ds.dsBitfields[1] | ds.dsBitfields[2]; + while (bits) { + format.depth++; + bits = bits >> 1; + } + if (format.depth > format.bpp) + throw Exception("Bad DIBSection format (depth exceeds bpp)"); + } else { + // Set the DIBSection's palette + refreshPalette(); + } + + } +} + +void DIBSectionBuffer::refreshPalette() { + if (format.bpp > 8) { + vlog.error("refresh palette called for truecolor DIB"); + return; + } + vlog.debug("refreshing palette"); + if (device) + copyPaletteToDIB(palette, device, bitmap); + else + copyPaletteToDIB(palette, WindowDC(window), bitmap); +} + + diff --git a/win/rfb_win32/DIBSectionBuffer.h b/win/rfb_win32/DIBSectionBuffer.h new file mode 100644 index 00000000..ad1a310c --- /dev/null +++ b/win/rfb_win32/DIBSectionBuffer.h @@ -0,0 +1,85 @@ +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +// -=- DIBSectionBuffer.h + +// A DIBSectionBuffer acts much like a standard PixelBuffer, but is associated +// with a particular window on-screen and can be drawn into that window if +// required, using the standard Win32 drawing operations. + +#ifndef __RFB_WIN32_DIB_SECTION_BUFFER_H__ +#define __RFB_WIN32_DIB_SECTION_BUFFER_H__ + +#include <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/win/rfb_win32/DeviceContext.cxx b/win/rfb_win32/DeviceContext.cxx new file mode 100644 index 00000000..4f70a1bf --- /dev/null +++ b/win/rfb_win32/DeviceContext.cxx @@ -0,0 +1,188 @@ +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +#include <rfb_win32/DeviceContext.h> +#include <rfb_win32/CompatibleBitmap.h> +#include <rfb_win32/BitmapInfo.h> +#include <rdr/Exception.h> +#include <rfb/LogWriter.h> + +using namespace rfb; +using namespace win32; + + +static LogWriter vlog("DeviceContext"); + +PixelFormat DeviceContext::getPF() const { + return getPF(dc); +} + +PixelFormat DeviceContext::getPF(HDC dc) { + PixelFormat format; + CompatibleBitmap bitmap(dc, 1, 1); + + // -=- Get the bitmap format information + BitmapInfo bi; + memset(&bi, 0, sizeof(bi)); + bi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER); + bi.bmiHeader.biBitCount = 0; + if (!::GetDIBits(dc, bitmap, 0, 1, NULL, (BITMAPINFO*)&bi, DIB_RGB_COLORS)) { + throw rdr::SystemException("unable to determine device pixel format", GetLastError()); + } + if (!::GetDIBits(dc, bitmap, 0, 1, NULL, (BITMAPINFO*)&bi, DIB_RGB_COLORS)) { + throw rdr::SystemException("unable to determine pixel shifts/palette", GetLastError()); + } + + // Set the initial format information + format.trueColour = bi.bmiHeader.biBitCount > 8; + format.bigEndian = 0; + format.bpp = bi.bmiHeader.biBitCount; + + if (format.trueColour) { + DWORD rMask=0, gMask=0, bMask=0; + + // Which true colour format is the DIB section using? + switch (bi.bmiHeader.biCompression) { + case BI_RGB: + // Default RGB layout + switch (bi.bmiHeader.biBitCount) { + case 16: + // RGB 555 - High Colour + vlog.info("16-bit High Colour"); + rMask = 0x7c00; + bMask = 0x001f; + gMask = 0x03e0; + break; + case 24: + case 32: + // RGB 888 - True Colour + vlog.info("24/32-bit High Colour"); + rMask = 0xff0000; + gMask = 0x00ff00; + bMask = 0x0000ff; + break; + default: + vlog.error("bits per pixel %u not supported", bi.bmiHeader.biBitCount); + throw rdr::Exception("unknown bits per pixel specified"); + }; + break; + case BI_BITFIELDS: + // Custom RGB layout + rMask = bi.mask.red; + gMask = bi.mask.green; + bMask = bi.mask.blue; + vlog.info("%lu-bit BitFields: (%lx, %lx, %lx)", + bi.bmiHeader.biBitCount, rMask, gMask, bMask); + break; + }; + + // Convert the data we just retrieved + initMaxAndShift(rMask, &format.redMax, &format.redShift); + initMaxAndShift(gMask, &format.greenMax, &format.greenShift); + initMaxAndShift(bMask, &format.blueMax, &format.blueShift); + + // Calculate the depth from the colour shifts + format.depth = 0; + Pixel bits = rMask | gMask | bMask; + while (bits) { + format.depth++; + bits = bits >> 1; + } + + // Check that the depth & bpp are valid + if (format.depth > format.bpp) { + vlog.error("depth exceeds bits per pixel!"); + format.bpp = format.depth; + } + + // Correct the bits-per-pixel to something we're happy with + if (format.bpp <= 16) + format.bpp = 16; + else if (format.bpp <= 32) + format.bpp = 32; + } else { + // Palettised format - depth reflects number of colours, + // but bits-per-pixel is ALWAYS 8 + format.depth = format.bpp; + if (format.bpp < 8) + format.bpp = 8; + vlog.info("%d-colour palettised", 1<<format.depth); + } + + return format; +} + +Rect DeviceContext::getClipBox() const { + return getClipBox(dc); +} + +Rect DeviceContext::getClipBox(HDC dc) { + // Get the display dimensions + RECT cr; + if (!GetClipBox(dc, &cr)) + throw rdr::SystemException("GetClipBox", GetLastError()); + return Rect(cr.left, cr.top, cr.right, cr.bottom); +} + + +DeviceDC::DeviceDC(const TCHAR* deviceName) { + dc = ::CreateDC(_T("DISPLAY"), deviceName, NULL, NULL); + if (!dc) + throw rdr::SystemException("failed to create DeviceDC", GetLastError()); +} + +DeviceDC::~DeviceDC() { + if (dc) + DeleteDC(dc); +} + + +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); +} diff --git a/win/rfb_win32/DeviceContext.h b/win/rfb_win32/DeviceContext.h new file mode 100644 index 00000000..9d91cec2 --- /dev/null +++ b/win/rfb_win32/DeviceContext.h @@ -0,0 +1,86 @@ +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +// DeviceContext base class, wrapping Windows HDC, plus some +// helper classes tailored to particular types of DC, such as +// window and device DCs. + +#ifndef __RFB_WIN32_DEVICECONTEXT_H__ +#define __RFB_WIN32_DEVICECONTEXT_H__ + +#include <windows.h> +#include <rfb/PixelFormat.h> +#include <rfb/Rect.h> +#include <rfb_win32/TCharArray.h> + +namespace rfb { + + namespace win32 { + + // Base class, providing methods to get the bounding (clip) box, + // and the pixel format, and access to the HDC itself. + class DeviceContext { + public: + DeviceContext() : dc(0) {} + virtual ~DeviceContext() {} + operator HDC() const {return dc;} + PixelFormat getPF() const; + static PixelFormat getPF(HDC dc); + Rect getClipBox() const; + static Rect getClipBox(HDC dc); + protected: + HDC dc; + }; + + // -=- DeviceContext that opens a specific display device + class DeviceDC : public DeviceContext { + public: + DeviceDC(const TCHAR* deviceName); + ~DeviceDC(); + }; + + // Get a DC for a particular window's client area. + class WindowDC : public DeviceContext { + public: + WindowDC(HWND wnd); + virtual ~WindowDC(); + protected: + HWND hwnd; + }; + + // Create a new DC, compatible with an existing one. + class CompatibleDC : public DeviceContext { + public: + CompatibleDC(HDC existing); + virtual ~CompatibleDC(); + }; + + // Create a new DC, compatible with an existing one, and + // select the specified bitmap into it. + class BitmapDC : public CompatibleDC { + public: + BitmapDC(HDC hdc, HBITMAP hbitmap); + ~BitmapDC(); + protected: + HBITMAP oldBitmap; + }; + + }; +}; + +#endif diff --git a/win/rfb_win32/DeviceFrameBuffer.cxx b/win/rfb_win32/DeviceFrameBuffer.cxx new file mode 100644 index 00000000..8da894e0 --- /dev/null +++ b/win/rfb_win32/DeviceFrameBuffer.cxx @@ -0,0 +1,289 @@ +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +// -=- DeviceFrameBuffer.cxx +// +// The DeviceFrameBuffer class encapsulates the pixel data of the system +// display. + +#include <vector> +#include <rfb_win32/DeviceFrameBuffer.h> +#include <rfb_win32/DeviceContext.h> +#include <rfb_win32/OSVersion.h> +#include <rfb_win32/IconInfo.h> +#include <rfb/VNCServer.h> +#include <rfb/LogWriter.h> + +using namespace rfb; +using namespace win32; + +static LogWriter vlog("DeviceFrameBuffer"); + +BoolParameter DeviceFrameBuffer::useCaptureBlt("UseCaptureBlt", + "Use a slower capture method that ensures that alpha blended windows appear correctly", + true); + + +// -=- DeviceFrameBuffer class + +DeviceFrameBuffer::DeviceFrameBuffer(HDC deviceContext, const Rect& wRect) + : DIBSectionBuffer(deviceContext), device(deviceContext), cursorBm(deviceContext), + ignoreGrabErrors(false) +{ + + // -=- Firstly, let's check that the device has suitable capabilities + + int capabilities = GetDeviceCaps(device, RASTERCAPS); + if (!(capabilities & RC_BITBLT)) { + throw Exception("device does not support BitBlt"); + } + if (!(capabilities & RC_DI_BITMAP)) { + throw Exception("device does not support GetDIBits"); + } + /* + if (GetDeviceCaps(device, PLANES) != 1) { + throw Exception("device does not support planar displays"); + } + */ + + // -=- Get the display dimensions and pixel format + + // Get the display dimensions + deviceCoords = DeviceContext::getClipBox(device); + if (!wRect.is_empty()) + deviceCoords = wRect.translate(deviceCoords.tl); + int w = deviceCoords.width(); + int h = deviceCoords.height(); + + // We can't handle uneven widths :( + if (w % 2) w--; + + // Configure the underlying DIB to match the device + DIBSectionBuffer::setPF(DeviceContext::getPF(device)); + DIBSectionBuffer::setSize(w, h); + + // Configure the cursor buffer + cursorBm.setPF(format); + + // Set up a palette if required + if (!format.trueColour) + updateColourMap(); +} + +DeviceFrameBuffer::~DeviceFrameBuffer() { +} + + +void +DeviceFrameBuffer::setPF(const PixelFormat &pf) { + throw Exception("setPF not supported"); +} + +void +DeviceFrameBuffer::setSize(int w, int h) { + throw Exception("setSize not supported"); +} + + +#ifndef CAPTUREBLT +#define CAPTUREBLT 0x40000000 +#endif + +void +DeviceFrameBuffer::grabRect(const Rect &rect) { + BitmapDC tmpDC(device, bitmap); + + // Map the rectangle coords from VNC Desktop-relative to device relative - usually (0,0) + Point src = desktopToDevice(rect.tl); + + // Note: Microsoft's documentation lies directly about CAPTUREBLT and claims it works on 98/ME + // If you try CAPTUREBLT on 98 then you get blank output... + if (!::BitBlt(tmpDC, rect.tl.x, rect.tl.y, rect.width(), rect.height(), device, src.x, src.y, + (osVersion.isPlatformNT && useCaptureBlt) ? (CAPTUREBLT | SRCCOPY) : SRCCOPY)) { + if (ignoreGrabErrors) + vlog.error("BitBlt failed:%ld", GetLastError()); + else + throw rdr::SystemException("BitBlt failed", GetLastError()); + } +} + +void +DeviceFrameBuffer::grabRegion(const Region &rgn) { + std::vector<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 color palette"); + for (unsigned int i=0;i<256;i++) { + int v = (i%16) >= 8 ? 127 : 255; + syspalette[i].peRed = i & 1 ? v : 0; + syspalette[i].peGreen = i & 2 ? v : 0; + syspalette[i].peBlue = i & 4 ? v : 0; + } + } else { + vlog.info("framebuffer has %u palette entries", entries); + } + + // - Update the bitmap's stored copy of the palette + for (unsigned int i=0;i<256;i++) { + int r, g, b; + r = (syspalette[i].peRed << 8) + 0x80; + g = (syspalette[i].peGreen << 8) + 0x80; + b = (syspalette[i].peBlue << 8) + 0x80; + dib->setColour(i, r, g, b); + } + + // - Update the DIB section to use the palette + dib->refreshPalette(); +} + + +void DeviceFrameBuffer::setCursor(HCURSOR hCursor, VNCServer* server) +{ + // - If hCursor is null then there is no cursor - clear the old one + + if (hCursor == 0) { + server->setCursor(0, 0, Point(), 0, 0); + return; + } + + try { + + // - Get the size and other details about the cursor. + + IconInfo iconInfo((HICON)hCursor); + + BITMAP maskInfo; + if (!GetObject(iconInfo.hbmMask, sizeof(BITMAP), &maskInfo)) + throw rdr::SystemException("GetObject() failed", GetLastError()); + if (maskInfo.bmPlanes != 1) + throw rdr::Exception("unsupported multi-plane cursor"); + if (maskInfo.bmBitsPixel != 1) + throw rdr::Exception("unsupported cursor mask format"); + + // - Create the cursor pixel buffer and mask storage + // NB: The cursor pixel buffer is NOT used here. Instead, we + // pass the cursorBm.data pointer directly, to save overhead. + + cursor.setSize(maskInfo.bmWidth, maskInfo.bmHeight); + cursor.setPF(format); + cursor.hotspot = Point(iconInfo.xHotspot, iconInfo.yHotspot); + + // - Get the AND and XOR masks. There is only an XOR mask if this is not a + // colour cursor. + + if (!iconInfo.hbmColor) + cursor.setSize(cursor.width(), cursor.height() / 2); + rdr::U8Array mask(maskInfo.bmWidthBytes * maskInfo.bmHeight); + rdr::U8* xorMask = mask.buf + cursor.height() * maskInfo.bmWidthBytes; + + if (!GetBitmapBits(iconInfo.hbmMask, + maskInfo.bmWidthBytes * maskInfo.bmHeight, mask.buf)) + throw rdr::SystemException("GetBitmapBits failed", GetLastError()); + + // Configure the cursor bitmap + cursorBm.setSize(cursor.width(), cursor.height()); + + // Copy the palette into it if required + if (format.bpp <= 8) + copyDevicePaletteToDIB(device, &cursorBm); + + // Draw the cursor into the bitmap + BitmapDC dc(device, cursorBm.bitmap); + if (!DrawIconEx(dc, 0, 0, hCursor, 0, 0, 0, NULL, DI_NORMAL | DI_COMPAT)) + throw rdr::SystemException("unable to render cursor", GetLastError()); + + // Replace any XORed pixels with xorColour, because RFB doesn't support + // XORing of cursors. XORing is used for the I-beam cursor, which is most + // often used over a white background, but also sometimes over a black + // background. We set the XOR'd pixels to black, then draw a white outline + // around the whole cursor. + + // *** should we replace any pixels not set in mask to zero, to ensure + // that irrelevant data doesn't screw compression? + + bool doOutline = false; + if (!iconInfo.hbmColor) { + Pixel xorColour = format.pixelFromRGB(0, 0, 0, cursorBm.getColourMap()); + for (int y = 0; y < cursor.height(); y++) { + bool first = true; + for (int x = 0; x < cursor.width(); x++) { + int byte = y * maskInfo.bmWidthBytes + x / 8; + int bit = 7 - x % 8; + if ((mask.buf[byte] & (1 << bit)) && (xorMask[byte] & (1 << bit))) + { + mask.buf[byte] &= ~(1 << bit); + + switch (format.bpp) { + case 8: + ((rdr::U8*)cursorBm.data)[y * cursor.width() + x] = xorColour; break; + case 16: + ((rdr::U16*)cursorBm.data)[y * cursor.width() + x] = xorColour; break; + case 32: + ((rdr::U32*)cursorBm.data)[y * cursor.width() + x] = xorColour; break; + } + + doOutline = true; + } + } + } + } + + // Finally invert the AND mask so it's suitable for RFB and pack it into + // the minimum number of bytes per row. + + int maskBytesPerRow = (cursor.width() + 7) / 8; + + for (int j = 0; j < cursor.height(); j++) { + for (int i = 0; i < maskBytesPerRow; i++) + cursor.mask.buf[j * maskBytesPerRow + i] + = ~mask.buf[j * maskInfo.bmWidthBytes + i]; + } + + if (doOutline) { + vlog.debug("drawing cursor outline!"); + memcpy(cursor.data, cursorBm.data, cursor.dataLen()); + cursor.drawOutline(format.pixelFromRGB(0xffff, 0xffff, 0xffff, cursorBm.getColourMap())); + memcpy(cursorBm.data, cursor.data, cursor.dataLen()); + } + + server->setCursor(cursor.width(), cursor.height(), cursor.hotspot, + cursorBm.data, cursor.mask.buf); + } catch (rdr::Exception& e) { + vlog.error(e.str()); + } +} + + +void +DeviceFrameBuffer::updateColourMap() { + if (!format.trueColour) + copyDevicePaletteToDIB(device, this); +} diff --git a/win/rfb_win32/DeviceFrameBuffer.h b/win/rfb_win32/DeviceFrameBuffer.h new file mode 100644 index 00000000..7718c339 --- /dev/null +++ b/win/rfb_win32/DeviceFrameBuffer.h @@ -0,0 +1,106 @@ +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +// -=- DeviceFrameBuffer.h +// +// The DeviceFrameBuffer class encapsulates the pixel data of a supplied +// Device Context Handle (HDC) + +// *** THIS INTERFACE NEEDS TIDYING TO SEPARATE COORDINATE SYSTEMS BETTER *** + +#ifndef __RFB_WIN32_DEVICE_FRAME_BUFFER_H__ +#define __RFB_WIN32_DEVICE_FRAME_BUFFER_H__ + +#include <windows.h> +#include <rfb_win32/DIBSectionBuffer.h> +#include <rfb/Cursor.h> +#include <rfb/Region.h> +#include <rfb/Exception.h> +#include <rfb/Configuration.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 ®ion); + + // - DIBSectionBuffer overrides + + virtual void setPF(const PixelFormat& pf); + virtual void setSize(int w, int h); + + // - DeviceFrameBuffer specific methods + + void setCursor(HCURSOR c, VNCServer* server); + void updateColourMap(); + + // Set whether grabRect should ignore errors or throw exceptions + // Only set this if you are sure you'll capture the errors some other way! + void setIgnoreGrabErrors(bool ie) {ignoreGrabErrors=ie;} + + static BoolParameter useCaptureBlt; + + protected: + // Translate supplied Desktop coordinates into Device-relative coordinates + // This translation may have been affected at start-time by the supplied sub-rect. + Point desktopToDevice(const Point p) const {return p.translate(deviceCoords.tl);} + + HDC device; + DIBSectionBuffer cursorBm; + Cursor cursor; + Rect deviceCoords; + bool ignoreGrabErrors; + }; + + }; + +}; + +#endif // __RFB_WIN32_DEVICE_FRAME_BUFFER_H__ diff --git a/win/rfb_win32/Dialog.cxx b/win/rfb_win32/Dialog.cxx new file mode 100644 index 00000000..398334f0 --- /dev/null +++ b/win/rfb_win32/Dialog.cxx @@ -0,0 +1,391 @@ +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +// -=- Dialog.cxx + +// Base-class for any Dialog classes we might require + +#include <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 +#ifdef PropSheet_IndexToId +#include <rfb_win32/DeviceFrameBuffer.h> +#include <extra/LoadBMP.cxx> +#else +#undef _DIALOG_CAPTURE +#pragma message(" NOTE: Not building Dialog Capture support.") +#endif +#endif + +using namespace rfb; +using namespace rfb::win32; + +static LogWriter dlog("Dialog"); +static LogWriter plog("PropSheet"); + + +Dialog::Dialog(HINSTANCE inst_) +: inst(inst_), alreadyShowing(false), handle(0) +{ +} + +Dialog::~Dialog() +{ +} + + +bool Dialog::showDialog(const TCHAR* resource, HWND owner) +{ + if (alreadyShowing) return false; + handle = 0; + alreadyShowing = true; + INT_PTR result = DialogBoxParam(inst, resource, owner, + staticDialogProc, (LPARAM)this); + if (result<0) + throw rdr::SystemException("DialogBoxParam failed", GetLastError()); + alreadyShowing = false; + return (result == 1); +} + + +bool Dialog::isItemChecked(int id) { + return SendMessage(GetDlgItem(handle, id), BM_GETCHECK, 0, 0) == BST_CHECKED; +} +int Dialog::getItemInt(int id) { + BOOL trans; + int result = GetDlgItemInt(handle, id, &trans, TRUE); + if (!trans) + throw rdr::Exception("unable to read dialog Int"); + return result; +} +TCHAR* Dialog::getItemString(int id) { + TCharArray tmp(256); + if (!GetDlgItemText(handle, id, tmp.buf, 256)) + tmp.buf[0] = 0; + return tmp.takeBuf(); +} + +void Dialog::setItemChecked(int id, bool state) { + dlog.debug("bool[%d]=%d", id, (int)state); + SendMessage(GetDlgItem(handle, id), BM_SETCHECK, state ? BST_CHECKED : BST_UNCHECKED, 0); +} +void Dialog::setItemInt(int id, int value) { + dlog.debug("int[%d]=%d", id, value); + SetDlgItemInt(handle, id, value, TRUE); +} +void Dialog::setItemString(int id, const TCHAR* s) { + dlog.debug("string[%d]=%s", id, (const char*)CStr(s)); + SetDlgItemText(handle, id, s); +} + + +void Dialog::enableItem(int id, bool state) { + dlog.debug("enable[%d]=%d", id, (int)state); + EnableWindow(GetDlgItem(handle, id), state); +} + + + + +BOOL CALLBACK Dialog::staticDialogProc(HWND hwnd, UINT msg, + WPARAM wParam, LPARAM lParam) +{ + if (msg == WM_INITDIALOG) + SetWindowLong(hwnd, GWL_USERDATA, (LONG)lParam); + + LONG self = GetWindowLong(hwnd, GWL_USERDATA); + if (!self) return FALSE; + + return ((Dialog*)self)->dialogProc(hwnd, msg, wParam, lParam); +} + +BOOL Dialog::dialogProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) +{ + switch (msg) { + + case WM_INITDIALOG: + handle = hwnd; + initDialog(); + return TRUE; + + case WM_COMMAND: + switch (LOWORD(wParam)) { + case IDOK: + if (onOk()) { + EndDialog(hwnd, 1); + return TRUE; + } + return FALSE; + case IDCANCEL: + EndDialog(hwnd, 0); + return TRUE; + default: + return onCommand(LOWORD(wParam), HIWORD(wParam)); + }; + + case WM_HELP: + return onHelp(((HELPINFO*)lParam)->iCtrlId); + + } + + return FALSE; +} + + +PropSheetPage::PropSheetPage(HINSTANCE inst, const TCHAR* id) : Dialog(inst), propSheet(0) { + page.dwSize = sizeof(page); + page.dwFlags = 0; // PSP_USECALLBACK; + page.hInstance = inst; + page.pszTemplate = id; + page.pfnDlgProc = staticPageProc; + page.lParam = (LPARAM)this; + page.pfnCallback = 0; // staticPageProc; +} + +PropSheetPage::~PropSheetPage() { +} + + +BOOL CALLBACK PropSheetPage::staticPageProc(HWND hwnd, UINT msg, + WPARAM wParam, LPARAM lParam) +{ + if (msg == WM_INITDIALOG) + SetWindowLong(hwnd, GWL_USERDATA, ((PROPSHEETPAGE*)lParam)->lParam); + + LONG self = GetWindowLong(hwnd, GWL_USERDATA); + if (!self) return FALSE; + + return ((PropSheetPage*)self)->dialogProc(hwnd, msg, wParam, lParam); +} + +BOOL PropSheetPage::dialogProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) +{ + switch (msg) { + + case WM_INITDIALOG: + handle = hwnd; + initDialog(); + return TRUE; + + case WM_NOTIFY: + switch (((NMHDR*)lParam)->code) { + case PSN_APPLY: + onOk(); + return FALSE; + }; + return FALSE; + + case WM_COMMAND: + return onCommand(LOWORD(wParam), HIWORD(wParam)); + + case WM_HELP: + return onHelp(((HELPINFO*)lParam)->iCtrlId); + + } + + return FALSE; +} + + +PropSheet::PropSheet(HINSTANCE inst_, const TCHAR* title_, std::list<PropSheetPage*> pages_, HICON icon_) +: title(tstrDup(title_)), inst(inst_), pages(pages_), alreadyShowing(0), handle(0), icon(icon_) { +} + +PropSheet::~PropSheet() { +} + + +// For some reason, DLGTEMPLATEEX isn't defined in the Windows headers - go figure... +struct DLGTEMPLATEEX { + WORD dlgVer; + WORD signature; + DWORD helpID; + DWORD exStyle; + DWORD style; + WORD cDlgItems; + short x; + short y; + short cx; + short cy; +}; + +static int CALLBACK removeCtxtHelp(HWND hwnd, UINT message, LPARAM lParam) { + if (message == PSCB_PRECREATE) { + // Remove the context-help style, to remove the titlebar ? button + // *** Nasty hack to cope with new & old dialog template formats... + if (((DLGTEMPLATEEX*)lParam)->signature == 0xffff) + ((DLGTEMPLATEEX*)lParam)->style &= ~DS_CONTEXTHELP; + else + ((LPDLGTEMPLATE)lParam)->style &= ~DS_CONTEXTHELP; + } + return TRUE; +} + + +bool PropSheet::showPropSheet(HWND owner, bool showApply, bool showCtxtHelp, bool capture) { + if (alreadyShowing) return false; + alreadyShowing = true; + int count = pages.size(); + + HPROPSHEETPAGE* hpages = new HPROPSHEETPAGE[count]; + try { + // Create the PropertSheet page GDI objects. + std::list<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_USECALLBACK); + header.pfnCallback = removeCtxtHelp; + header.hwndParent = owner; + header.hInstance = inst; + header.pszCaption = title.buf; + header.nPages = count; + header.nStartPage = 0; + header.phpage = hpages; + if (icon) { + header.hIcon = icon; + header.dwFlags |= PSH_USEHICON; + } + + handle = (HWND)PropertySheet(&header); + if ((handle == 0) || (handle == (HWND)-1)) + throw rdr::SystemException("PropertySheet failed", GetLastError()); + centerWindow(handle, owner); + plog.info("created %lx", handle); + +#ifdef _DIALOG_CAPTURE + if (capture) { + plog.info("capturing \"%s\"", (const char*)CStr(title.buf)); + char* tmpdir = getenv("TEMP"); + HDC dc = GetWindowDC(handle); + DeviceFrameBuffer fb(dc); + int i=0; + while (true) { + int id = PropSheet_IndexToId(handle, i); + if (!id) break; + PropSheet_SetCurSelByID(handle, id); + MSG msg; + while (PeekMessage(&msg, handle, 0, 0, PM_REMOVE)) { + if (!PropSheet_IsDialogMessage(handle, &msg)) + DispatchMessage(&msg); + } + fb.grabRect(fb.getRect()); + TCHAR title[128]; + if (!GetWindowText(PropSheet_GetCurrentPageHwnd(handle), title, sizeof(title))) + _stprintf(title, _T("capture%d"), i); + CharArray pageTitle(strDup(title)); + for (int j=0; j<strlen(pageTitle.buf); j++) { + if (pageTitle.buf[j] == '/' || pageTitle.buf[j] == '\\' || pageTitle.buf[j] == ':') + pageTitle.buf[j] = '-'; + } + char filename[256]; + sprintf(filename, "%s\\%s.bmp", tmpdir, pageTitle.buf); + vlog.debug("writing to %s", filename); + saveBMP(filename, &fb); + i++; + } + ReleaseDC(handle, dc); + } else { +#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; + } +#ifdef _DIALOG_CAPTURE + } +#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/win/rfb_win32/Dialog.h b/win/rfb_win32/Dialog.h new file mode 100644 index 00000000..9784ba46 --- /dev/null +++ b/win/rfb_win32/Dialog.h @@ -0,0 +1,158 @@ +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +// -=- RegConfig.h + +// Class which monitors the registry and reads in the registry settings +// whenever they change, or are added or removed. + +#ifndef __RFB_WIN32_DIALOG_H__ +#define __RFB_WIN32_DIALOG_H__ + +#include <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/win/rfb_win32/DynamicFn.cxx b/win/rfb_win32/DynamicFn.cxx new file mode 100644 index 00000000..e933f249 --- /dev/null +++ b/win/rfb_win32/DynamicFn.cxx @@ -0,0 +1,45 @@ +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +#include <rfb_win32/DynamicFn.h> +#include <rfb_win32/TCharArray.h> +#include <rfb/LogWriter.h> + +using namespace rfb; +using namespace win32; + +static LogWriter vlog("DynamicFn"); + + +DynamicFnBase::DynamicFnBase(const TCHAR* dllName, const char* fnName) : dllHandle(0), fnPtr(0) { + dllHandle = LoadLibrary(dllName); + if (!dllHandle) { + vlog.info("DLL %s not found (%d)", (const char*)CStr(dllName), GetLastError()); + return; + } + fnPtr = GetProcAddress(dllHandle, fnName); + if (!fnPtr) + vlog.info("proc %s not found in %s (%d)", fnName, (const char*)CStr(dllName), GetLastError()); +} + +DynamicFnBase::~DynamicFnBase() { + if (dllHandle) + FreeLibrary(dllHandle); +} + + diff --git a/win/rfb_win32/DynamicFn.h b/win/rfb_win32/DynamicFn.h new file mode 100644 index 00000000..57fdbec5 --- /dev/null +++ b/win/rfb_win32/DynamicFn.h @@ -0,0 +1,51 @@ +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +// Helper class managing dynamic linkage to DLL functions. + +#ifndef __RFB_WIN32_DYNAMICFN_H__ +#define __RFB_WIN32_DYNAMICFN_H__ + +#include <windows.h> + +namespace rfb { + namespace win32 { + + class DynamicFnBase { + public: + DynamicFnBase(const TCHAR* dllName, const char* fnName); + ~DynamicFnBase(); + bool isValid() const {return fnPtr != 0;} + protected: + void* fnPtr; + HMODULE dllHandle; + private: + DynamicFnBase(const DynamicFnBase&); + DynamicFnBase operator=(const DynamicFnBase&); + }; + + template<class T> class DynamicFn : public DynamicFnBase { + public: + DynamicFn(const TCHAR* dllName, const char* fnName) : DynamicFnBase(dllName, fnName) {} + T operator *() const {return (T)fnPtr;}; + }; + + }; +}; + +#endif diff --git a/win/rfb_win32/EventManager.cxx b/win/rfb_win32/EventManager.cxx new file mode 100644 index 00000000..0f9993b7 --- /dev/null +++ b/win/rfb_win32/EventManager.cxx @@ -0,0 +1,103 @@ +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +#include <rfb_win32/EventManager.h> +#include <rdr/Exception.h> +#include <rfb/LogWriter.h> + +using namespace rfb; +using namespace rfb::win32; + +static LogWriter vlog("EventManager"); + + +EventManager::EventManager() : eventCount(0) { +} + +EventManager::~EventManager() { +} + + +bool EventManager::addEvent(HANDLE event, EventHandler* ecb) { + if (eventCount >= MAXIMUM_WAIT_OBJECTS-1) + return false; + events[eventCount] = event; + handlers[eventCount] = ecb; + eventCount++; + return true; +} + +void EventManager::removeEvent(HANDLE event) { + for (int i=0; i<eventCount; i++) { + if (events[i] == event) { + for (int j=i; j<eventCount-1; j++) { + events[j] = events[j+1]; + handlers[j] = handlers[j+1]; + } + eventCount--; + return; + } + } + throw rdr::Exception("Event not registered"); +} + + +int EventManager::checkTimeouts() { + return 0; +} + +BOOL EventManager::getMessage(MSG* msg, HWND hwnd, UINT minMsg, UINT maxMsg) { + while (true) { + // - Process any pending timeouts + DWORD timeout = checkTimeouts(); + if (timeout == 0) + timeout = INFINITE; + + // - Events take precedence over messages + DWORD result; + if (eventCount) { + // - Check whether any events are set + result = WaitForMultipleObjects(eventCount, events, FALSE, 0); + if (result == WAIT_TIMEOUT) { + // - No events are set, so check for messages + if (PeekMessage(msg, hwnd, minMsg, maxMsg, PM_REMOVE)) + return msg->message != WM_QUIT; + + // - Block waiting for an event to be set, or a message + result = MsgWaitForMultipleObjects(eventCount, events, FALSE, timeout, + QS_ALLINPUT); + if (result == WAIT_OBJECT_0 + eventCount) { + // - Return the message, if any + if (PeekMessage(msg, hwnd, minMsg, maxMsg, PM_REMOVE)) + return msg->message != WM_QUIT; + continue; + } + } + } else + return GetMessage(msg, hwnd, minMsg, maxMsg); + + if ((result >= WAIT_OBJECT_0) && (result < (WAIT_OBJECT_0 + eventCount))) { + // - An event was set - call the handler + int index = result - WAIT_OBJECT_0; + handlers[index]->processEvent(events[index]); + } else if (result == WAIT_FAILED) { + // - An error has occurred, so return the error status code + return -1; + } + } +} diff --git a/win/rfb_win32/EventManager.h b/win/rfb_win32/EventManager.h new file mode 100644 index 00000000..bb66e340 --- /dev/null +++ b/win/rfb_win32/EventManager.h @@ -0,0 +1,77 @@ +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +// -=- EventManager.h + +// Win32 event manager. Caller supplies event & handler pairs and +// then uses getMessage() in place of ::GetMessage() in the main +// loop. EventManager calls the event handler whenever the event +// is set. +// Ownership of events remains with the caller. +// It is the responsibility of handlers to reset events. + +#ifndef __RFB_WIN32_EVENT_MGR_H__ +#define __RFB_WIN32_EVENT_MGR_H__ + +#include <rfb_win32/Win32Util.h> + +namespace rfb { + namespace win32 { + + class EventHandler { + public: + virtual ~EventHandler() {} + virtual void processEvent(HANDLE event) = 0; + }; + + class EventManager { + public: + EventManager(); + virtual ~EventManager(); + + // Add a Win32 event & handler for it + // NB: The handler must call ResetEvent on the event. + // NB: The caller retains ownership of the event. + virtual bool addEvent(HANDLE event, EventHandler* ecb); + + // Remove a Win32 event + virtual void removeEvent(HANDLE event); + + // getMessage + // Waits for a message to become available on the thread's message queue, + // and returns it. If any registered events become set while waiting then + // their handlers are called before returning. + // Returns zero if the message is WM_QUIT, -1 in case of error, >0 otherwise. + virtual BOOL getMessage(MSG* msg, HWND hwnd, UINT minMsg, UINT maxMsg); + + protected: + // checkTimeouts + // Derived classes should override this to perform any extra processing, + // returning the maximum number of milliseconds after which the callback + // should be called again. + virtual int checkTimeouts(); + + HANDLE events[MAXIMUM_WAIT_OBJECTS]; + EventHandler* handlers[MAXIMUM_WAIT_OBJECTS-1]; + int eventCount; + }; + + }; +}; + +#endif diff --git a/win/rfb_win32/FolderManager.cxx b/win/rfb_win32/FolderManager.cxx new file mode 100644 index 00000000..a2fa08d3 --- /dev/null +++ b/win/rfb_win32/FolderManager.cxx @@ -0,0 +1,279 @@ +/* Copyright (C) 2005 TightVNC Team. All Rights Reserved. + * + * Developed by Dennis Syrovatsky. + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + * + * TightVNC distribution homepage on the Web: http://www.tightvnc.com/ + * + */ + +// -=- FolderManager.cxx + +#include <rfb_win32/FolderManager.h> + +using namespace rfb; +using namespace rfb::win32; + +FolderManager::FolderManager() +{ + +} + +FolderManager::~FolderManager() +{ + +} + +bool +FolderManager::createDir(char *pFullPath) +{ + if (CreateDirectory(pFullPath, NULL) == 0) return false; + + return true; +} + +bool +FolderManager::renameIt(char *pPath, char *pOldName, char *pNewName) +{ + char fullOldName[FT_FILENAME_SIZE]; + char fullNewName[FT_FILENAME_SIZE]; + + sprintf(fullOldName, "%s\\%s", pPath, pOldName); + sprintf(fullNewName, "%s\\%s", pPath, pNewName); + + return renameIt(fullOldName, fullNewName); +} + +bool +FolderManager::renameIt(char *pOldName, char *pNewName) +{ + if (MoveFile(pOldName, pNewName)) return true; + + return false; +} + +bool +FolderManager::deleteIt(char *pFullPath) +{ + FileInfo fileInfo; + + FILEINFO FIStruct; + if (!getInfo(pFullPath, &FIStruct)) return false; + + fileInfo.add(&FIStruct); + + return deleteIt(&fileInfo); +} + +bool +FolderManager::deleteIt(char *pPrefix, FileInfo *pFI) +{ + char buf[FT_FILENAME_SIZE]; + for (unsigned int i = 0; i < pFI->getNumEntries(); i++) { + sprintf(buf, "%s\\%s", pPrefix, pFI->getNameAt(i)); + pFI->setNameAt(i,buf); + } + return deleteIt(pFI); +} + +bool +FolderManager::deleteIt(FileInfo *pFI) +{ + unsigned int num = pFI->getNumEntries(); + unsigned int last = num - 1; + + while (num > 0) { + if (pFI->getFlagsAt(last) & FT_ATTR_DIR) { + if (RemoveDirectory(pFI->getNameAt(last)) == 0) { + if (GetLastError() == ERROR_DIR_NOT_EMPTY) { + if (!getFolderInfoWithPrefix(pFI->getNameAt(last), pFI)) { + pFI->free(); + return false; + } + } + } else { + pFI->deleteAt(last); + } + } else { + if (DeleteFile(pFI->getNameAt(last)) == 0) { + pFI->free(); + return false; + } else { + pFI->deleteAt(last); + } + } + num = pFI->getNumEntries(); + last = num - 1; + } + + return true; +} + +bool +FolderManager::getFolderInfoWithPrefix(char *pPrefix, FileInfo *pFileInfo) +{ + char prefix[FT_FILENAME_SIZE]; + strcpy(prefix, pPrefix); + + FileInfo tmpFileInfo; + if (!getDirInfo(prefix, &tmpFileInfo, 0)) { + tmpFileInfo.free(); + return false; + } else { + char buf[FT_FILENAME_SIZE]; + for (unsigned int i = 0; i < tmpFileInfo.getNumEntries(); i++) { + sprintf(buf, "%s\\%s", prefix, tmpFileInfo.getNameAt(i)); + pFileInfo->add(buf, tmpFileInfo.getSizeAt(i), tmpFileInfo.getDataAt(i), tmpFileInfo.getFlagsAt(i)); + } + } + tmpFileInfo.free(); + return true; +} + +bool +FolderManager::getDirInfo(char *pPath, FileInfo *pFileInfo, unsigned int dirOnly) +{ + if (strlen(pPath) == 0) return getDrivesInfo(pFileInfo); + + char path[FT_FILENAME_SIZE]; + sprintf(path, "%s\\*", pPath); + + WIN32_FIND_DATA FindFileData; + SetErrorMode(SEM_FAILCRITICALERRORS); + HANDLE handle = FindFirstFile(path, &FindFileData); + DWORD lastError = GetLastError(); + SetErrorMode(0); + + if (handle != INVALID_HANDLE_VALUE) { + do { + if (strcmp(FindFileData.cFileName, ".") != 0 && + strcmp(FindFileData.cFileName, "..") != 0) { + unsigned int lastWriteTime = getTime70(FindFileData.ftLastWriteTime); + if ((FindFileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) { + pFileInfo->add(FindFileData.cFileName, 0, lastWriteTime, FT_ATTR_DIR); + } else { + if (!dirOnly) + pFileInfo->add(FindFileData.cFileName, FindFileData.nFileSizeLow, lastWriteTime, FT_ATTR_FILE); + } + } + + } while (FindNextFile(handle, &FindFileData)); + } else { + return false; + } + FindClose(handle); + return true; +} + +bool +FolderManager::getDrivesInfo(FileInfo *pFileInfo) +{ + TCHAR szDrivesList[256]; + if (GetLogicalDriveStrings(255, szDrivesList) == 0) + return false; + + int i = 0; + while (szDrivesList[i] != '\0') { + char *drive = strdup(&szDrivesList[i]); + char *backslash = strrchr(drive, '\\'); + if (backslash != NULL) + *backslash = '\0'; + pFileInfo->add(drive, 0, 0, FT_ATTR_DIR); + free(drive); + i += strcspn(&szDrivesList[i], "\0") + 1; + } + return true; +} + +bool +FolderManager::getInfo(char *pFullPath, FILEINFO *pFIStruct) +{ + WIN32_FIND_DATA FindFileData; + SetErrorMode(SEM_FAILCRITICALERRORS); + HANDLE hFile = FindFirstFile(pFullPath, &FindFileData); + DWORD lastError = GetLastError(); + SetErrorMode(0); + if (hFile != INVALID_HANDLE_VALUE) { + FindClose(hFile); + strcpy(pFIStruct->name, pFullPath); + pFIStruct->info.data = getTime70(FindFileData.ftLastWriteTime); + if (FindFileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) { + pFIStruct->info.size = 0; + pFIStruct->info.flags = FT_ATTR_DIR; + return true; + } else { + pFIStruct->info.size = FindFileData.nFileSizeLow; + pFIStruct->info.flags = FT_ATTR_FILE; + return true; + } + } + return false; +} + +unsigned int +FolderManager::getTime70(FILETIME ftime) +{ + LARGE_INTEGER uli; + uli.LowPart = ftime.dwLowDateTime; + uli.HighPart = ftime.dwHighDateTime; + uli.QuadPart = (uli.QuadPart - 116444736000000000) / 10000000; + return uli.LowPart; +} + +void +FolderManager::getFiletime(unsigned int time70, FILETIME *pftime) +{ + LONGLONG ll = Int32x32To64(time70, 10000000) + 116444736000000000; + pftime->dwLowDateTime = (DWORD) ll; + pftime->dwHighDateTime = (DWORD) (ll >> 32); +} + +bool +FolderManager::getDirSize(char *pFullPath, DWORD64 *dirSize) +{ + char fullPath[FT_FILENAME_SIZE]; + FileInfo fi; + fi.add(pFullPath, 0, 0, FT_ATTR_DIR); + DWORD64 dirFileSize64 = 0; + do { + sprintf(fullPath, "%s\\*", fi.getNameAt(0)); + WIN32_FIND_DATA FindFileData; + SetErrorMode(SEM_FAILCRITICALERRORS); + HANDLE hFile = FindFirstFile(fullPath, &FindFileData); + SetErrorMode(0); + + if (hFile != INVALID_HANDLE_VALUE) { + do { + if (strcmp(FindFileData.cFileName, ".") != 0 && + strcmp(FindFileData.cFileName, "..") != 0) { + char buff[MAX_PATH]; + if ((FindFileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) { + sprintf(buff, "%s\\%s", fi.getNameAt(0), FindFileData.cFileName); + fi.add(buff, 0, 0, FT_ATTR_DIR); + } else { + dirFileSize64 += FindFileData.nFileSizeLow; + } + } + } while (FindNextFile(hFile, &FindFileData)); + FindClose(hFile); + } + fi.deleteAt(0); + } while (fi.getNumEntries() > 0); + + *dirSize = dirFileSize64; + return true; +}
\ No newline at end of file diff --git a/win/rfb_win32/FolderManager.h b/win/rfb_win32/FolderManager.h new file mode 100644 index 00000000..24923dd6 --- /dev/null +++ b/win/rfb_win32/FolderManager.h @@ -0,0 +1,66 @@ +/* Copyright (C) 2005 TightVNC Team. All Rights Reserved. + * + * Developed by Dennis Syrovatsky. + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + * + * TightVNC distribution homepage on the Web: http://www.tightvnc.com/ + * + */ + +// -=- FolderManager.h + +#ifndef __RFB_WIN32_FOLDERMANAGER_H__ +#define __RFB_WIN32_FOLDERMANAGER_H__ + +#include <windows.h> + +#include <rfb/FileInfo.h> +#include <rfb/DirManager.h> + +namespace rfb { + namespace win32{ + class FolderManager : public DirManager { + public: + FolderManager(); + ~FolderManager(); + + bool createDir(char *pFullPath); + + bool renameIt(char *pOldName, char *pNewName); + bool renameIt(char *pPath, char *pOldName, char *pNewName); + + bool deleteIt(char *pPrefix, FileInfo *pFI); + bool deleteIt(char *pFullPath); + bool deleteIt(FileInfo *pFI); + + bool getInfo(char *pFullPath, FILEINFO *pFIStruct); + + bool getDirInfo(char *pPath, FileInfo *pFileInfo, unsigned int dirOnly); + bool getDrivesInfo(FileInfo *pFI); + + unsigned int getTime70(FILETIME ftime); + void getFiletime(unsigned int time70, FILETIME *pftime); + + bool getDirSize(char *pFullPath, DWORD64 *dirSize); + + private: + bool getFolderInfoWithPrefix(char *pPrefix, FileInfo *pFileInfo); + }; + } +} + +#endif // __RFB_WIN32_FOLDERMANAGER_H__
\ No newline at end of file diff --git a/win/rfb_win32/Handle.h b/win/rfb_win32/Handle.h new file mode 100644 index 00000000..d3baa580 --- /dev/null +++ b/win/rfb_win32/Handle.h @@ -0,0 +1,43 @@ +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +// Wrapper for Win32 HANDLEs that can/must be CloseHandle()d. + +#ifndef __RFB_WIN32_HANDLE_H__ +#define __RFB_WIN32_HANDLE_H__ + +#include <windows.h> + +namespace rfb { + namespace win32 { + + + class Handle { + public: + Handle(HANDLE h_=0) : h(h_) {} + ~Handle() { + if (h) CloseHandle(h); + } + operator HANDLE() {return h;} + HANDLE h; + }; + + }; +}; + +#endif diff --git a/win/rfb_win32/IconInfo.h b/win/rfb_win32/IconInfo.h new file mode 100644 index 00000000..cb33a42d --- /dev/null +++ b/win/rfb_win32/IconInfo.h @@ -0,0 +1,44 @@ +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +#ifndef __RFB_WIN32_ICONINFO_H__ +#define __RFB_WIN32_ICONINFO_H__ + +#include <windows.h> +#include <rdr/Exception.h> + +namespace rfb { + namespace win32 { + + struct IconInfo : public ICONINFO { + IconInfo(HICON icon) { + if (!GetIconInfo(icon, this)) + throw rdr::SystemException("GetIconInfo() failed", GetLastError()); + } + ~IconInfo() { + if (hbmColor) + DeleteObject(hbmColor); + if (hbmMask) + DeleteObject(hbmMask); + } + }; + + }; +}; + +#endif diff --git a/win/rfb_win32/IntervalTimer.h b/win/rfb_win32/IntervalTimer.h new file mode 100644 index 00000000..ddfae493 --- /dev/null +++ b/win/rfb_win32/IntervalTimer.h @@ -0,0 +1,70 @@ +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +// -=- IntervalTimer.h +// +// Simple wrapper for standard Win32 timers + +#ifndef __RFB_WIN32_INTERVAL_TIMER_H__ +#define __RFB_WIN32_INTERVAL_TIMER_H__ + +namespace rfb { + + namespace win32 { + + struct IntervalTimer { + IntervalTimer(HWND hwnd_, int id_) + : active(false), hwnd(hwnd_), id(id_) { + } + IntervalTimer() : active(false), hwnd(0), id(0) { + } + ~IntervalTimer() { + stop(); + } + + void start(int interval_) { + if (!active || interval_ != interval) { + interval = interval_; + if (!SetTimer(hwnd, id, interval, 0)) + throw rdr::SystemException("SetTimer", GetLastError()); + active = true; + } + } + void stop() { + if (active) + KillTimer(hwnd, id); + active = false; + } + + void setHWND(HWND hwnd_) {hwnd=hwnd_;} + void setId(int id_) {id = id_;} + int getId() const {return id;} + bool isActive() const {return active;} + + private: + HWND hwnd; + int id; + bool active; + int interval; + }; + + }; // win32 + +}; // rfb + +#endif // __RFB_WIN32_INTERVAL_TIMER_H__ diff --git a/win/rfb_win32/LaunchProcess.cxx b/win/rfb_win32/LaunchProcess.cxx new file mode 100644 index 00000000..56a712e6 --- /dev/null +++ b/win/rfb_win32/LaunchProcess.cxx @@ -0,0 +1,103 @@ +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +// -=- LaunchProcess.cxx + +#include <rfb_win32/LaunchProcess.h> +#include <rfb_win32/ModuleFileName.h> +#include <rfb_win32/Win32Util.h> +#include <rdr/Exception.h> +#include <stdio.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, bool createConsole) { + if (procInfo.hProcess && (WaitForSingleObject(procInfo.hProcess, 0) != WAIT_OBJECT_0)) + return; + await(); + returnCode = STILL_ACTIVE; + + // - Create storage for the process startup information + STARTUPINFO sinfo; + memset(&sinfo, 0, sizeof(sinfo)); + sinfo.cb = sizeof(sinfo); + + // - Concoct a suitable command-line + TCharArray exePath; + if (!tstrContains(exeName.buf, _T('\\'))) { + ModuleFileName filename; + TCharArray path; splitPath(filename.buf, &path.buf, 0); + exePath.buf = new TCHAR[_tcslen(path.buf) + _tcslen(exeName.buf) + 2]; + _stprintf(exePath.buf, _T("%s\\%s"), path.buf, exeName.buf); + } else { + exePath.buf = tstrDup(exeName.buf); + } + + // - Start the process + // Note: We specify the exe's precise path in the ApplicationName parameter, + // AND include the name as the first part of the CommandLine parameter, + // because CreateProcess doesn't make ApplicationName argv[0] in C programs. + TCharArray cmdLine(_tcslen(exeName.buf) + 3 + _tcslen(params.buf) + 1); + _stprintf(cmdLine.buf, _T("\"%s\" %s"), exeName.buf, params.buf); + DWORD flags = createConsole ? CREATE_NEW_CONSOLE : CREATE_NO_WINDOW; + BOOL success; + if (userToken != INVALID_HANDLE_VALUE) + success = CreateProcessAsUser(userToken, exePath.buf, cmdLine.buf, 0, 0, FALSE, flags, 0, 0, &sinfo, &procInfo); + else + success = CreateProcess(exePath.buf, cmdLine.buf, 0, 0, FALSE, flags, 0, 0, &sinfo, &procInfo); + if (!success) + throw rdr::SystemException("unable to launch process", GetLastError()); + + // Wait for it to finish initialising + WaitForInputIdle(procInfo.hProcess, 15000); +} + +void LaunchProcess::detach() +{ + if (!procInfo.hProcess) + return; + CloseHandle(procInfo.hProcess); + CloseHandle(procInfo.hThread); + memset(&procInfo, 0, sizeof(procInfo)); +} + +bool LaunchProcess::await(DWORD timeoutMs) { + if (!procInfo.hProcess) + return true; + DWORD result = WaitForSingleObject(procInfo.hProcess, timeoutMs); + if (result == WAIT_OBJECT_0) { + GetExitCodeProcess(procInfo.hProcess, &returnCode); + detach(); + return true; + } else if (result == WAIT_FAILED) { + throw rdr::SystemException("await() failed", GetLastError()); + } + return false; +} diff --git a/win/rfb_win32/LaunchProcess.h b/win/rfb_win32/LaunchProcess.h new file mode 100644 index 00000000..38521dcd --- /dev/null +++ b/win/rfb_win32/LaunchProcess.h @@ -0,0 +1,71 @@ +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +// -=- LaunchProcess.h + +// Helper class to launch a names process from the same directory as +// the current process executable resides in. + +#ifndef __RFB_WIN32_LAUNCHPROCESS_H__ +#define __RFB_WIN32_LAUNCHPROCESS_H__ + +#include <windows.h> +#include <rfb_win32/TCharArray.h> + +namespace rfb { + + namespace win32 { + + class LaunchProcess { + public: + LaunchProcess(const TCHAR* exeName_, const TCHAR* params); + ~LaunchProcess(); + + // start() starts the specified process with the supplied + // command-line. + // If userToken is INVALID_HANDLE_VALUE then starts the process + // as the current user, otherwise as the specified user. + // If createConsole is true then CREATE_CONSOLE_WINDOW is passed + // as an extra flag to the process creation call. + void start(HANDLE userToken, bool createConsole=false); + + // Detatch from the child process. After detatching from a child + // process, no other methods should be called on the object + // that started it + void detach(); + + // Wait for the process to quit, up to the specified timeout, and + // close the handles to it once it has quit. + // If the process quits within the timeout then true is returned + // and returnCode is set. If it has not quit then false is returned. + // If an error occurs then an exception will be thrown. + bool await(DWORD timeoutMs=INFINITE); + + PROCESS_INFORMATION procInfo; + DWORD returnCode; + protected: + TCharArray exeName; + TCharArray params; + }; + + + }; + +}; + +#endif diff --git a/win/rfb_win32/ListViewControl.cxx b/win/rfb_win32/ListViewControl.cxx new file mode 100644 index 00000000..12e04003 --- /dev/null +++ b/win/rfb_win32/ListViewControl.cxx @@ -0,0 +1,103 @@ +// ListViewControl.cxx: implementation of the ListViewControl class. +// +////////////////////////////////////////////////////////////////////// +#include <tchar.h> +#include "ListViewControl.h" +#include "commctrl.h" +////////////////////////////////////////////////////////////////////// +// Construction/Destruction +////////////////////////////////////////////////////////////////////// +using namespace rfb; +using namespace rfb::win32; + +ListViewControl::ListViewControl() +{ +} + +bool ListViewControl::IsSelectedLVItem(DWORD idListView, + HWND hDlg, int numberItem) +{ + return (ListView_GetItemState(GetDlgItem(hDlg, idListView), + numberItem, LVIS_SELECTED) == LVIS_SELECTED); +} + +void ListViewControl::SelectLVItem(DWORD idListView, HWND hDlg, int numberItem) +{ + ListView_SetItemState(GetDlgItem(hDlg, idListView), + numberItem, LVIS_SELECTED, LVIS_SELECTED); +} + +BOOL ListViewControl::InitLVColumns(DWORD idListView, HWND hDlg, int width, int columns, + TCHAR *title[], DWORD mask, DWORD LVStyle, DWORD format) +{ + ListView_SetExtendedListViewStyle(GetDlgItem(hDlg, idListView), LVStyle); + TCHAR szText[256]; + LVCOLUMN lvc; + int iCol; + + lvc.mask = mask; + + for (iCol = 0; iCol < columns; iCol++) { + lvc.iSubItem = iCol; + lvc.pszText = szText; + lvc.cx = width; + lvc.fmt = format; + + _tcscpy(szText, title[iCol]); + if (ListView_InsertColumn(GetDlgItem(hDlg, idListView), iCol, &lvc) == -1) + return FALSE; + } + return TRUE; +} + +BOOL ListViewControl::InsertLVItem(DWORD idListView, HWND hDlg, int number, TCHAR * texts[], + int columns) +{ + int i; + LVITEM lvI; + lvI.mask = LVIF_TEXT| LVIF_STATE; + lvI.state = 0; + lvI.stateMask = 0; + lvI.iItem = number; + lvI.iSubItem = 0; + lvI.pszText = texts[0]; + + if(ListView_InsertItem(GetDlgItem(hDlg, idListView), &lvI) == -1) + return NULL; + + for (i =1; i < columns; i++) { + SetLVItemText( + idListView, hDlg, + number, i, texts[i]); + } + return TRUE; +} + +void ListViewControl::SetLVItemText(DWORD idListView, HWND hDlg, int numberItem, + int namberColumn, TCHAR * text) +{ + ListView_SetItemText( + GetDlgItem(hDlg, idListView), + numberItem, namberColumn, text); +} + +void ListViewControl::GetLVItemText(DWORD idListView, HWND hDlg, int numberItem, + int namberColumn, TCHAR * text) +{ + ListView_GetItemText(GetDlgItem(hDlg, idListView), numberItem, + namberColumn, text, 256); +} + +void ListViewControl::DeleteLVItem(DWORD idListView, HWND hDlg, int number) +{ + ListView_DeleteItem(GetDlgItem(hDlg, idListView), number); +} + +void ListViewControl::DeleteAllLVItem(DWORD idListView, HWND hDlg) +{ + ListView_DeleteAllItems(GetDlgItem(hDlg, idListView)); +} + +ListViewControl::~ListViewControl() +{ +} diff --git a/win/rfb_win32/ListViewControl.h b/win/rfb_win32/ListViewControl.h new file mode 100644 index 00000000..8a163738 --- /dev/null +++ b/win/rfb_win32/ListViewControl.h @@ -0,0 +1,35 @@ +// ListViewControl.h: interface for the ListViewControl class. +// +////////////////////////////////////////////////////////////////////// + +#ifndef AFX_LISTVIEWCONTROL_H__ +#define AFX_LISTVIEWCONTROL_H__ + +#include <windows.h> +#include "commctrl.h" + +namespace rfb { + + namespace win32 { + class ListViewControl + { + public: + ListViewControl(); + bool IsSelectedLVItem(DWORD idListView, HWND hDlg, int numberItem); + void SelectLVItem(DWORD idListView, HWND hDlg, int numberItem); + BOOL InitLVColumns(DWORD idListView, HWND hDlg, int width, int columns, + TCHAR * title[], DWORD mask, DWORD style, DWORD format); + BOOL InsertLVItem(DWORD idListView, HWND hDlg, int number, TCHAR * texts[], + int columns); + void SetLVItemText(DWORD idListView, HWND hDlg, int numberItem, + int namberColumn, TCHAR * text); + void GetLVItemText(DWORD idListView, HWND hDlg, int numberItem, + int namberColumn, TCHAR * text); + void DeleteLVItem(DWORD idListView, HWND hDlg, int number); + void DeleteAllLVItem(DWORD idListView, HWND hDlg); + virtual ~ListViewControl(); + }; + }; +}; + +#endif;
\ No newline at end of file diff --git a/win/rfb_win32/LocalMem.h b/win/rfb_win32/LocalMem.h new file mode 100644 index 00000000..a99d3241 --- /dev/null +++ b/win/rfb_win32/LocalMem.h @@ -0,0 +1,45 @@ +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +#ifndef __RFB_WIN32_LOCALMEM_H__ +#define __RFB_WIN32_LOCALMEM_H__ + +#include <windows.h> +#include <rdr/Exception.h> + +namespace rfb { + namespace win32 { + + // Allocate and/or manage LocalAlloc memory. + struct LocalMem { + LocalMem(int size) : ptr(LocalAlloc(LMEM_FIXED, size)) { + if (!ptr) throw rdr::SystemException("LocalAlloc", GetLastError()); + } + LocalMem(void* p) : ptr(p) {} + ~LocalMem() {LocalFree(ptr);} + operator void*() {return ptr;} + void* takePtr() { + void* t = ptr; ptr = 0; return t; + } + void* ptr; + }; + + }; +}; + +#endif diff --git a/win/rfb_win32/LogicalPalette.h b/win/rfb_win32/LogicalPalette.h new file mode 100644 index 00000000..204f1081 --- /dev/null +++ b/win/rfb_win32/LogicalPalette.h @@ -0,0 +1,90 @@ +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +#ifndef __RFB_WIN32_LOGPALETTE_H__ +#define __RFB_WIN32_LOGPALETTE_H__ + +#include <windows.h> +#include <rdr/Exception.h> + +namespace rfb { + namespace win32 { + + class LogicalPalette { + public: + LogicalPalette() { + BYTE buf[sizeof(LOGPALETTE)+256*sizeof(PALETTEENTRY)]; + LOGPALETTE* logpal = (LOGPALETTE*)buf; + logpal->palVersion = 0x300; + logpal->palNumEntries = 256; + for (int i=0; i<256;i++) { + logpal->palPalEntry[i].peRed = 0; + logpal->palPalEntry[i].peGreen = 0; + logpal->palPalEntry[i].peBlue = 0; + logpal->palPalEntry[i].peFlags = 0; + } + palette = CreatePalette(logpal); + if (!palette) + throw rdr::SystemException("failed to CreatePalette", GetLastError()); + } + ~LogicalPalette() { + if (palette && !DeleteObject(palette)) + throw rdr::SystemException("del palette failed", GetLastError()); + } + void setEntries(int start, int count, const Colour* cols) { + if (numEntries < count) { + ResizePalette(palette, start+count); + numEntries = start+count; + } + PALETTEENTRY* logpal = new PALETTEENTRY[count]; + for (int i=0; i<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; + } + HPALETTE getHandle() {return palette;} + protected: + HPALETTE palette; + int numEntries; + }; + + class PaletteSelector { + public: + PaletteSelector(HDC dc, HPALETTE pal) : device(dc), redrawRequired(false) { + oldPal = SelectPalette(dc, pal, FALSE); + redrawRequired = RealizePalette(dc) > 0; + } + ~PaletteSelector() { + if (oldPal) SelectPalette(device, oldPal, TRUE); + } + bool isRedrawRequired() {return redrawRequired;} + protected: + HPALETTE oldPal; + HDC device; + bool redrawRequired; + }; + + }; +}; + +#endif diff --git a/win/rfb_win32/LowLevelKeyEvents.cxx b/win/rfb_win32/LowLevelKeyEvents.cxx new file mode 100644 index 00000000..322d1f40 --- /dev/null +++ b/win/rfb_win32/LowLevelKeyEvents.cxx @@ -0,0 +1,96 @@ +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +#include <windows.h> +#include <rfb_win32/LowLevelKeyEvents.h> +#include <rfb/Threading.h> +#include <rfb/LogWriter.h> +#include <list> + +using namespace rfb; +using namespace win32; + +static LogWriter vlog("LowLevelKeyEvents"); + + +HHOOK hook = 0; +std::list<HWND> windows; +Mutex windowLock; + + +static bool filterKeyEvent(int vkCode) { + switch (vkCode) { + case VK_LWIN: + case VK_RWIN: + case VK_SNAPSHOT: + return true; + case VK_TAB: + if (GetAsyncKeyState(VK_MENU) & 0x8000) + return true; + case VK_ESCAPE: + if (GetAsyncKeyState(VK_MENU) & 0x8000) + return true; + if (GetAsyncKeyState(VK_CONTROL) & 0x8000) + return true; + } + return false; +} + +LRESULT CALLBACK LowLevelKeyEventProc(int nCode, + WPARAM wParam, + LPARAM lParam) { + if (nCode >= 0) { + Lock l(windowLock); + HWND foreground = GetForegroundWindow(); + std::list<HWND>::iterator i; + for (i=windows.begin(); i!=windows.end(); i++) { + if (*i == foreground) { + UINT msgType = wParam; + KBDLLHOOKSTRUCT* msgInfo = (KBDLLHOOKSTRUCT*)lParam; + if (filterKeyEvent(msgInfo->vkCode)) { + vlog.debug("filtered event %lx(%lu) %lu", msgInfo->vkCode, msgInfo->vkCode, wParam); + PostMessage(*i, wParam, msgInfo->vkCode, (msgInfo->scanCode & 0xff) << 16); + return 1; + } + } + } + } + return CallNextHookEx(hook, nCode, wParam, lParam); +} + + +bool rfb::win32::enableLowLevelKeyEvents(HWND hwnd) { +// *** return false; // *** THIS CODE IS EXPERIMENTAL, SO DISABLED BY DEFAULT! + Lock l(windowLock); + if (windows.empty() && !hook) + hook = SetWindowsHookEx(WH_KEYBOARD_LL, &LowLevelKeyEventProc, GetModuleHandle(0), 0); + if (hook) + windows.push_back(hwnd); + vlog.debug("enable %p -> %s", hwnd, hook ? "success" : "failure"); + return hook != 0; +} + +void rfb::win32::disableLowLevelKeyEvents(HWND hwnd) { + vlog.debug("disable %p", hwnd); + Lock l(windowLock); + windows.remove(hwnd); + if (windows.empty() && hook) { + UnhookWindowsHookEx(hook); + hook = 0; + } +} diff --git a/win/rfb_win32/LowLevelKeyEvents.h b/win/rfb_win32/LowLevelKeyEvents.h new file mode 100644 index 00000000..40d2ecfa --- /dev/null +++ b/win/rfb_win32/LowLevelKeyEvents.h @@ -0,0 +1,49 @@ +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +// -=- LowLevelKeyEvents.h +// +// This interface allows keyboard events destined for a particular window +// to be intercepted early in the keyboard message queue and posted directly +// to the window. This is used to avoid having the operating system process +// keys such as VK_LWIN, VK_RWIN, etc. +// + +#ifndef __RFB_WIN32_LOW_LEVEL_KEY_EVENTS_H__ +#define __RFB_WIN32_LOW_LEVEL_KEY_EVENTS_H__ + +namespace rfb { + + namespace win32 { + + // enableLowLevelKeyEvents + // Specifies that keyboard events destined for the specified window should + // be posted directly to the window, rather than being passed via the normal + // Windows keyboard message queue. + bool enableLowLevelKeyEvents(HWND hwnd); + + // disableLowLevelKeyEvents + // Causes the specified window to revert to the normal Windows keyboard + // event processing mechanism. + void disableLowLevelKeyEvents(HWND hwnd); + + }; + +}; + +#endif // __RFB_WIN32_LOW_LEVEL_KEY_EVENTS_H__ diff --git a/win/rfb_win32/ModuleFileName.h b/win/rfb_win32/ModuleFileName.h new file mode 100644 index 00000000..2264e89d --- /dev/null +++ b/win/rfb_win32/ModuleFileName.h @@ -0,0 +1,40 @@ +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +#ifndef __RFB_WIN32_MODULE_FILENAME_H__ +#define __RFB_WIN32_MODULE_FILENAME_H__ + +#include <windows.h> +#include <rfb_win32/TCharArray.h> + +namespace rfb { + namespace win32 { + + struct ModuleFileName : public TCharArray { + ModuleFileName(HMODULE module=0) : TCharArray(MAX_PATH) { + if (!module) + module = GetModuleHandle(0); + if (!GetModuleFileName(module, buf, MAX_PATH)) + buf[0] = 0; + } + }; + + }; +}; + +#endif diff --git a/win/rfb_win32/MonitorInfo.cxx b/win/rfb_win32/MonitorInfo.cxx new file mode 100644 index 00000000..03772e97 --- /dev/null +++ b/win/rfb_win32/MonitorInfo.cxx @@ -0,0 +1,205 @@ +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +#include <rfb_win32/DynamicFn.h> +#include <rfb_win32/MonitorInfo.h> +#include <rfb_win32/Win32Util.h> +#include <rdr/Exception.h> +#include <rfb/LogWriter.h> + +using namespace rfb; +using namespace win32; + +static LogWriter vlog("MonitorInfo"); + + +// If we are building in multi-monitor support (i.e. the headers support it) +// then do dynamic imports of the required system calls, and provide any +// other code that wouldn't otherwise compile. +#ifdef RFB_HAVE_MONITORINFO +#include <tchar.h> +typedef HMONITOR (WINAPI *_MonitorFromWindow_proto)(HWND,DWORD); +static rfb::win32::DynamicFn<_MonitorFromWindow_proto> _MonitorFromWindow(_T("user32.dll"), "MonitorFromWindow"); +typedef HMONITOR (WINAPI *_MonitorFromRect_proto)(LPCRECT,DWORD); +static rfb::win32::DynamicFn<_MonitorFromRect_proto> _MonitorFromRect(_T("user32.dll"), "MonitorFromRect"); +typedef BOOL (WINAPI *_GetMonitorInfo_proto)(HMONITOR,LPMONITORINFO); +static rfb::win32::DynamicFn<_GetMonitorInfo_proto> _GetMonitorInfo(_T("user32.dll"), "GetMonitorInfoA"); +typedef BOOL (WINAPI *_EnumDisplayMonitors_proto)(HDC, LPCRECT, MONITORENUMPROC, LPARAM); +static rfb::win32::DynamicFn<_EnumDisplayMonitors_proto> _EnumDisplayMonitors(_T("user32.dll"), "EnumDisplayMonitors"); +static void fillMonitorInfo(HMONITOR monitor, MonitorInfo* mi) { + vlog.debug("monitor=%lx", monitor); + if (!_GetMonitorInfo.isValid()) + throw rdr::Exception("no GetMonitorInfo"); + memset(mi, 0, sizeof(MONITORINFOEXA)); + mi->cbSize = sizeof(MONITORINFOEXA); + if (!(*_GetMonitorInfo)(monitor, mi)) + throw rdr::SystemException("failed to GetMonitorInfo", GetLastError()); + vlog.debug("monitor is %d,%d-%d,%d", mi->rcMonitor.left, mi->rcMonitor.top, mi->rcMonitor.right, mi->rcMonitor.bottom); + vlog.debug("work area is %d,%d-%d,%d", mi->rcWork.left, mi->rcWork.top, mi->rcWork.right, mi->rcWork.bottom); + vlog.debug("device is \"%s\"", mi->szDevice); +} +#else +#pragma message(" NOTE: Not building Multi-Monitor support.") +#endif + + +MonitorInfo::MonitorInfo(HWND window) { + cbSize = sizeof(MonitorInfo); + szDevice[0] = 0; + +#ifdef RFB_HAVE_MONITORINFO + try { + if (_MonitorFromWindow.isValid()) { + HMONITOR monitor = (*_MonitorFromWindow)(window, MONITOR_DEFAULTTONEAREST); + if (!monitor) + throw rdr::SystemException("failed to get monitor", GetLastError()); + fillMonitorInfo(monitor, this); + return; + } + } catch (rdr::Exception& e) { + vlog.error(e.str()); + } +#endif + + // Legacy fallbacks - just return the desktop settings + vlog.debug("using legacy fall-backs"); + HWND desktop = GetDesktopWindow(); + GetWindowRect(desktop, &rcMonitor); + SystemParametersInfo(SPI_GETWORKAREA, 0, &rcWork, 0); + dwFlags = 0; +} + +MonitorInfo::MonitorInfo(const RECT& r) { + cbSize = sizeof(MonitorInfo); + szDevice[0] = 0; + +#ifdef RFB_HAVE_MONITORINFO + try { + if (_MonitorFromRect.isValid()) { + HMONITOR monitor = (*_MonitorFromRect)(&r, MONITOR_DEFAULTTONEAREST); + if (!monitor) + throw rdr::SystemException("failed to get monitor", GetLastError()); + fillMonitorInfo(monitor, this); + return; + } + } catch (rdr::Exception& e) { + vlog.error(e.str()); + } +#endif + + // Legacy fallbacks - just return the desktop settings + vlog.debug("using legacy fall-backs"); + HWND desktop = GetDesktopWindow(); + GetWindowRect(desktop, &rcMonitor); + SystemParametersInfo(SPI_GETWORKAREA, 0, &rcWork, 0); + dwFlags = 0; +} + + +#ifdef RFB_HAVE_MONITORINFO + +struct monitorByNameData { + MonitorInfo* info; + const char* monitorName; +}; + +static BOOL CALLBACK monitorByNameEnumProc(HMONITOR monitor, + HDC dc, + LPRECT pos, + LPARAM d) { + monitorByNameData* data = (monitorByNameData*)d; + memset(data->info, 0, sizeof(MONITORINFOEXA)); + data->info->cbSize = sizeof(MONITORINFOEXA); + if ((*_GetMonitorInfo)(monitor, data->info)) { + if (stricmp(data->monitorName, data->info->szDevice) == 0) + return FALSE; + } + + return TRUE; +} + +#endif + +MonitorInfo::MonitorInfo(const char* devName) { +#ifdef RFB_HAVE_MONITORINFO + if (!_EnumDisplayMonitors.isValid()) { + vlog.debug("EnumDisplayMonitors not found"); + } else { + monitorByNameData data; + data.info = this; + data.monitorName = devName; + + (*_EnumDisplayMonitors)(0, 0, &monitorByNameEnumProc, (LPARAM)&data); + if (stricmp(data.monitorName, szDevice) == 0) + return; + } +#endif + // If multi-monitor is not built, or not supported by the OS, + // or if the named monitor is not found, revert to the primary monitor. + vlog.debug("reverting to primary monitor"); + cbSize = sizeof(MonitorInfo); + szDevice[0] = 0; + + HWND desktop = GetDesktopWindow(); + GetWindowRect(desktop, &rcMonitor); + SystemParametersInfo(SPI_GETWORKAREA, 0, &rcWork, 0); + dwFlags = 0; +} + +void MonitorInfo::moveTo(HWND handle) { + vlog.debug("moveTo monitor=%s", szDevice); + +#ifdef RFB_HAVE_MONITORINFO + MonitorInfo mi(handle); + if (strcmp(szDevice, mi.szDevice) != 0) { + centerWindow(handle, rcWork); + clipTo(handle); + } +#endif +} + +void MonitorInfo::clipTo(RECT* r) { + vlog.debug("clipTo monitor=%s", szDevice); + + if (r->top < rcWork.top) { + r->bottom += rcWork.top - r->top; r->top = rcWork.top; + } + if (r->left < rcWork.left) { + r->right += rcWork.left - r->left; r->left = rcWork.left; + } + if (r->bottom > rcWork.bottom) { + r->top += rcWork.bottom - r->bottom; r->bottom = rcWork.bottom; + } + if (r->right > rcWork.right) { + r->left += rcWork.right - r->right; r->right = rcWork.right; + } + r->left = max(r->left, rcWork.left); + r->right = min(r->right, rcWork.right); + r->top = max(r->top, rcWork.top); + r->bottom = min(r->bottom, rcWork.bottom); +} + +void MonitorInfo::clipTo(HWND handle) { + RECT r; + GetWindowRect(handle, &r); + clipTo(&r); + SetWindowPos(handle, 0, r.left, r.top, r.right-r.left, r.bottom-r.top, + SWP_NOZORDER | SWP_NOACTIVATE | SWP_NOOWNERZORDER); +} + + diff --git a/win/rfb_win32/MonitorInfo.h b/win/rfb_win32/MonitorInfo.h new file mode 100644 index 00000000..acf27755 --- /dev/null +++ b/win/rfb_win32/MonitorInfo.h @@ -0,0 +1,72 @@ +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +// Helper class used to obtain information about a particular monitor. +// This class wraps the Windows MONITORINFOEX ASCII structure, providing +// methods that can safely be called on both multi-monitor aware systems +// and older "legacy" systems. + + +#ifndef __RFB_WIN32_MONITORINFO_H__ +#define __RFB_WIN32_MONITORINFO_H__ + +#include <windows.h> +#ifdef MONITOR_DEFAULTTONULL +#define RFB_HAVE_MONITORINFO +#endif + +namespace rfb { + namespace win32 { + + // Structure containing info on the monitor nearest the window. + // Copes with multi-monitor OSes and older ones. +#ifdef RFB_HAVE_MONITORINFO + struct MonitorInfo : MONITORINFOEXA { +#else + struct MonitorInfo { + DWORD cbSize; + RECT rcMonitor; + RECT rcWork; + DWORD dwFlags; + char szDevice[1]; // Always null... +#endif + + // Constructor: Obtains monitor info for the monitor that has the + // greatest overlap with the supplied window or rectangle. + MonitorInfo(HWND hwnd); + MonitorInfo(const RECT& r); + + // Constructor: Obtains monitor info for the name monitor. Monitor + // names should be those obtained from the MonitorInfo + // szDevice field, and usually look like "\\.\DISPLAY<n>" + MonitorInfo(const char* devName); + + // Move the specified window to reside on the monitor. + void moveTo(HWND handle); + + // Clip the specified rectangle or window to the monitor's working area. + // The rectangle/window is moved so that as much as possible resides + // on the working area of the monitor, and is then intersected with it. + void clipTo(HWND handle); + void clipTo(RECT* r); + }; + + }; +}; + +#endif diff --git a/win/rfb_win32/MsgBox.h b/win/rfb_win32/MsgBox.h new file mode 100644 index 00000000..59571395 --- /dev/null +++ b/win/rfb_win32/MsgBox.h @@ -0,0 +1,63 @@ +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +#ifndef __RFB_WIN32_MSGBOX_H__ +#define __RFB_WIN32_MSGBOX_H__ + +#include <windows.h> +#include <rfb_win32/TCharArray.h> + +namespace rfb { + namespace win32 { + + // Define rfb::win32::AppName somewhere in the application. + // The MsgBox function will use the specified application name + // as the prefix for the message box title. + // Message box titles are based on the (standard Win32) flags + // passed to the MsgBox helper function. + + extern TStr AppName; + + // Wrapper around Win32 MessageBox() + static int MsgBox(HWND parent, const TCHAR* msg, UINT flags) { + const TCHAR* msgType = 0; + UINT tflags = flags & 0x70; + if (tflags == MB_ICONHAND) + msgType = _T("Error"); + else if (tflags == MB_ICONQUESTION) + msgType = _T("Question"); + else if (tflags == MB_ICONEXCLAMATION) + msgType = _T("Warning"); + else if (tflags == MB_ICONASTERISK) + msgType = _T("Information"); + flags |= MB_TOPMOST | MB_SETFOREGROUND; + int len = _tcslen(AppName.buf) + 1; + if (msgType) len += _tcslen(msgType) + 3; + TCharArray title = new TCHAR[len]; + _tcscpy(title.buf, AppName.buf); + if (msgType) { + _tcscat(title.buf, _T(" : ")); + _tcscat(title.buf, msgType); + } + return MessageBox(parent, msg, title.buf, flags); + } + + }; +}; + +#endif diff --git a/win/rfb_win32/MsgWindow.cxx b/win/rfb_win32/MsgWindow.cxx new file mode 100644 index 00000000..95bd5237 --- /dev/null +++ b/win/rfb_win32/MsgWindow.cxx @@ -0,0 +1,116 @@ +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +// -=- MsgWindow.cxx + +#include <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); +} diff --git a/win/rfb_win32/MsgWindow.h b/win/rfb_win32/MsgWindow.h new file mode 100644 index 00000000..92b6cf20 --- /dev/null +++ b/win/rfb_win32/MsgWindow.h @@ -0,0 +1,53 @@ +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +// -=- MsgWindow.h + +// Base-class for any hidden message-handling windows used in the rfb::win32 +// implementation. + +#ifndef __RFB_WIN32_MSG_WINDOW_H__ +#define __RFB_WIN32_MSG_WINDOW_H__ + +#include <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/win/rfb_win32/OSVersion.cxx b/win/rfb_win32/OSVersion.cxx new file mode 100644 index 00000000..3d74c956 --- /dev/null +++ b/win/rfb_win32/OSVersion.cxx @@ -0,0 +1,47 @@ +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +// -=- OSVersion.cxx + +#include <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/win/rfb_win32/OSVersion.h b/win/rfb_win32/OSVersion.h new file mode 100644 index 00000000..18ec003e --- /dev/null +++ b/win/rfb_win32/OSVersion.h @@ -0,0 +1,53 @@ +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +// -=- OSVersion.h + +// Operating system version info. +// GetVersionInfo is called once at process initialisation, and any +// extra flags (such as isWinNT) are calculated and saved at that +// point. It is assumed that the OS Version seldom changes during a +// program's execution... + +#ifndef __RFB_WIN32_OS_VERSION_H__ +#define __RFB_WIN32_OS_VERSION_H__ + +#include <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/win/rfb_win32/ProgressControl.cxx b/win/rfb_win32/ProgressControl.cxx new file mode 100644 index 00000000..85bd15f3 --- /dev/null +++ b/win/rfb_win32/ProgressControl.cxx @@ -0,0 +1,97 @@ +/* Copyright (C) 2005 TightVNC Team. All Rights Reserved. + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + * + * TightVNC distribution homepage on the Web: http://www.tightvnc.com/ + * + */ + +// -=- ProgressControl.cxx + +#include <rfb_win32/ProgressControl.h> + +using namespace rfb; +using namespace rfb::win32; + +#define MAX_RANGE 0xFFFF + +ProgressControl::ProgressControl(HWND hwndProgress) +{ + m_hwndProgress = hwndProgress; + + m_dw64MaxValue = 0; + m_dw64CurrentValue = 0; +} + +ProgressControl::~ProgressControl() +{ +} + +bool +ProgressControl::init(DWORD64 maxValue, DWORD64 position) +{ + if (m_dw64CurrentValue > m_dw64MaxValue) return false; + + m_dw64CurrentValue = position; + m_dw64MaxValue = maxValue; + + if (!SendMessage(m_hwndProgress, PBM_SETRANGE, (WPARAM) 0, MAKELPARAM(0, MAX_RANGE))) + return false; + + return true; +} + +bool +ProgressControl::clear() +{ + m_dw64CurrentValue = 0; + return show(); +} + +bool +ProgressControl::increase(DWORD64 value) +{ + if ((m_dw64MaxValue - m_dw64CurrentValue) > value) { + m_dw64CurrentValue += value; + } else { + m_dw64CurrentValue = m_dw64MaxValue; + } + return show(); +} + +bool +ProgressControl::show() +{ + DWORD curPos; + if (m_dw64MaxValue != 0) { + curPos = (DWORD) ((m_dw64CurrentValue * MAX_RANGE) / m_dw64MaxValue); + } else { + curPos = 0; + } + + if (!SendMessage(m_hwndProgress, PBM_SETPOS, (WPARAM) curPos, (LPARAM) 0)) + return false; + + return true; +} + +int +ProgressControl::getCurrentPercent() +{ + if (m_dw64MaxValue == 0) return 0; + + return ((int) ((m_dw64CurrentValue * 100) / m_dw64MaxValue)); +}
\ No newline at end of file diff --git a/win/rfb_win32/ProgressControl.h b/win/rfb_win32/ProgressControl.h new file mode 100644 index 00000000..ceeb153f --- /dev/null +++ b/win/rfb_win32/ProgressControl.h @@ -0,0 +1,56 @@ +/* Copyright (C) 2005 TightVNC Team. All Rights Reserved. + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + * + * TightVNC distribution homepage on the Web: http://www.tightvnc.com/ + * + */ + +// -=- ProgressControl.h + +#ifndef __RFB_WIN32_PROGRESSCONTROL_H__ +#define __RFB_WIN32_PROGRESSCONTROL_H__ + +#include <windows.h> +#include <commctrl.h> + +namespace rfb { + namespace win32 { + class ProgressControl + { + public: + ProgressControl(HWND hwndProgress); + ~ProgressControl(); + + bool init(DWORD64 maxValue, DWORD64 position); + + bool increase(DWORD64 value); + bool clear(); + + int getCurrentPercent(); + + private: + HWND m_hwndProgress; + + DWORD64 m_dw64MaxValue; + DWORD64 m_dw64CurrentValue; + + bool show(); + }; + } +} + +#endif // __RFB_WIN32_PROGRESSCONTROL_H__ diff --git a/win/rfb_win32/RegConfig.cxx b/win/rfb_win32/RegConfig.cxx new file mode 100644 index 00000000..dd1c3b06 --- /dev/null +++ b/win/rfb_win32/RegConfig.cxx @@ -0,0 +1,114 @@ +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +// -=- RegConfig.cxx + +#include <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"); + + +RegConfig::RegConfig(EventManager* em) : eventMgr(em), event(CreateEvent(0, TRUE, FALSE, 0)), callback(0) { + if (em->addEvent(event, this)) + eventMgr = em; +} + +RegConfig::~RegConfig() { + if (eventMgr) + eventMgr->removeEvent(event); +} + +bool RegConfig::setKey(const HKEY rootkey, const TCHAR* keyname) { + try { + key.createKey(rootkey, keyname); + processEvent(event); + return true; + } catch (rdr::Exception& e) { + vlog.debug(e.str()); + return false; + } +} + +void RegConfig::loadRegistryConfig(RegKey& key) { + DWORD i = 0; + try { + while (1) { + TCharArray name = tstrDup(key.getValueName(i++)); + if (!name.buf) break; + TCharArray value = key.getRepresentation(name.buf); + if (!value.buf || !Configuration::setParam(CStr(name.buf), CStr(value.buf))) + vlog.info("unable to process %s", CStr(name.buf)); + } + } catch (rdr::SystemException& e) { + if (e.err != 6) + vlog.error(e.str()); + } +} + +void RegConfig::processEvent(HANDLE event_) { + vlog.info("registry changed"); + + // Reinstate the registry change notifications + ResetEvent(event); + key.awaitChange(true, REG_NOTIFY_CHANGE_NAME | REG_NOTIFY_CHANGE_LAST_SET, event); + + // Load settings + loadRegistryConfig(key); + + // Notify the callback, if supplied + if (callback) + callback->regConfigChanged(); +} + + +RegConfigThread::RegConfigThread() : Thread("RegConfigThread"), config(&eventMgr) { +} + +RegConfigThread::~RegConfigThread() { + join(); +} + +bool RegConfigThread::start(const HKEY rootKey, const TCHAR* keyname) { + if (config.setKey(rootKey, keyname)) { + Thread::start(); + return true; + } + return false; +} + +void RegConfigThread::run() { + DWORD result = 0; + MSG msg; + while ((result = eventMgr.getMessage(&msg, 0, 0, 0)) > 0) {} + if (result < 0) + throw rdr::SystemException("RegConfigThread failed", GetLastError()); +} + +Thread* RegConfigThread::join() { + PostThreadMessage(getThreadId(), WM_QUIT, 0, 0); + return Thread::join(); +} diff --git a/win/rfb_win32/RegConfig.h b/win/rfb_win32/RegConfig.h new file mode 100644 index 00000000..e9c01b1d --- /dev/null +++ b/win/rfb_win32/RegConfig.h @@ -0,0 +1,84 @@ +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +// -=- RegConfig.h + +// Class which monitors the registry and reads in the registry settings +// whenever they change, or are added or removed. + +#ifndef __RFB_WIN32_REG_CONFIG_H__ +#define __RFB_WIN32_REG_CONFIG_H__ + +#include <rfb/Threading.h> +#include <rfb/Configuration.h> +#include <rfb_win32/Registry.h> +#include <rfb_win32/EventManager.h> +#include <rfb_win32/Handle.h> + +namespace rfb { + + namespace win32 { + + class RegConfig : EventHandler { + public: + RegConfig(EventManager* em); + ~RegConfig(); + + // Specify the registry key to read Configuration items from + bool setKey(const HKEY rootkey, const TCHAR* keyname); + + // Support for a callback, run in the RegConfig host thread whenever + // the registry configuration changes + class Callback { + public: + virtual ~Callback() {} + virtual void regConfigChanged() = 0; + }; + void setCallback(Callback* cb) { callback = cb; } + + // Read entries from the specified key into the Configuration + static void loadRegistryConfig(RegKey& key); + protected: + // EventHandler interface and trigger event + virtual void processEvent(HANDLE event); + + EventManager* eventMgr; + Handle event; + Callback* callback; + RegKey key; + }; + + class RegConfigThread : Thread { + public: + RegConfigThread(); + ~RegConfigThread(); + + // Start the thread, reading from the specified key + bool start(const HKEY rootkey, const TCHAR* keyname); + protected: + void run(); + Thread* join(); + EventManager eventMgr; + RegConfig config; + }; + + }; + +}; + +#endif // __RFB_WIN32_REG_CONFIG_H__ diff --git a/win/rfb_win32/Registry.cxx b/win/rfb_win32/Registry.cxx new file mode 100644 index 00000000..4ece4bac --- /dev/null +++ b/win/rfb_win32/Registry.cxx @@ -0,0 +1,316 @@ +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +// -=- Registry.cxx + +#include <rfb_win32/Registry.h> +#include <rfb_win32/Security.h> +#include <rfb_win32/DynamicFn.h> +#include <rdr/MemOutStream.h> +#include <rdr/HexOutstream.h> +#include <rdr/HexInStream.h> +#include <stdlib.h> +#include <rfb/LogWriter.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) { + vlog.debug("setHKEY(%x,%d)", k, (int)fK); + close(); + freeKey = fK; + key = k; +} + + +bool RegKey::createKey(const RegKey& root, const TCHAR* name) { + close(); + LONG result = RegCreateKey(root.key, name, &key); + if (result != ERROR_SUCCESS) { + vlog.error("RegCreateKey(%x, %s): %x", root.key, name, result); + throw rdr::SystemException("RegCreateKeyEx", result); + } + vlog.debug("createKey(%x,%s) = %x", root.key, (const char*)CStr(name), key); + freeKey = true; + return true; +} + +void RegKey::openKey(const RegKey& root, const TCHAR* name, bool readOnly) { + close(); + LONG result = RegOpenKeyEx(root.key, name, 0, readOnly ? KEY_READ : KEY_ALL_ACCESS, &key); + if (result != ERROR_SUCCESS) + throw rdr::SystemException("RegOpenKeyEx (open)", result); + vlog.debug("openKey(%x,%s,%s) = %x", root.key, (const char*)CStr(name), + readOnly ? "ro" : "rw", key); + freeKey = true; +} + +void RegKey::setDACL(const PACL acl, bool inherit) { + DWORD result; + typedef DWORD (WINAPI *_SetSecurityInfo_proto) (HANDLE, SE_OBJECT_TYPE, SECURITY_INFORMATION, PSID, PSID, PACL, PACL); + DynamicFn<_SetSecurityInfo_proto> _SetSecurityInfo(_T("advapi32.dll"), "SetSecurityInfo"); + if (!_SetSecurityInfo.isValid()) + throw rdr::SystemException("RegKey::setDACL failed", ERROR_CALL_NOT_IMPLEMENTED); + if ((result = (*_SetSecurityInfo)(key, SE_REGISTRY_KEY, + DACL_SECURITY_INFORMATION | + (inherit ? UNPROTECTED_DACL_SECURITY_INFORMATION : PROTECTED_DACL_SECURITY_INFORMATION), + 0, 0, acl, 0)) != ERROR_SUCCESS) + throw rdr::SystemException("RegKey::setDACL failed", result); +} + +void RegKey::close() { + if (freeKey) { + vlog.debug("RegCloseKey(%x)", key); + RegCloseKey(key); + key = 0; + } +} + +void RegKey::deleteKey(const TCHAR* name) const { + LONG result = RegDeleteKey(key, name); + if (result != ERROR_SUCCESS) + throw rdr::SystemException("RegDeleteKey", result); +} + +void RegKey::deleteValue(const TCHAR* name) const { + LONG result = RegDeleteValue(key, name); + if (result != ERROR_SUCCESS) + throw rdr::SystemException("RegDeleteValue", result); +} + +void RegKey::awaitChange(bool watchSubTree, DWORD filter, HANDLE event) const { + LONG result = RegNotifyChangeKeyValue(key, watchSubTree, filter, event, event != 0); + if (result != ERROR_SUCCESS) + throw rdr::SystemException("RegNotifyChangeKeyValue", result); +} + + +RegKey::operator HKEY() const {return key;} + + +void RegKey::setExpandString(const TCHAR* valname, const TCHAR* value) const { + LONG result = RegSetValueEx(key, valname, 0, REG_EXPAND_SZ, (const BYTE*)value, (_tcslen(value)+1)*sizeof(TCHAR)); + if (result != ERROR_SUCCESS) throw rdr::SystemException("setExpandString", result); +} + +void RegKey::setString(const TCHAR* valname, const TCHAR* value) const { + LONG result = RegSetValueEx(key, valname, 0, REG_SZ, (const BYTE*)value, (_tcslen(value)+1)*sizeof(TCHAR)); + if (result != ERROR_SUCCESS) throw rdr::SystemException("setString", result); +} + +void RegKey::setBinary(const TCHAR* valname, const void* value, int length) const { + LONG result = RegSetValueEx(key, valname, 0, REG_BINARY, (const BYTE*)value, length); + if (result != ERROR_SUCCESS) throw rdr::SystemException("setBinary", result); +} + +void RegKey::setInt(const TCHAR* valname, int value) const { + LONG result = RegSetValueEx(key, valname, 0, REG_DWORD, (const BYTE*)&value, sizeof(value)); + if (result != ERROR_SUCCESS) throw rdr::SystemException("setInt", result); +} + +void RegKey::setBool(const TCHAR* valname, bool value) const { + setInt(valname, value ? 1 : 0); +} + +TCHAR* RegKey::getString(const TCHAR* valname) const {return getRepresentation(valname);} +TCHAR* RegKey::getString(const TCHAR* valname, const TCHAR* def) const { + try { + return getString(valname); + } catch(rdr::Exception) { + return tstrDup(def); + } +} + +void RegKey::getBinary(const TCHAR* valname, void** data, int* length) const { + TCharArray hex = getRepresentation(valname); + if (!rdr::HexInStream::hexStrToBin(CStr(hex.buf), (char**)data, length)) + throw rdr::Exception("getBinary failed"); +} +void RegKey::getBinary(const TCHAR* valname, void** data, int* length, void* def, int deflen) const { + try { + getBinary(valname, data, length); + } catch(rdr::Exception) { + if (deflen) { + *data = new char[deflen]; + memcpy(*data, def, deflen); + } else + *data = 0; + *length = deflen; + } +} + +int RegKey::getInt(const TCHAR* valname) const { + TCharArray tmp = getRepresentation(valname); + return _ttoi(tmp.buf); +} +int RegKey::getInt(const TCHAR* valname, int def) const { + try { + return getInt(valname); + } catch(rdr::Exception) { + return def; + } +} + +bool RegKey::getBool(const TCHAR* valname) const { + return getInt(valname) > 0; +} +bool RegKey::getBool(const TCHAR* valname, bool def) const { + return getInt(valname, def ? 1 : 0) > 0; +} + +static inline TCHAR* terminateData(char* data, int length) +{ + // We must terminate the string, just to be sure. Stupid Win32... + int len = length/sizeof(TCHAR); + TCharArray str(len+1); + memcpy(str.buf, data, length); + str.buf[len] = 0; + return str.takeBuf(); +} + +TCHAR* RegKey::getRepresentation(const TCHAR* valname) const { + DWORD type, length; + LONG result = RegQueryValueEx(key, valname, 0, &type, 0, &length); + if (result != ERROR_SUCCESS) + throw rdr::SystemException("get registry value length", result); + CharArray data(length); + result = RegQueryValueEx(key, valname, 0, &type, (BYTE*)data.buf, &length); + if (result != ERROR_SUCCESS) + throw rdr::SystemException("get registry value", result); + + switch (type) { + case REG_BINARY: + { + TCharArray hex = rdr::HexOutStream::binToHexStr(data.buf, length); + return hex.takeBuf(); + } + case REG_SZ: + if (length) { + return terminateData(data.buf, length); + } else { + return tstrDup(_T("")); + } + case REG_DWORD: + { + TCharArray tmp(16); + _stprintf(tmp.buf, _T("%u"), *((DWORD*)data.buf)); + return tmp.takeBuf(); + } + case REG_EXPAND_SZ: + { + if (length) { + TCharArray str(terminateData(data.buf, length)); + DWORD required = ExpandEnvironmentStrings(str.buf, 0, 0); + if (required==0) + throw rdr::SystemException("ExpandEnvironmentStrings", GetLastError()); + TCharArray result(required); + length = ExpandEnvironmentStrings(str.buf, result.buf, required); + if (required<length) + rdr::Exception("unable to expand environment strings"); + return result.takeBuf(); + } else { + return tstrDup(_T("")); + } + } + 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; +} + +const TCHAR* RegKey::getKeyName(int i) { + DWORD maxValueNameLen; + LONG result = RegQueryInfoKey(key, 0, 0, 0, 0, &maxValueNameLen, 0, 0, 0, 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 = RegEnumKeyEx(key, i, valueName.buf, &length, NULL, 0, 0, 0); + if (result == ERROR_NO_MORE_ITEMS) return 0; + if (result != ERROR_SUCCESS) + throw rdr::SystemException("RegEnumKey", result); + return valueName.buf; +} diff --git a/win/rfb_win32/Registry.h b/win/rfb_win32/Registry.h new file mode 100644 index 00000000..68d535cd --- /dev/null +++ b/win/rfb_win32/Registry.h @@ -0,0 +1,112 @@ +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ +// -=- Registry.h + +// C++ wrappers around the Win32 Registry APIs + +#ifndef __RFB_WIN32_REGISTRY_H__ +#define __RFB_WIN32_REGISTRY_H__ + +#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); + private: + RegKey& operator=(const RegKey& k); + HKEY& operator=(const HKEY& k); + public: + + // Returns true if key was created, false if already existed + bool createKey(const RegKey& root, const TCHAR* name); + + // Opens key if it exists, or raises an exception if not + void openKey(const RegKey& root, const TCHAR* name, bool readOnly=false); + + // Set the (discretionary) access control list for the key + void setDACL(const PACL acl, bool inheritFromParent=true); + + // Closes current key, if required + void close(); + + // Delete a subkey/value + void deleteKey(const TCHAR* name) const; + void deleteValue(const TCHAR* name) const; + + + // Block waiting for a registry change, OR return immediately and notify the + // event when there is a change, if specified + void awaitChange(bool watchSubTree, DWORD filter, HANDLE event=0) const; + + void setExpandString(const TCHAR* valname, const TCHAR* s) const; + void setString(const TCHAR* valname, const TCHAR* s) const; + void setBinary(const TCHAR* valname, const void* data, int length) const; + void setInt(const TCHAR* valname, int i) const; + void setBool(const TCHAR* valname, bool b) const; + + TCHAR* getString(const TCHAR* valname) const; + TCHAR* getString(const TCHAR* valname, const TCHAR* def) const; + + void getBinary(const TCHAR* valname, void** data, int* length) const; + void getBinary(const TCHAR* valname, void** data, int* length, void* def, int deflength) const; + + int getInt(const TCHAR* valname) const; + int getInt(const TCHAR* valname, int def) const; + + bool getBool(const TCHAR* valname) const; + bool getBool(const TCHAR* valname, bool def) const; + + TCHAR* getRepresentation(const TCHAR* valname) const; + + bool isValue(const TCHAR* valname) const; + + // Get the name of value/key number "i" + // If there are fewer than "i" values then return 0 + // NAME IS OWNED BY RegKey OBJECT! + const TCHAR* getValueName(int i); + const TCHAR* getKeyName(int i); + + operator HKEY() const; + protected: + HKEY key; + bool freeKey; + TCharArray valueName; + DWORD valueNameBufLen; + }; + + }; + +}; + +#endif // __RFB_WIN32_REG_CONFIG_H__ diff --git a/win/rfb_win32/SDisplay.cxx b/win/rfb_win32/SDisplay.cxx new file mode 100644 index 00000000..0af50649 --- /dev/null +++ b/win/rfb_win32/SDisplay.cxx @@ -0,0 +1,524 @@ +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +// -=- SDisplay.cxx +// +// The SDisplay class encapsulates a particular system display. + +#include <rfb_win32/SDisplay.h> +#include <rfb_win32/Service.h> +#include <rfb_win32/TsSessions.h> +#include <rfb_win32/CleanDesktop.h> +#include <rfb_win32/CurrentUser.h> +#include <rfb_win32/DynamicFn.h> +#include <rfb_win32/MonitorInfo.h> +#include <rfb_win32/SDisplayCorePolling.h> +#include <rfb_win32/SDisplayCoreWMHooks.h> +#include <rfb_win32/SDisplayCoreDriver.h> +#include <rfb/Exception.h> +#include <rfb/LogWriter.h> + + +using namespace rdr; +using namespace rfb; +using namespace rfb::win32; + +static LogWriter vlog("SDisplay"); + +// - SDisplay-specific configuration options + +IntParameter rfb::win32::SDisplay::updateMethod("UpdateMethod", + "How to discover desktop updates; 0 - Polling, 1 - Application hooking, 2 - Driver hooking.", 1); +BoolParameter rfb::win32::SDisplay::disableLocalInputs("DisableLocalInputs", + "Disable local keyboard and pointer input while the server is in use", false); +StringParameter rfb::win32::SDisplay::disconnectAction("DisconnectAction", + "Action to perform when all clients have disconnected. (None, Lock, Logoff)", "None"); +StringParameter displayDevice("DisplayDevice", + "Display device name of the monitor to be remoted, or empty to export the whole desktop.", ""); +BoolParameter rfb::win32::SDisplay::removeWallpaper("RemoveWallpaper", + "Remove the desktop wallpaper when the server is in use.", false); +BoolParameter rfb::win32::SDisplay::removePattern("RemovePattern", + "Remove the desktop background pattern when the server is in use.", false); +BoolParameter rfb::win32::SDisplay::disableEffects("DisableEffects", + "Disable desktop user interface effects when the server is in use.", false); + + +////////////////////////////////////////////////////////////////////////////// +// +// SDisplay +// + +typedef BOOL (WINAPI *_LockWorkStation_proto)(); +DynamicFn<_LockWorkStation_proto> _LockWorkStation(_T("user32.dll"), "LockWorkStation"); + +// -=- Constructor/Destructor + +SDisplay::SDisplay() + : server(0), pb(0), device(0), + core(0), ptr(0), kbd(0), clipboard(0), + inputs(0), monitor(0), cleanDesktop(0), cursor(0), + statusLocation(0) +{ + updateEvent.h = CreateEvent(0, TRUE, FALSE, 0); +} + +SDisplay::~SDisplay() +{ + // XXX when the VNCServer has been deleted with clients active, stop() + // doesn't get called - this ought to be fixed in VNCServerST. In any event, + // we should never call any methods on VNCServer once we're being deleted. + // This is because it is supposed to be guaranteed that the SDesktop exists + // throughout the lifetime of the VNCServer. So if we're being deleted, then + // the VNCServer ought not to exist and therefore we shouldn't invoke any + // methods on it. Setting server to zero here ensures that stop() doesn't + // call setPixelBuffer(0) on the server. + server = 0; + if (core) stop(); +} + + +// -=- SDesktop interface + +void SDisplay::start(VNCServer* vs) +{ + vlog.debug("starting"); + + // Try to make session zero the console session + if (!inConsoleSession()) + setConsoleSession(); + + // Start the SDisplay core + server = vs; + startCore(); + + vlog.debug("started"); + + if (statusLocation) *statusLocation = true; +} + +void SDisplay::stop() +{ + vlog.debug("stopping"); + + // If we successfully start()ed then perform the DisconnectAction + if (core) { + CurrentUserToken cut; + CharArray action = disconnectAction.getData(); + if (stricmp(action.buf, "Logoff") == 0) { + if (!cut.h) + vlog.info("ignoring DisconnectAction=Logoff - no current user"); + else + ExitWindowsEx(EWX_LOGOFF, 0); + } else if (stricmp(action.buf, "Lock") == 0) { + if (!cut.h) { + vlog.info("ignoring DisconnectAction=Lock - no current user"); + } else { + if (_LockWorkStation.isValid()) + (*_LockWorkStation)(); + else + ExitWindowsEx(EWX_LOGOFF, 0); + } + } + } + + // Stop the SDisplayCore + if (server) + server->setPixelBuffer(0); + stopCore(); + server = 0; + + vlog.debug("stopped"); + + if (statusLocation) *statusLocation = false; +} + + +void SDisplay::startCore() { + + // Currently, we just check whether we're in the console session, and + // fail if not + if (!inConsoleSession()) + throw rdr::Exception("Console is not session zero - oreconnect to restore Console sessin"); + + // Switch to the current input desktop + if (rfb::win32::desktopChangeRequired()) { + if (!rfb::win32::changeDesktop()) + throw rdr::Exception("unable to switch into input desktop"); + } + + // Initialise the change tracker and clipper + updates.clear(); + clipper.setUpdateTracker(server); + + // Create the framebuffer object + recreatePixelBuffer(true); + + // Create the SDisplayCore + updateMethod_ = updateMethod; + int tryMethod = updateMethod_; + while (!core) { + try { + if (tryMethod == 2) + core = new SDisplayCoreDriver(this, &updates); + else if (tryMethod == 1) + core = new SDisplayCoreWMHooks(this, &updates); + else + core = new SDisplayCorePolling(this, &updates); + core->setScreenRect(screenRect); + } catch (rdr::Exception& e) { + delete core; core = 0; + if (tryMethod == 0) + throw rdr::Exception("unable to access desktop"); + tryMethod--; + vlog.error(e.str()); + } + } + vlog.info("Started %s", core->methodName()); + + // Start display monitor, clipboard handler and input handlers + monitor = new WMMonitor; + monitor->setNotifier(this); + clipboard = new Clipboard; + clipboard->setNotifier(this); + ptr = new SPointer; + kbd = new SKeyboard; + inputs = new WMBlockInput; + cursor = new WMCursor; + + // Apply desktop optimisations + cleanDesktop = new CleanDesktop; + if (removePattern) + cleanDesktop->disablePattern(); + if (removeWallpaper) + cleanDesktop->disableWallpaper(); + if (disableEffects) + cleanDesktop->disableEffects(); + isWallpaperRemoved = removeWallpaper; + isPatternRemoved = removePattern; + areEffectsDisabled = disableEffects; +} + +void SDisplay::stopCore() { + if (core) + vlog.info("Stopping %s", core->methodName()); + delete core; core = 0; + delete pb; pb = 0; + delete device; device = 0; + delete monitor; monitor = 0; + delete clipboard; clipboard = 0; + delete inputs; inputs = 0; + delete ptr; ptr = 0; + delete kbd; kbd = 0; + delete cleanDesktop; cleanDesktop = 0; + delete cursor; cursor = 0; + ResetEvent(updateEvent); +} + + +bool SDisplay::areHooksAvailable() { + return WMHooks::areAvailable(); +} + +bool SDisplay::isDriverAvailable() { + return SDisplayCoreDriver::isAvailable(); +} + + +bool SDisplay::isRestartRequired() { + // - We must restart the SDesktop if: + // 1. We are no longer in the input desktop. + // 2. The any setting has changed. + + // - Check that our session is the Console + if (!inConsoleSession()) + return true; + + // - Check that we are in the input desktop + if (rfb::win32::desktopChangeRequired()) + return true; + + // - Check that the update method setting hasn't changed + // NB: updateMethod reflects the *selected* update method, not + // necessarily the one in use, since we fall back to simpler + // methods if more advanced ones fail! + if (updateMethod_ != updateMethod) + return true; + + // - Check that the desktop optimisation settings haven't changed + // This isn't very efficient, but it shouldn't change very often! + if ((isWallpaperRemoved != removeWallpaper) || + (isPatternRemoved != removePattern) || + (areEffectsDisabled != disableEffects)) + return true; + + return false; +} + + +void SDisplay::restartCore() { + vlog.info("restarting"); + + // Stop the existing Core related resources + stopCore(); + try { + // Start a new Core if possible + startCore(); + vlog.info("restarted"); + } catch (rdr::Exception& e) { + // If startCore() fails then we MUST disconnect all clients, + // to cause the server to stop() the desktop. + // Otherwise, the SDesktop is in an inconsistent state + // and the server will crash. + server->closeClients(e.str()); + } +} + + +void SDisplay::pointerEvent(const Point& pos, int buttonmask) { + if (pb->getRect().contains(pos)) { + Point screenPos = pos.translate(screenRect.tl); + // - Check that the SDesktop doesn't need restarting + if (isRestartRequired()) + restartCore(); + if (ptr) + ptr->pointerEvent(screenPos, buttonmask); + } +} + +void SDisplay::keyEvent(rdr::U32 key, bool down) { + // - Check that the SDesktop doesn't need restarting + if (isRestartRequired()) + restartCore(); + if (kbd) + kbd->keyEvent(key, down); +} + +void SDisplay::clientCutText(const char* text, int len) { + CharArray clip_sz(len+1); + memcpy(clip_sz.buf, text, len); + clip_sz.buf[len] = 0; + clipboard->setClipText(clip_sz.buf); +} + + +void SDisplay::framebufferUpdateRequest() +{ + SetEvent(updateEvent); +} + +Point SDisplay::getFbSize() { + bool startAndStop = !core; + + // If not started, do minimal initialisation to get desktop size. + if (startAndStop) + recreatePixelBuffer(); + Point result = Point(pb->width(), pb->height()); + + // Destroy the initialised structures. + if (startAndStop) + stopCore(); + return result; +} + + +void +SDisplay::notifyClipboardChanged(const char* text, int len) { + vlog.debug("clipboard text changed"); + if (server) + server->serverCutText(text, len); +} + + +void +SDisplay::notifyDisplayEvent(WMMonitor::Notifier::DisplayEventType evt) { + switch (evt) { + case WMMonitor::Notifier::DisplaySizeChanged: + vlog.debug("desktop size changed"); + recreatePixelBuffer(); + break; + case WMMonitor::Notifier::DisplayPixelFormatChanged: + vlog.debug("desktop format changed"); + recreatePixelBuffer(); + break; + case WMMonitor::Notifier::DisplayColourMapChanged: + vlog.debug("desktop colormap changed"); + pb->updateColourMap(); + if (server) + server->setColourMapEntries(); + break; + default: + vlog.error("unknown display event received"); + } +} + +void +SDisplay::processEvent(HANDLE event) { + if (event == updateEvent) { + vlog.write(120, "processEvent"); + ResetEvent(updateEvent); + + // - If the SDisplay isn't even started then quit now + if (!core) { + vlog.error("not start()ed"); + return; + } + + // - Ensure that the disableLocalInputs flag is respected + inputs->blockInputs(disableLocalInputs); + + // - Only process updates if the server is ready + if (server && server->clientsReadyForUpdate()) { + bool try_update = false; + + // - Check that the SDesktop doesn't need restarting + if (isRestartRequired()) { + restartCore(); + return; + } + + // - Flush any updates from the core + try { + core->flushUpdates(); + } catch (rdr::Exception& e) { + vlog.error(e.str()); + restartCore(); + return; + } + + // Ensure the cursor is up to date + WMCursor::Info info = cursor->getCursorInfo(); + if (old_cursor != info) { + // Update the cursor shape if the visibility has changed + bool set_cursor = info.visible != old_cursor.visible; + // OR if the cursor is visible and the shape has changed. + set_cursor |= info.visible && (old_cursor.cursor != info.cursor); + + // Update the cursor shape + if (set_cursor) + pb->setCursor(info.visible ? info.cursor : 0, server); + + // Update the cursor position + // NB: First translate from Screen coordinates to Desktop + Point desktopPos = info.position.translate(screenRect.tl.negate()); + server->setCursorPos(desktopPos); + try_update = true; + + old_cursor = info; + } + + // Flush any changes to the server + try_update = flushChangeTracker() || try_update; + if (try_update) { + server->tryUpdate(); + } + } + return; + } + throw rdr::Exception("No such event"); +} + + +// -=- Protected methods + +void +SDisplay::recreatePixelBuffer(bool force) { + // Open the specified display device + // If no device is specified, open entire screen using GetDC(). + // Opening the whole display with CreateDC doesn't work on multi-monitor + // systems for some reason. + DeviceContext* new_device = 0; + TCharArray deviceName(displayDevice.getData()); + if (deviceName.buf[0]) { + vlog.info("Attaching to device %s", (const char*)CStr(deviceName.buf)); + new_device = new DeviceDC(deviceName.buf); + } + if (!new_device) { + vlog.info("Attaching to virtual desktop"); + new_device = new WindowDC(0); + } + + // Get the coordinates of the specified dispay device + Rect newScreenRect; + if (deviceName.buf[0]) { + MonitorInfo info(CStr(deviceName.buf)); + newScreenRect = Rect(info.rcMonitor.left, info.rcMonitor.top, + info.rcMonitor.right, info.rcMonitor.bottom); + } else { + newScreenRect = new_device->getClipBox(); + } + + // If nothing has changed & a recreate has not been forced, delete + // the new device context and return + if (pb && !force && + newScreenRect.equals(screenRect) && + new_device->getPF().equal(pb->getPF())) { + delete new_device; + return; + } + + // Flush any existing changes to the server + flushChangeTracker(); + + // Delete the old pixelbuffer and device context + vlog.debug("deleting old pixel buffer & device"); + if (pb) + delete pb; + if (device) + delete device; + + // Create a DeviceFrameBuffer attached to the new device + vlog.debug("creating pixel buffer"); + DeviceFrameBuffer* new_buffer = new DeviceFrameBuffer(*new_device); + + // Replace the old PixelBuffer + screenRect = newScreenRect; + pb = new_buffer; + device = new_device; + + // Initialise the pixels + pb->grabRegion(pb->getRect()); + + // Prevent future grabRect operations from throwing exceptions + pb->setIgnoreGrabErrors(true); + + // Update the clipping update tracker + clipper.setClipRect(pb->getRect()); + + // Inform the core of the changes + if (core) + core->setScreenRect(screenRect); + + // Inform the server of the changes + if (server) + server->setPixelBuffer(pb); +} + +bool SDisplay::flushChangeTracker() { + if (updates.is_empty()) + return false; + + vlog.write(120, "flushChangeTracker"); + + // Translate the update coordinates from Screen coords to Desktop + updates.translate(screenRect.tl.negate()); + + // Clip the updates & flush them to the server + updates.copyTo(&clipper); + updates.clear(); + return true; +} diff --git a/win/rfb_win32/SDisplay.h b/win/rfb_win32/SDisplay.h new file mode 100644 index 00000000..6dbb50a5 --- /dev/null +++ b/win/rfb_win32/SDisplay.h @@ -0,0 +1,163 @@ +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +// -=- SDisplay.h +// +// The SDisplay class encapsulates a system display. + +#ifndef __RFB_SDISPLAY_H__ +#define __RFB_SDISPLAY_H__ + +#include <rfb/SDesktop.h> +#include <rfb/UpdateTracker.h> +#include <rfb/Configuration.h> +#include <rfb_win32/Handle.h> +#include <rfb_win32/EventManager.h> +#include <rfb_win32/SInput.h> +#include <rfb_win32/Clipboard.h> +#include <rfb_win32/CleanDesktop.h> +#include <rfb_win32/WMCursor.h> +#include <rfb_win32/WMNotifier.h> +#include <rfb_win32/DeviceFrameBuffer.h> +#include <rfb_win32/DeviceContext.h> + +namespace rfb { + + namespace win32 { + + // + // -=- SDisplay + // + + class SDisplayCore { + public: + virtual ~SDisplayCore() {}; + virtual void setScreenRect(const Rect& screenRect_) = 0; + virtual void flushUpdates() = 0; + virtual const char* methodName() const = 0; + }; + + class SDisplay : public SDesktop, + WMMonitor::Notifier, + Clipboard::Notifier, + public EventHandler + { + public: + SDisplay(); + virtual ~SDisplay(); + + // -=- SDesktop interface + + virtual void start(VNCServer* vs); + virtual void stop(); + virtual void pointerEvent(const Point& pos, int buttonmask); + virtual void keyEvent(rdr::U32 key, bool down); + virtual void clientCutText(const char* str, int len); + virtual void framebufferUpdateRequest(); + virtual Point getFbSize(); + + // -=- Clipboard + + virtual void notifyClipboardChanged(const char* text, int len); + + // -=- Display events + + virtual void notifyDisplayEvent(WMMonitor::Notifier::DisplayEventType evt); + + // -=- EventHandler interface + + HANDLE getUpdateEvent() {return updateEvent;} + virtual void processEvent(HANDLE event); + + // -=- Notification of whether or not SDisplay is started + + void setStatusLocation(bool* status) {statusLocation = status;} + + friend class SDisplayCore; + + static IntParameter updateMethod; + static BoolParameter disableLocalInputs; + static StringParameter disconnectAction; + static BoolParameter removeWallpaper; + static BoolParameter removePattern; + static BoolParameter disableEffects; + + // -=- Use by VNC Config to determine whether hooks, driver, etc are available + static bool areHooksAvailable(); + static bool isDriverAvailable(); + + + protected: + bool isRestartRequired(); + void startCore(); + void stopCore(); + void restartCore(); + void recreatePixelBuffer(bool force=false); + bool flushChangeTracker(); // true if flushed, false if empty + + VNCServer* server; + + // -=- Display pixel buffer + DeviceFrameBuffer* pb; + DeviceContext* device; + + // -=- The coordinates of Window's entire virtual Screen + Rect screenRect; + + // -=- All changes are collected in UN-CLIPPED Display coords and merged + // When they are to be flushed to the VNCServer, they are changed + // to server coords and clipped appropriately. + SimpleUpdateTracker updates; + ClippingUpdateTracker clipper; + + // -=- Internal SDisplay implementation + SDisplayCore* core; + int updateMethod_; + + // Inputs + SPointer* ptr; + SKeyboard* kbd; + Clipboard* clipboard; + WMBlockInput* inputs; + + // Desktop state + WMMonitor* monitor; + + // Desktop optimisation + CleanDesktop* cleanDesktop; + bool isWallpaperRemoved; + bool isPatternRemoved; + bool areEffectsDisabled; + + // Cursor + WMCursor* cursor; + WMCursor::Info old_cursor; + Region old_cursor_region; + Point cursor_renderpos; + + // -=- Event signalled to trigger an update to be flushed + Handle updateEvent; + + // -=- Where to write the active/inactive indicator to + bool* statusLocation; + }; + + } +} + +#endif // __RFB_SDISPLAY_H__ diff --git a/win/rfb_win32/SDisplayCoreDriver.h b/win/rfb_win32/SDisplayCoreDriver.h new file mode 100644 index 00000000..5fea75cc --- /dev/null +++ b/win/rfb_win32/SDisplayCoreDriver.h @@ -0,0 +1,52 @@ +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +// -=- SDisplayCoreDriver.h +// +// Placeholder for SDisplayCore mirror-driver implementation. + +#ifndef __RFB_SDISPLAY_CORE_DRIVER_H__ +#define __RFB_SDISPLAY_CORE_DRIVER_H__ + +#include <rfb_win32/SDisplay.h> + +namespace rfb { + namespace win32 { + + class SDisplayCoreDriver: public SDisplayCore { + public: + SDisplayCoreDriver(SDisplay* display, UpdateTracker* ut) { + throw rdr::Exception("Not supported"); + } + + // - Called by SDisplay to inform Core of the screen size + virtual void setScreenRect(const Rect& screenRect_) {} + + // - Called by SDisplay to flush updates to the specified tracker + virtual void flushUpdates() {} + + virtual const char* methodName() const { return "VNC Mirror Driver"; } + + // - Determine whether the display driver is installed & usable + static bool isAvailable() { return false; } + }; + + }; +}; + +#endif diff --git a/win/rfb_win32/SDisplayCorePolling.cxx b/win/rfb_win32/SDisplayCorePolling.cxx new file mode 100644 index 00000000..fc57ecd0 --- /dev/null +++ b/win/rfb_win32/SDisplayCorePolling.cxx @@ -0,0 +1,81 @@ +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +// -=- SDisplayCorePolling.cxx + +#include <rfb_win32/SDisplayCorePolling.h> +#include <rfb/LogWriter.h> + +using namespace rfb; +using namespace rfb::win32; + +static LogWriter vlog("SDisplayCorePolling"); + +const int POLLING_SEGMENTS = 16; + +const int SDisplayCorePolling::pollTimerId = 1; + +SDisplayCorePolling::SDisplayCorePolling(SDisplay* d, UpdateTracker* ut, int pollInterval_) + : MsgWindow(_T("rfb::win32::SDisplayCorePolling")), updateTracker(ut), + pollTimer(getHandle(), pollTimerId), pollNextStrip(false), display(d) { + pollInterval = max(10, (pollInterval_ / POLLING_SEGMENTS)); + copyrect.setUpdateTracker(ut); +} + +SDisplayCorePolling::~SDisplayCorePolling() { +} + +LRESULT SDisplayCorePolling::processMessage(UINT msg, WPARAM wParam, LPARAM lParam) { + if (msg == WM_TIMER && wParam == pollTimerId) { + pollNextStrip = true; + SetEvent(display->getUpdateEvent()); + return 0; + } + return MsgWindow::processMessage(msg, wParam, lParam); +} + +void SDisplayCorePolling::setScreenRect(const Rect& screenRect_) { + vlog.info("setScreenRect"); + screenRect = screenRect_; + pollIncrementY = (screenRect.height()+POLLING_SEGMENTS-1)/POLLING_SEGMENTS; + pollNextY = screenRect.tl.y; + pollTimer.start(pollInterval); +} + +void SDisplayCorePolling::flushUpdates() { + vlog.write(120, "flushUpdates"); + + // Check for window movement + while (copyrect.processEvent()) {} + + if (pollNextStrip) { + // Poll the next strip of the screen (in Screen coordinates) + pollNextStrip = false; + Rect pollrect = screenRect; + if (pollNextY >= pollrect.br.y) { + // Yes. Reset the counter and return + pollNextY = pollrect.tl.y; + } else { + // No. Poll the next section + pollrect.tl.y = pollNextY; + pollNextY += pollIncrementY; + pollrect.br.y = min(pollNextY, pollrect.br.y); + updateTracker->add_changed(pollrect); + } + } +} diff --git a/win/rfb_win32/SDisplayCorePolling.h b/win/rfb_win32/SDisplayCorePolling.h new file mode 100644 index 00000000..9e1b5ad1 --- /dev/null +++ b/win/rfb_win32/SDisplayCorePolling.h @@ -0,0 +1,75 @@ +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +// -=- SDisplayCorePolling.h +// +// SDisplayCore implementation that simply polls the screen, in sections, +// in order to detect changes. This Core will signal the SDisplay's +// updateEvent regularly, causing it to call the Core back to propagate +// changes to the VNC Server. + + +#ifndef __RFB_SDISPLAY_CORE_POLLING_H__ +#define __RFB_SDISPLAY_CORE_POLLING_H__ + +#include <rfb_win32/SDisplay.h> +#include <rfb_win32/IntervalTimer.h> +#include <rfb_win32/WMWindowCopyRect.h> + +namespace rfb { + namespace win32 { + + class SDisplayCorePolling : public SDisplayCore, protected MsgWindow { + public: + SDisplayCorePolling(SDisplay* display, UpdateTracker* ut, int pollIntervalMs=50); + ~SDisplayCorePolling(); + + // - Called by SDisplay to inform Core of the screen size + virtual void setScreenRect(const Rect& screenRect_); + + // - Called by SDisplay to flush updates to the specified tracker + virtual void flushUpdates(); + + virtual const char* methodName() const { return "Polling"; } + + protected: + // - MsgWindow overrides + // processMessage is used to service the cursor & polling timers + virtual LRESULT processMessage(UINT msg, WPARAM wParam, LPARAM lParam); + + // - Hooking subcomponents used to track the desktop state + WMCopyRect copyrect; + + // - Background full screen polling fields + IntervalTimer pollTimer; + static const int pollTimerId; + Rect screenRect; + int pollInterval; + int pollNextY; + int pollIncrementY; + bool pollNextStrip; + + // - Handle back to the owning SDisplay, and to the UpdateTracker to flush to + SDisplay* display; + UpdateTracker* updateTracker; + }; + + }; +}; + +#endif
\ No newline at end of file diff --git a/win/rfb_win32/SDisplayCoreWMHooks.cxx b/win/rfb_win32/SDisplayCoreWMHooks.cxx new file mode 100644 index 00000000..10b88e08 --- /dev/null +++ b/win/rfb_win32/SDisplayCoreWMHooks.cxx @@ -0,0 +1,74 @@ +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +// -=- SDisplayCoreWMHooks.cxx + +#include <rfb_win32/SDisplayCoreWMHooks.h> +#include <rfb/LogWriter.h> + +using namespace rfb; +using namespace rfb::win32; + +static LogWriter vlog("SDisplayCoreWMHooks"); + +const int SDisplayCoreWMHooks::cursorTimerId = 2; +const int SDisplayCoreWMHooks::consolePollTimerId = 3; + + +SDisplayCoreWMHooks::SDisplayCoreWMHooks(SDisplay* d, UpdateTracker* ut) + : SDisplayCorePolling(d, ut, 5000), + cursorTimer(getHandle(), cursorTimerId), + consolePollTimer(getHandle(), consolePollTimerId), + pollConsoles(false) { + if (!hooks.setEvent(display->getUpdateEvent())) + throw rdr::Exception("hook subsystem failed to initialise"); + poller.setUpdateTracker(updateTracker); + cursorTimer.start(20); + consolePollTimer.start(200); +} + +SDisplayCoreWMHooks::~SDisplayCoreWMHooks() { +} + +LRESULT SDisplayCoreWMHooks::processMessage(UINT msg, WPARAM wParam, LPARAM lParam) { + if (msg == WM_TIMER) { + if (wParam == cursorTimerId) { + SetEvent(display->getUpdateEvent()); + return 0; + } else if (wParam == consolePollTimerId) { + pollConsoles = true; + SetEvent(display->getUpdateEvent()); + return 0; + } + } + return SDisplayCorePolling::processMessage(msg, wParam, lParam); +} + +void SDisplayCoreWMHooks::flushUpdates() { + // Poll any visible console windows + if (pollConsoles) { + pollConsoles = false; + poller.processEvent(); + } + + // Check for updates from the hooks + hooks.getUpdates(updateTracker); + + // Check for updates from the polling Core + SDisplayCorePolling::flushUpdates(); +} diff --git a/win/rfb_win32/SDisplayCoreWMHooks.h b/win/rfb_win32/SDisplayCoreWMHooks.h new file mode 100644 index 00000000..24fa5cdc --- /dev/null +++ b/win/rfb_win32/SDisplayCoreWMHooks.h @@ -0,0 +1,68 @@ +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +// -=- SDisplayCoreWMHooks.h +// +// SDisplayCore implementation that uses WMHooks to capture changes to +// the display. +// Whenever changes are detected, the SDisplay's updateEvent is signalled, +// so that it can perform housekeeping tasks (like ensuring the currently +// active desktop is the correct one), before flushing changes from the +// Core to the VNC Server. The SDisplay will clip the changes before they +// reach the VNC Server. + + +#ifndef __RFB_SDISPLAY_CORE_WMHOOKS_H__ +#define __RFB_SDISPLAY_CORE_WMHOOKS_H__ + +#include <rfb_win32/SDisplayCorePolling.h> +#include <rfb_win32/WMHooks.h> +#include <rfb_win32/WMPoller.h> + +namespace rfb { + namespace win32 { + + class SDisplayCoreWMHooks : public SDisplayCorePolling { + public: + SDisplayCoreWMHooks(SDisplay* display, UpdateTracker* ut); + ~SDisplayCoreWMHooks(); + + // - Called by SDisplay to flush updates to the specified tracker + virtual void flushUpdates(); + + virtual const char* methodName() const { return "VNC Hooks"; } + + protected: + // - MsgWindow overrides + // processMessage is used to service the cursor & polling timers + virtual LRESULT processMessage(UINT msg, WPARAM wParam, LPARAM lParam); + + // - Hooking subcomponents used to track the desktop state + WMHooks hooks; + WMPoller poller; + IntervalTimer cursorTimer; + IntervalTimer consolePollTimer; + bool pollConsoles; + static const int consolePollTimerId; + static const int cursorTimerId; + }; + + }; +}; + +#endif diff --git a/win/rfb_win32/SFileTransferManagerWin32.cxx b/win/rfb_win32/SFileTransferManagerWin32.cxx new file mode 100644 index 00000000..edc898be --- /dev/null +++ b/win/rfb_win32/SFileTransferManagerWin32.cxx @@ -0,0 +1,71 @@ +/* Copyright (C) 2006 TightVNC Team. All Rights Reserved. + * + * Developed by Dennis Syrovatsky. + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + * + * TightVNC distribution homepage on the Web: http://www.tightvnc.com/ + * + */ + +// -=- SFileTransferManagerWin32.cxx + +#include <rfb_win32/SFileTransferManagerWin32.h> + +using namespace rfb; +using namespace win32; + +SFileTransferManagerWin32::SFileTransferManagerWin32() +{ + +} + +SFileTransferManagerWin32::~SFileTransferManagerWin32() +{ + +} + +SFileTransfer * +SFileTransferManagerWin32::createObject(network::Socket *sock) +{ + rfb::SFileTransfer *pFT = 0; + rfb::win32::SFileTransferWin32 *pFTWin32 = 0; + + pFTWin32 = new SFileTransferWin32(sock); + if (pFTWin32 == NULL) return NULL; + + pFT = (SFileTransfer *) pFTWin32; + + m_lstFTObjects.push_front(pFT); + + return pFT; +} + +void +SFileTransferManagerWin32::processDownloadMsg(MSG msg) +{ + SFileTransfer *pFT = (SFileTransfer *)msg.lParam; + + if (pFT != NULL) { + std::list<SFileTransfer*>::iterator i; + for (i=m_lstFTObjects.begin(); i!=m_lstFTObjects.end(); i++) { + if ((*i) == pFT) { + (*i)->sendFileDownloadPortion(); + return; + } + } + } +} diff --git a/win/rfb_win32/SFileTransferManagerWin32.h b/win/rfb_win32/SFileTransferManagerWin32.h new file mode 100644 index 00000000..ed1f997a --- /dev/null +++ b/win/rfb_win32/SFileTransferManagerWin32.h @@ -0,0 +1,48 @@ +/* Copyright (C) 2006 TightVNC Team. All Rights Reserved. + * + * Developed by Dennis Syrovatsky. + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + * + * TightVNC distribution homepage on the Web: http://www.tightvnc.com/ + * + */ + +// -=- SFileTransferManagerWin32.h + +#ifndef __RFB_WIN32_SFILETRANSFERMANAGERWIN32_H__ +#define __RFB_WIN32_SFILETRANSFERMANAGERWIN32_H__ + +#include <rfb/SFileTransfer.h> +#include <rfb/SFileTransferManager.h> +#include <rfb_win32/SFileTransferWin32.h> + +namespace rfb { + namespace win32 { + class SFileTransferManagerWin32 : public rfb::SFileTransferManager + { + public: + SFileTransferManagerWin32(); + virtual ~SFileTransferManagerWin32(); + + void processDownloadMsg(MSG msg); + + virtual SFileTransfer *createObject(network::Socket *sock); + }; + }; +} + +#endif // __RFB_WIN32_SFILETRANSFERMANAGERWIN32_H__ diff --git a/win/rfb_win32/SFileTransferWin32.cxx b/win/rfb_win32/SFileTransferWin32.cxx new file mode 100644 index 00000000..5aea4126 --- /dev/null +++ b/win/rfb_win32/SFileTransferWin32.cxx @@ -0,0 +1,125 @@ +/* Copyright (C) 2006 TightVNC Team. All Rights Reserved. + * + * Developed by Dennis Syrovatsky. + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + * + * TightVNC distribution homepage on the Web: http://www.tightvnc.com/ + * + */ + +// -=- SFileTransferWin32.cxx + +#include <rfb/msgTypes.h> +#include <rfb_win32/FolderManager.h> +#include <rfb_win32/SFileTransferWin32.h> + +using namespace rfb; +using namespace rfb::win32; + +SFileTransferWin32::SFileTransferWin32(network::Socket *sock) : SFileTransfer(sock) +{ +} + +SFileTransferWin32::~SFileTransferWin32() +{ +} + +bool +SFileTransferWin32::initDownloadCallback() +{ + PostThreadMessage(GetCurrentThreadId(), VNCM_FT_DOWNLOAD, (WPARAM) 0, (LPARAM) this); + return true; +} + +bool +SFileTransferWin32::processDownloadCallback() +{ + return sendFileDownloadPortion(); +} + +bool +SFileTransferWin32::convertPathFromNet(char *pszPath) +{ + int len = strlen(pszPath); + if (pszPath[0] == '/') { + if (len == 1) { + pszPath[0] = '\0'; + return true; + } + } else { + return false; + } + + for(int i = 0; i < (len - 1); i++) { + if(pszPath[i+1] == '/') pszPath[i+1] = '\\'; + pszPath[i] = pszPath[i+1]; + } + + pszPath[len-1] = '\0'; + return true; +} + +bool +SFileTransferWin32::makeFileList(char *pszPath, FileInfo *pFI, bool bDirOnly) +{ + FolderManager fm; + if (fm.getDirInfo(pszPath, pFI, bDirOnly)) + return true; + else + return false; +} + +bool +SFileTransferWin32::deleteIt(char *pszPath) +{ + FolderManager fm; + + return fm.deleteIt(pszPath); +} + +bool +SFileTransferWin32::renameIt(char *pszOldPath, char *pszNewPath) +{ + FolderManager fm; + + return fm.renameIt(pszOldPath, pszNewPath); +} + +bool +SFileTransferWin32::createDir(char *pszPath) +{ + FolderManager fm; + + return fm.createDir(pszPath); +} + +bool +SFileTransferWin32::getDirSize(char *pszName, unsigned short *pHighSize16, + unsigned int *pLowSize32) +{ + FolderManager fm; + DWORD64 dw64DirSize = 0; + + if (!fm.getDirSize(pszName, &dw64DirSize)) return false; + + if (dw64DirSize & 0xFFFF000000000000) return false; + + *pHighSize16 = ((dw64DirSize & 0x0000FFFF00000000) >> 32); + *pLowSize32 = (dw64DirSize & 0x00000000FFFFFFFF); + + return true; +} diff --git a/win/rfb_win32/SFileTransferWin32.h b/win/rfb_win32/SFileTransferWin32.h new file mode 100644 index 00000000..5f682a44 --- /dev/null +++ b/win/rfb_win32/SFileTransferWin32.h @@ -0,0 +1,59 @@ +/* Copyright (C) 2006 TightVNC Team. All Rights Reserved. + * + * Developed by Dennis Syrovatsky. + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + * + * TightVNC distribution homepage on the Web: http://www.tightvnc.com/ + * + */ + +// -=- SFileTransferWin32.h + +#ifndef __RFB_SFILETRANSFERWIN32_H__ +#define __RFB_SFILETRANSFERWIN32_H__ + +#include <windows.h> + +#include <rfb/SFileTransfer.h> + +const UINT VNCM_FT_DOWNLOAD = WM_USER + 2; + +namespace rfb { + namespace win32 { + class SFileTransferWin32 : public rfb::SFileTransfer + { + public: + SFileTransferWin32(network::Socket *sock); + virtual ~SFileTransferWin32(); + + bool processDownloadCallback(); + virtual bool initDownloadCallback(); + + virtual bool convertPathFromNet(char *pszPath); + virtual bool makeFileList(char *pszPath, FileInfo *pFI, bool bDirOnly); + + virtual bool deleteIt(char *pszPath); + virtual bool renameIt(char *pszOldPath, char *pszNewPath); + virtual bool createDir(char *pszPath); + + virtual bool getDirSize(char *pszName, unsigned short *pHighSize16, unsigned int *pLowSize32); + + }; + }; +} + +#endif // __RFB_SFILETRANSFERWIN32_H__ diff --git a/win/rfb_win32/SInput.cxx b/win/rfb_win32/SInput.cxx new file mode 100644 index 00000000..db59287a --- /dev/null +++ b/win/rfb_win32/SInput.cxx @@ -0,0 +1,466 @@ +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +// -=- SInput.cxx +// +// A number of routines that accept VNC input event data and perform +// the appropriate actions under Win32 + +#define XK_MISCELLANY +#define XK_LATIN1 +#define XK_CURRENCY +#include <rfb/keysymdef.h> + +#include <tchar.h> +#include <rfb_win32/SInput.h> +#include <rfb_win32/MonitorInfo.h> +#include <rfb_win32/Service.h> +#include <rfb_win32/OSVersion.h> +#include <rfb_win32/DynamicFn.h> +#include <rfb_win32/keymap.h> +#include <rdr/Exception.h> +#include <rfb/LogWriter.h> + +#if(defined(INPUT_MOUSE) && defined(RFB_HAVE_MONITORINFO)) +#define RFB_HAVE_SENDINPUT +#else +#pragma message(" NOTE: Not building SendInput support.") +#endif + +using namespace rfb; + +static LogWriter vlog("SInput"); + +#ifdef RFB_HAVE_SENDINPUT +typedef UINT (WINAPI *_SendInput_proto)(UINT, LPINPUT, int); +static win32::DynamicFn<_SendInput_proto> _SendInput(_T("user32.dll"), "SendInput"); +#endif + +// +// -=- Pointer implementation for Win32 +// + +static DWORD buttonDownMapping[8] = { + MOUSEEVENTF_LEFTDOWN, MOUSEEVENTF_MIDDLEDOWN, MOUSEEVENTF_RIGHTDOWN, + MOUSEEVENTF_WHEEL, MOUSEEVENTF_WHEEL, 0, 0, 0 +}; + +static DWORD buttonUpMapping[8] = { + MOUSEEVENTF_LEFTUP, MOUSEEVENTF_MIDDLEUP, MOUSEEVENTF_RIGHTUP, + MOUSEEVENTF_WHEEL, MOUSEEVENTF_WHEEL, 0, 0, 0 +}; + +static DWORD buttonDataMapping[8] = { + 0, 0, 0, 120, -120, 0, 0, 0 +}; + +win32::SPointer::SPointer() + : last_buttonmask(0) +{ +} + +void +win32::SPointer::pointerEvent(const Point& pos, int buttonmask) +{ + // - We are specifying absolute coordinates + DWORD flags = MOUSEEVENTF_ABSOLUTE; + + // - Has the pointer moved since the last event? + if (!last_position.equals(pos)) + flags |= MOUSEEVENTF_MOVE; + + // - If the system swaps left and right mouse buttons then we must + // swap them here to negate the effect, so that we do the actual + // action we mean to do + if (::GetSystemMetrics(SM_SWAPBUTTON)) { + bool leftDown = buttonmask & 1; + bool rightDown = buttonmask & 4; + buttonmask = (buttonmask & ~(1 | 4)); + if (leftDown) buttonmask |= 4; + if (rightDown) buttonmask |= 1; + } + + DWORD data = 0; + for (int i = 0; i < 8; i++) { + if ((buttonmask & (1<<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 RFB_HAVE_SENDINPUT + 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/win/rfb_win32/SInput.h b/win/rfb_win32/SInput.h new file mode 100644 index 00000000..2a0b3e67 --- /dev/null +++ b/win/rfb_win32/SInput.h @@ -0,0 +1,68 @@ +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +// -=- Input.h +// +// A number of routines that accept VNC-style input event data and perform +// the appropriate actions under Win32 + +#ifndef __RFB_WIN32_INPUT_H__ +#define __RFB_WIN32_INPUT_H__ + +#include <rfb/Rect.h> +#include <rfb/Configuration.h> +#include <rdr/types.h> +#include <map> +#include <vector> + +namespace rfb { + + namespace win32 { + + // -=- Pointer event handling + + class SPointer { + public: + SPointer(); + // - Create a pointer event at a the given coordinates, with the + // specified button state. The event must be specified using + // Screen coordinates. + void pointerEvent(const Point& pos, int buttonmask); + protected: + Point last_position; + rdr::U8 last_buttonmask; + }; + + // -=- Keyboard event handling + + class SKeyboard { + public: + SKeyboard(); + void keyEvent(rdr::U32 key, bool down); + static BoolParameter deadKeyAware; + private: + std::map<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/win/rfb_win32/ScaledDIBSectionBuffer.cxx b/win/rfb_win32/ScaledDIBSectionBuffer.cxx new file mode 100644 index 00000000..e6c15b8e --- /dev/null +++ b/win/rfb_win32/ScaledDIBSectionBuffer.cxx @@ -0,0 +1,124 @@ +/* Copyright (C) 2006 TightVNC Team. All Rights Reserved. + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + * + * TightVNC distribution homepage on the Web: http://www.tightvnc.com/ + * + */ + +// -=- ScaledDIBSectionBuffer.cxx + +#include <math.h> + +#include <rfb_win32/ScaledDIBSectionBuffer.h> + +using namespace rfb; +using namespace win32; + +ScaledDIBSectionBuffer::ScaledDIBSectionBuffer(HWND window) + : src_buffer(0), scaling(false), DIBSectionBuffer(window) { + scaled_data = data; +} + +ScaledDIBSectionBuffer::~ScaledDIBSectionBuffer() { + if (src_buffer) delete src_buffer; +} + +void ScaledDIBSectionBuffer::setScale(int scale_) { + if (scale_ == getScale()) return; + + if (src_buffer) { + delete src_buffer; + src_buffer = 0; + } + if (scale_ != 100) { + scaling = true; + src_buffer = new ManagedPixelBuffer(format, src_width, src_height); + src_data = &(src_buffer->data); + } else { + scaling = false; + } + ScaledPixelBuffer::setScale(scale_); +} + +void ScaledDIBSectionBuffer::setPF(const PixelFormat &pf) { + if (scaling) src_buffer->setPF(pf); + DIBSectionBuffer::setPF(pf); + scaled_data = data; +} + +void ScaledDIBSectionBuffer::setSize(int src_width_, int src_height_) { + src_width = src_width_; + src_height = src_height_; + if (scaling) { + src_buffer->setSize(src_width, src_height); + } + calculateScaledBufferSize(); + recreateScaledBuffer(); + scaled_data = data; +} + +void ScaledDIBSectionBuffer::recreateScaledBuffer() { + width_ = scaled_width; + height_ = scaled_height; + DIBSectionBuffer::recreateBuffer(); + scaled_data = data; +} + +void ScaledDIBSectionBuffer::fillRect(const Rect &dest, Pixel pix) { + if (scaling) { + src_buffer->fillRect(dest, pix); + scaleRect(dest); + } else { + DIBSectionBuffer::fillRect(dest, pix); + } +} + +void ScaledDIBSectionBuffer::imageRect(const Rect &dest, const void* pixels, int stride) { + if (scaling) { + src_buffer->imageRect(dest, pixels, stride); + scaleRect(dest); + } else { + DIBSectionBuffer::imageRect(dest, pixels, stride); + } +} + +void ScaledDIBSectionBuffer::copyRect(const Rect &dest, const Point &move_by_delta) { + if (scaling) { + src_buffer->copyRect(dest, move_by_delta); + scaleRect(dest); + } else { + DIBSectionBuffer::copyRect(dest, move_by_delta); + } +} + +void ScaledDIBSectionBuffer::maskRect(const Rect& r, const void* pixels, const void* mask_) { + if (scaling) { + src_buffer->maskRect(r, pixels, mask_); + scaleRect(r); + } else { + DIBSectionBuffer::maskRect(r, pixels, mask_); + } +} + +void ScaledDIBSectionBuffer::maskRect(const Rect& r, Pixel pixel, const void* mask_) { + if (scaling) { + src_buffer->maskRect(r, pixel, mask_); + scaleRect(r); + } else { + DIBSectionBuffer::maskRect(r, pixel, mask_); + } +} diff --git a/win/rfb_win32/ScaledDIBSectionBuffer.h b/win/rfb_win32/ScaledDIBSectionBuffer.h new file mode 100644 index 00000000..3cc267b2 --- /dev/null +++ b/win/rfb_win32/ScaledDIBSectionBuffer.h @@ -0,0 +1,78 @@ +/* Copyright (C) 2006 TightVNC Team. All Rights Reserved. + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + * + * TightVNC distribution homepage on the Web: http://www.tightvnc.com/ + * + */ + +// -=- ScaledDIBSectionBuffer.h + +#ifndef __RFB_WIN32_SCALED_DIB_SECTION_BUFFER_H__ +#define __RFB_WIN32_SCALED_DIB_SECTION_BUFFER_H__ + +#include <rfb/ScaledPixelBuffer.h> + +#include <rfb_win32/DIBSectionBuffer.h> + +namespace rfb { + + namespace win32 { + + // + // -=- ScaledDIBSectionBuffer + // + + class ScaledDIBSectionBuffer : public ScaledPixelBuffer, public DIBSectionBuffer { + public: + ScaledDIBSectionBuffer(HWND window); + virtual ~ScaledDIBSectionBuffer(); + + int width() const { return scaled_width; } + int height() const { return scaled_height; } + bool isScaling() const { return scaling; } + + virtual void setPF(const PixelFormat &pf); + virtual void setSize(int w, int h); + virtual void setScale(int scale); + + Rect getRect() const { return ScaledPixelBuffer::getRect(); } + Rect getRect(const Point& pos) const { return ScaledPixelBuffer::getRect(pos); } + + // -=- Overrides basic rendering operations of + // FullFramePixelBuffer class + + virtual void fillRect(const Rect &dest, Pixel pix); + virtual void imageRect(const Rect &dest, const void* pixels, int stride=0); + virtual void copyRect(const Rect &dest, const Point &move_by_delta); + virtual void maskRect(const Rect& r, const void* pixels, const void* mask_); + virtual void maskRect(const Rect& r, Pixel pixel, const void* mask_); + + protected: + virtual void recreateScaledBuffer(); + virtual void recreateBuffer() { + recreateScaledBuffer(); + }; + + ManagedPixelBuffer *src_buffer; + bool scaling; + }; + + }; + +}; + +#endif // __RFB_WIN32_SCALED_DIB_SECTION_BUFFER_H__ diff --git a/win/rfb_win32/Security.cxx b/win/rfb_win32/Security.cxx new file mode 100644 index 00000000..985f00cb --- /dev/null +++ b/win/rfb_win32/Security.cxx @@ -0,0 +1,192 @@ +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +// -=- Security.cxx + +#include <rfb_win32/Security.h> +#include <rfb_win32/DynamicFn.h> +#include <rfb/LogWriter.h> + +#include <lmcons.h> +#include <Accctrl.h> +#include <list> + +using namespace rfb; +using namespace rfb::win32; + +static LogWriter vlog("SecurityWin32"); + + +Trustee::Trustee(const TCHAR* name, + TRUSTEE_FORM form, + TRUSTEE_TYPE type) { + pMultipleTrustee = 0; + MultipleTrusteeOperation = NO_MULTIPLE_TRUSTEE; + TrusteeForm = form; + TrusteeType = type; + ptstrName = (TCHAR*)name; +} + + +ExplicitAccess::ExplicitAccess(const TCHAR* name, + TRUSTEE_FORM type, + DWORD perms, + ACCESS_MODE mode, + DWORD inherit) { + Trustee = rfb::win32::Trustee(name, type); + grfAccessPermissions = perms; + grfAccessMode = mode; + grfInheritance = inherit; +} + + +AccessEntries::AccessEntries() : entries(0), entry_count(0) {} + +AccessEntries::~AccessEntries() { + delete [] entries; +} + +void AccessEntries::allocMinEntries(int count) { + if (count > entry_count) { + EXPLICIT_ACCESS* new_entries = new EXPLICIT_ACCESS[entry_count+1]; + if (entries) { + memcpy(new_entries, entries, sizeof(EXPLICIT_ACCESS) * entry_count); + delete entries; + } + entries = new_entries; + } +} + +void AccessEntries::addEntry(const TCHAR* trusteeName, + DWORD permissions, + ACCESS_MODE mode) { + allocMinEntries(entry_count+1); + ZeroMemory(&entries[entry_count], sizeof(EXPLICIT_ACCESS)); + entries[entry_count] = ExplicitAccess(trusteeName, TRUSTEE_IS_NAME, permissions, mode); + entry_count++; +} + +void AccessEntries::addEntry(const PSID sid, + DWORD permissions, + ACCESS_MODE mode) { + allocMinEntries(entry_count+1); + ZeroMemory(&entries[entry_count], sizeof(EXPLICIT_ACCESS)); + entries[entry_count] = ExplicitAccess((TCHAR*)sid, TRUSTEE_IS_SID, permissions, mode); + entry_count++; +} + + +PSID Sid::copySID(const PSID sid) { + if (!IsValidSid(sid)) + throw rdr::Exception("invalid SID in copyPSID"); + PSID buf = (PSID)new rdr::U8[GetLengthSid(sid)]; + if (!CopySid(GetLengthSid(sid), buf, sid)) + throw rdr::SystemException("CopySid failed", GetLastError()); + return buf; +} + +void Sid::setSID(const PSID sid) { + delete [] buf; + buf = (rdr::U8*)copySID(sid); +} + +void Sid::getUserNameAndDomain(TCHAR** name, TCHAR** domain) { + DWORD nameLen = 0; + DWORD domainLen = 0; + SID_NAME_USE use; + LookupAccountSid(0, (PSID)buf, 0, &nameLen, 0, &domainLen, &use); + if (GetLastError() != ERROR_INSUFFICIENT_BUFFER) + throw rdr::SystemException("Unable to determine SID name lengths", GetLastError()); + vlog.info("nameLen=%d, domainLen=%d, use=%d", nameLen, domainLen, use); + *name = new TCHAR[nameLen]; + *domain = new TCHAR[domainLen]; + if (!LookupAccountSid(0, (PSID)buf, *name, &nameLen, *domain, &domainLen, &use)) + throw rdr::SystemException("Unable to lookup account SID", GetLastError()); +} + + +Sid::Administrators::Administrators() { + PSID sid = 0; + SID_IDENTIFIER_AUTHORITY ntAuth = SECURITY_NT_AUTHORITY; + if (!AllocateAndInitializeSid(&ntAuth, 2, + SECURITY_BUILTIN_DOMAIN_RID, + DOMAIN_ALIAS_RID_ADMINS, + 0, 0, 0, 0, 0, 0, &sid)) + throw rdr::SystemException("Sid::Administrators", GetLastError()); + setSID(sid); + FreeSid(sid); +} + +Sid::SYSTEM::SYSTEM() { + PSID sid = 0; + SID_IDENTIFIER_AUTHORITY ntAuth = SECURITY_NT_AUTHORITY; + if (!AllocateAndInitializeSid(&ntAuth, 1, + SECURITY_LOCAL_SYSTEM_RID, + 0, 0, 0, 0, 0, 0, 0, &sid)) + throw rdr::SystemException("Sid::SYSTEM", GetLastError()); + setSID(sid); + FreeSid(sid); +} + +Sid::FromToken::FromToken(HANDLE h) { + DWORD required = 0; + GetTokenInformation(h, TokenUser, 0, 0, &required); + rdr::U8Array tmp(required); + if (!GetTokenInformation(h, TokenUser, tmp.buf, required, &required)) + throw rdr::SystemException("GetTokenInformation", GetLastError()); + TOKEN_USER* tokenUser = (TOKEN_USER*)tmp.buf; + setSID(tokenUser->User.Sid); +} + + +PACL rfb::win32::CreateACL(const AccessEntries& ae, PACL existing_acl) { + typedef DWORD (WINAPI *_SetEntriesInAcl_proto) (ULONG, PEXPLICIT_ACCESS, PACL, PACL*); +#ifdef UNICODE + const char* fnName = "SetEntriesInAclW"; +#else + const char* fnName = "SetEntriesInAclA"; +#endif + DynamicFn<_SetEntriesInAcl_proto> _SetEntriesInAcl(_T("advapi32.dll"), fnName); + if (!_SetEntriesInAcl.isValid()) + throw rdr::SystemException("CreateACL failed; no SetEntriesInAcl", ERROR_CALL_NOT_IMPLEMENTED); + PACL new_dacl; + DWORD result; + if ((result = (*_SetEntriesInAcl)(ae.entry_count, ae.entries, existing_acl, &new_dacl)) != ERROR_SUCCESS) + throw rdr::SystemException("SetEntriesInAcl", result); + return new_dacl; +} + + +PSECURITY_DESCRIPTOR rfb::win32::CreateSdWithDacl(const PACL dacl) { + SECURITY_DESCRIPTOR absSD; + if (!InitializeSecurityDescriptor(&absSD, SECURITY_DESCRIPTOR_REVISION)) + throw rdr::SystemException("InitializeSecurityDescriptor", GetLastError()); + Sid::SYSTEM owner; + if (!SetSecurityDescriptorOwner(&absSD, owner, FALSE)) + throw rdr::SystemException("SetSecurityDescriptorOwner", GetLastError()); + Sid::Administrators group; + if (!SetSecurityDescriptorGroup(&absSD, group, FALSE)) + throw rdr::SystemException("SetSecurityDescriptorGroupp", GetLastError()); + if (!SetSecurityDescriptorDacl(&absSD, TRUE, dacl, FALSE)) + throw rdr::SystemException("SetSecurityDescriptorDacl", GetLastError()); + DWORD sdSize = GetSecurityDescriptorLength(&absSD); + SecurityDescriptorPtr sd(sdSize); + if (!MakeSelfRelativeSD(&absSD, sd, &sdSize)) + throw rdr::SystemException("MakeSelfRelativeSD", GetLastError()); + return sd.takeSD(); +} diff --git a/win/rfb_win32/Security.h b/win/rfb_win32/Security.h new file mode 100644 index 00000000..1e2e9068 --- /dev/null +++ b/win/rfb_win32/Security.h @@ -0,0 +1,123 @@ +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +// Security.h + +// Wrapper classes for a few Windows NT security structures/functions +// that are used by VNC + +#ifndef __RFB_WIN32_SECURITY_H__ +#define __RFB_WIN32_SECURITY_H__ + +#include <rdr/types.h> +#include <rfb_win32/LocalMem.h> +#include <rfb_win32/TCharArray.h> +#include <aclapi.h> + +namespace rfb { + + namespace win32 { + + struct Trustee : public TRUSTEE { + Trustee(const TCHAR* name, + TRUSTEE_FORM form=TRUSTEE_IS_NAME, + TRUSTEE_TYPE type=TRUSTEE_IS_UNKNOWN); + }; + + struct ExplicitAccess : public EXPLICIT_ACCESS { + ExplicitAccess(const TCHAR* name, + TRUSTEE_FORM type, + DWORD perms, + ACCESS_MODE mode, + DWORD inherit=0); + }; + + // Helper class for building access control lists + struct AccessEntries { + AccessEntries(); + ~AccessEntries(); + void allocMinEntries(int count); + void addEntry(const TCHAR* trusteeName, + DWORD permissions, + ACCESS_MODE mode); + void addEntry(const PSID sid, + DWORD permissions, + ACCESS_MODE mode); + + EXPLICIT_ACCESS* entries; + int entry_count; + }; + + // Helper class for handling SIDs + struct Sid : rdr::U8Array { + Sid() {} + operator PSID() const {return (PSID)buf;} + PSID takePSID() {PSID r = (PSID)buf; buf = 0; return r;} + + static PSID copySID(const PSID sid); + + void setSID(const PSID sid); + + void getUserNameAndDomain(TCHAR** name, TCHAR** domain); + + struct Administrators; + struct SYSTEM; + struct FromToken; + + private: + Sid(const Sid&); + Sid& operator=(const Sid&); + }; + + struct Sid::Administrators : public Sid { + Administrators(); + }; + struct Sid::SYSTEM : public Sid { + SYSTEM(); + }; + struct Sid::FromToken : public Sid { + FromToken(HANDLE h); + }; + + // Helper class for handling & freeing ACLs + struct AccessControlList : public LocalMem { + AccessControlList(int size) : LocalMem(size) {} + AccessControlList(PACL acl_=0) : LocalMem(acl_) {} + operator PACL() {return (PACL)ptr;} + }; + + // Create a new ACL based on supplied entries and, if supplied, existing ACL + PACL CreateACL(const AccessEntries& ae, PACL existing_acl=0); + + // Helper class for memory-management of self-relative SecurityDescriptors + struct SecurityDescriptorPtr : LocalMem { + SecurityDescriptorPtr(int size) : LocalMem(size) {} + SecurityDescriptorPtr(PSECURITY_DESCRIPTOR sd_=0) : LocalMem(sd_) {} + PSECURITY_DESCRIPTOR takeSD() {return takePtr();} + }; + + // Create a new self-relative Security Descriptor, owned by SYSTEM/Administrators, + // with the supplied DACL and no SACL. The returned value can be assigned + // to a SecurityDescriptorPtr to be managed. + PSECURITY_DESCRIPTOR CreateSdWithDacl(const PACL dacl); + + } + +} + +#endif diff --git a/win/rfb_win32/Service.cxx b/win/rfb_win32/Service.cxx new file mode 100644 index 00000000..2b11a22d --- /dev/null +++ b/win/rfb_win32/Service.cxx @@ -0,0 +1,645 @@ +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +// -=- Service.cxx + +#include <rfb_win32/Service.h> +#include <rfb_win32/MsgWindow.h> +#include <rfb_win32/DynamicFn.h> +#include <rfb_win32/ModuleFileName.h> +#include <rfb_win32/Registry.h> +#include <rfb_win32/OSVersion.h> +#include <rfb/Threading.h> +#include <logmessages/messages.h> +#include <rdr/Exception.h> +#include <rfb/LogWriter.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) { + switch (control) { + case SERVICE_CONTROL_INTERROGATE: + vlog.info("cmd: report status"); + service->setStatus(); + return; + case SERVICE_CONTROL_PARAMCHANGE: + vlog.info("cmd: param change"); + service->readParams(); + return; + case SERVICE_CONTROL_SHUTDOWN: + vlog.info("cmd: OS shutdown"); + service->osShuttingDown(); + return; + case SERVICE_CONTROL_STOP: + vlog.info("cmd: stop"); + service->setStatus(SERVICE_STOP_PENDING); + service->stop(); + return; + }; + vlog.debug("cmd: unknown %lu", control); +} + + +// -=- Message window derived class used under Win9x to implement stopService + +#define WM_SMSG_SERVICE_STOP WM_USER + +class ServiceMsgWindow : public MsgWindow { +public: + ServiceMsgWindow(const TCHAR* name) : MsgWindow(name) {} + LRESULT processMessage(UINT msg, WPARAM wParam, LPARAM lParam) { + switch (msg) { + case WM_SMSG_SERVICE_STOP: + service->stop(); + return TRUE; + } + return MsgWindow::processMessage(msg, wParam, lParam); + } + + static const TCHAR* baseName; +}; + +const TCHAR* ServiceMsgWindow::baseName = _T("ServiceWindow:"); + + +// -=- Service main procedure, used under WinNT/2K/XP by the SCM + +VOID WINAPI serviceProc(DWORD dwArgc, LPTSTR* lpszArgv) { + vlog.debug("entering %s serviceProc", service->getName()); + vlog.info("registering handler..."); + service->status_handle = RegisterServiceCtrlHandler(service->getName(), serviceHandler); + if (!service->status_handle) { + DWORD err = GetLastError(); + vlog.error("failed to register handler: %lu", err); + ExitProcess(err); + } + vlog.debug("registered handler (%lx)", service->status_handle); + service->setStatus(SERVICE_START_PENDING); + vlog.debug("entering %s serviceMain", service->getName()); + service->status.dwWin32ExitCode = service->serviceMain(dwArgc, lpszArgv); + vlog.debug("leaving %s serviceMain", service->getName()); + service->setStatus(SERVICE_STOPPED); +} + + +// -=- Service + +Service::Service(const TCHAR* name_) : name(name_) { + vlog.debug("Service"); + status_handle = 0; + status.dwControlsAccepted = SERVICE_CONTROL_INTERROGATE | SERVICE_ACCEPT_SHUTDOWN | SERVICE_ACCEPT_STOP; + status.dwServiceType = SERVICE_WIN32_OWN_PROCESS | SERVICE_INTERACTIVE_PROCESS; + status.dwWin32ExitCode = NO_ERROR; + status.dwServiceSpecificExitCode = 0; + status.dwCheckPoint = 0; + status.dwWaitHint = 30000; + status.dwCurrentState = SERVICE_STOPPED; +} + +void +Service::start() { + if (osVersion.isPlatformNT) { + SERVICE_TABLE_ENTRY entry[2]; + entry[0].lpServiceName = (TCHAR*)name; + entry[0].lpServiceProc = serviceProc; + entry[1].lpServiceName = NULL; + entry[1].lpServiceProc = NULL; + vlog.debug("entering dispatcher"); + if (!SetProcessShutdownParameters(0x100, 0)) + vlog.error("unable to set shutdown parameters: %d", GetLastError()); + service = this; + if (!StartServiceCtrlDispatcher(entry)) + throw SystemException("unable to start service", GetLastError()); + } else { + + // - Create the service window, so the service can be stopped + TCharArray wndName(_tcslen(getName()) + _tcslen(ServiceMsgWindow::baseName) + 1); + _tcscpy(wndName.buf, ServiceMsgWindow::baseName); + _tcscat(wndName.buf, getName()); + ServiceMsgWindow service_window(wndName.buf); + + // - Locate the RegisterServiceProcess function + typedef DWORD (WINAPI * _RegisterServiceProcess_proto)(DWORD, DWORD); + DynamicFn<_RegisterServiceProcess_proto> _RegisterServiceProcess(_T("kernel32.dll"), "RegisterServiceProcess"); + if (!_RegisterServiceProcess.isValid()) + throw Exception("unable to find RegisterServiceProcess"); + + // - Run the service + (*_RegisterServiceProcess)(NULL, 1); + service = this; + serviceMain(0, 0); + (*_RegisterServiceProcess)(NULL, 0); + } +} + +void +Service::setStatus() { + setStatus(status.dwCurrentState); +} + +void +Service::setStatus(DWORD state) { + if (!osVersion.isPlatformNT) + return; + if (status_handle == 0) { + vlog.debug("warning - cannot setStatus"); + return; + } + status.dwCurrentState = state; + status.dwCheckPoint++; + if (!SetServiceStatus(status_handle, &status)) { + status.dwCurrentState = SERVICE_STOPPED; + status.dwWin32ExitCode = GetLastError(); + vlog.error("unable to set service status:%u", status.dwWin32ExitCode); + } + vlog.debug("set status to %u(%u)", state, status.dwCheckPoint); +} + +Service::~Service() { + vlog.debug("~Service"); + service = 0; +} + + +// Find out whether this process is running as the WinVNC service +bool thisIsService() { + return service && (service->status.dwCurrentState != SERVICE_STOPPED); +} + + +// -=- Desktop handling code + +// Switch the current thread to the specified desktop +static bool +switchToDesktop(HDESK desktop) { + HDESK old_desktop = GetThreadDesktop(GetCurrentThreadId()); + if (!SetThreadDesktop(desktop)) { + vlog.debug("switchToDesktop failed:%u", GetLastError()); + return false; + } + if (!CloseDesktop(old_desktop)) + vlog.debug("unable to close old desktop:%u", GetLastError()); + return true; +} + +// Determine whether the thread's current desktop is the input one +static bool +inputDesktopSelected() { + HDESK current = GetThreadDesktop(GetCurrentThreadId()); + HDESK input = OpenInputDesktop(0, FALSE, + DESKTOP_CREATEMENU | DESKTOP_CREATEWINDOW | + DESKTOP_ENUMERATE | DESKTOP_HOOKCONTROL | + DESKTOP_WRITEOBJECTS | DESKTOP_READOBJECTS | + DESKTOP_SWITCHDESKTOP | GENERIC_WRITE); + if (!input) { + vlog.debug("unable to OpenInputDesktop(1):%u", GetLastError()); + return false; + } + + DWORD size; + char currentname[256]; + char inputname[256]; + + if (!GetUserObjectInformation(current, UOI_NAME, currentname, 256, &size)) { + vlog.debug("unable to GetUserObjectInformation(1):%u", GetLastError()); + CloseDesktop(input); + return false; + } + if (!GetUserObjectInformation(input, UOI_NAME, inputname, 256, &size)) { + vlog.debug("unable to GetUserObjectInformation(2):%u", GetLastError()); + CloseDesktop(input); + return false; + } + if (!CloseDesktop(input)) + vlog.debug("unable to close input desktop:%u", GetLastError()); + + // *** vlog.debug("current=%s, input=%s", currentname, inputname); + bool result = strcmp(currentname, inputname) == 0; + return result; +} + +// Switch the current thread into the input desktop +static bool +selectInputDesktop() { + // - Open the input desktop + HDESK desktop = OpenInputDesktop(0, FALSE, + DESKTOP_CREATEMENU | DESKTOP_CREATEWINDOW | + DESKTOP_ENUMERATE | DESKTOP_HOOKCONTROL | + DESKTOP_WRITEOBJECTS | DESKTOP_READOBJECTS | + DESKTOP_SWITCHDESKTOP | GENERIC_WRITE); + if (!desktop) { + vlog.debug("unable to OpenInputDesktop(2):%u", GetLastError()); + return false; + } + + // - Switch into it + if (!switchToDesktop(desktop)) { + CloseDesktop(desktop); + return false; + } + + // *** + DWORD size = 256; + char currentname[256]; + if (GetUserObjectInformation(desktop, UOI_NAME, currentname, 256, &size)) { + vlog.debug("switched to %s", currentname); + } + // *** + + vlog.debug("switched to input desktop"); + + return true; +} + + +// -=- Access points to desktop-switching routines + +bool +rfb::win32::desktopChangeRequired() { + if (!osVersion.isPlatformNT) + return false; + + return !inputDesktopSelected(); +} + +bool +rfb::win32::changeDesktop() { + if (!osVersion.isPlatformNT) + return true; + if (osVersion.cannotSwitchDesktop) + return false; + + return selectInputDesktop(); +} + + +// -=- Ctrl-Alt-Del emulation + +class CADThread : public Thread { +public: + CADThread() : Thread("CtrlAltDel Emulator"), result(false) {} + virtual void run() { + HDESK old_desktop = GetThreadDesktop(GetCurrentThreadId()); + + if (switchToDesktop(OpenDesktop(_T("Winlogon"), 0, FALSE, DESKTOP_CREATEMENU | DESKTOP_CREATEWINDOW | + DESKTOP_ENUMERATE | DESKTOP_HOOKCONTROL | + DESKTOP_WRITEOBJECTS | DESKTOP_READOBJECTS | + DESKTOP_SWITCHDESKTOP | GENERIC_WRITE))) { + PostMessage(HWND_BROADCAST, WM_HOTKEY, 0, MAKELONG(MOD_ALT | MOD_CONTROL, VK_DELETE)); + switchToDesktop(old_desktop); + result = true; + } + } + bool result; +}; + +bool +rfb::win32::emulateCtrlAltDel() { + if (!osVersion.isPlatformNT) + return false; + + CADThread* cad_thread = new CADThread(); + vlog.debug("emulate Ctrl-Alt-Del"); + if (cad_thread) { + cad_thread->start(); + cad_thread->join(); + bool result = cad_thread->result; + delete cad_thread; + return result; + } + return false; +} + + +// -=- Application Event Log target Logger class + +class Logger_EventLog : public Logger { +public: + Logger_EventLog(const TCHAR* srcname) : Logger("EventLog") { + eventlog = RegisterEventSource(NULL, srcname); + if (!eventlog) + printf("Unable to open event log:%ld\n", GetLastError()); + } + ~Logger_EventLog() { + if (eventlog) + DeregisterEventSource(eventlog); + } + + virtual void write(int level, const char *logname, const char *message) { + if (!eventlog) return; + TStr log(logname), msg(message); + const TCHAR* strings[] = {log, msg}; + WORD type = EVENTLOG_INFORMATION_TYPE; + if (level == 0) type = EVENTLOG_ERROR_TYPE; + if (!ReportEvent(eventlog, type, 0, VNC4LogMessage, NULL, 2, 0, strings, NULL)) { + // *** It's not at all clear what is the correct behaviour if this fails... + printf("ReportEvent failed:%ld\n", GetLastError()); + } + } + +protected: + HANDLE eventlog; +}; + +static Logger_EventLog* logger = 0; + +bool rfb::win32::initEventLogLogger(const TCHAR* srcname) { + if (logger) + return false; + if (osVersion.isPlatformNT) { + logger = new Logger_EventLog(srcname); + logger->registerLogger(); + return true; + } else { + return false; + } +} + + +// -=- Registering and unregistering the service + +bool rfb::win32::registerService(const TCHAR* name, const TCHAR* desc, + int argc, const char* argv[]) { + + // - Initialise the default service parameters + const TCHAR* defaultcmdline; + if (osVersion.isPlatformNT) + defaultcmdline = _T("-service"); + else + defaultcmdline = _T("-noconsole -service"); + + // - Get the full pathname of our executable + ModuleFileName buffer; + + // - Calculate the command-line length + int cmdline_len = _tcslen(buffer.buf) + 4; + int i; + for (i=0; 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; +} + +DWORD rfb::win32::getServiceState(const TCHAR* name) { + if (osVersion.isPlatformNT) { + // - Open the SCM + ServiceHandle scm = OpenSCManager(NULL, NULL, SC_MANAGER_CONNECT); + if (!scm) + throw rdr::SystemException("unable to open Service Control Manager", GetLastError()); + + // - Locate the service + ServiceHandle service = OpenService(scm, name, SERVICE_INTERROGATE); + if (!service) + throw rdr::SystemException("unable to open the service", GetLastError()); + + // - Get the service status + SERVICE_STATUS status; + if (!ControlService(service, SERVICE_CONTROL_INTERROGATE, (SERVICE_STATUS*)&status)) + throw rdr::SystemException("unable to query the service", GetLastError()); + + return status.dwCurrentState; + } else { + HWND service_window = findServiceWindow(name); + return service_window ? SERVICE_RUNNING : SERVICE_STOPPED; + } +} + +char* rfb::win32::serviceStateName(DWORD state) { + switch (state) { + case SERVICE_RUNNING: return strDup("Running"); + case SERVICE_STOPPED: return strDup("Stopped"); + case SERVICE_STOP_PENDING: return strDup("Stopping"); + }; + CharArray tmp(32); + sprintf(tmp.buf, "Unknown (%lu)", state); + return tmp.takeBuf(); +} + + +bool rfb::win32::isServiceProcess() { + return service != 0; +} diff --git a/win/rfb_win32/Service.h b/win/rfb_win32/Service.h new file mode 100644 index 00000000..00abe108 --- /dev/null +++ b/win/rfb_win32/Service.h @@ -0,0 +1,128 @@ +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +// -=- Service.h +// +// Win32 service-mode code. +// Derive your service from this code and let it handle the annoying Win32 +// service API. +// The underlying implementation takes care of the differences between +// Windows NT and Windows 95 based systems + +#ifndef __RFB_WIN32_SERVICE_H__ +#define __RFB_WIN32_SERVICE_H__ + +#include <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() {}; + + // To get notified when the service parameters change + virtual void readParams() {}; + + // To cause the serviceMain() routine to return + virtual void stop() {}; + + public: + SERVICE_STATUS_HANDLE status_handle; + SERVICE_STATUS status; + protected: + const TCHAR* name; + }; + + class ServiceHandle { + public: + ServiceHandle(SC_HANDLE h) : handle(h) {} + ~ServiceHandle() {CloseServiceHandle(handle);} + operator SC_HANDLE() const {return handle;} + protected: + SC_HANDLE handle; + }; + + // -=- Routines used by desktop back-end code to manage desktops/window stations + + // Returns false under Win9x + bool desktopChangeRequired(); + + // Returns true under Win9x + bool changeDesktop(); + + // -=- Routines used by the SInput Keyboard class to emulate Ctrl-Alt-Del + // Returns false under Win9x + bool emulateCtrlAltDel(); + + // -=- Routines to initialise the Event Log target Logger + // Returns false under Win9x + bool initEventLogLogger(const TCHAR* srcname); + + // -=- Routines to register/unregister the service + // These routines also take care of registering the required + // event source information, etc. + // *** should really accept TCHAR argv + + bool registerService(const TCHAR* name, const TCHAR* desc, int argc, const char* argv[]); + bool unregisterService(const TCHAR* name); + + bool startService(const TCHAR* name); + bool stopService(const TCHAR* name); + + // -=- Get the state of the named service (one of the NT service state values) + DWORD getServiceState(const TCHAR* name); + + // -=- Convert a supplied service state value to a printable string e.g. Running, Stopped... + // The caller must delete the returned string buffer + char* serviceStateName(DWORD state); + + // -=- Routine to determine whether the host process is running a service + bool isServiceProcess(); + + }; + +}; + +#endif // __RFB_WIN32_SERVICE_NT_H__ diff --git a/win/rfb_win32/SocketManager.cxx b/win/rfb_win32/SocketManager.cxx new file mode 100644 index 00000000..1d52bc86 --- /dev/null +++ b/win/rfb_win32/SocketManager.cxx @@ -0,0 +1,213 @@ +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +// -=- SocketManager.cxx + +#include <winsock2.h> +#include <list> +#include <rfb/LogWriter.h> +#include <rfb_win32/SocketManager.h> + +using namespace rfb; +using namespace rfb::win32; + +static LogWriter vlog("SocketManager"); + + +// -=- SocketManager + +SocketManager::SocketManager() { +} + +SocketManager::~SocketManager() { +} + + +static requestAddressChangeEvents(network::SocketListener* sock_) { + DWORD dummy = 0; + if (WSAIoctl(sock_->getFd(), SIO_ADDRESS_LIST_CHANGE, 0, 0, 0, 0, &dummy, 0, 0) == SOCKET_ERROR) { + DWORD err = WSAGetLastError(); + if (err != WSAEWOULDBLOCK) + vlog.error("Unable to track address changes", err); + } +} + + +void SocketManager::addListener(network::SocketListener* sock_, + network::SocketServer* srvr, + AddressChangeNotifier* acn) { + WSAEVENT event = WSACreateEvent(); + long flags = FD_ACCEPT | FD_CLOSE; + if (acn) + flags |= FD_ADDRESS_LIST_CHANGE; + try { + if (event && (WSAEventSelect(sock_->getFd(), event, flags) == SOCKET_ERROR)) + throw rdr::SystemException("Unable to select on listener", WSAGetLastError()); + + // requestAddressChangeEvents MUST happen after WSAEventSelect, so that the socket is non-blocking + if (acn) + requestAddressChangeEvents(sock_); + + // addEvent is the last thing we do, so that the event is NOT registered if previous steps fail + if (!event || !addEvent(event, this)) + throw rdr::Exception("Unable to add listener"); + } catch (rdr::Exception& e) { + if (event) + WSACloseEvent(event); + delete sock_; + vlog.error(e.str()); + throw; + } + + ListenInfo li; + li.sock = sock_; + li.server = srvr; + li.notifier = acn; + listeners[event] = li; +} + +void SocketManager::remListener(network::SocketListener* sock) { + std::map<HANDLE,ListenInfo>::iterator i; + for (i=listeners.begin(); i!=listeners.end(); i++) { + if (i->second.sock == sock) { + removeEvent(i->first); + WSACloseEvent(i->first); + delete sock; + listeners.erase(i); + return; + } + } + throw rdr::Exception("Listener not registered"); +} + + +void SocketManager::addSocket(network::Socket* sock_, network::SocketServer* srvr, bool outgoing) { + WSAEVENT event = WSACreateEvent(); + if (!event || !addEvent(event, this) || + (WSAEventSelect(sock_->getFd(), event, FD_READ | FD_CLOSE) == SOCKET_ERROR)) { + if (event) + WSACloseEvent(event); + delete sock_; + vlog.error("Unable to add connection"); + return; + } + ConnInfo ci; + ci.sock = sock_; + ci.server = srvr; + connections[event] = ci; + srvr->addSocket(sock_, outgoing); +} + +void SocketManager::remSocket(network::Socket* sock_) { + std::map<HANDLE,ConnInfo>::iterator i; + for (i=connections.begin(); i!=connections.end(); i++) { + if (i->second.sock == sock_) { + i->second.server->removeSocket(sock_); + removeEvent(i->first); + WSACloseEvent(i->first); + delete sock_; + connections.erase(i); + return; + } + } + throw rdr::Exception("Socket not registered"); +} + + +int SocketManager::checkTimeouts() { + network::SocketServer* server = 0; + int timeout = EventManager::checkTimeouts(); + + std::map<HANDLE,ListenInfo>::iterator i; + for (i=listeners.begin(); i!=listeners.end(); i++) + soonestTimeout(&timeout, i->second.server->checkTimeouts()); + + std::list<network::Socket*> shutdownSocks; + std::map<HANDLE,ConnInfo>::iterator j, j_next; + for (j=connections.begin(); j!=connections.end(); j=j_next) { + j_next = j; j_next++; + if (j->second.sock->isShutdown()) + shutdownSocks.push_back(j->second.sock); + } + + std::list<network::Socket*>::iterator k; + for (k=shutdownSocks.begin(); k!=shutdownSocks.end(); k++) + remSocket(*k); + + return timeout; +} + + +void SocketManager::processEvent(HANDLE event) { + if (listeners.count(event)) { + ListenInfo li = listeners[event]; + + // Accept an incoming connection + vlog.debug("accepting incoming connection"); + + // What kind of event is this? + WSANETWORKEVENTS network_events; + WSAEnumNetworkEvents(li.sock->getFd(), event, &network_events); + if (network_events.lNetworkEvents & FD_ACCEPT) { + network::Socket* new_sock = li.sock->accept(); + if (new_sock && li.server->getDisable()) { + delete new_sock; + new_sock = 0; + } + if (new_sock) + addSocket(new_sock, li.server, false); + } else if (network_events.lNetworkEvents & FD_CLOSE) { + vlog.info("deleting listening socket"); + remListener(li.sock); + } else if (network_events.lNetworkEvents & FD_ADDRESS_LIST_CHANGE) { + li.notifier->processAddressChange(li.sock); + DWORD dummy = 0; + requestAddressChangeEvents(li.sock); + } else { + vlog.error("unknown listener event: %lx", network_events.lNetworkEvents); + } + } else if (connections.count(event)) { + ConnInfo ci = connections[event]; + + try { + // Process data from an active connection + + // Cancel event notification for this socket + if (WSAEventSelect(ci.sock->getFd(), event, 0) == SOCKET_ERROR) + throw rdr::SystemException("unable to disable WSAEventSelect:%u", WSAGetLastError()); + + // Reset the event object + WSAResetEvent(event); + + // Call the socket server to process the event + ci.server->processSocketEvent(ci.sock); + if (ci.sock->isShutdown()) { + remSocket(ci.sock); + return; + } + + // Re-instate the required socket event + // If the read event is still valid, the event object gets set here + if (WSAEventSelect(ci.sock->getFd(), event, FD_READ | FD_CLOSE) == SOCKET_ERROR) + throw rdr::SystemException("unable to re-enable WSAEventSelect:%u", WSAGetLastError()); + } catch (rdr::Exception& e) { + vlog.error(e.str()); + remSocket(ci.sock); + } + } +} diff --git a/win/rfb_win32/SocketManager.h b/win/rfb_win32/SocketManager.h new file mode 100644 index 00000000..ef359749 --- /dev/null +++ b/win/rfb_win32/SocketManager.h @@ -0,0 +1,90 @@ +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +// -=- SocketManager.h + +// Socket manager class for Win32. +// Passed a network::SocketListener and a network::SocketServer when +// constructed. Uses WSAAsyncSelect to get notifications of network +// connection attempts. When an incoming connection is received, +// the manager will call network::SocketServer::addClient(). If +// addClient returns true then the manager registers interest in +// network events on that socket, and calls +// network::SocketServer::processSocketEvent(). + +#ifndef __RFB_WIN32_SOCKET_MGR_H__ +#define __RFB_WIN32_SOCKET_MGR_H__ + +#include <map> +#include <network/Socket.h> +#include <rfb_win32/EventManager.h> + +namespace rfb { + namespace win32 { + + class SocketManager : public EventManager, EventHandler { + public: + SocketManager(); + virtual ~SocketManager(); + + // AddressChangeNotifier callback interface + // If an object implementing this is passed to addListener then it will be + // called whenever the SocketListener's address list changes + class AddressChangeNotifier { + public: + virtual ~AddressChangeNotifier() {} + virtual void processAddressChange(network::SocketListener* sl) = 0; + }; + + // Add a listening socket. Incoming connections will be added to the supplied + // SocketServer. + void addListener(network::SocketListener* sock_, + network::SocketServer* srvr, + AddressChangeNotifier* acn = 0); + + // Remove and delete a listening socket. + void remListener(network::SocketListener* sock); + + // Add an already-connected socket. Socket events will cause the supplied + // SocketServer to be called. The socket must ALREADY BE REGISTERED with + // the SocketServer. + void addSocket(network::Socket* sock_, network::SocketServer* srvr, bool outgoing=true); + + protected: + virtual int checkTimeouts(); + virtual void processEvent(HANDLE event); + virtual void remSocket(network::Socket* sock); + + struct ConnInfo { + network::Socket* sock; + network::SocketServer* server; + }; + struct ListenInfo { + network::SocketListener* sock; + network::SocketServer* server; + AddressChangeNotifier* notifier; + }; + std::map<HANDLE, ListenInfo> listeners; + std::map<HANDLE, ConnInfo> connections; + }; + + } + +} + +#endif diff --git a/win/rfb_win32/TCharArray.cxx b/win/rfb_win32/TCharArray.cxx new file mode 100644 index 00000000..fd4c0783 --- /dev/null +++ b/win/rfb_win32/TCharArray.cxx @@ -0,0 +1,85 @@ +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +#include <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/win/rfb_win32/TCharArray.h b/win/rfb_win32/TCharArray.h new file mode 100644 index 00000000..dde63b78 --- /dev/null +++ b/win/rfb_win32/TCharArray.h @@ -0,0 +1,135 @@ +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +// -=- 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__ + +#include <windows.h> +#include <tchar.h> +#include <rfb/util.h> +#include <rfb/Password.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; + }; + + // -=- Wide-character-based password-buffer handler. Zeroes the password + // buffer when deleted or replaced. + class WPlainPasswd : public WCharArray { + public: + WPlainPasswd() {} + WPlainPasswd(WCHAR* str) : WCharArray(str) {} + ~WPlainPasswd() {replaceBuf(0);} + void replaceBuf(WCHAR* str) { + if (buf) + memset(buf, 0, sizeof(WCHAR)*wcslen(buf)); + WCharArray::replaceBuf(str); + } + }; + +#ifdef _UNICODE +#define tstrDup wstrDup +#define tstrFree wstrFree +#define tstrSplit wstrSplit +#define tstrContains wstrContains + typedef WCharArray TCharArray; + typedef WStr TStr; + typedef WPlainPasswd TPlainPasswd; +#else +#define tstrDup strDup +#define tstrFree strFree +#define tstrSplit strSplit +#define tstrContains strContains + typedef CharArray TCharArray; + typedef CStr TStr; + typedef PlainPasswd TPlainPasswd; +#endif + +}; + +#endif diff --git a/win/rfb_win32/Threading.cxx b/win/rfb_win32/Threading.cxx new file mode 100644 index 00000000..c41ac38b --- /dev/null +++ b/win/rfb_win32/Threading.cxx @@ -0,0 +1,151 @@ +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +// -=- Threading.cxx +// Win32 Threading interface implementation + +#include <malloc.h> + +#include <rdr/Exception.h> +#include <rfb/LogWriter.h> +#include <rfb/util.h> +#include <rfb_win32/Threading.h> + +using namespace rfb; + +static LogWriter vlog("Threading"); + +static DWORD threadStorage = TlsAlloc(); + + +inline void logAction(Thread* t, const char* action) { + vlog.debug("%-16.16s %s(%lx)", action, t->getName(), t); +} + +inline void logError(Thread* t, const char* err) { + vlog.error("%-16.16s %s(%lx):%s", "failed", t->getName(), t, err); +} + + +DWORD WINAPI +Thread::threadProc(LPVOID lpParameter) { + Thread* thread = (Thread*) lpParameter; + TlsSetValue(threadStorage, thread); + logAction(thread, "started"); + try { + thread->run(); + logAction(thread, "stopped"); + } catch (rdr::Exception& e) { + logError(thread, e.str()); + } + bool deleteThread = false; + { + Lock l(thread->mutex); + thread->state = ThreadStopped; + thread->sig->signal(); + deleteThread = thread->deleteAfterRun; + } + if (deleteThread) + delete thread; + return 0; +} + +Thread::Thread(const char* name_) : name(strDup(name_ ? name_ : "Unnamed")), sig(0), deleteAfterRun(false) { + sig = new Condition(mutex); + cond_event.h = CreateEvent(NULL, TRUE, FALSE, NULL); + thread.h = CreateThread(NULL, 0, threadProc, this, CREATE_SUSPENDED, &thread_id); + state = ThreadCreated; + logAction(this, "created"); +} + +Thread::Thread(HANDLE thread_, DWORD thread_id_) : name(strDup("Native")), sig(0), deleteAfterRun(false), + thread(thread_), thread_id(thread_id_) { + cond_event.h = CreateEvent(NULL, TRUE, FALSE, NULL); + state = ThreadNative; + logAction(this, "created"); +} + +Thread::~Thread() { + logAction(this, "destroying"); + if (!deleteAfterRun && state != ThreadNative) + this->join(); + if (sig) + delete sig; + logAction(this, "destroyed"); +} + +void +Thread::run() { +} + +void +Thread::start() { + Lock l(mutex); + if (state == ThreadCreated) { + state = ThreadStarted; + sig->signal(); + ResumeThread(thread); + } +} + +Thread* +Thread::join() { + if (deleteAfterRun) + throw rdr::Exception("attempt to join() with deleteAfterRun thread"); + Lock l(mutex); + if (state == ThreadJoined) { + logAction(this, "already joined"); + } else { + logAction(this, "joining"); + while (state == ThreadStarted) { + sig->wait(); + logAction(this, "checking"); + } + state = ThreadJoined; + logAction(this, "joined"); + } + return this; +} + +const char* +Thread::getName() const { + return name.buf; +} + +ThreadState +Thread::getState() const { + return state; +} + +unsigned long +Thread::getThreadId() const { + return thread_id; +} + + +Thread* +Thread::self() { + Thread* thread = (Thread*) TlsGetValue(threadStorage); + if (!thread) { + // *** memory leak - could use GetExitCodeThread to lazily detect when + // to clean up native thread objects + thread = new Thread(GetCurrentThread(), GetCurrentThreadId()); + TlsSetValue(threadStorage, thread); + } + return thread; +} diff --git a/win/rfb_win32/Threading.h b/win/rfb_win32/Threading.h new file mode 100644 index 00000000..850f04dd --- /dev/null +++ b/win/rfb_win32/Threading.h @@ -0,0 +1,154 @@ +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +// -=- Threading_win32.h +// Win32 Threading interface implementation + +#ifndef __RFB_THREADING_IMPL_WIN32 +#define __RFB_THREADING_IMPL_WIN32 + +#define __RFB_THREADING_IMPL WIN32 + +#include <rfb_win32/Handle.h> +#include <rfb/util.h> +#include <rdr/Exception.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, ThreadJoined, ThreadNative}; + + class Thread { + public: + Thread(const char* name_=0); + virtual ~Thread(); + + virtual void run(); + + virtual void start(); + virtual Thread* join(); + + const char* getName() const; + ThreadState getState() const; + + // Determines whether the thread should delete itself when run() returns + // If you set this, you must NEVER call join()! + void setDeleteAfterRun() {deleteAfterRun = true;}; + + unsigned long getThreadId() const; + + static Thread* self(); + + friend class Condition; + + protected: + Thread(HANDLE thread_, DWORD thread_id_); + static DWORD WINAPI threadProc(LPVOID lpParameter); + + win32::Handle thread; + DWORD thread_id; + CharArray name; + ThreadState state; + Condition* sig; + Mutex mutex; + + win32::Handle cond_event; + Thread* cond_next; + + bool deleteAfterRun; + }; + + class Condition { + public: + Condition(Mutex& m) : mutex(m), waiting(0) { + } + ~Condition() { + } + + // Wake up the specified number of threads that are waiting + // on this Condition, or all of them if -1 is specified. + void signal(int howMany=1) { + Lock l(cond_lock); + while (waiting && howMany!=0) { + SetEvent(waiting->cond_event); + waiting = waiting->cond_next; + if (howMany>0) --howMany; + } + } + + // NB: Must hold "mutex" to call wait() + // Wait until either the Condition is signalled or the timeout + // expires. + void wait(DWORD timeout=INFINITE) { + Thread* self = Thread::self(); + ResetEvent(self->cond_event); + { Lock l(cond_lock); + self->cond_next = waiting; + waiting = self; + } + mutex.exit(); + DWORD result = WaitForSingleObject(self->cond_event, timeout); + mutex.enter(); + if (result == WAIT_TIMEOUT) { + Lock l(cond_lock); + // Remove this thread from the Condition + for (Thread** removeFrom = &waiting; *removeFrom; removeFrom = &(*removeFrom)->cond_next) { + if (*removeFrom == self) { + *removeFrom = self->cond_next; + break; + } + } + } else if (result == WAIT_FAILED) { + throw rdr::SystemException("failed to wait on Condition", GetLastError()); + } + } + + protected: + Mutex& mutex; + Mutex cond_lock; + Thread* waiting; + }; + +}; + +#endif // __RFB_THREADING_IMPL diff --git a/win/rfb_win32/ToolBar.cxx b/win/rfb_win32/ToolBar.cxx new file mode 100644 index 00000000..6392ebdc --- /dev/null +++ b/win/rfb_win32/ToolBar.cxx @@ -0,0 +1,216 @@ +/* Copyright (C) 2004 TightVNC Team. All Rights Reserved. + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +// -=- ToolBar control class. + +#include "ToolBar.h" + +using namespace rfb::win32; + +ToolBar::ToolBar() : hwndToolBar(0), tbID(-1) { + INITCOMMONCONTROLSEX icex; + + // Ensure that the common control DLL is loaded + icex.dwSize = sizeof(INITCOMMONCONTROLSEX); + icex.dwICC = ICC_BAR_CLASSES; + InitCommonControlsEx(&icex); +} + +ToolBar::~ToolBar() { + DestroyWindow(getHandle()); +} + +bool ToolBar::create(int _tbID, HWND _parentHwnd, DWORD dwStyle) { + parentHwnd = _parentHwnd; + dwStyle |= WS_CHILD; + + // Create the ToolBar window + hwndToolBar = CreateWindowEx(0, TOOLBARCLASSNAME, 0, dwStyle, + 0, 0, 25, 25, parentHwnd, (HMENU)_tbID, GetModuleHandle(0), 0); + + if (hwndToolBar) { + tbID = _tbID; + + // It's required for backward compatibility + SendMessage(hwndToolBar, TB_BUTTONSTRUCTSIZE, (WPARAM)sizeof(TBBUTTON), 0); + } + return (hwndToolBar ? true : false); +}; + +int ToolBar::addBitmap(int nButtons, UINT bitmapID) { + assert(nButtons > 0); + TBADDBITMAP resBitmap; + resBitmap.hInst = GetModuleHandle(0); + resBitmap.nID = bitmapID; + return SendMessage(getHandle(), TB_ADDBITMAP, nButtons, (LPARAM)&resBitmap); +} + +int ToolBar::addSystemBitmap(UINT stdBitmapID) { + TBADDBITMAP resBitmap; + resBitmap.hInst = HINST_COMMCTRL; + resBitmap.nID = stdBitmapID; + return SendMessage(getHandle(), TB_ADDBITMAP, 0, (LPARAM)&resBitmap); +} + +bool ToolBar::setBitmapSize(int width, int height) { + int result = SendMessage(getHandle(), TB_SETBITMAPSIZE, + 0, MAKELONG(width, height)); + return (result ? true : false); +} + +bool ToolBar::addButton(int iBitmap, int idCommand, BYTE state, BYTE style, UINT dwData, int iString) { + TBBUTTON tbb; + tbb.iBitmap = iBitmap; + tbb.idCommand = idCommand; + tbb.fsState = state; + tbb.fsStyle = style; + tbb.dwData = dwData; + tbb.iString = iString; + + int result = SendMessage(getHandle(), TB_ADDBUTTONS, 1, (LPARAM)&tbb); + if (result) { + SendMessage(getHandle(), TB_AUTOSIZE, 0, 0); + } + return (result ? true : false); +} + +bool ToolBar::addNButton(int nButtons, LPTBBUTTON tbb) { + assert(nButtons > 0); + assert(tbb > 0); + int result = SendMessage(getHandle(), TB_ADDBUTTONS, nButtons, (LPARAM)tbb); + if (result) { + SendMessage(getHandle(), TB_AUTOSIZE, 0, 0); + } + return (result ? true : false); +} + +bool ToolBar::deleteButton(int indexButton) { + assert(indexButton >= 0); + int result = SendMessage(getHandle(), TB_DELETEBUTTON, indexButton, 0); + + if (result) { + SendMessage(getHandle(), TB_AUTOSIZE, 0, 0); + } + return (result ? true : false); +} + +bool ToolBar::insertButton(int indexButton, LPTBBUTTON tbb) { + assert(indexButton >= 0); + assert(tbb > 0); + int result = SendMessage(getHandle(), TB_INSERTBUTTON, + indexButton, (LPARAM)tbb); + + if (result) { + SendMessage(getHandle(), TB_AUTOSIZE, 0, 0); + } + return (result ? true : false); +} + +int ToolBar::getButtonInfo(int idButton, TBBUTTONINFO *btnInfo) { + assert(idButton >= 0); + assert(btnInfo > 0); + return SendMessage(getHandle(), TB_GETBUTTONINFO, idButton, (LPARAM)btnInfo); +} + +int ToolBar::getButtonsHeight() { + return HIWORD(SendMessage(getHandle(), TB_GETBUTTONSIZE, 0, 0)); +} + +int ToolBar::getButtonsWidth() { + return LOWORD(SendMessage(getHandle(), TB_GETBUTTONSIZE, 0, 0)); +} + +bool ToolBar::setButtonInfo(int idButton, TBBUTTONINFO* btnInfo) { + assert(idButton >= 0); + assert(btnInfo > 0); + int result = SendMessage(getHandle(), TB_SETBUTTONINFO, + idButton, (LPARAM)(LPTBBUTTONINFO)btnInfo); + return (result ? true : false); +} + +bool ToolBar::checkButton(int idButton, bool check) { + assert(idButton >= 0); + int result = SendMessage(getHandle(), TB_CHECKBUTTON, + idButton, MAKELONG(check, 0)); + return (result ? true : false); +} + +bool ToolBar::enableButton(int idButton, bool enable) { + assert(idButton >= 0); + int result = SendMessage(getHandle(), TB_ENABLEBUTTON, + idButton, MAKELONG(enable, 0)); + return (result ? true : false); +} + +bool ToolBar::pressButton(int idButton, bool press) { + assert(idButton >= 0); + int result = SendMessage(getHandle(), TB_PRESSBUTTON, + idButton, MAKELONG(press, 0)); + return (result ? true : false); +} + +bool ToolBar::getButtonRect(int nIndex, LPRECT buttonRect) { + int result = SendMessage(getHandle(), TB_GETITEMRECT, + nIndex, (LPARAM)buttonRect); + return (result ? true : false); +} + +bool ToolBar::setButtonSize(int width, int height) { + assert(width > 0); + assert(height > 0); + int result = SendMessage(getHandle(), TB_SETBUTTONSIZE, + 0, MAKELONG(width, height)); + if (result) { + SendMessage(getHandle(), TB_AUTOSIZE, 0, 0); + return true; + } + return false; +} + +void ToolBar::autoSize() { + DWORD style = SendMessage(getHandle(), TB_GETSTYLE, 0, 0); + if (style & CCS_NORESIZE) { + RECT r, btnRect; + GetClientRect(parentHwnd, &r); + getButtonRect(0, &btnRect); + int height = getButtonsHeight() + btnRect.top * 2 + 2; + SetWindowPos(getHandle(), HWND_TOP, 0, 0, r.right - r.left, height, + SWP_NOMOVE); + } else { + SendMessage(getHandle(), TB_AUTOSIZE, 0, 0); + } +} + +int ToolBar::getHeight() { + RECT r; + GetWindowRect(getHandle(), &r); + return r.bottom - r.top; +} + +void ToolBar::show() { + ShowWindow(getHandle(), SW_SHOW); +} + +void ToolBar::hide() { + ShowWindow(getHandle(), SW_HIDE); +} + +bool ToolBar::isVisible() { + DWORD style = GetWindowLong(getHandle(), GWL_STYLE); + return (bool)(style & WS_VISIBLE); +} diff --git a/win/rfb_win32/ToolBar.h b/win/rfb_win32/ToolBar.h new file mode 100644 index 00000000..2242c2a4 --- /dev/null +++ b/win/rfb_win32/ToolBar.h @@ -0,0 +1,132 @@ +/* Copyright (C) 2004 TightVNC Team. All Rights Reserved. + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +// -=- ToolBar control class. + +#include <windows.h> +#include <commctrl.h> +#include <assert.h> + +namespace rfb { + + namespace win32 { + + class ToolBar { + public: + ToolBar(); + virtual ~ToolBar(); + + // create() creates a windows toolbar. dwStyle is a combination of + // the toolbar control and button styles. It returns TRUE if successful, + // or FALSE otherwise. + bool create(int tbID, HWND parentHwnd, + DWORD dwStyle = WS_CHILD | WS_VISIBLE | TBSTYLE_FLAT); + + // -=- Button images operations + + // addBitmap() adds one or more images from resources to + // the list of button images available for a toolbar. + // Returns the index of the first new image if successful, + // or -1 otherwise. + int addBitmap(int nButtons, UINT bitmapID); + + // addSystemBitmap() adds the system-defined button bitmaps to the list + // of the toolbar button specifying by stdBitmapID. Returns the index of + // the first new image if successful, or -1 otherwise. + int addSystemBitmap(UINT stdBitmapID); + + // setBitmapSize() sets the size of the bitmapped images to be added + // to a toolbar. It returns TRUE if successful, or FALSE otherwise. + // You must call it before addBitmap(). + bool setBitmapSize(int width, int height); + + // -=- Button operations + + // addButton() adds one button. + bool addButton(int iBitmap, int idCommand, BYTE state=TBSTATE_ENABLED, + BYTE style=TBSTYLE_BUTTON, UINT dwData=0, int iString=0); + + // addNButton() adds nButtons buttons to a toolbar. + bool addNButton(int nButtons, LPTBBUTTON tbb); + + // deleteButton() removes a button from the toolbar. + bool deleteButton(int nIndex); + + // insertButton() inserts a button in a toolbar control by index. + bool insertButton(int nIndex, LPTBBUTTON tbb); + + // getButtonInfo() retrieves extended information about a toolbar's + // button. It returns index of the button if successful, or -1 otherwise. + int getButtonInfo(int idButton, TBBUTTONINFO *btnInfo); + + // getButtonsHeight() retrieves the height of the toolbar buttons. + int getButtonsHeight(); + + // getButtonsWidth() retrieves the width of the toolbar buttons. + int getButtonsWidth(); + + // setButtonInfo() sets the information for an existing button + // in a toolbar. + bool setButtonInfo(int idButton, TBBUTTONINFO* ptbbi); + + // checkButton() checks or unchecks a given button in a toolbar control. + bool checkButton(int idButton, bool check); + + // enableButton() enables or disables the specified button + // in the toolbar. + bool enableButton(int idButton, bool enable); + + // pressButton() presses or releases the specified button in the toolbar. + bool pressButton(int idButton, bool press); + + // getButtonRect() gets the bounding rectangle of a button in a toolbar. + bool getButtonRect(int nIndex, LPRECT buttonRect); + + // setButtonSize() sets the size of the buttons to be added to a toolbar. + // Button size must be largen the button bitmap. + bool setButtonSize(int width, int height); + + // -=- ToolBar operations + + // autoSize() resizes the toolbar window. + void autoSize(); + + // getHandle() returns handle to a toolbar window. + HWND getHandle() { return hwndToolBar; } + + // getHeight() returns the toolbar window height. + int getHeight(); + + // show() displays the toolbar window. + void show(); + + // hide() hides the toolbar window. + void hide(); + + // isVisible() check the toolbar window on visible. + bool isVisible(); + + protected: + HWND hwndToolBar; + HWND parentHwnd; + int tbID; + }; + + }; // win32 + +}; // rfb diff --git a/win/rfb_win32/TrayIcon.h b/win/rfb_win32/TrayIcon.h new file mode 100644 index 00000000..dc5102a9 --- /dev/null +++ b/win/rfb_win32/TrayIcon.h @@ -0,0 +1,89 @@ +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +// -=- CView.h + +// An instance of the CView class is created for each VNC Viewer connection. + +#ifndef __RFB_WIN32_TRAY_ICON_H__ +#define __RFB_WIN32_TRAY_ICON_H__ + +#include <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/win/rfb_win32/TsSessions.cxx b/win/rfb_win32/TsSessions.cxx new file mode 100644 index 00000000..efe75640 --- /dev/null +++ b/win/rfb_win32/TsSessions.cxx @@ -0,0 +1,93 @@ +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +#include <rfb_win32/TsSessions.h> +#include <rfb_win32/DynamicFn.h> +#include <rfb/LogWriter.h> +#include <rdr/Exception.h> +#include <tchar.h> + +#ifdef ERROR_CTX_WINSTATION_BUSY +#define RFB_HAVE_WINSTATION_CONNECT +#else +#pragma message(" NOTE: Not building WinStationConnect support.") +#endif + +static rfb::LogWriter vlog("TsSessions"); + +namespace rfb { +namespace win32 { + + // Windows XP (and later) functions used to handle session Ids + typedef BOOLEAN (WINAPI *_WinStationConnect_proto) (HANDLE,ULONG,ULONG,PCWSTR,ULONG); + DynamicFn<_WinStationConnect_proto> _WinStationConnect(_T("winsta.dll"), "WinStationConnectW"); + typedef DWORD (WINAPI *_WTSGetActiveConsoleSessionId_proto) (); + DynamicFn<_WTSGetActiveConsoleSessionId_proto> _WTSGetActiveConsoleSessionId(_T("kernel32.dll"), "WTSGetActiveConsoleSessionId"); + typedef BOOL (WINAPI *_ProcessIdToSessionId_proto) (DWORD, DWORD*); + DynamicFn<_ProcessIdToSessionId_proto> _ProcessIdToSessionId(_T("kernel32.dll"), "ProcessIdToSessionId"); + typedef BOOL (WINAPI *_LockWorkStation_proto)(); + DynamicFn<_LockWorkStation_proto> _LockWorkStation(_T("user32.dll"), "LockWorkStation"); + + + ProcessSessionId::ProcessSessionId(DWORD processId) { + id = 0; + if (!_ProcessIdToSessionId.isValid()) + return; + if (processId == -1) + processId = GetCurrentProcessId(); + if (!(*_ProcessIdToSessionId)(GetCurrentProcessId(), &id)) + throw rdr::SystemException("ProcessIdToSessionId", GetLastError()); + } + + ProcessSessionId mySessionId; + + ConsoleSessionId::ConsoleSessionId() { + if (_WTSGetActiveConsoleSessionId.isValid()) + id = (*_WTSGetActiveConsoleSessionId)(); + else + id = 0; + } + + bool inConsoleSession() { + ConsoleSessionId console; + return console.id == mySessionId.id; + } + + void setConsoleSession(DWORD sessionId) { +#ifdef RFB_HAVE_WINSTATION_CONNECT + if (!_WinStationConnect.isValid()) + throw rdr::Exception("WinSta APIs missing"); + if (sessionId == -1) + sessionId = mySessionId.id; + + // Try to reconnect our session to the console + ConsoleSessionId console; + vlog.info("Console session is %d", console.id); + if (!(*_WinStationConnect)(0, sessionId, console.id, L"", 0)) + throw rdr::SystemException("Unable to connect session to Console", GetLastError()); + + // Lock the newly connected session, for security + if (_LockWorkStation.isValid()) + (*_LockWorkStation)(); +#else + throw rdr::Exception("setConsoleSession not implemented"); +#endif + } + +}; +}; diff --git a/win/rfb_win32/TsSessions.h b/win/rfb_win32/TsSessions.h new file mode 100644 index 00000000..b15ada71 --- /dev/null +++ b/win/rfb_win32/TsSessions.h @@ -0,0 +1,61 @@ +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +// Windows version-independent Terminal Services Session discovery +// and manipulation API. This code will eventually be replaced +// by the full TS-compatibility scheme. + +#ifndef __RFB_WIN32_TSSESSIONS_H__ +#define __RFB_WIN32_TSSESSIONS_H__ + +#include <windows.h> + +namespace rfb { + + namespace win32 { + + struct SessionId { + DWORD id; + }; + + // Session Id for a given process + struct ProcessSessionId : SessionId { + ProcessSessionId(DWORD processId = -1); + }; + + // Session Id for current process + extern ProcessSessionId mySessionId; + + // Current console Session Id + struct ConsoleSessionId : SessionId { + ConsoleSessionId(); + }; + + // Check whether the process is in the Console session at present + bool inConsoleSession(); + + // Make the specified session the Console session. + // If sessionId is -1 then the process' session is + // made the Console session. + void setConsoleSession(DWORD sessionId = -1); + + }; + +}; + +#endif diff --git a/win/rfb_win32/WMCursor.cxx b/win/rfb_win32/WMCursor.cxx new file mode 100644 index 00000000..4d696cbc --- /dev/null +++ b/win/rfb_win32/WMCursor.cxx @@ -0,0 +1,104 @@ +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +// -=- WMCursor.cxx + +// *** DOESN'T SEEM TO WORK WITH GetCursorInfo POS CODE BUILT-IN UNDER NT4SP6 +// *** INSTEAD, WE LOOK FOR Win2000/Win98 OR ABOVE + +#include <rfb_win32/WMCursor.h> +#include <rfb_win32/OSVersion.h> +#include <rfb_win32/DynamicFn.h> +#include <rfb/Exception.h> +#include <rfb/LogWriter.h> + +using namespace rdr; +using namespace rfb; +using namespace rfb::win32; + +static LogWriter vlog("WMCursor"); + + +#ifdef CURSOR_SHOWING +#define RFB_HAVE_GETCURSORINFO +#else +#pragma message(" NOTE: Not building GetCursorInfo support.") +#endif + +#ifdef RFB_HAVE_GETCURSORINFO +typedef BOOL (WINAPI *_GetCursorInfo_proto)(PCURSORINFO pci); +DynamicFn<_GetCursorInfo_proto> _GetCursorInfo(_T("user32.dll"), "GetCursorInfo"); +#endif + +WMCursor::WMCursor() : hooks(0), use_getCursorInfo(false), cursor(0) { +#ifdef RFB_HAVE_GETCURSORINFO + // Check the OS version + bool is_win98 = (osVersion.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS) && + (osVersion.dwMajorVersion > 4) || ((osVersion.dwMajorVersion == 4) && (osVersion.dwMinorVersion > 0)); + bool is_win2K = (osVersion.dwPlatformId == VER_PLATFORM_WIN32_NT) && (osVersion.dwMajorVersion >= 5); + + // Use GetCursorInfo if OS version is sufficient + use_getCursorInfo = (is_win98 || is_win2K) && _GetCursorInfo.isValid(); +#endif + cursor = (HCURSOR)LoadImage(0, IDC_ARROW, IMAGE_CURSOR, 0, 0, LR_DEFAULTSIZE | LR_SHARED); + if (!use_getCursorInfo) { + hooks = new WMCursorHooks(); + if (hooks && hooks->start()) { + vlog.info("falling back to cursor hooking: %p", hooks); + } else { + delete hooks; + hooks = 0; + vlog.error("unable to monitor cursor shape"); + } + } else { + vlog.info("using GetCursorInfo"); + } +} + +WMCursor::~WMCursor() { + vlog.debug("deleting WMCursorHooks (%p)", hooks); + if (hooks) + delete hooks; +} + +WMCursor::Info +WMCursor::getCursorInfo() { + Info result; +#ifdef RFB_HAVE_GETCURSORINFO + if (use_getCursorInfo) { + CURSORINFO info; + info.cbSize = sizeof(CURSORINFO); + if ((*_GetCursorInfo)(&info)) { + result.cursor = info.hCursor; + result.position = Point(info.ptScreenPos.x, info.ptScreenPos.y); + result.visible = info.flags & CURSOR_SHOWING; + return result; + } + } +#endif + // Fall back to the old way of doing things + POINT pos; + if (hooks) + cursor = hooks->getCursor(); + result.cursor = cursor; + result.visible = cursor != 0; + GetCursorPos(&pos); + result.position.x = pos.x; + result.position.y = pos.y; + return result; +} diff --git a/win/rfb_win32/WMCursor.h b/win/rfb_win32/WMCursor.h new file mode 100644 index 00000000..41f9ee84 --- /dev/null +++ b/win/rfb_win32/WMCursor.h @@ -0,0 +1,61 @@ +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +// -=- WMCursor.h + +// WMCursor provides a single API through which the cursor state can be obtained +// The underlying implementation will use either GetCursorInfo, or use the +// wm_hooks library if GetCursorInfo is not available. + +#ifndef __RFB_WIN32_WM_CURSOR_H__ +#define __RFB_WIN32_WM_CURSOR_H__ + +#include <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; + bool use_getCursorInfo; + HCURSOR cursor; + }; + + }; +}; + +#endif // __RFB_WIN32_WM_CURSOR_H__ diff --git a/win/rfb_win32/WMHooks.cxx b/win/rfb_win32/WMHooks.cxx new file mode 100644 index 00000000..2d690538 --- /dev/null +++ b/win/rfb_win32/WMHooks.cxx @@ -0,0 +1,394 @@ +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +// -=- WMHooks.cxx + +#include <rfb_win32/WMHooks.h> +#include <rfb_win32/DynamicFn.h> +#include <rfb_win32/Service.h> +#include <rfb_win32/MsgWindow.h> +#include <rfb_win32/IntervalTimer.h> +#include <rfb/Threading.h> +#include <rfb/LogWriter.h> + +#include <list> + +using namespace rfb; +using namespace rfb::win32; + +static LogWriter vlog("WMHooks"); + + +typedef UINT (*WM_Hooks_WMVAL_proto)(); +typedef BOOL (*WM_Hooks_Install_proto)(DWORD owner, DWORD thread); +typedef BOOL (*WM_Hooks_Remove_proto)(DWORD owner); +typedef BOOL (*WM_Hooks_EnableCursorShape_proto)(BOOL enable); +#ifdef _DEBUG +typedef void (*WM_Hooks_SetDiagnosticRange_proto)(UINT min, UINT max); +DynamicFn<WM_Hooks_SetDiagnosticRange_proto> WM_Hooks_SetDiagnosticRange(_T("wm_hooks.dll"), "WM_Hooks_SetDiagnosticRange"); +#endif + + +class WMHooksThread : public Thread { +public: + WMHooksThread() : Thread("WMHookThread"), active(true), + WM_Hooks_Install(_T("wm_hooks.dll"), "WM_Hooks_Install"), + WM_Hooks_Remove(_T("wm_hooks.dll"), "WM_Hooks_Remove"), + WM_Hooks_EnableCursorShape(_T("wm_hooks.dll"), "WM_Hooks_EnableCursorShape") { + } + virtual void run(); + virtual Thread* join(); + DynamicFn<WM_Hooks_Install_proto> WM_Hooks_Install;; + DynamicFn<WM_Hooks_Remove_proto> WM_Hooks_Remove; + DynamicFn<WM_Hooks_EnableCursorShape_proto> WM_Hooks_EnableCursorShape; +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); + + +static bool StartHookThread() { + if (hook_mgr) + return true; + vlog.debug("creating thread"); + hook_mgr = new WMHooksThread(); + if (!hook_mgr->WM_Hooks_Install.isValid() || + !hook_mgr->WM_Hooks_Remove.isValid()) { + vlog.debug("hooks not available"); + return false; + } + vlog.debug("installing hooks"); + if (!(*hook_mgr->WM_Hooks_Install)(hook_mgr->getThreadId(), 0)) { + vlog.error("failed to initialise hooks"); + delete hook_mgr->join(); + hook_mgr = 0; + return false; + } + vlog.debug("starting thread"); + hook_mgr->start(); + return true; +} + +static void StopHookThread() { + if (!hook_mgr) + return; + if (!hooks.empty() || !cursor_hooks.empty()) + return; + vlog.debug("closing thread"); + delete hook_mgr->join(); + hook_mgr = 0; +} + + +static bool AddHook(WMHooks* hook) { + vlog.debug("adding hook"); + Lock l(hook_mgr_lock); + if (!StartHookThread()) + return false; + hooks.push_back(hook); + return true; +} + +static bool AddCursorHook(WMCursorHooks* hook) { + vlog.debug("adding cursor hook"); + Lock l(hook_mgr_lock); + if (!StartHookThread()) + return false; + if (!hook_mgr->WM_Hooks_EnableCursorShape.isValid()) + return false; + if (cursor_hooks.empty() && !(*hook_mgr->WM_Hooks_EnableCursorShape)(TRUE)) + return false; + cursor_hooks.push_back(hook); + return true; +} + +static bool RemHook(WMHooks* hook) { + { + vlog.debug("removing hook"); + Lock l(hook_mgr_lock); + hooks.remove(hook); + } + StopHookThread(); + return true; +} + +static bool RemCursorHook(WMCursorHooks* hook) { + { + vlog.debug("removing cursor hook"); + Lock l(hook_mgr_lock); + cursor_hooks.remove(hook); + if (hook_mgr->WM_Hooks_EnableCursorShape.isValid() && + cursor_hooks.empty()) + (*hook_mgr->WM_Hooks_EnableCursorShape)(FALSE); + } + StopHookThread(); + return true; +} + +static void NotifyHooksRegion(const Region& r) { + Lock l(hook_mgr_lock); + std::list<WMHooks*>::iterator i; + for (i=hooks.begin(); i!=hooks.end(); i++) + (*i)->NotifyHooksRegion(r); +} + +static void NotifyHooksCursor(HCURSOR c) { + Lock l(hook_mgr_lock); + hook_cursor = c; +} + + +static UINT GetMsgVal(DynamicFn<WM_Hooks_WMVAL_proto>& fn) { + if (fn.isValid()) + return (*fn)(); + return WM_NULL; +} + +void +WMHooksThread::run() { + // Obtain message ids for all supported hook messages + DynamicFn<WM_Hooks_WMVAL_proto> WM_Hooks_WindowChanged(_T("wm_hooks.dll"), "WM_Hooks_WindowChanged"); + DynamicFn<WM_Hooks_WMVAL_proto> WM_Hooks_WindowBorderChanged(_T("wm_hooks.dll"), "WM_Hooks_WindowBorderChanged"); + DynamicFn<WM_Hooks_WMVAL_proto> WM_Hooks_WindowClientAreaChanged(_T("wm_hooks.dll"), "WM_Hooks_WindowClientAreaChanged"); + DynamicFn<WM_Hooks_WMVAL_proto> WM_Hooks_RectangleChanged(_T("wm_hooks.dll"), "WM_Hooks_RectangleChanged"); + DynamicFn<WM_Hooks_WMVAL_proto> WM_Hooks_CursorChanged(_T("wm_hooks.dll"), "WM_Hooks_CursorChanged"); + UINT windowMsg = GetMsgVal(WM_Hooks_WindowChanged); + UINT clientAreaMsg = GetMsgVal(WM_Hooks_WindowClientAreaChanged); + UINT borderMsg = GetMsgVal(WM_Hooks_WindowBorderChanged); + UINT rectangleMsg = GetMsgVal(WM_Hooks_RectangleChanged); + UINT cursorMsg = GetMsgVal(WM_Hooks_CursorChanged); +#ifdef _DEBUG + DynamicFn<WM_Hooks_WMVAL_proto> WM_Hooks_Diagnostic(_T("wm_hooks.dll"), "WM_Hooks_Diagnostic"); + UINT diagnosticMsg = GetMsgVal(WM_Hooks_Diagnostic); +#endif + MSG msg; + RECT wrect; + HWND hwnd; + int count = 0; + + // Update delay handling + // We delay updates by 40-80ms, so that the triggering application has time to + // actually complete them before we notify the hook callbacks & they go off + // capturing screen state. + const int updateDelayMs = 40; + MsgWindow updateDelayWnd(_T("WMHooks::updateDelay")); + IntervalTimer updateDelayTimer(updateDelayWnd.getHandle(), 1); + Region updates[2]; + int activeRgn = 0; + + vlog.debug("starting hook thread"); + + while (active && GetMessage(&msg, NULL, 0, 0)) { + count++; + + if (msg.message == WM_TIMER) { + // Actually notify callbacks of graphical updates + NotifyHooksRegion(updates[1-activeRgn]); + if (updates[activeRgn].is_empty()) + updateDelayTimer.stop(); + activeRgn = 1-activeRgn; + updates[activeRgn].clear(); + + } else if (msg.message == windowMsg) { + // An entire window has (potentially) changed + hwnd = (HWND) msg.lParam; + if (IsWindow(hwnd) && IsWindowVisible(hwnd) && !IsIconic(hwnd) && + GetWindowRect(hwnd, &wrect) && !IsRectEmpty(&wrect)) { + updates[activeRgn].assign_union(Rect(wrect.left, wrect.top, + wrect.right, wrect.bottom)); + updateDelayTimer.start(updateDelayMs); + } + + } else if (msg.message == clientAreaMsg) { + // The client area of a window has (potentially) changed + hwnd = (HWND) msg.lParam; + if (IsWindow(hwnd) && IsWindowVisible(hwnd) && !IsIconic(hwnd) && + GetClientRect(hwnd, &wrect) && !IsRectEmpty(&wrect)) + { + POINT pt = {0,0}; + if (ClientToScreen(hwnd, &pt)) { + updates[activeRgn].assign_union(Rect(wrect.left+pt.x, wrect.top+pt.y, + wrect.right+pt.x, wrect.bottom+pt.y)); + updateDelayTimer.start(updateDelayMs); + } + } + + } else if (msg.message == borderMsg) { + hwnd = (HWND) msg.lParam; + if (IsWindow(hwnd) && IsWindowVisible(hwnd) && !IsIconic(hwnd) && + GetWindowRect(hwnd, &wrect) && !IsRectEmpty(&wrect)) + { + Region changed(Rect(wrect.left, wrect.top, wrect.right, wrect.bottom)); + RECT crect; + POINT pt = {0,0}; + if (GetClientRect(hwnd, &crect) && ClientToScreen(hwnd, &pt) && + !IsRectEmpty(&crect)) + { + changed.assign_subtract(Rect(crect.left+pt.x, crect.top+pt.y, + crect.right+pt.x, crect.bottom+pt.y)); + } + if (!changed.is_empty()) { + updates[activeRgn].assign_union(changed); + updateDelayTimer.start(updateDelayMs); + } + } + } else if (msg.message == rectangleMsg) { + Rect r = Rect(LOWORD(msg.wParam), HIWORD(msg.wParam), + LOWORD(msg.lParam), HIWORD(msg.lParam)); + if (!r.is_empty()) { + updates[activeRgn].assign_union(r); + updateDelayTimer.start(updateDelayMs); + } + + } else if (msg.message == cursorMsg) { + NotifyHooksCursor((HCURSOR)msg.lParam); +#ifdef _DEBUG + } else if (msg.message == diagnosticMsg) { + vlog.info("DIAG msg=%x(%d) wnd=%lx", msg.wParam, msg.wParam, msg.lParam); +#endif + } + } + + vlog.debug("stopping hook thread - processed %d events", count); + (*WM_Hooks_Remove)(getThreadId()); +} + +Thread* +WMHooksThread::join() { + vlog.debug("stopping WMHooks thread"); + active = false; + PostThreadMessage(thread_id, WM_QUIT, 0, 0); + vlog.debug("joining WMHooks thread"); + return Thread::join(); +} + +// -=- WMHooks class + +rfb::win32::WMHooks::WMHooks() : updateEvent(0) { +} + +rfb::win32::WMHooks::~WMHooks() { + RemHook(this); +} + +bool rfb::win32::WMHooks::setEvent(HANDLE ue) { + if (updateEvent) + RemHook(this); + updateEvent = ue; + return AddHook(this); +} + +bool rfb::win32::WMHooks::getUpdates(UpdateTracker* ut) { + if (!updatesReady) return false; + Lock l(hook_mgr_lock); + updates.copyTo(ut); + updates.clear(); + updatesReady = false; + return true; +} + +bool rfb::win32::WMHooks::areAvailable() { + WMHooksThread wmht; + return wmht.WM_Hooks_Install.isValid(); +} + +#ifdef _DEBUG +void +rfb::win32::WMHooks::setDiagnosticRange(UINT min, UINT max) { + if (WM_Hooks_SetDiagnosticRange.isValid()) + (*WM_Hooks_SetDiagnosticRange)(min, max); +} +#endif + +void rfb::win32::WMHooks::NotifyHooksRegion(const Region& r) { + // hook_mgr_lock is already held at this point + updates.add_changed(r); + updatesReady = true; + SetEvent(updateEvent); +} + + +// -=- WMBlockInput class + +rfb::win32::WMBlockInput::WMBlockInput() : active(false) { +} + +rfb::win32::WMBlockInput::~WMBlockInput() { + blockInputs(false); +} + +typedef BOOL (*WM_Hooks_EnableRealInputs_proto)(BOOL pointer, BOOL keyboard); +DynamicFn<WM_Hooks_EnableRealInputs_proto>* WM_Hooks_EnableRealInputs = 0; +static bool blockRealInputs(bool block_) { + // NB: Requires blockMutex to be held! + if (block_) { + if (WM_Hooks_EnableRealInputs) + return true; + // Enable blocking + WM_Hooks_EnableRealInputs = new DynamicFn<WM_Hooks_EnableRealInputs_proto>(_T("wm_hooks.dll"), "WM_Hooks_EnableRealInputs"); + if (WM_Hooks_EnableRealInputs->isValid() && (**WM_Hooks_EnableRealInputs)(false, false)) + return true; + } + if (WM_Hooks_EnableRealInputs) { + // Clean up the DynamicFn, either if init failed, or block_ is false + if (WM_Hooks_EnableRealInputs->isValid()) + (**WM_Hooks_EnableRealInputs)(true, true); + delete WM_Hooks_EnableRealInputs; + WM_Hooks_EnableRealInputs = 0; + } + return block_ == (WM_Hooks_EnableRealInputs != 0); +} + +Mutex blockMutex; +int blockCount = 0; + +bool rfb::win32::WMBlockInput::blockInputs(bool on) { + if (active == on) return true; + Lock l(blockMutex); + int newCount = on ? blockCount+1 : blockCount-1; + if (!blockRealInputs(newCount > 0)) + return false; + blockCount = newCount; + active = on; + return true; +} + + +// -=- WMCursorHooks class + +rfb::win32::WMCursorHooks::WMCursorHooks() { +} + +rfb::win32::WMCursorHooks::~WMCursorHooks() { + RemCursorHook(this); +} + +bool +rfb::win32::WMCursorHooks::start() { + return AddCursorHook(this); +} + +HCURSOR +rfb::win32::WMCursorHooks::getCursor() const { + return hook_cursor; +} diff --git a/win/rfb_win32/WMHooks.h b/win/rfb_win32/WMHooks.h new file mode 100644 index 00000000..4713b416 --- /dev/null +++ b/win/rfb_win32/WMHooks.h @@ -0,0 +1,92 @@ +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +// -=- WMHooks.h + +#ifndef __RFB_WIN32_WM_HOOKS_H__ +#define __RFB_WIN32_WM_HOOKS_H__ + +#include <windows.h> +#include <rfb/UpdateTracker.h> +#include <rdr/Exception.h> +#include <rfb_win32/Win32Util.h> + +namespace rfb { + + namespace win32 { + + // -=- WMHooks + // Uses the wm_hooks DLL to intercept window messages, to get _hints_ as + // to what may have changed on-screen. Updates are notified via a Win32 + // event, and retrieved using the getUpdates method, which is thread-safe. + class WMHooks { + public: + WMHooks(); + ~WMHooks(); + + // Specify the event object to notify. Starts the hook subsystem if it is + // not already active, and returns false if the hooks fail to start. + bool setEvent(HANDLE updateEvent); + + // Copies any new updates to the UpdateTracker. Returns true if new updates + // were added, false otherwise. + bool getUpdates(UpdateTracker* ut); + + // Determine whether the hooks DLL is installed on the system + static bool areAvailable(); + +#ifdef _DEBUG + // Get notifications of any messages in the given range, to any hooked window + void setDiagnosticRange(UINT min, UINT max); +#endif + + // * INTERNAL NOTIFICATION FUNCTION * + void NotifyHooksRegion(const Region& r); + protected: + HANDLE updateEvent; + bool updatesReady; + SimpleUpdateTracker updates; + }; + + // -=- Support for filtering out local input events while remote connections are + // active. Implemented using SetWindowsHookEx for portability. + class WMBlockInput { + public: + WMBlockInput(); + ~WMBlockInput(); + bool blockInputs(bool block); + protected: + bool active; + }; + + // - Legacy cursor handling support + class WMCursorHooks { + public: + WMCursorHooks(); + ~WMCursorHooks(); + + bool start(); + + HCURSOR getCursor() const; + }; + + }; + +}; + +#endif // __RFB_WIN32_WM_HOOKS_H__ diff --git a/win/rfb_win32/WMNotifier.cxx b/win/rfb_win32/WMNotifier.cxx new file mode 100644 index 00000000..20a5445f --- /dev/null +++ b/win/rfb_win32/WMNotifier.cxx @@ -0,0 +1,57 @@ +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +// -=- WMNotifier.cxx + +#include <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")), notifier(0) { +} + +WMMonitor::~WMMonitor() { +} + + +LRESULT +WMMonitor::processMessage(UINT msg, WPARAM wParam, LPARAM lParam) { + switch (msg) { + case WM_DISPLAYCHANGE: + if (notifier) { + notifier->notifyDisplayEvent(Notifier::DisplaySizeChanged); + notifier->notifyDisplayEvent(Notifier::DisplayPixelFormatChanged); + } + break; + case WM_SYSCOLORCHANGE: + case WM_PALETTECHANGED: + if (notifier) { + notifier->notifyDisplayEvent(Notifier::DisplayColourMapChanged); + } + break; + }; + return MsgWindow::processMessage(msg, wParam, lParam); +} diff --git a/win/rfb_win32/WMNotifier.h b/win/rfb_win32/WMNotifier.h new file mode 100644 index 00000000..a7609642 --- /dev/null +++ b/win/rfb_win32/WMNotifier.h @@ -0,0 +1,68 @@ +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +// -=- WMNotifier.h +// +// The WMNotifier is used to get callbacks indicating changes in the state +// of the system, for instance in the size/format/palette of the display. +// The WMNotifier contains a Win32 window, which receives notifications of +// system events and stores them. Whenever processEvent is called, any +// incoming events are processed and the appropriate notifier called. + +#ifndef __RFB_WIN32_NOTIFIER_H__ +#define __RFB_WIN32_NOTIFIER_H__ + +#include <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/win/rfb_win32/WMPoller.cxx b/win/rfb_win32/WMPoller.cxx new file mode 100644 index 00000000..f8505342 --- /dev/null +++ b/win/rfb_win32/WMPoller.cxx @@ -0,0 +1,85 @@ +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +// -=- WMPoller.cxx + +#include <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 + +bool +rfb::win32::WMPoller::processEvent() { + PollInfo info; + if (poll_console_windows && ut) { + ::EnumWindows(WMPoller::enumWindowProc, (LPARAM) &info); + ut->add_changed(info.poll_include); + } + return false; +} + +bool +rfb::win32::WMPoller::setUpdateTracker(UpdateTracker* ut_) { + ut = ut_; + return true; +} + +bool +rfb::win32::WMPoller::checkPollWindow(HWND w) { + TCHAR buffer[128]; + if (!GetClassName(w, buffer, 128)) + throw rdr::SystemException("unable to get window class:%u", GetLastError()); + if ((_tcscmp(buffer, _T("tty")) != 0) && + (_tcscmp(buffer, _T("ConsoleWindowClass")) != 0)) { + return false; + } + return true; +} + +void +rfb::win32::WMPoller::pollWindow(HWND w, PollInfo* i) { + RECT r; + if (IsWindowVisible(w) && GetWindowRect(w, &r)) { + if (IsRectEmpty(&r)) return; + Region wrgn(Rect(r.left, r.top, r.right, r.bottom)); + if (checkPollWindow(w)) { + wrgn.assign_subtract(i->poll_exclude); + i->poll_include.assign_union(wrgn); + } else { + i->poll_exclude.assign_union(wrgn); + } + } +} + +BOOL CALLBACK +rfb::win32::WMPoller::enumWindowProc(HWND w, LPARAM lp) { + pollWindow(w, (PollInfo*)lp); + return TRUE; +} diff --git a/win/rfb_win32/WMPoller.h b/win/rfb_win32/WMPoller.h new file mode 100644 index 00000000..851b69f4 --- /dev/null +++ b/win/rfb_win32/WMPoller.h @@ -0,0 +1,62 @@ +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +// -=- WMPoller.h +// +// Polls the foreground window. If the pollOnlyConsoles flag is set, +// then checks the window class of the foreground window first and +// only polls it if it's a console. +// If the pollAllWindows flag is set then iterates through visible +// windows, and polls the visible bits. If pollOnlyConsoles is also +// set then only visible parts of console windows will be polled. + +#ifndef __RFB_WIN32_WM_POLLER_H__ +#define __RFB_WIN32_WM_POLLER_H__ + +#include <windows.h> +#include <rfb/UpdateTracker.h> +#include <rfb/Configuration.h> + +namespace rfb { + + namespace win32 { + + class WMPoller { + public: + WMPoller() : ut(0) {} + + bool processEvent(); + bool setUpdateTracker(UpdateTracker* ut); + + static BoolParameter poll_console_windows; + protected: + struct PollInfo { + Region poll_include; + Region poll_exclude; + }; + static bool checkPollWindow(HWND w); + static void pollWindow(HWND w, PollInfo* info); + static BOOL CALLBACK enumWindowProc(HWND w, LPARAM lp); + UpdateTracker* ut; + }; + + }; + +}; + +#endif // __RFB_WIN32_WM_POLLER_H__ diff --git a/win/rfb_win32/WMShatter.cxx b/win/rfb_win32/WMShatter.cxx new file mode 100644 index 00000000..e68abfb1 --- /dev/null +++ b/win/rfb_win32/WMShatter.cxx @@ -0,0 +1,57 @@ +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +// -=- WMShatter.cxx + +#include <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/win/rfb_win32/WMShatter.h b/win/rfb_win32/WMShatter.h new file mode 100644 index 00000000..3ea63b1a --- /dev/null +++ b/win/rfb_win32/WMShatter.h @@ -0,0 +1,50 @@ +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +// -=- WMShatter.h +// +// WMShatter provides the IsSafeWM routine, which returns true iff the +// supplied window message is safe to pass to DispatchMessage, or to +// process in the window procedure. +// +// This is only required, of course, to avoid so-called "shatter" attacks +// to be made against the VNC server, which take advantage of the noddy +// design of the Win32 window messaging system. +// +// The API here is designed to hopefully be future proof, so that if they +// ever come up with a proper way to determine whether a message is safe +// or not then it can just be reimplemented here... + +#ifndef __RFB_WIN32_SHATTER_H__ +#define __RFB_WIN32_SHATTER_H__ + +#include <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/win/rfb_win32/WMWindowCopyRect.cxx b/win/rfb_win32/WMWindowCopyRect.cxx new file mode 100644 index 00000000..63c1da2e --- /dev/null +++ b/win/rfb_win32/WMWindowCopyRect.cxx @@ -0,0 +1,67 @@ +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +// -=- WMCopyRect.cxx + +#include <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() : ut(0), fg_window(0) { +} + +bool +rfb::win32::WMCopyRect::processEvent() { + // See if the foreground window has moved + HWND window = GetForegroundWindow(); + if (window) { + RECT wrect; + if (IsWindow(window) && IsWindowVisible(window) && GetWindowRect(window, &wrect)) { + Rect winrect(wrect.left, wrect.top, wrect.right, wrect.bottom); + if (fg_window == window) { + if (!fg_window_rect.tl.equals(winrect.tl) && ut) { + // Window has moved - send a copyrect event to the client + Point delta = Point(winrect.tl.x-fg_window_rect.tl.x, winrect.tl.y-fg_window_rect.tl.y); + Region copy_dest = winrect; + ut->add_copied(copy_dest, delta); + ut->add_changed(Region(fg_window_rect).subtract(copy_dest)); + } + } + fg_window = window; + fg_window_rect = winrect; + } else { + fg_window = 0; + } + } else { + fg_window = 0; + } + return false; +} + +bool +rfb::win32::WMCopyRect::setUpdateTracker(UpdateTracker* ut_) { + ut = ut_; + return true; +} diff --git a/win/rfb_win32/WMWindowCopyRect.h b/win/rfb_win32/WMWindowCopyRect.h new file mode 100644 index 00000000..5a0e876d --- /dev/null +++ b/win/rfb_win32/WMWindowCopyRect.h @@ -0,0 +1,53 @@ +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +// -=- WMWindowCopyRect.h +// +// Helper class which produces copyRect actions by monitoring the location +// of the current foreground window. +// Whenever processEvent is called, the foreground window's position is +// recalculated and a copy event flushed to the supplied UpdateTracker +// if appropriate. + +#ifndef __RFB_WIN32_WM_WINDOW_COPYRECT_H__ +#define __RFB_WIN32_WM_WINDOW_COPYRECT_H__ + +#include <rfb/UpdateTracker.h> + +namespace rfb { + + namespace win32 { + + class WMCopyRect { + public: + WMCopyRect(); + + bool processEvent(); + bool setUpdateTracker(UpdateTracker* ut); + + protected: + UpdateTracker* ut; + void* fg_window; + Rect fg_window_rect; + }; + + }; + +}; + +#endif // __RFB_WIN32_WM_WINDOW_COPYRECT_H__ diff --git a/win/rfb_win32/Win32Util.cxx b/win/rfb_win32/Win32Util.cxx new file mode 100644 index 00000000..ef8039ab --- /dev/null +++ b/win/rfb_win32/Win32Util.cxx @@ -0,0 +1,114 @@ +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +// Win32Util.cxx + +#include <rfb_win32/ModuleFileName.h> +#include <rfb_win32/Win32Util.h> +#include <rfb_win32/MonitorInfo.h> +#include <rfb_win32/Handle.h> +#include <rdr/HexOutStream.h> +#include <rdr/Exception.h> + +namespace rfb { +namespace win32 { + + +FileVersionInfo::FileVersionInfo(const TCHAR* filename) { + // Get executable name + ModuleFileName exeName; + if (!filename) + filename = exeName.buf; + + // Attempt to open the file, to cause Access Denied, etc, errors + // to be correctly reported, since the GetFileVersionInfoXXX calls lie... + { + Handle file(CreateFile(filename, GENERIC_READ, FILE_SHARE_READ, 0, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0)); + if (file.h == INVALID_HANDLE_VALUE) + throw rdr::SystemException("Failed to open file", GetLastError()); + } + + // Get version info size + DWORD handle; + int size = GetFileVersionInfoSize((TCHAR*)filename, &handle); + if (!size) + throw rdr::SystemException("GetVersionInfoSize failed", GetLastError()); + + // Get version info + buf = new TCHAR[size]; + if (!GetFileVersionInfo((TCHAR*)filename, handle, size, buf)) + throw rdr::SystemException("GetVersionInfo failed", GetLastError()); +} + +const TCHAR* FileVersionInfo::getVerString(const TCHAR* name, DWORD langId) { + char langIdBuf[sizeof(langId)]; + for (int i=sizeof(langIdBuf)-1; i>=0; i--) { + langIdBuf[i] = (char) (langId & 0xff); + langId = langId >> 8; + } + + TCharArray langIdStr = rdr::HexOutStream::binToHexStr(langIdBuf, sizeof(langId)); + TCharArray infoName(_tcslen(_T("StringFileInfo")) + 4 + _tcslen(name) + _tcslen(langIdStr.buf)); + _stprintf(infoName.buf, _T("\\StringFileInfo\\%s\\%s"), langIdStr.buf, name); + + // Locate the required version string within the version info + TCHAR* buffer = 0; + UINT length = 0; + if (!VerQueryValue(buf, infoName.buf, (void**)&buffer, &length)) { + printf("unable to find %s version string", CStr(infoName.buf)); + throw rdr::Exception("VerQueryValue failed"); + } + return buffer; +} + + +bool splitPath(const TCHAR* path, TCHAR** dir, TCHAR** file) { + return tstrSplit(path, '\\', dir, file, true); +} + + +void centerWindow(HWND handle, HWND parent) { + RECT r; + MonitorInfo mi(parent ? parent : handle); + if (!parent || !IsWindowVisible(parent) || !GetWindowRect(parent, &r)) + r=mi.rcWork; + centerWindow(handle, r); + mi.clipTo(handle); +} + +void centerWindow(HWND handle, const RECT& r) { + RECT wr; + if (!GetWindowRect(handle, &wr)) return; + int w = wr.right-wr.left; + int h = wr.bottom-wr.top; + int x = (r.left + r.right - w)/2; + int y = (r.top + r.bottom - h)/2; + UINT flags = SWP_NOACTIVATE | SWP_NOOWNERZORDER | SWP_NOZORDER | SWP_NOSIZE; + SetWindowPos(handle, 0, x, y, 0, 0, flags); +} + +void resizeWindow(HWND handle, int width, int height) { + RECT r; + GetWindowRect(handle, &r); + SetWindowPos(handle, 0, 0, 0, width, height, SWP_NOACTIVATE | SWP_NOOWNERZORDER | SWP_NOZORDER | SWP_NOMOVE); + centerWindow(handle, r); +} + + +}; +}; diff --git a/win/rfb_win32/Win32Util.h b/win/rfb_win32/Win32Util.h new file mode 100644 index 00000000..8cc1a2b9 --- /dev/null +++ b/win/rfb_win32/Win32Util.h @@ -0,0 +1,55 @@ +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +// -=- Win32Util.h + +// Miscellaneous but useful Win32 API utility functions & classes. +// In particular, a set of classes which wrap GDI objects, +// and some to handle palettes. + +#ifndef __RFB_WIN32_GDIUTIL_H__ +#define __RFB_WIN32_GDIUTIL_H__ + +#include <rfb_win32/TCharArray.h> + +namespace rfb { + + namespace win32 { + + struct FileVersionInfo : public TCharArray { + FileVersionInfo(const TCHAR* filename=0); + const TCHAR* getVerString(const TCHAR* name, DWORD langId = 0x080904b0); + }; + + bool splitPath(const TCHAR* path, TCHAR** dir, TCHAR** file); + + // Center the window to a rectangle, or to a parent window. + // Optionally, resize the window to lay within the rect or parent window + // If the parent window is NULL then the working area if the window's + // current monitor is used instead. + void centerWindow(HWND handle, const RECT& r); + void centerWindow(HWND handle, HWND parent); + + // resizeWindow resizes a window about its center + void resizeWindow(HWND handle, int width, int height); + + }; + +}; + +#endif diff --git a/win/rfb_win32/keymap.h b/win/rfb_win32/keymap.h new file mode 100644 index 00000000..a340d09d --- /dev/null +++ b/win/rfb_win32/keymap.h @@ -0,0 +1,149 @@ +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +// keymap.h - this file is shared between SInput.cxx and CKeyboard.cxx +// +// Mapping of X keysyms to and from Windows VK codes. Ordering here must be +// such that when we look up a Windows VK code we get the preferred X keysym. +// Going the other way there is no problem because an X keysym always maps to +// exactly one Windows VK code. This map only contain keys which are not the +// normal keys for printable ASCII characters. For example it does not contain +// VK_SPACE (note that things like VK_ADD are for the plus key on the keypad, +// not on the main keyboard). + +struct keymap_t { + rdr::U32 keysym; + rdr::U8 vk; + bool extended; +}; + +static keymap_t keymap[] = { + + { XK_BackSpace, VK_BACK, 0 }, + { XK_Tab, VK_TAB, 0 }, + { XK_Clear, VK_CLEAR, 0 }, + { XK_Return, VK_RETURN, 0 }, + { XK_Pause, VK_PAUSE, 0 }, + { XK_Escape, VK_ESCAPE, 0 }, + { XK_Delete, VK_DELETE, 1 }, + + // Cursor control & motion + + { XK_Home, VK_HOME, 1 }, + { XK_Left, VK_LEFT, 1 }, + { XK_Up, VK_UP, 1 }, + { XK_Right, VK_RIGHT, 1 }, + { XK_Down, VK_DOWN, 1 }, + { XK_Page_Up, VK_PRIOR, 1 }, + { XK_Page_Down, VK_NEXT, 1 }, + { XK_End, VK_END, 1 }, + + // Misc functions + + { XK_Select, VK_SELECT, 0 }, + { XK_Print, VK_SNAPSHOT, 0 }, + { XK_Execute, VK_EXECUTE, 0 }, + { XK_Insert, VK_INSERT, 1 }, + { XK_Help, VK_HELP, 0 }, + { XK_Break, VK_CANCEL, 1 }, + + // Auxilliary Functions - must come before XK_KP_F1, etc + + { XK_F1, VK_F1, 0 }, + { XK_F2, VK_F2, 0 }, + { XK_F3, VK_F3, 0 }, + { XK_F4, VK_F4, 0 }, + { XK_F5, VK_F5, 0 }, + { XK_F6, VK_F6, 0 }, + { XK_F7, VK_F7, 0 }, + { XK_F8, VK_F8, 0 }, + { XK_F9, VK_F9, 0 }, + { XK_F10, VK_F10, 0 }, + { XK_F11, VK_F11, 0 }, + { XK_F12, VK_F12, 0 }, + { XK_F13, VK_F13, 0 }, + { XK_F14, VK_F14, 0 }, + { XK_F15, VK_F15, 0 }, + { XK_F16, VK_F16, 0 }, + { XK_F17, VK_F17, 0 }, + { XK_F18, VK_F18, 0 }, + { XK_F19, VK_F19, 0 }, + { XK_F20, VK_F20, 0 }, + { XK_F21, VK_F21, 0 }, + { XK_F22, VK_F22, 0 }, + { XK_F23, VK_F23, 0 }, + { XK_F24, VK_F24, 0 }, + + // Keypad Functions, keypad numbers + + { XK_KP_Tab, VK_TAB, 0 }, + { XK_KP_Enter, VK_RETURN, 1 }, + { XK_KP_F1, VK_F1, 0 }, + { XK_KP_F2, VK_F2, 0 }, + { XK_KP_F3, VK_F3, 0 }, + { XK_KP_F4, VK_F4, 0 }, + { XK_KP_Home, VK_HOME, 0 }, + { XK_KP_Left, VK_LEFT, 0 }, + { XK_KP_Up, VK_UP, 0 }, + { XK_KP_Right, VK_RIGHT, 0 }, + { XK_KP_Down, VK_DOWN, 0 }, + { XK_KP_End, VK_END, 0 }, + { XK_KP_Page_Up, VK_PRIOR, 0 }, + { XK_KP_Page_Down, VK_NEXT, 0 }, + { XK_KP_Begin, VK_CLEAR, 0 }, + { XK_KP_Insert, VK_INSERT, 0 }, + { XK_KP_Delete, VK_DELETE, 0 }, + { XK_KP_Multiply, VK_MULTIPLY, 0 }, + { XK_KP_Add, VK_ADD, 0 }, + { XK_KP_Separator, VK_SEPARATOR, 0 }, + { XK_KP_Subtract, VK_SUBTRACT, 0 }, + { XK_KP_Decimal, VK_DECIMAL, 0 }, + { XK_KP_Divide, VK_DIVIDE, 1 }, + + { XK_KP_0, VK_NUMPAD0, 0 }, + { XK_KP_1, VK_NUMPAD1, 0 }, + { XK_KP_2, VK_NUMPAD2, 0 }, + { XK_KP_3, VK_NUMPAD3, 0 }, + { XK_KP_4, VK_NUMPAD4, 0 }, + { XK_KP_5, VK_NUMPAD5, 0 }, + { XK_KP_6, VK_NUMPAD6, 0 }, + { XK_KP_7, VK_NUMPAD7, 0 }, + { XK_KP_8, VK_NUMPAD8, 0 }, + { XK_KP_9, VK_NUMPAD9, 0 }, + + // Modifiers + + { XK_Shift_L, VK_SHIFT, 0 }, + { XK_Shift_R, VK_SHIFT, 0 }, + { XK_Control_L, VK_CONTROL, 0 }, + { XK_Control_R, VK_CONTROL, 1 }, + { XK_Alt_L, VK_MENU, 0 }, + { XK_Alt_R, VK_MENU, 1 }, + + // Left & Right Windows keys & Windows Menu Key + + { XK_Super_L, VK_LWIN, 0 }, + { XK_Super_R, VK_RWIN, 0 }, + { XK_Menu, VK_APPS, 0 }, + + // Japanese stuff - almost certainly wrong... + + { XK_Kanji, VK_KANJI, 0 }, + { XK_Kana_Shift, VK_KANA, 0 }, + +}; diff --git a/win/rfb_win32/rfb_win32.dsp b/win/rfb_win32/rfb_win32.dsp new file mode 100644 index 00000000..acfb5aaf --- /dev/null +++ b/win/rfb_win32/rfb_win32.dsp @@ -0,0 +1,513 @@ +# Microsoft Developer Studio Project File - Name="rfb_win32" - Package Owner=<4>
+# Microsoft Developer Studio Generated Build File, Format Version 6.00
+# ** DO NOT EDIT **
+
+# TARGTYPE "Win32 (x86) Static Library" 0x0104
+
+CFG=rfb_win32 - Win32 Debug Unicode
+!MESSAGE This is not a valid makefile. To build this project using NMAKE,
+!MESSAGE use the Export Makefile command and run
+!MESSAGE
+!MESSAGE NMAKE /f "rfb_win32.mak".
+!MESSAGE
+!MESSAGE You can specify a configuration when running NMAKE
+!MESSAGE by defining the macro CFG on the command line. For example:
+!MESSAGE
+!MESSAGE NMAKE /f "rfb_win32.mak" CFG="rfb_win32 - Win32 Debug Unicode"
+!MESSAGE
+!MESSAGE Possible choices for configuration are:
+!MESSAGE
+!MESSAGE "rfb_win32 - Win32 Release" (based on "Win32 (x86) Static Library")
+!MESSAGE "rfb_win32 - Win32 Debug" (based on "Win32 (x86) Static Library")
+!MESSAGE "rfb_win32 - Win32 Debug Unicode" (based on "Win32 (x86) Static Library")
+!MESSAGE
+
+# Begin Project
+# PROP AllowPerConfigDependencies 0
+# PROP Scc_ProjName ""
+# PROP Scc_LocalPath ""
+CPP=cl.exe
+RSC=rc.exe
+
+!IF "$(CFG)" == "rfb_win32 - Win32 Release"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 0
+# PROP BASE Output_Dir "Release"
+# PROP BASE Intermediate_Dir "Release"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 0
+# PROP Output_Dir "..\Release"
+# PROP Intermediate_Dir "..\Release\rfb_win32"
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /YX /FD /c
+# ADD CPP /nologo /MT /W3 /GX /O2 /I ".." /I "../../common" /FI"rdr/msvcwarning.h" /D "NDEBUG" /D "_MBCS" /D "_LIB" /D "WIN32" /YX /FD /c
+# ADD BASE RSC /l 0x809 /d "NDEBUG"
+# ADD RSC /l 0x809 /d "NDEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LIB32=link.exe -lib
+# ADD BASE LIB32 /nologo
+# ADD LIB32 /nologo
+
+!ELSEIF "$(CFG)" == "rfb_win32 - Win32 Debug"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 1
+# PROP BASE Output_Dir "Debug"
+# PROP BASE Intermediate_Dir "Debug"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 1
+# PROP Output_Dir "..\Debug"
+# PROP Intermediate_Dir "..\Debug\rfb_win32"
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /YX /FD /GZ /c
+# ADD CPP /nologo /MTd /W3 /Gm /GX /Zi /Od /I ".." /I "../../common" /FI"rdr/msvcwarning.h" /D "_DEBUG" /D "_MBCS" /D "_LIB" /D "WIN32" /YX /FD /GZ /c
+# ADD BASE RSC /l 0x809 /d "_DEBUG"
+# ADD RSC /l 0x809 /d "_DEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LIB32=link.exe -lib
+# ADD BASE LIB32 /nologo
+# ADD LIB32 /nologo
+
+!ELSEIF "$(CFG)" == "rfb_win32 - Win32 Debug Unicode"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 1
+# PROP BASE Output_Dir "rfb_win32___Win32_Debug_Unicode"
+# PROP BASE Intermediate_Dir "rfb_win32___Win32_Debug_Unicode"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 1
+# PROP Output_Dir "..\Debug_Unicode"
+# PROP Intermediate_Dir "..\Debug_Unicode\rfb_win32"
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /MTd /W3 /Gm /GX /Zi /Od /I ".." /FI"msvcwarning.h" /D "_DEBUG" /D "_LIB" /D "WIN32" /D "_MBCS" /YX /FD /GZ /c
+# ADD CPP /nologo /MTd /W3 /Gm /GX /Zi /Od /I ".." /I "../../common" /FI"rdr/msvcwarning.h" /D "_DEBUG" /D "_UNICODE" /D "UNICODE" /D "_LIB" /D "WIN32" /YX /FD /GZ /c
+# ADD BASE RSC /l 0x809 /d "_DEBUG"
+# ADD RSC /l 0x809 /d "_DEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LIB32=link.exe -lib
+# ADD BASE LIB32 /nologo
+# ADD LIB32 /nologo
+
+!ENDIF
+
+# Begin Target
+
+# Name "rfb_win32 - Win32 Release"
+# Name "rfb_win32 - Win32 Debug"
+# Name "rfb_win32 - Win32 Debug Unicode"
+# Begin Group "Source Files"
+
+# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat"
+# Begin Source File
+
+SOURCE=.\AboutDialog.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\CKeyboard.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\CleanDesktop.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\Clipboard.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\CPointer.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\CurrentUser.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\DeviceContext.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\DeviceFrameBuffer.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\Dialog.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\DIBSectionBuffer.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\DynamicFn.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\EventManager.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\FolderManager.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\LaunchProcess.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\ListViewControl.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\LowLevelKeyEvents.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\MonitorInfo.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\MsgWindow.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\OSVersion.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\ProgressControl.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\RegConfig.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\Registry.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\ScaledDIBSectionBuffer.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\SDisplay.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\SDisplayCorePolling.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\SDisplayCoreWMHooks.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\Security.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\Service.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\SFileTransferManagerWin32.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\SFileTransferWin32.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\SInput.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\SocketManager.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\TCharArray.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\Threading.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\ToolBar.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\TsSessions.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\Win32Util.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\WMCursor.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\WMHooks.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\WMNotifier.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\WMPoller.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\WMShatter.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\WMWindowCopyRect.cxx
+# End Source File
+# End Group
+# Begin Group "Header Files"
+
+# PROP Default_Filter "h;hpp;hxx;hm;inl"
+# Begin Source File
+
+SOURCE=.\AboutDialog.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\BitmapInfo.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\CKeyboard.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\CleanDesktop.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\Clipboard.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\CompatibleBitmap.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\ComputerName.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\CPointer.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\CurrentUser.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\DeviceContext.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\DeviceFrameBuffer.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\Dialog.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\DIBSectionBuffer.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\DynamicFn.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\EventManager.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\FolderManager.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\Handle.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\IconInfo.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\IntervalTimer.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\keymap.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\LaunchProcess.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\ListViewControl.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\LocalMem.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\LogicalPalette.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\LowLevelKeyEvents.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\ModuleFileName.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\MonitorInfo.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\MsgBox.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\MsgWindow.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\OSVersion.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\ProgressControl.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\RegConfig.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\Registry.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\ScaledDIBSectionBuffer.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\SDisplay.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\SDisplayCoreDriver.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\SDisplayCorePolling.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\SDisplayCoreWMHooks.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\Security.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\Service.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\SFileTransferManagerWin32.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\SFileTransferWin32.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\SInput.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\SocketManager.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\TCharArray.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\Threading.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\ToolBar.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\TrayIcon.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\TsSessions.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\Win32Util.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\WMCursor.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\WMHooks.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\WMNotifier.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\WMPoller.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\WMShatter.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\WMWindowCopyRect.h
+# End Source File
+# End Group
+# End Target
+# End Project
diff --git a/win/rfbplayer/ChoosePixelFormatDialog.h b/win/rfbplayer/ChoosePixelFormatDialog.h new file mode 100644 index 00000000..ada820b3 --- /dev/null +++ b/win/rfbplayer/ChoosePixelFormatDialog.h @@ -0,0 +1,59 @@ +/* Copyright (C) 2004 TightVNC Team. All Rights Reserved. + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +// -=- ChoosePixelFormatDialog.h + +#include <rfb_win32/Dialog.h> + +class ChoosePixelFormatDialog : public rfb::win32::Dialog { +public: + ChoosePixelFormatDialog(PixelFormatList *_supportedPF) + : Dialog(GetModuleHandle(0)), supportedPF(_supportedPF), combo(0) {} + // - Show the dialog and return true if OK was clicked, + // false in case of error or Cancel + virtual bool showDialog(HWND parent) { + return Dialog::showDialog(MAKEINTRESOURCE(IDD_PIXELFORMAT), parent); + } + const long getPFIndex() const {return pfIndex;} + bool isBigEndian() {return isItemChecked(IDC_BIG_ENDIAN);} +protected: + + // Dialog methods (protected) + virtual void initDialog() { + combo = GetDlgItem(handle, IDC_PIXELFORMAT); + SendMessage(combo, CB_ADDSTRING, 0, (LPARAM)(LPCTSTR)("Auto")); + for (int i = 0; i < supportedPF->count(); i++) { + SendMessage(combo, CB_ADDSTRING, + 0, (LPARAM)(LPCTSTR)(((*supportedPF)[i])->format_name)); + } + SendMessage(combo, CB_SETCURSEL, pfIndex + 1, 0); + setItemChecked(IDC_BIG_ENDIAN, bigEndian); + } + virtual bool onOk() { + pfIndex = SendMessage(combo, CB_GETCURSEL, 0, 0) - 1; + bigEndian = isItemChecked(IDC_BIG_ENDIAN); + return true; + } + virtual bool onCancel() { + return false; + } + static long pfIndex; + static bool bigEndian; + PixelFormatList *supportedPF; + HWND combo; +};
\ No newline at end of file diff --git a/win/rfbplayer/EditPixelFormatDialog.h b/win/rfbplayer/EditPixelFormatDialog.h new file mode 100644 index 00000000..0765285d --- /dev/null +++ b/win/rfbplayer/EditPixelFormatDialog.h @@ -0,0 +1,104 @@ +/* Copyright (C) 2004 TightVNC Team. All Rights Reserved. + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +// -=- EditPixelFormatDialog.h + +#include <rfb_win32/Dialog.h> + +#define MAX_STR_LEN 256 + +class EditPixelFormatDialog : public rfb::win32::Dialog { +public: + EditPixelFormatDialog(PixelFormatList *_supportedPF, char *_format_name, + PixelFormat *_pf) + : Dialog(GetModuleHandle(0)), format_name(_format_name), + supportedPF(_supportedPF), pf(_pf) {} + // - Show the dialog and return true if OK was clicked, + // false in case of error or Cancel + virtual bool showDialog(HWND parent) { + return Dialog::showDialog(MAKEINTRESOURCE(IDD_UPF_EDIT), parent); + } + +protected: + // Dialog methods (protected) + virtual void initDialog() { + HWND bppCombo = GetDlgItem(handle, IDC_BPP_COMBO); + SendMessage(bppCombo, CB_ADDSTRING, 0, (LPARAM)(LPCTSTR)("8")); + SendMessage(bppCombo, CB_ADDSTRING, 0, (LPARAM)(LPCTSTR)("16")); + SendMessage(bppCombo, CB_ADDSTRING, 0, (LPARAM)(LPCTSTR)("32")); + SendMessage(bppCombo, CB_SETCURSEL, min(2, (pf->bpp - 8) / 8), 0); + + HWND bigendianCombo = GetDlgItem(handle, IDC_BIGENDIAN_COMBO); + SendMessage(bigendianCombo, CB_ADDSTRING, 0, (LPARAM)(LPCTSTR)("No")); + SendMessage(bigendianCombo, CB_ADDSTRING, 0, (LPARAM)(LPCTSTR)("Yes")); + SendMessage(bigendianCombo, CB_SETCURSEL, pf->bigEndian, 0); + + setItemString(IDC_NAME_EDIT, format_name); + setItemInt(IDC_DEPTH_EDIT, pf->depth); + setItemInt(IDC_REDMAX_EDIT, pf->redMax); + setItemInt(IDC_GREENMAX_EDIT, pf->greenMax); + setItemInt(IDC_BLUEMAX_EDIT, pf->blueMax); + setItemInt(IDC_REDSHIFT_EDIT, pf->redShift); + setItemInt(IDC_GREENSHIFT_EDIT, pf->greenShift); + setItemInt(IDC_BLUESHIFT_EDIT, pf->blueShift); + } + virtual bool onOk() { + // Check for the errors + char err_msg[256] = "\0"; + if (((getItemString(IDC_NAME_EDIT))[0] == '\0') || + ((getItemString(IDC_DEPTH_EDIT))[0] == '\0') || + ((getItemString(IDC_REDMAX_EDIT))[0] == '\0') || + ((getItemString(IDC_GREENMAX_EDIT))[0] == '\0') || + ((getItemString(IDC_BLUEMAX_EDIT))[0] == '\0') || + ((getItemString(IDC_REDSHIFT_EDIT))[0] == '\0') || + ((getItemString(IDC_GREENSHIFT_EDIT))[0] == '\0') || + ((getItemString(IDC_BLUESHIFT_EDIT))[0] == '\0')) { + strcpy(err_msg, "Please fill the all fields in the dialog."); + } + int newIndex = supportedPF->getIndexByPFName(getItemString(IDC_NAME_EDIT)); + if ((supportedPF->getIndexByPFName(format_name) != newIndex) && + (newIndex != -1)) { + strcpy(err_msg, "The pixel format with that name is already exist."); + } + if (getItemInt(IDC_DEPTH_EDIT) <= 0) { + strcpy(err_msg, "The pixel depth must be larger than 0."); + } + if (err_msg[0] != 0) { + MessageBox(handle, err_msg, "RfbPlayer", MB_OK | MB_ICONWARNING); + return false; + } + // Fill the pixel format structure + strCopy(format_name, getItemString(IDC_NAME_EDIT), MAX_STR_LEN); + pf->bpp = getItemInt(IDC_BPP_COMBO); + pf->depth = getItemInt(IDC_DEPTH_EDIT); + pf->bigEndian = (SendMessage(GetDlgItem(handle, IDC_BIGENDIAN_COMBO), + CB_GETCURSEL, 0, 0)) ? true : false; + pf->trueColour = true; + pf->redMax = getItemInt(IDC_REDMAX_EDIT); + pf->greenMax = getItemInt(IDC_GREENMAX_EDIT); + pf->blueMax = getItemInt(IDC_BLUEMAX_EDIT); + pf->redShift = getItemInt(IDC_REDSHIFT_EDIT); + pf->greenShift = getItemInt(IDC_GREENSHIFT_EDIT); + pf->blueShift = getItemInt(IDC_BLUESHIFT_EDIT); + return true; + } + + char *format_name; + PixelFormatList *supportedPF; + PixelFormat *pf; +}; diff --git a/win/rfbplayer/FbsInputStream.cxx b/win/rfbplayer/FbsInputStream.cxx new file mode 100644 index 00000000..6381a166 --- /dev/null +++ b/win/rfbplayer/FbsInputStream.cxx @@ -0,0 +1,251 @@ +/* Copyright (C) 2004 TightVNC Team. All Rights Reserved. + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +// -=- FbsInputStream class + +#include <windows.h> + +#include <rfb/Exception.h> + +#include <rfbplayer/FbsInputStream.h> + +FbsInputStream::FbsInputStream(char* FileName) { + bufferSize = 0; + ptr = end = start = NULL; + + timeOffset = 0; + seekOffset = -1; + startTime = GetTickCount(); + + playbackSpeed = 1.0; + seekBackwards = false; + paused = false; + + interruptDelay = false; + + fbsFile = fopen(FileName, "rb"); + if (fbsFile == NULL) { + char *msg = new char[12 + sizeof(FileName)]; + strcpy(msg, "Can't open "); + strcat(msg, FileName); + throw rfb::Exception(msg); + } + + U8 b[12]; + readNByte(b, 12); + + if (b[0] != 'F' || b[1] != 'B' || b[2] != 'S' || b[3] != ' ' || + b[4] != '0' || b[5] != '0' || b[6] != '1' || b[7] != '.' || + b[8] < '0' || b[8] > '9' || b[9] < '0' || b[9] > '9' || + b[10] < '0' || b[10] > '9' || b[11] != '\n') { + throw rfb::Exception("Incorrect protocol version"); + } +} + +FbsInputStream::~FbsInputStream() { + if (start != NULL) + delete [] start; + ptr = end = start = NULL; + fclose(fbsFile); +} + +int FbsInputStream::pos() { + return ptr - start; +} + +// +// Close FbsInputStream and free data buffer +// + +void FbsInputStream::close() { + fclose(fbsFile); + + startTime = -1; + timeOffset = 0; + seekOffset = -1; + seekBackwards = false; + paused = false; + playbackSpeed = 1.0; + + if (start != NULL) + delete [] start; + ptr = end = start = NULL; + bufferSize = 0; +} + +// +// Fill data buffer from the session file (InStream::overrun() override) +// + +int FbsInputStream::overrun(int itemSize, int nItems, bool wait=true) { + // Just wait unless we are performing playback OR seeking. + waitWhilePaused(); + + // Perform backwardSeek (throws the special exception) + if (seekBackwards) { + throw rfb::Exception("[REWIND]"); + } + + // Save a tail of data + U8 *tmp; + int n = end - ptr; + if (n) { + tmp = new U8[n]; + memmove(tmp, ptr, n); + } + + bufferSize = (int)readUnsigned32(); + if (bufferSize >= 0) { + if (start != NULL) + delete [] start; + int realSize = (bufferSize + 3) & 0xFFFFFFFC; // padding to multiple of 32-bits + ptr = start = new U8[realSize + n]; + end = ptr + bufferSize + n; + if (n) { + memmove(start, tmp, n); + delete [] tmp; + } + readNByte(start + n, realSize); + timeOffset = (long)(readUnsigned32() / playbackSpeed); + + if (itemSize * nItems > bufferSize) + nItems = bufferSize / itemSize; + } + + if (bufferSize < 0 || timeOffset < 0) { + if (start != NULL) + delete [] start; + ptr = end = start = NULL; + bufferSize = 0; + return 0; + } + + if (seekOffset >= 0) { + if (timeOffset >= seekOffset) { + startTime = GetTickCount() - seekOffset; + seekOffset = -1; + } else { + return nItems; + } + } + + while (!interruptDelay) { + long timeDiff = startTime + timeOffset - GetTickCount(); + if (timeDiff <= 0) { + break; + } + Sleep(min(20, timeDiff)); + waitWhilePaused(); + } + interruptDelay = false; + + return nItems; +} + +int FbsInputStream::readUnsigned32() { + U8 buf[4]; + if (!readNByte(buf, 4)) + return -1; + + return ((long)(buf[0] & 0xFF) << 24 | + (buf[1] & 0xFF) << 16 | + (buf[2] & 0xFF) << 8 | + (buf[3] & 0xFF)); +} + +// +// Read n-bytes from the session file +// + +bool FbsInputStream::readNByte(U8 b[], int n) { + int off = 0; + + while (off != n) { + int count = fread(b, 1, n - off, fbsFile); + if (count < n) { + if (ferror(fbsFile)) + throw rfb::Exception("Read error from session file"); + if (feof(fbsFile)) + throw rfb::Exception("[End Of File]"); + } + off += count; + } + return true; +} + +void FbsInputStream::waitWhilePaused() { + while (paused && !isSeeking()) { + // A small delay helps to decrease the cpu usage + Sleep(20); + } +} + +void FbsInputStream::interruptFrameDelay() { + interruptDelay = true; +} + +// +// Methods providing additional functionality. +// + +long FbsInputStream::getTimeOffset() { + //long off = max(seekOffset, timeOffset); + return (long)(timeOffset * playbackSpeed); +} + +void FbsInputStream::setTimeOffset(long pos) { + seekOffset = (long)(pos / playbackSpeed); + if (seekOffset < timeOffset) { + seekBackwards = true; + } +} + +void FbsInputStream::setSpeed(double newSpeed) { + long newOffset = (long)(timeOffset * playbackSpeed / newSpeed); + startTime += timeOffset - newOffset; + timeOffset = newOffset; + if (isSeeking()) { + seekOffset = (long)(seekOffset * playbackSpeed / newSpeed); + } + playbackSpeed = newSpeed; +} + +double FbsInputStream::getSpeed() { + return playbackSpeed; +} + +bool FbsInputStream::isSeeking() { + return (seekOffset >= 0); +} + +long FbsInputStream::getSeekOffset() { + return (long)(seekOffset * playbackSpeed); +} + +bool FbsInputStream::isPaused() { + return paused; +} + +void FbsInputStream::pausePlayback() { + paused = true; +} + +void FbsInputStream::resumePlayback() { + paused = false; + startTime = GetTickCount() - timeOffset; +} diff --git a/win/rfbplayer/FbsInputStream.h b/win/rfbplayer/FbsInputStream.h new file mode 100644 index 00000000..96492739 --- /dev/null +++ b/win/rfbplayer/FbsInputStream.h @@ -0,0 +1,68 @@ +/* Copyright (C) 2004 TightVNC Team. All Rights Reserved. + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +// -=- FbsInputStream.h + +#include <rdr/InStream.h> + +using namespace rdr; + +class FbsInputStream : public InStream { + public: + FbsInputStream(char *FileName); + ~FbsInputStream(); + + // Methods are used to contol the Rfb Stream + long getTimeOffset(); + void setTimeOffset(long pos); + double getSpeed(); + void setSpeed(double newSpeed); + bool isSeeking(); + long getSeekOffset(); + bool isPaused(); + void pausePlayback(); + void resumePlayback(); + void interruptFrameDelay(); + void close(); + int pos(); + + private: + U8 *start; + int bufferSize; + long startTime; + long timeOffset; + long seekOffset; + double playbackSpeed; + bool seekBackwards; + bool paused; + bool interruptDelay; + + FILE *fbsFile; + + // overrun() - overrides InStream::overrun(). + // It is implemented to fill the data buffer from the session file. + // It ensures there are at least itemSize bytes of buffer data. Returns + // the number of items in the buffer (up to a maximum of nItems). itemSize + // is supposed to be "small" (a few bytes). + + int overrun(int itemSize, int nItems, bool wait); + + int readUnsigned32(); + bool readNByte(U8 *b, int n); + void waitWhilePaused(); +}; diff --git a/win/rfbplayer/GotoPosDialog.h b/win/rfbplayer/GotoPosDialog.h new file mode 100644 index 00000000..ddbbe53a --- /dev/null +++ b/win/rfbplayer/GotoPosDialog.h @@ -0,0 +1,44 @@ +/* Copyright (C) 2004 TightVNC Team. All Rights Reserved. + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +// -=- GotoPosDialog.h + +#include <rfb_win32/Dialog.h> + +class GotoPosDialog : public rfb::win32::Dialog { +public: + GotoPosDialog() : Dialog(GetModuleHandle(0)) {} + // - Show the dialog and return true if OK was clicked, + // false in case of error or Cancel + virtual bool showDialog(HWND parent) { + return Dialog::showDialog(MAKEINTRESOURCE(IDD_GOTO), parent); + } + const long getPos() const {return pos;} +protected: + + // Dialog methods (protected) + virtual void initDialog() { + setItemString(IDC_GOTO_EDIT, rfb::TStr("0")); + } + virtual bool onOk() { + pos = atol(rfb::CStr(getItemString(IDC_GOTO_EDIT))); + return true; + } + + long pos; +};
\ No newline at end of file diff --git a/win/rfbplayer/InfoDialog.h b/win/rfbplayer/InfoDialog.h new file mode 100644 index 00000000..bbe9b26f --- /dev/null +++ b/win/rfbplayer/InfoDialog.h @@ -0,0 +1,41 @@ +/* Copyright (C) 2004 TightVNC Team. All Rights Reserved. + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +// -=- InfoDialog.h + +#include <rfb_win32/Dialog.h> + +class InfoDialog : public rfb::win32::Dialog { +public: + InfoDialog(char *_info_message, char *_title = "Information") + : Dialog(GetModuleHandle(0)), info_message(_info_message), title(_title) {} + // - Show the dialog and return true if OK was clicked, + // false in case of error or Cancel + virtual bool showDialog(HWND parent = 0) { + return Dialog::showDialog(MAKEINTRESOURCE(IDD_INFO), parent); + } +protected: + + // Dialog methods (protected) + virtual void initDialog() { + SetWindowText(handle, title); + setItemString(IDC_INFO_EDIT, info_message); + } + char *info_message; + char *title; +};
\ No newline at end of file diff --git a/win/rfbplayer/OptionsDialog.h b/win/rfbplayer/OptionsDialog.h new file mode 100644 index 00000000..8c3a87d7 --- /dev/null +++ b/win/rfbplayer/OptionsDialog.h @@ -0,0 +1,112 @@ +/* Copyright (C) 2004 TightVNC Team. All Rights Reserved. + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +// -=- OptionsDialog.h + +#include <rfbplayer/PlayerOptions.h> +#include <rfbplayer/UserPixelFormatsDialog.h> + +class OptionsDialog : public rfb::win32::Dialog { +public: + OptionsDialog(PlayerOptions *_options, PixelFormatList *_supportedPF) + : Dialog(GetModuleHandle(0)), options(_options), combo(0), + supportedPF(_supportedPF) {} + // - Show the dialog and return true if OK was clicked, + // false in case of error or Cancel + virtual bool showDialog(HWND parent) { + return Dialog::showDialog(MAKEINTRESOURCE(IDD_OPTIONS), parent); + } +protected: + + // Dialog methods (protected) + virtual void initDialog() { + combo = GetDlgItem(handle, IDC_PIXELFORMAT); + SendMessage(combo, CB_ADDSTRING, 0, (LPARAM)(LPCTSTR)("Auto")); + for (int i = 0; i < supportedPF->count(); i++) { + SendMessage(combo, CB_ADDSTRING, + 0, (LPARAM)(LPCTSTR)(((*supportedPF)[i])->format_name)); + } + SendMessage(combo, CB_SETCURSEL, options->pixelFormatIndex + 1, 0); + setItemChecked(IDC_ACCEPT_BELL, options->acceptBell); + setItemChecked(IDC_ACCEPT_CUT_TEXT, options->acceptCutText); + setItemChecked(IDC_AUTOPLAY, options->autoPlay); + setItemChecked(IDC_BIG_ENDIAN, options->bigEndianFlag); + if (options->askPixelFormat) { + setItemChecked(IDC_ASK_PF, true); + enableItem(IDC_PIXELFORMAT, false); + enableItem(IDC_BIG_ENDIAN, false); + } + } + virtual bool onOk() { + options->askPixelFormat = isItemChecked(IDC_ASK_PF); + options->acceptBell = isItemChecked(IDC_ACCEPT_BELL); + options->acceptCutText = isItemChecked(IDC_ACCEPT_CUT_TEXT); + options->autoPlay = isItemChecked(IDC_AUTOPLAY); + options->bigEndianFlag = isItemChecked(IDC_BIG_ENDIAN); + if (!options->askPixelFormat) { + options->pixelFormatIndex = int(SendMessage(combo, CB_GETCURSEL, 0, 0)) - 1; + if (options->pixelFormatIndex < 0) { + options->autoDetectPF = true; + } else { + options->setPF(&((*supportedPF)[options->pixelFormatIndex])->PF); + options->pixelFormat.bigEndian = options->bigEndianFlag; + options->autoDetectPF = false; + } + } + options->writeToRegistry(); + return true; + } + virtual bool onCommand(int item, int cmd) { + if (item == IDC_ASK_PF) { + enableItem(IDC_PIXELFORMAT, !isItemChecked(IDC_ASK_PF)); + enableItem(IDC_BIG_ENDIAN, !isItemChecked(IDC_ASK_PF)); + } + if (item == IDC_DEFAULT) { + SendMessage(combo, CB_SETCURSEL, DEFAULT_PF_INDEX + 1, 0); + enableItem(IDC_PIXELFORMAT, !DEFAULT_ASK_PF); + enableItem(IDC_BIG_ENDIAN, !DEFAULT_ASK_PF); + setItemChecked(IDC_ASK_PF, DEFAULT_ASK_PF); + setItemChecked(IDC_ACCEPT_BELL, DEFAULT_ACCEPT_BELL); + setItemChecked(IDC_ACCEPT_CUT_TEXT, DEFAULT_ACCEPT_CUT_TEXT); + setItemChecked(IDC_AUTOPLAY, DEFAULT_AUTOPLAY); + setItemChecked(IDC_BIG_ENDIAN, DEFAULT_BIG_ENDIAN); + } + if (item == IDC_EDIT_UPF) { + UserPixelFormatsDialog UpfListDialog(supportedPF); + if (UpfListDialog.showDialog(handle)) { + int index = SendMessage(combo, CB_GETCURSEL, 0, 0); + SendMessage(combo, CB_RESETCONTENT, 0, 0); + SendMessage(combo, CB_ADDSTRING, 0, (LPARAM)(LPCTSTR)("Auto")); + for (int i = 0; i < supportedPF->count(); i++) { + SendMessage(combo, CB_ADDSTRING, + 0, (LPARAM)(LPCTSTR)(((*supportedPF)[i])->format_name)); + } + if ( index > (SendMessage(combo, CB_GETCOUNT, 0, 0) - 1)) { + index = SendMessage(combo, CB_GETCOUNT, 0, 0) - 1; + } + SendMessage(combo, CB_SETCURSEL, index, 0); + options->pixelFormatIndex = index - 1; + } + } + return false; + } + + HWND combo; + PlayerOptions *options; + PixelFormatList *supportedPF; +};
\ No newline at end of file diff --git a/win/rfbplayer/PixelFormatList.cxx b/win/rfbplayer/PixelFormatList.cxx new file mode 100644 index 00000000..25996f65 --- /dev/null +++ b/win/rfbplayer/PixelFormatList.cxx @@ -0,0 +1,159 @@ +/* Copyright (C) 2004 TightVNC Team. All Rights Reserved. + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +// -=- PixelFormatList class + +#include <rfbplayer/PixelFormatList.h> + +using namespace rfb; +using namespace rfb::win32; + +PixelFormatList::PixelFormatList() { + PixelFormatListElement PFElem; + + // -=- Add the default pixel formats to list + // PF_BGR233 + PFElem.setName("8-bits depth (BGR233)"); + PFElem.setPF(&PixelFormat(8,8,0,1,7,7,3,0,3,6)); + PFList.push_back(PFElem); + // PF_RGB555 + PFElem.setName("15-bits depth (RGB555)"); + PFElem.setPF(&PixelFormat(16,15,0,1,31,31,31,10,5,0)); + PFList.push_back(PFElem); + // PF_BGR555 + PFElem.setName("15-bits depth (BGR555)"); + PFElem.setPF(&PixelFormat(16,15,0,1,31,31,31,0,5,10)); + PFList.push_back(PFElem); + // PF_RGB565 + PFElem.setName("16-bits depth (RGB565)"); + PFElem.setPF(&PixelFormat(16,16,0,1,31,63,31,11,5,0)); + PFList.push_back(PFElem); + // PF_BGR565 + PFElem.setName("16-bits depth (BGR565)"); + PFElem.setPF(&PixelFormat(16,16,0,1,31,63,31,0,5,11)); + PFList.push_back(PFElem); + // PF_RGB888 + PFElem.setName("24-bits depth (RGB888)"); + PFElem.setPF(&PixelFormat(32,24,0,1,255,255,255,16,8,0)); + PFList.push_back(PFElem); + // PF_BGR888 + PFElem.setName("24-bits depth (BGR888)"); + PFElem.setPF(&PixelFormat(32,24,0,1,255,255,255,0,8,16)); + PFList.push_back(PFElem); + + PF_DEFAULT_COUNT = PFList.size(); +} + +PixelFormatListElement *PixelFormatList::operator[](int index) { + return &(*getIterator(index)); +} + +void PixelFormatList::add(char *format_name, PixelFormat PF) { + PixelFormatListElement PFElem; + PFElem.setName(format_name); + PFElem.setPF(&PF); + PFList.push_back(PFElem); +} + +void PixelFormatList::insert(int index, char *format_name, PixelFormat PF) { + if (isDefaultPF(index)) + rdr::Exception("PixelFormatList:can't insert to the default pixel format place"); + + PixelFormatListElement PFElem; + PFElem.setName(format_name); + PFElem.setPF(&PF); + PFList.insert(getIterator(index), PFElem); +} + +void PixelFormatList::remove(int index) { + if (isDefaultPF(index)) + rdr::Exception("PixelFormatList:can't remove the default pixel format"); + PFList.erase(getIterator(index)); +} + +list <PixelFormatListElement>::iterator PixelFormatList::getIterator(int index) { + if ((index >= PFList.size()) || (index < 0)) + rdr::Exception("PixelFormatList:out of range"); + + int i = 0; + list <PixelFormatListElement>::iterator iter; + for (iter = PFList.begin(); iter != PFList.end(); iter++) { + if (i++ == index) break; + } + return iter; +} + +bool PixelFormatList::isDefaultPF(int index) { + if (index < PF_DEFAULT_COUNT) return true; + return false; +} + +void PixelFormatList::readUserDefinedPF(HKEY root, const char *keypath) { + RegKey regKey; + regKey.createKey(root, keypath); + int count = regKey.getInt(_T("PixelFormatCount"), 0); + if (count > 0) { + // Erase all user defined pixel formats + int upf_count = getUserPFCount(); + if (upf_count > 0) { + for(int i = 0; i < upf_count; i++) { + remove(PF_DEFAULT_COUNT); + } + } + // Add the user defined pixel formats from the registry + for(int i = 0; i < count; i++) { + char upf_name[20] = "\0"; + sprintf(upf_name, "%s%i", "Upf", i); + int size = sizeof(PixelFormatListElement); + PixelFormatListElement *pPFElem = 0;// = &PFElem; + regKey.getBinary(upf_name, (void**)&pPFElem, &size); + PFList.push_back(*pPFElem); + if (pPFElem) delete pPFElem; + } + } +} + +void PixelFormatList::writeUserDefinedPF(HKEY root, const char *keypath) { + RegKey regKey; + + // Delete all user defined pixel formats from the regisry + regKey.createKey(root, keypath);//_T("Software\\TightVnc\\RfbPlayer\\UserDefinedPF")); + int count = regKey.getInt(_T("PixelFormatCount"), 0); + for (int i = 0; i < count; i++) { + char upf_name[20] = "\0"; + sprintf(upf_name, "%s%i", "Upf", i); + regKey.deleteValue(upf_name); + } + regKey.setInt(_T("PixelFormatCount"), 0); + + // Write new user defined pixel formats to the registry + regKey.setInt(_T("PixelFormatCount"), getUserPFCount()); + for (i = 0; i < getUserPFCount(); i++) { + char upf_name[20] = "\0"; + sprintf(upf_name, "%s%i", "Upf", i); + regKey.setBinary(upf_name, (void *)operator[](i+getDefaultPFCount()), + sizeof(PixelFormatListElement)); + } +} + +int PixelFormatList::getIndexByPFName(const char *format_name) { + for (int i = 0; i < PixelFormatList::count(); i++) { + if (_stricmp(operator[](i)->format_name, format_name) == 0) return i; + } + return -1; +}
\ No newline at end of file diff --git a/win/rfbplayer/PixelFormatList.h b/win/rfbplayer/PixelFormatList.h new file mode 100644 index 00000000..cd7c50aa --- /dev/null +++ b/win/rfbplayer/PixelFormatList.h @@ -0,0 +1,85 @@ +/* Copyright (C) 2004 TightVNC Team. All Rights Reserved. + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +// -=- PixelFormatList.h + +// Definition of the PixelFormatList class, responsible for +// controlling the list of supported pixel formats. + +#include <list> + +#include <rfb/Exception.h> +#include <rfb/PixelFormat.h> + +#include <rfb_win32/registry.h> + +// Definition indexes of the default pixel formats +#define PF_BGR233 0 +#define PF_RGB555 1 +#define PF_BGR555 2 +#define PF_RGB565 3 +#define PF_BGR565 4 +#define PF_RGB888 5 +#define PF_BGR888 6 + +using namespace rfb; +using namespace std; + +// PixelFormatListElement class, it is +// an item of the PixelFormatList list. + +class PixelFormatListElement { +public: + PixelFormatListElement() { + format_name[0] = 0; + } + char format_name[256]; + PixelFormat PF; + void setName(const char *name) { + format_name[0] = '\0'; + strcpy(format_name, name); + format_name[strlen(name)] = '\0'; + } + void setPF(PixelFormat *_PF) { + memcpy(&PF, _PF, sizeof(PixelFormat)); + } +}; + +class PixelFormatList { +public: + PixelFormatList(); + + PixelFormatListElement* operator[](int index); + void add(char *format_name, PixelFormat PF); + void insert(int index, char *format_name, PixelFormat PF); + void remove(int index); + + void readUserDefinedPF(HKEY root, const char *keypath); + void writeUserDefinedPF(HKEY root, const char *keypath); + + int count() { return PFList.size(); } + int getDefaultPFCount() { return PF_DEFAULT_COUNT; } + int getUserPFCount() { return max(0, count() - PF_DEFAULT_COUNT); } + int getIndexByPFName(const char *format_name); + bool isDefaultPF(int index); + +protected: + list <PixelFormatListElement>::iterator getIterator(int index); + list <PixelFormatListElement> PFList; + int PF_DEFAULT_COUNT; +};
\ No newline at end of file diff --git a/win/rfbplayer/PlayerOptions.cxx b/win/rfbplayer/PlayerOptions.cxx new file mode 100644 index 00000000..5384c6ed --- /dev/null +++ b/win/rfbplayer/PlayerOptions.cxx @@ -0,0 +1,152 @@ +/* Copyright (C) 2004 TightVNC Team. All Rights Reserved. + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +// -=- PlayerOptions class + +#include <rfbplayer/PlayerOptions.h> + +using namespace rfb::win32; + +PlayerOptions::PlayerOptions() { + writeDefaults(); +}; + +void PlayerOptions::readFromRegistry() { + try { + PixelFormat *pPF = 0; + int pfSize = sizeof(PixelFormat); + RegKey regKey; + regKey.createKey(HKEY_CURRENT_USER, _T("Software\\TightVnc\\RfbPlayer")); + autoPlay = regKey.getBool(_T("AutoPlay"), DEFAULT_AUTOPLAY); + autoDetectPF = regKey.getBool(_T("AutoDetectPixelFormat"), DEFAULT_AUTOPF); + bigEndianFlag = regKey.getBool(_T("BigEndianFlag"), DEFAULT_BIG_ENDIAN); + pixelFormatIndex = regKey.getInt(_T("PixelFormatIndex"), DEFAULT_PF_INDEX); + regKey.getBinary(_T("PixelFormat"), (void**)&pPF, &pfSize, + &PixelFormat(32,24,0,1,255,255,255,16,8,0), sizeof(PixelFormat)); + setPF(pPF); + acceptBell = regKey.getBool(_T("AcceptBell"), DEFAULT_ACCEPT_BELL); + acceptCutText = regKey.getBool(_T("AcceptCutText"), DEFAULT_ACCEPT_CUT_TEXT); + autoPlay = regKey.getBool(_T("AutoPlay"), DEFAULT_AUTOPLAY); + askPixelFormat = regKey.getBool(_T("AskPixelFormat"), DEFAULT_ASK_PF); + if (pPF) delete pPF; + } catch (rdr::Exception e) { + MessageBox(0, e.str(), "RFB Player", MB_OK | MB_ICONERROR); + } +} + +void PlayerOptions::writeToRegistry() { + try { + RegKey regKey; + regKey.createKey(HKEY_CURRENT_USER, _T("Software\\TightVnc\\RfbPlayer")); + regKey.setBool(_T("AutoPlay"), autoPlay); + regKey.setBool(_T("AutoDetectPixelFormat"), autoDetectPF); + regKey.setBool(_T("BigEndianFlag"), bigEndianFlag); + regKey.setInt(_T("PixelFormatIndex"), pixelFormatIndex); + regKey.setBinary(_T("PixelFormat"), &pixelFormat, sizeof(PixelFormat)); + regKey.setBool(_T("AcceptBell"), acceptBell); + regKey.setBool(_T("AcceptCutText"), acceptCutText); + regKey.setBool(_T("AutoPlay"), autoPlay); + regKey.setBool(_T("AskPixelFormat"), askPixelFormat); + } catch (rdr::Exception e) { + MessageBox(0, e.str(), "RFB Player", MB_OK | MB_ICONERROR); + } +} + +void PlayerOptions::writeDefaults() { + initTime = DEFAULT_INIT_TIME; + playbackSpeed = DEFAULT_SPEED; + autoDetectPF = DEFAULT_AUTOPF; + bigEndianFlag = DEFAULT_BIG_ENDIAN; + pixelFormatIndex = DEFAULT_PF_INDEX; + pixelFormat = PixelFormat(32,24,0,1,255,255,255,16,8,0); + frameScale = DEFAULT_FRAME_SCALE; + autoPlay = DEFAULT_AUTOPLAY; + fullScreen = DEFAULT_FULL_SCREEN; + acceptBell = DEFAULT_ACCEPT_BELL; + acceptCutText = DEFAULT_ACCEPT_CUT_TEXT; + loopPlayback = DEFAULT_LOOP_PLAYBACK; + askPixelFormat = DEFAULT_ASK_PF; + commandLineParam = false; +} + +void PlayerOptions::setPF(PixelFormat *newPF) { + memcpy(&pixelFormat, newPF, sizeof(PixelFormat)); +} + +bool PlayerOptions::setPF(int rgb_order, int rm, int gm, int bm, bool big_endian) { + PixelFormat newPF; + + // Calculate the colour bits per pixel + int bpp = rm + gm + bm; + if (bpp < 0) { + return false; + } else if (bpp <= 8 ) { + newPF.bpp = 8; + } else if (bpp <= 16) { + newPF.bpp = 16; + } else if (bpp <= 32) { + newPF.bpp = 32; + } else { + return false; + } + newPF.depth = bpp; + + // Calculate the r, g and b bits shifts + switch (rgb_order) { + case RGB_ORDER: + newPF.redShift = gm + bm; + newPF.greenShift = bm; + newPF.blueShift = 0; + break; + case RBG_ORDER: + newPF.redShift = bm + gm; + newPF.blueShift = gm; + newPF.greenShift = 0; + break; + case GRB_ORDER: + newPF.greenShift = rm + bm; + newPF.redShift = bm; + newPF.blueShift = 0; + break; + case GBR_ORDER: + newPF.greenShift = bm + rm; + newPF.blueShift = rm; + newPF.redShift = 0; + break; + case BGR_ORDER: + newPF.blueShift = gm + rm; + newPF.greenShift = rm; + newPF.redShift = 0; + break; + case BRG_ORDER: + newPF.blueShift = rm + gm; + newPF.redShift = gm; + newPF.greenShift = 0; + break; + default: + return false; + } + + newPF.trueColour = true; + newPF.bigEndian = big_endian; + newPF.redMax = (1 << rm) - 1; + newPF.greenMax = (1 << gm) - 1; + newPF.blueMax = (1 << bm) - 1; + setPF(&newPF); + return true; +}
\ No newline at end of file diff --git a/win/rfbplayer/PlayerOptions.h b/win/rfbplayer/PlayerOptions.h new file mode 100644 index 00000000..83618a75 --- /dev/null +++ b/win/rfbplayer/PlayerOptions.h @@ -0,0 +1,77 @@ +/* Copyright (C) 2004 TightVNC Team. All Rights Reserved. + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +// -=- PlayerOptions.h + +// Definition of the PlayerOptions class, responsible for +// storing & retrieving the RfbPlayer's options. + +#include <rfb/PixelFormat.h> + +#include <rfb_win32/registry.h> + +#define PF_MODES 3 + +// The R, G and B values order in the pixel +#define RGB_ORDER 0 +#define RBG_ORDER 1 +#define GRB_ORDER 2 +#define GBR_ORDER 3 +#define BGR_ORDER 4 +#define BRG_ORDER 5 + +// Default options values +//#define DEFAULT_PF 0 +#define DEFAULT_PF_INDEX -1 +#define DEFAULT_AUTOPF TRUE +#define DEFAULT_INIT_TIME -1 +#define DEFAULT_SPEED 1.0 +#define DEFAULT_FRAME_SCALE 100 +#define DEFAULT_ACCEPT_BELL FALSE +#define DEFAULT_ACCEPT_CUT_TEXT FALSE +#define DEFAULT_BIG_ENDIAN FALSE +#define DEFAULT_LOOP_PLAYBACK FALSE +#define DEFAULT_ASK_PF FALSE +#define DEFAULT_AUTOPLAY FALSE +#define DEFAULT_FULL_SCREEN FALSE + +using namespace rfb; + +class PlayerOptions { +public: + PlayerOptions(); + void readFromRegistry(); + void writeToRegistry(); + void writeDefaults(); + void setPF(PixelFormat *pf); + bool setPF(int rgb_order, int rm, int gm, int bm, bool big_endian=false); + long initTime; + double playbackSpeed; + bool autoPlay; + bool fullScreen; + bool autoDetectPF; + bool bigEndianFlag; + long pixelFormatIndex; + PixelFormat pixelFormat; + bool acceptBell; + bool acceptCutText; + bool loopPlayback; + bool askPixelFormat; + long frameScale; + bool commandLineParam; +};
\ No newline at end of file diff --git a/win/rfbplayer/PlayerToolBar.cxx b/win/rfbplayer/PlayerToolBar.cxx new file mode 100644 index 00000000..3056f283 --- /dev/null +++ b/win/rfbplayer/PlayerToolBar.cxx @@ -0,0 +1,248 @@ +/* Copyright (C) 2005 TightVNC Team. All Rights Reserved. + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +// -=- PlayerToolBar.cxx + +#include <rfbplayer/rfbplayer.h> +#include <rfbplayer/resource.h> + +PlayerToolBar::PlayerToolBar() +: ToolBar(), hFont(0), timeStatic(0), speedEdit(0), posTrackBar(0), + speedUpDown(0), sliderDragging(false), sliderStepMs(0) +{ +} + +void PlayerToolBar::create(RfbPlayer *player_, HWND parentHwnd_) { + HDC hdc; + SIZE sz; + RECT tRect; + NONCLIENTMETRICS nonClientMetrics; + + player = player_; + + // Get the default font for the main menu + nonClientMetrics.cbSize = sizeof(NONCLIENTMETRICS); + if (!SystemParametersInfo(SPI_GETNONCLIENTMETRICS, sizeof(NONCLIENTMETRICS), &nonClientMetrics, 0)) + MessageBox(0, "Can't access to the system font.", + "RfbPlayer", MB_OK | MB_ICONERROR); + nonClientMetrics.lfMenuFont.lfHeight = 16; + hFont = CreateFontIndirect(&nonClientMetrics.lfMenuFont); + + // Create the toolbar panel + ToolBar::create(ID_TOOLBAR, parentHwnd_, + WS_CHILD | WS_VISIBLE | TBSTYLE_FLAT | CCS_NORESIZE); + addBitmap(4, IDB_TOOLBAR); + + // Create the control buttons + addButton(0, ID_PLAY); + addButton(1, ID_PAUSE); + addButton(2, ID_STOP); + addButton(0, 0, TBSTATE_ENABLED, TBSTYLE_SEP); + + // Create the static control for the time output + timeStatic = CreateWindowEx(0, "Static", "00m:00s (00m:00s)", + WS_CHILD | WS_VISIBLE, 0, 0, 20, 20, getHandle(), (HMENU)ID_TIME_STATIC, + GetModuleHandle(0), 0); + SendMessage(timeStatic, WM_SETFONT,(WPARAM) hFont, TRUE); + hdc = GetDC(timeStatic); + SelectObject(hdc, hFont); + GetTextExtentPoint32(hdc, "00m:00s (00m:00s)", 16, &sz); + addButton(sz.cx + 10, 0, TBSTATE_ENABLED, TBSTYLE_SEP); + addButton(0, 10, TBSTATE_ENABLED, TBSTYLE_SEP); + getButtonRect(4, &tRect); + MoveWindow(timeStatic, tRect.left, tRect.top+2, tRect.right-tRect.left, + tRect.bottom-tRect.top, FALSE); + + // Create the trackbar control for the time position + addButton(200, 0, TBSTATE_ENABLED, TBSTYLE_SEP); + getButtonRect(6, &tRect); + posTrackBar = CreateWindowEx(0, TRACKBAR_CLASS, "Trackbar Control", + WS_CHILD | WS_VISIBLE | TBS_AUTOTICKS | TBS_ENABLESELRANGE, + tRect.left, tRect.top, tRect.right-tRect.left, tRect.bottom-tRect.top, + parentHwnd, (HMENU)ID_POS_TRACKBAR, GetModuleHandle(0), 0); + // It's need to send notify messages to toolbar parent window + SetParent(posTrackBar, getHandle()); + addButton(0, 10, TBSTATE_ENABLED, TBSTYLE_SEP); + + // Create the label with "Speed:" caption + HWND speedStatic = CreateWindowEx(0, "Static", "Speed:", WS_CHILD | WS_VISIBLE, + 0, 0, 5, 5, getHandle(), (HMENU)ID_SPEED_STATIC, GetModuleHandle(0), 0); + SendMessage(speedStatic, WM_SETFONT,(WPARAM) hFont, TRUE); + hdc = GetDC(speedStatic); + SelectObject(hdc, hFont); + GetTextExtentPoint32(hdc, "Speed:", 6, &sz); + addButton(sz.cx + 10, 0, TBSTATE_ENABLED, TBSTYLE_SEP); + getButtonRect(8, &tRect); + MoveWindow(speedStatic, tRect.left, tRect.top+2, tRect.right-tRect.left, + tRect.bottom-tRect.top, FALSE); + + // Create the edit control and the spin for the speed managing + addButton(60, 0, TBSTATE_ENABLED, TBSTYLE_SEP); + getButtonRect(9, &tRect); + speedEdit = CreateWindowEx(WS_EX_CLIENTEDGE, "Edit", "1.00", + WS_CHILD | WS_VISIBLE | ES_RIGHT, tRect.left, tRect.top, + tRect.right-tRect.left, tRect.bottom-tRect.top, parentHwnd, + (HMENU)ID_SPEED_EDIT, GetModuleHandle(0), 0); + SendMessage(speedEdit, WM_SETFONT,(WPARAM) hFont, TRUE); + // It's need to send notify messages to toolbar parent window + SetParent(speedEdit, getHandle()); + + speedUpDown = CreateUpDownControl(WS_CHILD | WS_VISIBLE + | WS_BORDER | UDS_ALIGNRIGHT, 0, 0, 0, 0, getHandle(), + ID_SPEED_UPDOWN, GetModuleHandle(0), speedEdit, 20, 1, 2); + + // Resize the toolbar window + autoSize(); +} + +void PlayerToolBar::init(long sessionTimeMs_) { + sessionTimeMs = sessionTimeMs_; + + setSessionTimeStr(sessionTimeMs); + SendMessage(posTrackBar, TBM_SETRANGE, + TRUE, MAKELONG(0, min(sessionTimeMs / 1000, MAX_POS_TRACKBAR_RANGE))); + if (sessionTimeMs == 0) { + sliderStepMs = 1; + } else { + sliderStepMs = sessionTimeMs / SendMessage(posTrackBar, TBM_GETRANGEMAX, 0, 0); + } + updatePos(0); +} + +void PlayerToolBar::enable() { + enableButton(ID_PLAY, true); + enableButton(ID_PAUSE, true); + enableButton(ID_STOP, true); + enableButton(ID_FULLSCREEN, true); + EnableWindow(posTrackBar, true); + EnableWindow(speedEdit, true); + EnableWindow(speedUpDown, true); +} + +void PlayerToolBar::disable() { + enableButton(ID_PLAY, false); + enableButton(ID_PAUSE, false); + enableButton(ID_STOP, false); + enableButton(ID_FULLSCREEN, false); + EnableWindow(posTrackBar, false); + EnableWindow(speedEdit, false); + EnableWindow(speedUpDown, false); +} + +LRESULT PlayerToolBar::processWM_COMMAND(WPARAM wParam, LPARAM lParam) { + switch (LOWORD(wParam)) { + + case ID_RETURN: + // Update the speed if return pressed in speedEdit + if (getSpeedEditHwnd() == GetFocus()) { + char speedStr[20], *stopStr; + GetWindowText(getSpeedEditHwnd(), speedStr, sizeof(speedStr)); + double speed = strtod(speedStr, &stopStr); + if (speed > 0) { + speed = min(MAX_SPEED, speed); + } else { + speed = player->getSpeed(); + } + player->setSpeed(speed); + return FALSE; + } + } + + return TRUE; +} + +LRESULT PlayerToolBar::processWM_HSCROLL(WPARAM wParam, LPARAM lParam) { + long Pos = SendMessage(posTrackBar, TBM_GETPOS, 0, 0); + Pos *= sliderStepMs; + + switch (LOWORD(wParam)) { + case TB_PAGEUP: + case TB_PAGEDOWN: + case TB_LINEUP: + case TB_LINEDOWN: + case TB_THUMBTRACK: + sliderDragging = true; + updatePos(Pos); + return FALSE; + case TB_THUMBPOSITION: + case TB_ENDTRACK: + player->setPos(Pos); + player->setPaused(player->isPaused());; + updatePos(Pos); + sliderDragging = false; + return FALSE; + default: + break; + } + + return TRUE; +} + +LRESULT PlayerToolBar::processWM_NOTIFY(WPARAM wParam, LPARAM lParam) { + switch (((NMHDR*)lParam)->code) { + case UDN_DELTAPOS: + if ((int)wParam == ID_SPEED_UPDOWN) { + char speedStr[20] = "\0"; + DWORD speedRange = SendMessage(speedUpDown, UDM_GETRANGE, 0, 0); + LPNM_UPDOWN upDown = (LPNM_UPDOWN)lParam; + double speed; + + // The out of range checking + if (upDown->iDelta > 0) { + speed = min(upDown->iPos + upDown->iDelta, LOWORD(speedRange)) * 0.5; + } else { + // It's need to round the UpDown position + if ((upDown->iPos * 0.5) != player->getSpeed()) { + upDown->iDelta = 0; + } + speed = max(upDown->iPos + upDown->iDelta, HIWORD(speedRange)) * 0.5; + } + player->setSpeed(speed); + } + } + + // We always return TRUE to prevent the change in the updown contol + // position. The control's position changes in the RfbPlayer::setSpeed(). + return TRUE; +} + +void PlayerToolBar::updatePos(long newPos) { + // Update time pos in static control + char timePos[30] = "\0"; + long time = newPos / 1000; + sprintf(timePos, "%.2um:%.2us (%s)", time/60, time%60, fullSessionTimeStr); + SetWindowText(timeStatic, timePos); + + // Update the position of slider + if (!sliderDragging) { + double error = SendMessage(posTrackBar, TBM_GETPOS, 0, 0) * + sliderStepMs / double(newPos); + if (!((error > 1 - CALCULATION_ERROR) && (error <= 1 + CALCULATION_ERROR))) { + SendMessage(posTrackBar, TBM_SETPOS, TRUE, newPos / sliderStepMs); + } + } +} + +void PlayerToolBar::setSessionTimeStr(long sessionTimeMs) { + sprintf(fullSessionTimeStr, "%.2um:%.2us", + sessionTimeMs / 1000 / 60, sessionTimeMs / 1000 % 60); +} + +void PlayerToolBar::setTimePos(long pos) { + SendMessage(posTrackBar, TBM_SETPOS, TRUE, pos); +}
\ No newline at end of file diff --git a/win/rfbplayer/PlayerToolBar.h b/win/rfbplayer/PlayerToolBar.h new file mode 100644 index 00000000..17ab82e4 --- /dev/null +++ b/win/rfbplayer/PlayerToolBar.h @@ -0,0 +1,77 @@ +/* Copyright (C) 2005 TightVNC Team. All Rights Reserved. + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +// -=- PlayerToolBar.h + +// ToolBar for the RfbPlayer + +#include <rfb_win32/ToolBar.h> + +using namespace rfb::win32; + +#define ID_TOOLBAR 500 +#define ID_PLAY 510 +#define ID_PAUSE 520 +#define ID_TIME_STATIC 530 +#define ID_SPEED_STATIC 540 +#define ID_SPEED_EDIT 550 +#define ID_POS_TRACKBAR 560 +#define ID_SPEED_UPDOWN 570 + +#define MAX_SPEED 10.00 +#define CALCULATION_ERROR MAX_SPEED / 1000 +#define MAX_POS_TRACKBAR_RANGE 50 + +class RfbPlayer; + +class PlayerToolBar : public ToolBar { +public: + PlayerToolBar(); + ~PlayerToolBar() {} + + void create(RfbPlayer *player, HWND parentHwnd); + + void init(long sessionTimeMs); + + void enable(); + void disable(); + + LRESULT processWM_COMMAND(WPARAM wParam, LPARAM lParam); + LRESULT processWM_HSCROLL(WPARAM wParam, LPARAM lParam); + LRESULT processWM_NOTIFY(WPARAM wParam, LPARAM lParam); + + HWND getSpeedEditHwnd() { return speedEdit; } + HWND getSpeedUpDownHwnd() { return speedUpDown; } + + bool isPosSliderDragging() { return sliderDragging; }; + void updatePos(long newPos); + void setSessionTimeStr(long sessionTimeMs); + void setTimePos(long newPos); + +protected: + RfbPlayer *player; + HFONT hFont; + HWND timeStatic; + HWND speedEdit; + HWND posTrackBar; + HWND speedUpDown; + char fullSessionTimeStr[20]; + long sessionTimeMs; + bool sliderDragging; + long sliderStepMs; +}; diff --git a/win/rfbplayer/RfbProto.cxx b/win/rfbplayer/RfbProto.cxx new file mode 100644 index 00000000..5a7ff7f9 --- /dev/null +++ b/win/rfbplayer/RfbProto.cxx @@ -0,0 +1,142 @@ +/* Copyright (C) 2004 TightVNC Team. All Rights Reserved. + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +// -=- RFB Protocol + +#include <rfb/Exception.h> +#include <rfb/LogWriter.h> + +#include <rfbplayer/RfbProto.h> + +using namespace rfb; + +static LogWriter vlog("RfbProto"); + +// +// Constructor +// + +RfbProto::RfbProto(char *fileName) { + is = NULL; + reader = NULL; + newSession(fileName); +} + +// +// Destructor +// + +RfbProto::~RfbProto() { + if (is) delete is; + is = 0; + if (reader) delete reader; + reader = 0; +} + +void RfbProto::newSession(char *fileName) { + // Close the previous session + if (is) { + delete is; + is = NULL; + delete reader; + reader = NULL; + } + + // Begin the new session + if (fileName != NULL) { + is = new FbsInputStream(fileName); + reader = new CMsgReaderV3(this, is); + initialiseProtocol(); + } +} + +void RfbProto::initialiseProtocol() { + state_ = RFBSTATE_PROTOCOL_VERSION; +} + +void RfbProto::processMsg() +{ + switch (state_) { + + case RFBSTATE_PROTOCOL_VERSION: processVersionMsg(); break; + case RFBSTATE_SECURITY: processSecurityMsg(); break; + case RFBSTATE_INITIALISATION: processInitMsg(); break; + case RFBSTATE_NORMAL: reader->readMsg(); break; + default: + throw rfb::Exception("RfbProto::processMsg: invalid state"); + } +} + +void RfbProto::processVersionMsg() +{ + vlog.debug("reading protocol version"); + memset(&cp, 0, sizeof(cp)); + bool done; + if (!cp.readVersion(is, &done)) { + state_ = RFBSTATE_INVALID; + throw rfb::Exception("reading version failed: wrong file format?"); + } + if (!done) return; + + // The only official RFB protocol versions are currently 3.3, 3.7 and 3.8 + if (!cp.isVersion(3,3) && !cp.isVersion(3,7) && !cp.isVersion(3,8)) { + char msg[256]; + sprintf(msg,"File have unsupported RFB protocol version %d.%d", + cp.majorVersion, cp.minorVersion); + state_ = RFBSTATE_INVALID; + throw rfb::Exception(msg); + } + + state_ = RFBSTATE_SECURITY; + + vlog.info("Using RFB protocol version %d.%d", + cp.majorVersion, cp.minorVersion); +} + +void RfbProto::processSecurityMsg() +{ + vlog.debug("processing security types message"); + + int secType = secTypeInvalid; + + // legacy 3.3 server may only offer "vnc authentication" or "none" + secType = is->readU32(); + if (secType == secTypeInvalid) { + int reasonLen = is->readU32(); + char *reason = new char[reasonLen]; + is->readBytes(reason, reasonLen); + throw rfb::Exception(reason); + } + + if (secType != secTypeNone) { + throw rfb::Exception("Wrong authentication type in the session file"); + } + + state_ = RFBSTATE_INITIALISATION; +} + +void RfbProto::processInitMsg() { + vlog.debug("reading server initialisation"); + reader->readServerInit(); +} + +void RfbProto::serverInit() +{ + state_ = RFBSTATE_NORMAL; + vlog.debug("initialisation done"); +} diff --git a/win/rfbplayer/RfbProto.h b/win/rfbplayer/RfbProto.h new file mode 100644 index 00000000..316ea269 --- /dev/null +++ b/win/rfbplayer/RfbProto.h @@ -0,0 +1,69 @@ +/* Copyright (C) 2004 TightVNC Team. All Rights Reserved. + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +// -=- RfbProto.h + +#include <rfb/CMsgReaderV3.h> +#include <rfb/CMsgHandler.h> +#include <rfb/CSecurity.h> +#include <rfb/secTypes.h> + +#include <rfbplayer/FbsInputStream.h> + +using namespace rfb; + +class RfbProto : public CMsgHandler { + public: + + RfbProto(char *fileName); + ~RfbProto(); + + void newSession(char *filename); + void initialiseProtocol(); + void interruptFrameDelay() { is->interruptFrameDelay(); }; + const rdr::InStream* getInStream() { return is; } + + virtual void processMsg(); + + // serverInit() is called when the ServerInit message is received. The + // derived class must call on to CMsgHandler::serverInit(). + void serverInit(); + + enum stateEnum { + RFBSTATE_PROTOCOL_VERSION, + RFBSTATE_SECURITY, + RFBSTATE_INITIALISATION, + RFBSTATE_NORMAL, + RFBSTATE_INVALID + }; + + stateEnum state() { return state_; } + + protected: + void setState(stateEnum s) { state_ = s; } + virtual void framebufferUpdateEnd() {}; + + FbsInputStream* is; + CMsgReaderV3* reader; + stateEnum state_; + + private: + void processVersionMsg(); + void processSecurityMsg(); + void processInitMsg(); +}; diff --git a/win/rfbplayer/SessionInfoDialog.h b/win/rfbplayer/SessionInfoDialog.h new file mode 100644 index 00000000..2c036db0 --- /dev/null +++ b/win/rfbplayer/SessionInfoDialog.h @@ -0,0 +1,100 @@ +/* Copyright (C) 2004 TightVNC Team. All Rights Reserved. + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +// -=- SessionInfoDialog.h + +#include <math.h> + +#include <rfb/ConnParams.h> + +#include <rfb_win32/Dialog.h> + +#define log2(n) log(n) / 0.693147180559945 + +int max3(int v1, int v2, int v3) { + return max(v1, max(v2, v3)); +} + +class SessionInfoDialog : public rfb::win32::Dialog { +public: + SessionInfoDialog(ConnParams *_cp, int _currentEncoding) + : Dialog(GetModuleHandle(0)), cp(_cp), currentEncoding(_currentEncoding) {} + // - Show the dialog and return true if OK was clicked, + // false in case of error or Cancel + virtual bool showDialog(HWND parent = 0) { + return Dialog::showDialog(MAKEINTRESOURCE(IDD_SESSION_INFO), parent); + } +protected: + // Dialog methods (protected) + virtual void initDialog() { + char strValue[255] = "\0"; + setItemString(IDC_DESKTOP_NAME, cp->name()); + + sprintf(strValue, "%ix%i", cp->width, cp->height); + setItemString(IDC_DESKTOP_SIZE, strValue); + + int r = cp->pf().redShift, g = cp->pf().greenShift, b = cp->pf().blueShift; + int i = 3; + char buffer[10]; + sprintf(strValue, "depth %i(%ibpp), ", cp->pf().depth, cp->pf().bpp); + while (i) { + if (r == max3(r, g, b)) { + strcat(strValue, "r"); + _itoa(ceil(log2(cp->pf().redMax)), buffer, 10); + strcat(strValue, buffer); + r = -1; + i--; + continue; + } else if (g == max3(r, g, b)) { + strcat(strValue, "g"); + _itoa(ceil(log2(cp->pf().greenMax)), buffer, 10); + strcat(strValue, buffer); + g = -1; + i--; + continue; + } else if (b == max3(r, g, b)) { + strcat(strValue, "b"); + _itoa(ceil(log2(cp->pf().blueMax)), buffer, 10); + strcat(strValue, buffer); + b = -1; + i--; + continue; + } else break; + } + if (cp->pf().bigEndian) strcat(strValue, ", big-endian"); + else strcat(strValue, ", little-endian"); + setItemString(IDC_PIXEL_FORMAT, strValue); + + switch (currentEncoding) { + case encodingRaw: strcpy(strValue, "Raw"); break; + case encodingCopyRect: strcpy(strValue, "CopyRect"); break; + case encodingRRE: strcpy(strValue, "RRE"); break; + case encodingCoRRE: strcpy(strValue, "CoRRE"); break; + case encodingHextile: strcpy(strValue, "Hextile"); break; + case encodingTight: strcpy(strValue, "Tight"); break; + case encodingZRLE: strcpy(strValue, "ZRLE"); break; + default: strcpy(strValue, "Unknown"); + } + setItemString(IDC_CURRENT_ENCODING, strValue); + + sprintf(strValue, "%i.%i", cp->majorVersion, cp->minorVersion); + setItemString(IDC_VERSION, strValue); + } + ConnParams *cp; + int currentEncoding; +};
\ No newline at end of file diff --git a/win/rfbplayer/UserPixelFormatsDialog.h b/win/rfbplayer/UserPixelFormatsDialog.h new file mode 100644 index 00000000..fe2ad223 --- /dev/null +++ b/win/rfbplayer/UserPixelFormatsDialog.h @@ -0,0 +1,105 @@ +/* Copyright (C) 2004 TightVNC Team. All Rights Reserved. + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +// -=- UserPixelFormatsDialog.h + +#include <rfbplayer/EditPixelFormatDialog.h> + +#define UPF_REGISTRY_PATH "Software\\TightVnc\\RfbPlayer\\UserDefinedPF" + +class UserPixelFormatsDialog : public rfb::win32::Dialog { +public: + UserPixelFormatsDialog(PixelFormatList *_supportedPF) + : Dialog(GetModuleHandle(0)), supportedPF(_supportedPF), pfList(0) {} + // - Show the dialog and return true if OK was clicked, + // false in case of error or Cancel + virtual bool showDialog(HWND parent) { + return Dialog::showDialog(MAKEINTRESOURCE(IDD_USERPF_LIST), parent); + } + +protected: + // Dialog methods (protected) + virtual void initDialog() { + pfList = GetDlgItem(handle, IDC_PF_LIST); + for (int i = supportedPF->getDefaultPFCount(); i < supportedPF->count(); i++) { + SendMessage(pfList, LB_ADDSTRING, + 0, (LPARAM)(LPCTSTR)(((*supportedPF)[i])->format_name)); + } + SendMessage(pfList, LB_SETCURSEL, 0, 0); + } + virtual bool onCommand(int item, int cmd) { + switch (item) { + case IDC_ADD_BUTTON: + { + char format_name[MAX_STR_LEN] = ""; + PixelFormat pf(32, 24, 0, 1, 0, 0, 0, 0, 0, 0); + EditPixelFormatDialog edit(supportedPF, format_name, &pf); + if (edit.showDialog(handle)) { + supportedPF->add(format_name, pf); + SendMessage(pfList, LB_ADDSTRING, 0, (LPARAM)(LPCTSTR)format_name); + } + } + break; + case IDC_REMOVE_BUTTON: + { + int index = SendMessage(pfList, LB_GETCURSEL, 0, 0); + if (index == LB_ERR) { + MessageBox(handle, "You must select the pixel format for remove.", + "RfbPlayer", MB_OK | MB_ICONWARNING); + return false; + } else { + supportedPF->remove(supportedPF->getDefaultPFCount() + index); + SendMessage(pfList, LB_DELETESTRING, index, 0); + } + } + break; + case IDC_PF_LIST: + if (cmd != LBN_DBLCLK) break; + case IDC_EDIT_BUTTON: + { + int index = SendMessage(pfList, LB_GETCURSEL, 0, 0); + if (index == LB_ERR) { + MessageBox(handle, "You must select the pixel format for remove.", + "RfbPlayer", MB_OK | MB_ICONWARNING); + return false; + } + PixelFormat *pf = + &(supportedPF->operator[](index + supportedPF->getDefaultPFCount())->PF); + char *format_name = + (supportedPF)->operator[](index + supportedPF->getDefaultPFCount())->format_name; + EditPixelFormatDialog edit(supportedPF, format_name, pf); + if (edit.showDialog(handle)) { + SendMessage(pfList, LB_DELETESTRING, index, 0); + SendMessage(pfList, LB_INSERTSTRING, index, (LPARAM)(LPCTSTR)format_name); + SendMessage(pfList, LB_SETCURSEL, index, 0); + } + } + break; + default: + break; + } + return false; + } + virtual bool onOk() { + supportedPF->writeUserDefinedPF(HKEY_CURRENT_USER, UPF_REGISTRY_PATH); + return true; + } + + HWND pfList; + PixelFormatList *supportedPF; +};
\ No newline at end of file diff --git a/win/rfbplayer/buildTime.cxx b/win/rfbplayer/buildTime.cxx new file mode 100644 index 00000000..bab2e137 --- /dev/null +++ b/win/rfbplayer/buildTime.cxx @@ -0,0 +1 @@ +const char* buildTime = "Built on " __DATE__ " at " __TIME__;
\ No newline at end of file diff --git a/win/rfbplayer/resource.h b/win/rfbplayer/resource.h new file mode 100644 index 00000000..90a057fe --- /dev/null +++ b/win/rfbplayer/resource.h @@ -0,0 +1,81 @@ +//{{NO_DEPENDENCIES}} +// Microsoft Developer Studio generated include file. +// Used by rfbplayer.rc +// +#define IDI_ICON 105 +#define IDR_MENU 128 +#define IDR_ACCELERATOR 131 +#define IDB_TOOLBAR 132 +#define IDD_GOTO 133 +#define IDD_PIXELFORMAT 134 +#define IDD_OPTIONS 137 +#define IDD_ABOUT 138 +#define IDD_USERPF_LIST 139 +#define IDD_UPF_EDIT 140 +#define IDD_INFO 141 +#define IDD_SESSION_INFO 142 +#define IDC_GOTO_EDIT 1003 +#define IDC_PIXELFORMAT 1004 +#define IDC_ASK_PF 1006 +#define IDC_DEFAULT 1008 +#define IDC_VERSION 1008 +#define IDC_ACCEPT_BELL 1009 +#define IDC_BUILDTIME 1009 +#define IDC_ACCEPT_CUT_TEXT 1010 +#define IDC_AUTO_STORE_PARAM 1011 +#define IDC_AUTOPLAY 1012 +#define IDC_BIG_ENDIAN 1013 +#define IDC_EDIT_UPF 1015 +#define IDC_PF_LIST 1016 +#define IDC_BPP_COMBO 1016 +#define IDC_ADD_BUTTON 1017 +#define IDC_NAME_EDIT 1017 +#define IDC_REMOVE_BUTTON 1018 +#define IDC_EDIT_BUTTON 1019 +#define IDC_COPYRIGHT 1021 +#define IDC_DEPTH_EDIT 1021 +#define IDC_DESCRIPTION 1022 +#define IDC_BIGENDIAN_COMBO 1022 +#define IDC_REDMAX_EDIT 1023 +#define IDC_GREENMAX_EDIT 1024 +#define IDC_BLUEMAX_EDIT 1025 +#define IDC_REDSHIFT_EDIT 1026 +#define IDC_DESKTOP_NAME 1026 +#define IDC_GREENSHIFT_EDIT 1027 +#define IDC_DESKTOP_SIZE 1027 +#define IDC_BLUESHIFT_EDIT 1028 +#define IDC_CURRENT_ENCODING 1029 +#define IDC_PIXEL_FORMAT 1030 +#define IDC_INFO_EDIT 1076 +#define ID_OPENFILE 40011 +#define ID_CLOSEFILE 40012 +#define ID_EXIT 40013 +#define ID_FULLSCREEN 40014 +#define ID_ZOOM_50 40015 +#define ID_ZOOM_100 40016 +#define ID_ZOOM_200 40017 +#define ID_PLAYPAUSE 40018 +#define ID_STOP 40019 +#define ID_GOTO 40020 +#define ID_LOOP 40022 +#define ID_COPYTOCLIPBOARD 40023 +#define ID_FRAMEEXTRACT 40024 +#define ID_HELP_HOMEPAGE 40025 +#define ID_HOMEPAGE 40025 +#define ID_HELP_COMMANDLINESWITCHES 40026 +#define ID_HELP_ABOUT 40027 +#define ID_ABOUT 40027 +#define ID_OPTIONS 40029 +#define ID_RETURN 40044 +#define ID_SESSION_INFO 40045 + +// Next default values for new objects +// +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS +#define _APS_NEXT_RESOURCE_VALUE 143 +#define _APS_NEXT_COMMAND_VALUE 40046 +#define _APS_NEXT_CONTROL_VALUE 1031 +#define _APS_NEXT_SYMED_VALUE 101 +#endif +#endif diff --git a/win/rfbplayer/rfbSessionReader.h b/win/rfbplayer/rfbSessionReader.h new file mode 100644 index 00000000..31985bc5 --- /dev/null +++ b/win/rfbplayer/rfbSessionReader.h @@ -0,0 +1,59 @@ +/* Copyright (C) 2004 TightVNC Team. All Rights Reserved. + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +// -=- rfbSessionReader.h + +#include <rfb/Threading.h> + +#include <rfbplayer/RfbProto.h> + +using namespace rfb; + +class rfbSessionReader : public Thread { +public: + rfbSessionReader(RfbProto *_rfbSession) { + rfbSession = _rfbSession; + fStop = false; + rfbSession->interruptFrameDelay(); + }; + + ~rfbSessionReader() { + } + + virtual Thread* join() { + ((FbsInputStream*)(rfbSession->getInStream()))->resumePlayback(); + fStop = true; + return Thread::join(); + } + + virtual void rfbSessionReader::run() { + // Process the rfb messages + while (!fStop) { + try { + rfbSession->processMsg(); + } catch (rdr::Exception e) { + MessageBox(0, e.str(), "RFB Player", MB_OK | MB_ICONERROR); + break; + } + } + } + +protected: + bool fStop; + RfbProto *rfbSession; +};
\ No newline at end of file diff --git a/win/rfbplayer/rfbplayer.cxx b/win/rfbplayer/rfbplayer.cxx new file mode 100644 index 00000000..b304aece --- /dev/null +++ b/win/rfbplayer/rfbplayer.cxx @@ -0,0 +1,1294 @@ +/* Copyright (C) 2004 TightVNC Team. All Rights Reserved. + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +// -=- RFB Player for Win32 +#include <windows.h> +#include <commdlg.h> +#include <shellapi.h> + +#include <rfb/LogWriter.h> + +#include <rfb_win32/AboutDialog.h> +#include <rfb_win32/DeviceContext.h> +#include <rfb_win32/MsgBox.h> +#include <rfb_win32/WMShatter.h> +#include <rfb_win32/Win32Util.h> + +#include <rfbplayer/rfbplayer.h> +#include <rfbplayer/ChoosePixelFormatDialog.h> +#include <rfbplayer/GotoPosDialog.h> +#include <rfbplayer/InfoDialog.h> +#include <rfbplayer/SessionInfoDialog.h> + +using namespace rfb; +using namespace rfb::win32; + +// -=- Variables & consts + +static LogWriter vlog("RfbPlayer"); + +TStr rfb::win32::AppName("RfbPlayer"); +extern const char* buildTime; +HFONT hFont = 0; + +char wrong_cmd_msg[] = + "Wrong command-line parameters!\n" + "Use for help: rfbplayer -help"; + +char usage_msg[] = + "usage: rfbplayer <options> <filename>\r\n" + "Command-line options:\r\n" + " -help \t- Provide usage information.\r\n" + " -pf <mode> \t- Forces the pixel format for the session.\r\n" + " \t <mode>=r<r_bits>g<g_bits>b<b_bits>[le|be],\r\n" + " \t r_bits - size the red component, in bits,\r\n" + " \t g_bits - size the green component, in bits,\r\n" + " \t b_bits - size the blue component, in bits,\r\n" + " \t le - little endian byte order (default),\r\n" + " \t be - big endian byte order.\r\n" + " \t The r, g, b component is in any order.\r\n" + " \t Default: auto detect the pixel format.\r\n" + " -upf <name> \t- Forces the user defined pixel format for\r\n" + " \t the session. If <name> is empty then application\r\n" + " \t shows the list of user defined pixel formats.\r\n" + " \t Don't use this option with -pf.\r\n" + " -speed <value>\t- Sets playback speed, where 1 is normal speed,\r\n" + " \t is double speed, 0.5 is half speed. Default: 1.0.\r\n" + " -pos <ms> \t- Sets initial time position in the session file,\r\n" + " \t in milliseconds. Default: 0.\r\n" + " -autoplay \t- Runs the player in the playback mode.\r\n" + " -loop \t- Replays the rfb session."; + +// -=- RfbPlayer's defines and consts + +#define strcasecmp _stricmp +#define DEFAULT_PLAYER_WIDTH 640 +#define DEFAULT_PLAYER_HEIGHT 480 + +// +// -=- AboutDialog global values +// + +const WORD rfb::win32::AboutDialog::DialogId = IDD_ABOUT; +const WORD rfb::win32::AboutDialog::Copyright = IDC_COPYRIGHT; +const WORD rfb::win32::AboutDialog::Version = IDC_VERSION; +const WORD rfb::win32::AboutDialog::BuildTime = IDC_BUILDTIME; +const WORD rfb::win32::AboutDialog::Description = IDC_DESCRIPTION; + +// +// -=- RfbPlayerClass + +// +// Window class used as the basis for RfbPlayer instance +// + +class RfbPlayerClass { +public: + RfbPlayerClass(); + ~RfbPlayerClass(); + ATOM classAtom; + HINSTANCE instance; +}; + +LRESULT CALLBACK RfbPlayerProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) { + LRESULT result; + + if (msg == WM_CREATE) + SetWindowLong(hwnd, GWL_USERDATA, (long)((CREATESTRUCT*)lParam)->lpCreateParams); + else if (msg == WM_DESTROY) { + RfbPlayer* _this = (RfbPlayer*) GetWindowLong(hwnd, GWL_USERDATA); + SetWindowLong(hwnd, GWL_USERDATA, 0); + } + RfbPlayer* _this = (RfbPlayer*) GetWindowLong(hwnd, GWL_USERDATA); + if (!_this) { + vlog.info("null _this in %x, message %u", hwnd, msg); + return DefWindowProc(hwnd, msg, wParam, lParam); + } + + try { + result = _this->processMainMessage(hwnd, msg, wParam, lParam); + } catch (rdr::Exception& e) { + vlog.error("untrapped: %s", e.str()); + } + + return result; +}; + +RfbPlayerClass::RfbPlayerClass() : classAtom(0) { + WNDCLASS wndClass; + wndClass.style = 0; + wndClass.lpfnWndProc = RfbPlayerProc; + wndClass.cbClsExtra = 0; + wndClass.cbWndExtra = 0; + wndClass.hInstance = instance = GetModuleHandle(0); + wndClass.hIcon = (HICON)LoadImage(GetModuleHandle(0), + MAKEINTRESOURCE(IDI_ICON), IMAGE_ICON, 0, 0, LR_SHARED); + if (!wndClass.hIcon) + printf("unable to load icon:%ld", GetLastError()); + wndClass.hCursor = LoadCursor(NULL, IDC_ARROW); + wndClass.hbrBackground = HBRUSH(COLOR_WINDOW); + wndClass.lpszMenuName = MAKEINTRESOURCE(IDR_MENU); + wndClass.lpszClassName = _T("RfbPlayerClass"); + classAtom = RegisterClass(&wndClass); + if (!classAtom) { + throw rdr::SystemException("unable to register RfbPlayer window class", + GetLastError()); + } +} + +RfbPlayerClass::~RfbPlayerClass() { + if (classAtom) { + UnregisterClass((const TCHAR*)classAtom, instance); + } +} + +RfbPlayerClass baseClass; + +// +// -=- RfbFrameClass + +// +// Window class used to displaying the rfb data +// + +class RfbFrameClass { +public: + RfbFrameClass(); + ~RfbFrameClass(); + ATOM classAtom; + HINSTANCE instance; +}; + +LRESULT CALLBACK FrameProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) { + LRESULT result; + + if (msg == WM_CREATE) + SetWindowLong(hwnd, GWL_USERDATA, (long)((CREATESTRUCT*)lParam)->lpCreateParams); + else if (msg == WM_DESTROY) + SetWindowLong(hwnd, GWL_USERDATA, 0); + RfbPlayer* _this = (RfbPlayer*) GetWindowLong(hwnd, GWL_USERDATA); + if (!_this) { + vlog.info("null _this in %x, message %u", hwnd, msg); + return DefWindowProc(hwnd, msg, wParam, lParam); + } + + try { + result = _this->processFrameMessage(hwnd, msg, wParam, lParam); + } catch (rdr::Exception& e) { + vlog.error("untrapped: %s", e.str()); + } + + return result; +} + +RfbFrameClass::RfbFrameClass() : classAtom(0) { + WNDCLASS wndClass; + wndClass.style = 0; + wndClass.lpfnWndProc = FrameProc; + wndClass.cbClsExtra = 0; + wndClass.cbWndExtra = 0; + wndClass.hInstance = instance = GetModuleHandle(0); + wndClass.hIcon = 0; + wndClass.hCursor = LoadCursor(NULL, IDC_ARROW); + wndClass.hbrBackground = 0; + wndClass.lpszMenuName = 0; + wndClass.lpszClassName = _T("RfbPlayerClass1"); + classAtom = RegisterClass(&wndClass); + if (!classAtom) { + throw rdr::SystemException("unable to register RfbPlayer window class", + GetLastError()); + } +} + +RfbFrameClass::~RfbFrameClass() { + if (classAtom) { + UnregisterClass((const TCHAR*)classAtom, instance); + } +} + +RfbFrameClass frameClass; + +// +// -=- RfbPlayer instance implementation +// + +RfbPlayer::RfbPlayer(char *_fileName, PlayerOptions *_options) +: RfbProto(_fileName), fileName(_fileName), buffer(0), client_size(0, 0, 32, 32), + window_size(0, 0, 32, 32), cutText(0), seekMode(false), lastPos(0), + rfbReader(0), sessionTimeMs(0), sliderStepMs(0), imageDataStartTime(0), + rewindFlag(false), stopped(false), currentEncoding(-1) { + + // Save the player options + memcpy(&options, _options, sizeof(options)); + + // Reset the full session time + strcpy(fullSessionTime, "00m:00s"); + + // Load the user defined pixel formats from the registry + supportedPF.readUserDefinedPF(HKEY_CURRENT_USER, UPF_REGISTRY_PATH); + + // Create the main window + const TCHAR* name = _T("RfbPlayer"); + int x = max(0, (GetSystemMetrics(SM_CXSCREEN) - DEFAULT_PLAYER_WIDTH) / 2); + int y = max(0, (GetSystemMetrics(SM_CYSCREEN) - DEFAULT_PLAYER_HEIGHT) / 2); + mainHwnd = CreateWindow((const TCHAR*)baseClass.classAtom, name, WS_OVERLAPPEDWINDOW, + x, y, DEFAULT_PLAYER_WIDTH, DEFAULT_PLAYER_HEIGHT, 0, 0, baseClass.instance, this); + if (!mainHwnd) { + throw rdr::SystemException("unable to create WMNotifier window instance", GetLastError()); + } + vlog.debug("created window \"%s\" (%x)", (const char*)CStr(name), getMainHandle()); + + // Create the backing buffer + buffer = new win32::DIBSectionBuffer(getFrameHandle()); + setVisible(true); + + // If run with command-line parameters, + // open the session file with default settings, otherwise + // restore player settings from the registry + if (fileName) { + openSessionFile(fileName); + if (options.initTime > 0) setPos(options.initTime); + setSpeed(options.playbackSpeed); + } else { + options.readFromRegistry(); + disableTBandMenuItems(); + setTitle("None"); + } + init(); +} + +RfbPlayer::~RfbPlayer() { + vlog.debug("~RfbPlayer"); + if (rfbReader) { + delete rfbReader->join(); + rfbReader = 0; + } + if (mainHwnd) { + setVisible(false); + DestroyWindow(mainHwnd); + mainHwnd = 0; + } + if (buffer) delete buffer; + if (cutText) delete [] cutText; + if (fileName) delete [] fileName; + if (hFont) DeleteObject(hFont); + vlog.debug("~RfbPlayer done"); +} + +LRESULT +RfbPlayer::processMainMessage(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) { + + switch (msg) { + + // -=- Process standard window messages + + case WM_CREATE: + { + tb.create(this, hwnd); + + // Create the frame window + frameHwnd = CreateWindowEx(WS_EX_CLIENTEDGE, (const TCHAR*)frameClass.classAtom, + 0, WS_CHILD | WS_VISIBLE, 0, tb.getHeight(), 10, tb.getHeight() + 10, + hwnd, 0, frameClass.instance, this); + + hMenu = GetMenu(hwnd); + + return 0; + } + + // Process the main menu and toolbar's messages + + case WM_COMMAND: + + switch (LOWORD(wParam)) { + case ID_OPENFILE: + { + char curDir[_MAX_DIR]; + static char filename[_MAX_PATH]; + OPENFILENAME ofn; + memset((void *) &ofn, 0, sizeof(OPENFILENAME)); + GetCurrentDirectory(sizeof(curDir), curDir); + + ofn.lStructSize = sizeof(OPENFILENAME); + ofn.hwndOwner = getMainHandle(); + ofn.lpstrFile = filename; + ofn.nMaxFile = sizeof(filename); + ofn.lpstrInitialDir = curDir; + ofn.lpstrFilter = "Rfb Session files (*.rfb, *.fbs)\0*.rfb;*.fbs\0" \ + "All files (*.*)\0*.*\0"; + ofn.lpstrDefExt = "rfb"; + ofn.Flags = OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST; + if (GetOpenFileName(&ofn)) { + openSessionFile(filename); + } + } + break; + case ID_CLOSEFILE: + closeSessionFile(); + break; + case ID_SESSION_INFO: + { + SessionInfoDialog sessionInfo(&cp, currentEncoding); + sessionInfo.showDialog(getMainHandle()); + } + break; + case ID_PLAY: + setPaused(false); + break; + case ID_PAUSE: + setPaused(true); + break; + case ID_STOP: + stopPlayback(); + break; + case ID_PLAYPAUSE: + if (rfbReader) { + if (isPaused()) { + setPaused(false); + } else { + setPaused(true); + } + } + break; + case ID_GOTO: + { + GotoPosDialog gotoPosDlg; + if (gotoPosDlg.showDialog(getMainHandle())) { + long gotoTime = min(gotoPosDlg.getPos(), sessionTimeMs); + setPos(gotoTime); + tb.updatePos(gotoTime); + setPaused(isPaused());; + } + } + break; + case ID_LOOP: + options.loopPlayback = !options.loopPlayback; + if (options.loopPlayback) CheckMenuItem(hMenu, ID_LOOP, MF_CHECKED); + else CheckMenuItem(hMenu, ID_LOOP, MF_UNCHECKED); + break; + case ID_RETURN: + tb.processWM_COMMAND(wParam, lParam); + break; + case ID_OPTIONS: + { + OptionsDialog optionsDialog(&options, &supportedPF); + optionsDialog.showDialog(getMainHandle()); + } + break; + case ID_EXIT: + PostQuitMessage(0); + break; + case ID_HOMEPAGE: + ShellExecute(getMainHandle(), _T("open"), _T("http://www.tightvnc.com/"), + NULL, NULL, SW_SHOWDEFAULT); + break; + case ID_HELP_COMMANDLINESWITCHES: + { + InfoDialog usageDialog(usage_msg); + usageDialog.showDialog(getMainHandle()); + } + break; + case ID_ABOUT: + AboutDialog::instance.showDialog(); + break; + } + break; + + // Update frame's window size and add scrollbars if required + + case WM_SIZE: + { + + Point old_offset = bufferToClient(Point(0, 0)); + + // Update the cached sizing information + RECT r; + GetClientRect(getMainHandle(), &r); + MoveWindow(getFrameHandle(), 0, tb.getHeight(), r.right - r.left, + r.bottom - r.top - tb.getHeight(), TRUE); + + GetWindowRect(getFrameHandle(), &r); + window_size = Rect(r.left, r.top, r.right, r.bottom); + GetClientRect(getFrameHandle(), &r); + client_size = Rect(r.left, r.top, r.right, r.bottom); + + // Determine whether scrollbars are required + calculateScrollBars(); + + // Resize the ToolBar + tb.autoSize(); + + // Redraw if required + if (!old_offset.equals(bufferToClient(Point(0, 0)))) + InvalidateRect(getFrameHandle(), 0, TRUE); + } + break; + + case WM_HSCROLL: + tb.processWM_HSCROLL(wParam, lParam); + break; + + case WM_NOTIFY: + return tb.processWM_NOTIFY(wParam, lParam); + + case WM_CLOSE: + vlog.debug("WM_CLOSE %x", getMainHandle()); + PostQuitMessage(0); + break; + } + + return rfb::win32::SafeDefWindowProc(getMainHandle(), msg, wParam, lParam); +} + +LRESULT RfbPlayer::processFrameMessage(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) { + switch (msg) { + + case WM_PAINT: + { + if (isSeeking() || rewindFlag) { + seekMode = true; + return 0; + } else { + if (seekMode) { + seekMode = false; + InvalidateRect(getFrameHandle(), 0, true); + UpdateWindow(getFrameHandle()); + return 0; + } + } + + PAINTSTRUCT ps; + HDC paintDC = BeginPaint(getFrameHandle(), &ps); + if (!paintDC) + throw SystemException("unable to BeginPaint", GetLastError()); + Rect pr = Rect(ps.rcPaint.left, ps.rcPaint.top, ps.rcPaint.right, ps.rcPaint.bottom); + + if (!pr.is_empty()) { + + if (buffer->bitmap) { + + // Get device context + BitmapDC bitmapDC(paintDC, buffer->bitmap); + + // Blit the border if required + Rect bufpos = bufferToClient(buffer->getRect()); + if (!pr.enclosed_by(bufpos)) { + vlog.debug("draw border"); + HBRUSH black = (HBRUSH) GetStockObject(BLACK_BRUSH); + RECT r; + SetRect(&r, 0, 0, bufpos.tl.x, client_size.height()); FillRect(paintDC, &r, black); + SetRect(&r, bufpos.tl.x, 0, bufpos.br.x, bufpos.tl.y); FillRect(paintDC, &r, black); + SetRect(&r, bufpos.br.x, 0, client_size.width(), client_size.height()); FillRect(paintDC, &r, black); + SetRect(&r, bufpos.tl.x, bufpos.br.y, bufpos.br.x, client_size.height()); FillRect(paintDC, &r, black); + } + + // Do the blit + Point buf_pos = clientToBuffer(pr.tl); + if (!BitBlt(paintDC, pr.tl.x, pr.tl.y, pr.width(), pr.height(), + bitmapDC, buf_pos.x, buf_pos.y, SRCCOPY)) + throw SystemException("unable to BitBlt to window", GetLastError()); + + } else { + // Blit a load of black + if (!BitBlt(paintDC, pr.tl.x, pr.tl.y, pr.width(), pr.height(), + 0, 0, 0, BLACKNESS)) + throw SystemException("unable to BitBlt to blank window", GetLastError()); + } + } + EndPaint(getFrameHandle(), &ps); + } + return 0; + + // Process play/pause by the left mouse button + case WM_LBUTTONDOWN: + SendMessage(getMainHandle(), WM_COMMAND, ID_PLAYPAUSE, 0); + return 0; + + case WM_VSCROLL: + case WM_HSCROLL: + { + Point delta; + int newpos = (msg == WM_VSCROLL) ? scrolloffset.y : scrolloffset.x; + + switch (LOWORD(wParam)) { + case SB_PAGEUP: newpos -= 50; break; + case SB_PAGEDOWN: newpos += 50; break; + case SB_LINEUP: newpos -= 5; break; + case SB_LINEDOWN: newpos += 5; break; + case SB_THUMBTRACK: + case SB_THUMBPOSITION: newpos = HIWORD(wParam); break; + default: vlog.info("received unknown scroll message"); + }; + + if (msg == WM_HSCROLL) + setViewportOffset(Point(newpos, scrolloffset.y)); + else + setViewportOffset(Point(scrolloffset.x, newpos)); + + SCROLLINFO si; + si.cbSize = sizeof(si); + si.fMask = SIF_POS; + si.nPos = newpos; + SetScrollInfo(getFrameHandle(), (msg == WM_VSCROLL) ? SB_VERT : SB_HORZ, &si, TRUE); + } + break; + } + + return DefWindowProc(hwnd, msg, wParam, lParam); +} + +void RfbPlayer::disableTBandMenuItems() { + // Disable the menu items + EnableMenuItem(hMenu, ID_CLOSEFILE, MF_GRAYED | MF_BYCOMMAND); + EnableMenuItem(hMenu, ID_SESSION_INFO, MF_GRAYED | MF_BYCOMMAND); + ///EnableMenuItem(hMenu, ID_FULLSCREEN, MF_GRAYED | MF_BYCOMMAND); + ///EnableMenuItem(GetSubMenu(hMenu, 1), 1, MF_GRAYED | MF_BYPOSITION); + EnableMenuItem(hMenu, ID_PLAYPAUSE, MF_GRAYED | MF_BYCOMMAND); + EnableMenuItem(hMenu, ID_STOP, MF_GRAYED | MF_BYCOMMAND); + EnableMenuItem(hMenu, ID_GOTO, MF_GRAYED | MF_BYCOMMAND); + EnableMenuItem(hMenu, ID_LOOP, MF_GRAYED | MF_BYCOMMAND); + ///EnableMenuItem(hMenu, ID_COPYTOCLIPBOARD, MF_GRAYED | MF_BYCOMMAND); + ///EnableMenuItem(hMenu, ID_FRAMEEXTRACT, MF_GRAYED | MF_BYCOMMAND); + + // Disable the toolbar buttons and child controls + tb.disable(); +} + +void RfbPlayer::enableTBandMenuItems() { + // Enable the menu items + EnableMenuItem(hMenu, ID_CLOSEFILE, MF_ENABLED | MF_BYCOMMAND); + EnableMenuItem(hMenu, ID_SESSION_INFO, MF_ENABLED | MF_BYCOMMAND); + ///EnableMenuItem(hMenu, ID_FULLSCREEN, MF_ENABLED | MF_BYCOMMAND); + ///EnableMenuItem(GetSubMenu(hMenu, 1), 1, MF_ENABLED | MF_BYPOSITION); + EnableMenuItem(hMenu, ID_PLAYPAUSE, MF_ENABLED | MF_BYCOMMAND); + EnableMenuItem(hMenu, ID_STOP, MF_ENABLED | MF_BYCOMMAND); + EnableMenuItem(hMenu, ID_GOTO, MF_ENABLED | MF_BYCOMMAND); + EnableMenuItem(hMenu, ID_LOOP, MF_ENABLED | MF_BYCOMMAND); + ///EnableMenuItem(hMenu, ID_COPYTOCLIPBOARD, MF_ENABLED | MF_BYCOMMAND); + ///EnableMenuItem(hMenu, ID_FRAMEEXTRACT, MF_ENABLED | MF_BYCOMMAND); + + // Enable the toolbar buttons and child controls + tb.enable(); +} + +void RfbPlayer::setVisible(bool visible) { + ShowWindow(getMainHandle(), visible ? SW_SHOW : SW_HIDE); + if (visible) { + // When the window becomes visible, make it active + SetForegroundWindow(getMainHandle()); + SetActiveWindow(getMainHandle()); + } +} + +void RfbPlayer::setTitle(const char *title) { + char _title[256]; + strcpy(_title, AppName); + strcat(_title, " - "); + strcat(_title, title); + SetWindowText(getMainHandle(), _title); +} + +void RfbPlayer::setFrameSize(int width, int height) { + // Calculate and set required size for main window + RECT r = {0, 0, width, height}; + AdjustWindowRectEx(&r, GetWindowLong(getFrameHandle(), GWL_STYLE), TRUE, + GetWindowLong(getFrameHandle(), GWL_EXSTYLE)); + r.bottom += tb.getHeight(); // Include RfbPlayr's controls area + AdjustWindowRect(&r, GetWindowLong(getMainHandle(), GWL_STYLE), FALSE); + int x = max(0, (GetSystemMetrics(SM_CXSCREEN) - (r.right - r.left)) / 2); + int y = max(0, (GetSystemMetrics(SM_CYSCREEN) - (r.bottom - r.top)) / 2); + SetWindowPos(getMainHandle(), 0, x, y, r.right-r.left, r.bottom-r.top, + SWP_NOZORDER | SWP_NOACTIVATE | SWP_NOOWNERZORDER); + + // Enable/disable scrollbars as appropriate + calculateScrollBars(); +} + +void RfbPlayer::calculateScrollBars() { + // Calculate the required size of window + DWORD current_style = GetWindowLong(getFrameHandle(), GWL_STYLE); + DWORD style = current_style & ~(WS_VSCROLL | WS_HSCROLL); + DWORD old_style; + RECT r; + SetRect(&r, 0, 0, buffer->width(), buffer->height()); + AdjustWindowRectEx(&r, style, FALSE, GetWindowLong(getFrameHandle(), GWL_EXSTYLE)); + Rect reqd_size = Rect(r.left, r.top, r.right, r.bottom); + + // Work out whether scroll bars are required + do { + old_style = style; + + if (!(style & WS_HSCROLL) && (reqd_size.width() > window_size.width())) { + style |= WS_HSCROLL; + reqd_size.br.y += GetSystemMetrics(SM_CXHSCROLL); + } + if (!(style & WS_VSCROLL) && (reqd_size.height() > window_size.height())) { + style |= WS_VSCROLL; + reqd_size.br.x += GetSystemMetrics(SM_CXVSCROLL); + } + } while (style != old_style); + + // Tell Windows to update the window style & cached settings + if (style != current_style) { + SetWindowLong(getFrameHandle(), GWL_STYLE, style); + SetWindowPos(getFrameHandle(), NULL, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_FRAMECHANGED); + } + + // Update the scroll settings + SCROLLINFO si; + if (style & WS_VSCROLL) { + si.cbSize = sizeof(si); + si.fMask = SIF_RANGE | SIF_PAGE | SIF_POS; + si.nMin = 0; + si.nMax = buffer->height(); + si.nPage = buffer->height() - (reqd_size.height() - window_size.height()); + maxscrolloffset.y = max(0, si.nMax-si.nPage); + scrolloffset.y = min(maxscrolloffset.y, scrolloffset.y); + si.nPos = scrolloffset.y; + SetScrollInfo(getFrameHandle(), SB_VERT, &si, TRUE); + } + if (style & WS_HSCROLL) { + si.cbSize = sizeof(si); + si.fMask = SIF_RANGE | SIF_PAGE | SIF_POS; + si.nMin = 0; + si.nMax = buffer->width(); + si.nPage = buffer->width() - (reqd_size.width() - window_size.width()); + maxscrolloffset.x = max(0, si.nMax-si.nPage); + scrolloffset.x = min(maxscrolloffset.x, scrolloffset.x); + si.nPos = scrolloffset.x; + SetScrollInfo(getFrameHandle(), SB_HORZ, &si, TRUE); + } + + // Update the cached client size + GetClientRect(getFrameHandle(), &r); + client_size = Rect(r.left, r.top, r.right, r.bottom); +} + +bool RfbPlayer::setViewportOffset(const Point& tl) { +/* *** + Point np = Point(max(0, min(maxscrolloffset.x, tl.x)), + max(0, min(maxscrolloffset.y, tl.y))); + */ + Point np = Point(max(0, min(tl.x, buffer->width()-client_size.width())), + max(0, min(tl.y, buffer->height()-client_size.height()))); + Point delta = np.translate(scrolloffset.negate()); + if (!np.equals(scrolloffset)) { + scrolloffset = np; + ScrollWindowEx(getFrameHandle(), -delta.x, -delta.y, 0, 0, 0, 0, SW_INVALIDATE); + UpdateWindow(getFrameHandle()); + return true; + } + return false; +} + +void RfbPlayer::close(const char* reason) { + setVisible(false); + if (reason) { + vlog.info("closing - %s", reason); + MessageBox(NULL, TStr(reason), "RfbPlayer", MB_ICONINFORMATION | MB_OK); + } + SendMessage(getFrameHandle(), WM_CLOSE, 0, 0); +} + +void RfbPlayer::blankBuffer() { + fillRect(buffer->getRect(), 0); +} + +void RfbPlayer::rewind() { + bool paused = isPaused(); + blankBuffer(); + newSession(fileName); + skipHandshaking(); + is->setSpeed(options.playbackSpeed); + if (paused) is->pausePlayback(); + else is->resumePlayback(); +} + +void RfbPlayer::processMsg() { + // Perform return if waitWhilePaused processed because + // rfbReader thread could receive the signal to close + if (waitWhilePaused()) return; + + static long update_time = GetTickCount(); + try { + if ((!isSeeking()) && ((GetTickCount() - update_time) > 250) + && (!tb.isPosSliderDragging())) { + // Update pos in the toolbar 4 times in 1 second + tb.updatePos(getTimeOffset()); + update_time = GetTickCount(); + } + RfbProto::processMsg(); + } catch (rdr::Exception e) { + if (strcmp(e.str(), "[End Of File]") == 0) { + rewind(); + setPaused(!options.loopPlayback); + tb.updatePos(getTimeOffset()); + return; + } + // It's a special exception to perform backward seeking. + // We only rewind the stream and seek the offset + if (strcmp(e.str(), "[REWIND]") == 0) { + rewindFlag = true; + long seekOffset = max(getSeekOffset(), imageDataStartTime); + rewind(); + if (!stopped) setPos(seekOffset); + else stopped = false; + tb.updatePos(seekOffset); + rewindFlag = false; + return; + } + // It's a special exception which is used to terminate the playback + if (strcmp(e.str(), "[TERMINATE]") == 0) { + sessionTerminateThread *terminate = new sessionTerminateThread(this); + terminate->start(); + } else { + // Show the exception message and close the session playback + is->pausePlayback(); + char message[256] = "\0"; + strcat(message, e.str()); + strcat(message, "\nMaybe you force wrong the pixel format for this session"); + MessageBox(getMainHandle(), message, "RFB Player", MB_OK | MB_ICONERROR); + sessionTerminateThread *terminate = new sessionTerminateThread(this); + terminate->start(); + return; + } + } +} + +long ChoosePixelFormatDialog::pfIndex = DEFAULT_PF_INDEX; +bool ChoosePixelFormatDialog::bigEndian = false; + +void RfbPlayer::serverInit() { + RfbProto::serverInit(); + + // Save the image data start time + imageDataStartTime = is->getTimeOffset(); + + // Resize the backing buffer + buffer->setSize(cp.width, cp.height); + + // Check on the true colour mode + if (!(cp.pf()).trueColour) + throw rdr::Exception("This version plays only true color session!"); + + // Set the session pixel format + if (options.askPixelFormat) { + ChoosePixelFormatDialog choosePixelFormatDialog(&supportedPF); + if (choosePixelFormatDialog.showDialog(getMainHandle())) { + long pixelFormatIndex = choosePixelFormatDialog.getPFIndex(); + if (pixelFormatIndex < 0) { + options.autoDetectPF = true; + options.setPF((PixelFormat *)&cp.pf()); + } else { + options.autoDetectPF = false; + options.setPF(&supportedPF[pixelFormatIndex]->PF); + options.pixelFormat.bigEndian = choosePixelFormatDialog.isBigEndian(); + } + } else { + is->pausePlayback(); + throw rdr::Exception("[TERMINATE]"); + } + } else { + if (!options.commandLineParam) { + if (options.autoDetectPF) { + options.setPF((PixelFormat *)&cp.pf()); + } else { + options.setPF(&supportedPF[options.pixelFormatIndex]->PF); + options.pixelFormat.bigEndian = options.bigEndianFlag; + } + } else if (options.autoDetectPF) { + options.setPF((PixelFormat *)&cp.pf()); + } + } + cp.setPF(options.pixelFormat); + buffer->setPF(options.pixelFormat); + + // If the window is not maximised then resize it + if (!(GetWindowLong(getMainHandle(), GWL_STYLE) & WS_MAXIMIZE)) + setFrameSize(cp.width, cp.height); + + // Set the window title and show it + setTitle(cp.name()); + + // Calculate the full session time and update posTrackBar control in toolbar + sessionTimeMs = calculateSessionTime(fileName); + tb.init(sessionTimeMs); + tb.updatePos(getTimeOffset()); + + setPaused(!options.autoPlay); + // Restore the parameters from registry, + // which was replaced by command-line parameters. + if (options.commandLineParam) { + options.readFromRegistry(); + options.commandLineParam = false; + } +} + +void RfbPlayer::setColourMapEntries(int first, int count, U16* rgbs) { + vlog.debug("setColourMapEntries: first=%d, count=%d", first, count); + throw rdr::Exception("Can't handle SetColourMapEntries message"); +/* int i; + for (i=0;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(getFrameHandle(), 0, FALSE);*/ +} + +void RfbPlayer::bell() { + if (options.acceptBell) + MessageBeep(-1); +} + +void RfbPlayer::serverCutText(const char* str, int len) { + if (cutText != NULL) + delete [] cutText; + cutText = new char[len + 1]; + memcpy(cutText, str, len); + cutText[len] = '\0'; +} + +void RfbPlayer::frameBufferUpdateEnd() { +}; + +void RfbPlayer::beginRect(const Rect& r, unsigned int encoding) { + currentEncoding = encoding; +} + +void RfbPlayer::endRect(const Rect& r, unsigned int encoding) { +} + + +void RfbPlayer::fillRect(const Rect& r, Pixel pix) { + buffer->fillRect(r, pix); + invalidateBufferRect(r); +} + +void RfbPlayer::imageRect(const Rect& r, void* pixels) { + buffer->imageRect(r, pixels); + invalidateBufferRect(r); +} + +void RfbPlayer::copyRect(const Rect& r, int srcX, int srcY) { + buffer->copyRect(r, Point(r.tl.x-srcX, r.tl.y-srcY)); + invalidateBufferRect(r); +} + +bool RfbPlayer::invalidateBufferRect(const Rect& crect) { + Rect rect = bufferToClient(crect); + if (rect.intersect(client_size).is_empty()) return false; + RECT invalid = {rect.tl.x, rect.tl.y, rect.br.x, rect.br.y}; + InvalidateRect(getFrameHandle(), &invalid, FALSE); + return true; +} + +bool RfbPlayer::waitWhilePaused() { + bool result = false; + while(isPaused() && !isSeeking()) { + Sleep(20); + result = true; + } + return result; +} + +long RfbPlayer::calculateSessionTime(char *filename) { + FbsInputStream sessionFile(filename); + sessionFile.setTimeOffset(100000000); + try { + while (TRUE) { + sessionFile.skip(1024); + } + } catch (rdr::Exception e) { + if (strcmp(e.str(), "[End Of File]") == 0) { + return sessionFile.getTimeOffset(); + } else { + MessageBox(getMainHandle(), e.str(), "RFB Player", MB_OK | MB_ICONERROR); + return 0; + } + } + return 0; +} + +void RfbPlayer::init() { + if (options.loopPlayback) CheckMenuItem(hMenu, ID_LOOP, MF_CHECKED); + else CheckMenuItem(hMenu, ID_LOOP, MF_UNCHECKED); +} + +void RfbPlayer::closeSessionFile() { + DWORD dwStyle; + RECT r; + + // Uncheck all toolbar buttons + if (tb.getHandle()) { + tb.checkButton(ID_PLAY, false); + tb.checkButton(ID_PAUSE, false); + tb.checkButton(ID_STOP, false); + } + + // Stop playback and update the player state + disableTBandMenuItems(); + if (rfbReader) { + delete rfbReader->join(); + rfbReader = 0; + delete [] fileName; + fileName = 0; + } + blankBuffer(); + setTitle("None"); + options.playbackSpeed = 1.0; + tb.init(0); + + // Change the player window size and frame size to default + if ((dwStyle = GetWindowLong(getMainHandle(), GWL_STYLE)) & WS_MAXIMIZE) { + dwStyle &= ~WS_MAXIMIZE; + SetWindowLong(getMainHandle(), GWL_STYLE, dwStyle); + } + int x = max(0, (GetSystemMetrics(SM_CXSCREEN) - DEFAULT_PLAYER_WIDTH) / 2); + int y = max(0, (GetSystemMetrics(SM_CYSCREEN) - DEFAULT_PLAYER_HEIGHT) / 2); + SetWindowPos(getMainHandle(), 0, x, y, + DEFAULT_PLAYER_WIDTH, DEFAULT_PLAYER_HEIGHT, + SWP_NOZORDER | SWP_FRAMECHANGED); + buffer->setSize(32, 32); + calculateScrollBars(); + + // Update the cached sizing information and repaint the frame window + GetWindowRect(getFrameHandle(), &r); + window_size = Rect(r.left, r.top, r.right, r.bottom); + GetClientRect(getFrameHandle(), &r); + client_size = Rect(r.left, r.top, r.right, r.bottom); + InvalidateRect(getFrameHandle(), 0, TRUE); + UpdateWindow(getFrameHandle()); +} + +void RfbPlayer::openSessionFile(char *_fileName) { + fileName = strDup(_fileName); + + // Close the previous reading thread + if (rfbReader) { + delete rfbReader->join(); + rfbReader = 0; + } + blankBuffer(); + newSession(fileName); + setSpeed(options.playbackSpeed); + rfbReader = new rfbSessionReader(this); + rfbReader->start(); + tb.setTimePos(0); + enableTBandMenuItems(); +} + +void RfbPlayer::setPaused(bool paused) { + if (paused) { + is->pausePlayback(); + tb.checkButton(ID_PAUSE, true); + tb.checkButton(ID_PLAY, false); + tb.checkButton(ID_STOP, false); + } else { + if (is) is->resumePlayback(); + tb.checkButton(ID_PLAY, true); + tb.checkButton(ID_STOP, false); + tb.checkButton(ID_PAUSE, false); + } + tb.enableButton(ID_PAUSE, true); + EnableMenuItem(hMenu, ID_STOP, MF_ENABLED | MF_BYCOMMAND); +} + +void RfbPlayer::stopPlayback() { + stopped = true; + setPos(0); + if (is) { + is->pausePlayback(); + is->interruptFrameDelay(); + } + tb.checkButton(ID_STOP, true); + tb.checkButton(ID_PLAY, false); + tb.checkButton(ID_PAUSE, false); + tb.enableButton(ID_PAUSE, false); + tb.setTimePos(0); + EnableMenuItem(hMenu, ID_STOP, MF_GRAYED | MF_BYCOMMAND); +} + +void RfbPlayer::setSpeed(double speed) { + if (speed > 0) { + char speedStr[20] = "\0"; + double newSpeed = min(speed, MAX_SPEED); + is->setSpeed(newSpeed); + options.playbackSpeed = newSpeed; + SendMessage(tb.getSpeedUpDownHwnd(), UDM_SETPOS, + 0, MAKELONG((short)(newSpeed / 0.5), 0)); + sprintf(speedStr, "%.2f", newSpeed); + SetWindowText(tb.getSpeedEditHwnd(), speedStr); + } +} + +double RfbPlayer::getSpeed() { + return is->getSpeed(); +} + +void RfbPlayer::setPos(long pos) { + is->setTimeOffset(max(pos, imageDataStartTime)); +} + +long RfbPlayer::getSeekOffset() { + return is->getSeekOffset(); +} + +bool RfbPlayer::isSeeking() { + if (is) return is->isSeeking(); + else return false; +} + +bool RfbPlayer::isSeekMode() { + return seekMode; +} + +bool RfbPlayer::isPaused() { + return is->isPaused(); +} + +long RfbPlayer::getTimeOffset() { + return max(is->getTimeOffset(), is->getSeekOffset()); +} + +void RfbPlayer::skipHandshaking() { + int skipBytes = 12 + 4 + 24 + strlen(cp.name()); + is->skip(skipBytes); + state_ = RFBSTATE_NORMAL; +} + +void programInfo() { + win32::FileVersionInfo inf; + _tprintf(_T("%s - %s, Version %s\n"), + inf.getVerString(_T("ProductName")), + inf.getVerString(_T("FileDescription")), + inf.getVerString(_T("FileVersion"))); + printf("%s\n", buildTime); + _tprintf(_T("%s\n\n"), inf.getVerString(_T("LegalCopyright"))); +} + +void programUsage() { + InfoDialog usageDialog(usage_msg); + usageDialog.showDialog(); +} + +char *fileName = 0; + +// playerOptions is the player options with default parameters values, +// it is used only for run the player with command-line parameters +PlayerOptions playerOptions; +bool print_usage = false; +bool print_upf_list = false; + +bool processParams(int argc, char* argv[]) { + playerOptions.commandLineParam = true; + for (int i = 1; i < argc; i++) { + if ((strcasecmp(argv[i], "-help") == 0) || + (strcasecmp(argv[i], "--help") == 0) || + (strcasecmp(argv[i], "/help") == 0) || + (strcasecmp(argv[i], "-h") == 0) || + (strcasecmp(argv[i], "/h") == 0) || + (strcasecmp(argv[i], "/?") == 0) || + (strcasecmp(argv[i], "-?") == 0)) { + print_usage = true; + return true; + } + + if ((strcasecmp(argv[i], "-pf") == 0) || + (strcasecmp(argv[i], "/pf") == 0) && (i < argc-1)) { + char *pf = argv[++i]; + char rgb_order[4] = "\0"; + int order = RGB_ORDER; + int r = -1, g = -1, b = -1; + bool big_endian = false; + if (strlen(pf) < 6) return false; + while (strlen(pf)) { + if ((pf[0] == 'r') || (pf[0] == 'R')) { + if (r >=0 ) return false; + r = atoi(++pf); + strcat(rgb_order, "r"); + continue; + } + if ((pf[0] == 'g') || (pf[0] == 'G')) { + if (g >=0 ) return false; + g = atoi(++pf); + strcat(rgb_order, "g"); + continue; + } + if (((pf[0] == 'b') || (pf[0] == 'B')) && + (pf[1] != 'e') && (pf[1] != 'E')) { + if (b >=0 ) return false; + b = atoi(++pf); + strcat(rgb_order, "b"); + continue; + } + if ((pf[0] == 'l') || (pf[0] == 'L') || + (pf[0] == 'b') || (pf[0] == 'B')) { + if (strcasecmp(pf, "le") == 0) break; + if (strcasecmp(pf, "be") == 0) { big_endian = true; break;} + return false; + } + pf++; + } + if ((r < 0) || (g < 0) || (b < 0) || (r + g + b > 32)) return false; + if (strcasecmp(rgb_order, "rgb") == 0) { order = RGB_ORDER; } + else if (strcasecmp(rgb_order, "rbg") == 0) { order = RBG_ORDER; } + else if (strcasecmp(rgb_order, "grb") == 0) { order = GRB_ORDER; } + else if (strcasecmp(rgb_order, "gbr") == 0) { order = GBR_ORDER; } + else if (strcasecmp(rgb_order, "bgr") == 0) { order = BGR_ORDER; } + else if (strcasecmp(rgb_order, "brg") == 0) { order = BRG_ORDER; } + else return false; + playerOptions.autoDetectPF = false; + playerOptions.setPF(order, r, g, b, big_endian); + continue; + } + + if ((strcasecmp(argv[i], "-upf") == 0) || + (strcasecmp(argv[i], "/upf") == 0) && (i < argc-1)) { + if ((i == argc - 1) || (argv[++i][0] == '-')) { + print_upf_list = true; + return true; + } + PixelFormatList userPfList; + userPfList.readUserDefinedPF(HKEY_CURRENT_USER, UPF_REGISTRY_PATH); + int index = userPfList.getIndexByPFName(argv[i]); + if (index > 0) { + playerOptions.autoDetectPF = false; + playerOptions.setPF(&userPfList[index]->PF); + } else { + return false; + } + continue; + } + + if ((strcasecmp(argv[i], "-speed") == 0) || + (strcasecmp(argv[i], "/speed") == 0) && (i < argc-1)) { + double playbackSpeed = atof(argv[++i]); + if (playbackSpeed <= 0) { + return false; + } + playerOptions.playbackSpeed = playbackSpeed; + continue; + } + + if ((strcasecmp(argv[i], "-pos") == 0) || + (strcasecmp(argv[i], "/pos") == 0) && (i < argc-1)) { + long initTime = atol(argv[++i]); + if (initTime <= 0) + return false; + playerOptions.initTime = initTime; + continue; + } + + if ((strcasecmp(argv[i], "-autoplay") == 0) || + (strcasecmp(argv[i], "/autoplay") == 0) && (i < argc-1)) { + playerOptions.autoPlay = true; + continue; + } + + if ((strcasecmp(argv[i], "-loop") == 0) || + (strcasecmp(argv[i], "/loop") == 0) && (i < argc-1)) { + playerOptions.loopPlayback = true; + continue; + } + + if (i != argc - 1) + return false; + } + + fileName = strDup(argv[argc-1]); + if (fileName[0] == '-') return false; + else return true; +} + +// +// -=- WinMain +// + +int WINAPI WinMain(HINSTANCE inst, HINSTANCE prevInst, char* cmdLine, int cmdShow) { + + // - Process the command-line + + int argc = __argc; + char** argv = __argv; + if ((argc > 1) && (!processParams(argc, argv))) { + MessageBox(0, wrong_cmd_msg, "RfbPlayer", MB_OK | MB_ICONWARNING); + return 0; + } + + if (print_usage) { + programUsage(); + return 0; + } + // Show the user defined pixel formats if required + if (print_upf_list) { + int list_size = 256; + char *upf_list = new char[list_size]; + PixelFormatList userPfList; + userPfList.readUserDefinedPF(HKEY_CURRENT_USER, UPF_REGISTRY_PATH); + strcpy(upf_list, "The list of the user defined pixel formats:\r\n"); + for (int i = userPfList.getDefaultPFCount(); i < userPfList.count(); i++) { + if ((list_size - strlen(upf_list) - 1) < + (strlen(userPfList[i]->format_name) + 2)) { + char *tmpStr = new char[list_size = + list_size + strlen(userPfList[i]->format_name) + 2]; + strcpy(tmpStr, upf_list); + delete [] upf_list; + upf_list = new char[list_size]; + strcpy(upf_list, tmpStr); + delete [] tmpStr; + } + strcat(upf_list, userPfList[i]->format_name); + strcat(upf_list, "\r\n"); + } + InfoDialog upfInfoDialog(upf_list); + upfInfoDialog.showDialog(); + delete [] upf_list; + return 0; + } + + // Create the player + RfbPlayer *player = NULL; + try { + player = new RfbPlayer(fileName, &playerOptions); + } catch (rdr::Exception e) { + MessageBox(NULL, e.str(), "RFB Player", MB_OK | MB_ICONERROR); + delete player; + return 0; + } + + // Run the player + HACCEL hAccel = LoadAccelerators(inst, MAKEINTRESOURCE(IDR_ACCELERATOR)); + MSG msg; + while (GetMessage(&msg, NULL, 0, 0) > 0) { + if(!TranslateAccelerator(player->getMainHandle(), hAccel, &msg)) { + TranslateMessage(&msg); + DispatchMessage(&msg); + } + } + + // Destroy the player + try{ + if (player) delete player; + } catch (rdr::Exception e) { + MessageBox(NULL, e.str(), "RFB Player", MB_OK | MB_ICONERROR); + } + + return 0; +}; diff --git a/win/rfbplayer/rfbplayer.dsp b/win/rfbplayer/rfbplayer.dsp new file mode 100644 index 00000000..90685369 --- /dev/null +++ b/win/rfbplayer/rfbplayer.dsp @@ -0,0 +1,207 @@ +# Microsoft Developer Studio Project File - Name="rfbplayer" - Package Owner=<4>
+# Microsoft Developer Studio Generated Build File, Format Version 6.00
+# ** DO NOT EDIT **
+
+# TARGTYPE "Win32 (x86) Application" 0x0101
+
+CFG=rfbplayer - Win32 Debug
+!MESSAGE This is not a valid makefile. To build this project using NMAKE,
+!MESSAGE use the Export Makefile command and run
+!MESSAGE
+!MESSAGE NMAKE /f "rfbplayer.mak".
+!MESSAGE
+!MESSAGE You can specify a configuration when running NMAKE
+!MESSAGE by defining the macro CFG on the command line. For example:
+!MESSAGE
+!MESSAGE NMAKE /f "rfbplayer.mak" CFG="rfbplayer - Win32 Debug"
+!MESSAGE
+!MESSAGE Possible choices for configuration are:
+!MESSAGE
+!MESSAGE "rfbplayer - Win32 Release" (based on "Win32 (x86) Application")
+!MESSAGE "rfbplayer - Win32 Debug" (based on "Win32 (x86) Application")
+!MESSAGE
+
+# Begin Project
+# PROP AllowPerConfigDependencies 0
+# PROP Scc_ProjName ""
+# PROP Scc_LocalPath ""
+CPP=cl.exe
+MTL=midl.exe
+RSC=rc.exe
+
+!IF "$(CFG)" == "rfbplayer - Win32 Release"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 0
+# PROP BASE Output_Dir "Release"
+# PROP BASE Intermediate_Dir "Release"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 0
+# PROP Output_Dir "..\Release"
+# PROP Intermediate_Dir "..\Release\rfbplayer"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /YX /FD /c
+# ADD CPP /nologo /MT /W3 /GX /O2 /I ".." /I "../../common" /FI"rdr/msvcwarning.h" /D "NDEBUG" /D "_WINDOWS" /D "WIN32" /D "_MBCS" /YX /FD /c
+# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32
+# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32
+# ADD BASE RSC /l 0x419 /d "NDEBUG"
+# ADD RSC /l 0x409 /d "NDEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /machine:I386
+# ADD LINK32 user32.lib gdi32.lib Advapi32.lib comctl32.lib shell32.lib comdlg32.lib version.lib /nologo /subsystem:windows /machine:I386
+# Begin Special Build Tool
+SOURCE="$(InputPath)"
+PreLink_Cmds=cl /c /nologo /Fo..\Release\ /Fd..\Release\rfbplayer /MTd buildTime.cxx
+# End Special Build Tool
+
+!ELSEIF "$(CFG)" == "rfbplayer - Win32 Debug"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 1
+# PROP BASE Output_Dir "Debug"
+# PROP BASE Intermediate_Dir "Debug"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 1
+# PROP Output_Dir "..\Debug"
+# PROP Intermediate_Dir "..\Debug\rfbplayer"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /YX /FD /GZ /c
+# ADD CPP /nologo /MTd /W3 /Gm /GX /Zi /Od /I ".." /I "../../common" /FI"rdr/msvcwarning.h" /D "_DEBUG" /D "_WINDOWS" /D "WIN32" /D "_MBCS" /FR /YX /FD /GZ /c
+# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /win32
+# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32
+# ADD BASE RSC /l 0x419 /d "_DEBUG"
+# ADD RSC /l 0x409 /d "_DEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /debug /machine:I386 /pdbtype:sept
+# ADD LINK32 user32.lib gdi32.lib advapi32.lib ws2_32.lib version.lib comctl32.lib shell32.lib comdlg32.lib /nologo /subsystem:windows /debug /machine:I386 /pdbtype:sept
+# Begin Special Build Tool
+SOURCE="$(InputPath)"
+PreLink_Cmds=cl /c /nologo /Fo..\Debug\ /Fd..\Debug\rfbplayer /MTd buildTime.cxx
+# End Special Build Tool
+
+!ENDIF
+
+# Begin Target
+
+# Name "rfbplayer - Win32 Release"
+# Name "rfbplayer - Win32 Debug"
+# Begin Group "Source Files"
+
+# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat"
+# Begin Source File
+
+SOURCE=.\buildTime.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\FbsInputStream.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\PixelFormatList.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\PlayerOptions.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\PlayerToolBar.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\rfbplayer.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\rfbplayer.rc
+# End Source File
+# Begin Source File
+
+SOURCE=.\RfbProto.cxx
+# End Source File
+# End Group
+# Begin Group "Header Files"
+
+# PROP Default_Filter "h;hpp;hxx;hm;inl"
+# Begin Source File
+
+SOURCE=.\ChoosePixelFormatDialog.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\EditPixelFormatDialog.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\FbsInputStream.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\GotoPosDialog.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\InfoDialog.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\OptionsDialog.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\PixelFormatList.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\PlayerOptions.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\PlayerToolBar.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\rfbplayer.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\RfbProto.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\rfbSessionReader.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\SessionInfoDialog.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\UserPixelFormatsDialog.h
+# End Source File
+# End Group
+# Begin Group "Resource Files"
+
+# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe"
+# Begin Source File
+
+SOURCE=.\rfbplayer.ico
+# End Source File
+# Begin Source File
+
+SOURCE=.\toolbar.bmp
+# End Source File
+# End Group
+# End Target
+# End Project
diff --git a/win/rfbplayer/rfbplayer.h b/win/rfbplayer/rfbplayer.h new file mode 100644 index 00000000..c5c5da8d --- /dev/null +++ b/win/rfbplayer/rfbplayer.h @@ -0,0 +1,195 @@ +/* Copyright (C) 2004 TightVNC Team. All Rights Reserved. + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +// -=- RfbPlayer.h + +#include <windows.h> + +#include <rfb_win32/DIBSectionBuffer.h> + +#include <rfbplayer/resource.h> +#include <rfbplayer/PixelFormatList.h> +#include <rfbplayer/PlayerToolBar.h> +#include <rfbplayer/OptionsDialog.h> +#include <rfbplayer/rfbSessionReader.h> + +using namespace rfb; +using namespace rfb::win32; + +class PlayerToolBar; + +class RfbPlayer : public RfbProto { + public: + RfbPlayer(char *fileName, PlayerOptions *options); + ~RfbPlayer(); + + // -=- Window Message handling + + LRESULT processMainMessage(HWND wnd, UINT msg, WPARAM wParam, LPARAM lParam); + LRESULT processFrameMessage(HWND wnd, UINT msg, WPARAM wParam, LPARAM lParam); + + // -=- Window interface + + HWND getMainHandle() const {return mainHwnd;} + HWND getFrameHandle() const {return frameHwnd;} + void disableTBandMenuItems(); + void enableTBandMenuItems(); + void setFrameSize(int width, int height); + void setVisible(bool visible); + void setTitle(const char *title); + void calculateScrollBars(); + void close(const char* reason=0); + void init(); + + // -=- Coordinate conversions + + inline Point bufferToClient(const Point& p) { + Point pos = p; + if (client_size.width() > buffer->width()) + pos.x += (client_size.width() - buffer->width()) / 2; + else if (client_size.width() < buffer->width()) + pos.x -= scrolloffset.x; + if (client_size.height() > buffer->height()) + pos.y += (client_size.height() - buffer->height()) / 2; + else if (client_size.height() < buffer->height()) + pos.y -= scrolloffset.y; + return pos; + } + inline Rect bufferToClient(const Rect& r) { + return Rect(bufferToClient(r.tl), bufferToClient(r.br)); + } + + inline Point clientToBuffer(const Point& p) { + Point pos = p; + if (client_size.width() > buffer->width()) + pos.x -= (client_size.width() - buffer->width()) / 2; + else if (client_size.width() < buffer->width()) + pos.x += scrolloffset.x; + if (client_size.height() > buffer->height()) + pos.y -= (client_size.height() - buffer->height()) / 2; + else if (client_size.height() < buffer->height()) + pos.y += scrolloffset.y; + return pos; + } + inline Rect clientToBuffer(const Rect& r) { + return Rect(clientToBuffer(r.tl), clientToBuffer(r.br)); + } + + bool setViewportOffset(const Point& tl); + + // -=- RfbProto interface overrides + + virtual void processMsg(); + virtual void serverInit(); + virtual void frameBufferUpdateEnd(); + virtual void setColourMapEntries(int first, int count, U16* rgbs); + virtual void serverCutText(const char* str, int len); + virtual void bell(); + + virtual void beginRect(const Rect& r, unsigned int encoding); + virtual void endRect(const Rect& r, unsigned int encoding); + virtual void fillRect(const Rect& r, Pixel pix); + virtual void imageRect(const Rect& r, void* pixels); + virtual void copyRect(const Rect& r, int srcX, int srcY); + + // -=- Player functions + + // calculateSessionTime() calculates the full session time in sec + long calculateSessionTime(char *fileName); + + // closeSessionFile() closes the session file and blanks the frame buffer + void closeSessionFile(); + + // openSessionFile() opens the new session file and starts play it + void openSessionFile(char *fileName); + + // skipHandshaking() - is implemented to skip the initial handshaking when + // perform backward seeking OR replaying. + void skipHandshaking(); + + void blankBuffer(); + void rewind(); + void setPaused(bool paused); + void stopPlayback(); + long getTimeOffset(); + bool isSeekMode(); + bool isSeeking(); + bool isPaused(); + long getSeekOffset(); + void setPos(long pos); + void setSpeed(double speed); + double getSpeed(); + + protected: + bool seekMode; + bool stopped; + long lastPos; + long sliderStepMs; + char fullSessionTime[20]; + int time_pos_m; + int time_pos_s; + char *fileName; + + // rfbReader is a class which used to reading the rfb data from the file + rfbSessionReader *rfbReader; + + // Returns true if part of the supplied rect is visible, false otherwise + bool invalidateBufferRect(const Rect& crect); + + bool waitWhilePaused(); + + // rewindFlag is a flag wich disable the update of the frame buffer window + // while the rewind is performing. + bool rewindFlag; + + // Local window state + HWND mainHwnd; + HWND frameHwnd; + HMENU hMenu; + Rect window_size; + Rect client_size; + Point scrolloffset; + Point maxscrolloffset; + char *cutText; + win32::DIBSectionBuffer* buffer; + PlayerToolBar tb; + + // The player's parameters + PlayerOptions options; + PixelFormatList supportedPF; + long imageDataStartTime; + long sessionTimeMs; + int currentEncoding; +}; + +// -=- sessionTerminateThread class + +// It is a special thread class, wich is allow the rfbSessionReader class +// terminate itself. + +class sessionTerminateThread : public rfb::Thread { +public: + sessionTerminateThread(RfbPlayer *_player) : player(_player) { + setDeleteAfterRun(); + } + virtual void run() { + player->closeSessionFile(); + } +protected: + RfbPlayer *player; +}; diff --git a/win/rfbplayer/rfbplayer.ico b/win/rfbplayer/rfbplayer.ico Binary files differnew file mode 100644 index 00000000..c9136bfe --- /dev/null +++ b/win/rfbplayer/rfbplayer.ico diff --git a/win/rfbplayer/rfbplayer.rc b/win/rfbplayer/rfbplayer.rc new file mode 100644 index 00000000..169d8502 --- /dev/null +++ b/win/rfbplayer/rfbplayer.rc @@ -0,0 +1,466 @@ +//Microsoft Developer Studio generated resource script. +// +#include "resource.h" + +#define APSTUDIO_READONLY_SYMBOLS +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 2 resource. +// +#include "afxres.h" + +///////////////////////////////////////////////////////////////////////////// +#undef APSTUDIO_READONLY_SYMBOLS + +///////////////////////////////////////////////////////////////////////////// +// English (U.S.) resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) +#ifdef _WIN32 +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US +#pragma code_page(1252) +#endif //_WIN32 + +#ifdef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// TEXTINCLUDE +// + +1 TEXTINCLUDE DISCARDABLE +BEGIN + "resource.h\0" +END + +2 TEXTINCLUDE DISCARDABLE +BEGIN + "#include ""afxres.h""\r\n" + "\0" +END + +3 TEXTINCLUDE DISCARDABLE +BEGIN + "\r\n" + "\0" +END + +#endif // APSTUDIO_INVOKED + +#endif // English (U.S.) resources +///////////////////////////////////////////////////////////////////////////// + + +///////////////////////////////////////////////////////////////////////////// +// English (U.K.) resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENG) +#ifdef _WIN32 +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_UK +#pragma code_page(1252) +#endif //_WIN32 + +///////////////////////////////////////////////////////////////////////////// +// +// Icon +// + +// Icon with lowest ID value placed first to ensure application icon +// remains consistent on all systems. +IDI_ICON ICON DISCARDABLE "rfbplayer.ico" + +#ifndef _MAC +///////////////////////////////////////////////////////////////////////////// +// +// Version +// + +VS_VERSION_INFO VERSIONINFO + FILEVERSION 1,0,0,1 + PRODUCTVERSION 1,0,0,1 + FILEFLAGSMASK 0x3fL +#ifdef _DEBUG + FILEFLAGS 0x1L +#else + FILEFLAGS 0x0L +#endif + FILEOS 0x40004L + FILETYPE 0x1L + FILESUBTYPE 0x0L +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "080904b0" + BEGIN + VALUE "Comments", "\0" + VALUE "CompanyName", "TightVNC Team\0" + VALUE "FileDescription", "RFB Session Player for Win32\0" + VALUE "FileVersion", "1, 0, 0, 1\0" + VALUE "InternalName", "rfbplayer\0" + VALUE "LegalCopyright", "Copyright (C) 2004-2005 TightVNC Team.\0" + VALUE "LegalTrademarks", "\0" + VALUE "OriginalFilename", "rfbplayer.exe\0" + VALUE "PrivateBuild", "\0" + VALUE "ProductName", "Rfb Session Player 1.0\0" + VALUE "ProductVersion", "1, 0, 0, 1\0" + VALUE "SpecialBuild", "\0" + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x809, 1200 + END +END + +#endif // !_MAC + + +///////////////////////////////////////////////////////////////////////////// +// +// Menu +// + +IDR_MENU MENU DISCARDABLE +BEGIN + POPUP "File" + BEGIN + MENUITEM "Open File...\tCtrl+O", ID_OPENFILE + MENUITEM "Close File...\tCtrl+Q", ID_CLOSEFILE + MENUITEM SEPARATOR + MENUITEM "Info...\tCtrl+I", ID_SESSION_INFO + MENUITEM SEPARATOR + MENUITEM "Exit\tAlt+X", ID_EXIT + END + POPUP "Play" + BEGIN + MENUITEM "Play/Pause\tSpace", ID_PLAYPAUSE + MENUITEM "Stop\tC", ID_STOP + MENUITEM "Go To...\tCtrl+G", ID_GOTO + MENUITEM SEPARATOR + MENUITEM "Loop\tCtrl+L", ID_LOOP + MENUITEM SEPARATOR + MENUITEM "Options...\tO", ID_OPTIONS + END + POPUP "Help" + BEGIN + MENUITEM "Home Page", ID_HOMEPAGE + MENUITEM "Command Line Switches", ID_HELP_COMMANDLINESWITCHES + MENUITEM SEPARATOR + MENUITEM "About RfbPlayer...", ID_ABOUT + END +END + + +///////////////////////////////////////////////////////////////////////////// +// +// Accelerator +// + +IDR_ACCELERATOR ACCELERATORS DISCARDABLE +BEGIN + "C", ID_STOP, VIRTKEY, NOINVERT + "C", ID_COPYTOCLIPBOARD, VIRTKEY, CONTROL, NOINVERT + "C", ID_FRAMEEXTRACT, VIRTKEY, ALT, NOINVERT + "G", ID_GOTO, VIRTKEY, CONTROL, NOINVERT + "I", ID_SESSION_INFO, VIRTKEY, CONTROL, NOINVERT + "L", ID_LOOP, VIRTKEY, CONTROL, NOINVERT + "O", ID_OPTIONS, VIRTKEY, NOINVERT + "O", ID_OPENFILE, VIRTKEY, CONTROL, NOINVERT + "P", ID_OPTIONS, VIRTKEY, CONTROL, NOINVERT + "Q", ID_CLOSEFILE, VIRTKEY, CONTROL, NOINVERT + VK_F5, ID_ZOOM_50, VIRTKEY, NOINVERT + VK_F6, ID_ZOOM_100, VIRTKEY, NOINVERT + VK_F7, ID_ZOOM_200, VIRTKEY, NOINVERT + VK_RETURN, ID_RETURN, VIRTKEY, NOINVERT + VK_RETURN, ID_FULLSCREEN, VIRTKEY, ALT, NOINVERT + VK_SPACE, ID_PLAYPAUSE, VIRTKEY, NOINVERT + "X", ID_EXIT, VIRTKEY, ALT, NOINVERT +END + + +///////////////////////////////////////////////////////////////////////////// +// +// Bitmap +// + +IDB_TOOLBAR BITMAP DISCARDABLE "toolbar.bmp" + +///////////////////////////////////////////////////////////////////////////// +// +// Dialog +// + +IDD_GOTO DIALOG DISCARDABLE 0, 0, 153, 54 +STYLE DS_MODALFRAME | DS_CENTER | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "RfbPlayer : Go to position" +FONT 8, "MS Sans Serif" +BEGIN + EDITTEXT IDC_GOTO_EDIT,40,9,106,14,ES_AUTOHSCROLL + DEFPUSHBUTTON "OK",IDOK,40,33,50,14 + PUSHBUTTON "Cancel",IDCANCEL,95,33,51,14 + LTEXT "Pos (ms):",IDC_STATIC,7,9,33,15,SS_CENTERIMAGE +END + +IDD_PIXELFORMAT DIALOG DISCARDABLE 0, 0, 144, 78 +STYLE DS_MODALFRAME | DS_CENTER | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "RfbPlayer : Pixel Format" +FONT 8, "MS Sans Serif" +BEGIN + COMBOBOX IDC_PIXELFORMAT,7,20,130,98,CBS_DROPDOWNLIST | + WS_VSCROLL | WS_TABSTOP + DEFPUSHBUTTON "OK",IDOK,20,57,50,14 + PUSHBUTTON "Cancel",IDCANCEL,75,57,50,14 + LTEXT "Choose the pixel format:",IDC_STATIC,7,7,130,13 + CONTROL "Big endian flag",IDC_BIG_ENDIAN,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,7,40,63,12 +END + +IDD_OPTIONS DIALOG DISCARDABLE 0, 0, 187, 180 +STYLE DS_MODALFRAME | DS_CENTER | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "Options" +FONT 8, "MS Sans Serif" +BEGIN + COMBOBOX IDC_PIXELFORMAT,15,30,157,75,CBS_DROPDOWNLIST | + WS_VSCROLL | WS_TABSTOP + CONTROL "Big endian flag",IDC_BIG_ENDIAN,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,15,52,60,10 + CONTROL "Ask the pixel format before playing",IDC_ASK_PF,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,15,67,157,10 + CONTROL "Accept the bells",IDC_ACCEPT_BELL,"Button", + BS_AUTOCHECKBOX | BS_TOP | WS_TABSTOP,15,105,157,15 + CONTROL "Accept the cut text",IDC_ACCEPT_CUT_TEXT,"Button", + BS_AUTOCHECKBOX | BS_TOP | WS_TABSTOP,15,120,157,15 + CONTROL "Start play the session when it is opened",IDC_AUTOPLAY, + "Button",BS_AUTOCHECKBOX | BS_TOP | WS_TABSTOP,15,135, + 157,9 + DEFPUSHBUTTON "OK",IDOK,20,161,50,13 + PUSHBUTTON "Cancel",IDCANCEL,75,161,50,13 + PUSHBUTTON "Default",IDC_DEFAULT,130,161,50,13 + PUSHBUTTON "Edit User PF",IDC_EDIT_UPF,110,52,62,14 + GROUPBOX "Pixel format",IDC_STATIC,7,6,173,79 + LTEXT "Forces the pixel format for the rfb session:", + IDC_STATIC,15,17,157,13 + GROUPBOX "Other",IDC_STATIC,7,90,173,65 +END + +IDD_ABOUT DIALOG DISCARDABLE 0, 0, 251, 95 +STYLE DS_MODALFRAME | DS_CENTER | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "About Rfb Session Player for Windows" +FONT 8, "MS Sans Serif" +BEGIN + DEFPUSHBUTTON "OK",IDOK,195,70,47,15 + ICON IDI_ICON,IDC_STATIC,7,10,20,20 + LTEXT ">appname<",IDC_DESCRIPTION,40,10,125,15 + LTEXT ">version<",IDC_VERSION,165,10,77,15 + LTEXT ">buildtime<",IDC_BUILDTIME,40,25,202,15 + LTEXT ">copyright<",IDC_COPYRIGHT,40,40,202,15 + LTEXT "See http://www.tightvnc.com for more information on TightVNC.", + IDC_STATIC,40,55,202,15 +END + +IDD_USERPF_LIST DIALOG DISCARDABLE 0, 0, 207, 162 +STYLE DS_MODALFRAME | DS_CENTER | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "Add / Remove the user pixel formats" +FONT 8, "MS Sans Serif" +BEGIN + LISTBOX IDC_PF_LIST,7,7,136,148,LBS_NOINTEGRALHEIGHT | + WS_VSCROLL | WS_TABSTOP + PUSHBUTTON "Add",IDC_ADD_BUTTON,150,7,50,14 + PUSHBUTTON "Remove",IDC_REMOVE_BUTTON,150,26,50,14 + PUSHBUTTON "Edit",IDC_EDIT_BUTTON,150,45,50,14 + PUSHBUTTON "Close",IDOK,150,64,50,14 +END + +IDD_UPF_EDIT DIALOG DISCARDABLE 0, 0, 204, 126 +STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "Edit the user pixel format" +FONT 8, "MS Sans Serif" +BEGIN + EDITTEXT IDC_NAME_EDIT,68,7,129,13,ES_AUTOHSCROLL + COMBOBOX IDC_BPP_COMBO,68,23,39,45,CBS_DROPDOWNLIST | WS_VSCROLL | + WS_TABSTOP + EDITTEXT IDC_DEPTH_EDIT,157,22,40,13,ES_AUTOHSCROLL | ES_NUMBER + COMBOBOX IDC_BIGENDIAN_COMBO,68,38,39,45,CBS_DROPDOWNLIST | + WS_VSCROLL | WS_TABSTOP + EDITTEXT IDC_REDMAX_EDIT,68,54,39,13,ES_AUTOHSCROLL | ES_NUMBER + EDITTEXT IDC_GREENMAX_EDIT,68,70,39,13,ES_AUTOHSCROLL | ES_NUMBER + EDITTEXT IDC_BLUEMAX_EDIT,68,86,39,13,ES_AUTOHSCROLL | ES_NUMBER + EDITTEXT IDC_REDSHIFT_EDIT,157,54,39,13,ES_AUTOHSCROLL | + ES_NUMBER + EDITTEXT IDC_GREENSHIFT_EDIT,157,70,40,13,ES_AUTOHSCROLL | + ES_NUMBER + EDITTEXT IDC_BLUESHIFT_EDIT,157,86,40,13,ES_AUTOHSCROLL | + ES_NUMBER + PUSHBUTTON "OK",IDOK,93,105,50,14 + PUSHBUTTON "Cancel",IDCANCEL,147,105,50,14 + LTEXT "Pixel format name:",IDC_STATIC,7,7,57,13,SS_CENTERIMAGE + LTEXT "Bit per pixel:",IDC_STATIC,7,23,38,12,SS_CENTERIMAGE + LTEXT "Big endian flag :",IDC_STATIC,7,38,53,13,SS_CENTERIMAGE + LTEXT "Red max :",IDC_STATIC,7,54,33,13,SS_CENTERIMAGE + LTEXT "Green max :",IDC_STATIC,7,70,38,13,SS_CENTERIMAGE + LTEXT "Blue max :",IDC_STATIC,7,86,38,13,SS_CENTERIMAGE + LTEXT "Depth:",IDC_STATIC,112,23,21,12,SS_CENTERIMAGE + LTEXT "Red shifts :",IDC_STATIC,112,54,36,13,SS_CENTERIMAGE + LTEXT "Green shifts :",IDC_STATIC,112,70,43,13,SS_CENTERIMAGE + LTEXT "Blue shifts :",IDC_STATIC,112,86,43,13,SS_CENTERIMAGE +END + +IDD_INFO DIALOG DISCARDABLE 0, 0, 295, 207 +STYLE DS_MODALFRAME | DS_CENTER | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "Information" +FONT 8, "MS Sans Serif" +BEGIN + DEFPUSHBUTTON "&OK",IDOK,122,186,50,14 + EDITTEXT IDC_INFO_EDIT,7,7,281,172,ES_MULTILINE | ES_AUTOVSCROLL | + ES_AUTOHSCROLL | ES_READONLY | ES_WANTRETURN | + WS_VSCROLL | WS_HSCROLL +END + +IDD_SESSION_INFO DIALOG DISCARDABLE 0, 0, 239, 106 +STYLE DS_MODALFRAME | DS_CENTER | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "RFB Session Info" +FONT 8, "MS Sans Serif" +BEGIN + DEFPUSHBUTTON "OK",IDOK,182,85,50,14 + LTEXT "Desktop Name:",IDC_STATIC,7,10,73,15 + LTEXT "Desktop Size:",IDC_STATIC,7,25,73,15 + LTEXT "Pixel Format:",IDC_STATIC,7,40,73,15 + LTEXT "Current Encoding:",IDC_STATIC,7,55,73,15 + LTEXT "RFB Protocol Version:",IDC_STATIC,7,70,73,15 + LTEXT "",IDC_DESKTOP_NAME,80,10,152,15 + LTEXT "",IDC_DESKTOP_SIZE,80,25,152,15 + LTEXT "",IDC_CURRENT_ENCODING,80,55,152,15 + LTEXT "",IDC_VERSION,80,70,152,15 + LTEXT "",IDC_PIXEL_FORMAT,80,40,152,15 +END + + +///////////////////////////////////////////////////////////////////////////// +// +// DESIGNINFO +// + +#ifdef APSTUDIO_INVOKED +GUIDELINES DESIGNINFO DISCARDABLE +BEGIN + IDD_GOTO, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 146 + VERTGUIDE, 40 + VERTGUIDE, 90 + VERTGUIDE, 95 + TOPMARGIN, 9 + BOTTOMMARGIN, 47 + HORZGUIDE, 9 + HORZGUIDE, 24 + HORZGUIDE, 35 + END + + IDD_PIXELFORMAT, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 137 + VERTGUIDE, 20 + VERTGUIDE, 70 + VERTGUIDE, 75 + VERTGUIDE, 125 + TOPMARGIN, 7 + BOTTOMMARGIN, 71 + HORZGUIDE, 7 + HORZGUIDE, 20 + HORZGUIDE, 35 + HORZGUIDE, 40 + HORZGUIDE, 49 + HORZGUIDE, 57 + END + + IDD_OPTIONS, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 180 + VERTGUIDE, 15 + VERTGUIDE, 20 + VERTGUIDE, 70 + VERTGUIDE, 75 + VERTGUIDE, 125 + VERTGUIDE, 130 + VERTGUIDE, 172 + TOPMARGIN, 6 + BOTTOMMARGIN, 174 + HORZGUIDE, 17 + HORZGUIDE, 30 + HORZGUIDE, 42 + HORZGUIDE, 52 + HORZGUIDE, 67 + HORZGUIDE, 85 + HORZGUIDE, 90 + HORZGUIDE, 105 + HORZGUIDE, 120 + HORZGUIDE, 135 + HORZGUIDE, 144 + HORZGUIDE, 150 + HORZGUIDE, 155 + HORZGUIDE, 161 + END + + IDD_USERPF_LIST, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 200 + TOPMARGIN, 7 + BOTTOMMARGIN, 155 + HORZGUIDE, 21 + HORZGUIDE, 26 + HORZGUIDE, 40 + HORZGUIDE, 45 + HORZGUIDE, 59 + HORZGUIDE, 64 + END + + IDD_UPF_EDIT, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 197 + TOPMARGIN, 7 + BOTTOMMARGIN, 119 + END + + IDD_INFO, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 288 + TOPMARGIN, 7 + BOTTOMMARGIN, 200 + END + + IDD_SESSION_INFO, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 232 + TOPMARGIN, 7 + BOTTOMMARGIN, 99 + HORZGUIDE, 10 + HORZGUIDE, 25 + HORZGUIDE, 40 + HORZGUIDE, 55 + HORZGUIDE, 70 + HORZGUIDE, 85 + END +END +#endif // APSTUDIO_INVOKED + +#endif // English (U.K.) resources +///////////////////////////////////////////////////////////////////////////// + + + +#ifndef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 3 resource. +// + + +///////////////////////////////////////////////////////////////////////////// +#endif // not APSTUDIO_INVOKED + diff --git a/win/rfbplayer/toolbar.bmp b/win/rfbplayer/toolbar.bmp Binary files differnew file mode 100644 index 00000000..9347a738 --- /dev/null +++ b/win/rfbplayer/toolbar.bmp diff --git a/win/vnc.dsw b/win/vnc.dsw new file mode 100644 index 00000000..686cc692 --- /dev/null +++ b/win/vnc.dsw @@ -0,0 +1,248 @@ +Microsoft Developer Studio Workspace File, Format Version 6.00
+# WARNING: DO NOT EDIT OR DELETE THIS WORKSPACE FILE!
+
+###############################################################################
+
+Project: "Xregion"=..\common\Xregion\Xregion.dsp - Package Owner=<4>
+
+Package=<5>
+{{{
+}}}
+
+Package=<4>
+{{{
+}}}
+
+###############################################################################
+
+Project: "logmessages"=.\logmessages\logmessages.dsp - Package Owner=<4>
+
+Package=<5>
+{{{
+}}}
+
+Package=<4>
+{{{
+}}}
+
+###############################################################################
+
+Project: "network"=..\common\network\network.dsp - Package Owner=<4>
+
+Package=<5>
+{{{
+}}}
+
+Package=<4>
+{{{
+}}}
+
+###############################################################################
+
+Project: "rdr"=..\common\rdr\rdr.dsp - Package Owner=<4>
+
+Package=<5>
+{{{
+}}}
+
+Package=<4>
+{{{
+}}}
+
+###############################################################################
+
+Project: "rfb"=..\common\rfb\rfb.dsp - Package Owner=<4>
+
+Package=<5>
+{{{
+}}}
+
+Package=<4>
+{{{
+ Begin Project Dependency
+ Project_Dep_Name rdr
+ End Project Dependency
+ Begin Project Dependency
+ Project_Dep_Name Xregion
+ End Project Dependency
+}}}
+
+###############################################################################
+
+Project: "rfb_win32"=.\rfb_win32\rfb_win32.dsp - Package Owner=<4>
+
+Package=<5>
+{{{
+}}}
+
+Package=<4>
+{{{
+ Begin Project Dependency
+ Project_Dep_Name rfb
+ End Project Dependency
+}}}
+
+###############################################################################
+
+Project: "rfbplayer"=.\rfbplayer\rfbplayer.dsp - Package Owner=<4>
+
+Package=<5>
+{{{
+}}}
+
+Package=<4>
+{{{
+ Begin Project Dependency
+ Project_Dep_Name rdr
+ End Project Dependency
+ Begin Project Dependency
+ Project_Dep_Name rfb
+ End Project Dependency
+ Begin Project Dependency
+ Project_Dep_Name rfb_win32
+ End Project Dependency
+ Begin Project Dependency
+ Project_Dep_Name Xregion
+ End Project Dependency
+ Begin Project Dependency
+ Project_Dep_Name zlib
+ End Project Dependency
+ Begin Project Dependency
+ Project_Dep_Name jpeg
+ End Project Dependency
+}}}
+
+###############################################################################
+
+Project: "vncconfig"=.\vncconfig\vncconfig.dsp - Package Owner=<4>
+
+Package=<5>
+{{{
+}}}
+
+Package=<4>
+{{{
+ Begin Project Dependency
+ Project_Dep_Name rfb
+ End Project Dependency
+ Begin Project Dependency
+ Project_Dep_Name rfb_win32
+ End Project Dependency
+ Begin Project Dependency
+ Project_Dep_Name network
+ End Project Dependency
+ Begin Project Dependency
+ Project_Dep_Name Xregion
+ End Project Dependency
+}}}
+
+###############################################################################
+
+Project: "vncviewer"=.\vncviewer\vncviewer.dsp - Package Owner=<4>
+
+Package=<5>
+{{{
+}}}
+
+Package=<4>
+{{{
+ Begin Project Dependency
+ Project_Dep_Name rfb
+ End Project Dependency
+ Begin Project Dependency
+ Project_Dep_Name zlib
+ End Project Dependency
+ Begin Project Dependency
+ Project_Dep_Name jpeg
+ End Project Dependency
+ Begin Project Dependency
+ Project_Dep_Name rfb_win32
+ End Project Dependency
+ Begin Project Dependency
+ Project_Dep_Name network
+ End Project Dependency
+ Begin Project Dependency
+ Project_Dep_Name rdr
+ End Project Dependency
+ Begin Project Dependency
+ Project_Dep_Name Xregion
+ End Project Dependency
+}}}
+
+###############################################################################
+
+Project: "winvnc"=.\winvnc\winvnc.dsp - Package Owner=<4>
+
+Package=<5>
+{{{
+}}}
+
+Package=<4>
+{{{
+ Begin Project Dependency
+ Project_Dep_Name network
+ End Project Dependency
+ Begin Project Dependency
+ Project_Dep_Name rfb_win32
+ End Project Dependency
+ Begin Project Dependency
+ Project_Dep_Name zlib
+ End Project Dependency
+ Begin Project Dependency
+ Project_Dep_Name jpeg
+ End Project Dependency
+ Begin Project Dependency
+ Project_Dep_Name Xregion
+ End Project Dependency
+}}}
+
+###############################################################################
+
+Project: "wm_hooks"=.\wm_hooks\wm_hooks.dsp - Package Owner=<4>
+
+Package=<5>
+{{{
+}}}
+
+Package=<4>
+{{{
+}}}
+
+###############################################################################
+
+Project: "zlib"=..\common\zlib\zlib.dsp - Package Owner=<4>
+
+Package=<5>
+{{{
+}}}
+
+Package=<4>
+{{{
+}}}
+
+###############################################################################
+
+Project: "jpeg"=..\common\jpeg\jpeg.dsp - Package Owner=<4>
+
+Package=<5>
+{{{
+}}}
+
+Package=<4>
+{{{
+}}}
+
+###############################################################################
+
+Global:
+
+Package=<5>
+{{{
+}}}
+
+Package=<3>
+{{{
+}}}
+
+###############################################################################
+
diff --git a/win/vncconfig/Authentication.h b/win/vncconfig/Authentication.h new file mode 100644 index 00000000..f4b38f8c --- /dev/null +++ b/win/vncconfig/Authentication.h @@ -0,0 +1,142 @@ +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ +#ifndef WINVNCCONF_AUTHENTICATION +#define WINVNCCONF_AUTHENTICATION + +#include <vncconfig/PasswordDialog.h> +#include <rfb_win32/Registry.h> +#include <rfb_win32/Dialog.h> +#include <rfb_win32/OSVersion.h> +#include <rfb_win32/MsgBox.h> +#include <rfb/ServerCore.h> +#include <rfb/secTypes.h> +#include <rfb/Password.h> + +static rfb::BoolParameter queryOnlyIfLoggedOn("QueryOnlyIfLoggedOn", + "Only prompt for a local user to accept incoming connections if there is a user logged on", false); + +namespace rfb { + + namespace win32 { + + class AuthenticationPage : public PropSheetPage { + public: + AuthenticationPage(const RegKey& rk) + : PropSheetPage(GetModuleHandle(0), MAKEINTRESOURCE(IDD_AUTHENTICATION)), regKey(rk) {} + void initDialog() { + CharArray sec_types_str(SSecurityFactoryStandard::sec_types.getData()); + std::list<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; + } + + HWND security = GetDlgItem(handle, IDC_ENCRYPTION); + SendMessage(security, CB_ADDSTRING, 0, (LPARAM)_T("Always Off")); + SendMessage(security, CB_SETCURSEL, 0, 0); + enableItem(IDC_AUTH_NT, false); enableItem(IDC_AUTH_NT_CONF, false); + enableItem(IDC_ENCRYPTION, false); enableItem(IDC_AUTH_RA2_CONF, false); + + setItemChecked(IDC_AUTH_NONE, useNone); + setItemChecked(IDC_AUTH_VNC, useVNC); + setItemChecked(IDC_QUERY_CONNECT, rfb::Server::queryConnect); + setItemChecked(IDC_QUERY_LOGGED_ON, queryOnlyIfLoggedOn); + onCommand(IDC_AUTH_NONE, 0); + } + bool onCommand(int id, int cmd) { + switch (id) { + case IDC_AUTH_VNC_PASSWD: + { + PasswordDialog passwdDlg(regKey, registryInsecure); + passwdDlg.showDialog(handle); + } + return true; + case IDC_AUTH_NONE: + case IDC_AUTH_VNC: + enableItem(IDC_AUTH_VNC_PASSWD, isItemChecked(IDC_AUTH_VNC)); + case IDC_QUERY_CONNECT: + case IDC_QUERY_LOGGED_ON: + setChanged((useNone != isItemChecked(IDC_AUTH_NONE)) || + (useVNC != isItemChecked(IDC_AUTH_VNC)) || + (rfb::Server::queryConnect != isItemChecked(IDC_QUERY_CONNECT)) || + (queryOnlyIfLoggedOn != isItemChecked(IDC_QUERY_LOGGED_ON))); + enableItem(IDC_QUERY_LOGGED_ON, enableQueryOnlyIfLoggedOn()); + return false; + }; + return false; + } + bool onOk() { + bool useVncChanged = useVNC != isItemChecked(IDC_AUTH_VNC); + useVNC = isItemChecked(IDC_AUTH_VNC); + useNone = isItemChecked(IDC_AUTH_NONE); + if (useVNC) { + verifyVncPassword(regKey); + regKey.setString(_T("SecurityTypes"), _T("VncAuth")); + } else { + if (haveVncPassword() && useVncChanged && + MsgBox(0, _T("The VNC authentication method is disabled, but a password is still stored for it.\n") + _T("Do you want to remove the VNC authentication password from the registry?"), + MB_ICONWARNING | MB_YESNO) == IDYES) { + regKey.setBinary(_T("Password"), 0, 0); + } + regKey.setString(_T("SecurityTypes"), _T("None")); + } + regKey.setString(_T("ReverseSecurityTypes"), _T("None")); + regKey.setBool(_T("QueryConnect"), isItemChecked(IDC_QUERY_CONNECT)); + regKey.setBool(_T("QueryOnlyIfLoggedOn"), isItemChecked(IDC_QUERY_LOGGED_ON)); + return true; + } + void setWarnPasswdInsecure(bool warn) { + registryInsecure = warn; + } + bool enableQueryOnlyIfLoggedOn() { + return isItemChecked(IDC_QUERY_CONNECT) && osVersion.isPlatformNT && (osVersion.dwMajorVersion >= 5); + } + + + static bool haveVncPassword() { + PlainPasswd password(SSecurityFactoryStandard::vncAuthPasswd.getVncAuthPasswd()); + return password.buf && strlen(password.buf) != 0; + } + + static void verifyVncPassword(const RegKey& regKey) { + if (!haveVncPassword()) { + MsgBox(0, _T("The VNC authentication method is enabled, but no password is specified.\n") + _T("The password dialog will now be shown."), MB_ICONINFORMATION | MB_OK); + PasswordDialog passwd(regKey, registryInsecure); + passwd.showDialog(); + } + } + + protected: + RegKey regKey; + static bool registryInsecure; + bool useNone; + bool useVNC; + }; + + }; + + bool AuthenticationPage::registryInsecure = false; + +}; + +#endif diff --git a/win/vncconfig/Connections.h b/win/vncconfig/Connections.h new file mode 100644 index 00000000..7512cc65 --- /dev/null +++ b/win/vncconfig/Connections.h @@ -0,0 +1,298 @@ +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ +#ifndef WINVNCCONF_CONNECTIONS +#define WINVNCCONF_CONNECTIONS + +#include <vector> + +#include <rfb_win32/Registry.h> +#include <rfb_win32/Dialog.h> +#include <rfb_win32/ModuleFileName.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) { + pattern.replaceBuf(tstrDup(pat)); + return Dialog::showDialog(MAKEINTRESOURCE(IDD_CONN_HOST)); + } + void initDialog() { + if (_tcslen(pattern.buf) == 0) + pattern.replaceBuf(tstrDup("+")); + + if (pattern.buf[0] == _T('+')) + setItemChecked(IDC_ALLOW, true); + else if (pattern.buf[0] == _T('?')) + setItemChecked(IDC_QUERY, true); + else + setItemChecked(IDC_DENY, true); + + setItemString(IDC_HOST_PATTERN, &pattern.buf[1]); + pattern.replaceBuf(0); + } + bool onOk() { + TCharArray host = getItemString(IDC_HOST_PATTERN); + TCharArray newPat(_tcslen(host.buf)+2); + if (isItemChecked(IDC_ALLOW)) + newPat.buf[0] = _T('+'); + else if (isItemChecked(IDC_QUERY)) + newPat.buf[0] = _T('?'); + else + newPat.buf[0] = _T('-'); + newPat.buf[1] = 0; + _tcscat(newPat.buf, host.buf); + + network::TcpFilter::Pattern pat(network::TcpFilter::parsePattern(CStr(newPat.buf))); + pattern.replaceBuf(TCharArray(network::TcpFilter::patternToStr(pat)).takeBuf()); + return true; + } + const TCHAR* getPattern() {return pattern.buf;} + protected: + TCharArray pattern; + }; + + class ConnectionsPage : public PropSheetPage { + public: + ConnectionsPage(const RegKey& rk) + : PropSheetPage(GetModuleHandle(0), MAKEINTRESOURCE(IDD_CONNECTIONS)), regKey(rk) {} + void initDialog() { + vlog.debug("set IDC_PORT %d", (int)port_number); + setItemInt(IDC_PORT, port_number ? port_number : 5900); + setItemChecked(IDC_RFB_ENABLE, port_number != 0); + setItemInt(IDC_IDLE_TIMEOUT, rfb::Server::idleTimeout); + vlog.debug("set IDC_HTTP_PORT %d", (int)http_port); + setItemInt(IDC_HTTP_PORT, http_port ? http_port : 5800); + setItemChecked(IDC_HTTP_ENABLE, http_port != 0); + enableItem(IDC_HTTP_PORT, http_port != 0); + setItemChecked(IDC_LOCALHOST, localHost); + + HWND listBox = GetDlgItem(handle, IDC_HOSTS); + while (SendMessage(listBox, LB_GETCOUNT, 0, 0)) + SendMessage(listBox, LB_DELETESTRING, 0, 0); + + CharArray tmp; + tmp.buf = hosts.getData(); + while (tmp.buf) { + CharArray first; + strSplit(tmp.buf, ',', &first.buf, &tmp.buf); + if (strlen(first.buf)) + SendMessage(listBox, LB_ADDSTRING, 0, (LPARAM)(const TCHAR*)TStr(first.buf)); + } + + onCommand(IDC_RFB_ENABLE, EN_CHANGE); + } + bool onCommand(int id, int cmd) { + switch (id) { + case IDC_HOSTS: + { + DWORD selected = SendMessage(GetDlgItem(handle, IDC_HOSTS), LB_GETCURSEL, 0, 0); + int count = SendMessage(GetDlgItem(handle, IDC_HOSTS), LB_GETCOUNT, 0, 0); + bool enable = selected != LB_ERR; + enableItem(IDC_HOST_REMOVE, enable); + enableItem(IDC_HOST_UP, enable && (selected > 0)); + enableItem(IDC_HOST_DOWN, enable && (selected < count-1)); + enableItem(IDC_HOST_EDIT, enable); + setChanged(isChanged()); + } + return true; + + case IDC_PORT: + if (cmd == EN_CHANGE) { + try { + setItemInt(IDC_HTTP_PORT, rfbPortToHTTP(getItemInt(IDC_PORT))); + } catch (...) { + } + } + case IDC_HTTP_PORT: + case IDC_IDLE_TIMEOUT: + if (cmd == EN_CHANGE) + setChanged(isChanged()); + return false; + + case IDC_HTTP_ENABLE: + case IDC_RFB_ENABLE: + case IDC_LOCALHOST: + { + // HTTP port + enableItem(IDC_HTTP_PORT, isItemChecked(IDC_HTTP_ENABLE) && isItemChecked(IDC_RFB_ENABLE)); + enableItem(IDC_HTTP_ENABLE, isItemChecked(IDC_RFB_ENABLE)); + + // RFB port + enableItem(IDC_PORT, isItemChecked(IDC_RFB_ENABLE)); + + // Hosts + enableItem(IDC_LOCALHOST, isItemChecked(IDC_RFB_ENABLE)); + + bool enableHosts = !isItemChecked(IDC_LOCALHOST) && isItemChecked(IDC_RFB_ENABLE); + enableItem(IDC_HOSTS, enableHosts); + enableItem(IDC_HOST_ADD, enableHosts); + if (!enableHosts) { + enableItem(IDC_HOST_REMOVE, enableHosts); + enableItem(IDC_HOST_UP, enableHosts); + enableItem(IDC_HOST_DOWN, enableHosts); + enableItem(IDC_HOST_EDIT, enableHosts); + } else { + onCommand(IDC_HOSTS, EN_CHANGE); + } + setChanged(isChanged()); + return false; + } + + case IDC_HOST_ADD: + if (hostDialog.showDialog(_T(""))) + { + const TCHAR* pattern = hostDialog.getPattern(); + if (pattern) + SendMessage(GetDlgItem(handle, IDC_HOSTS), LB_ADDSTRING, 0, (LPARAM)pattern); + } + return true; + + case IDC_HOST_EDIT: + { + HWND listBox = GetDlgItem(handle, IDC_HOSTS); + int item = SendMessage(listBox, LB_GETCURSEL, 0, 0); + TCharArray pattern(SendMessage(listBox, LB_GETTEXTLEN, item, 0)+1); + SendMessage(listBox, LB_GETTEXT, item, (LPARAM)pattern.buf); + + if (hostDialog.showDialog(pattern.buf)) { + const TCHAR* newPat = hostDialog.getPattern(); + if (newPat) { + item = SendMessage(listBox, LB_FINDSTRINGEXACT, item, (LPARAM)pattern.buf); + if (item != LB_ERR) { + SendMessage(listBox, LB_DELETESTRING, item, 0); + SendMessage(listBox, LB_INSERTSTRING, item, (LPARAM)newPat); + SendMessage(listBox, LB_SETCURSEL, item, 0); + onCommand(IDC_HOSTS, EN_CHANGE); + } + } + } + } + return true; + + case IDC_HOST_UP: + { + HWND listBox = GetDlgItem(handle, IDC_HOSTS); + int item = SendMessage(listBox, LB_GETCURSEL, 0, 0); + TCharArray pattern(SendMessage(listBox, LB_GETTEXTLEN, item, 0)+1); + SendMessage(listBox, LB_GETTEXT, item, (LPARAM)pattern.buf); + SendMessage(listBox, LB_DELETESTRING, item, 0); + SendMessage(listBox, LB_INSERTSTRING, item-1, (LPARAM)pattern.buf); + SendMessage(listBox, LB_SETCURSEL, item-1, 0); + onCommand(IDC_HOSTS, EN_CHANGE); + } + return true; + + case IDC_HOST_DOWN: + { + HWND listBox = GetDlgItem(handle, IDC_HOSTS); + int item = SendMessage(listBox, LB_GETCURSEL, 0, 0); + TCharArray pattern(SendMessage(listBox, LB_GETTEXTLEN, item, 0)+1); + SendMessage(listBox, LB_GETTEXT, item, (LPARAM)pattern.buf); + SendMessage(listBox, LB_DELETESTRING, item, 0); + SendMessage(listBox, LB_INSERTSTRING, item+1, (LPARAM)pattern.buf); + SendMessage(listBox, LB_SETCURSEL, item+1, 0); + onCommand(IDC_HOSTS, EN_CHANGE); + } + return true; + + case IDC_HOST_REMOVE: + { + HWND listBox = GetDlgItem(handle, IDC_HOSTS); + int item = SendMessage(listBox, LB_GETCURSEL, 0, 0); + SendMessage(listBox, LB_DELETESTRING, item, 0); + onCommand(IDC_HOSTS, EN_CHANGE); + } + + } + return false; + } + bool onOk() { + regKey.setInt(_T("PortNumber"), isItemChecked(IDC_RFB_ENABLE) ? getItemInt(IDC_PORT) : 0); + regKey.setInt(_T("IdleTimeout"), getItemInt(IDC_IDLE_TIMEOUT)); + regKey.setInt(_T("HTTPPortNumber"), isItemChecked(IDC_HTTP_ENABLE) && isItemChecked(IDC_RFB_ENABLE) + ? getItemInt(IDC_HTTP_PORT) : 0); + regKey.setInt(_T("LocalHost"), isItemChecked(IDC_LOCALHOST)); + regKey.setString(_T("Hosts"), TCharArray(getHosts()).buf); + return true; + } + bool isChanged() { + try { + CharArray new_hosts = getHosts(); + CharArray old_hosts = hosts.getData(); + return (strcmp(new_hosts.buf, old_hosts.buf) != 0) || + (localHost != isItemChecked(IDC_LOCALHOST)) || + (port_number != getItemInt(IDC_PORT)) || + (http_port != getItemInt(IDC_HTTP_PORT)) || + ((http_port!=0) != (isItemChecked(IDC_HTTP_ENABLE)!=0)) || + (rfb::Server::idleTimeout != getItemInt(IDC_IDLE_TIMEOUT)); + } catch (rdr::Exception) { + return false; + } + } + char* getHosts() { + int bufLen = 1, i; + HWND listBox = GetDlgItem(handle, IDC_HOSTS); + for (i=0; i<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); + } + int rfbPortToHTTP(int rfbPort) { + int offset = -100; + if (http_port) + offset = http_port - port_number; + int httpPort = rfbPort + offset; + if (httpPort <= 0) + httpPort = rfbPort; + return httpPort; + } + + protected: + RegKey regKey; + ConnHostDialog hostDialog; + }; + + }; + +}; + +#endif diff --git a/win/vncconfig/Desktop.h b/win/vncconfig/Desktop.h new file mode 100644 index 00000000..164269a2 --- /dev/null +++ b/win/vncconfig/Desktop.h @@ -0,0 +1,94 @@ +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ +#ifndef WINVNCCONF_DESKTOP +#define WINVNCCONF_DESKTOP + +#include <rfb_win32/Registry.h> +#include <rfb_win32/Dialog.h> +#include <rfb_win32/SDisplay.h> +#include <rfb_win32/DynamicFn.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 diff --git a/win/vncconfig/Hooking.h b/win/vncconfig/Hooking.h new file mode 100644 index 00000000..9be82f3a --- /dev/null +++ b/win/vncconfig/Hooking.h @@ -0,0 +1,88 @@ +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ +#ifndef WINVNCCONF_HOOKING +#define WINVNCCONF_HOOKING + +#include <rfb_win32/Registry.h> +#include <rfb_win32/Dialog.h> +#include <rfb_win32/SDisplay.h> +#include <rfb_win32/WMPoller.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_USEPOLLING, rfb::win32::SDisplay::updateMethod == 0); + setItemChecked(IDC_USEHOOKS, (rfb::win32::SDisplay::updateMethod == 1) && + rfb::win32::SDisplay::areHooksAvailable()); + enableItem(IDC_USEHOOKS, rfb::win32::SDisplay::areHooksAvailable()); + setItemChecked(IDC_USEDRIVER, (rfb::win32::SDisplay::updateMethod == 2) && + rfb::win32::SDisplay::isDriverAvailable()); + enableItem(IDC_USEDRIVER, rfb::win32::SDisplay::isDriverAvailable()); + setItemChecked(IDC_POLLCONSOLES, rfb::win32::WMPoller::poll_console_windows); + setItemChecked(IDC_CAPTUREBLT, osVersion.isPlatformNT && + rfb::win32::DeviceFrameBuffer::useCaptureBlt); + enableItem(IDC_CAPTUREBLT, osVersion.isPlatformNT); + onCommand(IDC_USEHOOKS, 0); + } + bool onCommand(int id, int cmd) { + switch (id) { + case IDC_USEPOLLING: + case IDC_USEHOOKS: + case IDC_USEDRIVER: + case IDC_POLLCONSOLES: + case IDC_CAPTUREBLT: + setChanged(((rfb::win32::SDisplay::updateMethod == 0) != isItemChecked(IDC_USEPOLLING)) || + ((rfb::win32::SDisplay::updateMethod == 1) != isItemChecked(IDC_USEHOOKS)) || + ((rfb::win32::SDisplay::updateMethod == 2) != isItemChecked(IDC_USEDRIVER)) || + (rfb::win32::WMPoller::poll_console_windows != isItemChecked(IDC_POLLCONSOLES)) || + (rfb::win32::DeviceFrameBuffer::useCaptureBlt != isItemChecked(IDC_CAPTUREBLT))); + enableItem(IDC_POLLCONSOLES, isItemChecked(IDC_USEHOOKS)); + break; + } + return false; + } + bool onOk() { + if (isItemChecked(IDC_USEPOLLING)) + regKey.setInt(_T("UpdateMethod"), 0); + if (isItemChecked(IDC_USEHOOKS)) + regKey.setInt(_T("UpdateMethod"), 1); + if (isItemChecked(IDC_USEDRIVER)) + regKey.setInt(_T("UpdateMethod"), 2); + regKey.setBool(_T("PollConsoleWindows"), isItemChecked(IDC_POLLCONSOLES)); + regKey.setBool(_T("UseCaptureBlt"), isItemChecked(IDC_CAPTUREBLT)); + + // *** LEGACY compatibility *** + regKey.setBool(_T("UseHooks"), isItemChecked(IDC_USEHOOKS)); + return true; + } + protected: + RegKey regKey; + }; + + }; + +}; + +#endif diff --git a/win/vncconfig/Inputs.h b/win/vncconfig/Inputs.h new file mode 100644 index 00000000..1e0b4bac --- /dev/null +++ b/win/vncconfig/Inputs.h @@ -0,0 +1,84 @@ +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ +#ifndef WINVNCCONF_INPUTS +#define WINVNCCONF_INPUTS + +#ifndef SPI_GETBLOCKSENDINPUTRESETS +#define SPI_GETBLOCKSENDINPUTRESETS 0x1026 +#define SPI_SETBLOCKSENDINPUTRESETS 0x1027 +#endif + +#include <rfb_win32/Registry.h> +#include <rfb_win32/Dialog.h> +#include <rfb_win32/OSVersion.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); + enableItem(IDC_DISABLE_LOCAL_INPUTS, !osVersion.isPlatformWindows); + BOOL blocked = FALSE; + if (SystemParametersInfo(SPI_GETBLOCKSENDINPUTRESETS, 0, &blocked, 0)) + setItemChecked(IDC_AFFECT_SCREENSAVER, !blocked); + else + enableAffectSSaver = false; + enableItem(IDC_AFFECT_SCREENSAVER, enableAffectSSaver); + } + bool onCommand(int id, int cmd) { + BOOL inputResetsBlocked; + SystemParametersInfo(SPI_GETBLOCKSENDINPUTRESETS, 0, &inputResetsBlocked, 0); + setChanged((rfb::Server::acceptKeyEvents != isItemChecked(IDC_ACCEPT_KEYS)) || + (rfb::Server::acceptPointerEvents != isItemChecked(IDC_ACCEPT_PTR)) || + (rfb::Server::acceptCutText != isItemChecked(IDC_ACCEPT_CUTTEXT)) || + (rfb::Server::sendCutText != isItemChecked(IDC_SEND_CUTTEXT)) || + (SDisplay::disableLocalInputs != isItemChecked(IDC_DISABLE_LOCAL_INPUTS)) || + (enableAffectSSaver && (!inputResetsBlocked != isItemChecked(IDC_AFFECT_SCREENSAVER)))); + return false; + } + bool onOk() { + regKey.setBool(_T("AcceptKeyEvents"), isItemChecked(IDC_ACCEPT_KEYS)); + regKey.setBool(_T("AcceptPointerEvents"), isItemChecked(IDC_ACCEPT_PTR)); + regKey.setBool(_T("AcceptCutText"), isItemChecked(IDC_ACCEPT_CUTTEXT)); + regKey.setBool(_T("SendCutText"), isItemChecked(IDC_SEND_CUTTEXT)); + regKey.setBool(_T("DisableLocalInputs"), isItemChecked(IDC_DISABLE_LOCAL_INPUTS)); + if (enableAffectSSaver) { + BOOL blocked = !isItemChecked(IDC_AFFECT_SCREENSAVER); + SystemParametersInfo(SPI_SETBLOCKSENDINPUTRESETS, blocked, 0, SPIF_UPDATEINIFILE | SPIF_SENDCHANGE); + } + return true; + } + protected: + RegKey regKey; + bool enableAffectSSaver; + }; + + }; +}; + +#endif diff --git a/win/vncconfig/Legacy.cxx b/win/vncconfig/Legacy.cxx new file mode 100644 index 00000000..ae5d7166 --- /dev/null +++ b/win/vncconfig/Legacy.cxx @@ -0,0 +1,248 @@ +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +#include <vncconfig/Legacy.h> + +#include <rfb/LogWriter.h> +#include <rfb/Password.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)); + + ObfuscatedPasswd passwd; + key.getBinary(_T("Password"), (void**)&passwd.buf, &passwd.length, 0, 0); + regKey.setBinary(_T("Password"), passwd.buf, passwd.length); + + bool enableInputs = key.getBool(_T("InputsEnabled"), true); + regKey.setBool(_T("AcceptKeyEvents"), enableInputs); + regKey.setBool(_T("AcceptPointerEvents"), enableInputs); + regKey.setBool(_T("AcceptCutText"), enableInputs); + regKey.setBool(_T("SendCutText"), enableInputs); + + switch (key.getInt(_T("LockSetting"), 0)) { + case 0: regKey.setString(_T("DisconnectAction"), _T("None")); break; + case 1: regKey.setString(_T("DisconnectAction"), _T("Lock")); break; + case 2: regKey.setString(_T("DisconnectAction"), _T("Logoff")); break; + }; + + regKey.setBool(_T("DisableLocalInputs"), key.getBool(_T("LocalInputsDisabled"), false)); + + // *** ignore polling preferences + // PollUnderCursor, PollForeground, OnlyPollConsole, OnlyPollOnEvent + regKey.setBool(_T("UseHooks"), !key.getBool(_T("PollFullScreen"), false)); + + if (key.isValue(_T("AllowShutdown"))) + MsgBox(0, _T("The AllowShutdown option is not supported by this release."), MB_ICONWARNING | MB_OK); + if (key.isValue(_T("AllowEditClients"))) + MsgBox(0, _T("The AllowEditClients option is not supported by this release."), MB_ICONWARNING | MB_OK); + + allowProperties = key.getBool(_T("AllowProperties"), allowProperties); + } diff --git a/win/vncconfig/Legacy.h b/win/vncconfig/Legacy.h new file mode 100644 index 00000000..02059a64 --- /dev/null +++ b/win/vncconfig/Legacy.h @@ -0,0 +1,85 @@ +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +#ifndef WINVNCCONF_LEGACY +#define WINVNCCONF_LEGACY + +#include <windows.h> +#include <lmcons.h> +#include <vncconfig/resource.h> +#include <rfb_win32/Registry.h> +#include <rfb_win32/Dialog.h> +#include <rfb_win32/MsgBox.h> +#include <rfb/ServerCore.h> +#include <rfb/secTypes.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 diff --git a/win/vncconfig/PasswordDialog.cxx b/win/vncconfig/PasswordDialog.cxx new file mode 100644 index 00000000..d26d86f6 --- /dev/null +++ b/win/vncconfig/PasswordDialog.cxx @@ -0,0 +1,52 @@ +/* Copyright (C) 2004-2005 RealVNC Ltd. All Rights Reserved. + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +#include <vncconfig/resource.h> +#include <vncconfig/PasswordDialog.h> +#include <rfb_win32/MsgBox.h> +#include <rfb/Password.h> + +using namespace rfb; +using namespace win32; + +PasswordDialog::PasswordDialog(const RegKey& rk, bool registryInsecure_) + : Dialog(GetModuleHandle(0)), regKey(rk), registryInsecure(registryInsecure_) { +} + +bool PasswordDialog::showDialog(HWND owner) { + return Dialog::showDialog(MAKEINTRESOURCE(IDD_AUTH_VNC_PASSWD), owner); +} + +bool PasswordDialog::onOk() { + TPlainPasswd password1(getItemString(IDC_PASSWORD1)); + TPlainPasswd password2(getItemString(IDC_PASSWORD2)); + if (_tcscmp(password1.buf, password2.buf) != 0) { + MsgBox(0, _T("The supplied passwords do not match"), + MB_ICONEXCLAMATION | MB_OK); + return false; + } + if (registryInsecure && + (MsgBox(0, _T("Please note that your password cannot be stored securely on this system. ") + _T("Are you sure you wish to continue?"), + MB_YESNO | MB_ICONWARNING) == IDNO)) + return false; + PlainPasswd password(strDup(password1.buf)); + ObfuscatedPasswd obfPwd(password); + regKey.setBinary(_T("Password"), obfPwd.buf, obfPwd.length); + return true; +} diff --git a/win/vncconfig/PasswordDialog.h b/win/vncconfig/PasswordDialog.h new file mode 100644 index 00000000..dd23f8e3 --- /dev/null +++ b/win/vncconfig/PasswordDialog.h @@ -0,0 +1,40 @@ +/* Copyright (C) 2004-2005 RealVNC Ltd. All Rights Reserved. + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ +#ifndef WINVNCCONF_PASSWORD_DIALOG +#define WINVNCCONF_PASSWORD_DIALOG + +#include <rfb_win32/Registry.h> +#include <rfb_win32/Dialog.h> + +namespace rfb { + namespace win32 { + + class PasswordDialog : public Dialog { + public: + PasswordDialog(const RegKey& rk, bool registryInsecure_); + bool showDialog(HWND owner=0); + bool onOk(); + protected: + const RegKey& regKey; + bool registryInsecure; + }; + + }; +}; + +#endif
\ No newline at end of file diff --git a/win/vncconfig/Sharing.h b/win/vncconfig/Sharing.h new file mode 100644 index 00000000..872ae133 --- /dev/null +++ b/win/vncconfig/Sharing.h @@ -0,0 +1,59 @@ +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ +#ifndef WINVNCCONF_SHARING +#define WINVNCCONF_SHARING + +#include <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 diff --git a/win/vncconfig/resource.h b/win/vncconfig/resource.h new file mode 100644 index 00000000..ca1fbf5a --- /dev/null +++ b/win/vncconfig/resource.h @@ -0,0 +1,102 @@ +//{{NO_DEPENDENCIES}} +// Microsoft Developer Studio generated include file. +// Used by vncconfig.rc +// +#define IDR_MANIFEST 1 +#define IDI_ICON 101 +#define IDD_DIALOG1 102 +#define IDD_DIALOG2 103 +#define IDD_SECURITY 104 +#define IDD_AUTHENTICATION 104 +#define IDD_CONNECTIONS 105 +#define IDD_HOOKING 106 +#define IDD_VNC_PASSWD 107 +#define IDD_AUTH_VNC_PASSWD 107 +#define IDD_LEGACY 108 +#define IDD_CONN_HOST 109 +#define IDD_SHARING 110 +#define IDD_INPUTS 111 +#define IDR_TRAY 112 +#define IDD_ABOUT 113 +#define IDI_CONNECTED 115 +#define IDD_DESKTOP 116 +#define IDC_EDIT1 1000 +#define IDC_PORT 1000 +#define IDC_PASSWORD1 1000 +#define IDC_HOST_PATTERN 1000 +#define IDC_AUTH_NONE 1002 +#define IDC_AUTH_VNC 1003 +#define IDC_AUTH_VNC_PASSWD 1009 +#define IDC_USEHOOKS 1011 +#define IDC_POLLCONSOLES 1012 +#define IDC_COMPAREFB 1013 +#define IDC_IDLE_TIMEOUT 1015 +#define IDC_HOSTS 1016 +#define IDC_HOST_ADD 1017 +#define IDC_HOST_REMOVE 1018 +#define IDC_HOST_UP 1019 +#define IDC_BUTTON4 1020 +#define IDC_HOST_DOWN 1020 +#define IDC_AUTH_INPUTONLY_PASSWD 1020 +#define IDC_HOST_EDIT 1021 +#define IDC_PASSWORD2 1022 +#define IDC_LEGACY_IMPORT 1023 +#define IDC_ALLOW 1024 +#define IDC_DENY 1025 +#define IDC_SHARE_ALWAYS 1030 +#define IDC_SHARE_NEVER 1031 +#define IDC_SHARE_CLIENT 1032 +#define IDC_DISCONNECT_CLIENTS 1033 +#define IDC_ACCEPT_KEYS 1034 +#define IDC_ACCEPT_PTR 1035 +#define IDC_ACCEPT_CUTTEXT 1036 +#define IDC_SEND_CUTTEXT 1037 +#define IDC_PROTOCOL_3_3 1038 +#define IDC_DESCRIPTION 1039 +#define IDC_BUILDTIME 1040 +#define IDC_VERSION 1041 +#define IDC_COPYRIGHT 1042 +#define IDC_HTTP_ENABLE 1043 +#define IDC_HTTP_PORT 1044 +#define IDC_BL_THRESHOLD 1046 +#define IDC_BL_TIMEOUT 1047 +#define IDC_AFFECT_SCREENSAVER 1048 +#define IDC_LOCALHOST 1049 +#define IDC_DISABLE_LOCAL_INPUTS 1050 +#define IDC_AUTH_NT 1051 +#define IDC_AUTH_NT_CONF 1052 +#define IDC_AUTH_RA2_CONF 1053 +#define IDC_QUERY_CONNECT 1055 +#define IDC_DISCONNECT_NONE 1056 +#define IDC_DISCONNECT_LOCK 1057 +#define IDC_DISCONNECT_LOGOFF 1058 +#define IDC_REMOVE_WALLPAPER 1059 +#define IDC_REMOVE_PATTERN 1060 +#define IDC_DISABLE_EFFECTS 1061 +#define IDC_CAPTUREBLT 1062 +#define IDC_ENCRYPTION 1063 +#define IDC_QUERY 1064 +#define IDC_USEPOLLING 1066 +#define IDC_USEDRIVER 1068 +#define IDC_QUERY_LOGGED_ON 1069 +#define IDC_AUTH_ADMIN_PASSWD 1076 +#define IDC_AUTH_VIEWONLY_PASSWD 1077 +#define IDC_AUTH_ADMIN_ENABLE 1078 +#define IDC_AUTH_VIEWONLY_ENABLE 1079 +#define IDC_AUTH_INPUTONLY_ENABLE 1080 +#define IDC_AUTH_VNC_EXT 1081 +#define IDC_RFB_ENABLE 1082 +#define ID_OPTIONS 40001 +#define ID_CLOSE 40002 +#define ID_ABOUT 40003 + +// Next default values for new objects +// +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS +#define _APS_NEXT_RESOURCE_VALUE 117 +#define _APS_NEXT_COMMAND_VALUE 40004 +#define _APS_NEXT_CONTROL_VALUE 1083 +#define _APS_NEXT_SYMED_VALUE 101 +#endif +#endif diff --git a/win/vncconfig/vncconfig.cxx b/win/vncconfig/vncconfig.cxx new file mode 100644 index 00000000..6c9e1c5a --- /dev/null +++ b/win/vncconfig/vncconfig.cxx @@ -0,0 +1,191 @@ +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +#include <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"); + + +#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\\TightVNC\\WinVNC4")); + + // Override whatever security it already had (NT only) + bool warnOnChangePassword = false; + try { + AccessEntries access; + Sid::Administrators adminSID; + Sid::SYSTEM systemSID; + access.addEntry(adminSID, KEY_ALL_ACCESS, GRANT_ACCESS); + access.addEntry(systemSID, KEY_ALL_ACCESS, GRANT_ACCESS); + UserSID userSID; + if (configKey == HKEY_CURRENT_USER) + access.addEntry(userSID, 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 a RegConfig thread, to load in existing settings + RegConfigThread config; + config.start(configKey, _T("Software\\TightVNC\\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 + } 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; + } + + return 0; +} diff --git a/win/vncconfig/vncconfig.dsp b/win/vncconfig/vncconfig.dsp new file mode 100644 index 00000000..b096ded0 --- /dev/null +++ b/win/vncconfig/vncconfig.dsp @@ -0,0 +1,196 @@ +# 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\vncconfig"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /YX /FD /c
+# ADD CPP /nologo /MT /W3 /GX /O2 /I ".." /I "../../common" /FI"rdr/msvcwarning.h" /D "NDEBUG" /D "_WINDOWS" /D "WIN32" /D "_MBCS" /YX /FD /c
+# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32
+# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32
+# ADD BASE RSC /l 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\vncconfig"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /YX /FD /GZ /c
+# ADD CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /I ".." /I "../../common" /FI"rdr/msvcwarning.h" /D "_DEBUG" /D "_WINDOWS" /D "WIN32" /D "_MBCS" /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\vncconfig"
+# 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 ".." /I "../../common" /FI"rdr/msvcwarning.h" /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=.\PasswordDialog.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=.\PasswordDialog.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/win/vncconfig/vncconfig.exe.manifest b/win/vncconfig/vncconfig.exe.manifest new file mode 100644 index 00000000..77cb1b9a --- /dev/null +++ b/win/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="TightVNC.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/win/vncconfig/vncconfig.ico b/win/vncconfig/vncconfig.ico Binary files differnew file mode 100644 index 00000000..1b42416c --- /dev/null +++ b/win/vncconfig/vncconfig.ico diff --git a/win/vncconfig/vncconfig.rc b/win/vncconfig/vncconfig.rc new file mode 100644 index 00000000..bf2f9696 --- /dev/null +++ b/win/vncconfig/vncconfig.rc @@ -0,0 +1,496 @@ +//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, 193, 135 +STYLE DS_MODALFRAME | DS_CONTROL | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "Authentication" +FONT 8, "MS Sans Serif" +BEGIN + CONTROL "No Authentication",IDC_AUTH_NONE,"Button", + BS_AUTORADIOBUTTON | WS_GROUP,7,10,113,15 + CONTROL "VNC Password Authentication",IDC_AUTH_VNC,"Button", + BS_AUTORADIOBUTTON,7,30,113,15 + PUSHBUTTON "Configure",IDC_AUTH_VNC_PASSWD,125,30,61,15 + CONTROL "NT Logon Authentication",IDC_AUTH_NT,"Button", + BS_AUTORADIOBUTTON,7,50,113,15 + PUSHBUTTON "Configure",IDC_AUTH_NT_CONF,125,50,61,15 + LTEXT "Encryption:",IDC_STATIC,7,70,42,15,SS_CENTERIMAGE + COMBOBOX IDC_ENCRYPTION,49,70,71,50,CBS_DROPDOWN | WS_VSCROLL | + WS_TABSTOP + PUSHBUTTON "Generate Keys",IDC_AUTH_RA2_CONF,125,70,61,15 + CONTROL "Prompt local user to accept connections", + IDC_QUERY_CONNECT,"Button",BS_AUTOCHECKBOX | WS_TABSTOP, + 7,95,181,15 + CONTROL "Only prompt when there is a user logged on", + IDC_QUERY_LOGGED_ON,"Button",BS_AUTOCHECKBOX | + WS_TABSTOP,20,110,166,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 + CONTROL "Accept connections on port:",IDC_RFB_ENABLE,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,7,10,138,15 + EDITTEXT IDC_PORT,150,10,61,15,ES_AUTOHSCROLL | ES_NUMBER + LTEXT "Disconnect idle clients after (seconds):",IDC_STATIC,7, + 25,138,15,SS_CENTERIMAGE + 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, 197, 101 +STYLE DS_MODALFRAME | DS_CONTROL | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "Capture Method" +FONT 8, "MS Sans Serif" +BEGIN + CONTROL "Poll for changes to the desktop",IDC_USEPOLLING,"Button", + BS_AUTORADIOBUTTON | WS_GROUP,7,10,183,15 + CONTROL "Use VNC hooks to track changes",IDC_USEHOOKS,"Button", + BS_AUTORADIOBUTTON,7,25,183,15 + CONTROL "Poll console windows for updates",IDC_POLLCONSOLES, + "Button",BS_AUTOCHECKBOX | WS_TABSTOP,25,40,165,15 + CONTROL "Use VNC Mirror driver to track changes",IDC_USEDRIVER, + "Button",BS_AUTORADIOBUTTON,7,55,183,15 + CONTROL "Capture alpha-blended windows",IDC_CAPTUREBLT,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,7,70,183,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 "VNC Server Password" +FONT 8, "MS Sans Serif" +BEGIN + LTEXT "New Password:",IDC_STATIC,7,10,63,15 + EDITTEXT IDC_PASSWORD1,75,10,130,15,ES_PASSWORD | ES_AUTOHSCROLL + LTEXT "Confirm Password:",IDC_STATIC,7,30,63,14 + EDITTEXT IDC_PASSWORD2,75,30,130,14,ES_PASSWORD | ES_AUTOHSCROLL + DEFPUSHBUTTON "OK",IDOK,100,50,50,15 + PUSHBUTTON "Cancel",IDCANCEL,155,50,50,15 +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, 57 +STYLE DS_SYSMODAL | DS_MODALFRAME | DS_CENTER | WS_POPUP | WS_CAPTION +CAPTION "Specify Host IP Address Pattern" +FONT 8, "MS Sans Serif" +BEGIN + EDITTEXT IDC_HOST_PATTERN,65,5,100,15,ES_AUTOHSCROLL + CONTROL "&Allow",IDC_ALLOW,"Button",BS_AUTORADIOBUTTON | + WS_GROUP,7,5,53,15 + CONTROL "&Deny",IDC_DENY,"Button",BS_AUTORADIOBUTTON,7,20,53,15 + CONTROL "Query",IDC_QUERY,"Button",BS_AUTORADIOBUTTON,7,35,53,15 + DEFPUSHBUTTON "OK",IDOK,115,35,50,15 + PUSHBUTTON "Cancel",IDCANCEL,170,35,50,15 + LTEXT "e.g. 192.168.0.0/255.255.0.0",IDC_STATIC,65,20,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 pointer events from clients",IDC_ACCEPT_PTR, + "Button",BS_AUTOCHECKBOX | WS_TABSTOP,7,10,172,15 + CONTROL "Accept keyboard events from clients",IDC_ACCEPT_KEYS, + "Button",BS_AUTOCHECKBOX | WS_TABSTOP,7,25,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.tightvnc.com for more information on TightVNC.", + 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 + 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 + GROUPBOX "When last client disconnects",IDC_STATIC,7,70,171,60 + 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 +END + + +///////////////////////////////////////////////////////////////////////////// +// +// DESIGNINFO +// + +#ifdef APSTUDIO_INVOKED +GUIDELINES DESIGNINFO DISCARDABLE +BEGIN + IDD_AUTHENTICATION, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 186 + VERTGUIDE, 20 + VERTGUIDE, 49 + VERTGUIDE, 120 + VERTGUIDE, 125 + TOPMARGIN, 7 + BOTTOMMARGIN, 128 + HORZGUIDE, 10 + HORZGUIDE, 25 + HORZGUIDE, 30 + HORZGUIDE, 45 + HORZGUIDE, 50 + HORZGUIDE, 65 + HORZGUIDE, 70 + HORZGUIDE, 85 + HORZGUIDE, 90 + HORZGUIDE, 105 + HORZGUIDE, 110 + HORZGUIDE, 125 + 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, 190 + VERTGUIDE, 25 + TOPMARGIN, 7 + BOTTOMMARGIN, 94 + HORZGUIDE, 10 + HORZGUIDE, 25 + HORZGUIDE, 40 + HORZGUIDE, 55 + HORZGUIDE, 70 + HORZGUIDE, 85 + END + + IDD_AUTH_VNC_PASSWD, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 205 + VERTGUIDE, 70 + VERTGUIDE, 75 + VERTGUIDE, 90 + 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, 220 + VERTGUIDE, 60 + VERTGUIDE, 65 + VERTGUIDE, 115 + VERTGUIDE, 165 + VERTGUIDE, 170 + TOPMARGIN, 5 + BOTTOMMARGIN, 50 + HORZGUIDE, 20 + HORZGUIDE, 35 + 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, 182 + TOPMARGIN, 7 + BOTTOMMARGIN, 32 + END +END +#endif // APSTUDIO_INVOKED + + +#ifndef _MAC +///////////////////////////////////////////////////////////////////////////// +// +// Version +// + +VS_VERSION_INFO VERSIONINFO + FILEVERSION 4,1,1,0 + PRODUCTVERSION 4,1,1,0 + 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", "Constantin Kaplinsky\0" + VALUE "FileDescription", "TightVNC Server Configuration Applet for Win32\0" + VALUE "FileVersion", "4.1.1\0" + VALUE "InternalName", "vncconfig\0" + VALUE "LegalCopyright", "Copyright (C) 1998-2005 [many holders]\0" + VALUE "LegalTrademarks", "TightVNC\0" + VALUE "OriginalFilename", "vncconfig.exe\0" + VALUE "PrivateBuild", "\0" + VALUE "ProductName", "TightVNC Configurator\0" + VALUE "ProductVersion", "4.1.1\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/win/vncviewer/CConn.cxx b/win/vncviewer/CConn.cxx new file mode 100644 index 00000000..73597f52 --- /dev/null +++ b/win/vncviewer/CConn.cxx @@ -0,0 +1,712 @@ +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +#include <windows.h> +#include <winsock2.h> +#include <vncviewer/UserPasswdDialog.h> +#include <vncviewer/CConn.h> +#include <vncviewer/CConnThread.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/AboutDialog.h> + +using namespace rfb; +using namespace rfb::win32; +using namespace rdr; + +// - Statics & consts + +static LogWriter vlog("CConn"); + + +const int IDM_FULLSCREEN = ID_FULLSCREEN; +const int IDM_SEND_MENU_KEY = ID_SEND_MENU_KEY; +const int IDM_SEND_CAD = ID_SEND_CAD; +const int IDM_SEND_CTLESC = ID_SEND_CTLESC; +const int IDM_ABOUT = ID_ABOUT; +const int IDM_OPTIONS = ID_OPTIONS; +const int IDM_INFO = ID_INFO; +const int IDM_NEWCONN = ID_NEW_CONNECTION; +const int IDM_REQUEST_REFRESH = ID_REQUEST_REFRESH; +const int IDM_CTRL_KEY = ID_CTRL_KEY; +const int IDM_ALT_KEY = ID_ALT_KEY; +const int IDM_FILE_TRANSFER = ID_FILE_TRANSFER; +const int IDM_CONN_SAVE_AS = ID_CONN_SAVE_AS; + + +static IntParameter debugDelay("DebugDelay","Milliseconds to display inverted " + "pixel data - a debugging feature", 0); + + +// +// -=- CConn implementation +// + +RegKey CConn::userConfigKey; + + +CConn::CConn() + : window(0), sock(0), sockEvent(CreateEvent(0, TRUE, FALSE, 0)), requestUpdate(false), + sameMachine(false), encodingChange(false), formatChange(false), + reverseConnection(false), lastUsedEncoding_(encodingRaw), isClosed_(false) { +} + +CConn::~CConn() { + delete window; +} + +bool CConn::initialise(network::Socket* s, bool reverse) { + // 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, in order of preference + addSecType(secTypeVncAuth); + addSecType(secTypeNone); + + // Start the RFB protocol + sock = s; + reverseConnection = reverse; + initialiseProtocol(); + + m_fileTransfer.initialize(&s->inStream(), &s->outStream()); + + return true; +} + + +void +CConn::applyOptions(CConnOptions& opt) { + // - If any encoding-related settings have changed then we must + // notify the server of the new settings + encodingChange |= ((options.useLocalCursor != opt.useLocalCursor) || + (options.useDesktopResize != opt.useDesktopResize) || + (options.customCompressLevel != opt.customCompressLevel) || + (options.compressLevel != opt.compressLevel) || + (options.noJpeg != opt.noJpeg) || + (options.qualityLevel != opt.qualityLevel) || + (options.preferredEncoding != opt.preferredEncoding)); + + // - If the preferred pixel format has changed then notify the server + formatChange |= (options.fullColour != opt.fullColour); + if (!opt.fullColour) + formatChange |= (options.lowColourLevel != opt.lowColourLevel); + + // - Save the new set of options + options = opt; + + // - Set optional features in ConnParams + cp.supportsLocalCursor = options.useLocalCursor; + cp.supportsDesktopResize = options.useDesktopResize; + cp.customCompressLevel = options.customCompressLevel; + cp.compressLevel = options.compressLevel; + cp.noJpeg = options.noJpeg; + cp.qualityLevel = options.qualityLevel; + + // - Configure connection sharing on/off + setShared(options.shared); + + // - Whether to use protocol 3.3 for legacy compatibility + setProtocol3_3(options.protocol3_3); + + // - Apply settings that affect the window, if it is visible + if (window) { + window->setMonitor(options.monitor.buf); + window->setFullscreen(options.fullScreen); + window->setEmulate3(options.emulate3); + window->setPointerEventInterval(options.pointerEventInterval); + window->setMenuKey(options.menuKey); + window->setDisableWinKeys(options.disableWinKeys); + window->setShowToolbar(options.showToolbar); + if (!options.useLocalCursor) + window->setCursor(0, 0, Point(), 0, 0); + } +} + + +void +CConn::displayChanged() { + // Display format has changed - recalculate the full-colour pixel format + calculateFullColourPF(); +} + +void +CConn::paintCompleted() { + // A repaint message has just completed - request next update if necessary + requestNewUpdate(); +} + +bool +CConn::sysCommand(WPARAM wParam, LPARAM lParam) { + // - If it's one of our (F8 Menu) messages + switch (wParam) { + case IDM_FULLSCREEN: + options.fullScreen = !window->isFullscreen(); + window->setFullscreen(options.fullScreen); + return true; + case IDM_SHOW_TOOLBAR: + options.showToolbar = !window->isToolbarEnabled(); + window->setShowToolbar(options.showToolbar); + return true; + case IDM_CTRL_KEY: + window->kbd.keyEvent(this, VK_CONTROL, 0, !window->kbd.keyPressed(VK_CONTROL)); + return true; + case IDM_ALT_KEY: + window->kbd.keyEvent(this, VK_MENU, 0, !window->kbd.keyPressed(VK_MENU)); + return true; + case IDM_SEND_MENU_KEY: + window->kbd.keyEvent(this, options.menuKey, 0, true); + window->kbd.keyEvent(this, options.menuKey, 0, false); + return true; + case IDM_SEND_CAD: + window->kbd.keyEvent(this, VK_CONTROL, 0, true); + window->kbd.keyEvent(this, VK_MENU, 0, true); + window->kbd.keyEvent(this, VK_DELETE, 0x1000000, true); + window->kbd.keyEvent(this, VK_DELETE, 0x1000000, false); + window->kbd.keyEvent(this, VK_MENU, 0, false); + window->kbd.keyEvent(this, VK_CONTROL, 0, false); + return true; + case IDM_SEND_CTLESC: + window->kbd.keyEvent(this, VK_CONTROL, 0, true); + window->kbd.keyEvent(this, VK_ESCAPE, 0, true); + window->kbd.keyEvent(this, VK_ESCAPE, 0, false); + window->kbd.keyEvent(this, VK_CONTROL, 0, false); + return true; + 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 true; + case IDM_NEWCONN: + { + Thread* newThread = new CConnThread; + } + return true; + case IDM_OPTIONS: + // Update the monitor device name in the CConnOptions instance + options.monitor.replaceBuf(window->getMonitor()); + showOptionsDialog(); + return true; + case IDM_INFO: + infoDialog.showDialog(this); + return true; + case IDM_ABOUT: + AboutDialog::instance.showDialog(); + return true; + case IDM_FILE_TRANSFER: + m_fileTransfer.show(window->getHandle()); + return true; + case IDM_CONN_SAVE_AS: + return true; + case ID_CLOSE: + // FIXME: Remove the corresponding toolbar button. + return true; + }; + return false; +} + + +void +CConn::closeWindow() { + vlog.info("window closed"); + close(); +} + + +void +CConn::refreshMenu(bool enableSysItems) { + HMENU menu = GetSystemMenu(window->getHandle(), FALSE); + + if (!enableSysItems) { + // Gray out menu items that might cause a World Of Pain + 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 = window->kbd.keyPressed(VK_CONTROL) ? MF_CHECKED : MF_UNCHECKED; + UINT altCheckFlags = window->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); + } + + // Set the menu fullscreen option tick + CheckMenuItem(menu, IDM_FULLSCREEN, (window->isFullscreen() ? MF_CHECKED : 0) | MF_BYCOMMAND); + + // Set the menu toolbar option tick + int toolbarFlags = window->isToolbarEnabled() ? MF_CHECKED : 0; + CheckMenuItem(menu, IDM_SHOW_TOOLBAR, MF_BYCOMMAND | toolbarFlags); + + // In the full-screen mode, "Show toolbar" should be grayed. + toolbarFlags = window->isFullscreen() ? MF_GRAYED : MF_ENABLED; + EnableMenuItem(menu, IDM_SHOW_TOOLBAR, MF_BYCOMMAND | toolbarFlags); +} + + +void +CConn::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. + if (!isClosed()) { + if (WSAEventSelect(sock->getFd(), sockEvent, FD_READ | FD_CLOSE) == SOCKET_ERROR) + throw rdr::SystemException("Unable to wait for sokcet data", WSAGetLastError()); + } + while (true) { + // If we have closed then we can't block waiting for data + if (isClosed()) + throw rdr::EndOfStream(); + + // Wait for socket data, or a message to process + DWORD result = MsgWaitForMultipleObjects(1, &sockEvent.h, FALSE, INFINITE, QS_ALLINPUT); + if (result == WAIT_OBJECT_0) { + // - Network event notification. Return control to I/O routine. + break; + } else if (result == WAIT_FAILED) { + // - The wait operation failed - raise an exception + throw rdr::SystemException("blockCallback wait error", GetLastError()); + } + + // - There should be a message in the message queue + MSG msg; + while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) { + // 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); + } + } + + // Before we return control to the InStream, reset the network event + WSAEventSelect(sock->getFd(), sockEvent, 0); + ResetEvent(sockEvent); +} + + +void CConn::keyEvent(rdr::U32 key, bool down) { + if (!options.sendKeyEvents) return; + try { + writer()->keyEvent(key, down); + } catch (rdr::Exception& e) { + close(e.str()); + } +} +void CConn::pointerEvent(const Point& pos, int buttonMask) { + if (!options.sendPtrEvents) return; + try { + writer()->pointerEvent(pos, buttonMask); + } catch (rdr::Exception& e) { + close(e.str()); + } +} +void CConn::clientCutText(const char* str, int len) { + if (!options.clientCutText) return; + if (state() != RFBSTATE_NORMAL) return; + try { + writer()->clientCutText(str, len); + } catch (rdr::Exception& e) { + close(e.str()); + } +} + + +CSecurity* CConn::getCSecurity(int secType) +{ + switch (secType) { + case secTypeNone: + return new CSecurityNone(); + case secTypeVncAuth: + return new CSecurityVncAuth(this); + default: + throw Exception("Unsupported secType?"); + } +} + + +void +CConn::setColourMapEntries(int first, int count, U16* rgbs) { + vlog.debug("setColourMapEntries: first=%d, count=%d", first, count); + int i; + for (i=0;i<count;i++) + window->setColour(i+first, rgbs[i*3], rgbs[i*3+1], rgbs[i*3+2]); + // *** change to 0, 256? + window->refreshWindowPalette(first, count); +} + +void +CConn::bell() { + if (options.acceptBell) + MessageBeep(-1); +} + + +void +CConn::setDesktopSize(int w, int h) { + vlog.debug("setDesktopSize %dx%d", w, h); + + // Resize the window's buffer + if (window) + window->setSize(w, h); + + // Tell the underlying CConnection + CConnection::setDesktopSize(w, h); +} + +void +CConn::setCursor(int w, int h, const Point& hotspot, void* data, void* mask) { + if (!options.useLocalCursor) return; + + // Set the window to use the new cursor + window->setCursor(w, h, hotspot, data, mask); +} + + +void +CConn::close(const char* reason) { + // If already closed then ignore this + if (isClosed()) + return; + + // Hide the window, if it exists + if (window) + ShowWindow(window->getHandle(), SW_HIDE); + + // Save the reason & flag that we're closed & shutdown the socket + isClosed_ = true; + closeReason_.replaceBuf(strDup(reason)); + sock->shutdown(); +} + + +void +CConn::showOptionsDialog() { + optionsDialog.showDialog(this); +} + + +void +CConn::framebufferUpdateEnd() { + if (debugDelay != 0) { + vlog.debug("debug delay %d",(int)debugDelay); + UpdateWindow(window->getHandle()); + Sleep(debugDelay); + std::list<rfb::Rect>::iterator i; + for (i = debugRects.begin(); i != debugRects.end(); i++) { + window->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(window->getHandle(), 0, FALSE)) { + if (!(GetWindowLong(window->getHandle(), GWL_STYLE) & WS_MINIMIZE)) + requestNewUpdate(); + } + + // Make sure the local cursor is shown + window->showCursor(); +} + + +// Note: The method below is duplicated in vncviewer_unix/CConn.cxx! + +// autoSelectFormatAndEncoding() chooses the format and encoding appropriate +// to the connection speed: +// +// Above 16Mbps (timing for at least a second), switch to hextile +// Otherwise, switch to ZRLE +// +// Above 256Kbps, use full colour mode +// +void +CConn::autoSelectFormatAndEncoding() { + int kbitsPerSecond = sock->inStream().kbitsPerSecond(); + unsigned int newEncoding = options.preferredEncoding; + + bool newFullColour = options.fullColour; + unsigned int timeWaited = sock->inStream().timeWaited(); + + // Select best encoding + if (kbitsPerSecond > 16000 && timeWaited >= 10000) { + newEncoding = encodingHextile; + } else { + 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 == 0) { + return; + } + + if (cp.beforeVersion(3, 8)) { + // Xvnc from TightVNC 1.2.9 sends out FramebufferUpdates with + // cursors "asynchronously". If this happens in the middle of a + // pixel format change, the server will encode the cursor with + // the old format, but the client will try to decode it + // according to the new format. This will lead to a + // crash. Therefore, we do not allow automatic format change for + // old servers. + return; + } + + // Select best color level + newFullColour = (kbitsPerSecond > 256); + if (newFullColour != options.fullColour) { + vlog.info("Throughput %d kbit/s - full color is now %s", + kbitsPerSecond, + newFullColour ? "enabled" : "disabled"); + options.fullColour = newFullColour; + formatChange = true; + } +} + +void +CConn::requestNewUpdate() { + if (!requestUpdate) return; + + if (formatChange) { + // Select the required pixel format + if (options.fullColour) { + window->setPF(fullColourPF); + } else { + switch (options.lowColourLevel) { + case 0: + window->setPF(PixelFormat(8,3,0,1,1,1,1,2,1,0)); + break; + case 1: + window->setPF(PixelFormat(8,6,0,1,3,3,3,4,2,0)); + break; + case 2: + window->setPF(PixelFormat(8,8,0,0,0,0,0,0,0,0)); + break; + } + } + + // Print the current pixel format + char str[256]; + window->getPF().print(str, 256); + vlog.info("Using pixel format %s",str); + + // Save the connection pixel format and tell server to use it + cp.setPF(window->getPF()); + writer()->writeSetPixelFormat(cp.pf()); + + // Correct the local window's palette + if (!window->getNativePF().trueColour) + window->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 +CConn::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 = window->getNativePF(); + if ((serverDefaultPF.bpp < native.bpp) || + ((serverDefaultPF.bpp == native.bpp) && + (serverDefaultPF.depth < native.depth))) + fullColourPF = serverDefaultPF; + else + fullColourPF = window->getNativePF(); + } + formatChange = true; +} + + +void +CConn::setName(const char* name) { + if (window) + window->setName(name); + CConnection::setName(name); +} + + +void CConn::serverInit() { + CConnection::serverInit(); + + // If using AutoSelect with old servers, start in FullColor + // mode. See comment in autoSelectFormatAndEncoding. + if (cp.beforeVersion(3, 8) && options.autoSelect) { + options.fullColour = true; + } + + // Show the window + window = new DesktopWindow(this); + window->setName(cp.name()); + window->setSize(cp.width, cp.height); + applyOptions(options); + + // 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(); + + // Update the window menu + HMENU wndmenu = GetSystemMenu(window->getHandle(), FALSE); + int toolbarChecked = options.showToolbar ? MF_CHECKED : 0; + + AppendMenu(wndmenu, MF_SEPARATOR, 0, 0); + AppendMenu(wndmenu, MF_STRING, IDM_FULLSCREEN, _T("&Full screen")); + AppendMenu(wndmenu, MF_STRING | toolbarChecked, IDM_SHOW_TOOLBAR, + _T("Show tool&bar")); + 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_SEND_CTLESC, _T("Send Ctrl-&Esc")); + AppendMenu(wndmenu, MF_STRING, IDM_REQUEST_REFRESH, _T("Refres&h Screen")); + AppendMenu(wndmenu, MF_SEPARATOR, 0, 0); + 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...")); +} + +void +CConn::serverCutText(const char* str, int len) { + if (!options.serverCutText) return; + window->serverCutText(str, len); +} + + +void CConn::beginRect(const Rect& r, unsigned int encoding) { + sock->inStream().startTiming(); +} + +void CConn::endRect(const Rect& r, unsigned int encoding) { + sock->inStream().stopTiming(); + lastUsedEncoding_ = encoding; + if (debugDelay != 0) { + window->invertRect(r); + debugRects.push_back(r); + } +} + +void CConn::fillRect(const Rect& r, Pixel pix) { + window->fillRect(r, pix); +} +void CConn::imageRect(const Rect& r, void* pixels) { + window->imageRect(r, pixels); +} +void CConn::copyRect(const Rect& r, int srcX, int srcY) { + window->copyRect(r, srcX, srcY); +} + +void CConn::getUserPasswd(char** user, char** password) { + if (!user && options.passwordFile.buf[0]) { + FILE* fp = fopen(options.passwordFile.buf, "rb"); + if (fp) { + char data[256]; + int datalen = fread(data, 1, 256, fp); + fclose(fp); + if (datalen == 8) { + ObfuscatedPasswd obfPwd; + obfPwd.buf = data; + obfPwd.length = datalen; + PlainPasswd passwd(obfPwd); + *password = strDup(passwd.buf); + memset(data, 0, strlen(data)); + } + } + } + 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()); + userPasswdDialog.getUserPasswd(user, password); + } + if (user) options.setUserName(*user); + if (password) options.setPassword(*password); +} + +bool CConn::processFTMsg(int type) { + return m_fileTransfer.processFTMsg(type); +} diff --git a/win/vncviewer/CConn.h b/win/vncviewer/CConn.h new file mode 100644 index 00000000..29023f39 --- /dev/null +++ b/win/vncviewer/CConn.h @@ -0,0 +1,165 @@ +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +// -=- CConn.h + +// Windows-specific implementation of CConnection + +#ifndef __RFB_WIN32_CCONN_H__ +#define __RFB_WIN32_CCONN_H__ + +#include <network/Socket.h> +#include <rfb/CConnection.h> +#include <rfb/Cursor.h> +#include <rfb/UserPasswdGetter.h> +#include <rfb_win32/Registry.h> +#include <rfb_win32/Handle.h> +#include <vncviewer/InfoDialog.h> +#include <vncviewer/OptionsDialog.h> +#include <vncviewer/CConnOptions.h> +#include <vncviewer/DesktopWindow.h> +#include <vncviewer/FileTransfer.h> +#include <list> + + +namespace rfb { + + namespace win32 { + + class CConn : public CConnection, + UserPasswdGetter, + DesktopWindow::Callback, + rdr::FdInStreamBlockCallback + { + public: + CConn(); + ~CConn(); + + // - Start the VNC session on the supplied socket + // The socket must already be connected to a host + bool initialise(network::Socket* s, bool reverse=false); + + // - Set/get the session options + void applyOptions(CConnOptions& opt); + const CConnOptions& getOptions() const { return options; }; + + // - Show the options dialog for the connection + void showOptionsDialog(); + + // - Close the socket & set the reason for closure + void close(const char* reason=0); + bool isClosed() const { return isClosed_; } + const char* closeReason() const { return closeReason_.buf; } + + // - Last received encoding, for the Info dialog + int lastUsedEncoding() const { return lastUsedEncoding_; } + + // - Get at the DesktopWindow, if any + DesktopWindow* getWindow() { return window; } + + // - Get at the underlying Socket + network::Socket* getSocket() { return sock; } + + // - Get the server's preferred format + const PixelFormat& getServerDefaultPF() const { return serverDefaultPF; } + + // Global user-config registry key + static RegKey userConfigKey; + + bool processFTMsg(int type); + + protected: + // InputHandler interface (via DesktopWindow::Callback) + void keyEvent(rdr::U32 key, bool down); + void pointerEvent(const Point& pos, int buttonMask); + void clientCutText(const char* str, int len); + + // DesktopWindow::Callback interface + void displayChanged(); + void paintCompleted(); + bool sysCommand(WPARAM wParam, LPARAM lParam); + void closeWindow(); + void refreshMenu(bool enableSysCommands); + + // CConnection interface + CSecurity* getCSecurity(int secType); + void setColourMapEntries(int firstColour, int nColours, rdr::U16* rgbs); + void bell(); + void framebufferUpdateEnd(); + void setDesktopSize(int w, int h); + void setCursor(int w, int h, const Point& hotspot, void* data, void* mask); + void setName(const char* name); + void serverInit(); + void serverCutText(const char* str, int len); + void beginRect(const Rect& r, unsigned int encoding); + void endRect(const Rect& r, unsigned int encoding); + void fillRect(const Rect& r, Pixel pix); + void imageRect(const Rect& r, void* pixels); + void copyRect(const Rect& r, int srcX, int srcY); + + // rdr::FdInStreamBlockCallback interface + void blockCallback(); + + // UserPasswdGetter interface + // (overridden to allow a pre-supplied username & password) + void getUserPasswd(char** user, char** password); + + // CConn-specific internal interface + void autoSelectFormatAndEncoding(); + void requestNewUpdate(); + void calculateFullColourPF(); + + // The desktop window + DesktopWindow* window; + + // Info and Options dialogs + OptionsDialog optionsDialog; + InfoDialog infoDialog; + + // VNC Viewer options + CConnOptions options; + + // Pixel format and encoding + PixelFormat serverDefaultPF; + PixelFormat fullColourPF; + bool sameMachine; + bool encodingChange; + bool formatChange; + int lastUsedEncoding_; + + // Networking and RFB protocol + network::Socket* sock; + Handle sockEvent; + bool reverseConnection; + bool requestUpdate; + + // Debugging/logging + std::list<Rect> debugRects; + CharArray closeReason_; + bool isClosed_; + + FileTransfer m_fileTransfer; + }; + + }; + +}; + +#endif + + diff --git a/win/vncviewer/CConnOptions.cxx b/win/vncviewer/CConnOptions.cxx new file mode 100644 index 00000000..4ea0ada1 --- /dev/null +++ b/win/vncviewer/CConnOptions.cxx @@ -0,0 +1,450 @@ +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +#include <vncviewer/CConnOptions.h> +#include <rfb/Configuration.h> +#include <rfb/encodings.h> +#include <rfb/LogWriter.h> +#include <rfb_win32/MsgBox.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 StringParameter passwordFile("PasswordFile", + "Password file for VNC authentication", ""); + +// - Settings stored in the registry & in .vnc files, by Save Defaults and +// Save Configuration respectively. + +static BoolParameter useLocalCursor("UseLocalCursor", "Render the mouse cursor locally", true); +static BoolParameter useDesktopResize("UseDesktopResize", "Support dynamic desktop resizing", true); + +static BoolParameter fullColour("FullColor", + "Use full color", true); +static AliasParameter fullColourAlias("FullColour", "Alias for FullColor", &fullColour); + +static IntParameter lowColourLevel("LowColorLevel", + "Color level to use on slow connections. " + "0 = Very Low (8 colors), 1 = Low (64 colors), 2 = Medium (256 colors)", + 2); +static AliasParameter lowColourLevelAlias("LowColourLevel", "Alias for LowColorLevel", &lowColourLevel); + +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 encoding to use (Tight, ZRLE, Hextile or" + " Raw)", "Tight"); +static BoolParameter autoSelect("AutoSelect", + "Auto select pixel format and encoding. " + "Default if PreferredEncoding and FullColor are not specified.", + 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 disableWinKeys("DisableWinKeys", + "Pass special Windows keys directly to 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 BoolParameter showToolbar("ShowToolbar", "Show toolbar by default.", 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"); +static BoolParameter autoReconnect("AutoReconnect", "Offer to reconnect to the remote server if the connection" + "is dropped because an error occurs.", true); + +static BoolParameter customCompressLevel("CustomCompressLevel", + "Use custom compression level. " + "Default if CompressLevel is specified.", false); + +static IntParameter compressLevel("CompressLevel", + "Use specified compression level" + "0 = Low, 9 = High", + 6); + +static BoolParameter noJpeg("NoJPEG", + "Disable lossy JPEG compression in Tight encoding.", + false); + +static IntParameter qualityLevel("QualityLevel", + "JPEG quality level. " + "0 = Low, 9 = High", + 6); + +CConnOptions::CConnOptions() +: useLocalCursor(::useLocalCursor), useDesktopResize(::useDesktopResize), +autoSelect(::autoSelect), fullColour(::fullColour), fullScreen(::fullScreen), +shared(::sharedConnection), sendPtrEvents(::sendPtrEvents), sendKeyEvents(::sendKeyEvents), +preferredEncoding(encodingZRLE), clientCutText(::clientCutText), serverCutText(::serverCutText), +disableWinKeys(::disableWinKeys), protocol3_3(::protocol3_3), acceptBell(::acceptBell), +lowColourLevel(::lowColourLevel), pointerEventInterval(ptrEventInterval), +emulate3(::emulate3), monitor(::monitor.getData()), showToolbar(::showToolbar), +customCompressLevel(::customCompressLevel), compressLevel(::compressLevel), +noJpeg(::noJpeg), qualityLevel(::qualityLevel), passwordFile(::passwordFile.getData()), +autoReconnect(::autoReconnect) +{ + if (autoSelect) { + preferredEncoding = encodingZRLE; + } else { + CharArray encodingName(::preferredEncoding.getData()); + preferredEncoding = encodingNum(encodingName.buf); + } + setMenuKey(CharArray(::menuKey.getData()).buf); + + if (!::autoSelect.hasBeenSet()) { + // Default to AutoSelect=0 if -PreferredEncoding or -FullColor is used + autoSelect = (!::preferredEncoding.hasBeenSet() + && !::fullColour.hasBeenSet() + && !::fullColourAlias.hasBeenSet()); + } + if (!::customCompressLevel.hasBeenSet()) { + // Default to CustomCompressLevel=1 if CompressLevel is used. + customCompressLevel = ::compressLevel.hasBeenSet(); + } +} + + +void CConnOptions::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], ']', §ion.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) { + ObfuscatedPasswd obfPwd; + rdr::HexInStream::hexStrToBin(value.buf, (char**)&obfPwd.buf, &obfPwd.length); + PlainPasswd passwd(obfPwd); + password.replaceBuf(passwd.takeBuf()); + } + } 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, "DisableWinKeys") == 0) { + disableWinKeys = atoi(value.buf); + } else if (stricmp(name.buf, "AcceptBell") == 0) { + acceptBell = atoi(value.buf); + } else if (stricmp(name.buf, "Emulate3") == 0) { + emulate3 = atoi(value.buf); + } else if (stricmp(name.buf, "ShowToolbar") == 0) { + showToolbar = 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); + } else if (stricmp(name.buf, "AutoReconnect") == 0) { + autoReconnect = atoi(value.buf); + + } else if (stricmp(name.buf, "CustomCompressLevel") == 0) { + customCompressLevel = atoi(value.buf); + } else if (stricmp(name.buf, "CompressLevel") == 0) { + compressLevel = atoi(value.buf); + } else if (stricmp(name.buf, "NoJPEG") == 0) { + noJpeg = atoi(value.buf); + } else if (stricmp(name.buf, "QualityLevel") == 0) { + qualityLevel = atoi(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); + } + } + + // If AutoSelect is enabled then override the preferred encoding + if (autoSelect) + preferredEncoding = encodingZRLE; + + setConfigFileName(filename); + } catch (rdr::Exception&) { + if (f) fclose(f); + throw; + } +} + +void CConnOptions::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) { + ObfuscatedPasswd obfPwd(password); + CharArray obfuscatedHex = rdr::HexOutStream::binToHexStr(obfPwd.buf, obfPwd.length); + 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, "DisableWinKeys=%d\n", (int)disableWinKeys); + fprintf(f, "AcceptBell=%d\n", (int)acceptBell); + fprintf(f, "Emulate3=%d\n", (int)emulate3); + fprintf(f, "ShowToolbar=%d\n", (int)showToolbar); + fprintf(f, "PointerEventInterval=%d\n", pointerEventInterval); + if (monitor.buf) + fprintf(f, "Monitor=%s\n", monitor.buf); + fprintf(f, "MenuKey=%s\n", CharArray(menuKeyName()).buf); + fprintf(f, "AutoReconnect=%d\n", (int)autoReconnect); + fprintf(f, "CustomCompressLevel=%d\n", customCompressLevel); + fprintf(f, "CompressLevel=%d\n", compressLevel); + fprintf(f, "NoJPEG=%d\n", noJpeg); + fprintf(f, "QualityLevel=%d\n", qualityLevel); + fclose(f); f=0; + + setConfigFileName(filename); + } catch (rdr::Exception&) { + if (f) fclose(f); + throw; + } +} + + +void CConnOptions::writeDefaults() { + RegKey key; + key.createKey(HKEY_CURRENT_USER, _T("Software\\TightVNC\\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("DisableWinKeys"), disableWinKeys); + key.setBool(_T("Protocol3.3"), protocol3_3); + key.setBool(_T("AcceptBell"), acceptBell); + key.setBool(_T("ShowToolbar"), showToolbar); + 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); + key.setBool(_T("AutoReconnect"), autoReconnect); + key.setInt(_T("CustomCompressLevel"), customCompressLevel); + key.setInt(_T("CompressLevel"), compressLevel); + key.setInt(_T("NoJPEG"), noJpeg); + key.setInt(_T("QualityLevel"), qualityLevel); +} + + +void CConnOptions::setUserName(const char* user) {userName.replaceBuf(strDup(user));} +void CConnOptions::setPassword(const char* pwd) {password.replaceBuf(strDup(pwd));} +void CConnOptions::setConfigFileName(const char* cfn) {configFileName.replaceBuf(strDup(cfn));} +void CConnOptions::setHost(const char* h) {host.replaceBuf(strDup(h));} +void CConnOptions::setMonitor(const char* m) {monitor.replaceBuf(strDup(m));} + +void CConnOptions::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* CConnOptions::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(); +} + + +CConnOptions& CConnOptions::operator=(const CConnOptions& 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; + disableWinKeys = o.disableWinKeys; + emulate3 = o.emulate3; + pointerEventInterval = o.pointerEventInterval; + protocol3_3 = o.protocol3_3; + acceptBell = o.acceptBell; + showToolbar = o.showToolbar; + setUserName(o.userName.buf); + setPassword(o.password.buf); + setConfigFileName(o.configFileName.buf); + setHost(o.host.buf); + setMonitor(o.monitor.buf); + menuKey = o.menuKey; + autoReconnect = o.autoReconnect; + customCompressLevel = o.customCompressLevel; + compressLevel = o.compressLevel; + noJpeg = o.noJpeg; + qualityLevel = o.qualityLevel; + + return *this; +} diff --git a/win/vncviewer/CConnOptions.h b/win/vncviewer/CConnOptions.h new file mode 100644 index 00000000..59fd0a3c --- /dev/null +++ b/win/vncviewer/CConnOptions.h @@ -0,0 +1,97 @@ +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +// -=- CConnOptions.h + +// Definition of the CConnOptions class, responsible for storing the +// current & requested VNC Viewer options. + +#ifndef __RFB_WIN32_CCONN_OPTIONS_H__ +#define __RFB_WIN32_CCONN_OPTIONS_H__ + +#include <rfb/Password.h> + +namespace rfb { + + namespace win32 { + + // + // -=- Options structure. Each viewer option has a corresponding + // entry in CConnOptions. The viewer options are set by calling + // CConn::applyOptions(...) + // The CConnOptions 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 CConnOptions { + public: + CConnOptions(); + CConnOptions(const CConnOptions& o) {operator=(o);} + CConnOptions& operator=(const CConnOptions& 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 showToolbar; + bool clientCutText; + bool serverCutText; + bool disableWinKeys; + bool emulate3; + int pointerEventInterval; + bool protocol3_3; + bool acceptBell; + CharArray userName; + void setUserName(const char* user); + PlainPasswd 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(); + bool autoReconnect; + + bool customCompressLevel; + int compressLevel; + bool noJpeg; + int qualityLevel; + + CharArray passwordFile; + }; + + + }; + +}; + +#endif diff --git a/win/vncviewer/CConnThread.cxx b/win/vncviewer/CConnThread.cxx new file mode 100644 index 00000000..cfd26952 --- /dev/null +++ b/win/vncviewer/CConnThread.cxx @@ -0,0 +1,198 @@ +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +// -=- CConnThread.cxx + +// A CConnThread instance is created for each new connection. +// The CConnThread creates the corresponding CConn instance +// and manages it. + +#include <stdlib.h> +#include <rfb/LogWriter.h> +#include <rfb/Hostname.h> +#include <rfb_win32/MsgBox.h> +#include <network/TcpSocket.h> +#include <vncviewer/CConnThread.h> +#include <vncviewer/CConn.h> +#include <vncviewer/ConnectionDialog.h> +#include <vncviewer/ConnectingDialog.h> +#include <vncviewer/UserPasswdDialog.h> +#include <set> + +using namespace rfb; +using namespace win32; + +static LogWriter vlog("CConnThread"); + +static std::set<CConnThread*> threads; +static Mutex threadsLock; +static Handle noMoreThreads(CreateEvent(0, TRUE, FALSE, 0)); + + +CConnThread::CConnThread() : Thread("CConnThread"), isConfig(false), + sock(0), reverse(false) { + vlog.info("CConnThread (dialog)"); + setDeleteAfterRun(); + Lock l(threadsLock); + threads.insert(this); + start(); +} + +CConnThread::CConnThread(const char* hostOrConfig_, bool isConfig_) + : Thread("CConnThread"), hostOrConfig(strDup(hostOrConfig_)), + isConfig(isConfig_), sock(0), reverse(false) { + vlog.info("CConnThread (host/port)"); + setDeleteAfterRun(); + Lock l(threadsLock); + threads.insert(this); + start(); +} + +CConnThread::CConnThread(network::Socket* sock_, bool reverse_) + : Thread("CConnThread"), isConfig(false), sock(sock_), reverse(reverse_) { + vlog.info("CConnThread (reverse connection)"); + setDeleteAfterRun(); + Lock l(threadsLock); + threads.insert(this); + start(); +} + +CConnThread::~CConnThread() { + Lock l(threadsLock); + threads.erase(this); + if (threads.empty()) + SetEvent(noMoreThreads); + delete sock; +} + + +void CConnThread::run() { + CConnOptions options; + bool reconnect; + + do { + { + CConn conn; + reconnect = false; + + // If there is no socket object then set the host & port info + if (!sock && !options.host.buf) { + try { + if (isConfig) { + // A configuration file name was specified - load it + CharArray filename = hostOrConfig.takeBuf(); + options.readFromFile(filename.buf); + } else { + // An actual hostname (and possibly port) was specified + options.host.replaceBuf(hostOrConfig.takeBuf()); + } + + if (!options.host.buf) { + // No host was specified - prompt for one + ConnectionDialog connDlg(&conn); + if (!connDlg.showDialog()) + return; + options = conn.getOptions(); + options.setHost(CStr(connDlg.hostname.buf)); + } + } catch (rdr::Exception& e) { + MsgBox(0, TStr(e.str()), MB_ICONERROR | MB_OK); + return; + } + } + + // Apply the connection options to the CConn + conn.applyOptions(options); + + if (!sock) { + // There is no existing connection - better make one + const char* hostAndPort = conn.getOptions().host.buf; + + try { + ConnectingDialog dlg; + sock = dlg.connect(hostAndPort); + + // If the connection was cancelled by the user, just quit + if (!sock) + return; + } catch(rdr::Exception& e) { + MsgBox(NULL, TStr(e.str()), MB_ICONERROR | MB_OK); + return; + } + + // Try to add the caller to the MRU + MRU::addToMRU(hostAndPort); + } + + // Run the RFB protocol over the connected socket + conn.initialise(sock, reverse); + while (!conn.isClosed()) { + try { + conn.getInStream()->check(1,1); + conn.processMsg(); + } catch (rdr::EndOfStream) { + if (conn.state() == CConnection::RFBSTATE_NORMAL) + conn.close(); + else + conn.close("The connection closed unexpectedly"); + } catch (rfb::AuthCancelledException) { + conn.close(); + } catch (rfb::AuthFailureException& e) { + // Clear the password, in case we auto-reconnect + options = conn.getOptions(); + options.password.replaceBuf(0); + conn.applyOptions(options); + conn.close(e.str()); + } catch (rdr::Exception& e) { + conn.close(e.str()); + } + } + + // If there is a cause for closing the connection logged then display it + if (conn.closeReason()) { + reconnect = !reverse && conn.getOptions().autoReconnect; + if (!reconnect) { + MsgBox(0, TStr(conn.closeReason()), MB_ICONINFORMATION | MB_OK); + } else { + options = conn.getOptions(); + const char* format = "%s\nDo you wish to attempt to reconnect to %s?"; + CharArray message(strlen(conn.closeReason()) + strlen(format) + + strlen(conn.getOptions().host.buf)); + sprintf(message.buf, format, conn.closeReason(), conn.getOptions().host.buf); + if (MsgBox(0, TStr(message.buf), MB_ICONQUESTION | MB_YESNO | MB_DEFBUTTON2) != IDYES) + reconnect = false; + } + } + } // Exit the CConn's scope, implicitly destroying it & making it safe to delete the TcpSocket + + // Clean up the old socket, if any + delete sock; sock = 0; + } while (reconnect); +} + + +BOOL CConnThread::getMessage(MSG* msg, HWND hwnd, UINT minMsg, UINT maxMsg) { + while (!PeekMessage(msg, hwnd, minMsg, maxMsg, PM_REMOVE)) { + DWORD result = MsgWaitForMultipleObjects(1, &noMoreThreads.h, FALSE, INFINITE, QS_ALLINPUT); + if (result == WAIT_OBJECT_0) + return FALSE; + else if (result == WAIT_FAILED) + throw rdr::SystemException("CConnThread::getMessage wait failed", GetLastError()); + } + return msg->message != WM_QUIT; +} diff --git a/win/vncviewer/CConnThread.h b/win/vncviewer/CConnThread.h new file mode 100644 index 00000000..7a8451c1 --- /dev/null +++ b/win/vncviewer/CConnThread.h @@ -0,0 +1,57 @@ +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +// -=- CConnThread.h + +// CConn-managing Thread implementation. + +#ifndef __RFB_WIN32_CCONN_THREAD_H__ +#define __RFB_WIN32_CCONN_THREAD_H__ + +#include <network/Socket.h> +#include <rfb/Threading.h> +#include <rfb/util.h> + +namespace rfb { + + namespace win32 { + + class CConnThread : public Thread { + public: + CConnThread(); + CConnThread(const char* hostOrConfig, bool isConfig=false); + CConnThread(network::Socket* sock, bool reverse=false); + ~CConnThread(); + + void run(); + + // Special getMessage call that returns FALSE if message is WM_QUIT, + // OR if there are no more CConnThreads running. + static BOOL getMessage(MSG* msg, HWND hwnd, UINT minMsg, UINT maxMsg); + protected: + CharArray hostOrConfig; + bool isConfig; + network::Socket* sock; + bool reverse; + }; + + }; + +}; + +#endif // __RFB_WIN32_CCONN_THREAD_H__ diff --git a/win/vncviewer/ConnectingDialog.cxx b/win/vncviewer/ConnectingDialog.cxx new file mode 100644 index 00000000..60fcb661 --- /dev/null +++ b/win/vncviewer/ConnectingDialog.cxx @@ -0,0 +1,160 @@ +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +// -=- ConnectingDialog.cxx + +#include <stdlib.h> +#include <vncviewer/ConnectingDialog.h> +#include <vncviewer/resource.h> +#include <network/TcpSocket.h> +#include <rfb/Threading.h> +#include <rfb/Hostname.h> +#include <map> + +using namespace rfb; +using namespace rfb::win32; + + +// ConnectingDialog callback +static BOOL CALLBACK ConnectingDlgProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam ) { + bool* activePtr = (bool*)GetWindowLong(hwnd, GWL_USERDATA); + switch (uMsg) { + case WM_INITDIALOG: + SetWindowLong(hwnd, GWL_USERDATA, lParam); + return TRUE; + case WM_COMMAND: + switch (LOWORD(wParam)) { + case IDCANCEL: + if (activePtr) + *activePtr = false; + return TRUE; + } + break; + case WM_DESTROY: + if (activePtr) + *activePtr = false; + return TRUE; + } + return 0; +} + + +// Global map, used by ConnectingDialog::Threads to call back to their owning +// ConnectingDialogs, while coping with the fact that the owner may already have quit. +static std::map<int, ConnectingDialog*> dialogs; +static int nextDialogId = 0; +static Mutex dialogsLock; + + +// ConnectingDialog::Thread +// Attempts to connect to the specified host. If the connection succeeds, the +// socket is saved in the owning ConnectingDialog, if still available, and the +// event is signalled. If the connection fails, the Exception text is returned +// to the dialog. If the dialog is already gone, the Exception/socket are discarded. +// NB: This thread class cleans itself up on exit - DO NOT join()! +class ConnectingDialog::Thread : public rfb::Thread { +public: + Thread(int dialogId_, const char* hostAndPort) : dialogId(dialogId_) { + setDeleteAfterRun(); + getHostAndPort(hostAndPort, &host.buf, &port); + } + virtual void run() { + try { + returnSock(new network::TcpSocket(host.buf, port)); + } catch (rdr::Exception& e) { + returnException(e); + } + } + void returnSock(network::Socket* s) { + Lock l(dialogsLock); + if (dialogs.count(dialogId)) { + dialogs[dialogId]->newSocket = s; + SetEvent(dialogs[dialogId]->readyEvent); + } else { + delete s; + } + } + void returnException(const rdr::Exception& e) { + Lock l(dialogsLock); + if (dialogs.count(dialogId)) { + dialogs[dialogId]->errMsg.replaceBuf(strDup(e.str())); + SetEvent(dialogs[dialogId]->readyEvent); + } + }; + CharArray host; + int port; + int dialogId; +}; + + +ConnectingDialog::ConnectingDialog() : dialog(0), readyEvent(CreateEvent(0, TRUE, FALSE, 0)), + newSocket(0), dialogId(0) { +} + +network::Socket* ConnectingDialog::connect(const char* hostAndPort) { + Thread* connectThread = 0; + bool active = true; + errMsg.replaceBuf(0); + newSocket = 0; + + // Get a unique dialog identifier and create the dialog window + { + Lock l(dialogsLock); + dialogId = ++nextDialogId; + dialogs[dialogId] = this; + dialog = CreateDialogParam(GetModuleHandle(0), + MAKEINTRESOURCE(IDD_CONNECTING_DLG), 0, &ConnectingDlgProc, (long)&active); + ShowWindow(dialog, SW_SHOW); + ResetEvent(readyEvent); + } + + // Create and start the connection thread + try { + connectThread = new Thread(dialogId, hostAndPort); + connectThread->start(); + } catch (rdr::Exception& e) { + errMsg.replaceBuf(strDup(e.str())); + active = false; + } + + // Process window messages until the connection thread signals readyEvent, or the dialog is cancelled + while (active && (MsgWaitForMultipleObjects(1, &readyEvent.h, FALSE, INFINITE, QS_ALLINPUT) == WAIT_OBJECT_0 + 1)) { + MSG msg; + while (PeekMessage(&msg, 0, 0, 0, PM_REMOVE)) + DispatchMessage(&msg); + } + + // Remove this dialog from the table + // NB: If the dialog was cancelled then the thread is still running, and will only + // discover that we're gone when it looks up our unique Id in the dialog table. + { + Lock l(dialogsLock); + dialogs.erase(dialogId); + } + + // Close the dialog window + DestroyWindow(dialog); dialog=0; + + // Throw the exception, if there was one + if (errMsg.buf) + throw rdr::Exception(errMsg.buf); + + // Otherwise, return the socket + // NB: The socket will be null if the dialog was cancelled + return newSocket; +} diff --git a/win/vncviewer/ConnectingDialog.h b/win/vncviewer/ConnectingDialog.h new file mode 100644 index 00000000..c38b3a1b --- /dev/null +++ b/win/vncviewer/ConnectingDialog.h @@ -0,0 +1,65 @@ +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +// -=- ConnectingDialog.h + +// ConnectingDialog instances are used to display a status dialog while a +// connection attempt is in progress. The connection attempt is performed +// in a background thread by the ConnectingDialog, to allow the status dialog +// to remain interactive. If the dialog is cancelled then it will close and +// the connection dialog will eventually tidy itself up. + +#ifndef __RFB_WIN32_CONNECTING_DLG_H__ +#define __RFB_WIN32_CONNECTING_DLG_H__ + +#include <windows.h> +#include <network/Socket.h> +#include <rfb/util.h> +#include <rfb_win32/Handle.h> + +namespace rfb { + + namespace win32 { + + class ConnectingDialog { + public: + ConnectingDialog(); + + // connect + // Show a Connecting dialog and attempt to connect to the specified host + // in the background. + // If the connection succeeds then the Socket is returned. + // If an error occurs, an Exception is thrown. + // If the dialog is cancelled then null is returned. + network::Socket* connect(const char* hostAndPort); + protected: + HWND dialog; + network::Socket* newSocket; + CharArray errMsg; + Handle readyEvent; + int dialogId; + + class Thread; + friend class Thread; + }; + + }; + +}; + +#endif diff --git a/win/vncviewer/ConnectionDialog.cxx b/win/vncviewer/ConnectionDialog.cxx new file mode 100644 index 00000000..e7c6b0a5 --- /dev/null +++ b/win/vncviewer/ConnectionDialog.cxx @@ -0,0 +1,79 @@ +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +#include <vncviewer/ConnectionDialog.h> +#include <vncviewer/CConn.h> +#include <vncviewer/resource.h> +#include <rfb_win32/AboutDialog.h> + +#include <tchar.h> + +using namespace rfb; +using namespace rfb::win32; + + +ConnectionDialog::ConnectionDialog(CConn* conn_) : Dialog(GetModuleHandle(0)), conn(conn_) { +} + + +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); + + // Fill out the Security: drop-down and select the preferred option + HWND security = GetDlgItem(handle, IDC_SECURITY_LEVEL); + LRESULT n = SendMessage(security, CB_ADDSTRING, 0, (LPARAM)_T("Always Off")); + if (n != CB_ERR) + SendMessage(security, CB_SETCURSEL, n, 0); + enableItem(IDC_SECURITY_LEVEL, false); +} + + +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: + conn->showOptionsDialog(); + return true; + }; + return false; +} diff --git a/win/vncviewer/ConnectionDialog.h b/win/vncviewer/ConnectionDialog.h new file mode 100644 index 00000000..f739280c --- /dev/null +++ b/win/vncviewer/ConnectionDialog.h @@ -0,0 +1,52 @@ +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +// -=- 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 CConn; + + class ConnectionDialog : Dialog { + public: + ConnectionDialog(CConn* view); + virtual bool showDialog(); + virtual void initDialog(); + virtual bool onOk(); + virtual bool onCommand(int id, int cmd); + TCharArray hostname; + protected: + CConn* conn; + }; + + }; + +}; + +#endif diff --git a/win/vncviewer/DesktopWindow.cxx b/win/vncviewer/DesktopWindow.cxx new file mode 100644 index 00000000..27ef2dc2 --- /dev/null +++ b/win/vncviewer/DesktopWindow.cxx @@ -0,0 +1,1103 @@ +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +#include <windows.h> +#include <commctrl.h> +#include <rfb/Configuration.h> +#include <rfb/LogWriter.h> +#include <rfb_win32/WMShatter.h> +#include <rfb_win32/LowLevelKeyEvents.h> +#include <rfb_win32/MonitorInfo.h> +#include <rfb_win32/DeviceContext.h> +#include <rfb_win32/Win32Util.h> +#include <vncviewer/DesktopWindow.h> +#include <vncviewer/resource.h> + +using namespace rfb; +using namespace rfb::win32; + + +// - Statics & consts + +static LogWriter vlog("DesktopWindow"); + +const int TIMER_BUMPSCROLL = 1; +const int TIMER_POINTER_INTERVAL = 2; +const int TIMER_POINTER_3BUTTON = 3; + + +// +// -=- DesktopWindowClass + +// +// Window class used as the basis for all DesktopWindow instances +// + +class DesktopWindowClass { +public: + DesktopWindowClass(); + ~DesktopWindowClass(); + ATOM classAtom; + HINSTANCE instance; +}; + +LRESULT CALLBACK DesktopWindowProc(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); + DesktopWindow* _this = (DesktopWindow*) 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; +}; + +static HCURSOR dotCursor = (HCURSOR)LoadImage(GetModuleHandle(0), MAKEINTRESOURCE(IDC_DOT_CURSOR), IMAGE_CURSOR, 0, 0, LR_SHARED); +static HCURSOR arrowCursor = (HCURSOR)LoadImage(NULL, IDC_ARROW, IMAGE_CURSOR, 0, 0, LR_SHARED); + +DesktopWindowClass::DesktopWindowClass() : classAtom(0) { + WNDCLASS wndClass; + wndClass.style = 0; + wndClass.lpfnWndProc = DesktopWindowProc; + wndClass.cbClsExtra = 0; + wndClass.cbWndExtra = 0; + wndClass.hInstance = instance = GetModuleHandle(0); + wndClass.hIcon = (HICON)LoadImage(GetModuleHandle(0), MAKEINTRESOURCE(IDI_ICON), IMAGE_ICON, 0, 0, LR_SHARED); + if (!wndClass.hIcon) + printf("unable to load icon:%ld", GetLastError()); + wndClass.hCursor = LoadCursor(NULL, IDC_ARROW); + wndClass.hbrBackground = NULL; + wndClass.lpszMenuName = 0; + wndClass.lpszClassName = _T("rfb::win32::DesktopWindowClass"); + classAtom = RegisterClass(&wndClass); + if (!classAtom) { + throw rdr::SystemException("unable to register DesktopWindow window class", GetLastError()); + } +} + +DesktopWindowClass::~DesktopWindowClass() { + if (classAtom) { + UnregisterClass((const TCHAR*)classAtom, instance); + } +} + +DesktopWindowClass baseClass; + +// +// -=- FrameClass + +// +// Window class used for child windows that display pixel data +// + +class FrameClass { +public: + FrameClass(); + ~FrameClass(); + ATOM classAtom; + HINSTANCE instance; +}; + +LRESULT CALLBACK FrameProc(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); + DesktopWindow* _this = (DesktopWindow*) 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->processFrameMessage(msg, wParam, lParam); + } catch (rdr::Exception& e) { + vlog.error("untrapped: %s", e.str()); + } + + return result; +} + +FrameClass::FrameClass() : classAtom(0) { + WNDCLASS wndClass; + wndClass.style = 0; + wndClass.lpfnWndProc = FrameProc; + wndClass.cbClsExtra = 0; + wndClass.cbWndExtra = 0; + wndClass.hInstance = instance = GetModuleHandle(0); + wndClass.hIcon = 0; + wndClass.hCursor = NULL; + wndClass.hbrBackground = NULL; + wndClass.lpszMenuName = 0; + wndClass.lpszClassName = _T("rfb::win32::FrameClass"); + classAtom = RegisterClass(&wndClass); + if (!classAtom) { + throw rdr::SystemException("unable to register Frame window class", GetLastError()); + } +} + +FrameClass::~FrameClass() { + if (classAtom) { + UnregisterClass((const TCHAR*)classAtom, instance); + } +} + +FrameClass frameClass; + + +// +// -=- DesktopWindow instance implementation +// + +DesktopWindow::DesktopWindow(Callback* cb) + : buffer(0), + showToolbar(false), + client_size(0, 0, 16, 16), window_size(0, 0, 32, 32), + cursorVisible(false), cursorAvailable(false), cursorInBuffer(false), + systemCursorVisible(true), trackingMouseLeave(false), + handle(0), frameHandle(0), has_focus(false), palette_changed(false), + fullscreenActive(false), fullscreenRestore(false), + bumpScroll(false), callback(cb) { + + // Create the window + const char* name = "DesktopWindow"; + handle = CreateWindow((const TCHAR*)baseClass.classAtom, TStr(name), + WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN, + 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)", name, handle); + + // Create the toolbar + tb.create(handle); + vlog.debug("created toolbar window \"%s\" (%x)", "ViewerToolBar", tb.getHandle()); + + // Create the frame window + frameHandle = CreateWindowEx(WS_EX_CLIENTEDGE, (const TCHAR*)frameClass.classAtom, + 0, WS_CHILD | WS_CLIPSIBLINGS | WS_VISIBLE, CW_USEDEFAULT, CW_USEDEFAULT, + CW_USEDEFAULT, CW_USEDEFAULT, handle, 0, frameClass.instance, this); + if (!frameHandle) { + throw rdr::SystemException("unable to create rfb frame window instance", GetLastError()); + } + vlog.debug("created window \"%s\" (%x)", "Frame Window", frameHandle); + + // Initialise the CPointer pointer handler + ptr.setHWND(frameHandle); + ptr.setIntervalTimerId(TIMER_POINTER_INTERVAL); + ptr.set3ButtonTimerId(TIMER_POINTER_3BUTTON); + + // Initialise the bumpscroll timer + bumpScrollTimer.setHWND(handle); + bumpScrollTimer.setId(TIMER_BUMPSCROLL); + + // Hook the clipboard + clipboard.setNotifier(this); + + // Create the backing buffer + buffer = new win32::ScaledDIBSectionBuffer(frameHandle); + + // Show the window + centerWindow(handle, 0); + ShowWindow(handle, SW_SHOW); +} + +DesktopWindow::~DesktopWindow() { + vlog.debug("~DesktopWindow"); + showSystemCursor(); + if (handle) { + disableLowLevelKeyEvents(handle); + DestroyWindow(handle); + handle = 0; + } + delete buffer; + vlog.debug("~DesktopWindow done"); +} + + +void DesktopWindow::setFullscreen(bool fs) { + if (fs && !fullscreenActive) { + fullscreenActive = bumpScroll = true; + + // Un-minimize the window if required + if (GetWindowLong(handle, GWL_STYLE) & WS_MINIMIZE) + ShowWindow(handle, SW_RESTORE); + + // Save the current window position + GetWindowRect(handle, &fullscreenOldRect); + + // Find the size of the display the window is on + MonitorInfo mi(handle); + + // Hide the toolbar + if (tb.isVisible()) + tb.hide(); + SetWindowLong(frameHandle, GWL_EXSTYLE, 0); + + // Set the window full-screen + DWORD flags = GetWindowLong(handle, GWL_STYLE); + fullscreenOldFlags = flags; + flags = flags & ~(WS_CAPTION | WS_THICKFRAME | WS_MAXIMIZE | WS_MINIMIZE); + vlog.debug("flags=%x", flags); + + SetWindowLong(handle, GWL_STYLE, flags); + SetWindowPos(handle, 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; + + // Show the toolbar + if (showToolbar) + tb.show(); + SetWindowLong(frameHandle, GWL_EXSTYLE, WS_EX_CLIENTEDGE); + + // Set the window non-fullscreen + SetWindowLong(handle, GWL_STYLE, fullscreenOldFlags); + + // Set the window position + SetWindowPos(handle, HWND_NOTOPMOST, + fullscreenOldRect.left, fullscreenOldRect.top, + fullscreenOldRect.right - fullscreenOldRect.left, + fullscreenOldRect.bottom - fullscreenOldRect.top, + SWP_FRAMECHANGED); + } + + // Adjust the viewport offset to cope with change in size between FS + // and previous window state. + setViewportOffset(scrolloffset); +} + +void DesktopWindow::setShowToolbar(bool st) +{ + showToolbar = st; + + if (showToolbar && !tb.isVisible() && !fullscreenActive) { + tb.show(); + } else if (!showToolbar && tb.isVisible()) { + tb.hide(); + } +} + +void DesktopWindow::setDisableWinKeys(bool dwk) { + // Enable low-level event hooking, so we get special keys directly + if (dwk) + enableLowLevelKeyEvents(handle); + else + disableLowLevelKeyEvents(handle); +} + + +void DesktopWindow::setMonitor(const char* monitor) { + MonitorInfo mi(monitor); + mi.moveTo(handle); +} + +char* DesktopWindow::getMonitor() const { + MonitorInfo mi(handle); + return strDup(mi.szDevice); +} + + +bool DesktopWindow::setViewportOffset(const Point& tl) { + 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(frameHandle, -delta.x, -delta.y, 0, 0, 0, 0, SW_INVALIDATE); + UpdateWindow(frameHandle); + return true; + } + return false; +} + + +bool DesktopWindow::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 +DesktopWindow::processMessage(UINT msg, WPARAM wParam, LPARAM lParam) { + switch (msg) { + + // -=- Process standard window messages + + case WM_NOTIFY: + if (wParam == ID_TOOLBAR) + tb.processWM_NOTIFY(wParam, lParam); + break; + + case WM_DISPLAYCHANGE: + // Display format has changed - notify callback + callback->displayChanged(); + break; + + // -=- 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(frameHandle, GWL_STYLE); + DWORD style = current_style & ~(WS_VSCROLL | WS_HSCROLL); + DWORD style_ex = GetWindowLong(frameHandle, GWL_EXSTYLE); + + RECT r; + SetRect(&r, 0, 0, buffer->width(), buffer->height()); + AdjustWindowRectEx(&r, style, FALSE, style_ex); + 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); + + SetRect(&r, reqd_size.tl.x, reqd_size.tl.y, reqd_size.br.x, reqd_size.br.y); + if (tb.isVisible()) + r.bottom += tb.getHeight(); + AdjustWindowRect(&r, GetWindowLong(handle, GWL_STYLE), FALSE); + reqd_size = Rect(r.left, r.top, r.right, r.bottom); + + RECT current; + GetWindowRect(handle, ¤t); + + if (!(GetWindowLong(handle, GWL_STYLE) & WS_MAXIMIZE) && !fullscreenActive) { + // Ensure that the window isn't resized too large + 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; + + // Resize child windows and update window size info we have cached. + + case WM_SIZE: + { + Point old_offset = desktopToClient(Point(0, 0)); + RECT r; + + // Resize child windows + GetClientRect(handle, &r); + if (tb.isVisible()) { + MoveWindow(frameHandle, 0, tb.getHeight(), + r.right, r.bottom - tb.getHeight(), TRUE); + } else { + MoveWindow(frameHandle, 0, 0, r.right, r.bottom, TRUE); + } + tb.autoSize(); + + // Update the cached sizing information + GetWindowRect(frameHandle, &r); + window_size = Rect(r.left, r.top, r.right, r.bottom); + GetClientRect(frameHandle, &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(desktopToClient(Point(0, 0))))) + InvalidateRect(frameHandle, 0, 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: + ptr.handleTimer(callback, wParam); + break; + } + 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 keyboard to a consistent state + kbd.releaseAllKeys(callback); + break; + + // -=- If the menu is about to be shown, make sure it's up to date + + case WM_INITMENU: + callback->refreshMenu(true); + break; + + // -=- Handle the extra window menu items + + // Pass system menu messages to the callback and only attempt + // to process them ourselves if the callback returns false. + case WM_SYSCOMMAND: + // Call the supplied callback + if (callback->sysCommand(wParam, lParam)) + break; + + // - Not processed by the callback, 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: + { + if (GetWindowLong(handle, GWL_STYLE) & WS_MINIMIZE) { + rfb::win32::SafeDefWindowProc(handle, msg, wParam, lParam); + setFullscreen(fullscreenRestore); + } + else if (fullscreenActive) + setFullscreen(false); + else + rfb::win32::SafeDefWindowProc(handle, msg, wParam, lParam); + + return 0; + } + + // If we are maximized or minimized then that cancels full-screen mode. + case SC_MINIMIZE: + case SC_MAXIMIZE: + fullscreenRestore = fullscreenActive; + setFullscreen(false); + break; + + } + break; + + // Treat all menu commands as system menu commands + case WM_COMMAND: + SendMessage(handle, WM_SYSCOMMAND, wParam, lParam); + return 0; + + // -=- Handle keyboard input + + case WM_KEYUP: + case WM_KEYDOWN: + // Hook the MenuKey to pop-up the window menu + if (menuKey && (wParam == 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 + // + // NOTE: Here we call refreshMenu only to grey out Move and Size + // menu items. Other things will be refreshed once again + // while processing the WM_INITMENU message. + // + callback->refreshMenu(false); + + // Show it under the pointer + POINT pt; + GetCursorPos(&pt); + cursorInBuffer = false; + TrackPopupMenu(GetSystemMenu(handle, FALSE), + TPM_CENTERALIGN | TPM_VCENTERALIGN, pt.x, pt.y, 0, handle, 0); + } + + // Ignore the MenuKey keypress for both press & release events + return 0; + } + } + case WM_SYSKEYDOWN: + case WM_SYSKEYUP: + kbd.keyEvent(callback, wParam, lParam, (msg == WM_KEYDOWN) || (msg == WM_SYSKEYDOWN)); + return 0; + + // -=- Handle the window closing + + case WM_CLOSE: + vlog.debug("WM_CLOSE %x", handle); + callback->closeWindow(); + break; + + } + + return rfb::win32::SafeDefWindowProc(handle, msg, wParam, lParam); +} + +LRESULT +DesktopWindow::processFrameMessage(UINT msg, WPARAM wParam, LPARAM lParam) { + switch (msg) { + + // -=- Paint the remote frame buffer + + case WM_PAINT: + { + PAINTSTRUCT ps; + HDC paintDC = BeginPaint(frameHandle, &ps); + if (!paintDC) + throw rdr::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 = desktopToClient(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 = clientToDesktop(pr.tl); + + if (!BitBlt(paintDC, pr.tl.x, pr.tl.y, pr.width(), pr.height(), + bitmapDC, buf_pos.x, buf_pos.y, SRCCOPY)) + throw rdr::SystemException("unable to BitBlt to window", GetLastError()); + } + } + + EndPaint(frameHandle, &ps); + + // - Notify the callback that a paint message has finished processing + callback->paintCompleted(); + } + return 0; + + // -=- Palette management + + case WM_PALETTECHANGED: + vlog.debug("WM_PALETTECHANGED"); + if ((HWND)wParam == frameHandle) { + vlog.debug("ignoring"); + break; + } + case WM_QUERYNEWPALETTE: + vlog.debug("re-selecting palette"); + { + WindowDC wdc(frameHandle); + PaletteSelector pSel(wdc, windowPalette.getHandle()); + if (pSel.isRedrawRequired()) { + InvalidateRect(frameHandle, 0, FALSE); + UpdateWindow(frameHandle); + } + } + return TRUE; + + 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(frameHandle, (msg == WM_VSCROLL) ? SB_VERT : SB_HORZ, &si, TRUE); + } + 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: +#ifdef WM_MOUSEWHEEL + case WM_MOUSEWHEEL: +#endif + if (has_focus) + { + if (!trackingMouseLeave) { + TRACKMOUSEEVENT tme; + tme.cbSize = sizeof(TRACKMOUSEEVENT); + tme.dwFlags = TME_LEAVE; + tme.hwndTrack = frameHandle; + _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; + +#ifdef WM_MOUSEWHEEL + 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++) { + ptr.pointerEvent(callback, oldpos, mask | wheelMask); + ptr.pointerEvent(callback, oldpos, mask); + } + } else { +#endif + Point clientPos = Point(LOWORD(lParam), HIWORD(lParam)); + Point p = clientToDesktop(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 + oldpos = p; + if (buffer->isScaling()) { + p.x /= double(buffer->getScale()) / 100.0; + p.y /= double(buffer->getScale()) / 100.0; + } + ptr.pointerEvent(callback, p, mask); +#ifdef WM_MOUSEWHEEL + } +#endif + } else { + cursorOutsideBuffer(); + } + break; + } + + return rfb::win32::SafeDefWindowProc(frameHandle, msg, wParam, lParam); +} + + +void +DesktopWindow::hideLocalCursor() { + // - Blit the cursor backing store over the cursor + // *** ALWAYS call this BEFORE changing buffer PF!!! + if (cursorVisible) { + cursorVisible = false; + buffer->DIBSectionBuffer::imageRect(cursorBackingRect, cursorBacking.data); + invalidateDesktopRect(cursorBackingRect, false); + } +} + +void +DesktopWindow::showLocalCursor() { + if (cursorAvailable && !cursorVisible && cursorInBuffer) { + if (!buffer->getPF().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(); + + invalidateDesktopRect(cursorBackingRect, false); + } +} + +void DesktopWindow::cursorOutsideBuffer() +{ + cursorInBuffer = false; + hideLocalCursor(); + showSystemCursor(); +} + +void +DesktopWindow::renderLocalCursor() +{ + Rect r = cursor.getRect(); + r = r.translate(cursorPos).translate(cursor.hotspot.negate()); + buffer->DIBSectionBuffer::maskRect(r, cursor.data, cursor.mask.buf); +} + +void +DesktopWindow::hideSystemCursor() { + if (systemCursorVisible) { + vlog.debug("hide system cursor"); + systemCursorVisible = false; + ShowCursor(FALSE); + } +} + +void +DesktopWindow::showSystemCursor() { + if (!systemCursorVisible) { + vlog.debug("show system cursor"); + systemCursorVisible = true; + ShowCursor(TRUE); + } +} + + +bool +DesktopWindow::invalidateDesktopRect(const Rect& crect, bool scaling) { + Rect rect; + if (buffer->isScaling() && scaling) { + rect = desktopToClient(buffer->calculateScaleBoundary(crect)); + } else rect = desktopToClient(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(frameHandle, &invalid, FALSE); + return true; +} + + +void +DesktopWindow::notifyClipboardChanged(const char* text, int len) { + callback->clientCutText(text, len); +} + + +void +DesktopWindow::setPF(const PixelFormat& pf) { + // If the cursor is the wrong format then clear it + if (!pf.equal(buffer->getPF())) + setCursor(0, 0, Point(), 0, 0); + + // Update the desktop buffer + buffer->setPF(pf); + + // Redraw the window + InvalidateRect(frameHandle, 0, FALSE); +} + +void +DesktopWindow::setSize(int w, int h) { + vlog.debug("setSize %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(handle, GWL_STYLE) & WS_MAXIMIZE) && !fullscreenActive) { + // Resize the window to the required size + RECT r = {0, 0, w, h}; + AdjustWindowRectEx(&r, GetWindowLong(frameHandle, GWL_STYLE), FALSE, + GetWindowLong(frameHandle, GWL_EXSTYLE)); + if (tb.isVisible()) + r.bottom += tb.getHeight(); + AdjustWindowRect(&r, GetWindowLong(handle, GWL_STYLE), FALSE); + + // Resize about the center of the window, and clip to current monitor + MonitorInfo mi(handle); + resizeWindow(handle, r.right-r.left, r.bottom-r.top); + mi.clipTo(handle); + } else { + // Ensure the screen contents are consistent + InvalidateRect(frameHandle, 0, FALSE); + } + + // Enable/disable scrollbars as appropriate + calculateScrollBars(); +} + +void +DesktopWindow::setCursor(int w, int h, const Point& hotspot, void* data, void* mask) { + hideLocalCursor(); + + cursor.hotspot = hotspot; + + cursor.setSize(w, h); + cursor.setPF(buffer->getPF()); + cursor.imageRect(cursor.getRect(), data); + memcpy(cursor.mask.buf, mask, cursor.maskLen()); + cursor.crop(); + + cursorBacking.setSize(w, h); + cursorBacking.setPF(buffer->getPF()); + + cursorAvailable = true; + + showLocalCursor(); +} + +PixelFormat +DesktopWindow::getNativePF() const { + vlog.debug("getNativePF()"); + return WindowDC(handle).getPF(); +} + + +void +DesktopWindow::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 + palette_changed = true; + InvalidateRect(handle, 0, FALSE); +} + + +void DesktopWindow::calculateScrollBars() { + // Calculate the required size of window + DWORD current_style = GetWindowLong(frameHandle, GWL_STYLE); + DWORD style = current_style & ~(WS_VSCROLL | WS_HSCROLL); + DWORD style_ex = GetWindowLong(frameHandle, GWL_EXSTYLE); + DWORD old_style; + RECT r; + SetRect(&r, 0, 0, buffer->width(), buffer->height()); + AdjustWindowRectEx(&r, style, FALSE, style_ex); + 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(frameHandle, GWL_STYLE, style); + SetWindowPos(frameHandle, 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(frameHandle, 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(frameHandle, SB_HORZ, &si, TRUE); + } + + // Update the cached client size + GetClientRect(frameHandle, &r); + client_size = Rect(r.left, r.top, r.right, r.bottom); +} + + +void +DesktopWindow::setName(const char* name) { + SetWindowText(handle, TStr(name)); +} + + +void +DesktopWindow::serverCutText(const char* str, int len) { + CharArray t(len+1); + memcpy(t.buf, str, len); + t.buf[len] = 0; + clipboard.setClipText(t.buf); +} + + +void DesktopWindow::fillRect(const Rect& r, Pixel pix) { + Rect img_rect = buffer->isScaling() ? buffer->calculateScaleBoundary(r) : r; + if (cursorBackingRect.overlaps(img_rect)) hideLocalCursor(); + buffer->fillRect(r, pix); + invalidateDesktopRect(r); +} +void DesktopWindow::imageRect(const Rect& r, void* pixels) { + Rect img_rect = buffer->isScaling() ? buffer->calculateScaleBoundary(r) : r; + if (cursorBackingRect.overlaps(img_rect)) hideLocalCursor(); + buffer->imageRect(r, pixels); + invalidateDesktopRect(r); +} +void DesktopWindow::copyRect(const Rect& r, int srcX, int srcY) { + Rect img_rect = buffer->isScaling() ? buffer->calculateScaleBoundary(r) : r; + if (cursorBackingRect.overlaps(img_rect) || + cursorBackingRect.overlaps(Rect(srcX, srcY, srcX+img_rect.width(), srcY+img_rect.height()))) + hideLocalCursor(); + buffer->copyRect(r, Point(r.tl.x-srcX, r.tl.y-srcY)); + invalidateDesktopRect(r); +} + +void DesktopWindow::invertRect(const Rect& r) { + int stride; + rdr::U8* p = buffer->isScaling() ? buffer->getPixelsRW(buffer->calculateScaleBoundary(r), &stride) + : 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; + } + } + } + invalidateDesktopRect(r); +} diff --git a/win/vncviewer/DesktopWindow.h b/win/vncviewer/DesktopWindow.h new file mode 100644 index 00000000..3d2211f8 --- /dev/null +++ b/win/vncviewer/DesktopWindow.h @@ -0,0 +1,254 @@ +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +// -=- DesktopWindow.h + +// Each VNC connection instance (CConn) creates a DesktopWindow the +// server initialisation message has been received. The CConn is +// responsible for all RFB-specific and network issues. The +// DesktopWindow is responsible for all GUI management issues. +// +// DesktopWindow provides a FullFramePixelBuffer interface for the +// CConn to render updates into. It also requires a callback object +// to be supplied, which will be notified when various events occur. + +#ifndef __RFB_WIN32_DESKTOP_WINDOW_H__ +#define __RFB_WIN32_DESKTOP_WINDOW_H__ + +#include <rfb/Cursor.h> +#include <rfb_win32/CKeyboard.h> +#include <rfb_win32/CPointer.h> +#include <rfb_win32/Clipboard.h> +#include <rfb_win32/ScaledDIBSectionBuffer.h> +#include <rfb_win32/LogicalPalette.h> +#include <vncviewer/ViewerToolBar.h> + + +namespace rfb { + + namespace win32 { + + class DesktopWindow : rfb::win32::Clipboard::Notifier { + public: + class Callback; + + DesktopWindow(Callback* cb_); + ~DesktopWindow(); + + // - Window message handling procedure + LRESULT processMessage(UINT msg, WPARAM wParam, LPARAM lParam); + + // - Window message handling procedure for the framebuffer window + LRESULT processFrameMessage(UINT msg, WPARAM wParam, LPARAM lParam); + + // - Determine the native pixel format of the window + // This can (and often will) differ from the PixelBuffer format + PixelFormat getNativePF() const; + + // - Get the underlying window handle + // This is used by F8Menu to modify the window's menu + HWND getHandle() const {return handle;} + + // - Get the framebuffer window handle + HWND getFrameHandle() const {return frameHandle;} + + // - Set the window title + void setName(const char* name); + + // - Set the key that causes the system/F8 menu to be displayed + void setMenuKey(rdr::U8 key) { menuKey = key; } + + // - Pointer event handling + void setEmulate3(bool em3) { ptr.enableEmulate3(em3); } + void setPointerEventInterval(int interval) { ptr.enableInterval(interval); } + + // - Set the pixel format, size etc of the underlying PixelBuffer + void setPF(const PixelFormat& pf); + PixelFormat getPF() const { return buffer->getPF(); } + void setSize(int w, int h); + void setColour(int i, int r, int g, int b) {buffer->setColour(i, r, g, b);} + + // - Set the cursor to render when the pointer is within the desktop buffer + void setCursor(int w, int h, const Point& hotspot, void* data, void* mask); + void showCursor() { showLocalCursor(); } + + // - Set the window fullscreen / determine whether it is fullscreen + void setFullscreen(bool fs); + bool isFullscreen() { return fullscreenActive; } + + // - Set/get the toolbar's state + void setShowToolbar(bool st); + bool isToolbarEnabled() { return showToolbar; } + + // - Set whether to disable special Windows keys & pass them straight to server + void setDisableWinKeys(bool dwk); + + // - Set/get which monitor the window should be displayed on + void setMonitor(const char* monitor); + char* getMonitor() const; + + // - Set the local clipboard + void serverCutText(const char* str, int len); + + // - Draw into the desktop buffer & update the window + void fillRect(const Rect& r, Pixel pix); + void imageRect(const Rect& r, void* pixels); + void copyRect(const Rect& r, int srcX, int srcY); + + void invertRect(const Rect& r); + + // - Update the window palette if the display is palette-based. + // Colours are pulled from the desktop buffer'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); + + // Clipboard::Notifier interface + void notifyClipboardChanged(const char* text, int len); + + // DesktopWindow Callback interface + class Callback : public InputHandler { + public: + virtual ~Callback() {} + virtual void displayChanged() = 0; + virtual void paintCompleted() = 0; + virtual bool sysCommand(WPARAM wParam, LPARAM lParam) = 0; + virtual void closeWindow() = 0; + virtual void refreshMenu(bool enableSysItems) = 0; + }; + + // Currently accessible so that the CConn can releaseAllKeys & check + // whether Ctrl and Alt are down... + rfb::win32::CKeyboard kbd; + + protected: + // Routines to convert between Desktop and client (window) coordinates + Point desktopToClient(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; + } + Rect desktopToClient(const Rect& r) { + return Rect(desktopToClient(r.tl), desktopToClient(r.br)); + } + Point clientToDesktop(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; + } + Rect clientToDesktop(const Rect& r) { + return Rect(clientToDesktop(r.tl), clientToDesktop(r.br)); + } + + // Internal routine used by the scrollbars & bump scroller to select + // the portion of the Desktop to display + bool setViewportOffset(const Point& tl); + + // Bump scroll handling. Bump scrolling is used if the window is + // in fullscreen mode and the Desktop is larger than the window + bool processBumpScroll(const Point& cursorPos); + void setBumpScroll(bool on); + bool bumpScroll; + Point bumpScrollDelta; + IntervalTimer bumpScrollTimer; + + // 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 invalidateDesktopRect(const Rect& crect, bool scaling=true); + + // Determine whether or not we need to enable/disable scrollbars and set the + // window style accordingly + void calculateScrollBars(); + + // Win32-specific input handling + rfb::win32::CPointer ptr; + Point oldpos; + rfb::win32::Clipboard clipboard; + + // Palette handling + LogicalPalette windowPalette; + bool palette_changed; + + // - Full-screen mode + RECT fullscreenOldRect; + DWORD fullscreenOldFlags; + bool fullscreenActive; + bool fullscreenRestore; + + // 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; + + // ToolBar handling + ViewerToolBar tb; + bool showToolbar; + + // Local window state + win32::ScaledDIBSectionBuffer* buffer; + bool has_focus; + Rect window_size; + Rect client_size; + Point scrolloffset; + Point maxscrolloffset; + HWND handle; + HWND frameHandle; + rdr::U8 menuKey; + + Callback* callback; + }; + + }; + +}; + +#endif // __RFB_WIN32_DESKTOP_WINDOW_H__ + + diff --git a/win/vncviewer/FTBrowseDlg.cxx b/win/vncviewer/FTBrowseDlg.cxx new file mode 100644 index 00000000..1b6dfb67 --- /dev/null +++ b/win/vncviewer/FTBrowseDlg.cxx @@ -0,0 +1,196 @@ +/* Copyright (C) 2005 TightVNC Team. All Rights Reserved. + * + * Developed by Dennis Syrovatsky + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + * + * TightVNC distribution homepage on the Web: http://www.tightvnc.com/ + * + */ + +// -=- FTBrpwseDlg.cxx + +#include <vncviewer/FTBrowseDlg.h> + +using namespace rfb; +using namespace rfb::win32; + +FTBrowseDlg::FTBrowseDlg(FTDialog *pFTDlg) +{ + m_pFTDlg = pFTDlg; + m_hwndDlg = NULL; + m_hwndTree = NULL; + m_hParentItem = NULL; +} + +FTBrowseDlg::~FTBrowseDlg() +{ + destroy(); +} + +bool +FTBrowseDlg::create() +{ + m_hwndDlg = CreateDialogParam(GetModuleHandle(0), MAKEINTRESOURCE(IDD_FTBROWSE), + m_pFTDlg->getWndHandle(), (DLGPROC) FTBrowseDlgProc, + (LONG) this); + + if (m_hwndDlg == NULL) return false; + + m_hwndTree = GetDlgItem(m_hwndDlg, IDC_FTBROWSETREE); + + ShowWindow(m_hwndDlg, SW_SHOW); + UpdateWindow(m_hwndDlg); + + return true; +} + +void +FTBrowseDlg::destroy() +{ + EndDialog(m_hwndDlg, 0); +} + +void +FTBrowseDlg::addItems(FileInfo *pFI) +{ + TVITEM tvi; + TVINSERTSTRUCT tvins; + + if (pFI->getNumEntries() <= 0) return; + + for (unsigned int i = 0; i < pFI->getNumEntries(); i++) + { + tvi.mask = TVIF_TEXT; + tvi.pszText = pFI->getNameAt(i);; + tvins.hParent = m_hParentItem; + tvins.item = tvi; + tvins.hParent = TreeView_InsertItem(m_hwndTree, &tvins); + TreeView_InsertItem(m_hwndTree, &tvins); + } +} + +char * +FTBrowseDlg::getTVPath(HTREEITEM hTItem) +{ + char path[FT_FILENAME_SIZE]; + char szText[FT_FILENAME_SIZE]; + + TVITEM tvi; + path[0] = '\0'; + + do { + tvi.mask = TVIF_TEXT | TVIF_HANDLE; + tvi.hItem = hTItem; + tvi.pszText = szText; + tvi.cchTextMax = FT_FILENAME_SIZE; + TreeView_GetItem(m_hwndTree, &tvi); + sprintf(path, "%s\\%s", path, tvi.pszText); + hTItem = TreeView_GetParent(m_hwndTree, hTItem); + } while(hTItem != NULL); + + return pathInvert(path); +} + +char * +FTBrowseDlg::pathInvert(char *pPath) +{ + int len = strlen(pPath); + m_szPath[0] = '\0'; + char *pos = NULL; + + while ((pos = strrchr(pPath, '\\')) != NULL) { + if (strlen(m_szPath) == 0) { + strcpy(m_szPath, (pos + 1)); + } else { + sprintf(m_szPath, "%s\\%s", m_szPath, (pos + 1)); + } + *pos = '\0'; + } + + m_szPath[len] = '\0'; + return m_szPath; +} + +char * +FTBrowseDlg::getPath() +{ + GetDlgItemText(m_hwndDlg, IDC_FTBROWSEPATH, m_szPath, FT_FILENAME_SIZE); + return m_szPath; +} + +void +FTBrowseDlg::deleteChildItems() +{ + while (TreeView_GetChild(m_hwndTree, m_hParentItem) != NULL) { + TreeView_DeleteItem(m_hwndTree, TreeView_GetChild(m_hwndTree, m_hParentItem)); + } +} + +BOOL CALLBACK +FTBrowseDlg::FTBrowseDlgProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) +{ + FTBrowseDlg *_this = (FTBrowseDlg *) GetWindowLong(hwnd, GWL_USERDATA); + switch (uMsg) + { + case WM_INITDIALOG: + { + SetWindowLong(hwnd, GWL_USERDATA, lParam); + return FALSE; + } + break; + case WM_COMMAND: + { + switch (LOWORD(wParam)) + { + case IDOK: + _this->m_pFTDlg->onEndBrowseDlg(true); + return FALSE; + case IDCANCEL: + _this->m_pFTDlg->onEndBrowseDlg(false); + return FALSE; + } + } + break; + case WM_NOTIFY: + switch (LOWORD(wParam)) + { + case IDC_FTBROWSETREE: + switch (((LPNMHDR) lParam)->code) + { + case TVN_SELCHANGED: + SetDlgItemText(hwnd, IDC_FTBROWSEPATH, _this->getTVPath(((NMTREEVIEW *) lParam)->itemNew.hItem)); + return FALSE; +// case TVN_ITEMEXPANDING: + case TVN_ITEMEXPANDED: + { + NMTREEVIEW *nmCode = (NMTREEVIEW *) lParam; + if (nmCode->action == 2) { + _this->m_hParentItem = nmCode->itemNew.hItem; + _this->deleteChildItems(); + _this->m_pFTDlg->getBrowseItems(_this->getTVPath(_this->m_hParentItem)); + } + } + return FALSE; + } + break; + case WM_CLOSE: + _this->m_pFTDlg->onEndBrowseDlg(false); + return FALSE; + } + } + return 0; +} diff --git a/win/vncviewer/FTBrowseDlg.h b/win/vncviewer/FTBrowseDlg.h new file mode 100644 index 00000000..5c9ba357 --- /dev/null +++ b/win/vncviewer/FTBrowseDlg.h @@ -0,0 +1,70 @@ +/* Copyright (C) 2005 TightVNC Team. All Rights Reserved. + * + * Developed by Dennis Syrovatsky + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + * + * TightVNC distribution homepage on the Web: http://www.tightvnc.com/ + * + */ + +// -=- FTBrowseDlg.h + +#ifndef __RFB_WIN32_FTBROWSEDLG_H__ +#define __RFB_WIN32_FTBROWSEDLG_H__ + +#include <windows.h> +#include <commctrl.h> + +#include <rfb/FileInfo.h> +#include <vncviewer/FTDialog.h> +#include <vncviewer/resource.h> + +namespace rfb { + namespace win32 { + class FTDialog; + + class FTBrowseDlg + { + public: + FTBrowseDlg(FTDialog *pFTDlg); + ~FTBrowseDlg(); + + static BOOL CALLBACK FTBrowseDlgProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam); + + bool create(); + void destroy(); + + void addItems(FileInfo *pFI); + char *getPath(); + + void deleteChildItems(); + + private: + HWND m_hwndDlg; + HWND m_hwndTree; + FTDialog *m_pFTDlg; + HTREEITEM m_hParentItem; + + char m_szPath[FT_FILENAME_SIZE]; + + char *pathInvert(char *pPath); + char *getTVPath(HTREEITEM hTItem); + }; + } +} + +#endif // __RFB_WIN32_FTBROWSEDLG_H__ diff --git a/win/vncviewer/FTDialog.cxx b/win/vncviewer/FTDialog.cxx new file mode 100644 index 00000000..5f71f7dd --- /dev/null +++ b/win/vncviewer/FTDialog.cxx @@ -0,0 +1,1162 @@ +/* Copyright (C) 2005 TightVNC Team. All Rights Reserved. + * + * Developed by Dennis Syrovatsky + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + * + * TightVNC distribution homepage on the Web: http://www.tightvnc.com/ + * + */ + +// -=- FTDialog.cxx + +#include <vncviewer/FTDialog.h> + +using namespace rfb; +using namespace rfb::win32; + +const char FTDialog::szCheckDeleteQueueText[] = "TightVNC.Viewer.CheckDeleteQueue.Msg"; +const char FTDialog::szCheckTransferQueueText[] = "TightVNC.Viewer.CheckTransferQueue.Msg"; +const char FTDialog::szUploadFilePortionText[] = "TightVNC.Viewer.UploadFilePortion.Msg"; + +FTDialog::FTDialog(HINSTANCE hInst, FileTransfer *pFT) +{ + m_pFileTransfer = pFT; + m_hInstance = hInst; + + m_bDlgShown = false; + m_bLocalBrowsing = true; + m_bCloseDlgAfterCancel = false; + + m_pLocalLV = NULL; + m_pRemoteLV = NULL; + m_pProgress = NULL; + m_pCancelingDlg = NULL; + m_pCreateFolderDlg = NULL; + m_pRenameDlg = NULL; + m_pBrowseDlg = NULL; + + m_hwndFTDialog = NULL; + m_hwndLocalPath = NULL; + m_hwndRemotePath = NULL; + + m_FTMenuSource = 0; + m_dwNumStatusStrings = 0; + + m_szLocalPath[0] = '\0'; + m_szRemotePath[0] = '\0'; + m_szLocalPathTmp[0] = '\0'; + m_szRemotePathTmp[0] = '\0'; + m_szCreateFolderName[0] = '\0'; +} + +FTDialog::~FTDialog() +{ + destroyFTDialog(); +} + +bool +FTDialog::createFTDialog(HWND hwndParent) +{ + if (m_hwndFTDialog != NULL) { + ShowWindow(m_hwndFTDialog, SW_SHOW); + m_bDlgShown = true; + showLocalLVItems(); + showRemoteLVItems(); + return true; + } + + if (!initFTWndMsgs()) return false; + + m_hwndFTDialog = CreateDialogParam(m_hInstance, + MAKEINTRESOURCE(IDD_FILETRANSFER_DLG), + hwndParent, + (DLGPROC) FTDialogProc, + (LONG) this); + + if (m_hwndFTDialog == NULL) return false; + + HWND hwndLocalList = GetDlgItem(m_hwndFTDialog, IDC_FTLOCALLIST); + HWND hwndRemoteList = GetDlgItem(m_hwndFTDialog, IDC_FTREMOTELIST); + + if ((hwndLocalList == NULL) || (hwndRemoteList == NULL)) { + destroyFTDialog(); + return false; + } + + m_pLocalLV = new FTListView(hwndLocalList); + m_pRemoteLV = new FTListView(hwndRemoteList); + + m_pProgress = new FTProgress(m_hwndFTDialog); + + if ((m_pLocalLV == NULL) || (m_pRemoteLV == NULL) || (m_pProgress == NULL)) { + destroyFTDialog(); + return false; + } + + initFTDialog(); + + ShowWindow(m_hwndFTDialog, SW_SHOW); + UpdateWindow(m_hwndFTDialog); + m_bDlgShown = true; + return true; +} + +bool +FTDialog::initFTDialog() +{ + m_pLocalLV->initialize(m_hInstance); + m_pRemoteLV->initialize(m_hInstance); + + m_hwndLocalPath = GetDlgItem(m_hwndFTDialog, IDC_FTLOCALPATH); + m_hwndRemotePath = GetDlgItem(m_hwndFTDialog, IDC_FTREMOTEPATH); + + setIcon(IDC_FTLOCALUP, IDI_FTUP); + setIcon(IDC_FTREMOTEUP, IDI_FTUP); + setIcon(IDC_FTLOCALRELOAD, IDI_FTRELOAD); + setIcon(IDC_FTREMOTERELOAD, IDI_FTRELOAD); + + showLocalLVItems(); + showRemoteLVItems(); + + return true; +} + +bool +FTDialog::initFTWndMsgs() +{ + m_msgCheckDeleteQueue = RegisterWindowMessage(szCheckDeleteQueueText); + m_msgCheckTransferQueue = RegisterWindowMessage(szCheckTransferQueueText); + m_msgUploadFilePortion = RegisterWindowMessage(szUploadFilePortionText); + + if ((m_msgCheckDeleteQueue) && + (m_msgCheckTransferQueue) && + (m_msgUploadFilePortion)) return true; + + return false; +} + +bool +FTDialog::closeFTDialog() +{ + ShowWindow(m_hwndFTDialog, SW_HIDE); + m_bDlgShown = false; + return true; +} + +void +FTDialog::destroyFTDialog() +{ + if (m_pLocalLV != NULL) { + delete m_pLocalLV; + m_pLocalLV = NULL; + } + + if (m_pRemoteLV != NULL) { + delete m_pRemoteLV; + m_pRemoteLV = NULL; + } + + if (m_pProgress != NULL) { + delete m_pProgress; + m_pProgress = NULL; + } + + if(m_pCancelingDlg != NULL) { + delete m_pCancelingDlg; + m_pCancelingDlg = NULL; + } + + if (m_pCreateFolderDlg != NULL) { + delete m_pCreateFolderDlg; + m_pCreateFolderDlg = NULL; + } + + if(m_pRenameDlg != NULL) { + delete m_pRenameDlg; + m_pRenameDlg = NULL; + } + + if (DestroyWindow(m_hwndFTDialog)) m_hwndFTDialog = NULL; +} + +BOOL CALLBACK +FTDialog::FTDialogProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) +{ + FTDialog *_this = (FTDialog *) GetWindowLong(hwnd, GWL_USERDATA); + + switch (uMsg) + { + case WM_INITDIALOG: + _this = (FTDialog*)lParam; + SetWindowLong(hwnd, GWL_USERDATA, (LONG) lParam); + SetForegroundWindow(hwnd); + return TRUE; + case WM_COMMAND: + { + switch (LOWORD(wParam)) + { + case IDC_FTLOCALPATH: + switch (HIWORD (wParam)) + { + case CBN_SETFOCUS: + _this->setButtonsState(); + return FALSE; + } + break; + case IDC_FTREMOTEPATH: + switch (HIWORD (wParam)) + { + case CBN_SETFOCUS: + _this->setButtonsState(); + return FALSE; + } + break; + case IDC_FTCLOSE: + _this->onClose(); + return FALSE; + case IDC_FTLOCALBROWSE: + _this->onLocalBrowse(); + return FALSE; + case IDC_FTREMOTEBROWSE: + _this->onRemoteBrowse(); + return FALSE; + case IDC_FTLOCALUP: + _this->setButtonsState(); + _this->onLocalOneUpFolder(); + return FALSE; + case IDC_FTREMOTEUP: + _this->setButtonsState(); + _this->onRemoteOneUpFolder(); + return FALSE; + case IDC_FTLOCALRELOAD: + _this->setButtonsState(); + _this->onLocalReload(); + return FALSE; + case IDC_FTREMOTERELOAD: + _this->setButtonsState(); + _this->onRemoteReload(); + return FALSE; + case IDC_FTUPLOAD: + _this->onUpload(); + return FALSE; + case IDC_FTDOWNLOAD: + _this->onDownload(); + return FALSE; + case IDC_FTCANCEL: + _this->onFTCancel(); + return FALSE; + case IDM_FTCOPY: + case IDM_FTRENAME: + case IDM_FTDELETE: + case IDM_FTCANCEL: + case IDM_FTCREATEFOLDER: + _this->onFTMenuCommand(LOWORD(wParam)); + return FALSE; + } + } + break; + + case WM_NOTIFY: + switch (LOWORD(wParam)) + { + case IDC_FTLOCALLIST: + switch (((LPNMHDR) lParam)->code) + { + case NM_CLICK: + case NM_SETFOCUS: + case LVN_ITEMCHANGED: + _this->setButtonsState(); + return FALSE; + case LVN_GETDISPINFO: + _this->m_pLocalLV->onGetDispInfo((NMLVDISPINFO *) lParam); + return FALSE; + case LVN_ITEMACTIVATE: + _this->onLocalItemActivate((LPNMITEMACTIVATE) lParam); + return FALSE; + case NM_RCLICK: + _this->onLocalRButton(); + return FALSE; + } + break; + case IDC_FTREMOTELIST: + switch (((LPNMHDR) lParam)->code) + { + case NM_CLICK: + case NM_SETFOCUS: + case LVN_ITEMCHANGED: + _this->setButtonsState(); + case LVN_GETDISPINFO: + _this->m_pRemoteLV->onGetDispInfo((NMLVDISPINFO *) lParam); + return FALSE; + case LVN_ITEMACTIVATE: + _this->onRemoteItemActivate((LPNMITEMACTIVATE) lParam); + return FALSE; + case NM_RCLICK: + _this->onRemoteRButton(); + return FALSE; + } + break; + } + break; + case WM_CLOSE: + _this->onClose(); + return FALSE; + } + + if (_this != NULL) { + if (uMsg == _this->m_msgCheckTransferQueue) + _this->m_pFileTransfer->checkTransferQueue(); + + if (uMsg == _this->m_msgUploadFilePortion) + _this->m_pFileTransfer->uploadFilePortion(); + + if (uMsg == _this->m_msgCheckDeleteQueue) + _this->m_pFileTransfer->checkDeleteQueue(); + } + + return FALSE; +} + +void +FTDialog::onClose() +{ + if (m_pFileTransfer->isTransferEnable()) { + m_bCloseDlgAfterCancel = true; + onFTCancel(); + } else { + closeFTDialog(); + } +} + +void +FTDialog::onLocalItemActivate(LPNMITEMACTIVATE lpnmia) +{ + if (strlen(m_szLocalPath) == 0) { + strcpy(m_szLocalPathTmp, m_pLocalLV->getActivateItemName(lpnmia)); + } else { + sprintf(m_szLocalPathTmp, "%s\\%s", m_szLocalPath, m_pLocalLV->getActivateItemName(lpnmia)); + } + showLocalLVItems(); +} + +void +FTDialog::onRemoteItemActivate(LPNMITEMACTIVATE lpnmia) +{ + if (strlen(m_szRemotePath) == 0) { + strcpy(m_szRemotePathTmp, m_pRemoteLV->getActivateItemName(lpnmia)); + } else { + sprintf(m_szRemotePathTmp, "%s\\%s", m_szRemotePath, m_pRemoteLV->getActivateItemName(lpnmia)); + } + showRemoteLVItems(); +} + +void +FTDialog::onEndBrowseDlg(bool bResult) +{ + if (m_pBrowseDlg == NULL) return; + + if (bResult) { + if (m_bLocalBrowsing) { + strcpy(m_szLocalPathTmp, m_pBrowseDlg->getPath()); + showLocalLVItems(); + } else { + strcpy(m_szRemotePathTmp, m_pBrowseDlg->getPath()); + showRemoteLVItems(); + } + } + delete m_pBrowseDlg; + m_pBrowseDlg = NULL; +} + +void +FTDialog::onLocalBrowse() +{ + if (m_pBrowseDlg != NULL) return; + + m_bLocalBrowsing = true; + + m_pBrowseDlg = new FTBrowseDlg(this); + m_pBrowseDlg->create(); + + getBrowseItems(""); +} + +void +FTDialog::onRemoteBrowse() +{ + if (m_pBrowseDlg != NULL) return; + + m_bLocalBrowsing = false; + + m_pBrowseDlg = new FTBrowseDlg(this); + if (m_pBrowseDlg->create()) { + m_pFileTransfer->requestFileList("", FT_FLR_DEST_BROWSE, true); + } else { + delete m_pBrowseDlg; + m_pBrowseDlg = NULL; + } +} + +void +FTDialog::onLocalReload() +{ + strcpy(m_szLocalPathTmp, m_szLocalPath); + showLocalLVItems(); +} + +void +FTDialog::onRemoteReload() +{ + strcpy(m_szRemotePathTmp, m_szRemotePath); + showRemoteLVItems(); +} + +void +FTDialog::showLocalLVItems() +{ + FolderManager fm; + FileInfo fileInfo; + if (fm.getDirInfo(m_szLocalPathTmp, &fileInfo, 0)) { + fileInfo.sort(); + m_pLocalLV->deleteAllItems(); + m_pLocalLV->addItems(&fileInfo); + strcpy(m_szLocalPath, m_szLocalPathTmp); + SetWindowText(m_hwndLocalPath, m_szLocalPath); + } +} + +void +FTDialog::showRemoteLVItems() +{ + m_pFileTransfer->requestFileList(m_szRemotePathTmp, FT_FLR_DEST_MAIN, false); +} + +void +FTDialog::addRemoteLVItems(FileInfo *pFI) +{ + pFI->sort(); + m_pRemoteLV->deleteAllItems(); + m_pRemoteLV->addItems(pFI); + strcpy(m_szRemotePath, m_szRemotePathTmp); + SetWindowText(m_hwndRemotePath, m_szRemotePath); + UpdateWindow(m_hwndFTDialog); +} + +void +FTDialog::onLocalOneUpFolder() +{ + strcpy(m_szLocalPathTmp, m_szLocalPath); + makeOneUpFolder(m_szLocalPathTmp); + showLocalLVItems(); +} + +void +FTDialog::onRemoteOneUpFolder() +{ + strcpy(m_szRemotePathTmp, m_szRemotePath); + makeOneUpFolder(m_szRemotePathTmp); + showRemoteLVItems(); +} + +void +FTDialog::reqFolderUnavailable() +{ + strcpy(m_szRemotePathTmp, m_szRemotePath); + SetWindowText(m_hwndRemotePath, m_szRemotePath); + UpdateWindow(m_hwndFTDialog); +} + +int +FTDialog::makeOneUpFolder(char *pPath) +{ + if (strcmp(pPath, "") == 0) return strlen(pPath); + for (int i=(strlen(pPath)-2); i>=0; i--) { + if (pPath[i] == '\\') { + pPath[i] = '\0'; + break; + } + if (i == 0) pPath[0] = '\0'; + } + return strlen(pPath); +} + +void +FTDialog::onUpload() +{ + FileInfo fi; + char prefix[FT_FILENAME_SIZE]; + prefix[0] = '\0'; + + if (m_pFileTransfer->isTransferEnable()) + strcpy(prefix, "File Transfer is active.\nDo you want to add selected file(s)/folder(s) to transfer queue?\n\n"); + + int numSel = m_pLocalLV->getSelectedItems(&fi); + if (numSel > 0) { + char *pBuf = new char [(numSel + 4) * (MAX_PATH + 3) + strlen(prefix) + 1]; + sprintf(pBuf, "%sFrom: Local Computer %s\\\n\n", prefix, m_szLocalPath); + + for (unsigned int i = 0; i < fi.getNumEntries(); i++) + sprintf(pBuf, "%s%s, ", pBuf, fi.getNameAt(i)); + + sprintf(pBuf, "%s\n\nTo: Remote Computer %s\\", pBuf, m_szRemotePath); + + if (strlen(pBuf) > 2048) + sprintf(pBuf, "%sFrom: Local Computer %s\\\n\nTo: Remote Computer %s\\\n\nTotal %d file(s)/folder(s)", + prefix, m_szLocalPath, m_szRemotePath, numSel); + + if (MessageBox(m_hwndFTDialog, pBuf, "Copy Selected Files and Folders", MB_OKCANCEL) == IDOK) + m_pFileTransfer->addTransferQueue(m_szLocalPath, m_szRemotePath, &fi, FT_ATTR_COPY_UPLOAD); + + setButtonsState(); + + delete [] pBuf; + return; + } + + setButtonsState(); + setStatusText("File Transfer Impossible. No file(s) selected for transfer"); +} + +void +FTDialog::onDownload() +{ + FileInfo fi; + char prefix[FT_FILENAME_SIZE]; + prefix[0] = '\0'; + + if (m_pFileTransfer->isTransferEnable()) + strcpy(prefix, "File Transfer is active.\nDo you want to add selected file(s)/folder(s) to transfer queue?\n\n"); + + int numSel = m_pRemoteLV->getSelectedItems(&fi); + if (numSel > 0) { + char *pBuf = new char [(numSel + 4) * (MAX_PATH + 3) + strlen(prefix) + 1]; + sprintf(pBuf, "%sFrom: Remote Computer %s\\\n\n", prefix, m_szRemotePath); + + for (unsigned int i = 0; i < fi.getNumEntries(); i++) + sprintf(pBuf, "%s%s, ", pBuf, fi.getNameAt(i)); + + sprintf(pBuf, "%s\n\nTo: Local Computer %s\\", pBuf, m_szLocalPath); + + if (strlen(pBuf) > 2048) + sprintf(pBuf, "%sFrom: Remote Computer %s\\\n\nTo: Local Computer %s\\\n\nTotal %d file(s)/folder(s)", + prefix, m_szRemotePath, m_szLocalPath, numSel); + + if (MessageBox(m_hwndFTDialog, pBuf, "Copy Selected Files and Folders", MB_OKCANCEL) == IDOK) + m_pFileTransfer->addTransferQueue(m_szLocalPath, m_szRemotePath, &fi, FT_ATTR_COPY_DOWNLOAD); + + setButtonsState(); + + delete [] pBuf; + return; + } + + setButtonsState(); + setStatusText("File Transfer Impossible. No file(s) selected for transfer"); +} + +void +FTDialog::onLocalRename() +{ + if (m_pRenameDlg != NULL) return; + + FileInfo fi; + if (m_pLocalLV->getSelectedItems(&fi) == 1) { + unsigned int flags = fi.getFlagsAt(0); + m_pRenameDlg = new FTDialog::RenameDlg(this); + if (m_pRenameDlg != NULL) { + if (flags & FT_ATTR_DIR) { + m_pRenameDlg->create(fi.getNameAt(0), true); + } else { + m_pRenameDlg->create(fi.getNameAt(0), false); + } + if (strlen(m_pRenameDlg->getFolderName()) > 0) { + FolderManager fm; + fm.renameIt(m_szLocalPath, fi.getNameAt(0), m_pRenameDlg->getFolderName()); + showLocalLVItems(); + } + delete m_pRenameDlg; + m_pRenameDlg = NULL; + } + } + setButtonsState(); +} + +void +FTDialog::onRemoteRename() +{ + if (m_pRenameDlg != NULL) return; + + FileInfo fi; + if (m_pRemoteLV->getSelectedItems(&fi) == 1) { + unsigned int flags = fi.getFlagsAt(0); + m_pRenameDlg = new FTDialog::RenameDlg(this); + if (m_pRenameDlg != NULL) { + if (flags & FT_ATTR_DIR) { + m_pRenameDlg->create(fi.getNameAt(0), true); + } else { + m_pRenameDlg->create(fi.getNameAt(0), false); + } + if (strlen(m_pRenameDlg->getFolderName()) > 0) { + m_pFileTransfer->renameRemote(m_szRemotePath, fi.getNameAt(0), m_pRenameDlg->getFolderName()); + } + delete m_pRenameDlg; + m_pRenameDlg = NULL; + } + } + setButtonsState(); +} + +void +FTDialog::onLocalDelete() +{ + FileInfo fi; + if (m_pLocalLV->getSelectedItems(&fi) > 0) { + m_pFileTransfer->addDeleteQueue(m_szLocalPath, &fi, FT_ATTR_DELETE_LOCAL); + } + setButtonsState(); +} + +void +FTDialog::onRemoteDelete() +{ + FileInfo fi; + if (m_pRemoteLV->getSelectedItems(&fi) > 0) { + m_pFileTransfer->addDeleteQueue(m_szRemotePath, &fi, FT_ATTR_DELETE_REMOTE); + } + setButtonsState(); +} + +bool +FTDialog::getCreateFolderName() +{ + if (m_pCreateFolderDlg != NULL) return false; + + bool bResult = false; + + m_pCreateFolderDlg = new FTDialog::CreateFolderDlg(this); + m_pCreateFolderDlg->create(); + if (strlen(m_pCreateFolderDlg->getFolderName()) != 0) { + strcpy(m_szCreateFolderName, m_pCreateFolderDlg->getFolderName()); + bResult = true; + } else { + m_szCreateFolderName[0] = '\0'; + bResult = false; + } + delete m_pCreateFolderDlg; + m_pCreateFolderDlg = NULL; + + return bResult; +} + +void +FTDialog::onLocalCreateFolder() +{ + if (getCreateFolderName()) { + char path[FT_FILENAME_SIZE]; + sprintf(path, "%s\\%s", m_szLocalPath, m_szCreateFolderName); + setStatusText("Creating Local Folder: %s", path); + FolderManager fm; + if (fm.createDir(path)) showLocalLVItems(); + } +} + +void +FTDialog::onRemoteCreateFolder() +{ + if (getCreateFolderName()) { + m_pFileTransfer->createRemoteFolder(m_szRemotePath, m_szCreateFolderName); + } +} + +void +FTDialog::onFTCancel() +{ + if (m_pCancelingDlg != NULL) return; + + m_pCancelingDlg = new CancelingDlg(this); + + m_pCancelingDlg->create(); +} + +void +FTDialog::cancelTransfer(bool bResult) +{ + if (m_pCancelingDlg != NULL) { + delete m_pCancelingDlg; + m_pCancelingDlg = NULL; + } + + setButtonsState(); + + if ((m_bCloseDlgAfterCancel) && (bResult)) { + m_bCloseDlgAfterCancel = false; + closeFTDialog(); + } + + m_pFileTransfer->m_bCancel = bResult; +} + +void +FTDialog::afterCancelTransfer() +{ + if (m_pCancelingDlg != NULL) { + delete m_pCancelingDlg; + m_pCancelingDlg = NULL; + } +} + +void +FTDialog::onFTMenuCommand(int command) +{ + switch (command) + { + case IDM_FTCOPY: + { + switch (m_FTMenuSource) + { + case IDC_FTLOCALLIST: onUpload(); break; + case IDC_FTREMOTELIST: onDownload(); break; + default: break; + } + } + break; + case IDM_FTRENAME: + { + switch (m_FTMenuSource) + { + case IDC_FTLOCALLIST: onLocalRename(); break; + case IDC_FTREMOTELIST: onRemoteRename(); break; + default: break; + } + } + break; + case IDM_FTDELETE: + { + switch (m_FTMenuSource) + { + case IDC_FTLOCALLIST: onLocalDelete(); break; + case IDC_FTREMOTELIST: onRemoteDelete(); break; + default: break; + } + } + break; + case IDM_FTCANCEL: onFTCancel(); break; + case IDM_FTCREATEFOLDER: + { + switch (m_FTMenuSource) + { + case IDC_FTLOCALLIST: onLocalCreateFolder(); break; + case IDC_FTREMOTELIST: onRemoteCreateFolder(); break; + default: break; + } + } + break; + default: + break; + } +} + +void +FTDialog::onLocalRButton() +{ + refreshBtnState(); + m_FTMenuSource = IDC_FTLOCALLIST; + showFTMenu(m_BtnState.uploadBtn, m_BtnState.createLocalFldBtn, + m_BtnState.renameLocalBtn, m_BtnState.deleteLocalBtn, + m_BtnState.cancelBtn); +} + +void +FTDialog::onRemoteRButton() +{ + refreshBtnState(); + m_FTMenuSource = IDC_FTREMOTELIST; + showFTMenu(m_BtnState.downloadBtn, m_BtnState.createRemoteFldBtn, + m_BtnState.renameRemoteBtn, m_BtnState.deleteRemoteBtn, + m_BtnState.cancelBtn); +} + +void +FTDialog::showFTMenu(bool copyBtnState, bool createFldBtnState, bool renameBtnState, + bool deleteBtnState, bool cancelBtnState) +{ + HMENU hMenu = LoadMenu(m_hInstance, MAKEINTRESOURCE(IDR_FTMENU)); + HMENU hFTMenu = GetSubMenu(hMenu, 0); + + SetMenuDefaultItem(hFTMenu, IDM_FTCOPY, FALSE); + + SetForegroundWindow(m_hwndFTDialog); + + if (copyBtnState) { + EnableMenuItem(hMenu, IDM_FTCOPY, MF_ENABLED); + } else { + EnableMenuItem(hMenu, IDM_FTCOPY, MF_DISABLED | MF_GRAYED); + } + if (createFldBtnState) { + EnableMenuItem(hMenu, IDM_FTCREATEFOLDER, MF_ENABLED); + } else { + EnableMenuItem(hMenu, IDM_FTCREATEFOLDER, MF_DISABLED | MF_GRAYED); + } + if (renameBtnState) { + EnableMenuItem(hMenu, IDM_FTRENAME, MF_ENABLED); + } else { + EnableMenuItem(hMenu, IDM_FTRENAME, MF_DISABLED | MF_GRAYED); + } + if (deleteBtnState) { + EnableMenuItem(hMenu, IDM_FTDELETE, MF_ENABLED); + } else { + EnableMenuItem(hMenu, IDM_FTDELETE, MF_DISABLED | MF_GRAYED); + } + if (cancelBtnState) { + EnableMenuItem(hMenu, IDM_FTCANCEL, MF_ENABLED); + } else { + EnableMenuItem(hMenu, IDM_FTCANCEL, MF_DISABLED | MF_GRAYED); + } + DrawMenuBar(m_hwndFTDialog); + + POINT cursorPosition; + GetCursorPos(&cursorPosition); + TrackPopupMenu(hFTMenu, 0, cursorPosition.x, cursorPosition.y, 0, m_hwndFTDialog, 0); + DestroyMenu(hFTMenu); +} + +void +FTDialog::setIcon(int dest, int idIcon) +{ + HANDLE hIcon = LoadImage(m_hInstance, MAKEINTRESOURCE(idIcon), IMAGE_ICON, 16, 16, LR_SHARED); + SendMessage(GetDlgItem(m_hwndFTDialog, dest), BM_SETIMAGE, (WPARAM) IMAGE_ICON, (LPARAM) hIcon); + DestroyIcon((HICON) hIcon); +} + +void +FTDialog::refreshBtnState() +{ + if (!m_bDlgShown) return; + + int locPathLen = strlen(m_szLocalPath); + int remPathLen = strlen(m_szRemotePath); + + if (GetFocus() == m_pLocalLV->getWndHandle()) { + m_BtnState.downloadBtn = false; + if (strlen(m_szLocalPath) != 0) { + m_BtnState.createLocalFldBtn = true; + int nCount = ListView_GetSelectedCount(m_pLocalLV->getWndHandle()); + if (nCount <= 0) { + m_BtnState.uploadBtn = false; + m_BtnState.deleteLocalBtn = false; + m_BtnState.renameLocalBtn = false; + } else { + m_BtnState.deleteLocalBtn = true; + if (remPathLen == 0) { + m_BtnState.uploadBtn = false; + } else { + m_BtnState.uploadBtn = true; + } + if (nCount == 1) { + m_BtnState.renameLocalBtn = true; + } else { + m_BtnState.renameLocalBtn = false; + } + } + } else { + m_BtnState.uploadBtn = false; + m_BtnState.createLocalFldBtn = false; + m_BtnState.deleteLocalBtn = false; + m_BtnState.renameLocalBtn = false; + m_BtnState.downloadBtn = false; + } + } else { + if (GetFocus() == m_pRemoteLV->getWndHandle()) { + m_BtnState.uploadBtn = false; + if (strlen(m_szRemotePath) != 0) { + m_BtnState.createRemoteFldBtn = true; + int nCount = ListView_GetSelectedCount(m_pRemoteLV->getWndHandle()); + if (nCount <= 0) { + m_BtnState.downloadBtn = false; + m_BtnState.deleteRemoteBtn = false; + m_BtnState.renameRemoteBtn = false; + } else { + m_BtnState.deleteRemoteBtn = true; + if (locPathLen == 0) { + m_BtnState.downloadBtn = false; + } else { + m_BtnState.downloadBtn = true; + } + if (nCount == 1) { + m_BtnState.renameRemoteBtn = true; + } else { + m_BtnState.renameRemoteBtn = false; + } + } + } else { + m_BtnState.downloadBtn = false; + m_BtnState.createRemoteFldBtn = false; + m_BtnState.deleteRemoteBtn = false; + m_BtnState.renameRemoteBtn = false; + m_BtnState.uploadBtn = false; + } + } else { + } + } + if (m_pFileTransfer->isTransferEnable()) + m_BtnState.cancelBtn = true; + else + m_BtnState.cancelBtn = false; +} + +void +FTDialog::setButtonsState() +{ + refreshBtnState(); + + switch (m_BtnState.uploadBtn) + { + case false: EnableWindow(GetDlgItem(m_hwndFTDialog, IDC_FTUPLOAD), FALSE); break; + case true: EnableWindow(GetDlgItem(m_hwndFTDialog, IDC_FTUPLOAD), TRUE); break; + } + + switch (m_BtnState.downloadBtn) + { + case false: EnableWindow(GetDlgItem(m_hwndFTDialog, IDC_FTDOWNLOAD), FALSE); break; + case true: EnableWindow(GetDlgItem(m_hwndFTDialog, IDC_FTDOWNLOAD), TRUE); break; + } + + switch (m_BtnState.cancelBtn) + { + case false: EnableWindow(GetDlgItem(m_hwndFTDialog, IDC_FTCANCEL), FALSE); break; + case true: EnableWindow(GetDlgItem(m_hwndFTDialog, IDC_FTCANCEL), TRUE); break; + } + + UpdateWindow(m_hwndFTDialog); +} + +void +FTDialog::setStatusText(LPCSTR format,...) +{ + char text[1024]; + va_list args; + va_start(args, format); + int nSize = _vsnprintf(text, sizeof(text), format, args); + + HWND hStatusBox = GetDlgItem(m_hwndFTDialog, IDC_FTSTATUS); + + LRESULT lRes = SendMessage(hStatusBox, (UINT) CB_INSERTSTRING, (WPARAM) 0, (LPARAM) text); + if ((lRes != CB_ERR) && (lRes != CB_ERRSPACE)) { + lRes = SendMessage(hStatusBox, (UINT) CB_SETCURSEL, (WPARAM) lRes, (LPARAM) 0); + } + + m_dwNumStatusStrings++; + if (m_dwNumStatusStrings > FT_MAX_STATUS_STRINGS) { + int numItems = SendMessage(hStatusBox, (UINT) CB_GETCOUNT, (WPARAM) 0, (LPARAM) 0); + if (numItems != CB_ERR) { + m_dwNumStatusStrings--; + SendMessage(hStatusBox, (UINT) CB_DELETESTRING, (WPARAM) numItems - 1, (LPARAM) 0); + } + } +} + +void +FTDialog::getBrowseItems(char *pPath) +{ + if (m_bLocalBrowsing) { + FileInfo fi; + FolderManager fm; + fm.getDirInfo(pPath, &fi, 1); + if (m_pBrowseDlg != NULL) m_pBrowseDlg->addItems(&fi); + } else { + m_pFileTransfer->requestFileList(pPath, FT_FLR_DEST_BROWSE, true); + } +} + +void +FTDialog::addBrowseItems(FileInfo *pFI) +{ + if (m_pBrowseDlg == NULL) return; + + m_pBrowseDlg->addItems(pFI); +} + +void +FTDialog::processDlgMsgs() +{ + MSG msg; + while(PeekMessage(&msg, NULL, 0, 0, PM_REMOVE) != 0) { + TranslateMessage(&msg); + DispatchMessage(&msg); + } +} + +void +FTDialog::postCheckTransferQueueMsg() +{ + PostMessage(m_hwndFTDialog, m_msgCheckTransferQueue, 0, 0); +} + +void +FTDialog::postUploadFilePortionMsg() +{ + PostMessage(m_hwndFTDialog, m_msgUploadFilePortion, 0, 0); +} + +void +FTDialog::postCheckDeleteQueueMsg() +{ + PostMessage(m_hwndFTDialog, m_msgCheckDeleteQueue, 0, 0); +} + +FTDialog::CancelingDlg::CancelingDlg(FTDialog *pFTDlg) +{ + m_pFTDlg = pFTDlg; + m_hwndDlg = NULL; +} + +FTDialog::CancelingDlg::~CancelingDlg() +{ + destroy(); +} + +bool +FTDialog::CancelingDlg::create() +{ + if (m_hwndDlg != NULL) return false; + + m_hwndDlg = CreateDialogParam(GetModuleHandle(0), + MAKEINTRESOURCE(IDD_FTCANCELING), + NULL, + (DLGPROC) cancelingDlgProc, + (LONG) this); + + if (m_hwndDlg == NULL) return false; + + ShowWindow(m_hwndDlg, SW_SHOW); + DrawIcon(GetDC(m_hwndDlg), 15, 22, LoadIcon(NULL, IDI_QUESTION)); + UpdateWindow(m_hwndDlg); + + return true; +} + +bool +FTDialog::CancelingDlg::destroy() +{ + if (m_hwndDlg == NULL) return true; + + if (DestroyWindow(m_hwndDlg)) { + m_hwndDlg = NULL; + return true; + } else { + return false; + } +} + +bool +FTDialog::CancelingDlg::close(bool bResult) +{ + if (m_hwndDlg == NULL) return true; + + destroy(); + + m_pFTDlg->cancelTransfer(bResult); + + return false; +} + +BOOL CALLBACK +FTDialog::CancelingDlg::cancelingDlgProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) +{ + FTDialog::CancelingDlg *_this = (FTDialog::CancelingDlg *) GetWindowLong(hwnd, GWL_USERDATA); + switch (uMsg) + { + case WM_INITDIALOG: + SetWindowLong(hwnd, GWL_USERDATA, (LONG) lParam); + SetForegroundWindow(hwnd); + return TRUE; + + case WM_COMMAND: + switch (LOWORD(wParam)) + { + case IDOK: + _this->close(true); + return TRUE; + case IDCANCEL: + _this->close(false); + return TRUE; + } + break; + case WM_CLOSE: + _this->close(false); + return TRUE; + } + return FALSE; +} + +FTDialog::CreateFolderDlg::CreateFolderDlg(FTDialog *pFTDlg) : Dialog(GetModuleHandle(0)) +{ + m_pFTDlg = pFTDlg; + m_szFolderName[0] = '\0'; +} + +FTDialog::CreateFolderDlg::~CreateFolderDlg() +{ + +} + +bool +FTDialog::CreateFolderDlg::create() +{ + return showDialog(MAKEINTRESOURCE(IDD_FTCREATEFOLDER), m_pFTDlg->getWndHandle()); +} + +bool +FTDialog::CreateFolderDlg::onOk() +{ + strcpy(m_szFolderName, getItemString(IDC_FTFOLDERNAME)); + return true; +} + +FTDialog::RenameDlg::RenameDlg(FTDialog *pFTDlg) : CreateFolderDlg(pFTDlg) +{ + m_bFolder = false; +} + +FTDialog::RenameDlg::~RenameDlg() +{ +} + +bool +FTDialog::RenameDlg::create(char *pFilename, bool bFolder) +{ + m_bFolder = bFolder; + strcpy(m_szFilename, pFilename); + return showDialog(MAKEINTRESOURCE(IDD_FTCREATEFOLDER), m_pFTDlg->getWndHandle()); +} + +void +FTDialog::RenameDlg::initDialog() +{ + char buf[2*FT_FILENAME_SIZE]; + if (m_bFolder) { + SetWindowText(handle, "Rename Folder"); + sprintf(buf, "Rename Folder '%s'", m_szFilename); + } else { + SetWindowText(handle, "Rename File"); + sprintf(buf, "Rename File '%s'", m_szFilename); + } + + setItemString(IDC_FTTEXT, buf); + setItemString(IDC_FTFOLDERNAME, m_szFilename); + SendDlgItemMessage(handle, IDC_FTFOLDERNAME, EM_SETSEL, (WPARAM) 0, (LPARAM) -1); +} diff --git a/win/vncviewer/FTDialog.h b/win/vncviewer/FTDialog.h new file mode 100644 index 00000000..a4b55461 --- /dev/null +++ b/win/vncviewer/FTDialog.h @@ -0,0 +1,243 @@ +/* Copyright (C) 2005 TightVNC Team. All Rights Reserved. + * + * Developed by Dennis Syrovatsky + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + * + * TightVNC distribution homepage on the Web: http://www.tightvnc.com/ + * + */ + +// -=- FTDialog.h + +#ifndef __RFB_WIN32_FTDIALOG_H__ +#define __RFB_WIN32_FTDIALOG_H__ + +#include <windows.h> +#include <commctrl.h> + +#include <rfb/FileInfo.h> +#include <rfb_win32/Dialog.h> +#include <vncviewer/FileTransfer.h> +#include <vncviewer/FTListView.h> +#include <vncviewer/FTProgress.h> +#include <vncviewer/FTBrowseDlg.h> +#include <vncviewer/resource.h> + +namespace rfb { + namespace win32 { + class FileTransfer; + class FTBrowseDlg; + + class FTDialog + { + public: + FTDialog(HINSTANCE hInst, FileTransfer *pFT); + ~FTDialog(); + + bool createFTDialog(HWND hwndParent); + bool closeFTDialog(); + void destroyFTDialog(); + + void processDlgMsgs(); + + void cancelTransfer(bool bResult); + void afterCancelTransfer(); + + static BOOL CALLBACK FTDialogProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam); + + void addRemoteLVItems(FileInfo *pFI); + void reqFolderUnavailable(); + + void onEndBrowseDlg(bool bResult); + void getBrowseItems(char *pPath); + void addBrowseItems(FileInfo *pFI); + + void setStatusText(LPCSTR format,...); + + HWND getWndHandle() { return m_hwndFTDialog; } + + void postCheckDeleteQueueMsg(); + void postCheckTransferQueueMsg(); + void postUploadFilePortionMsg(); + void postDownloadFilePortionMsg(); + + char *getLocalPath() { return m_szLocalPath; } + char *getRemotePath() { return m_szRemotePath; } + + FTProgress *m_pProgress; + FileTransfer *m_pFileTransfer; + + private: + HWND m_hwndFTDialog; + HWND m_hwndLocalPath; + HWND m_hwndRemotePath; + HINSTANCE m_hInstance; + + void showLocalLVItems(); + void showRemoteLVItems(); + + void onClose(); + + void onLocalItemActivate(LPNMITEMACTIVATE lpnmia); + void onRemoteItemActivate(LPNMITEMACTIVATE lpnmia); + + void onLocalBrowse(); + void onRemoteBrowse(); + + void onLocalReload(); + void onRemoteReload(); + + void onLocalRButton(); + void onRemoteRButton(); + + bool getCreateFolderName(); + void onLocalCreateFolder(); + void onRemoteCreateFolder(); + + void showFTMenu(bool copyBtnState, bool createFldBtnState, + bool renameBtnState, bool deleteBtnState, + bool cancelBtnState); + void onFTMenuCommand(int command); + + void onUpload(); + void onDownload(); + + void onLocalRename(); + void onRemoteRename(); + + void onLocalDelete(); + void onRemoteDelete(); + + void onFTCancel(); + + void setIcon(int dest, int idIcon); + bool initFTDialog(); + bool initFTWndMsgs(); + + void onLocalOneUpFolder(); + void onRemoteOneUpFolder(); + int makeOneUpFolder(char *pPath); + + void refreshBtnState(); + void setButtonsState(); + + bool m_bDlgShown; + bool m_bLocalBrowsing; + bool m_bCloseDlgAfterCancel; + + UINT m_msgCheckDeleteQueue; + UINT m_msgCheckTransferQueue; + UINT m_msgUploadFilePortion; + UINT m_msgDownloadFilePortion; + + DWORD m_dwNumStatusStrings; + + FTListView *m_pLocalLV; + FTListView *m_pRemoteLV; + + FTBrowseDlg *m_pBrowseDlg; + + int m_FTMenuSource; + + char m_szLocalPath[FT_FILENAME_SIZE]; + char m_szRemotePath[FT_FILENAME_SIZE]; + char m_szLocalPathTmp[FT_FILENAME_SIZE]; + char m_szRemotePathTmp[FT_FILENAME_SIZE]; + char m_szCreateFolderName[FT_FILENAME_SIZE]; + + static const char szCheckDeleteQueueText[]; + static const char szCheckTransferQueueText[]; + static const char szUploadFilePortionText[]; + + typedef struct tagFTBUTTONSSTATE + { + bool uploadBtn; + bool downloadBtn; + bool createLocalFldBtn; + bool createRemoteFldBtn; + bool renameLocalBtn; + bool renameRemoteBtn; + bool deleteLocalBtn; + bool deleteRemoteBtn; + bool cancelBtn; + } FTBUTTONSSTATE; + + FTBUTTONSSTATE m_BtnState; + + public: + class CancelingDlg + { + public: + CancelingDlg(FTDialog *pFTDlg); + ~CancelingDlg(); + + static BOOL CALLBACK cancelingDlgProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam); + + bool create(); + bool destroy(); + + private: + FTDialog *m_pFTDlg; + HWND m_hwndDlg; + + bool close(bool bResult); + }; + + private: + CancelingDlg *m_pCancelingDlg; + + public: + class CreateFolderDlg : public Dialog + { + public: + CreateFolderDlg(FTDialog *pFTDlg); + ~CreateFolderDlg(); + + bool onOk(); + bool create(); + char *getFolderName() { return m_szFolderName; } + + protected: + FTDialog *m_pFTDlg; + char m_szFolderName[FT_FILENAME_SIZE]; + }; + + private: + CreateFolderDlg *m_pCreateFolderDlg; + + public: + class RenameDlg : public CreateFolderDlg + { + public: + RenameDlg(FTDialog *pFTDlg); + ~RenameDlg(); + + bool create(char *pFilename, bool bFolder); + void initDialog(); + + private: + bool m_bFolder; + char m_szFilename[FT_FILENAME_SIZE]; + }; + + private: + RenameDlg *m_pRenameDlg; + }; + } +} + +#endif // __RFB_WIN32_FTDIALOG_H__ diff --git a/win/vncviewer/FTListView.cxx b/win/vncviewer/FTListView.cxx new file mode 100644 index 00000000..8f41abf6 --- /dev/null +++ b/win/vncviewer/FTListView.cxx @@ -0,0 +1,211 @@ +/* Copyright (C) 2005 TightVNC Team. All Rights Reserved. + * + * Developed by Dennis Syrovatsky + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + * + * TightVNC distribution homepage on the Web: http://www.tightvnc.com/ + * + */ + +// -=- FTListView.cxx + +#include <vncviewer/FTListView.h> + +using namespace rfb; +using namespace rfb::win32; + +FTListView::FTListView(HWND hListView) +{ + m_bInitialized = false; + m_hListView = hListView; + m_fileInfo.free(); +} + +FTListView::~FTListView() +{ + m_fileInfo.free(); +} + + +bool +FTListView::initialize(HINSTANCE hInst) +{ + if (m_bInitialized) return false; + + initImageList(hInst); + + RECT Rect; + GetClientRect(m_hListView, &Rect); + Rect.right -= GetSystemMetrics(SM_CXHSCROLL); + int xwidth0 = (int) (0.35 * Rect.right); + int xwidth1 = (int) (0.22 * Rect.right); + int xwidth2 = (int) (0.43 * Rect.right); + + addColumn("Name", 0, xwidth0, LVCFMT_LEFT); + addColumn("Size", 1, xwidth1, LVCFMT_RIGHT); + addColumn("Data", 2, xwidth2, LVCFMT_LEFT); + + ListView_SetExtendedListViewStyleEx(m_hListView, LVS_EX_FULLROWSELECT, LVS_EX_FULLROWSELECT); + + return true; +} + +void +FTListView::onGetDispInfo(NMLVDISPINFO *pDI) +{ + if (m_fileInfo.getFlagsAt(pDI->item.iItem) & FT_ATTR_DIR) { + pDI->item.iImage = 0; + } else { + pDI->item.iImage = 1; + } + + switch (pDI->item.iSubItem) + { + case 0: + pDI->item.pszText = m_fileInfo.getNameAt(pDI->item.iItem); + break; + case 1: + { + unsigned int flags = m_fileInfo.getFlagsAt(pDI->item.iItem); + switch(flags & 0x000000FF) + { + case FT_ATTR_FILE: + { + char buf[32]; + unsigned int size = m_fileInfo.getSizeAt(pDI->item.iItem); + sprintf(buf, "%u", size); + pDI->item.pszText = buf; + } + break; + case FT_ATTR_DIR: + pDI->item.pszText = ""; + break; + default: + pDI->item.pszText = "Unspecified"; + } + } + break; + case 2: + { + unsigned int data = m_fileInfo.getDataAt(pDI->item.iItem); + if (data == 0) { + pDI->item.pszText = "Unspecified"; + } else { + FILETIME ft; + FolderManager fm; + fm.getFiletime(data, &ft); + + SYSTEMTIME st; + FileTimeToSystemTime(&ft, &st); + + char pDateTimeStr[1024]; + char timeFmt[128]; + GetLocaleInfo(LOCALE_USER_DEFAULT, LOCALE_STIMEFORMAT, timeFmt, 128); + char dateFmt[128]; + GetLocaleInfo(LOCALE_USER_DEFAULT, LOCALE_SSHORTDATE, dateFmt, 128); + + char timeStr[128]; + GetTimeFormat(LOCALE_USER_DEFAULT, TIME_NOSECONDS, &st, timeFmt, timeStr, 128); + char dateStr[128]; + GetDateFormat(LOCALE_USER_DEFAULT, 0, &st, dateFmt, dateStr, 128); + + sprintf(pDateTimeStr, "%s %s", dateStr, timeStr); + pDI->item.pszText = pDateTimeStr; + } + } + break; + default: + break; + } +} + +void +FTListView::addItems(FileInfo *pFI) +{ + m_fileInfo.add(pFI); + LVITEM LVItem; + LVItem.mask = LVIF_TEXT | LVIF_STATE | LVIF_IMAGE; + LVItem.state = 0; + LVItem.stateMask = 0; + for (unsigned int i = 0; i < m_fileInfo.getNumEntries(); i++) { + LVItem.iItem = i; + LVItem.iSubItem = 0; + LVItem.iImage = I_IMAGECALLBACK; + LVItem.pszText = LPSTR_TEXTCALLBACK; + ListView_InsertItem(m_hListView, &LVItem); + } +} + +void +FTListView::deleteAllItems() +{ + ListView_DeleteAllItems(m_hListView); + m_fileInfo.free(); +} + +char * +FTListView::getActivateItemName(LPNMITEMACTIVATE lpnmia) +{ + return m_fileInfo.getNameAt(lpnmia->iItem); +} + +int +FTListView::getSelectedItems(FileInfo *pFI) +{ + int selCount = ListView_GetSelectedCount(m_hListView); + int selItem = ListView_GetSelectionMark(m_hListView); + if ((selCount < 1) || (selItem < 0)) return -1; + + selItem = -1; + selItem = ListView_GetNextItem(m_hListView, selItem, LVNI_SELECTED); + do { + pFI->add(m_fileInfo.getFullDataAt(selItem)); + selItem = ListView_GetNextItem(m_hListView, selItem, LVNI_SELECTED); + } while (selItem >= 0); + + return selCount; +} + +void +FTListView::initImageList(HINSTANCE hInst) +{ + m_hImageList = ImageList_Create(16, 16, ILC_MASK, 2, 2); + + HICON hiconItem = LoadIcon(hInst, MAKEINTRESOURCE(IDI_FTDIR)); + ImageList_AddIcon(m_hImageList, hiconItem); + DestroyIcon(hiconItem); + + hiconItem = LoadIcon(hInst, MAKEINTRESOURCE(IDI_FTFILE)); + ImageList_AddIcon(m_hImageList, hiconItem); + DestroyIcon(hiconItem); + + ListView_SetImageList(m_hListView, m_hImageList, LVSIL_SMALL); +} + +void +FTListView::addColumn(char *iText, int iOrder, int xWidth, int alignFmt) +{ + LVCOLUMN lvc; + lvc.mask = LVCF_FMT | LVCF_WIDTH | LVCF_TEXT | LVCF_SUBITEM | LVCF_ORDER; + lvc.fmt = alignFmt; + lvc.iSubItem = iOrder; + lvc.pszText = iText; + lvc.cchTextMax = 32; + lvc.cx = xWidth; + lvc.iOrder = iOrder; + ListView_InsertColumn(m_hListView, iOrder, &lvc); +} diff --git a/win/vncviewer/FTListView.h b/win/vncviewer/FTListView.h new file mode 100644 index 00000000..c920fa00 --- /dev/null +++ b/win/vncviewer/FTListView.h @@ -0,0 +1,69 @@ +/* Copyright (C) 2005 TightVNC Team. All Rights Reserved. + * + * Developed by Dennis Syrovatsky. + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + * + * TightVNC distribution homepage on the Web: http://www.tightvnc.com/ + * + */ + +// -=- FTListView.h + +#ifndef __RFB_WIN32_FTLISTVIEW_H__ +#define __RFB_WIN32_FTLISTVIEW_H__ + +#include <windows.h> +#include <commctrl.h> + +#include <rfb/FileInfo.h> +#include <rfb_win32/FolderManager.h> +#include <vncviewer/resource.h> + +namespace rfb { + namespace win32{ + class FTListView + { + public: + FTListView(HWND hListView); + ~FTListView(); + + bool initialize(HINSTANCE hInst); + + void onGetDispInfo(NMLVDISPINFO *di); + void addItems(FileInfo *pFI); + void deleteAllItems(); + + char *getActivateItemName(LPNMITEMACTIVATE lpnmia); + int getSelectedItems(FileInfo *pFI); + + HWND getWndHandle() { return m_hListView; }; + + private: + HWND m_hListView; + FileInfo m_fileInfo; + HIMAGELIST m_hImageList; + + bool m_bInitialized; + + void addColumn(char *iText, int iOrder, int xWidth, int alignFmt); + void initImageList(HINSTANCE hInst); + + }; + } +} + +#endif // __RFB_WIN32_FTLISTVIEW_H__ diff --git a/win/vncviewer/FTProgress.cxx b/win/vncviewer/FTProgress.cxx new file mode 100644 index 00000000..e6cc952f --- /dev/null +++ b/win/vncviewer/FTProgress.cxx @@ -0,0 +1,151 @@ +/* Copyright (C) 2005 TightVNC Team. All Rights Reserved. + * + * Developed by Dennis Syrovatsky + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + * + * TightVNC distribution homepage on the Web: http://www.tightvnc.com/ + * + */ + +// -=- FTProgress.cxx + +#include <vncviewer/FTProgress.h> + +using namespace rfb; +using namespace rfb::win32; + +FTProgress::FTProgress(HWND hwndParent) +{ + m_bInitialized = false; + m_hwndParent = hwndParent; + + m_pSingleProgress = NULL; + m_pGeneralProgress = NULL; + + m_hwndSinglePercent = GetDlgItem(m_hwndParent, IDC_FTSINGLEPERCENT); + m_hwndGeneralPercent = GetDlgItem(m_hwndParent, IDC_FTGENERALPERCENT); + + m_bInitialized = createProgressBarObjects(); +} + +FTProgress::~FTProgress() +{ + destroyProgressBarObjects(); +} + +void +FTProgress::increase(DWORD value) +{ + if (!m_bInitialized) return; + + m_pSingleProgress->increase(value); + m_pGeneralProgress->increase(value); + + setProgressText(); +} + +void +FTProgress::clearAndInitGeneral(DWORD64 dw64MaxValue, DWORD64 dw64Position) +{ + if (!m_bInitialized) return; + + m_pGeneralProgress->clear(); + m_pGeneralProgress->init(dw64MaxValue, dw64Position); + + setProgressText(); +} + +void +FTProgress::clearAndInitSingle(DWORD dwMaxValue, DWORD dwPosition) +{ + if (!m_bInitialized) return; + + m_pSingleProgress->clear(); + m_pSingleProgress->init(dwMaxValue, dwPosition); + + setProgressText(); +} + +void +FTProgress::clearAll() +{ + if (!m_bInitialized) return; + + m_pSingleProgress->clear(); + m_pGeneralProgress->clear(); + + setProgressText(); +} + +bool +FTProgress::createProgressBarObjects() +{ + if ((m_pSingleProgress != NULL) || (m_pGeneralProgress != NULL)) { + return false; + } else { + HWND hwndSingleProgr = GetDlgItem(m_hwndParent, IDC_FTSINGLEPROGRESS); + HWND hwndGeneralProgr = GetDlgItem(m_hwndParent, IDC_FTGENERALPROGRESS); + + m_pSingleProgress = new ProgressControl(hwndSingleProgr); + if (m_pSingleProgress == NULL) return false; + + m_pGeneralProgress = new ProgressControl(hwndGeneralProgr); + if (m_pGeneralProgress == NULL) { + delete m_pSingleProgress; + m_pSingleProgress = NULL; + return false; + } + } + return true; +} + +bool +FTProgress::destroyProgressBarObjects() +{ + clearAll(); + + if (m_pSingleProgress != NULL) { + delete m_pSingleProgress; + m_pSingleProgress = NULL; + } + + if (m_pGeneralProgress != NULL) { + delete m_pGeneralProgress; + m_pGeneralProgress = NULL; + } + + return true; +} + +void +FTProgress::setProgressText() +{ + char buf[16] = {0}; + char buf2[16] = {0}; + + int percent = m_pSingleProgress->getCurrentPercent(); + sprintf(buf, "%d%%", percent); + GetWindowText(m_hwndSinglePercent, buf2, 16); + if (strcmp(buf, buf2) != 0) + SetWindowText(m_hwndSinglePercent, buf); + + percent = m_pGeneralProgress->getCurrentPercent(); + sprintf(buf, "%d%%", percent); + GetWindowText(m_hwndGeneralPercent, buf2, 16); + if (strcmp(buf, buf2) != 0) + SetWindowText(m_hwndGeneralPercent, buf); +}
\ No newline at end of file diff --git a/win/vncviewer/FTProgress.h b/win/vncviewer/FTProgress.h new file mode 100644 index 00000000..caad7798 --- /dev/null +++ b/win/vncviewer/FTProgress.h @@ -0,0 +1,66 @@ +/* Copyright (C) 2005 TightVNC Team. All Rights Reserved. + * + * Developed by Dennis Syrovatsky + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + * + * TightVNC distribution homepage on the Web: http://www.tightvnc.com/ + * + */ + +// -=- FTProgress.h + +#ifndef __RFB_WIN32_FTPROGRESS_H__ +#define __RFB_WIN32_FTPROGRESS_H__ + +#include <windows.h> +#include <commctrl.h> +#include <stdio.h> + +#include <rfb_win32/ProgressControl.h> +#include <vncviewer/resource.h> + +namespace rfb { + namespace win32 { + class FTProgress + { + public: + FTProgress(HWND hwndParent); + ~FTProgress(); + + void increase(DWORD value); + void clearAndInitGeneral(DWORD64 dw64MaxValue, DWORD64 dw64Position); + void clearAndInitSingle(DWORD dwMaxValue, DWORD dwPosition); + void clearAll(); + + private: + ProgressControl *m_pSingleProgress; + ProgressControl *m_pGeneralProgress; + + HWND m_hwndParent; + HWND m_hwndSinglePercent; + HWND m_hwndGeneralPercent; + + bool m_bInitialized; + + void setProgressText(); + bool createProgressBarObjects(); + bool destroyProgressBarObjects(); + }; + } +} + +#endif // __RFB_WIN32_FTPROGRESS_H__ diff --git a/win/vncviewer/FileTransfer.cxx b/win/vncviewer/FileTransfer.cxx new file mode 100644 index 00000000..cf57072f --- /dev/null +++ b/win/vncviewer/FileTransfer.cxx @@ -0,0 +1,803 @@ +/* Copyright (C) 2005 TightVNC Team. All Rights Reserved. + * + * Developed by Dennis Syrovatsky. + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + * + * TightVNC distribution homepage on the Web: http://www.tightvnc.com/ + * + */ + +// -=- FileTransfer.cxx + +#include <vncviewer/FileTransfer.h> + +using namespace rfb; +using namespace rfb::win32; + +FileTransfer::FileTransfer() +{ + m_bFTDlgShown = false; + m_bInitialized = false; + m_bResized = false; + m_bCancel = false; + m_bOverwriteAll = false; + + m_pFTDialog = new FTDialog(GetModuleHandle(0), this); + + m_pReader = NULL; + m_pWriter = NULL; + + m_dw64SizeSending = 0; + m_dirSizeRqstNum = 0; +} + +FileTransfer::~FileTransfer() +{ + if (m_pFTDialog != NULL) { + delete m_pFTDialog; + m_pFTDialog = NULL; + } + + if (m_pReader != NULL) { + delete m_pReader; + m_pReader = NULL; + } + + if (m_pWriter != NULL) { + delete m_pWriter; + m_pWriter = NULL; + } + + freeQueues(); +} + +bool +FileTransfer::initialize(rdr::InStream *pIS, rdr::OutStream *pOS) +{ + if (m_bInitialized) return false; + + m_pReader = new CFTMsgReader(pIS); + m_pWriter = new CFTMsgWriter(pOS); + + freeQueues(); + + m_bInitialized = true; + return true; +} + +bool +FileTransfer::show(HWND hwndParent) +{ + if (!m_bInitialized) return false; + + m_bFTDlgShown = m_pFTDialog->createFTDialog(hwndParent); + + return m_bFTDlgShown; +} + +bool +FileTransfer::processFTMsg(int type) +{ + if (!m_bInitialized) return false; + + switch (type) + { + case msgTypeFileListData: + return procFileListDataMsg(); + case msgTypeFileDownloadData: + return procFileDownloadDataMsg(); + case msgTypeFileUploadCancel: + return procFileUploadCancelMsg(); + case msgTypeFileDownloadFailed: + return procFileDownloadFailedMsg(); + case msgTypeFileDirSizeData: + return procFileDirSizeDataMsg(); + case msgTypeFileLastRequestFailed: + return procFileLastRqstFailedMsg(); + default: + return false; + } +} + +bool +FileTransfer::isTransferEnable() +{ + if (m_TransferQueue.getNumEntries() > 0) return true; else return false; +} + +void +FileTransfer::addDeleteQueue(char *pPathPrefix, FileInfo *pFI, unsigned int flags) +{ + if ((m_bFTDlgShown) && (m_DeleteQueue.getNumEntries() > 0)) + m_pFTDialog->setStatusText("Starting Delete Operation"); + + m_DeleteQueue.add(pPathPrefix, pPathPrefix, pFI, flags); + + checkDeleteQueue(); +} + +void +FileTransfer::checkDeleteQueue() +{ + if (m_DeleteQueue.getNumEntries() > 0) { + if (m_bFTDlgShown) + m_pFTDialog->setStatusText("Delete Operation Executing: %s", m_DeleteQueue.getFullLocPathAt(0)); + if (m_DeleteQueue.getFlagsAt(0) & FT_ATTR_DELETE_LOCAL) { + FolderManager fm; + if (!fm.deleteIt(m_DeleteQueue.getFullLocPathAt(0))) { + if (m_bFTDlgShown) m_pFTDialog->setStatusText("Delete Operation Failed"); + } else { + if (m_bFTDlgShown) m_pFTDialog->setStatusText("Delete Operation Completed"); + } + m_DeleteQueue.deleteAt(0); + m_pFTDialog->postCheckDeleteQueueMsg(); + } else { + if (m_DeleteQueue.getFlagsAt(0) & FT_ATTR_DELETE_REMOTE) { + writeFileDeleteRqst(strlen(m_DeleteQueue.getFullLocPathAt(0)), + m_DeleteQueue.getFullLocPathAt(0)); + + char *pPath = m_DeleteQueue.getLocPathAt(0); + m_queueFileListRqst.add(pPath, 0, 0, FT_FLR_DEST_DELETE); + writeFileListRqst(strlen(pPath), pPath, false); + } + } + } else { + if (m_bFTDlgShown) { + m_pFTDialog->setStatusText("Delete Operation Completed Successfully"); + PostMessage(m_pFTDialog->getWndHandle(), WM_COMMAND, MAKEWPARAM(IDC_FTLOCALRELOAD, 0), 0); + PostMessage(m_pFTDialog->getWndHandle(), WM_COMMAND, MAKEWPARAM(IDC_FTREMOTERELOAD, 0), 0); + } + } +} + +void +FileTransfer::addTransferQueue(char *pLocalPath, char *pRemotePath, + FileInfo *pFI, unsigned int flags) +{ + if (!isTransferEnable()) { + if (m_bFTDlgShown) m_pFTDialog->setStatusText("Starting Copy Operation"); + m_bTransferSuccess = true; + } + + m_TransferQueue.add(pLocalPath, pRemotePath, pFI, (flags | FT_ATTR_RESIZE_NEEDED)); + + checkTransferQueue(); +} + +bool +FileTransfer::resizeSending() +{ + for (unsigned int i = 0; i < m_TransferQueue.getNumEntries(); i++) { + unsigned int flags = m_TransferQueue.getFlagsAt(i); + if (flags & FT_ATTR_RESIZE_NEEDED) { + if (flags & FT_ATTR_FILE) { + m_bResized = true; + m_dw64SizeSending += m_TransferQueue.getSizeAt(i); + m_TransferQueue.clearFlagAt(i, FT_ATTR_RESIZE_NEEDED); + } else { + if (flags & FT_ATTR_DIR) { + if (flags & FT_ATTR_COPY_DOWNLOAD) { + m_bResized = true; + char *pPath = m_TransferQueue.getFullRemPathAt(i); + m_dirSizeRqstNum = i; + writeFileDirSizeRqst(strlen(pPath), pPath); + return false; + } else { + if (flags & FT_ATTR_COPY_UPLOAD) { + FolderManager fm; + DWORD64 dw64Size; + m_bResized = true; + fm.getDirSize(m_TransferQueue.getFullLocPathAt(i), &dw64Size); + m_dw64SizeSending += dw64Size; + m_TransferQueue.clearFlagAt(i, FT_ATTR_RESIZE_NEEDED); + } + } // if (flags & FT_ATTR_COPY_DOWNLOAD) + } // if (flags & FT_ATTR_FOLDER) + } // if (flags & FT_ATTR_FILE) + } // if (flags & FT_ATTR_NEEDED_RESIZE) + } // for (unsigned int i = 0; i < m_TransferQueue.getNumEntries(); i++) + + if ((m_bFTDlgShown) && (m_bResized)) { + m_pFTDialog->m_pProgress->clearAndInitGeneral(m_dw64SizeSending, 0); + m_bResized = false; + } + + return true; +} + +void +FileTransfer::checkTransferQueue() +{ + if (!isTransferEnable()) { + if (m_bFTDlgShown) { + m_pFTDialog->m_pProgress->clearAll(); + m_dw64SizeSending = 0; + m_bResized = false; + + if (m_bTransferSuccess) + m_pFTDialog->setStatusText("File Transfer Operation Completed Successfully"); + else + m_pFTDialog->setStatusText("File Transfer Operation Completed"); + + m_pFTDialog->afterCancelTransfer(); + PostMessage(m_pFTDialog->getWndHandle(), WM_COMMAND, MAKEWPARAM(IDC_FTLOCALRELOAD, 0), 0); + PostMessage(m_pFTDialog->getWndHandle(), WM_COMMAND, MAKEWPARAM(IDC_FTREMOTERELOAD, 0), 0); + return; + } + } else { + if (!resizeSending()) return; + + unsigned int flag0 = m_TransferQueue.getFlagsAt(0); + + if (flag0 & FT_ATTR_COPY_UPLOAD) { + if (flag0 & FT_ATTR_FILE) { + uploadFile(); + return; + } + if (flag0 & FT_ATTR_DIR) { + char *pFullPath = m_TransferQueue.getFullRemPathAt(0); + if (m_bFTDlgShown) m_pFTDialog->setStatusText("Creating Remote Folder. %s", pFullPath); + writeFileCreateDirRqst(strlen(pFullPath), pFullPath); + + char *pPath = m_TransferQueue.getRemPathAt(0); + m_TransferQueue.setFlagsAt(0, (flag0 | FT_ATTR_FLR_UPLOAD_CHECK)); + m_queueFileListRqst.add(pPath, 0, 0, FT_FLR_DEST_UPLOAD); + writeFileListRqst(strlen(pPath), pPath, false); + return; + } + } else { + if (flag0 & FT_ATTR_COPY_DOWNLOAD) { + if (flag0 & FT_ATTR_FILE) { + downloadFile(); + return; + } + if (flag0 & FT_ATTR_DIR) { + FolderManager fm; + char *pLocPath = m_TransferQueue.getFullLocPathAt(0); + if (m_bFTDlgShown) m_pFTDialog->setStatusText("Creating Local Folder. %s", pLocPath); + + if (!fm.createDir(pLocPath)) { + if (m_bFTDlgShown) m_pFTDialog->setStatusText("Creating Local Folder Failed."); + m_bTransferSuccess = false; + m_TransferQueue.deleteAt(0); + m_pFTDialog->postCheckTransferQueueMsg(); + return; + } else { + if ((m_bFTDlgShown) && (strcmp(m_TransferQueue.getLocPathAt(0), m_pFTDialog->getLocalPath()) == 0)) + PostMessage(m_pFTDialog->getWndHandle(), WM_COMMAND, MAKEWPARAM(IDC_FTLOCALRELOAD, 0), 0); + + m_TransferQueue.setFlagsAt(0, (m_TransferQueue.getFlagsAt(0) | FT_ATTR_FLR_DOWNLOAD_ADD)); + char *pRemPath = m_TransferQueue.getFullRemPathAt(0); + m_queueFileListRqst.add(pRemPath, 0, 0, FT_FLR_DEST_DOWNLOAD); + writeFileListRqst(strlen(pRemPath), pRemPath, 0); + return; + } + } + } + } + m_bTransferSuccess = false; + if (m_bFTDlgShown) m_pFTDialog->setStatusText("File Transfer Operation Failed. Unknown data in the transfer queue"); + } // if (!isTransferEnable()) +} + +bool +FileTransfer::uploadFile() +{ + if (m_TransferQueue.getFlagsAt(0) & FT_ATTR_FILE) { + if (m_fileReader.create(m_TransferQueue.getFullLocPathAt(0))) { + + if (m_bFTDlgShown) { + m_pFTDialog->setStatusText("Upload Started: %s to %s", + m_TransferQueue.getFullLocPathAt(0), + m_TransferQueue.getFullRemPathAt(0)); + m_pFTDialog->m_pProgress->clearAndInitSingle(m_TransferQueue.getSizeAt(0), 0); + } + + writeFileUploadRqst(strlen(m_TransferQueue.getFullRemPathAt(0)), + m_TransferQueue.getFullRemPathAt(0), 0); + uploadFilePortion(); + } + } + return false; +} + +bool +FileTransfer::downloadFile() +{ + if (m_TransferQueue.getFlagsAt(0) & FT_ATTR_FILE) { + if (m_fileWriter.create(m_TransferQueue.getFullLocPathAt(0))) { + if (m_bFTDlgShown) { + m_pFTDialog->setStatusText("Download Started: %s to %s", + m_TransferQueue.getFullRemPathAt(0), + m_TransferQueue.getFullLocPathAt(0)); + m_pFTDialog->m_pProgress->clearAndInitSingle(m_TransferQueue.getSizeAt(0), 0); + } + writeFileDownloadRqst(strlen(m_TransferQueue.getFullRemPathAt(0)), + m_TransferQueue.getFullRemPathAt(0), 0); + return true; + } else return false; + } + return false; +} + +void +FileTransfer::uploadFilePortion() +{ + if (checkCancelOperations()) { + char reason[] = "The user cancel transfer"; + m_pWriter->writeFileUploadFailed(strlen(reason), reason); + } + + if (m_fileReader.isCreated()) { + char buf[FT_MAX_SENDING_SIZE]; + unsigned int bytesRead = 0; + if (m_fileReader.read((void *)buf, FT_MAX_SENDING_SIZE, &bytesRead)) { + if (bytesRead == 0) { + m_pWriter->writeFileUploadData(m_TransferQueue.getDataAt(0)); + m_fileReader.close(); + if (m_bFTDlgShown) { + m_pFTDialog->m_pProgress->clearAndInitSingle(0, 0); + m_pFTDialog->setStatusText("Upload Completed"); + } + m_TransferQueue.deleteAt(0); + m_pFTDialog->postCheckTransferQueueMsg(); + } else { + if (m_bFTDlgShown) m_pFTDialog->m_pProgress->increase(bytesRead); + m_pWriter->writeFileUploadData(bytesRead, (char *)buf); + m_pFTDialog->postUploadFilePortionMsg(); + } + } else { + m_fileReader.close(); + m_bTransferSuccess = false; + char reason[] = "Error While Reading File"; + m_pWriter->writeFileUploadFailed(strlen(reason), reason); + if (m_bFTDlgShown) { + m_pFTDialog->m_pProgress->clearAndInitSingle(0, 0); + m_pFTDialog->setStatusText("Upload Failed"); + } + m_TransferQueue.deleteAt(0); + m_pFTDialog->postCheckTransferQueueMsg(); + } + } +} + +void +FileTransfer::createRemoteFolder(char *pPath, char *pName) +{ + char fullPath[FT_FILENAME_SIZE]; + sprintf(fullPath, "%s\\%s", pPath, pName); + m_pFTDialog->setStatusText("Creating Remote Folder: %s", fullPath); + writeFileCreateDirRqst(strlen(fullPath), fullPath); + requestFileList(pPath, FT_FLR_DEST_MAIN, false); +} + +void +FileTransfer::renameRemote(char *pPath, char *pOldName, char *pNewName) +{ + char fullOldName[FT_FILENAME_SIZE]; + char fullNewName[FT_FILENAME_SIZE]; + + sprintf(fullOldName, "%s\\%s", pPath, pOldName); + sprintf(fullNewName, "%s\\%s", pPath, pNewName); + + writeFileRenameRqst(strlen(fullOldName), strlen(fullNewName), + fullOldName, fullNewName); + requestFileList(pPath, FT_FLR_DEST_MAIN, false); +} + +bool +FileTransfer::procFileListDataMsg() +{ + FileInfo fileInfo; + int res = m_pReader->readFileListData(&fileInfo); + + bool bResult; + switch (m_queueFileListRqst.getFlagsAt(0)) + { + case FT_FLR_DEST_MAIN: + if (!m_bFTDlgShown) break; + + if (res < 0) { + m_pFTDialog->reqFolderUnavailable(); + bResult = true; + } else { + bResult = procFLRMain(&fileInfo); + } + break; + case FT_FLR_DEST_BROWSE: + bResult = procFLRBrowse(&fileInfo); + break; + case FT_FLR_DEST_UPLOAD: + bResult = procFLRUpload(&fileInfo); + break; + case FT_FLR_DEST_DOWNLOAD: + bResult = procFLRDownload(&fileInfo); + break; + case FT_FLR_DEST_DELETE: + bResult = procFLRDelete(&fileInfo); + break; + case FT_FLR_DEST_RENAME: + bResult = procFLRRename(&fileInfo); + break; + } + m_queueFileListRqst.deleteAt(0); + return bResult; +} + +bool +FileTransfer::procFileDownloadDataMsg() +{ + unsigned int bufSize = 0; + unsigned int modTime = 0; + + void *pFile = m_pReader->readFileDownloadData(&bufSize, &modTime); + + if (checkCancelOperations()) { + char reason[] = "The user cancel transfer"; + m_pWriter->writeFileDownloadCancel(strlen(reason), reason); + } + + if ((!m_fileWriter.isCreated()) || (!isTransferEnable())) { + m_bTransferSuccess = false; + if (pFile != NULL) delete [] pFile; + return false; + } + + if (bufSize > 0) { + unsigned int bytesWritten = 0; + m_fileWriter.write(pFile, bufSize, &bytesWritten); + delete [] pFile; + if (bytesWritten != bufSize) { + m_bTransferSuccess = false; + char reason[] = "Error File Writting to File"; + m_pWriter->writeFileDownloadCancel(strlen(reason), reason); + if (m_bFTDlgShown) { + m_pFTDialog->setStatusText("Download Failed"); + m_pFTDialog->m_pProgress->clearAndInitSingle(0, 0); + } + m_TransferQueue.deleteAt(0); + m_pFTDialog->postCheckTransferQueueMsg(); + return false; + } else { + if (m_bFTDlgShown) { + m_pFTDialog->m_pProgress->increase(bufSize); + } + } + return true; + } else { + if (modTime != 0) { + m_fileWriter.setTime(modTime); + m_fileWriter.close(); + if (m_bFTDlgShown) { + m_pFTDialog->setStatusText("Download Completed"); + m_pFTDialog->m_pProgress->clearAndInitSingle(0, 0); + } + m_TransferQueue.deleteAt(0); + m_pFTDialog->postCheckTransferQueueMsg(); + return true; + } else { + m_fileWriter.close(); + m_bTransferSuccess = false; + char reason[] = "Error File Writting"; + if (m_bFTDlgShown) { + m_pFTDialog->setStatusText("Download Failed"); + m_pFTDialog->m_pProgress->clearAndInitSingle(0, 0); + } + m_pWriter->writeFileDownloadCancel(strlen(reason), reason); + m_TransferQueue.deleteAt(0); + m_pFTDialog->postCheckTransferQueueMsg(); + } + } + return false; +} + +bool +FileTransfer::procFileUploadCancelMsg() +{ + unsigned int reasonSize = 0; + char *pReason = m_pReader->readFileUploadCancel(&reasonSize); + + if (m_bFTDlgShown) { + m_pFTDialog->setStatusText("Upload Canceled by Remote Computer : %s", pReason); + } + endUndoneOperation(); + m_pFTDialog->postCheckTransferQueueMsg(); + + delete [] pReason; + return true; +} + +bool +FileTransfer::procFileDownloadFailedMsg() +{ + unsigned int reasonSize = 0; + char *pReason = m_pReader->readFileDownloadFailed(&reasonSize); + + if (m_bFTDlgShown) { + m_pFTDialog->setStatusText("Download Failed by Remote Computer : %s", pReason); + } + endUndoneOperation(); + m_pFTDialog->postCheckTransferQueueMsg(); + + delete [] pReason; + return true; +} + +bool +FileTransfer::procFileDirSizeDataMsg() +{ + DWORD64 dw64DirSize = 0; + unsigned short dirSizeLow16 = 0; + unsigned int dirSizeHigh32 = 0; + m_pReader->readFileDirSizeData(&dirSizeLow16, &dirSizeHigh32); + + dw64DirSize = dirSizeLow16; + dw64DirSize = (dw64DirSize << 32) + dirSizeHigh32; + + m_dw64SizeSending += dw64DirSize; + m_TransferQueue.clearFlagAt(m_dirSizeRqstNum, FT_ATTR_RESIZE_NEEDED); + checkTransferQueue(); + return true; +} + +bool +FileTransfer::procFileLastRqstFailedMsg() +{ + unsigned int reasonSize = 0; + int requestType; + char *pReason = m_pReader->readFileLastRqstFailed(&requestType, &reasonSize); + delete [] pReason; + return true; +} + +bool +FileTransfer::procFLRMain(FileInfo *pFI) +{ + if (m_bFTDlgShown) m_pFTDialog->addRemoteLVItems(pFI); + return true; +} + +bool +FileTransfer::procFLRBrowse(FileInfo *pFI) +{ + m_pFTDialog->addBrowseItems(pFI); + return false; +} + +bool +FileTransfer::procFLRUpload(FileInfo *pFI) +{ + unsigned int flags = m_TransferQueue.getFlagsAt(0); + if (flags & FT_ATTR_FLR_UPLOAD_CHECK) { + int num = isExistName(pFI, m_TransferQueue.getRemNameAt(0)); + if (num >= 0) { + if ((m_bFTDlgShown) && (strcmp(m_TransferQueue.getRemPathAt(0), m_pFTDialog->getRemotePath()) == 0)) { + m_pFTDialog->addRemoteLVItems(pFI); + } + } else { + if (flags & FT_ATTR_DIR) { + m_TransferQueue.deleteAt(0); + m_bTransferSuccess = false; + if (m_bFTDlgShown) m_pFTDialog->setStatusText("Create Remote Folder Failed."); + } + } + } + FolderManager fm; + FileInfo fi; + flags = m_TransferQueue.getFlagsAt(0); + if (flags & FT_ATTR_FILE) { + uploadFile(); + return true; + } else { + if (fm.getDirInfo(m_TransferQueue.getFullLocPathAt(0), &fi, 0)) { + m_TransferQueue.add(m_TransferQueue.getFullLocPathAt(0), + m_TransferQueue.getFullRemPathAt(0), + &fi, FT_ATTR_COPY_UPLOAD); + } + } + m_TransferQueue.deleteAt(0); + m_pFTDialog->postCheckTransferQueueMsg(); + return true; +} + +bool +FileTransfer::procFLRDownload(FileInfo *pFI) +{ + unsigned int flags = m_TransferQueue.getFlagsAt(0); + + if ((flags & FT_ATTR_DIR) && (flags & FT_ATTR_FLR_DOWNLOAD_ADD)) { + m_TransferQueue.add(m_TransferQueue.getFullLocPathAt(0), + m_TransferQueue.getFullRemPathAt(0), + pFI, FT_ATTR_COPY_DOWNLOAD); + m_TransferQueue.deleteAt(0); + m_pFTDialog->postCheckTransferQueueMsg(); + return true; + } else { + m_bTransferSuccess = false; + if (m_bFTDlgShown) m_pFTDialog->setStatusText("File Transfer Operation Failed: Unknown data from server."); + } + return false; +} + +bool +FileTransfer::procFLRDelete(FileInfo *pFI) +{ + if (isExistName(pFI, m_DeleteQueue.getLocNameAt(0)) >= 0) { + if (m_bFTDlgShown) m_pFTDialog->setStatusText("Delete Operation Failed."); + } else { + if (m_bFTDlgShown) m_pFTDialog->setStatusText("Delete Operation Completed."); + } + m_DeleteQueue.deleteAt(0); + checkDeleteQueue(); + return true; +} + +bool +FileTransfer::procFLRRename(FileInfo *pFI) +{ + return false; +} + +void +FileTransfer::requestFileList(char *pPath, int dest, bool bDirOnly) +{ + m_queueFileListRqst.add(pPath, 0, 0, dest); + + writeFileListRqst(strlen(pPath), pPath, bDirOnly); +} + +int +FileTransfer::isExistName(FileInfo *pFI, char *pName) +{ + for (unsigned int i = 0; i < pFI->getNumEntries(); i++) { + if (strcmp(pFI->getNameAt(i), pName) == 0) { + return i; + } + } + return -1; +} + +bool +FileTransfer::checkCancelOperations() +{ + if (m_bFTDlgShown) m_pFTDialog->processDlgMsgs(); + if (m_bCancel) { + endUndoneOperation(); + if (m_bFTDlgShown) { + m_pFTDialog->setStatusText("All Operations Canceled"); + } + return true; + } else { + return false; + } +} + +void +FileTransfer::endUndoneOperation() +{ + m_bCancel = false; + m_bTransferSuccess = false; + m_fileReader.close(); + m_fileWriter.close(); + freeQueues(); + m_dw64SizeSending = 0; + m_pFTDialog->m_pProgress->clearAll(); +} + +void +FileTransfer::freeQueues() +{ + m_TransferQueue.free(); + m_DeleteQueue.free(); + m_queueFileListRqst.free(); +} + +int +FileTransfer::convertToUnixPath(char *path) +{ + int len = strlen(path); + if (len >= FT_FILENAME_SIZE) return -1; + if (len == 0) {strcpy(path, "/"); return 1;} + for (int i = (len - 1); i >= 0; i--) { + if (path[i] == '\\') path[i] = '/'; + path[i+1] = path[i]; + } + path[len + 1] = '\0'; + path[0] = '/'; + return strlen(path); +} + +bool +FileTransfer::writeFileListRqst(unsigned short dirnameLen, char *pDirName, bool bDirOnly) +{ + char dirName[FT_FILENAME_SIZE]; + strcpy(dirName, pDirName); + int len = convertToUnixPath(dirName); + if (len <= 0) return false; + + return m_pWriter->writeFileListRqst(len, dirName, bDirOnly); +} + +bool +FileTransfer::writeFileDownloadRqst(unsigned short filenameLen, char *pFilename, + unsigned int position) +{ + char filename[FT_FILENAME_SIZE]; + strcpy(filename, pFilename); + unsigned short len = (unsigned short) convertToUnixPath(filename); + if (len <= 0) return false; + + return m_pWriter->writeFileDownloadRqst(len, filename, position); +} + +bool +FileTransfer::writeFileUploadRqst(unsigned short filenameLen, char *pFilename, + unsigned int position) +{ + char filename[FT_FILENAME_SIZE]; + strcpy(filename, pFilename); + unsigned short len = (unsigned short) convertToUnixPath(filename); + if (len <= 0) return false; + + return m_pWriter->writeFileUploadRqst(len, filename, position); +} + +bool +FileTransfer::writeFileCreateDirRqst(unsigned short dirNameLen, char *pDirName) +{ + char path[FT_FILENAME_SIZE]; + strcpy(path, pDirName); + int nameLen = convertToUnixPath(path); + + return m_pWriter->writeFileCreateDirRqst(nameLen, path); +} + +bool +FileTransfer::writeFileDirSizeRqst(unsigned short dirNameLen, char *pDirName) +{ + char path[FT_FILENAME_SIZE]; + strcpy(path, pDirName); + int nameLen = convertToUnixPath(path); + + return m_pWriter->writeFileDirSizeRqst(nameLen, path); +} + +bool +FileTransfer::writeFileRenameRqst(unsigned short oldNameLen, unsigned short newNameLen, + char *pOldName, char *pNewName) +{ + char oldName[FT_FILENAME_SIZE]; + char newName[FT_FILENAME_SIZE]; + + strcpy(oldName, pOldName); + strcpy(newName, pNewName); + + int _oldNameLen = convertToUnixPath(oldName); + int _newNameLen = convertToUnixPath(newName); + + return m_pWriter->writeFileRenameRqst(_oldNameLen, _newNameLen, oldName, newName); +} + +bool +FileTransfer::writeFileDeleteRqst(unsigned short nameLen, char *pName) +{ + char path[FT_FILENAME_SIZE]; + strcpy(path, pName); + int _nameLen = convertToUnixPath(path); + + return m_pWriter->writeFileDeleteRqst(_nameLen, path); +} diff --git a/win/vncviewer/FileTransfer.h b/win/vncviewer/FileTransfer.h new file mode 100644 index 00000000..1bfded2c --- /dev/null +++ b/win/vncviewer/FileTransfer.h @@ -0,0 +1,136 @@ +/* Copyright (C) 2005 TightVNC Team. All Rights Reserved. + * + * Developed by Dennis Syrovatsky + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + * + * TightVNC distribution homepage on the Web: http://www.tightvnc.com/ + * + */ + +// -=- FileTransfer.h + +#ifndef __RFB_WIN32_FILETRANSFER_H__ +#define __RFB_WIN32_FILETRANSFER_H__ + +#include <rdr/InStream.h> +#include <rdr/OutStream.h> +#include <rfb/msgTypes.h> +#include <rfb/FileInfo.h> +#include <rfb/FileReader.h> +#include <rfb/FileWriter.h> +#include <rfb/TransferQueue.h> +#include <rfb/CFTMsgReader.h> +#include <rfb/CFTMsgWriter.h> +#include <vncviewer/FTDialog.h> + +namespace rfb { + namespace win32 { + class FTDialog; + + class FileTransfer + { + public: + FileTransfer(); + ~FileTransfer(); + + bool initialize(rdr::InStream *pIS, rdr::OutStream *pOS); + bool processFTMsg(int type); + bool show(HWND hwndParent); + + void requestFileList(char *pPath, int dest, bool bDirOnly); + + void addTransferQueue(char *pLocalPath, char *pRemotePath, + FileInfo *pFI, unsigned int flags); + void addDeleteQueue(char *pPathPrefix, FileInfo *pFI, + unsigned int flags); + + bool isTransferEnable(); + + void checkTransferQueue(); + void checkDeleteQueue(); + bool checkCancelOperations(); + + void uploadFilePortion(); + + void createRemoteFolder(char *pPath, char *pName); + void renameRemote(char *pPath, char *pOldName, char *pNewName); + + bool m_bCancel; + + private: + bool m_bFTDlgShown; + bool m_bInitialized; + bool m_bResized; + bool m_bTransferSuccess; + bool m_bOverwriteAll; + + FTDialog *m_pFTDialog; + + rfb::CFTMsgReader *m_pReader; + rfb::CFTMsgWriter *m_pWriter; + + FileReader m_fileReader; + FileWriter m_fileWriter; + + FileInfo m_queueFileListRqst; + + TransferQueue m_TransferQueue; + TransferQueue m_DeleteQueue; + + bool resizeSending(); + bool uploadFile(); + bool downloadFile(); + + int isExistName(FileInfo *pFI, char *pName); + void freeQueues(); + + void endUndoneOperation(); + + bool procFileListDataMsg(); + bool procFileDownloadDataMsg(); + bool procFileUploadCancelMsg(); + bool procFileDownloadFailedMsg(); + bool procFileDirSizeDataMsg(); + bool procFileLastRqstFailedMsg(); + + bool procFLRMain(FileInfo *pFI); + bool procFLRBrowse(FileInfo *pFI); + bool procFLRUpload(FileInfo *pFI); + bool procFLRDownload(FileInfo *pFI); + bool procFLRDelete(FileInfo *pFI); + bool procFLRRename(FileInfo *pFI); + + int convertToUnixPath(char *path); + + bool writeFileListRqst(unsigned short dirnameLen, char *pDirName, bool bDirOnly); + bool writeFileDownloadRqst(unsigned short filenameLen, char *pFilename, + unsigned int position); + bool writeFileUploadRqst(unsigned short filenameLen, char *pFilename, + unsigned int position); + bool writeFileCreateDirRqst(unsigned short dirNameLen, char *pDirName); + bool writeFileDirSizeRqst(unsigned short dirNameLen, char *pDirName); + bool writeFileRenameRqst(unsigned short oldNameLen, unsigned short newNameLen, + char *pOldName, char *pNewName); + bool writeFileDeleteRqst(unsigned short nameLen, char *pName); + + DWORD64 m_dw64SizeSending; + unsigned int m_dirSizeRqstNum; + }; + } +} + +#endif // __RFB_WIN32_FILETRANSFER_H__ diff --git a/win/vncviewer/InfoDialog.cxx b/win/vncviewer/InfoDialog.cxx new file mode 100644 index 00000000..e74896dc --- /dev/null +++ b/win/vncviewer/InfoDialog.cxx @@ -0,0 +1,65 @@ +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ +#include <vncviewer/InfoDialog.h> +#include <vncviewer/resource.h> +#include <vncviewer/CConn.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(CConn* cc) { + conn = cc; + return Dialog::showDialog(MAKEINTRESOURCE(IDD_CONNECTION_INFO)); +} + +void InfoDialog::initDialog() { + char buf[256]; + + setItemString(IDC_INFO_NAME, TStr(conn->cp.name())); + + setItemString(IDC_INFO_HOST, TCharArray(conn->getSocket()->getPeerAddress()).buf); + + sprintf(buf, "%dx%d", conn->cp.width, conn->cp.height); + setItemString(IDC_INFO_SIZE, TStr(buf)); + + conn->cp.pf().print(buf, 256); + setItemString(IDC_INFO_PF, TStr(buf)); + + conn->getServerDefaultPF().print(buf, 256); + setItemString(IDC_INFO_DEF_PF, TStr(buf)); + + setItemString(IDC_REQUESTED_ENCODING, TStr(encodingName(conn->getOptions().preferredEncoding))); + setItemString(IDC_LAST_ENCODING, TStr(encodingName(conn->lastUsedEncoding()))); + + sprintf(buf, "%d kbits/s", conn->getSocket()->inStream().kbitsPerSecond()); + setItemString(IDC_INFO_LINESPEED, TStr(buf)); + + sprintf(buf, "%d.%d", conn->cp.majorVersion, conn->cp.minorVersion); + setItemString(IDC_INFO_VERSION, TStr(buf)); + + const CSecurity* cSec = conn->getCurrentCSecurity(); + setItemString(IDC_INFO_SECURITY, TStr(secTypeName(cSec->getType()))); + setItemString(IDC_INFO_ENCRYPTION, TStr(cSec->description())); +} diff --git a/win/vncviewer/InfoDialog.h b/win/vncviewer/InfoDialog.h new file mode 100644 index 00000000..752d53c5 --- /dev/null +++ b/win/vncviewer/InfoDialog.h @@ -0,0 +1,48 @@ +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +// -=- 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 CConn; + + class InfoDialog : Dialog { + public: + InfoDialog() : Dialog(GetModuleHandle(0)), conn(0) {} + virtual bool showDialog(CConn* vw); + virtual void initDialog(); + protected: + CConn* conn; + }; + + }; + +}; + +#endif diff --git a/win/vncviewer/ListenServer.h b/win/vncviewer/ListenServer.h new file mode 100644 index 00000000..4d1590c0 --- /dev/null +++ b/win/vncviewer/ListenServer.h @@ -0,0 +1,56 @@ +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +// -=- ListenServer.h + +#ifndef __RFB_WIN32_LISTEN_SERVER_H__ +#define __RFB_WIN32_LISTEN_SERVER_H__ + +#include <windows.h> +#include <winsock2.h> +#include <network/Socket.h> +#include <rfb_win32/MsgWindow.h> +#include <vncviewer/CConnThread.h> + + +namespace rfb { + namespace win32 { + + class ListenServer : MsgWindow { + public: + ListenServer(network::SocketListener* l) : MsgWindow(_T("rfb::win32::ListenServer")), sock(l) { + if (WSAAsyncSelect(l->getFd(), getHandle(), WM_USER, FD_ACCEPT) == SOCKET_ERROR) + throw rdr::SystemException("unable to monitor listen socket", WSAGetLastError()); + } + + LRESULT processMessage(UINT msg, WPARAM wParam, LPARAM lParam) { + if (msg == WM_USER) { + network::Socket* newConn = sock->accept(); + Thread* newThread = new CConnThread(newConn, true); + return 0; + } + return MsgWindow::processMessage(msg, wParam, lParam); + } + protected: + network::SocketListener* sock; + }; + + }; +}; + +#endif
\ No newline at end of file diff --git a/win/vncviewer/ListenTrayIcon.h b/win/vncviewer/ListenTrayIcon.h new file mode 100644 index 00000000..7e334d91 --- /dev/null +++ b/win/vncviewer/ListenTrayIcon.h @@ -0,0 +1,95 @@ +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +// -=- ListenTrayIcon.h + +#ifndef __RFB_WIN32_LISTEN_TRAY_ICON_H__ +#define __RFB_WIN32_LISTEN_TRAY_ICON_H__ + +#include <rfb_win32/TrayIcon.h> +#include <rfb_win32/AboutDialog.h> + +namespace rfb { + namespace win32 { + + class ListenTrayIcon : public TrayIcon { + public: + ListenTrayIcon() { + 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: + { + Thread* connThread = new CConnThread(); + 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); + } + }; + + }; +}; + +#endif // __RFB_WIN32_LISTEN_TRAY_ICON_H__
\ No newline at end of file diff --git a/win/vncviewer/MRU.h b/win/vncviewer/MRU.h new file mode 100644 index 00000000..ae703b3a --- /dev/null +++ b/win/vncviewer/MRU.h @@ -0,0 +1,133 @@ +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ +#ifndef __VIEWER_MRU_H__ +#define __VIEWER_MRU_H__ + +#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\\TightVNC\\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 (stricmp(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; + } + + 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; + + TCharArray keyname = rdr::HexOutStream::binToHexStr((char*)&keycode, 1); + key.setString(keyname.buf, TStr(name)); + key.setBinary(_T("Order"), order, orderlen); + } + + }; + + }; + +}; + +#endif diff --git a/win/vncviewer/OptionsDialog.cxx b/win/vncviewer/OptionsDialog.cxx new file mode 100644 index 00000000..2e43b38f --- /dev/null +++ b/win/vncviewer/OptionsDialog.cxx @@ -0,0 +1,337 @@ +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +#include <vncviewer/OptionsDialog.h> +#include <vncviewer/CConn.h> +#include <vncviewer/resource.h> +#include <rfb_win32/Registry.h> +#include <rfb_win32/MsgBox.h> +#include <rfb_win32/OSVersion.h> +#include <rfb/encodings.h> +#include <rfb/CConnection.h> +#include <commdlg.h> +#include <rfb/LogWriter.h> + +using namespace rfb; +using namespace rfb::win32; + +static LogWriter vlog("Options"); + + +struct OptionsInfo { + CConn* view; + CConnOptions options; +}; + + +OptionsDialog rfb::win32::OptionsDialog::global; + + +class ViewerOptions : public PropSheet { +public: + ViewerOptions(OptionsInfo& info_, std::list<PropSheetPage*> pages) + : PropSheet(GetModuleHandle(0), + info_.view ? _T("VNC Viewer Options") : _T("VNC Viewer Defaults"), pages), + info(info_), changed(false) { + } + ~ViewerOptions() { + 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 encodingTight: setItemChecked(IDC_ENCODING_TIGHT, true); break; + case encodingZRLE: setItemChecked(IDC_ENCODING_ZRLE, true); break; + case encodingHextile: setItemChecked(IDC_ENCODING_HEXTILE, true); break; + case encodingRaw: setItemChecked(IDC_ENCODING_RAW, true); break; + } + setItemChecked(IDC_CUSTOM_COMPRESSLEVEL, dlg->options.customCompressLevel); + setItemInt(IDC_COMPRESSLEVEL, dlg->options.compressLevel); + setItemChecked(IDC_ALLOW_JPEG, !dlg->options.noJpeg); + setItemInt(IDC_QUALITYLEVEL, dlg->options.qualityLevel); + onCommand(IDC_ENCODING_AUTO, 0 /* ? */); // Force enableItem status to refresh + onCommand(IDC_CUSTOM_COMPRESSLEVEL, 0 /* ? */); // Force enableItem status to refresh + onCommand(IDC_ALLOW_JPEG, 0 /* ? */); // Force enableItem status to refresh + } + virtual bool onOk() { + dlg->options.autoSelect = isItemChecked(IDC_ENCODING_AUTO); + dlg->options.fullColour = isItemChecked(IDC_FORMAT_FULLCOLOUR); + dlg->options.customCompressLevel = isItemChecked(IDC_CUSTOM_COMPRESSLEVEL); + dlg->options.compressLevel = getItemInt(IDC_COMPRESSLEVEL); + dlg->options.noJpeg = !isItemChecked(IDC_ALLOW_JPEG); + dlg->options.qualityLevel = getItemInt(IDC_QUALITYLEVEL); + 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 = encodingTight; + if (isItemChecked(IDC_ENCODING_ZRLE)) + dlg->options.preferredEncoding = encodingZRLE; + if (isItemChecked(IDC_ENCODING_HEXTILE)) + dlg->options.preferredEncoding = encodingHextile; + if (isItemChecked(IDC_ENCODING_RAW)) + dlg->options.preferredEncoding = encodingRaw; + ((ViewerOptions*)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_TIGHT, ok); + enableItem(IDC_ENCODING_ZRLE, ok); + enableItem(IDC_ENCODING_HEXTILE, ok); + enableItem(IDC_ENCODING_RAW, ok); + enableItem(IDC_FORMAT_FULLCOLOUR, ok); + enableItem(IDC_FORMAT_MEDIUMCOLOUR, ok); + enableItem(IDC_FORMAT_LOWCOLOUR, ok); + enableItem(IDC_FORMAT_VERYLOWCOLOUR, ok); + return true; + } + if (id == IDC_CUSTOM_COMPRESSLEVEL) { + enableItem(IDC_COMPRESSLEVEL, isItemChecked(IDC_CUSTOM_COMPRESSLEVEL)); + return true; + } + if (id == IDC_ALLOW_JPEG) { + enableItem(IDC_QUALITYLEVEL, isItemChecked(IDC_ALLOW_JPEG)); + 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); + setItemChecked(IDC_AUTO_RECONNECT, dlg->options.autoReconnect); + setItemChecked(IDC_SHOW_TOOLBAR, dlg->options.showToolbar); + } + 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); + dlg->options.autoReconnect = isItemChecked(IDC_AUTO_RECONNECT); + dlg->options.showToolbar = isItemChecked(IDC_SHOW_TOOLBAR); + ((ViewerOptions*)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_DISABLE_WINKEYS, dlg->options.disableWinKeys && !osVersion.isPlatformWindows); + enableItem(IDC_DISABLE_WINKEYS, !osVersion.isPlatformWindows); + 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.disableWinKeys = isItemChecked(IDC_DISABLE_WINKEYS); + 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)); + + ((ViewerOptions*)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) { + switch (id) { + case IDC_LOAD_DEFAULTS: + dlg->options = CConnOptions(); + 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(handle, _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 = handle; + 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(handle, _T("Options saved successfully"), + MB_OK | MB_ICONINFORMATION); + return 0; + }; + propSheet->reInitPages(); + return true; + } +protected: + OptionsInfo* dlg; +}; + + +OptionsDialog::OptionsDialog() : visible(false) { +} + +bool OptionsDialog::showDialog(CConn* 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 + ViewerOptions dialog(info, pages); + dialog.showPropSheet(view && view->getWindow() ? view->getWindow()->getHandle() : 0, + false, false, capture); + + visible = false; + return dialog.changed; +} diff --git a/win/vncviewer/OptionsDialog.h b/win/vncviewer/OptionsDialog.h new file mode 100644 index 00000000..fcddc71c --- /dev/null +++ b/win/vncviewer/OptionsDialog.h @@ -0,0 +1,48 @@ +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +// -=- OptionsDialog.h + +// Options dialog for VNC Viewer 4.0 + +#ifndef __RFB_WIN32_OPTIONS_DIALOG_H__ +#define __RFB_WIN32_OPTIONS_DIALOG_H__ + +#include <rfb_win32/Dialog.h> + +namespace rfb { + + namespace win32 { + + class CConn; + + class OptionsDialog { + public: + OptionsDialog(); + virtual bool showDialog(CConn* cfg, bool capture=false); + + static OptionsDialog global; + protected: + bool visible; + }; + + }; + +}; + +#endif diff --git a/win/vncviewer/UserPasswdDialog.cxx b/win/vncviewer/UserPasswdDialog.cxx new file mode 100644 index 00000000..2eea0ea0 --- /dev/null +++ b/win/vncviewer/UserPasswdDialog.cxx @@ -0,0 +1,85 @@ +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +#include <vncviewer/UserPasswdDialog.h> +#include <vncviewer/resource.h> +#include <rfb/Exception.h> + +using namespace rfb; +using namespace rfb::win32; + + +UserPasswdDialog::UserPasswdDialog() : Dialog(GetModuleHandle(0)), + showUsername(false), showPassword(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); + if (password.buf) + setItemString(IDC_PASSWORD, password.buf); + if (!showUsername) { + setItemString(IDC_USERNAME, _T("")); + enableItem(IDC_USERNAME, false); + } + if (!showPassword) { + setItemString(IDC_PASSWORD, _T("")); + enableItem(IDC_PASSWORD, 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.replaceBuf(getItemString(IDC_USERNAME)); + password.replaceBuf(getItemString(IDC_PASSWORD)); + return true; +} + + +void UserPasswdDialog::getUserPasswd(char** user, char** passwd) { + showUsername = user != 0; + showPassword = passwd != 0; + if (user && *user) + username.replaceBuf(tstrDup(*user)); + if (passwd && *passwd) + password.replaceBuf(tstrDup(*passwd)); + + if (!showDialog()) + throw rfb::AuthCancelledException(); + + if (user) + *user = strDup(username.buf); + if (passwd) + *passwd = strDup(password.buf); +} diff --git a/win/vncviewer/UserPasswdDialog.h b/win/vncviewer/UserPasswdDialog.h new file mode 100644 index 00000000..bf006f4d --- /dev/null +++ b/win/vncviewer/UserPasswdDialog.h @@ -0,0 +1,58 @@ +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +// -=- UserPasswdDialog.h + +// Username and password dialog for VNC Viewer 4.0 +// Note that the password and username fields are only freed +// when the dialog instance is deleted - it is important to +// ensure that the instance is deleted as early as possible, to +// avoid the password being retained in memory for too long. + +#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 void getUserPasswd(char** user, char** passwd); + void setCSecurity(const CSecurity* cs); + protected: + TCharArray username; + TPlainPasswd password; + bool showUsername, showPassword; + TCharArray description; + }; + + }; + +}; + +#endif diff --git a/win/vncviewer/ViewerToolBar.cxx b/win/vncviewer/ViewerToolBar.cxx new file mode 100644 index 00000000..29b80301 --- /dev/null +++ b/win/vncviewer/ViewerToolBar.cxx @@ -0,0 +1,117 @@ +/* Copyright (C) 2005 TightVNC Team. All Rights Reserved. + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +// -=- ViewerToolBar.cxx + +#include <vncviewer/ViewerToolBar.h> +#include <vncviewer/resource.h> + +void ViewerToolBar::create(HWND parentHwnd) { + // Create the toolbar panel + ToolBar::create(ID_TOOLBAR, parentHwnd, WS_CHILD | + TBSTYLE_FLAT | TBSTYLE_TOOLTIPS | CCS_NORESIZE); + addBitmap(4, IDB_TOOLBAR); + + // Create the control buttons + addButton(0, ID_OPTIONS); + addButton(1, ID_INFO); + addButton(0, 0, TBSTATE_ENABLED, TBSTYLE_SEP); + addButton(2, ID_FULLSCREEN); + addButton(3, ID_REQUEST_REFRESH); + addButton(0, 0, TBSTATE_ENABLED, TBSTYLE_SEP); + addButton(4, ID_SEND_CAD); + addButton(5, ID_SEND_CTLESC); + addButton(6, ID_CTRL_KEY); + addButton(7, ID_ALT_KEY); + addButton(0, 0, TBSTATE_ENABLED, TBSTYLE_SEP); + addButton(8, ID_FILE_TRANSFER); + addButton(0, 0, TBSTATE_ENABLED, TBSTYLE_SEP); + addButton(9, ID_NEW_CONNECTION); + addButton(10, ID_CONN_SAVE_AS); + addButton(11, ID_CLOSE); + + // Resize the toolbar window + autoSize(); +} + +LRESULT ViewerToolBar::processWM_NOTIFY(WPARAM wParam, LPARAM lParam) { + switch (((LPNMHDR)lParam)->code) { + // Process tooltips text + case TTN_NEEDTEXT: + { + LPTOOLTIPTEXT TTStr = (LPTOOLTIPTEXT)lParam; + if (TTStr->hdr.code != TTN_NEEDTEXT) + return 0; + + switch (TTStr->hdr.idFrom) { + case ID_OPTIONS: + TTStr->lpszText = "Connection options..."; + break; + case ID_INFO: + TTStr->lpszText = "Connection info"; + break; + case ID_FULLSCREEN: + TTStr->lpszText = "Full screen"; + break; + case ID_REQUEST_REFRESH: + TTStr->lpszText = "Request screen refresh"; + break; + case ID_SEND_CAD: + TTStr->lpszText = "Send Ctrl-Alt-Del"; + break; + case ID_SEND_CTLESC: + TTStr->lpszText = "Send Ctrl-Esc"; + break; + case ID_CTRL_KEY: + TTStr->lpszText = "Send Ctrl key press/release"; + break; + case ID_ALT_KEY: + TTStr->lpszText = "Send Alt key press/release"; + break; + case ID_FILE_TRANSFER: + TTStr->lpszText = "Transfer files..."; + break; + case ID_NEW_CONNECTION: + TTStr->lpszText = "New connection..."; + break; + case ID_CONN_SAVE_AS: + TTStr->lpszText = "Save connection info as..."; + break; + case ID_CLOSE: + TTStr->lpszText = "Disconnect"; + break; + default: + break; + } + } + + default: + break; + } + return 0; +} + +void ViewerToolBar::show() { + ShowWindow(getHandle(), SW_SHOW); + SendMessage(parentHwnd, WM_SIZE, 0, 0); +} + +void ViewerToolBar::hide() { + ShowWindow(getHandle(), SW_HIDE); + SendMessage(parentHwnd, WM_SIZE, 0, 0); +} diff --git a/win/vncviewer/ViewerToolBar.h b/win/vncviewer/ViewerToolBar.h new file mode 100644 index 00000000..30e7708f --- /dev/null +++ b/win/vncviewer/ViewerToolBar.h @@ -0,0 +1,38 @@ +/* Copyright (C) 2005 TightVNC Team. All Rights Reserved. + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +// -=- ViewerToolBar.h + +// ToolBar for the Vnc Viewer + +#include <rfb_win32/ToolBar.h> + +using namespace rfb::win32; + +class ViewerToolBar : public ToolBar { +public: + ViewerToolBar() {} + ~ViewerToolBar() {} + + void create(HWND parentHwnd); + + LRESULT processWM_NOTIFY(WPARAM wParam, LPARAM lParam); + + void show(); + void hide(); +}; diff --git a/win/vncviewer/buildTime.cxx b/win/vncviewer/buildTime.cxx new file mode 100644 index 00000000..9f37b387 --- /dev/null +++ b/win/vncviewer/buildTime.cxx @@ -0,0 +1,18 @@ +/* Copyright (C) 2005 RealVNC Ltd. All Rights Reserved. + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ +const char* buildTime = "Built on " __DATE__ " at " __TIME__; diff --git a/win/vncviewer/cursor1.cur b/win/vncviewer/cursor1.cur Binary files differnew file mode 100644 index 00000000..20a713f7 --- /dev/null +++ b/win/vncviewer/cursor1.cur diff --git a/win/vncviewer/ftdir.ico b/win/vncviewer/ftdir.ico Binary files differnew file mode 100644 index 00000000..7a7f7419 --- /dev/null +++ b/win/vncviewer/ftdir.ico diff --git a/win/vncviewer/ftfile.ico b/win/vncviewer/ftfile.ico Binary files differnew file mode 100644 index 00000000..a8084b88 --- /dev/null +++ b/win/vncviewer/ftfile.ico diff --git a/win/vncviewer/ftreload.ico b/win/vncviewer/ftreload.ico Binary files differnew file mode 100644 index 00000000..34383e90 --- /dev/null +++ b/win/vncviewer/ftreload.ico diff --git a/win/vncviewer/ftup.ico b/win/vncviewer/ftup.ico Binary files differnew file mode 100644 index 00000000..fc215ada --- /dev/null +++ b/win/vncviewer/ftup.ico diff --git a/win/vncviewer/resource.h b/win/vncviewer/resource.h new file mode 100644 index 00000000..5493fd0b --- /dev/null +++ b/win/vncviewer/resource.h @@ -0,0 +1,154 @@ +//{{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 IDB_BITMAP 120 +#define IDD_FILETRANSFER_DLG 121 +#define IDB_TOOLBAR 122 +#define IDD_FTCONFIRM_DLG 124 +#define IDI_FTUP 125 +#define IDI_FTDIR 126 +#define IDI_FTFILE 127 +#define IDI_FTRELOAD 128 +#define IDR_FTMENU 130 +#define IDD_FTCANCELING 131 +#define IDD_FTCREATEFOLDER 132 +#define IDD_FTBROWSE 133 +#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_FTLOCALLIST 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_SHOW_TOOLBAR 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 IDC_SECURITY_LEVEL 1054 +#define IDC_INFO_ENCRYPTION 1055 +#define IDC_AUTO_RECONNECT 1056 +#define IDC_DISABLE_WINKEYS 1057 +#define IDC_QUALITYLEVEL 1058 +#define IDC_FTLOCALUP 1058 +#define IDC_SEND_SYSKEYS 1059 +#define IDC_FTLOCALBROWSE 1059 +#define IDC_FTREMOTERELOAD 1060 +#define IDC_FTREMOTEUP 1061 +#define IDC_FTREMOTEBROWSE 1062 +#define IDC_FTPROGRESS 1063 +#define IDC_FTGENERALPROGRESS 1063 +#define IDC_PROGRESS 1064 +#define IDC_FTSINGLEPROGRESS 1064 +#define IDC_FTSTATUS 1065 +#define IDC_FTCURRENTPROCENT 1066 +#define IDC_FTSINGLEPERCENT 1066 +#define IDC_FTTOTALPROCENT 1067 +#define IDC_FTGENERALPERCENT 1067 +#define IDC_FTUPLOAD 1072 +#define IDC_FTCANCEL 1073 +#define IDC_FTDOWNLOAD 1074 +#define IDC_FTCLOSE 1075 +#define IDC_FTLOCALLABEL 1076 +#define IDC_FTREMOTELABEL 1077 +#define IDC_FTDIRNAME 1078 +#define IDC_CONFIRM_YESTOALL 1079 +#define IDC_CONFIRM_TEXT 1080 +#define IDC_EDIT2 1082 +#define IDC_FTFOLDERNAME 1083 +#define IDC_FTTEXT 1084 +#define IDC_FTBROWSEPATH 1085 +#define IDC_FTBROWSETREE 1086 +#define IDC_TYPE 1088 +#define IDC_ENCODING_TIGHT 1089 +#define IDC_FTLOCALPATH 1090 +#define IDC_CUSTOM_COMPRESSLEVEL 1091 +#define IDC_FTREMOTEPATH 1092 +#define IDC_COMPRESSLEVEL 1093 +#define IDC_FTREMOTELIST 1094 +#define IDC_ALLOW_JPEG 1095 +#define IDC_FTLOCALRELOAD 1096 +#define ID_TOOLBAR 40002 +#define ID_CLOSE 40003 +#define ID_OPTIONS 40004 +#define ID_NEW_CONNECTION 40005 +#define ID_ABOUT 40006 +#define ID_FULLSCREEN 40007 +#define ID_SEND_CAD 40008 +#define ID_INFO 40009 +#define ID_REQUEST_REFRESH 40010 +#define ID_CTRL_KEY 40011 +#define ID_ALT_KEY 40012 +#define ID_SEND_MENU_KEY 40013 +#define ID_SEND_CTLESC 40014 +#define ID_CONN_SAVE_AS 40015 +#define ID_FILE_TRANSFER 40016 +#define IDM_FTCOPY 40022 +#define IDM_FTRENAME 40023 +#define IDM_FTDELETE 40024 +#define IDM_FTCANCEL 40025 +#define IDM_FTCREATEFOLDER 40026 +#define IDM_SHOW_TOOLBAR 40027 + +// Next default values for new objects +// +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS +#define _APS_NEXT_RESOURCE_VALUE 134 +#define _APS_NEXT_COMMAND_VALUE 40028 +#define _APS_NEXT_CONTROL_VALUE 1097 +#define _APS_NEXT_SYMED_VALUE 101 +#endif +#endif diff --git a/win/vncviewer/toolbar.bmp b/win/vncviewer/toolbar.bmp Binary files differnew file mode 100644 index 00000000..0a146361 --- /dev/null +++ b/win/vncviewer/toolbar.bmp diff --git a/win/vncviewer/vncviewer.bmp b/win/vncviewer/vncviewer.bmp Binary files differnew file mode 100644 index 00000000..4ea9c378 --- /dev/null +++ b/win/vncviewer/vncviewer.bmp diff --git a/win/vncviewer/vncviewer.cxx b/win/vncviewer/vncviewer.cxx new file mode 100644 index 00000000..3a5214a3 --- /dev/null +++ b/win/vncviewer/vncviewer.cxx @@ -0,0 +1,295 @@ +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +// -=- VNC Viewer for Win32 + +#include <string.h> +#ifdef WIN32 +#define strcasecmp _stricmp +#endif +#include <list> + +#include <vncviewer/resource.h> +#include <vncviewer/CConn.h> +#include <vncviewer/CConnThread.h> +#include <vncviewer/OptionsDialog.h> +#include <vncviewer/ListenServer.h> +#include <vncviewer/ListenTrayIcon.h> +#include <network/TcpSocket.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/MsgBox.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; + + +// +// -=- 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 VNC Viewer 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(); + printf("Press Enter/Return key to continue\n"); + char c = getchar(); + exit(1); +} + + +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 if (strContains(argv[i], '\\')) { + configFiles.push_back(strDup(argv[i])); + } 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) { + CConn::userConfigKey.openKey(HKEY_CURRENT_USER, _T("Software\\TightVNC\\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::RegConfigThread config; + config.start(HKEY_CURRENT_USER, _T("Software\\TightVNC\\VNCViewer4")); + + // - Tell the rest of VNC Viewer where to write config data to + CConn::userConfigKey.createKey(HKEY_CURRENT_USER, _T("Software\\TightVNC\\VNCViewer4")); + + if (acceptIncoming) { + int port = 5500; + + // Listening viewer + if (hosts.size() > 1) + programUsage(); + if (!hosts.empty()) + port = atoi(hosts.front()); + + // Show the tray icon & menu + ListenTrayIcon tray; + + // Listen for reverse connections + network::TcpListener sock(port); + ListenServer listener(&sock); + + // Run the view manager + // Also processes the tray icon if necessary + MSG msg; + while (GetMessage(&msg, NULL, 0, 0) > 0) { + TranslateMessage(&msg); + DispatchMessage(&msg); + } + } else { + // Read each config file in turn + while (!configFiles.empty()) { + char* filename = configFiles.front(); + Thread* connThread = new CConnThread(filename, true); + strFree(filename); + configFiles.pop_front(); + } + + // Connect to each client in turn + while (!hosts.empty()) { + char* hostinfo = hosts.front(); + Thread* connThread = new CConnThread(hostinfo); + strFree(hostinfo); + hosts.pop_front(); + } + + // Run the view manager + MSG msg; + while (CConnThread::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/win/vncviewer/vncviewer.dsp b/win/vncviewer/vncviewer.dsp new file mode 100644 index 00000000..e245cd6e --- /dev/null +++ b/win/vncviewer/vncviewer.dsp @@ -0,0 +1,321 @@ +# 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\vncviewer"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /YX /FD /c
+# ADD CPP /nologo /MT /W3 /GX /O2 /I ".." /I "../../common" /FI"rdr/msvcwarning.h" /D "NDEBUG" /D "_WINDOWS" /D "WIN32" /D "_MBCS" /YX /FD /c
+# ADD BASE 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 /Fo..\Release\ /Fd..\Release\vncviewer /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\vncviewer"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /YX /FD /GZ /c
+# ADD CPP /nologo /MTd /W3 /Gm /GX /Zi /Od /I ".." /I "../../common" /FI"rdr/msvcwarning.h" /D "_DEBUG" /D "_WINDOWS" /D "WIN32" /D "_MBCS" /FR /YX /FD /GZ /c
+# ADD BASE 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 /Fo..\Debug\ /Fd..\Debug\vncviewer /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\vncviewer"
+# 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 ".." /I "../../common" /FI"rdr/msvcwarning.h" /D "_WINDOWS" /D "_DEBUG" /D "WIN32" /D "_UNICODE" /D "UNICODE" /FR /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 /Fo..\Debug_Unicode\ /Fd..\Debug_Unicode\vncviewer /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=.\CConn.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\CConnOptions.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\CConnThread.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\ConnectingDialog.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\ConnectionDialog.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\DesktopWindow.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\FileTransfer.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\FTBrowseDlg.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\FTDialog.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\FTListView.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\FTProgress.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=.\ViewerToolBar.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=.\CConn.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\CConnOptions.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\CConnThread.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\ConnectingDialog.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\ConnectionDialog.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\DesktopWindow.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\FileTransfer.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\FTBrowseDlg.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\FTDialog.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\FTListView.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\FTProgress.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\InfoDialog.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\ListenServer.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\ListenTrayIcon.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
+# Begin Source File
+
+SOURCE=.\ViewerToolBar.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=.\ftdir.ico
+# End Source File
+# Begin Source File
+
+SOURCE=.\ftfile.ico
+# End Source File
+# Begin Source File
+
+SOURCE=.\ftreload.ico
+# End Source File
+# Begin Source File
+
+SOURCE=.\ftup.ico
+# End Source File
+# Begin Source File
+
+SOURCE=.\toolbar.bmp
+# End Source File
+# Begin Source File
+
+SOURCE=.\vncviewer.bmp
+# 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/win/vncviewer/vncviewer.exe.manifest b/win/vncviewer/vncviewer.exe.manifest new file mode 100644 index 00000000..7fd947ff --- /dev/null +++ b/win/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="TightVNC.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/win/vncviewer/vncviewer.ico b/win/vncviewer/vncviewer.ico Binary files differnew file mode 100644 index 00000000..65726612 --- /dev/null +++ b/win/vncviewer/vncviewer.ico diff --git a/win/vncviewer/vncviewer.rc b/win/vncviewer/vncviewer.rc new file mode 100644 index 00000000..ea5dd65c --- /dev/null +++ b/win/vncviewer/vncviewer.rc @@ -0,0 +1,708 @@ +//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" +IDI_FTDIR ICON DISCARDABLE "ftdir.ico" +IDI_FTFILE ICON DISCARDABLE "ftfile.ico" +IDI_FTRELOAD ICON DISCARDABLE "ftreload.ico" +IDI_FTUP ICON DISCARDABLE "ftup.ico" + +#ifndef _MAC +///////////////////////////////////////////////////////////////////////////// +// +// Version +// + +VS_VERSION_INFO VERSIONINFO + FILEVERSION 4,1,1,0 + PRODUCTVERSION 4,1,1,0 + 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", "Constantin Kaplinsky\0" + VALUE "FileDescription", "TightVNC Viewer for Win32\0" + VALUE "FileVersion", "4.1.1\0" + VALUE "InternalName", "free4/vncviewer/win\0" + VALUE "LegalCopyright", "Copyright (C) 1998-2006 [many holders]\0" + VALUE "LegalTrademarks", "TightVNC\0" + VALUE "OriginalFilename", "vncviewer.exe\0" + VALUE "PrivateBuild", "\0" + VALUE "ProductName", "TightVNC Viewer\0" + VALUE "ProductVersion", "4.1.1\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,85,6,100,14,ES_AUTOHSCROLL + EDITTEXT IDC_PASSWORD,85,25,100,15,ES_PASSWORD | ES_AUTOHSCROLL | + ES_WANTRETURN + DEFPUSHBUTTON "OK",IDOK,190,6,45,14 + PUSHBUTTON "Cancel",IDCANCEL,190,25,45,15 + CONTROL 120,IDI_ICON,"Static",SS_BITMAP,7,6,21,20 + LTEXT "Username:",IDC_STATIC,45,6,35,14 + LTEXT "Password:",IDC_STATIC,45,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 + CTEXT "Attempting to connect to host...",IDC_CONNECTING_TEXT,7, + 7,171,14,SS_CENTERIMAGE +END + +IDD_CONNECTION_DLG DIALOG DISCARDABLE 0, 0, 224, 66 +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,85,6,105,234,CBS_DROPDOWN | + CBS_AUTOHSCROLL | WS_VSCROLL | WS_TABSTOP + PUSHBUTTON "&About...",IDC_ABOUT,5,45,50,14 + PUSHBUTTON "&Options...",IDC_OPTIONS,60,45,50,14 + DEFPUSHBUTTON "OK",IDOK,115,45,50,14 + PUSHBUTTON "Cancel",IDCANCEL,170,45,48,14 + CONTROL 120,IDI_ICON,"Static",SS_BITMAP | SS_REALSIZEIMAGE,5,6, + 20,20 + RTEXT "Server:",IDC_STATIC,43,6,37,13,SS_CENTERIMAGE + RTEXT "Encryption:",IDC_STATIC,43,24,37,12,SS_CENTERIMAGE + COMBOBOX IDC_SECURITY_LEVEL,85,24,105,76,CBS_DROPDOWNLIST | + CBS_SORT | WS_VSCROLL | WS_TABSTOP +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 + CONTROL 120,IDC_STATIC,"Static",SS_BITMAP | SS_REALSIZEIMAGE,7, + 10,33,31 + LTEXT ">appname<",IDC_DESCRIPTION,46,10,119,15 + LTEXT ">version<",IDC_VERSION,165,10,77,15 + LTEXT ">buildtime<",IDC_BUILDTIME,46,25,196,15 + LTEXT ">copyright<",IDC_COPYRIGHT,46,40,196,15 + LTEXT "Visit www.tightvnc.com for more information on TightVNC.", + IDC_STATIC,46,55,196,15 +END + +IDD_FORMAT DIALOG DISCARDABLE 0, 0, 201, 161 +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,20,83,75 + CONTROL "Tight",IDC_ENCODING_TIGHT,"Button",BS_AUTORADIOBUTTON | + WS_GROUP,10,30,75,14 + CONTROL "ZRLE",IDC_ENCODING_ZRLE,"Button",BS_AUTORADIOBUTTON,10, + 45,75,14 + CONTROL "Hextile",IDC_ENCODING_HEXTILE,"Button", + BS_AUTORADIOBUTTON,10,60,75,16 + CONTROL "Raw",IDC_ENCODING_RAW,"Button",BS_AUTORADIOBUTTON,10,75, + 75,15 + GROUPBOX "Color level",IDC_STATIC,95,20,99,75 + CONTROL "&Full (all available colors)",IDC_FORMAT_FULLCOLOUR, + "Button",BS_AUTORADIOBUTTON | WS_GROUP,100,30,90,15 + CONTROL "&Medium (256 colors)",IDC_FORMAT_MEDIUMCOLOUR,"Button", + BS_AUTORADIOBUTTON,100,45,90,14 + CONTROL "&Low (64 colors)",IDC_FORMAT_LOWCOLOUR,"Button", + BS_AUTORADIOBUTTON,100,60,90,16 + CONTROL "&Very low (8 colors)",IDC_FORMAT_VERYLOWCOLOUR,"Button", + BS_AUTORADIOBUTTON,100,75,90,15 + CONTROL "Custom compression level:",IDC_CUSTOM_COMPRESSLEVEL, + "Button",BS_AUTOCHECKBOX | WS_TABSTOP,10,100,99,10 + EDITTEXT IDC_COMPRESSLEVEL,25,111,15,12,ES_AUTOHSCROLL | + ES_NUMBER + LTEXT "level (1=fast, 9=best)",IDC_STATIC,44,114,81,9,NOT + WS_GROUP + CONTROL "Allow JPEG compression:",IDC_ALLOW_JPEG,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,10,126,96,10 + EDITTEXT IDC_QUALITYLEVEL,25,137,15,12,ES_AUTOHSCROLL | ES_NUMBER + LTEXT "quality (1=poor, 9=best)",IDC_STATIC,44,140,81,9 +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,99,15 + CONTROL "Show toolbar",IDC_SHOW_TOOLBAR,"Button",BS_AUTOCHECKBOX | + WS_TABSTOP,106,25,100,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,85,199,15 + CONTROL "Offer to automatically reconnect",IDC_AUTO_RECONNECT, + "Button",BS_AUTOCHECKBOX | WS_TABSTOP,7,100,199,15 +END + +IDD_INPUTS DIALOG DISCARDABLE 0, 0, 186, 162 +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 + CONTROL "Pass special keys directly to server", + IDC_DISABLE_WINKEYS,"Button",BS_AUTOCHECKBOX | + WS_TABSTOP,7,115,172,15 +END + +IDD_CONNECTION_INFO DIALOG DISCARDABLE 0, 0, 239, 199 +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,178,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 + LTEXT "Static",IDC_INFO_ENCRYPTION,80,160,152,15 + LTEXT "Encryption:",IDC_STATIC,7,160,73,15 +END + +IDD_DEFAULTS DIALOG DISCARDABLE 0, 0, 217, 87 +STYLE DS_MODALFRAME | DS_CONTROL | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "Load / Save" +FONT 8, "MS Sans Serif" +BEGIN + PUSHBUTTON "&Reload",IDC_LOAD_CONFIG,15,20,85,15 + PUSHBUTTON "&Save",IDC_SAVE_CONFIG,15,40,85,15 + PUSHBUTTON "Save &As ...",IDC_SAVE_CONFIG_AS,15,60,85,15 + PUSHBUTTON "R&eload",IDC_LOAD_DEFAULTS,120,20,85,15 + PUSHBUTTON "S&ave",IDC_SAVE_DEFAULTS,120,40,85,15 + GROUPBOX "Configuration File",IDC_STATIC,7,7,100,74 + GROUPBOX "Defaults",IDC_STATIC,113,7,97,53 +END + +IDD_FILETRANSFER_DLG DIALOGEX 0, 0, 530, 282 +STYLE DS_MODALFRAME | DS_CONTEXTHELP | WS_POPUP | WS_VISIBLE | WS_CAPTION | + WS_SYSMENU +EXSTYLE WS_EX_CONTEXTHELP | WS_EX_CONTROLPARENT +CAPTION "TightVNC File Transfers" +FONT 8, "MS Sans Serif", 0, 0, 0x1 +BEGIN + CONTROL "List1",IDC_FTLOCALLIST,"SysListView32",LVS_REPORT | + LVS_SHOWSELALWAYS | LVS_NOSORTHEADER | WS_BORDER | + WS_TABSTOP,7,40,200,196 + CONTROL "List2",IDC_FTREMOTELIST,"SysListView32",LVS_REPORT | + LVS_SHOWSELALWAYS | LVS_NOSORTHEADER | WS_BORDER | + WS_TABSTOP,323,40,200,196 + PUSHBUTTON "Upload Files and Folders",IDC_FTUPLOAD,218,66,94,12, + WS_DISABLED + PUSHBUTTON "Download Files and Folders",IDC_FTDOWNLOAD,218,85,94,12, + WS_DISABLED + PUSHBUTTON "Cancel File Transfer",IDC_FTCANCEL,218,167,94,12, + WS_DISABLED + PUSHBUTTON "Close",IDC_FTCLOSE,218,217,94,12 + EDITTEXT IDC_FTLOCALPATH,7,20,155,12,ES_AUTOHSCROLL | NOT + WS_TABSTOP + CTEXT "Local Computer",IDC_FTLOCALLABEL,7,7,200,10 + PUSHBUTTON "...",IDC_FTLOCALBROWSE,165,20,14,12,NOT WS_TABSTOP + PUSHBUTTON "",IDC_FTLOCALUP,179,20,14,12,BS_ICON | NOT WS_TABSTOP + PUSHBUTTON "",IDC_FTLOCALRELOAD,193,20,14,12,BS_ICON | NOT + WS_TABSTOP + CONTROL "Progress1",IDC_FTGENERALPROGRESS,"msctls_progress32", + WS_BORDER,55,244,128,10 + LTEXT "File Transfer",IDC_STATIC,7,245,40,8 + COMBOBOX IDC_FTSTATUS,7,263,516,65,CBS_DROPDOWNLIST | + CBS_NOINTEGRALHEIGHT | WS_VSCROLL + CONTROL "Progress1",IDC_FTSINGLEPROGRESS,"msctls_progress32", + WS_BORDER,370,244,128,10 + EDITTEXT IDC_FTREMOTEPATH,323,20,155,12,ES_AUTOHSCROLL | NOT + WS_TABSTOP + PUSHBUTTON "...",IDC_FTREMOTEBROWSE,481,20,14,12,NOT WS_TABSTOP + PUSHBUTTON "",IDC_FTREMOTEUP,495,20,14,12,BS_ICON | NOT WS_TABSTOP + PUSHBUTTON "",IDC_FTREMOTERELOAD,509,20,14,12,BS_ICON | NOT + WS_TABSTOP + CTEXT "TightVNC Server",IDC_FTREMOTELABEL,323,7,200,10 + LTEXT "Current File",IDC_STATIC,323,245,36,8 + CTEXT "0%",IDC_FTGENERALPERCENT,189,245,18,8 + CTEXT "0%",IDC_FTSINGLEPERCENT,505,245,18,8 +END + +IDD_FTBROWSE DIALOGEX 0, 0, 183, 196 +STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU +EXSTYLE WS_EX_CONTROLPARENT +CAPTION "Browse Folders" +FONT 8, "MS Sans Serif", 0, 0, 0x1 +BEGIN + DEFPUSHBUTTON "OK",IDOK,38,175,50,14 + PUSHBUTTON "Cancel",IDCANCEL,95,175,50,14 + EDITTEXT IDC_FTBROWSEPATH,7,7,169,12,ES_AUTOHSCROLL | ES_READONLY | + NOT WS_TABSTOP + CONTROL "Tree1",IDC_FTBROWSETREE,"SysTreeView32",TVS_HASBUTTONS | + TVS_HASLINES | TVS_LINESATROOT | TVS_DISABLEDRAGDROP | + TVS_SHOWSELALWAYS | WS_BORDER | WS_TABSTOP,7,25,169,143 +END + +IDD_FTCANCELING DIALOG DISCARDABLE 0, 0, 193, 63 +STYLE DS_SYSMODAL | DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "Canceling Active File Transfer" +FONT 8, "MS Sans Serif" +BEGIN + DEFPUSHBUTTON "Yes",IDOK,40,42,50,14 + PUSHBUTTON "No",IDCANCEL,102,42,50,14 + LTEXT "FileTransfer is active.\nAre you sure you want to cancel transfer?", + IDC_STATIC,42,14,133,19 +END + +IDD_FTCONFIRM_DLG DIALOG DISCARDABLE 0, 0, 188, 143 +STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU +FONT 8, "MS Sans Serif" +BEGIN + DEFPUSHBUTTON "Yes",IDOK,69,122,50,14,WS_GROUP + PUSHBUTTON "No",IDCANCEL,131,122,50,14 + PUSHBUTTON "Yes to All",IDC_CONFIRM_YESTOALL,7,122,50,14 + LTEXT "Static",IDC_CONFIRM_TEXT,7,7,174,107 +END + +IDD_FTCREATEFOLDER DIALOG DISCARDABLE 0, 0, 193, 63 +STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "Create a New Folder" +FONT 8, "MS Sans Serif" +BEGIN + EDITTEXT IDC_FTFOLDERNAME,7,19,179,14,ES_AUTOHSCROLL + DEFPUSHBUTTON "OK",IDOK,80,42,50,14 + PUSHBUTTON "Cancel",IDCANCEL,136,42,50,14 + LTEXT "New folder name:",IDC_FTTEXT,7,7,179,8 +END + + +///////////////////////////////////////////////////////////////////////////// +// +// DESIGNINFO +// + +#ifdef APSTUDIO_INVOKED +GUIDELINES DESIGNINFO DISCARDABLE +BEGIN + IDD_VNC_AUTH_DLG, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 235 + VERTGUIDE, 45 + VERTGUIDE, 80 + VERTGUIDE, 85 + VERTGUIDE, 185 + VERTGUIDE, 190 + 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, 218 + VERTGUIDE, 30 + VERTGUIDE, 43 + VERTGUIDE, 55 + VERTGUIDE, 60 + VERTGUIDE, 80 + VERTGUIDE, 85 + VERTGUIDE, 110 + VERTGUIDE, 115 + VERTGUIDE, 165 + VERTGUIDE, 170 + VERTGUIDE, 190 + TOPMARGIN, 6 + BOTTOMMARGIN, 59 + HORZGUIDE, 19 + HORZGUIDE, 24 + HORZGUIDE, 36 + HORZGUIDE, 45 + END + + IDD_ABOUT, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 242 + VERTGUIDE, 46 + 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, 154 + 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 + VERTGUIDE, 106 + TOPMARGIN, 7 + BOTTOMMARGIN, 130 + HORZGUIDE, 10 + HORZGUIDE, 25 + HORZGUIDE, 40 + HORZGUIDE, 55 + HORZGUIDE, 70 + HORZGUIDE, 85 + HORZGUIDE, 100 + HORZGUIDE, 115 + HORZGUIDE, 130 + END + + IDD_INPUTS, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 179 + VERTGUIDE, 105 + TOPMARGIN, 7 + BOTTOMMARGIN, 155 + HORZGUIDE, 10 + HORZGUIDE, 25 + HORZGUIDE, 40 + HORZGUIDE, 55 + HORZGUIDE, 70 + HORZGUIDE, 85 + HORZGUIDE, 100 + HORZGUIDE, 115 + HORZGUIDE, 130 + HORZGUIDE, 145 + END + + IDD_CONNECTION_INFO, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 232 + VERTGUIDE, 80 + TOPMARGIN, 7 + BOTTOMMARGIN, 192 + HORZGUIDE, 10 + HORZGUIDE, 25 + HORZGUIDE, 40 + HORZGUIDE, 55 + HORZGUIDE, 70 + HORZGUIDE, 85 + HORZGUIDE, 100 + HORZGUIDE, 115 + HORZGUIDE, 130 + HORZGUIDE, 145 + HORZGUIDE, 160 + HORZGUIDE, 175 + END + + IDD_DEFAULTS, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 210 + VERTGUIDE, 15 + VERTGUIDE, 100 + VERTGUIDE, 107 + VERTGUIDE, 113 + VERTGUIDE, 120 + VERTGUIDE, 205 + TOPMARGIN, 7 + BOTTOMMARGIN, 80 + HORZGUIDE, 20 + HORZGUIDE, 35 + HORZGUIDE, 40 + HORZGUIDE, 55 + HORZGUIDE, 60 + HORZGUIDE, 75 + END + + IDD_FILETRANSFER_DLG, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 523 + VERTGUIDE, 207 + VERTGUIDE, 265 + VERTGUIDE, 323 + TOPMARGIN, 7 + BOTTOMMARGIN, 275 + HORZGUIDE, 12 + HORZGUIDE, 26 + HORZGUIDE, 40 + HORZGUIDE, 47 + HORZGUIDE, 249 + END + + IDD_FTBROWSE, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 176 + TOPMARGIN, 7 + BOTTOMMARGIN, 189 + END + + IDD_FTCANCELING, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 186 + TOPMARGIN, 7 + BOTTOMMARGIN, 56 + END + + IDD_FTCONFIRM_DLG, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 181 + TOPMARGIN, 7 + BOTTOMMARGIN, 136 + END + + IDD_FTCREATEFOLDER, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 186 + TOPMARGIN, 7 + BOTTOMMARGIN, 56 + 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 + +IDR_FTMENU MENU DISCARDABLE +BEGIN + POPUP "File Transfer" + BEGIN + MENUITEM "Copy Files and Folders", IDM_FTCOPY + MENUITEM SEPARATOR + MENUITEM "Create a Folder", IDM_FTCREATEFOLDER + MENUITEM "Rename File or Folder", IDM_FTRENAME + MENUITEM "Delete Files and Folders", IDM_FTDELETE + MENUITEM SEPARATOR + MENUITEM "Cancel File Transfer", IDM_FTCANCEL + END +END + + +///////////////////////////////////////////////////////////////////////////// +// +// 24 +// + +IDR_MANIFEST 24 DISCARDABLE "vncviewer.exe.manifest" + +///////////////////////////////////////////////////////////////////////////// +// +// Bitmap +// + +IDB_BITMAP BITMAP DISCARDABLE "vncviewer.bmp" +IDB_TOOLBAR BITMAP DISCARDABLE "toolbar.bmp" +#endif // English (U.K.) resources +///////////////////////////////////////////////////////////////////////////// + + + +#ifndef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 3 resource. +// + + +///////////////////////////////////////////////////////////////////////////// +#endif // not APSTUDIO_INVOKED + diff --git a/win/winvnc/AddNewClientDialog.h b/win/winvnc/AddNewClientDialog.h new file mode 100644 index 00000000..9bf51355 --- /dev/null +++ b/win/winvnc/AddNewClientDialog.h @@ -0,0 +1,56 @@ +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +// -=- 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/win/winvnc/ControlPanel.cxx b/win/winvnc/ControlPanel.cxx new file mode 100644 index 00000000..74282303 --- /dev/null +++ b/win/winvnc/ControlPanel.cxx @@ -0,0 +1,161 @@ +// ControlPanel.cxx: implementation of the ControlPanel class. +// +////////////////////////////////////////////////////////////////////// + +#include "ControlPanel.h" + +////////////////////////////////////////////////////////////////////// +// Construction/Destruction +////////////////////////////////////////////////////////////////////// + +using namespace winvnc; + +bool ControlPanel::showDialog() +{ + return Dialog::showDialog(MAKEINTRESOURCE(IDD_CONTROL_PANEL), NULL); +} + +void ControlPanel::initDialog() +{ + TCHAR *ColumnsStrings[] = { + "IP address", + "Time connected", + "Status" + }; + InitLVColumns(IDC_LIST_CONNECTIONS, handle, 120, 3, ColumnsStrings, + LVCF_FMT | LVCF_WIDTH | LVCF_TEXT | LVCF_SUBITEM, + LVS_EX_FULLROWSELECT, LVCFMT_LEFT); + SendCommand(4, -1); +} + +bool ControlPanel::onCommand(int cmd) +{ + switch (cmd) { + case IDC_PROPERTIES: + SendMessage(m_hSTIcon, WM_COMMAND, ID_OPTIONS, 0); + return false; + case IDC_ADD_CLIENT: + SendMessage(m_hSTIcon, WM_COMMAND, ID_CONNECT, 0); + return false; + case IDC_KILL_ALL: + { + SendCommand(2, -1); + return false; + } + case IDC_KILL_SEL_CLIENT: + { + SendCommand(3, 3); + return false; + } + case IDC_VIEW_ONLY: + { + SendCommand(3, 1); + return false; + } + case IDC_FULL_CONTROL: + { + SendCommand(3, 0); + return false; + } + case IDC_STOP_UPDATE: + { + stop_updating = true; + EndDialog(handle, 0); + return false; + } + case IDC_DISABLE_CLIENTS: + { + ListConnStatus.setDisable(isItemChecked(IDC_DISABLE_CLIENTS)); + SendCommand(3, -1); + return false; + } + } + return false; + +} + +void ControlPanel::UpdateListView(rfb::ListConnInfo* LCInfo) +{ + getSelConnInfo(); + DeleteAllLVItem(IDC_LIST_CONNECTIONS, handle); + setItemChecked(IDC_DISABLE_CLIENTS, LCInfo->getDisable()); + + if(LCInfo->Empty()) + return; + + ListConn.Copy(LCInfo); + + char* ItemString[3]; + int i = 0; + + for (ListConn.iBegin(); !ListConn.iEnd(); ListConn.iNext()) { + ListConn.iGetCharInfo(ItemString); + InsertLVItem(IDC_LIST_CONNECTIONS, handle, i, ItemString, 3); + for (ListSelConn.iBegin(); !ListSelConn.iEnd(); ListSelConn.iNext()) { + if (ListSelConn.iGetConn() == ListConn.iGetConn()) + SelectLVItem(IDC_LIST_CONNECTIONS, handle, i); + } + i++; + } +} + +BOOL ControlPanel::dialogProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) +{ + switch (msg) { + case WM_INITDIALOG: + handle = hwnd; + initDialog(); + return TRUE; + case WM_DESTROY: + if (stop_updating) { + stop_updating = false; + SendCommand(3, 2); + } + return TRUE; + case WM_COMMAND: + switch (LOWORD(wParam)) { + case IDCANCEL: + handle = NULL; + EndDialog(hwnd, 0); + return TRUE; + default: + return onCommand(LOWORD(wParam)); + } + } + return FALSE; +} + +void ControlPanel::getSelConnInfo() +{ + int i = 0; + ListSelConn.Clear(); + if(ListConn.Empty()) return; + for (ListConn.iBegin(); !ListConn.iEnd(); ListConn.iNext()) { + if (IsSelectedLVItem(IDC_LIST_CONNECTIONS, handle, i)) + ListSelConn.iAdd(&ListConn); + i++; + } +} + +void ControlPanel::SendCommand(DWORD command, int data) +{ + COPYDATASTRUCT copyData; + copyData.dwData = command; + copyData.lpData = 0; + getSelConnInfo(); + if (data != -1) { + ListConnStatus.Copy(&ListSelConn); + ListConnStatus.setAllStatus(data); + ListConnStatus.setDisable(isItemChecked(IDC_DISABLE_CLIENTS)); + copyData.cbData = (DWORD)&ListConnStatus; + } else { + ListConnStatus.Clear(); + } + copyData.cbData = (DWORD)&ListConnStatus; + SendMessage(m_hSTIcon, WM_COPYDATA, 0, (LPARAM)©Data); +} + +ControlPanel::~ControlPanel() +{ + +} diff --git a/win/winvnc/ControlPanel.h b/win/winvnc/ControlPanel.h new file mode 100644 index 00000000..73b859f8 --- /dev/null +++ b/win/winvnc/ControlPanel.h @@ -0,0 +1,45 @@ +// ControlPanel.h: interface for the ControlPanel class. +// +////////////////////////////////////////////////////////////////////// + +#ifndef AFX_CONTROLPANEL_H__ +#define AFX_CONTROLPANEL_H__ + + +#pragma once + + +#include <list> +#include <winvnc/resource.h> +#include <rfb_win32/Dialog.h> +#include <rfb_win32/ListViewControl.h> +#include <rfb_win32/Win32Util.h> +#include <rfb/ListConnInfo.h> + +namespace winvnc { + + class ControlPanel : rfb::win32::Dialog, rfb::win32::ListViewControl { + public: + ControlPanel(HWND hSTIcon) : Dialog(GetModuleHandle(0)), ListViewControl(){ + m_hSTIcon = hSTIcon; + stop_updating = false; + }; + virtual bool showDialog(); + virtual void initDialog(); + virtual bool onCommand(int cmd); + void UpdateListView(rfb::ListConnInfo* LCInfo); + HWND GetHandle() {return handle;}; + void SendCommand(DWORD command, int data); + ~ControlPanel(); + rfb::ListConnInfo ListConnStatus; + protected: + virtual BOOL dialogProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam); + void getSelConnInfo(); + HWND m_hSTIcon; + rfb::ListConnInfo ListConn; + rfb::ListConnInfo ListSelConn; + bool stop_updating; + }; +}; + +#endif
\ No newline at end of file diff --git a/win/winvnc/JavaViewer.cxx b/win/winvnc/JavaViewer.cxx new file mode 100644 index 00000000..15c05c46 --- /dev/null +++ b/win/winvnc/JavaViewer.cxx @@ -0,0 +1,98 @@ +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +#include <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, + int* contentLength, + time_t* lastModified) +{ + 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/win/winvnc/JavaViewer.h b/win/winvnc/JavaViewer.h new file mode 100644 index 00000000..ecda4d3e --- /dev/null +++ b/win/winvnc/JavaViewer.h @@ -0,0 +1,55 @@ +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +// -=- 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, + int* contentLength, time_t* lastModified); + + // 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/win/winvnc/ManagedListener.cxx b/win/winvnc/ManagedListener.cxx new file mode 100644 index 00000000..9bf1b9a1 --- /dev/null +++ b/win/winvnc/ManagedListener.cxx @@ -0,0 +1,94 @@ +/* Copyright (C) 2005 RealVNC Ltd. All Rights Reserved. + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ +#include <winvnc/ManagedListener.h> +#include <rfb/LogWriter.h> + +using namespace winvnc; +using namespace rfb; +using namespace win32; + +static LogWriter vlog("ManagedListener"); + + +ManagedListener::ManagedListener(SocketManager* mgr) +: sock(0), filter(0), manager(mgr), addrChangeNotifier(0), server(0), port(0), localOnly(false) { +} + +ManagedListener::~ManagedListener() { + if (sock) + manager->remListener(sock); + delete filter; +} + + +void ManagedListener::setServer(network::SocketServer* svr) { + if (svr == server) + return; + vlog.info("set server to %p", svr); + server = svr; + refresh(); +} + +void ManagedListener::setPort(int port_, bool localOnly_) { + if ((port_ == port) && (localOnly == localOnly_)) + return; + vlog.info("set port to %d", port_); + port = port_; + localOnly = localOnly_; + refresh(); +} + +void ManagedListener::setFilter(const char* filterStr) { + vlog.info("set filter to %s", filterStr); + delete filter; + filter = new network::TcpFilter(filterStr); + if (sock && !localOnly) + sock->setFilter(filter); +} + +void ManagedListener::setAddressChangeNotifier(SocketManager::AddressChangeNotifier* acn) { + if (acn == addrChangeNotifier) + return; + addrChangeNotifier = acn; + refresh(); +} + + +void ManagedListener::refresh() { + if (sock) + manager->remListener(sock); + sock = 0; + if (!server) + return; + try { + if (port) + sock = new network::TcpListener(port, localOnly); + } catch (rdr::Exception& e) { + vlog.error(e.str()); + } + if (sock) { + if (!localOnly) + sock->setFilter(filter); + try { + manager->addListener(sock, server, addrChangeNotifier); + } catch (...) { + sock = 0; + throw; + } + } +} diff --git a/win/winvnc/ManagedListener.h b/win/winvnc/ManagedListener.h new file mode 100644 index 00000000..e83aa0b6 --- /dev/null +++ b/win/winvnc/ManagedListener.h @@ -0,0 +1,57 @@ +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +#ifndef __VNCSERVER_MANAGED_LISTENER_H__ +#define __VNCSERVER_MANAGED_LISTENER_H__ + +#include <winsock2.h> +#include <network/TcpSocket.h> +#include <rfb_win32/SocketManager.h> + +namespace winvnc { + + // -=- ManagedListener + // Wrapper class which simplifies the management of a listening socket + // on a specified port, attached to a SocketManager and SocketServer. + // Reopens sockets & reconfigures filters & callbacks as appropriate. + // Handles addition/removal of Listeners from SocketManager internally. + + class ManagedListener { + public: + ManagedListener(rfb::win32::SocketManager* mgr); + ~ManagedListener(); + + void setServer(network::SocketServer* svr); + void setPort(int port, bool localOnly=false); + void setFilter(const char* filter); + void setAddressChangeNotifier(rfb::win32::SocketManager::AddressChangeNotifier* acn); + + network::TcpListener* sock; + protected: + void refresh(); + network::TcpFilter* filter; + rfb::win32::SocketManager* manager; + rfb::win32::SocketManager::AddressChangeNotifier* addrChangeNotifier; + network::SocketServer* server; + int port; + bool localOnly; + }; + +}; + +#endif diff --git a/win/winvnc/QueryConnectDialog.cxx b/win/winvnc/QueryConnectDialog.cxx new file mode 100644 index 00000000..dc50eab3 --- /dev/null +++ b/win/winvnc/QueryConnectDialog.cxx @@ -0,0 +1,100 @@ +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +#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/win/winvnc/QueryConnectDialog.h b/win/winvnc/QueryConnectDialog.h new file mode 100644 index 00000000..b28de198 --- /dev/null +++ b/win/winvnc/QueryConnectDialog.h @@ -0,0 +1,60 @@ +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +// -=- 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/win/winvnc/STrayIcon.cxx b/win/winvnc/STrayIcon.cxx new file mode 100644 index 00000000..0b10fa5b --- /dev/null +++ b/win/winvnc/STrayIcon.cxx @@ -0,0 +1,280 @@ +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +// -=- WinVNC Version 4.0 Tray Icon implementation + +#include <winvnc/STrayIcon.h> +#include <winvnc/VNCServerService.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/MsgBox.h> +#include <rfb_win32/Service.h> +#include <rfb_win32/CurrentUser.h> +#include <winvnc/ControlPanel.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); +BoolParameter STrayIconThread::disableClose("DisableClose", "Disable the Close 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("-noconsole -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); + CPanel = new ControlPanel(getHandle()); + } + + virtual LRESULT processMessage(UINT msg, WPARAM wParam, LPARAM lParam) { + switch(msg) { + + case WM_USER: + { + bool userKnown = CurrentUserToken().canImpersonate(); + bool allowOptions = !STrayIconThread::disableOptions && userKnown; + bool allowClose = !STrayIconThread::disableClose && userKnown; + + switch (lParam) { + case WM_LBUTTONDBLCLK: + SendMessage(getHandle(), WM_COMMAND, ID_CONTR0L_PANEL, 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, ID_CONTR0L_PANEL, 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, (!allowClose ? MF_GRAYED : MF_ENABLED) | MF_BYCOMMAND); + + thread.server.getClientsInfo(&LCInfo); + CheckMenuItem(trayMenu, ID_DISABLE_NEW_CLIENTS, (LCInfo.getDisable() ? MF_CHECKED : MF_UNCHECKED) | 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_CONTR0L_PANEL: + CPanel->showDialog(); + break; + case ID_DISABLE_NEW_CLIENTS: + { + thread.server.getClientsInfo(&LCInfo); + LCInfo.setDisable(!LCInfo.getDisable()); + thread.server.setClientsStatus(&LCInfo); + CPanel->UpdateListView(&LCInfo); + } + break; + case ID_OPTIONS: + { + CurrentUserToken token; + if (token.canImpersonate()) + vncConfig.start(isServiceProcess() ? (HANDLE)token : INVALID_HANDLE_VALUE); + else + vlog.error("Options: unknown current user"); + } + break; + case ID_CONNECT: + { + CurrentUserToken token; + if (token.canImpersonate()) + vncConnect.start(isServiceProcess() ? (HANDLE)token : INVALID_HANDLE_VALUE); + else + vlog.error("Options: unknown current user"); + } + break; + case ID_DISCONNECT: + thread.server.disconnectClients("tray menu disconnect"); + break; + case ID_CLOSE: + if (MsgBox(0, _T("Are you sure you want to close the server?"), + MB_ICONQUESTION | MB_YESNO | MB_DEFBUTTON2) == IDYES) { + if (isServiceProcess()) { + ImpersonateCurrentUser icu; + try { + rfb::win32::stopService(VNCServerService::Name); + } catch (rdr::Exception& e) { + MsgBox(0, TStr(e.str()), MB_ICONERROR | MB_OK); + } + } else { + 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; + return thread.server.addNewClient(viewer.buf) ? 1 : 0; + } + case 2: + return thread.server.disconnectClients("IPC disconnect") ? 1 : 0; + case 3: + thread.server.setClientsStatus((rfb::ListConnInfo *)command->cbData); + case 4: + thread.server.getClientsInfo(&LCInfo); + CPanel->UpdateListView(&LCInfo); + break; + }; + }; + break; + + case WM_CLOSE: + PostQuitMessage(0); + break; + + case WM_TIMER: + if (rfb::win32::desktopChangeRequired()) { + SendMessage(getHandle(), WM_CLOSE, 0, 0); + return 0; + } + + thread.server.getClientsInfo(&LCInfo); + CPanel->UpdateListView(&LCInfo); + + setIcon(thread.server.isServerInUse() ? + (!LCInfo.getDisable() ? thread.activeIcon : thread.dis_activeIcon) : + (!LCInfo.getDisable() ? thread.inactiveIcon : thread.dis_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; + ControlPanel * CPanel; + rfb::ListConnInfo LCInfo; +}; + + +STrayIconThread::STrayIconThread(VNCServerWin32& sm, UINT inactiveIcon_, UINT activeIcon_, + UINT dis_inactiveIcon_, UINT dis_activeIcon_, UINT menu_) +: Thread("TrayIcon"), server(sm), inactiveIcon(inactiveIcon_), activeIcon(activeIcon_), + dis_inactiveIcon(dis_inactiveIcon_), dis_activeIcon(dis_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/win/winvnc/STrayIcon.h b/win/winvnc/STrayIcon.h new file mode 100644 index 00000000..309d3f4a --- /dev/null +++ b/win/winvnc/STrayIcon.h @@ -0,0 +1,61 @@ +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +#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 dis_inactiveIcon, UINT dis_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; + static rfb::BoolParameter disableClose; + + friend class STrayIcon; + protected: + rfb::Mutex lock; + HWND windowHandle; + rfb::TCharArray toolTip; + VNCServerWin32& server; + UINT inactiveIcon; + UINT activeIcon; + UINT dis_inactiveIcon; + UINT dis_activeIcon; + UINT menu; + bool runTrayIcon; + }; + +}; + +#endif diff --git a/win/winvnc/VNCServerService.cxx b/win/winvnc/VNCServerService.cxx new file mode 100644 index 00000000..2ef2ee08 --- /dev/null +++ b/win/winvnc/VNCServerService.cxx @@ -0,0 +1,52 @@ +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +// -=- 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/win/winvnc/VNCServerService.h b/win/winvnc/VNCServerService.h new file mode 100644 index 00000000..c7a76cce --- /dev/null +++ b/win/winvnc/VNCServerService.h @@ -0,0 +1,41 @@ +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +#ifndef __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(); + + static const TCHAR* Name; + protected: + VNCServerWin32& server; + }; + +}; + +#endif diff --git a/win/winvnc/VNCServerWin32.cxx b/win/winvnc/VNCServerWin32.cxx new file mode 100644 index 00000000..3b0e1a0a --- /dev/null +++ b/win/winvnc/VNCServerWin32.cxx @@ -0,0 +1,333 @@ +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +// -=- WinVNC Version 4.0 Main Routine + +#include <winvnc/VNCServerWin32.h> +#include <winvnc/resource.h> +#include <winvnc/STrayIcon.h> +#include <rfb_win32/ComputerName.h> +#include <rfb_win32/CurrentUser.h> +#include <rfb_win32/Service.h> +#include <rfb/SSecurityFactoryStandard.h> +#include <rfb/Hostname.h> +#include <rfb/LogWriter.h> +#include <rfb_win32/SFileTransferWin32.h> + +using namespace rfb; +using namespace win32; +using namespace winvnc; +using namespace network; + +static LogWriter vlog("VNCServerWin32"); + + +const TCHAR* winvnc::VNCServerWin32::RegConfigPath = _T("Software\\TightVNC\\WinVNC4"); + +// FIXME: Move into an .h file? +extern const UINT VNCM_FT_DOWNLOAD; + + +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", "+0.0.0.0/0.0.0.0"); +static BoolParameter localHost("LocalHost", + "Only accept connections from via the local loop-back network interface", false); +static BoolParameter queryOnlyIfLoggedOn("QueryOnlyIfLoggedOn", + "Only prompt for a local user to accept incoming connections if there is a user logged on", false); + + +VNCServerWin32::VNCServerWin32() + : command(NoCommand), commandSig(commandLock), + commandEvent(CreateEvent(0, TRUE, FALSE, 0)), + vncServer(CStr(ComputerName().buf), &desktop), + hostThread(0), runServer(false), isDesktopStarted(false), + httpServer(&vncServer), config(&sockMgr), trayIcon(0), + rfbSock(&sockMgr), httpSock(&sockMgr), + queryConnectDialog(0) +{ + // 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); + + // Register the queued command event to be handled + sockMgr.addEvent(commandEvent, this); + + vncServer.setFTManager((rfb::SFileTransferManager *)&m_FTManager); +} + +VNCServerWin32::~VNCServerWin32() { + delete trayIcon; + + // Stop the SDisplay from updating our state + desktop.setStatusLocation(0); + + // Join the Accept/Reject dialog thread + if (queryConnectDialog) + delete queryConnectDialog->join(); +} + + +void VNCServerWin32::processAddressChange(network::SocketListener* sock_) { + if (!trayIcon || (sock_ != rfbSock.sock)) + return; + + // Tool-tip prefix depends on server mode + const TCHAR* prefix = _T("VNC Server (User):"); + if (isServiceProcess()) + prefix = _T("VNC Server (Service):"); + + // Fetch the list of addresses + std::list<char*> addrs; + if (rfbSock.sock) + rfbSock.sock->getMyAddresses(&addrs); + else + addrs.push_front(strDup("Not accepting connections")); + + // Allocate space for the new tip + std::list<char*>::iterator i, next_i; + int length = _tcslen(prefix)+1; + for (i=addrs.begin(); i!= addrs.end(); i++) + length += strlen(*i) + 1; + + // Build the new tip + 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(",")); + } + + // Pass the new tip to the tray icon + vlog.info("Refreshing tray icon"); + trayIcon->setToolTip(toolTip.buf); +} + +void VNCServerWin32::regConfigChanged() { + // -=- Make sure we're listening on the right ports. + rfbSock.setServer(&vncServer); + rfbSock.setPort(port_number, localHost); + httpSock.setServer(&httpServer); + httpSock.setPort(http_port, localHost); + + // -=- Update the Java viewer's web page port number. + httpServer.setRFBport(rfbSock.sock ? port_number : 0); + + // -=- Update the TCP address filter for both ports, if open. + CharArray pattern(hosts.getData()); + rfbSock.setFilter(pattern.buf); + httpSock.setFilter(pattern.buf); + + // -=- Update the tray icon tooltip text with IP addresses + processAddressChange(rfbSock.sock); +} + + +int VNCServerWin32::run() { + { Lock l(runLock); + hostThread = Thread::self(); + runServer = true; + } + + // - Create the tray icon (if possible) + trayIcon = new STrayIconThread(*this, IDI_ICON, IDI_CONNECTED, + IDI_ICON_DISABLE, IDI_CONNECTED_DISABLE, + IDR_TRAY); + + // - Register for notification of configuration changes + config.setCallback(this); + if (isServiceProcess()) + config.setKey(HKEY_LOCAL_MACHINE, RegConfigPath); + else + config.setKey(HKEY_CURRENT_USER, RegConfigPath); + + // - Set the address-changed handler for the RFB socket + rfbSock.setAddressChangeNotifier(this); + + DWORD result = 0; + try { + vlog.debug("Entering message loop"); + + // - Run the server until we're told to quit + MSG msg; + int result = 0; + while (runServer) { + result = sockMgr.getMessage(&msg, NULL, 0, 0); + if (result < 0) + throw rdr::SystemException("getMessage", GetLastError()); + if (!isServiceProcess() && (result == 0)) + break; + if (msg.message == VNCM_FT_DOWNLOAD) + m_FTManager.processDownloadMsg(msg); + TranslateMessage(&msg); + DispatchMessage(&msg); + } + + 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; + if (hostThread) + 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; +} + +bool VNCServerWin32::getClientsInfo(rfb::ListConnInfo* LCInfo) { + return queueCommand(GetClientsInfo, LCInfo, 0); +} + +bool VNCServerWin32::setClientsStatus(rfb::ListConnInfo* LCInfo) { + return queueCommand(SetClientsStatus, LCInfo, 0); +} + +VNCServerST::queryResult VNCServerWin32::queryConnection(network::Socket* sock, + const char* userName, + char** reason) +{ + if (queryOnlyIfLoggedOn && CurrentUserToken().noUserLoggedOn()) + return VNCServerST::ACCEPT; + 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() { + queueCommand(QueryConnectionComplete, 0, 0, false); +} + + +bool VNCServerWin32::queueCommand(Command cmd, const void* data, int len, bool wait) { + Lock l(commandLock); + while (command != NoCommand) + commandSig.wait(); + command = cmd; + commandData = data; + commandDataLen = len; + SetEvent(commandEvent); + if (wait) { + while (command != NoCommand) + commandSig.wait(); + commandSig.signal(); + } + return true; +} + +void VNCServerWin32::processEvent(HANDLE event_) { + ResetEvent(event_); + + if (event_ == commandEvent.h) { + // If there is no command queued then return immediately + { + 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 + sockMgr.addSocket((network::Socket*)commandData, &vncServer); + break; + case GetClientsInfo: + vncServer.getConnInfo((ListConnInfo*)commandData); + break; + case SetClientsStatus: + vncServer.setConnStatus((ListConnInfo*)commandData); + 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"); + delete queryConnectDialog->join(); + queryConnectDialog = 0; + break; + + default: + vlog.error("unknown command %d queued", command); + }; + + // Clear the command and signal completion + { + Lock l(commandLock); + command = NoCommand; + commandSig.signal(); + } + } +} + diff --git a/win/winvnc/VNCServerWin32.h b/win/winvnc/VNCServerWin32.h new file mode 100644 index 00000000..579a6a0b --- /dev/null +++ b/win/winvnc/VNCServerWin32.h @@ -0,0 +1,130 @@ +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +#ifndef __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 <rfb_win32/SFileTransferManagerWin32.h> +#include <winvnc/QueryConnectDialog.h> +#include <winvnc/JavaViewer.h> +#include <winvnc/ManagedListener.h> + +namespace winvnc { + + class STrayIconThread; + + class VNCServerWin32 : rfb::VNCServerST::QueryConnectionHandler, + rfb::win32::SocketManager::AddressChangeNotifier, + rfb::win32::RegConfig::Callback, + rfb::win32::EventHandler { + 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(); + + // Where to read the configuration settings from + static const TCHAR* RegConfigPath; + + bool getClientsInfo(rfb::ListConnInfo* LCInfo); + + bool setClientsStatus(rfb::ListConnInfo* LCInfo); + + protected: + // VNCServerST::QueryConnectionHandler interface + // 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); + + // SocketManager::AddressChangeNotifier interface + // Used to keep tray icon up to date + virtual void processAddressChange(network::SocketListener* sl); + + // RegConfig::Callback interface + // Called via the EventManager whenver RegConfig sees the registry change + virtual void regConfigChanged(); + + // EventHandler interface + // Used to perform queued commands + virtual void processEvent(HANDLE event); + + protected: + // Perform a particular internal function in the server thread + typedef enum {NoCommand, DisconnectClients, AddClient, QueryConnectionComplete, SetClientsStatus, GetClientsInfo} Command; + bool queueCommand(Command cmd, const void* data, int len, bool wait=true); + Command command; + const void* commandData; + int commandDataLen; + rfb::Mutex commandLock; + rfb::Condition commandSig; + rfb::win32::Handle commandEvent; + + // 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::SocketManager sockMgr; + rfb::win32::RegConfig config; + + ManagedListener rfbSock; + ManagedListener httpSock; + STrayIconThread* trayIcon; + + //rfb::SSecurityFactoryStandard securityFactory; + + QueryConnectDialog* queryConnectDialog; + rfb::win32::SFileTransferManagerWin32 m_FTManager; + }; + +}; + +#endif diff --git a/win/winvnc/buildTime.cxx b/win/winvnc/buildTime.cxx new file mode 100644 index 00000000..9f37b387 --- /dev/null +++ b/win/winvnc/buildTime.cxx @@ -0,0 +1,18 @@ +/* Copyright (C) 2005 RealVNC Ltd. All Rights Reserved. + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ +const char* buildTime = "Built on " __DATE__ " at " __TIME__; diff --git a/win/winvnc/connecte.ico b/win/winvnc/connecte.ico Binary files differnew file mode 100644 index 00000000..8f814b33 --- /dev/null +++ b/win/winvnc/connecte.ico diff --git a/win/winvnc/connected.ico b/win/winvnc/connected.ico Binary files differnew file mode 100644 index 00000000..44ce3c97 --- /dev/null +++ b/win/winvnc/connected.ico diff --git a/win/winvnc/icon_dis.ico b/win/winvnc/icon_dis.ico Binary files differnew file mode 100644 index 00000000..0a6ea15c --- /dev/null +++ b/win/winvnc/icon_dis.ico diff --git a/win/winvnc/resource.h b/win/winvnc/resource.h new file mode 100644 index 00000000..68316be9 --- /dev/null +++ b/win/winvnc/resource.h @@ -0,0 +1,54 @@ +//{{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 IDB_BITMAP 109 +#define IDD_CONTROL_PANEL 110 +#define IDI_ICON_DISABLE 111 +#define IDI_CONNECTED_DISABLE 112 +#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 IDC_LIST_CONNECTIONS 1012 +#define IDC_STATIC_KLIENTS_LIST 1013 +#define IDC_STATIC_SELECTED_KLIENTS 1014 +#define IDC_VIEW_ONLY 1015 +#define IDC_FULL_CONTROL 1016 +#define IDC_STOP_UPDATE 1017 +#define IDC_KILL_SEL_CLIENT 1018 +#define IDC_PROPERTIES 1019 +#define IDC_ADD_CLIENT 1020 +#define IDC_KILL_ALL 1021 +#define IDC_DISABLE_CLIENTS 1022 +#define ID_CONTR0L_PANEL 40001 +#define ID_CLOSE 40002 +#define ID_ABOUT 40003 +#define ID_DISCONNECT 40004 +#define ID_CONNECT 40005 +#define ID_OPTIONS 40006 +#define ID_DISABLE_NEW_CLIENTS 40007 + +// Next default values for new objects +// +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS +#define _APS_NEXT_RESOURCE_VALUE 113 +#define _APS_NEXT_COMMAND_VALUE 40008 +#define _APS_NEXT_CONTROL_VALUE 1023 +#define _APS_NEXT_SYMED_VALUE 101 +#endif +#endif diff --git a/win/winvnc/winvnc.bmp b/win/winvnc/winvnc.bmp Binary files differnew file mode 100644 index 00000000..90c02f89 --- /dev/null +++ b/win/winvnc/winvnc.bmp diff --git a/win/winvnc/winvnc.cxx b/win/winvnc/winvnc.cxx new file mode 100644 index 00000000..2d01f893 --- /dev/null +++ b/win/winvnc/winvnc.cxx @@ -0,0 +1,262 @@ +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +// -=- 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/MsgBox.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. +// + +static 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"))); +} + +static 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(); +} + +static void MsgBoxOrLog(const char* msg, bool isError=false) { + if (close_console) { + MsgBox(0, TStr(msg), (isError ? MB_ICONERROR : MB_ICONINFORMATION) | MB_OK); + } else { + if (isError) { + try { + vlog.error(msg); + return; + } catch (...) { + } + } + fprintf(stderr, "%s\n", msg); + } +} + +static 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]); + i++; + } else { + AddNewClientDialog ancd; + if (ancd.showDialog()) + host.buf = strDup(ancd.getHostName()); + } + if (host.buf) { + HWND hwnd = FindWindow(0, _T("winvnc::IPC_Interface")); + if (!hwnd) + throw rdr::Exception("Unable to locate existing VNC Server."); + COPYDATASTRUCT copyData; + copyData.dwData = 1; // *** AddNewClient + copyData.cbData = strlen(host.buf); + copyData.lpData = (void*)host.buf; + printf("Sending connect request to VNC Server...\n"); + if (!SendMessage(hwnd, WM_COPYDATA, 0, (LPARAM)©Data)) + MsgBoxOrLog("Connection failed.", true); + } + } else if (strcasecmp(argv[i], "-disconnect") == 0) { + runServer = false; + HWND hwnd = FindWindow(0, _T("winvnc::IPC_Interface")); + if (!hwnd) + throw rdr::Exception("Unable to locate existing VNC Server."); + COPYDATASTRUCT copyData; + copyData.dwData = 2; // *** DisconnectClients + copyData.lpData = 0; + copyData.cbData = 0; + printf("Sending disconnect request to VNC Server...\n"); + if (!SendMessage(hwnd, WM_COPYDATA, 0, (LPARAM)©Data)) + MsgBoxOrLog("Failed to disconnect clients.", true); + } else if (strcasecmp(argv[i], "-start") == 0) { + printf("Attempting to start service...\n"); + runServer = false; + if (rfb::win32::startService(VNCServerService::Name)) + MsgBoxOrLog("Started service successfully"); + } else if (strcasecmp(argv[i], "-stop") == 0) { + printf("Attempting to stop service...\n"); + runServer = false; + if (rfb::win32::stopService(VNCServerService::Name)) + MsgBoxOrLog("Stopped service successfully"); + } else if (strcasecmp(argv[i], "-status") == 0) { + printf("Querying service status...\n"); + runServer = false; + DWORD state = rfb::win32::getServiceState(VNCServerService::Name); + CharArray stateStr(rfb::win32::serviceStateName(state)); + const char* stateMsg = "The %s Service is in the %s state."; + CharArray result(strlen(stateStr.buf) + _tcslen(VNCServerService::Name) + strlen(stateMsg) + 1); + sprintf(result.buf, stateMsg, (const char*)CStr(VNCServerService::Name), stateStr.buf); + MsgBoxOrLog(result.buf); + } 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])) + MsgBoxOrLog("Registered service successfully"); + } else if (strcasecmp(argv[i], "-unregister") == 0) { + printf("Attempting to unregister service...\n"); + runServer = false; + if (rfb::win32::unregisterService(VNCServerService::Name)) + MsgBoxOrLog("Unregistered service successfully"); + + } else if (strcasecmp(argv[i], "-noconsole") == 0) { + close_console = true; + vlog.info("closing console"); + if (!FreeConsole()) + vlog.info("unable to close console:%u", GetLastError()); + + } 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) { + MsgBoxOrLog(e.str(), true); + } + } +} + + +// +// -=- 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) { + // Start the network subsystem and run the server + 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) { + MsgBoxOrLog(e.str(), true); + } + + vlog.debug("WinVNC process quitting"); + return result; +} diff --git a/win/winvnc/winvnc.dsp b/win/winvnc/winvnc.dsp new file mode 100644 index 00000000..ec76f3f2 --- /dev/null +++ b/win/winvnc/winvnc.dsp @@ -0,0 +1,252 @@ +# 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\winvnc"
+# 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 ".." /I "../../common" /FI"rdr/msvcwarning.h" /D "NDEBUG" /D "_CONSOLE" /D "WIN32" /D "_MBCS" /FR /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 /Fo..\Release\ /Fd..\Release\winvnc /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\winvnc"
+# 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 ".." /I "../../common" /FI"rdr/msvcwarning.h" /D "_DEBUG" /D "_CONSOLE" /D "WIN32" /D "_MBCS" /FR /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 /Fo..\Debug\ /Fd..\Debug\winvnc /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\winvnc"
+# 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 ".." /I "../../common" /FI"rdr/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 /Fo..\Debug_Unicode /Fd..\Debug_Unicode\winvnc /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=.\ControlPanel.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\JavaViewer.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\ManagedListener.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=.\ControlPanel.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\JavaViewer.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\ManagedListener.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=.\connecte.ico
+# End Source File
+# Begin Source File
+
+SOURCE=.\connected.ico
+# End Source File
+# Begin Source File
+
+SOURCE=.\icon_dis.ico
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\common\javabin\logo150x150.gif
+# End Source File
+# Begin Source File
+
+SOURCE=.\winvnc.bmp
+# End Source File
+# Begin Source File
+
+SOURCE=.\winvnc.ico
+# End Source File
+# End Group
+# Begin Source File
+
+SOURCE=..\..\common\javabin\index.vnc
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\common\javabin\vncviewer.jar
+# End Source File
+# Begin Source File
+
+SOURCE=.\winvnc4.exe.manifest
+# End Source File
+# End Target
+# End Project
diff --git a/win/winvnc/winvnc.ico b/win/winvnc/winvnc.ico Binary files differnew file mode 100644 index 00000000..1b42416c --- /dev/null +++ b/win/winvnc/winvnc.ico diff --git a/win/winvnc/winvnc.rc b/win/winvnc/winvnc.rc new file mode 100644 index 00000000..143b0d6f --- /dev/null +++ b/win/winvnc/winvnc.rc @@ -0,0 +1,328 @@ +//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,1,1,0 + PRODUCTVERSION 4,1,1,0 + 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", "Constantin Kaplinsky\0" + VALUE "FileDescription", "TightVNC Server for Win32\0" + VALUE "FileVersion", "4.1.1\0" + VALUE "InternalName", "winvnc\0" + VALUE "LegalCopyright", "Copyright (C) 1998-2005 [many holders]\0" + VALUE "LegalTrademarks", "TightVNC\0" + VALUE "OriginalFilename", "winvnc4.exe\0" + VALUE "PrivateBuild", "\0" + VALUE "ProductName", "TightVNC Server\0" + VALUE "ProductVersion", "4.1.1\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" +IDI_ICON_DISABLE ICON DISCARDABLE "icon_dis.ico" +IDI_CONNECTED_DISABLE ICON DISCARDABLE "connecte.ico" + +///////////////////////////////////////////////////////////////////////////// +// +// Menu +// + +IDR_TRAY MENU DISCARDABLE +BEGIN + POPUP "Tray Menu" + BEGIN + MENUITEM "Control &Panel", ID_CONTR0L_PANEL + MENUITEM SEPARATOR + MENUITEM "&Options...", ID_OPTIONS + MENUITEM "Add &New Client...", ID_CONNECT + MENUITEM "&Disconnect Clients", ID_DISCONNECT + MENUITEM "D&isable New Clients", ID_DISABLE_NEW_CLIENTS + 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 + CONTROL 109,IDC_STATIC,"Static",SS_BITMAP,5,10,33,31 + LTEXT ">appname<",IDC_DESCRIPTION,45,10,125,15 + LTEXT ">version<",IDC_VERSION,170,10,72,15 + LTEXT ">buildtime<",IDC_BUILDTIME,45,25,202,15 + LTEXT ">copyright<",IDC_COPYRIGHT,45,40,202,15 + LTEXT "See http://www.tightvnc.com for more information on VNC.", + IDC_STATIC,45,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, 177, 52 +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,80,10,90,15,ES_AUTOHSCROLL + DEFPUSHBUTTON "OK",IDOK,80,31,40,14 + PUSHBUTTON "Cancel",IDCANCEL,125,31,45,14 + CONTROL 109,IDC_STATIC,"Static",SS_BITMAP | SS_REALSIZEIMAGE,7, + 10,33,31 + RTEXT "Viewer:",IDC_STATIC,45,10,30,15,SS_CENTERIMAGE +END + +IDD_CONTROL_PANEL DIALOG DISCARDABLE 0, 0, 267, 238 +STYLE DS_MODALFRAME | DS_SETFOREGROUND | DS_CENTER | DS_CONTEXTHELP | + WS_VISIBLE | WS_CAPTION | WS_SYSMENU +CAPTION "Control Panel" +FONT 8, "MS Sans Serif" +BEGIN + CONTROL "List1",IDC_LIST_CONNECTIONS,"SysListView32",LVS_REPORT | + LVS_SHOWSELALWAYS | LVS_ALIGNLEFT | LVS_NOSORTHEADER | + WS_BORDER | WS_TABSTOP,7,25,253,76 + LTEXT "Authorised clients list",IDC_STATIC_KLIENTS_LIST,87,7, + 74,11,SS_CENTERIMAGE + GROUPBOX "Control of selected clients", + IDC_STATIC_SELECTED_KLIENTS,7,108,124,103 + PUSHBUTTON "View-only",IDC_VIEW_ONLY,13,121,111,14 + PUSHBUTTON "Full control ",IDC_FULL_CONTROL,13,145,112,14 + PUSHBUTTON "Stop updating",IDC_STOP_UPDATE,13,167,111,14 + PUSHBUTTON "Kill Clients",IDC_KILL_SEL_CLIENT,13,190,111,14 + PUSHBUTTON "Properties",IDC_PROPERTIES,144,121,111,14 + PUSHBUTTON "Add New Client",IDC_ADD_CLIENT,144,145,111,14 + PUSHBUTTON "Kill All Clients",IDC_KILL_ALL,144,167,111,14 + CONTROL "Disable New Clients",IDC_DISABLE_CLIENTS,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,144,191,111,13 + PUSHBUTTON "Close",IDCANCEL,144,217,111,14 +END + + +///////////////////////////////////////////////////////////////////////////// +// +// HTTPFILE +// + +/VNCVIEWER.JAR HTTPFILE DISCARDABLE "..\\..\\common\\javabin\\vncviewer.jar" +/LOGO150X150.GIF HTTPFILE DISCARDABLE "..\\..\\common\\javabin\\logo150x150.gif" +/INDEX.VNC HTTPFILE DISCARDABLE "..\\..\\common\\javabin\\index.vnc" + +///////////////////////////////////////////////////////////////////////////// +// +// 24 +// + +IDR_MANIFEST 24 DISCARDABLE "winvnc4.exe.manifest" + +///////////////////////////////////////////////////////////////////////////// +// +// DESIGNINFO +// + +#ifdef APSTUDIO_INVOKED +GUIDELINES DESIGNINFO DISCARDABLE +BEGIN + IDD_ABOUT, DIALOG + BEGIN + LEFTMARGIN, 5 + VERTGUIDE, 45 + VERTGUIDE, 170 + VERTGUIDE, 195 + VERTGUIDE, 242 + TOPMARGIN, 7 + BOTTOMMARGIN, 85 + HORZGUIDE, 10 + HORZGUIDE, 25 + HORZGUIDE, 40 + HORZGUIDE, 55 + HORZGUIDE, 70 + END + + 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, 170 + VERTGUIDE, 45 + VERTGUIDE, 75 + VERTGUIDE, 80 + VERTGUIDE, 120 + VERTGUIDE, 125 + VERTGUIDE, 170 + TOPMARGIN, 7 + BOTTOMMARGIN, 45 + HORZGUIDE, 10 + HORZGUIDE, 25 + HORZGUIDE, 30 + HORZGUIDE, 45 + END + + IDD_CONTROL_PANEL, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 260 + VERTGUIDE, 13 + VERTGUIDE, 124 + VERTGUIDE, 144 + VERTGUIDE, 255 + TOPMARGIN, 7 + BOTTOMMARGIN, 231 + HORZGUIDE, 121 + HORZGUIDE, 135 + HORZGUIDE, 145 + HORZGUIDE, 159 + HORZGUIDE, 181 + HORZGUIDE, 191 + HORZGUIDE, 204 + HORZGUIDE, 217 + END +END +#endif // APSTUDIO_INVOKED + + +///////////////////////////////////////////////////////////////////////////// +// +// Bitmap +// + +IDB_BITMAP BITMAP DISCARDABLE "winvnc.bmp" +#endif // English (U.K.) resources +///////////////////////////////////////////////////////////////////////////// + + + +#ifndef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 3 resource. +// + + +///////////////////////////////////////////////////////////////////////////// +#endif // not APSTUDIO_INVOKED + diff --git a/win/winvnc/winvnc4.exe.manifest b/win/winvnc/winvnc4.exe.manifest new file mode 100644 index 00000000..69be5a04 --- /dev/null +++ b/win/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="TightVNC.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/win/wm_hooks/resource.h b/win/wm_hooks/resource.h new file mode 100644 index 00000000..0d8f646f --- /dev/null +++ b/win/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/win/wm_hooks/wm_hooks.cxx b/win/wm_hooks/wm_hooks.cxx new file mode 100644 index 00000000..fd01159c --- /dev/null +++ b/win/wm_hooks/wm_hooks.cxx @@ -0,0 +1,465 @@ +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +// -=- wm_hooks.cxx +// +// Window Message Hooks Dynamic Link library + +#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_WINDOWPOSCHANGED: + NotifyWindow(wnd, msg); + 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(" NOTE: 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 TRUE; +} diff --git a/win/wm_hooks/wm_hooks.def b/win/wm_hooks/wm_hooks.def new file mode 100644 index 00000000..b9198ab9 --- /dev/null +++ b/win/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/win/wm_hooks/wm_hooks.dsp b/win/wm_hooks/wm_hooks.dsp new file mode 100644 index 00000000..65e4b866 --- /dev/null +++ b/win/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\wm_hooks"
+# 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 ".." /FI"../../common/rdr/msvcwarning.h" /D "NDEBUG" /D "_MBCS" /D "_WINDOWS" /D "_USRDLL" /D "WM_HOOKS_EXPORTS" /D "WIN32" /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\wm_hooks"
+# 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"../../common/rdr/msvcwarning.h" /D "_DEBUG" /D "_MBCS" /D "_WINDOWS" /D "_USRDLL" /D "WM_HOOKS_EXPORTS" /D "WIN32" /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\wm_hooks"
+# 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"../../common/rdr/msvcwarning.h" /D "_DEBUG" /D "_UNICODE" /D "UNICODE" /D "_WINDOWS" /D "_USRDLL" /D "WM_HOOKS_EXPORTS" /D "WIN32" /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/win/wm_hooks/wm_hooks.h b/win/wm_hooks/wm_hooks.h new file mode 100644 index 00000000..f65412ea --- /dev/null +++ b/win/wm_hooks/wm_hooks.h @@ -0,0 +1,102 @@ +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +// -=- 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__ + +#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/win/wm_hooks/wm_hooks.rc b/win/wm_hooks/wm_hooks.rc new file mode 100644 index 00000000..3f171d26 --- /dev/null +++ b/win/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,1,0,0 + PRODUCTVERSION 4,1,0,0 + 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", "Constantin Kaplinsky\0" + VALUE "FileDescription", "TightVNC Server Hooking DLL for Win32\0" + VALUE "FileVersion", "4.1\0" + VALUE "InternalName", "\0" + VALUE "LegalCopyright", "Copyright (C) 1998-2005 [many holders]\0" + VALUE "LegalTrademarks", "TightVNC\0" + VALUE "OriginalFilename", "wm_hooks.dll\0" + VALUE "PrivateBuild", "\0" + VALUE "ProductName", "TightVNC Server\0" + VALUE "ProductVersion", "4.1\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 + |