summaryrefslogtreecommitdiffstats
path: root/rfb_win32
diff options
context:
space:
mode:
authorConstantin Kaplinsky <const@tightvnc.com>2004-10-08 09:43:57 +0000
committerConstantin Kaplinsky <const@tightvnc.com>2004-10-08 09:43:57 +0000
commit47ed8d321c32c6b741cff1f4ff686165c4f269f4 (patch)
treeda413648adbff4ff10c8ee26124673f8e7cf238a /rfb_win32
parent266bb36cd47555280fffd3aab1ed86683e26d748 (diff)
downloadtigervnc-47ed8d321c32c6b741cff1f4ff686165c4f269f4.tar.gz
tigervnc-47ed8d321c32c6b741cff1f4ff686165c4f269f4.zip
Initial revision
git-svn-id: svn://svn.code.sf.net/p/tigervnc/code/trunk@2 3789f03b-4d11-0410-bbf8-ca57d06f2519
Diffstat (limited to 'rfb_win32')
-rw-r--r--rfb_win32/AboutDialog.cxx49
-rw-r--r--rfb_win32/AboutDialog.h55
-rw-r--r--rfb_win32/CKeyboard.cxx259
-rw-r--r--rfb_win32/CKeyboard.h53
-rw-r--r--rfb_win32/CPointer.cxx188
-rw-r--r--rfb_win32/CPointer.h76
-rw-r--r--rfb_win32/CleanDesktop.cxx255
-rw-r--r--rfb_win32/CleanDesktop.h57
-rw-r--r--rfb_win32/Clipboard.cxx199
-rw-r--r--rfb_win32/Clipboard.h66
-rw-r--r--rfb_win32/CurrentUser.cxx93
-rw-r--r--rfb_win32/CurrentUser.h78
-rw-r--r--rfb_win32/DIBSectionBuffer.cxx221
-rw-r--r--rfb_win32/DIBSectionBuffer.h87
-rw-r--r--rfb_win32/DeviceFrameBuffer.cxx298
-rw-r--r--rfb_win32/DeviceFrameBuffer.h121
-rw-r--r--rfb_win32/Dialog.cxx353
-rw-r--r--rfb_win32/Dialog.h159
-rw-r--r--rfb_win32/IntervalTimer.h70
-rw-r--r--rfb_win32/LaunchProcess.cxx92
-rw-r--r--rfb_win32/LaunchProcess.h59
-rw-r--r--rfb_win32/MsgWindow.cxx116
-rw-r--r--rfb_win32/MsgWindow.h55
-rw-r--r--rfb_win32/OSVersion.cxx47
-rw-r--r--rfb_win32/OSVersion.h54
-rw-r--r--rfb_win32/RegConfig.cxx151
-rw-r--r--rfb_win32/RegConfig.h56
-rw-r--r--rfb_win32/Registry.cxx272
-rw-r--r--rfb_win32/Registry.h111
-rw-r--r--rfb_win32/SDisplay.cxx612
-rw-r--r--rfb_win32/SDisplay.h149
-rw-r--r--rfb_win32/SInput.cxx459
-rw-r--r--rfb_win32/SInput.h70
-rw-r--r--rfb_win32/Security.h198
-rw-r--r--rfb_win32/Service.cxx638
-rw-r--r--rfb_win32/Service.h123
-rw-r--r--rfb_win32/SocketManager.cxx246
-rw-r--r--rfb_win32/SocketManager.h107
-rw-r--r--rfb_win32/TCharArray.cxx85
-rw-r--r--rfb_win32/TCharArray.h119
-rw-r--r--rfb_win32/TrayIcon.h90
-rw-r--r--rfb_win32/WMCursor.cxx98
-rw-r--r--rfb_win32/WMCursor.h65
-rw-r--r--rfb_win32/WMHooks.cxx324
-rw-r--r--rfb_win32/WMHooks.h85
-rw-r--r--rfb_win32/WMNotifier.cxx57
-rw-r--r--rfb_win32/WMNotifier.h68
-rw-r--r--rfb_win32/WMPoller.cxx101
-rw-r--r--rfb_win32/WMPoller.h67
-rw-r--r--rfb_win32/WMShatter.cxx57
-rw-r--r--rfb_win32/WMShatter.h53
-rw-r--r--rfb_win32/WMWindowCopyRect.cxx83
-rw-r--r--rfb_win32/WMWindowCopyRect.h56
-rw-r--r--rfb_win32/Win32Util.cxx447
-rw-r--r--rfb_win32/Win32Util.h217
-rw-r--r--rfb_win32/keymap.h149
-rw-r--r--rfb_win32/msvcwarning.h20
-rw-r--r--rfb_win32/rfb_win32.dsp341
58 files changed, 8934 insertions, 0 deletions
diff --git a/rfb_win32/AboutDialog.cxx b/rfb_win32/AboutDialog.cxx
new file mode 100644
index 00000000..efb15c00
--- /dev/null
+++ b/rfb_win32/AboutDialog.cxx
@@ -0,0 +1,49 @@
+/* Copyright (C) 2002-2003 RealVNC Ltd. All Rights Reserved.
+ *
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+ * USA.
+ */
+
+#include <rfb_win32/AboutDialog.h>
+#include <rfb_win32/Win32Util.h>
+#include <rfb_win32/TCharArray.h>
+#include <rfb/LogWriter.h>
+
+using namespace rfb;
+using namespace rfb::win32;
+
+static LogWriter vlog("AboutDialog");
+
+AboutDialog AboutDialog::instance;
+
+
+AboutDialog::AboutDialog() : Dialog(GetModuleHandle(0)) {
+}
+
+bool AboutDialog::showDialog() {
+ return Dialog::showDialog(MAKEINTRESOURCE(DialogId));
+}
+
+void AboutDialog::initDialog() {
+ // Set the build time field
+ SetWindowText(GetDlgItem(handle, BuildTime), TStr(buildTime));
+
+ // Get our executable's version info
+ FileVersionInfo verInfo;
+
+ SetWindowText(GetDlgItem(handle, Version), verInfo.getVerString(_T("FileVersion")));
+ SetWindowText(GetDlgItem(handle, Copyright), verInfo.getVerString(_T("LegalCopyright")));
+ SetWindowText(GetDlgItem(handle, Description), verInfo.getVerString(_T("FileDescription")));
+}
diff --git a/rfb_win32/AboutDialog.h b/rfb_win32/AboutDialog.h
new file mode 100644
index 00000000..cb1713d4
--- /dev/null
+++ b/rfb_win32/AboutDialog.h
@@ -0,0 +1,55 @@
+/* Copyright (C) 2002-2003 RealVNC Ltd. All Rights Reserved.
+ *
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+ * USA.
+ */
+
+// -=- AboutDialog.h
+
+#ifndef __RFB_WIN32_ABOUT_DIALOG_H__
+#define __RFB_WIN32_ABOUT_DIALOG_H__
+
+#include <rfb_win32/Dialog.h>
+#include <rfb/util.h>
+
+extern const char* buildTime;
+
+namespace rfb {
+
+ namespace win32 {
+
+ class AboutDialog : Dialog {
+ public:
+ AboutDialog();
+ virtual bool showDialog();
+ virtual void initDialog();
+
+ static AboutDialog instance;
+
+ typedef WORD LabelId;
+ static const LabelId DialogId; // Resource ID of the About dialog
+ static const LabelId BuildTime; // Resource ID of the BuildTime label in the dialog
+ static const LabelId Version; // etc...
+ static const LabelId Copyright;
+ static const LabelId Description;
+ protected:
+ WORD dialogId;
+ };
+
+ };
+
+};
+
+#endif
diff --git a/rfb_win32/CKeyboard.cxx b/rfb_win32/CKeyboard.cxx
new file mode 100644
index 00000000..ad852a0e
--- /dev/null
+++ b/rfb_win32/CKeyboard.cxx
@@ -0,0 +1,259 @@
+/* Copyright (C) 2002-2003 RealVNC Ltd. All Rights Reserved.
+ *
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+ * USA.
+ */
+
+#include <map>
+
+#define XK_MISCELLANY
+#define XK_LATIN1
+#define XK_CURRENCY
+#include <rfb/keysymdef.h>
+
+#include <rfb_win32/CKeyboard.h>
+#include <rfb/CMsgWriter.h>
+#include <rfb/LogWriter.h>
+#include <rfb_win32/OSVersion.h>
+#include "keymap.h"
+
+using namespace rfb;
+
+static LogWriter vlog("CKeyboard");
+
+
+// Client-side RFB keyboard event sythesis
+
+class CKeymapper {
+
+public:
+ CKeymapper()
+ {
+ for (int i = 0; i < sizeof(keymap) / sizeof(keymap_t); i++) {
+ int extendedVkey = keymap[i].vk + (keymap[i].extended ? 256 : 0);
+ if (keysymMap.find(extendedVkey) == keysymMap.end()) {
+ keysymMap[extendedVkey] = keymap[i].keysym;
+ }
+ }
+ }
+
+ // lookup() tries to find a match for vkey with the extended flag. We check
+ // first for an exact match including the extended flag, then try without the
+ // extended flag.
+ rdr::U32 lookup(int extendedVkey) {
+ if (keysymMap.find(extendedVkey) != keysymMap.end())
+ return keysymMap[extendedVkey];
+ if (keysymMap.find(extendedVkey ^ 256) != keysymMap.end())
+ return keysymMap[extendedVkey ^ 256];
+ return 0;
+ }
+
+private:
+ std::map<int,rdr::U32> keysymMap;
+} ckeymapper;
+
+
+class ModifierKeyReleaser {
+public:
+ ModifierKeyReleaser(CMsgWriter* writer_, int vkCode, bool extended)
+ : writer(writer_), extendedVkey(vkCode + (extended ? 256 : 0)),
+ keysym(0)
+ {}
+ void release(std::map<int,rdr::U32>* downKeysym) {
+ if (downKeysym->find(extendedVkey) != downKeysym->end()) {
+ keysym = (*downKeysym)[extendedVkey];
+ vlog.debug("fake release extendedVkey 0x%x, keysym 0x%x",
+ extendedVkey, keysym);
+ writer->writeKeyEvent(keysym, false);
+ }
+ }
+ ~ModifierKeyReleaser() {
+ if (keysym) {
+ vlog.debug("fake press extendedVkey 0x%x, keysym 0x%x",
+ extendedVkey, keysym);
+ writer->writeKeyEvent(keysym, true);
+ }
+ }
+ CMsgWriter* writer;
+ int extendedVkey;
+ rdr::U32 keysym;
+};
+
+// IS_PRINTABLE_LATIN1 tests if a character is either a printable latin1
+// character, or 128, which is the Euro symbol on Windows.
+#define IS_PRINTABLE_LATIN1(c) (((c) >= 32 && (c) <= 126) || (c) == 128 || \
+ ((c) >= 160 && (c) <= 255))
+
+void win32::CKeyboard::keyEvent(CMsgWriter* writer, rdr::U8 vkey,
+ rdr::U32 flags, bool down)
+{
+ bool extended = (flags & 0x1000000);
+ int extendedVkey = vkey + (extended ? 256 : 0);
+
+ // If it's a release, just release whichever keysym corresponded to the same
+ // key being pressed, regardless of how it would be interpreted in the
+ // current keyboard state.
+ if (!down) {
+ releaseKey(writer, extendedVkey);
+ return;
+ }
+
+ // We should always pass every down event to ToAscii() otherwise it can get
+ // out of sync.
+
+ // XXX should we pass CapsLock, ScrollLock or NumLock to ToAscii - they
+ // actually alter the lock state on the keyboard?
+
+ BYTE keystate[256];
+ GetKeyboardState(keystate);
+ rdr::U8 chars[2];
+
+ int nchars = ToAscii(vkey, 0, keystate, (WORD*)&chars, 0);
+
+ // See if it's in the Windows VK code -> X keysym map. We do this before
+ // looking at the result of ToAscii so that e.g. we recognise that it's
+ // XK_KP_Add rather than '+'.
+
+ rdr::U32 keysym = ckeymapper.lookup(extendedVkey);
+ if (keysym) {
+ vlog.debug("mapped key: extendedVkey 0x%x", extendedVkey);
+ pressKey(writer, extendedVkey, keysym);
+ return;
+ }
+
+ if (nchars < 0) {
+ // Dead key - the next call to ToAscii() will give us either the accented
+ // character or two characters.
+ vlog.debug("ToAscii dead key (1): extendedVkey 0x%x", extendedVkey);
+ return;
+ }
+
+ if (nchars > 0 && IS_PRINTABLE_LATIN1(chars[0])) {
+ // Got a printable latin1 character. We must release Control and Alt
+ // (AltGr) if they were both pressed, so that the latin1 character is seen
+ // without them by the VNC server.
+ ModifierKeyReleaser lctrl(writer, VK_CONTROL, 0);
+ ModifierKeyReleaser rctrl(writer, VK_CONTROL, 1);
+ ModifierKeyReleaser lalt(writer, VK_MENU, 0);
+ ModifierKeyReleaser ralt(writer, VK_MENU, 1);
+
+ if ((keystate[VK_CONTROL] & 0x80) && (keystate[VK_MENU] & 0x80)) {
+ lctrl.release(&downKeysym);
+ rctrl.release(&downKeysym);
+ lalt.release(&downKeysym);
+ ralt.release(&downKeysym);
+ }
+
+ for (int i = 0; i < nchars; i++) {
+ vlog.debug("ToAscii key (1): extendedVkey 0x%x", extendedVkey);
+ if (chars[i] == 128) { // special hack for euro!
+ pressKey(writer, extendedVkey, XK_EuroSign);
+ } else {
+ pressKey(writer, extendedVkey, chars[i]);
+ }
+ }
+ return;
+ }
+
+ // Either no chars were generated, or something outside the printable
+ // character range. Try ToAscii() without the Control and Alt keys down to
+ // see if that yields an ordinary character.
+
+ keystate[VK_CONTROL] = keystate[VK_LCONTROL] = keystate[VK_RCONTROL] = 0;
+ keystate[VK_MENU] = keystate[VK_LMENU] = keystate[VK_RMENU] = 0;
+
+ nchars = ToAscii(vkey, 0, keystate, (WORD*)&chars, 0);
+
+ if (nchars < 0) {
+ // So it would be a dead key if neither control nor alt were pressed.
+ // However, we know that at least one of control and alt must be pressed.
+ // We can't leave it at this stage otherwise the next call to ToAscii()
+ // with a valid character will get wrongly interpreted in the context of
+ // this bogus dead key. Working on the assumption that a dead key followed
+ // by space usually returns the dead character itself, try calling ToAscii
+ // with VK_SPACE.
+ vlog.debug("ToAscii dead key (2): extendedVkey 0x%x", extendedVkey);
+ nchars = ToAscii(VK_SPACE, 0, keystate, (WORD*)&chars, 0);
+ if (nchars < 0) {
+ vlog.debug("ToAscii dead key (3): extendedVkey 0x%x - giving up!",
+ extendedVkey);
+ return;
+ }
+ }
+
+ if (nchars > 0 && IS_PRINTABLE_LATIN1(chars[0])) {
+ for (int i = 0; i < nchars; i++) {
+ vlog.debug("ToAscii key (2) (no ctrl/alt): extendedVkey 0x%x",
+ extendedVkey);
+ if (chars[i] == 128) { // special hack for euro!
+ pressKey(writer, extendedVkey, XK_EuroSign);
+ } else {
+ pressKey(writer, extendedVkey, chars[i]);
+ }
+ }
+ return;
+ }
+
+ vlog.debug("no chars regardless of control and alt: extendedVkey 0x%x",
+ extendedVkey);
+}
+
+// releaseAllKeys() - write key release events to the server for all keys
+// that are currently regarded as being down.
+void win32::CKeyboard::releaseAllKeys(CMsgWriter* writer) {
+ std::map<int,rdr::U32>::iterator i, next_i;
+ for (i=downKeysym.begin(); i!=downKeysym.end(); i=next_i) {
+ next_i = i; next_i++;
+ writer->writeKeyEvent((*i).second, false);
+ downKeysym.erase(i);
+ }
+}
+
+// releaseKey() - write a key up event to the server, but only if we've
+// actually sent a key down event for the given key. The key up event always
+// contains the same keysym we used in the key down event, regardless of what
+// it would look up as using the current keyboard state.
+void win32::CKeyboard::releaseKey(CMsgWriter* writer, int extendedVkey)
+{
+ if (downKeysym.find(extendedVkey) != downKeysym.end()) {
+ vlog.debug("release extendedVkey 0x%x, keysym 0x%x",
+ extendedVkey, downKeysym[extendedVkey]);
+ writer->writeKeyEvent(downKeysym[extendedVkey], false);
+ downKeysym.erase(extendedVkey);
+ }
+}
+
+// pressKey() - write a key down event to the server, and record which keysym
+// was sent as corresponding to the given extendedVkey. The only tricky bit is
+// that if we are trying to press an extendedVkey which is already marked as
+// down but with a different keysym, then we need to release the old keysym
+// first. This can happen in two cases: (a) when a single key press results in
+// more than one character, and (b) when shift is released while another key is
+// autorepeating.
+void win32::CKeyboard::pressKey(CMsgWriter* writer, int extendedVkey,
+ rdr::U32 keysym)
+{
+ if (downKeysym.find(extendedVkey) != downKeysym.end()) {
+ if (downKeysym[extendedVkey] != keysym) {
+ vlog.debug("release extendedVkey 0x%x, keysym 0x%x",
+ extendedVkey, downKeysym[extendedVkey]);
+ writer->writeKeyEvent(downKeysym[extendedVkey], false);
+ }
+ }
+ vlog.debug("press extendedVkey 0x%x, keysym 0x%x",
+ extendedVkey, keysym);
+ writer->writeKeyEvent(keysym, true);
+ downKeysym[extendedVkey] = keysym;
+}
diff --git a/rfb_win32/CKeyboard.h b/rfb_win32/CKeyboard.h
new file mode 100644
index 00000000..10346ffc
--- /dev/null
+++ b/rfb_win32/CKeyboard.h
@@ -0,0 +1,53 @@
+/* Copyright (C) 2002-2003 RealVNC Ltd. All Rights Reserved.
+ *
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+ * USA.
+ */
+
+// -=- CKeyboard.h
+//
+// Client-side keyboard handling for Win32
+
+#ifndef __RFB_WIN32_CKEYBOARD_H__
+#define __RFB_WIN32_CKEYBOARD_H__
+
+#include <rdr/types.h>
+#include <map>
+
+namespace rfb {
+
+ class CMsgWriter;
+
+ namespace win32 {
+
+ class CKeyboard {
+ public:
+ void keyEvent(CMsgWriter* writer, rdr::U8 vkey, rdr::U32 flags,
+ bool down);
+ void releaseAllKeys(CMsgWriter* writer);
+ const std::map<int,rdr::U32>& pressedKeys() const {return downKeysym;};
+ bool keyPressed(int k) const {return downKeysym.find(k)!=downKeysym.end();}
+ private:
+ void win32::CKeyboard::releaseKey(CMsgWriter* writer, int extendedVkey);
+ void win32::CKeyboard::pressKey(CMsgWriter* writer, int extendedVkey,
+ rdr::U32 keysym);
+ std::map<int,rdr::U32> downKeysym;
+ };
+
+ }; // win32
+
+}; // rfb
+
+#endif // __RFB_WIN32_CKEYBOARD_H__
diff --git a/rfb_win32/CPointer.cxx b/rfb_win32/CPointer.cxx
new file mode 100644
index 00000000..1cab662a
--- /dev/null
+++ b/rfb_win32/CPointer.cxx
@@ -0,0 +1,188 @@
+/* Copyright (C) 2002-2003 RealVNC Ltd. All Rights Reserved.
+ *
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+ * USA.
+ */
+
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+
+#include <rfb/LogWriter.h>
+#include <rfb_win32/CPointer.h>
+
+using namespace rfb;
+using namespace win32;
+
+static LogWriter vlog("CPointer");
+
+
+CPointer::CPointer() : currButtonMask(0), intervalQueued(false), threeEmulating(false) {
+}
+
+CPointer::~CPointer() {
+ intervalTimer.stop();
+ threeTimer.stop();
+}
+
+
+void CPointer::pointerEvent(CMsgWriter* writer, int x, int y, int buttonMask) {
+ //
+ // - Duplicate Event Filtering
+ //
+
+ bool maskChanged = buttonMask != currButtonMask;
+ bool posChanged = !Point(x, y).equals(currPos);
+ if (!(posChanged || maskChanged))
+ return;
+
+ // Pass on the event to the event-interval handler
+ threePointerEvent(writer, x, y, buttonMask);
+
+ // Save the position and mask
+ currPos = Point(x, y);
+ currButtonMask = buttonMask;
+}
+
+
+inline abs(int x) {return x>0 ? x : 0;}
+
+int emulate3Mask(int buttonMask) {
+ // - Release left & right and press middle
+ vlog.debug("emulate3: active");
+ buttonMask &= ~5;
+ buttonMask |= 2;
+ return buttonMask;
+}
+
+void CPointer::threePointerEvent(CMsgWriter* writer, int x, int y, int buttonMask) {
+ //
+ // - 3-Button Mouse Emulation
+ //
+
+ if (emulate3) {
+
+ bool leftChanged = (buttonMask & 1) != (currButtonMask & 1);
+ bool rightChanged = (buttonMask & 4) != (currButtonMask & 4);
+
+ if (leftChanged || rightChanged) {
+ // - One of left or right have changed
+
+ if ((buttonMask & 5) == 1 || (buttonMask & 5) == 4) {
+ // - One is up, one is down. Start a timer, so that if it
+ // expires then we know we should actually send this event
+ vlog.debug("emulate3: start timer");
+ threeTimer.start(100);
+ threePos = Point(x, y);
+ threeMask = buttonMask;
+ return;
+
+ } else if (threeTimer.isActive()) {
+ // - Both are up or both are down, and we were timing for an emulation event
+ // Stop the timer and flush the stored event
+ vlog.debug("emulate3: stop timer (state)");
+ threeTimer.stop();
+ if (threeEmulating == ((buttonMask & 5) == 5))
+ intervalPointerEvent(writer, threePos.x, threePos.y, threeMask);
+ else
+ threeEmulating = ((buttonMask & 5) == 5);
+ }
+
+ } else {
+
+ if (threeTimer.isActive()) {
+ // - We are timing for an emulation event
+
+ if (abs(threePos.x - x) <= 4 || abs(threePos.y - y) <= 4) {
+ // If the mouse has moved too far since the button-change event then flush
+ vlog.debug("emulate3: stop timer (moved)");
+ threeTimer.stop();
+ intervalPointerEvent(writer, threePos.x, threePos.y, threeMask);
+
+ } else {
+ // Otherwise, we ignore the new event
+ return;
+ }
+ }
+
+ }
+
+ // - If neither left nor right are down, stop emulating
+ if ((buttonMask & 5) == 0)
+ threeEmulating = false;
+
+ // - If emulating, release left & right and press middle
+ if (threeEmulating)
+ buttonMask = emulate3Mask(buttonMask);
+
+ }
+
+ // - Let the event pass through to the next stage of processing
+ intervalPointerEvent(writer, x, y, buttonMask);
+}
+
+void CPointer::intervalPointerEvent(CMsgWriter* writer, int x, int y, int buttonMask) {
+ //
+ // - Pointer Event Interval
+ //
+ vlog.write(101, "ptrEvent: %d,%d (%lx)", x, y, buttonMask);
+
+ // Send the event immediately if we haven't sent one for a while
+ bool sendNow = !intervalTimer.isActive();
+
+ if (intervalMask != buttonMask) {
+ // If the buttons have changed then flush queued events and send now
+ sendNow = true;
+ if (intervalQueued)
+ writer->writePointerEvent(intervalPos.x, intervalPos.y, intervalMask);
+ intervalQueued = false;
+ }
+
+ if (!sendNow) {
+ // If we're not sending now then just queue the event
+ intervalQueued = true;
+ intervalPos = Point(x, y);
+ intervalMask = buttonMask;
+ } else {
+ // Start the interval timer if required, and send the event
+ intervalQueued = false;
+ intervalMask = buttonMask;
+ if (pointerEventInterval)
+ intervalTimer.start(pointerEventInterval);
+ writer->writePointerEvent(x, y, buttonMask);
+ }
+}
+
+void CPointer::handleTimer(CMsgWriter* writer, int timerId) {
+ if (timerId == intervalTimer.getId()) {
+ // Pointer interval has expired - send any queued events
+ if (intervalQueued) {
+ writer->writePointerEvent(intervalPos.x, intervalPos.y, intervalMask);
+ intervalQueued = false;
+ } else {
+ intervalTimer.stop();
+ }
+
+ } else if (timerId = threeTimer.getId()) {
+ // 3-Button emulation timer has expired - send what we've got
+ vlog.debug("emulate3: timeout");
+ threeTimer.stop();
+
+ // If emulating, release left & right and press middle
+ if (threeEmulating)
+ threeMask = emulate3Mask(threeMask);
+
+ intervalPointerEvent(writer, threePos.x, threePos.y, threeMask);
+ }
+}
diff --git a/rfb_win32/CPointer.h b/rfb_win32/CPointer.h
new file mode 100644
index 00000000..f111de74
--- /dev/null
+++ b/rfb_win32/CPointer.h
@@ -0,0 +1,76 @@
+/* Copyright (C) 2002-2003 RealVNC Ltd. All Rights Reserved.
+ *
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+ * USA.
+ */
+
+// -=- CPointer.h
+//
+// Client-side pointer event handling for Win32
+
+#ifndef __RFB_WIN32_CPOINTER_H__
+#define __RFB_WIN32_CPOINTER_H__
+
+#include <rdr/Exception.h>
+#include <rfb/Configuration.h>
+#include <rfb/CMsgWriter.h>
+#include <rfb/Rect.h>
+#include <rfb_win32/IntervalTimer.h>
+
+namespace rfb {
+
+ class CMsgWriter;
+
+ namespace win32 {
+
+ class CPointer {
+ public:
+ CPointer();
+ ~CPointer();
+
+ void pointerEvent(CMsgWriter* writer, int x, int y, int buttonMask);
+ void handleTimer(CMsgWriter* writer, int timerId);
+
+ void setHWND(HWND w) {intervalTimer.setHWND(w); threeTimer.setHWND(w);}
+ void setIntervalTimerId(int id) {intervalTimer.setId(id);}
+ void set3ButtonTimerId(int id) {threeTimer.setId(id);}
+
+ void enableEmulate3(bool enable) {emulate3 = enable;}
+ void enableInterval(int millis) {pointerEventInterval = millis;}
+ private:
+ Point currPos;
+ int currButtonMask;
+
+ bool emulate3;
+ int pointerEventInterval;
+
+ void intervalPointerEvent(CMsgWriter* writer, int x, int y, int buttonMask);
+ IntervalTimer intervalTimer;
+ bool intervalQueued;
+ Point intervalPos;
+ int intervalMask;
+
+ void threePointerEvent(CMsgWriter* writer, int x, int y, int buttonMask);
+ IntervalTimer threeTimer;
+ Point threePos;
+ int threeMask;
+ bool threeEmulating;
+ };
+
+ }; // win32
+
+}; // rfb
+
+#endif // __RFB_WIN32_CPOINTER_H__
diff --git a/rfb_win32/CleanDesktop.cxx b/rfb_win32/CleanDesktop.cxx
new file mode 100644
index 00000000..9fb83478
--- /dev/null
+++ b/rfb_win32/CleanDesktop.cxx
@@ -0,0 +1,255 @@
+/* Copyright (C) 2002-2004 RealVNC Ltd. All Rights Reserved.
+ *
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+ * USA.
+ */
+
+// -=- CleanDesktop.cxx
+
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+#include <wininet.h>
+#include <shlobj.h>
+
+#include <rfb_win32/CleanDesktop.h>
+#include <rfb_win32/CurrentUser.h>
+#include <rfb_win32/Registry.h>
+#include <rfb/LogWriter.h>
+#include <rdr/Exception.h>
+
+using namespace rfb;
+using namespace rfb::win32;
+
+static LogWriter vlog("CleanDesktop");
+
+
+struct ActiveDesktop {
+ ActiveDesktop() : handle(0) {
+ // - Contact Active Desktop
+ HRESULT result = CoCreateInstance(CLSID_ActiveDesktop, NULL, CLSCTX_INPROC_SERVER,
+ IID_IActiveDesktop, (PVOID*)&handle);
+ if (result != S_OK)
+ throw rdr::SystemException("failed to contact Active Desktop", result);
+ }
+ ~ActiveDesktop() {
+ if (handle)
+ handle->Release();
+ }
+ bool enable(bool enable_) {
+ // - Get the current Active Desktop options
+ COMPONENTSOPT adOptions;
+ memset(&adOptions, 0, sizeof(adOptions));
+ adOptions.dwSize = sizeof(adOptions);
+
+ HRESULT result = handle->GetDesktopItemOptions(&adOptions, 0);
+ if (result != S_OK) {
+ vlog.error("failed to get Active Desktop options: %d", result);
+ return false;
+ }
+
+ // - If Active Desktop is active, disable it
+ if ((adOptions.fActiveDesktop==0) != (enable_==0)) {
+ if (enable_)
+ vlog.debug("enabling Active Desktop");
+ else
+ vlog.debug("disabling Active Desktop");
+
+ adOptions.fActiveDesktop = enable_;
+ result = handle->SetDesktopItemOptions(&adOptions, 0);
+ if (result != S_OK) {
+ vlog.error("failed to disable ActiveDesktop: %d", result);
+ return false;
+ }
+ handle->ApplyChanges(AD_APPLY_REFRESH);
+ return true;
+ }
+ return false;
+ }
+ IActiveDesktop* handle;
+};
+
+
+DWORD SysParamsInfo(UINT action, UINT param, PVOID ptr, UINT ini) {
+ DWORD r = ERROR_SUCCESS;
+ if (!SystemParametersInfo(action, param, ptr, ini)) {
+ r = GetLastError();
+ vlog.info("SPI error: %d", r);
+ }
+ return r;
+}
+
+
+CleanDesktop::CleanDesktop() : restoreActiveDesktop(false), restoreWallpaper(false), restoreEffects(false) {
+ CoInitialize(0);
+}
+
+CleanDesktop::~CleanDesktop() {
+ enableEffects();
+ enablePattern();
+ enableWallpaper();
+ CoUninitialize();
+}
+
+void CleanDesktop::disableWallpaper() {
+ try {
+ ImpersonateCurrentUser icu;
+
+ vlog.debug("disable desktop wallpaper/Active Desktop");
+
+ // -=- First attempt to remove the wallpaper using Active Desktop
+ try {
+ ActiveDesktop ad;
+ if (ad.enable(false))
+ restoreActiveDesktop = true;
+ } catch (rdr::Exception& e) {
+ vlog.error(e.str());
+ }
+
+ // -=- Switch of normal wallpaper and notify apps
+ SysParamsInfo(SPI_SETDESKWALLPAPER, 0, "", SPIF_SENDCHANGE);
+ restoreWallpaper = true;
+
+ } catch (rdr::Exception& e) {
+ vlog.info(e.str());
+ }
+}
+
+void CleanDesktop::enableWallpaper() {
+ try {
+ ImpersonateCurrentUser icu;
+
+ if (restoreActiveDesktop) {
+ vlog.debug("restore Active Desktop");
+
+ // -=- First attempt to re-enable Active Desktop
+ try {
+ ActiveDesktop ad;
+ ad.enable(true);
+ restoreActiveDesktop = false;
+ } catch (rdr::Exception& e) {
+ vlog.error(e.str());
+ }
+ }
+
+ if (restoreWallpaper) {
+ vlog.debug("restore desktop wallpaper");
+
+ // -=- Then restore the standard wallpaper if required
+ SysParamsInfo(SPI_SETDESKWALLPAPER, 0, NULL, SPIF_SENDCHANGE);
+ restoreWallpaper = false;
+ }
+
+ } catch (rdr::Exception& e) {
+ vlog.info(e.str());
+ }
+}
+
+
+void CleanDesktop::disablePattern() {
+ try {
+ ImpersonateCurrentUser icu;
+
+ vlog.debug("disable desktop pattern");
+ SysParamsInfo(SPI_SETDESKPATTERN, 0, "", SPIF_SENDCHANGE);
+ restorePattern = true;
+
+ } catch (rdr::Exception& e) {
+ vlog.info(e.str());
+ }
+}
+
+void CleanDesktop::enablePattern() {
+ try {
+ if (restorePattern) {
+ ImpersonateCurrentUser icu;
+
+ vlog.debug("restoring pattern...");
+
+ TCharArray pattern;
+ if (osVersion.isPlatformWindows) {
+ RegKey cfgKey;
+ cfgKey.openKey(HKEY_CURRENT_USER, _T("Control Panel\\Desktop"));
+ pattern.buf = cfgKey.getString(_T("Pattern"));
+ }
+ SysParamsInfo(SPI_SETDESKPATTERN, 0, pattern.buf, SPIF_SENDCHANGE);
+ restorePattern = false;
+ }
+
+ } catch (rdr::Exception& e) {
+ vlog.info(e.str());
+ }
+}
+
+
+void CleanDesktop::disableEffects() {
+#if (WINVER >= 0x500)
+ try {
+ ImpersonateCurrentUser icu;
+
+ vlog.debug("disable desktop effects");
+
+ SysParamsInfo(SPI_SETFONTSMOOTHING, FALSE, 0, SPIF_SENDCHANGE);
+ if (SysParamsInfo(SPI_GETUIEFFECTS, 0, &uiEffects, 0) == ERROR_CALL_NOT_IMPLEMENTED) {
+ SysParamsInfo(SPI_GETCOMBOBOXANIMATION, 0, &comboBoxAnim, 0);
+ SysParamsInfo(SPI_GETGRADIENTCAPTIONS, 0, &gradientCaptions, 0);
+ SysParamsInfo(SPI_GETHOTTRACKING, 0, &hotTracking, 0);
+ SysParamsInfo(SPI_GETLISTBOXSMOOTHSCROLLING, 0, &listBoxSmoothScroll, 0);
+ SysParamsInfo(SPI_GETMENUANIMATION, 0, &menuAnim, 0);
+ SysParamsInfo(SPI_SETCOMBOBOXANIMATION, 0, FALSE, SPIF_SENDCHANGE);
+ SysParamsInfo(SPI_SETGRADIENTCAPTIONS, 0, FALSE, SPIF_SENDCHANGE);
+ SysParamsInfo(SPI_SETHOTTRACKING, 0, FALSE, SPIF_SENDCHANGE);
+ SysParamsInfo(SPI_SETLISTBOXSMOOTHSCROLLING, 0, FALSE, SPIF_SENDCHANGE);
+ SysParamsInfo(SPI_SETMENUANIMATION, 0, FALSE, SPIF_SENDCHANGE);
+ } else {
+ SysParamsInfo(SPI_SETUIEFFECTS, 0, FALSE, SPIF_SENDCHANGE);
+ }
+ restoreEffects = true;
+
+ } catch (rdr::Exception& e) {
+ vlog.info(e.str());
+ }
+#else
+ vlog.info("disableffects not implemented");
+#endif
+}
+
+void CleanDesktop::enableEffects() {
+ try {
+ if (restoreEffects) {
+#if (WINVER >= 0x500)
+ ImpersonateCurrentUser icu;
+
+ vlog.debug("restore desktop effects");
+
+ RegKey desktopCfg;
+ desktopCfg.openKey(HKEY_CURRENT_USER, _T("Control Panel\\Desktop"));
+ SysParamsInfo(SPI_SETFONTSMOOTHING, desktopCfg.getInt(_T("FontSmoothing"), 0) != 0, 0, SPIF_SENDCHANGE);
+ if (SysParamsInfo(SPI_SETUIEFFECTS, 0, (void*)uiEffects, SPIF_SENDCHANGE) == ERROR_CALL_NOT_IMPLEMENTED) {
+ SysParamsInfo(SPI_SETCOMBOBOXANIMATION, 0, (void*)comboBoxAnim, SPIF_SENDCHANGE);
+ SysParamsInfo(SPI_SETGRADIENTCAPTIONS, 0, (void*)gradientCaptions, SPIF_SENDCHANGE);
+ SysParamsInfo(SPI_SETHOTTRACKING, 0, (void*)hotTracking, SPIF_SENDCHANGE);
+ SysParamsInfo(SPI_SETLISTBOXSMOOTHSCROLLING, 0, (void*)listBoxSmoothScroll, SPIF_SENDCHANGE);
+ SysParamsInfo(SPI_SETMENUANIMATION, 0, (void*)menuAnim, SPIF_SENDCHANGE);
+ }
+ restoreEffects = false;
+#else
+ vlog.info("enableEffects not implemented");
+#endif
+ }
+
+ } catch (rdr::Exception& e) {
+ vlog.info(e.str());
+ }
+}
diff --git a/rfb_win32/CleanDesktop.h b/rfb_win32/CleanDesktop.h
new file mode 100644
index 00000000..73f41534
--- /dev/null
+++ b/rfb_win32/CleanDesktop.h
@@ -0,0 +1,57 @@
+/* Copyright (C) 2002-2003 RealVNC Ltd. All Rights Reserved.
+ *
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+ * USA.
+ */
+
+// -=- CleanDesktop.h
+
+#ifndef __RFB_WIN32_CLEANDESKTOP_H__
+#define __RFB_WIN32_CLEANDESKTOP_H__
+
+#include <rfb_win32/TCharArray.h>
+
+namespace rfb {
+
+ namespace win32 {
+
+ class CleanDesktop {
+ public:
+ CleanDesktop();
+ ~CleanDesktop();
+
+ void disableWallpaper();
+ void enableWallpaper();
+
+ void disablePattern();
+ void enablePattern();
+
+ void disableEffects();
+ void enableEffects();
+
+ private:
+ bool restoreActiveDesktop;
+ bool restoreWallpaper;
+ bool restorePattern;
+ bool restoreEffects;
+ BOOL uiEffects;
+ BOOL comboBoxAnim, gradientCaptions, hotTracking, listBoxSmoothScroll, menuAnim;
+ };
+
+ }; // win32
+
+}; // rfb
+
+#endif // __RFB_WIN32_CLEANDESKTOP_H__
diff --git a/rfb_win32/Clipboard.cxx b/rfb_win32/Clipboard.cxx
new file mode 100644
index 00000000..96d1e942
--- /dev/null
+++ b/rfb_win32/Clipboard.cxx
@@ -0,0 +1,199 @@
+/* Copyright (C) 2002-2004 RealVNC Ltd. All Rights Reserved.
+ *
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+ * USA.
+ */
+
+// -=- Clipboard.cxx
+
+#include <rfb_win32/Clipboard.h>
+#include <rfb_win32/WMShatter.h>
+#include <rfb/util.h>
+
+#include <rfb/LogWriter.h>
+
+using namespace rfb;
+using namespace rfb::win32;
+
+static LogWriter vlog("Clipboard");
+
+
+//
+// -=- CR/LF handlers
+//
+
+char*
+dos2unix(const char* text) {
+ int len = strlen(text)+1;
+ char* unix = new char[strlen(text)+1];
+ int i, j=0;
+ for (i=0; i<len; i++) {
+ if (text[i] != '\x0d')
+ unix[j++] = text[i];
+ }
+ return unix;
+}
+
+char*
+unix2dos(const char* text) {
+ int len = strlen(text)+1;
+ char* dos = new char[strlen(text)*2+1];
+ int i, j=0;
+ for (i=0; i<len; i++) {
+ if (text[i] == '\x0a')
+ dos[j++] = '\x0d';
+ dos[j++] = text[i];
+ }
+ return dos;
+}
+
+
+//
+// -=- ASCII filter (in-place)
+//
+
+void
+removeNonAsciiChars(char* text) {
+ int len = strlen(text);
+ int i=0, j=0;
+ for (; i<len; i++) {
+ if ((text[i] >= 1) && (text[i] <= 127))
+ text[j++] = text[i];
+ }
+ text[j] = 0;
+}
+
+//
+// -=- Clipboard object
+//
+
+Clipboard::Clipboard()
+ : MsgWindow(_T("Clipboard")), notifier(0), next_window(0) {
+ next_window = SetClipboardViewer(getHandle());
+ vlog.debug("registered clipboard handler");
+}
+
+Clipboard::~Clipboard() {
+ vlog.debug("removing %x from chain (next is %x)", getHandle(), next_window);
+ ChangeClipboardChain(getHandle(), next_window);
+}
+
+LRESULT
+Clipboard::processMessage(UINT msg, WPARAM wParam, LPARAM lParam) {
+ switch (msg) {
+
+ case WM_CHANGECBCHAIN:
+ vlog.debug("change clipboard chain (%x, %x)", wParam, lParam);
+ if ((HWND) wParam == next_window)
+ next_window = (HWND) lParam;
+ else if (next_window != 0)
+ SendMessage(next_window, msg, wParam, lParam);
+ else
+ vlog.error("bad clipboard chain change!");
+ break;
+
+ case WM_DRAWCLIPBOARD:
+ {
+ HWND owner = GetClipboardOwner();
+ if (owner == getHandle()) {
+ vlog.debug("local clipboard changed by me");
+ } else {
+ vlog.debug("local clipboard changed by %x", owner);
+
+ // Open the clipboard
+ if (OpenClipboard(getHandle())) {
+ // Get the clipboard data
+ HGLOBAL cliphandle = GetClipboardData(CF_TEXT);
+ if (cliphandle) {
+ char* clipdata = (char*) GlobalLock(cliphandle);
+
+ // Notify clients
+ if (notifier) {
+ if (!clipdata) {
+ notifier->notifyClipboardChanged(0, 0);
+ } else {
+ CharArray unix_text;
+ unix_text.buf = dos2unix(clipdata);
+ removeNonAsciiChars(unix_text.buf);
+ notifier->notifyClipboardChanged(unix_text.buf, strlen(unix_text.buf));
+ }
+ } else {
+ vlog.debug("no clipboard notifier registered");
+ }
+
+ // Release the buffer and close the clipboard
+ GlobalUnlock(cliphandle);
+ }
+
+ CloseClipboard();
+ }
+ }
+ }
+ if (next_window)
+ SendMessage(next_window, msg, wParam, lParam);
+ return 0;
+
+ };
+ return MsgWindow::processMessage(msg, wParam, lParam);
+};
+
+void
+Clipboard::setClipText(const char* text) {
+ HANDLE clip_handle = 0;
+
+ try {
+
+ // - Firstly, we must open the clipboard
+ if (!OpenClipboard(getHandle()))
+ throw rdr::SystemException("unable to open Win32 clipboard", GetLastError());
+
+ // - Pre-process the supplied clipboard text into DOS format
+ CharArray dos_text;
+ dos_text.buf = unix2dos(text);
+ removeNonAsciiChars(dos_text.buf);
+ int dos_text_len = strlen(dos_text.buf);
+
+ // - Allocate global memory for the data
+ clip_handle = ::GlobalAlloc(GMEM_MOVEABLE, dos_text_len+1);
+
+ char* data = (char*) GlobalLock(clip_handle);
+ memcpy(data, dos_text.buf, dos_text_len+1);
+ data[dos_text_len] = 0;
+ GlobalUnlock(clip_handle);
+
+ // - Next, we must clear out any existing data
+ if (!EmptyClipboard())
+ throw rdr::SystemException("unable to empty Win32 clipboard", GetLastError());
+
+ // - Set the new clipboard data
+ if (!SetClipboardData(CF_TEXT, clip_handle))
+ throw rdr::SystemException("unable to set Win32 clipboard", GetLastError());
+ clip_handle = 0;
+
+ vlog.debug("set clipboard");
+ } catch (rdr::Exception& e) {
+ vlog.debug(e.str());
+ }
+
+ // - Close the clipboard
+ if (!CloseClipboard())
+ vlog.debug("unable to close Win32 clipboard: %u", GetLastError());
+ else
+ vlog.debug("closed clipboard");
+ if (clip_handle) {
+ vlog.debug("freeing clipboard handle");
+ GlobalFree(clip_handle);
+ }
+}
diff --git a/rfb_win32/Clipboard.h b/rfb_win32/Clipboard.h
new file mode 100644
index 00000000..57297e1e
--- /dev/null
+++ b/rfb_win32/Clipboard.h
@@ -0,0 +1,66 @@
+/* Copyright (C) 2002-2003 RealVNC Ltd. All Rights Reserved.
+ *
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+ * USA.
+ */
+
+// -=- Clipboard.h
+//
+// The Clipboard is used to set the system clipboard, and to get callbacks
+// when the system clipboard has changed.
+
+#ifndef __RFB_WIN32_CLIPBOARD_H__
+#define __RFB_WIN32_CLIPBOARD_H__
+
+#include <rfb/SDesktop.h>
+#include <rfb/Threading.h>
+#include <rfb_win32/MsgWindow.h>
+#include <rfb_win32/DeviceFrameBuffer.h>
+
+namespace rfb {
+
+ namespace win32 {
+
+ class Clipboard : MsgWindow {
+ public:
+
+ // -=- Abstract base class for callback recipients
+ class Notifier {
+ public:
+ virtual void notifyClipboardChanged(const char* text, int len) = 0;
+ };
+
+ Clipboard();
+ ~Clipboard();
+
+ // - Set the notifier to use
+ void setNotifier(Notifier* cbn) {notifier = cbn;}
+
+ // - Set the clipboard contents
+ void setClipText(const char* text);
+
+ protected:
+ // - Internal MsgWindow callback
+ virtual LRESULT processMessage(UINT msg, WPARAM wParam, LPARAM lParam);
+
+ Notifier* notifier;
+ HWND next_window;
+ };
+
+ };
+
+};
+
+#endif // __RFB_WIN32_CLIPBOARD_H__
diff --git a/rfb_win32/CurrentUser.cxx b/rfb_win32/CurrentUser.cxx
new file mode 100644
index 00000000..1a244853
--- /dev/null
+++ b/rfb_win32/CurrentUser.cxx
@@ -0,0 +1,93 @@
+/* Copyright (C) 2002-2004 RealVNC Ltd. All Rights Reserved.
+ *
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+ * USA.
+ */
+
+// -=- Currentuser.cxx
+
+#include <windows.h>
+#include <lmcons.h>
+#include <rfb_win32/CurrentUser.h>
+#include <rfb_win32/Service.h>
+#include <rfb_win32/OSVersion.h>
+
+using namespace rfb;
+using namespace win32;
+
+
+CurrentUserToken::CurrentUserToken() : isValid_(false) {
+ // - If the OS doesn't support security, ignore it
+ if (!isServiceProcess()) {
+ // - Running in User-Mode - just get our current token
+ if (!OpenProcessToken(GetCurrentProcess(), GENERIC_ALL, &h)) {
+ DWORD err = GetLastError();
+ if (err != ERROR_CALL_NOT_IMPLEMENTED)
+ throw rdr::SystemException("OpenProcessToken failed", GetLastError());
+ }
+ isValid_ = true;
+ } else {
+ // - Under XP/2003 and above, we can just ask the operating system
+ typedef BOOL (WINAPI *WTSQueryUserToken_proto)(ULONG, PHANDLE);
+ DynamicFn<WTSQueryUserToken_proto> _WTSQueryUserToken(_T("wtsapi32.dll"), "WTSQueryUserToken");
+ if (_WTSQueryUserToken.isValid()) {
+ (*_WTSQueryUserToken)(-1, &h);
+ isValid_ = true;
+ } else {
+ // - Under NT/2K we have to resort to a nasty hack...
+ HWND tray = FindWindow(_T("Shell_TrayWnd"), 0);
+ if (!tray)
+ return;
+ DWORD processId = 0;
+ GetWindowThreadProcessId(tray, &processId);
+ if (!processId)
+ return;
+ Handle process = OpenProcess(MAXIMUM_ALLOWED, FALSE, processId);
+ if (!process.h)
+ return;
+ OpenProcessToken(process, MAXIMUM_ALLOWED, &h);
+ isValid_ = true;
+ }
+ }
+}
+
+
+ImpersonateCurrentUser::ImpersonateCurrentUser() {
+ RegCloseKey(HKEY_CURRENT_USER);
+ if (!isServiceProcess())
+ return;
+ if (!token.isValid())
+ throw rdr::Exception("CurrentUserToken is not valid");
+ if (!ImpersonateLoggedOnUser(token)) {
+ DWORD err = GetLastError();
+ if (err != ERROR_CALL_NOT_IMPLEMENTED)
+ throw rdr::SystemException("Failed to impersonate user", GetLastError());
+ }
+}
+
+ImpersonateCurrentUser::~ImpersonateCurrentUser() {
+ if (!RevertToSelf()) {
+ DWORD err = GetLastError();
+ if (err != ERROR_CALL_NOT_IMPLEMENTED)
+ exit(err);
+ }
+}
+
+
+UserName::UserName() : TCharArray(UNLEN+1) {
+ DWORD len = UNLEN+1;
+ if (!GetUserName(buf, &len))
+ throw rdr::SystemException("GetUserName failed", GetLastError());
+}
diff --git a/rfb_win32/CurrentUser.h b/rfb_win32/CurrentUser.h
new file mode 100644
index 00000000..469946b6
--- /dev/null
+++ b/rfb_win32/CurrentUser.h
@@ -0,0 +1,78 @@
+/* Copyright (C) 2002-2004 RealVNC Ltd. All Rights Reserved.
+ *
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+ * USA.
+ */
+
+// CurrentUser.h
+
+// Helper class providing the session's logged on username, if
+// a user is logged on. Also allows processes running under
+// XP/2K3 etc to masquerade as the logged on user for security
+// purposes
+
+#ifndef __RFB_WIN32_CURRENT_USER_H__
+#define __RFB_WIN32_CURRENT_USER_H__
+
+#include <rfb_win32/Service.h>
+#include <rfb_win32/OSVersion.h>
+#include <rfb_win32/Win32Util.h>
+
+namespace rfb {
+
+ namespace win32 {
+
+ // CurrentUserToken
+ // h == 0
+ // if platform is not NT *or* unable to get token
+ // *or* if process is hosting service.
+ // h != 0
+ // if process is hosting service *and*
+ // if platform is NT *and* able to get token.
+ // isValid() == true
+ // if platform is not NT *or* token is valid.
+
+ struct CurrentUserToken : public Handle {
+ CurrentUserToken();
+ bool isValid() const {return isValid_;};
+ private:
+ bool isValid_;
+ };
+
+ // ImpersonateCurrentUser
+ // Throws an exception on failure.
+ // Succeeds (trivially) if process is not running as service.
+ // Fails if CurrentUserToken is not valid.
+ // Fails if platform is NT AND cannot impersonate token.
+ // Succeeds otherwise.
+
+ struct ImpersonateCurrentUser {
+ ImpersonateCurrentUser();
+ ~ImpersonateCurrentUser();
+ CurrentUserToken token;
+ };
+
+ // UserName
+ // Returns the name of the user the thread is currently running as.
+
+ struct UserName : public TCharArray {
+ UserName();
+ };
+
+ }
+
+}
+
+#endif
diff --git a/rfb_win32/DIBSectionBuffer.cxx b/rfb_win32/DIBSectionBuffer.cxx
new file mode 100644
index 00000000..21743768
--- /dev/null
+++ b/rfb_win32/DIBSectionBuffer.cxx
@@ -0,0 +1,221 @@
+/* Copyright (C) 2002-2004 RealVNC Ltd. All Rights Reserved.
+ *
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+ * USA.
+ */
+#include <rfb/LogWriter.h>
+
+#include <rfb_win32/DIBSectionBuffer.h>
+#include <rfb_win32/Win32Util.h>
+
+
+using namespace rfb;
+using namespace win32;
+
+static LogWriter vlog("DIBSection");
+
+
+DIBSectionBuffer::DIBSectionBuffer(HWND window_)
+ : bitmap(0), device(0), window(window_) {
+ memset(&format, 0, sizeof(format));
+ memset(palette, 0, sizeof(palette));
+}
+
+DIBSectionBuffer::DIBSectionBuffer(HDC device_)
+ : bitmap(0), window(0), device(device_) {
+ memset(&format, 0, sizeof(format));
+ memset(palette, 0, sizeof(palette));
+}
+
+DIBSectionBuffer::~DIBSectionBuffer() {
+ if (bitmap)
+ DeleteObject(bitmap);
+}
+
+
+void DIBSectionBuffer::setPF(const PixelFormat& pf) {
+ if (memcmp(&getPF(), &pf, sizeof(pf)) == 0) {
+ vlog.debug("pixel format unchanged by setPF()");
+ return;
+ }
+ format = pf;
+ recreateBuffer();
+ if ((pf.bpp <= 8) && pf.trueColour) {
+ vlog.debug("creating %d-bit TrueColour palette", pf.depth);
+ for (int i=0; i < (1<<(pf.depth)); i++) {
+ palette[i].b = ((((i >> pf.blueShift) & pf.blueMax) * 65535) + pf.blueMax/2) / pf.blueMax;
+ palette[i].g = ((((i >> pf.greenShift) & pf.greenMax) * 65535) + pf.greenMax/2) / pf.greenMax;
+ palette[i].r = ((((i >> pf.redShift) & pf.redMax) * 65535) + pf.redMax/2) / pf.redMax;
+ }
+ refreshPalette();
+ }
+}
+
+void DIBSectionBuffer::setSize(int w, int h) {
+ if (width_ == w && height_ == h) {
+ vlog.debug("size unchanged by setSize()");
+ return;
+ }
+ width_ = w;
+ height_ = h;
+ recreateBuffer();
+}
+
+
+// * copyPaletteToDIB MUST NEVER be called on a truecolour DIB! *
+
+void copyPaletteToDIB(Colour palette[256], HDC wndDC, HBITMAP dib) {
+ BitmapDC dibDC(wndDC, dib);
+ RGBQUAD rgb[256];
+ for (unsigned int i=0;i<256;i++) {
+ rgb[i].rgbRed = palette[i].r >> 8;
+ rgb[i].rgbGreen = palette[i].g >> 8;
+ rgb[i].rgbBlue = palette[i].b >> 8;
+ }
+ if (!SetDIBColorTable(dibDC, 0, 256, (RGBQUAD*) rgb))
+ throw rdr::SystemException("unable to SetDIBColorTable", GetLastError());
+}
+
+inline void initMaxAndShift(DWORD mask, int* max, int* shift) {
+ for ((*shift) = 0; (mask & 1) == 0; (*shift)++) mask >>= 1;
+ (*max) = (rdr::U16)mask;
+}
+
+void DIBSectionBuffer::recreateBuffer() {
+ HBITMAP new_bitmap = 0;
+ rdr::U8* new_data = 0;
+
+ if (width_ && height_ && (format.depth != 0)) {
+ BitmapInfo bi;
+ memset(&bi, 0, sizeof(bi));
+ // *** wrong?
+ UINT iUsage = format.trueColour ? DIB_RGB_COLORS : DIB_PAL_COLORS;
+ // ***
+ bi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
+ bi.bmiHeader.biBitCount = format.bpp;
+ bi.bmiHeader.biSizeImage = (format.bpp / 8) * width_ * height_;
+ bi.bmiHeader.biPlanes = 1;
+ bi.bmiHeader.biWidth = width_;
+ bi.bmiHeader.biHeight = -height_;
+ bi.bmiHeader.biCompression = (format.bpp > 8) ? BI_BITFIELDS : BI_RGB;
+ bi.mask.red = format.redMax << format.redShift;
+ bi.mask.green = format.greenMax << format.greenShift;
+ bi.mask.blue = format.blueMax << format.blueShift;
+
+ vlog.debug("area=%d, bpp=%d", width_ * height_, format.bpp);
+
+ // Create a DIBSection to draw into
+ if (device)
+ new_bitmap = ::CreateDIBSection(device, (BITMAPINFO*)&bi.bmiHeader, iUsage,
+ (void**)&new_data, NULL, 0);
+ else
+ new_bitmap = ::CreateDIBSection(WindowDC(window), (BITMAPINFO*)&bi.bmiHeader, iUsage,
+ (void**)&new_data, NULL, 0);
+
+ if (!new_bitmap) {
+ int err = GetLastError();
+ throw rdr::SystemException("unable to create DIB section", err);
+ }
+
+ vlog.debug("recreateBuffer()");
+ } else {
+ vlog.debug("one of area or format not set");
+ }
+
+ if (new_bitmap && bitmap) {
+ vlog.debug("preserving bitmap contents");
+
+ // Copy the contents across
+ if (device) {
+ if (format.bpp <= 8)
+ copyPaletteToDIB(palette, device, new_bitmap);
+ BitmapDC src_dev(device, bitmap);
+ BitmapDC dest_dev(device, new_bitmap);
+ BitBlt(dest_dev, 0, 0, width_, height_, src_dev, 0, 0, SRCCOPY);
+ } else {
+ WindowDC wndDC(window);
+ if (format.bpp <= 8)
+ copyPaletteToDIB(palette, wndDC, new_bitmap);
+ BitmapDC src_dev(wndDC, bitmap);
+ BitmapDC dest_dev(wndDC, new_bitmap);
+ BitBlt(dest_dev, 0, 0, width_, height_, src_dev, 0, 0, SRCCOPY);
+ }
+ }
+
+ if (bitmap) {
+ // Delete the old bitmap
+ DeleteObject(bitmap);
+ bitmap = 0;
+ data = 0;
+ }
+
+ if (new_bitmap) {
+ // Set up the new bitmap
+ bitmap = new_bitmap;
+ data = new_data;
+
+ // Determine the *actual* DIBSection format
+ DIBSECTION ds;
+ if (!GetObject(bitmap, sizeof(ds), &ds))
+ throw rdr::SystemException("GetObject", GetLastError());
+
+ // Correct the "stride" of the DIB
+ // *** This code DWORD aligns each row - is that right???
+ stride = width_;
+ int bytesPerRow = stride * format.bpp/8;
+ if (bytesPerRow % 4) {
+ bytesPerRow += 4 - (bytesPerRow % 4);
+ stride = (bytesPerRow * 8) / format.bpp;
+ vlog.info("adjusting DIB stride: %d to %d", width_, stride);
+ }
+
+ // Calculate the PixelFormat for the DIB
+ format.bigEndian = 0;
+ format.bpp = format.depth = ds.dsBm.bmBitsPixel;
+ format.trueColour = format.trueColour || format.bpp > 8;
+ if (format.bpp > 8) {
+
+ // Get the truecolour format used by the DIBSection
+ initMaxAndShift(ds.dsBitfields[0], &format.redMax, &format.redShift);
+ initMaxAndShift(ds.dsBitfields[1], &format.greenMax, &format.greenShift);
+ initMaxAndShift(ds.dsBitfields[2], &format.blueMax, &format.blueShift);
+
+ // Calculate the effective depth
+ format.depth = 0;
+ Pixel bits = ds.dsBitfields[0] | ds.dsBitfields[1] | ds.dsBitfields[2];
+ while (bits) {
+ format.depth++;
+ bits = bits >> 1;
+ }
+ } else {
+ // Set the DIBSection's palette
+ refreshPalette();
+ }
+ }
+}
+
+void DIBSectionBuffer::refreshPalette() {
+ if (format.bpp > 8) {
+ vlog.error("refresh palette called for truecolour DIB");
+ return;
+ }
+ vlog.debug("refreshing palette");
+ if (device)
+ copyPaletteToDIB(palette, device, bitmap);
+ else
+ copyPaletteToDIB(palette, WindowDC(window), bitmap);
+}
+
+
diff --git a/rfb_win32/DIBSectionBuffer.h b/rfb_win32/DIBSectionBuffer.h
new file mode 100644
index 00000000..51e2da31
--- /dev/null
+++ b/rfb_win32/DIBSectionBuffer.h
@@ -0,0 +1,87 @@
+/* Copyright (C) 2002-2003 RealVNC Ltd. All Rights Reserved.
+ *
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+ * USA.
+ */
+
+// -=- DIBSectionBuffer.h
+
+// A DIBSectionBuffer acts much like a standard PixelBuffer, but is associated
+// with a particular window on-screen and can be drawn into that window if
+// required, using the standard Win32 drawing operations.
+
+#ifndef __RFB_WIN32_DIB_SECTION_BUFFER_H__
+#define __RFB_WIN32_DIB_SECTION_BUFFER_H__
+
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+
+#include <rfb/PixelBuffer.h>
+#include <rfb/Region.h>
+#include <rfb/ColourMap.h>
+#include <rfb/Exception.h>
+
+namespace rfb {
+
+ namespace win32 {
+
+ //
+ // -=- DIBSectionBuffer
+ //
+
+ class DIBSectionBuffer : public FullFramePixelBuffer, ColourMap {
+ public:
+ DIBSectionBuffer(HWND window);
+ DIBSectionBuffer(HDC device);
+ virtual ~DIBSectionBuffer();
+
+ virtual void setPF(const PixelFormat &pf);
+ virtual void setSize(int w, int h);
+
+ virtual int getStride() const {return stride;}
+
+ virtual ColourMap* getColourMap() const {return (ColourMap*)this;}
+
+ // - ColourMap interface
+ virtual void lookup(int index, int* r, int *g, int* b) {
+ *r = palette[index].r;
+ *g = palette[index].g;
+ *b = palette[index].b;
+ }
+
+ // Custom colourmap interface
+ void setColour(int index, int r, int g, int b) {
+ palette[index].r = r;
+ palette[index].g = g;
+ palette[index].b = b;
+ }
+ void refreshPalette();
+
+ // *** virtual void copyRect(const Rect &dest, const Point &move_by_delta);
+ public:
+ HBITMAP bitmap;
+ protected:
+ void recreateBuffer();
+ Colour palette[256];
+ int stride;
+ HWND window;
+ HDC device;
+ };
+
+ };
+
+};
+
+#endif // __RFB_WIN32_DIB_SECTION_BUFFER_H__
diff --git a/rfb_win32/DeviceFrameBuffer.cxx b/rfb_win32/DeviceFrameBuffer.cxx
new file mode 100644
index 00000000..a4d10211
--- /dev/null
+++ b/rfb_win32/DeviceFrameBuffer.cxx
@@ -0,0 +1,298 @@
+/* Copyright (C) 2002-2004 RealVNC Ltd. All Rights Reserved.
+ *
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+ * USA.
+ */
+
+// -=- DeviceFrameBuffer.cxx
+//
+// The DeviceFrameBuffer class encapsulates the pixel data of the system
+// display.
+
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+#include <assert.h>
+
+#include <vector>
+
+#include <rfb/LogWriter.h>
+#include <rfb/Exception.h>
+#include <rdr/types.h>
+#include <rfb/VNCServer.h>
+#include <rfb_win32/DeviceFrameBuffer.h>
+#include <rfb_win32/Win32Util.h>
+#include <rfb_win32/OSVersion.h>
+
+namespace rfb {
+
+namespace win32 {
+
+static LogWriter vlog("FrameBuffer");
+
+
+// -=- DeviceFrameBuffer class
+
+DeviceFrameBuffer::DeviceFrameBuffer(HDC deviceContext, const Rect& wRect)
+ : DIBSectionBuffer(deviceContext), device(deviceContext), cursorBm(deviceContext),
+ ignoreGrabErrors(false)
+{
+
+ // -=- Firstly, let's check that the device has suitable capabilities
+
+ int capabilities = GetDeviceCaps(device, RASTERCAPS);
+ if (!(capabilities & RC_BITBLT)) {
+ throw Exception("device does not support BitBlt");
+ }
+ if (!(capabilities & RC_DI_BITMAP)) {
+ throw Exception("device does not support GetDIBits");
+ }
+ /*
+ if (GetDeviceCaps(device, PLANES) != 1) {
+ throw Exception("device does not support planar displays");
+ }
+ */
+
+ // -=- Get the display dimensions and pixel format
+
+ // Get the display dimensions
+ RECT cr;
+ if (!GetClipBox(device, &cr))
+ throw rdr::SystemException("GetClipBox", GetLastError());
+ deviceCoords = Rect(cr.left, cr.top, cr.right, cr.bottom);
+ if (!wRect.is_empty())
+ deviceCoords = wRect.translate(deviceCoords.tl);
+ int w = deviceCoords.width();
+ int h = deviceCoords.height();
+
+ // We can't handle uneven widths :(
+ if (w % 2) w--;
+
+ // Configure the underlying DIB to match the device
+ DIBSectionBuffer::setPF(DeviceContext::getPF(device));
+ DIBSectionBuffer::setSize(w, h);
+
+ // Configure the cursor buffer
+ cursorBm.setPF(format);
+
+ // Set up a palette if required
+ if (!format.trueColour)
+ updateColourMap();
+}
+
+DeviceFrameBuffer::~DeviceFrameBuffer() {
+}
+
+
+void
+DeviceFrameBuffer::setPF(const PixelFormat &pf) {
+ throw Exception("setPF not supported");
+}
+
+void
+DeviceFrameBuffer::setSize(int w, int h) {
+ throw Exception("setSize not supported");
+}
+
+
+#ifndef CAPTUREBLT
+#define CAPTUREBLT 0x40000000
+#endif
+
+void
+DeviceFrameBuffer::grabRect(const Rect &rect) {
+ BitmapDC tmpDC(device, bitmap);
+
+ // Map the rectangle coords from VNC Desktop-relative to device relative - usually (0,0)
+ Point src = desktopToDevice(rect.tl);
+
+ // Note: Microsoft's documentation lies directly about CAPTUREBLT and claims it works on 98/ME
+ // If you try CAPTUREBLT on 98 then you get blank output...
+ if (!::BitBlt(tmpDC, rect.tl.x, rect.tl.y, rect.width(), rect.height(), device, src.x, src.y,
+ osVersion.isPlatformNT ? CAPTUREBLT | SRCCOPY : SRCCOPY)) {
+ if (ignoreGrabErrors)
+ vlog.error("BitBlt failed:%ld", GetLastError());
+ else
+ throw rdr::SystemException("BitBlt failed", GetLastError());
+ }
+}
+
+void
+DeviceFrameBuffer::grabRegion(const Region &rgn) {
+ std::vector<Rect> rects;
+ std::vector<Rect>::const_iterator i;
+ rgn.get_rects(&rects);
+ for(i=rects.begin(); i!=rects.end(); i++) {
+ grabRect(*i);
+ }
+ ::GdiFlush();
+}
+
+
+void copyDevicePaletteToDIB(HDC dc, DIBSectionBuffer* dib) {
+ // - Fetch the system palette for the framebuffer
+ PALETTEENTRY syspalette[256];
+ UINT entries = ::GetSystemPaletteEntries(dc, 0, 256, syspalette);
+
+ if (entries == 0) {
+ vlog.info("resorting to standard 16 colour palette");
+ for (unsigned int i=0;i<256;i++) {
+ int v = (i%16) >= 8 ? 127 : 255;
+ syspalette[i].peRed = i & 1 ? v : 0;
+ syspalette[i].peGreen = i & 2 ? v : 0;
+ syspalette[i].peBlue = i & 4 ? v : 0;
+ }
+ } else {
+ vlog.info("framebuffer has %u palette entries", entries);
+ }
+
+ // - Update the bitmap's stored copy of the palette
+ for (unsigned int i=0;i<256;i++) {
+ int r, g, b;
+ r = (syspalette[i].peRed << 8) + 0x80;
+ g = (syspalette[i].peGreen << 8) + 0x80;
+ b = (syspalette[i].peBlue << 8) + 0x80;
+ dib->setColour(i, r, g, b);
+ }
+
+ // - Update the DIB section to use the palette
+ dib->refreshPalette();
+}
+
+
+void DeviceFrameBuffer::setCursor(HCURSOR hCursor, VNCServer* server)
+{
+ // - If hCursor is null then there is no cursor - clear the old one
+
+ if (hCursor == 0) {
+ server->setCursor(0, 0, 0, 0, 0, 0);
+ return;
+ }
+
+ try {
+
+ // - Get the size and other details about the cursor.
+
+ IconInfo iconInfo((HICON)hCursor);
+
+ BITMAP maskInfo;
+ if (!GetObject(iconInfo.hbmMask, sizeof(BITMAP), &maskInfo))
+ throw rdr::SystemException("GetObject() failed", GetLastError());
+
+ assert(maskInfo.bmPlanes == 1 && maskInfo.bmBitsPixel == 1);
+
+ // - Create the cursor pixel buffer and mask storage
+ // NB: The cursor pixel buffer is NOT used here. Instead, we
+ // pass the cursorBm.data pointer directly, to save overhead.
+
+ cursor.setSize(maskInfo.bmWidth, maskInfo.bmHeight);
+ cursor.setPF(format);
+ cursor.hotspot = Point(iconInfo.xHotspot, iconInfo.yHotspot);
+
+ // - Get the AND and XOR masks. There is only an XOR mask if this is not a
+ // colour cursor.
+
+ if (!iconInfo.hbmColor)
+ cursor.setSize(cursor.width(), cursor.height() / 2);
+ rdr::U8Array mask(maskInfo.bmWidthBytes * maskInfo.bmHeight);
+ rdr::U8* xorMask = mask.buf + cursor.height() * maskInfo.bmWidthBytes;
+
+ if (!GetBitmapBits(iconInfo.hbmMask,
+ maskInfo.bmWidthBytes * maskInfo.bmHeight, mask.buf))
+ throw rdr::SystemException("GetBitmapBits failed", GetLastError());
+
+ // Configure the cursor bitmap
+ cursorBm.setSize(cursor.width(), cursor.height());
+
+ // Copy the palette into it if required
+ if (format.bpp <= 8)
+ copyDevicePaletteToDIB(device, &cursorBm);
+
+ // Draw the cursor into the bitmap
+ BitmapDC dc(device, cursorBm.bitmap);
+ if (!DrawIconEx(dc, 0, 0, hCursor, 0, 0, 0, NULL, DI_NORMAL | DI_COMPAT))
+ throw rdr::SystemException("unable to render cursor", GetLastError());
+
+ // Replace any XORed pixels with xorColour, because RFB doesn't support
+ // XORing of cursors. XORing is used for the I-beam cursor, which is most
+ // often used over a white background, but also sometimes over a black
+ // background. We set the XOR'd pixels to black, then draw a white outline
+ // around the whole cursor.
+
+ // *** should we replace any pixels not set in mask to zero, to ensure
+ // that irrelevant data doesn't screw compression?
+
+ bool doOutline = false;
+ if (!iconInfo.hbmColor) {
+ Pixel xorColour = format.pixelFromRGB(0, 0, 0, cursorBm.getColourMap());
+ for (int y = 0; y < cursor.height(); y++) {
+ bool first = true;
+ for (int x = 0; x < cursor.width(); x++) {
+ int byte = y * maskInfo.bmWidthBytes + x / 8;
+ int bit = 7 - x % 8;
+ if ((mask.buf[byte] & (1 << bit)) && (xorMask[byte] & (1 << bit)))
+ {
+ mask.buf[byte] &= ~(1 << bit);
+
+ switch (format.bpp) {
+ case 8:
+ ((rdr::U8*)cursorBm.data)[y * cursor.width() + x] = xorColour; break;
+ case 16:
+ ((rdr::U16*)cursorBm.data)[y * cursor.width() + x] = xorColour; break;
+ case 32:
+ ((rdr::U32*)cursorBm.data)[y * cursor.width() + x] = xorColour; break;
+ }
+
+ doOutline = true;
+ }
+ }
+ }
+ }
+
+ // Finally invert the AND mask so it's suitable for RFB and pack it into
+ // the minimum number of bytes per row.
+
+ int maskBytesPerRow = (cursor.width() + 7) / 8;
+
+ for (int j = 0; j < cursor.height(); j++) {
+ for (int i = 0; i < maskBytesPerRow; i++)
+ cursor.mask.buf[j * maskBytesPerRow + i]
+ = ~mask.buf[j * maskInfo.bmWidthBytes + i];
+ }
+
+ if (doOutline) {
+ vlog.debug("drawing cursor outline!");
+ memcpy(cursor.data, cursorBm.data, cursor.dataLen());
+ cursor.drawOutline(format.pixelFromRGB(0xffff, 0xffff, 0xffff, cursorBm.getColourMap()));
+ memcpy(cursorBm.data, cursor.data, cursor.dataLen());
+ }
+
+ server->setCursor(cursor.width(), cursor.height(),
+ cursor.hotspot.x, cursor.hotspot.y,
+ cursorBm.data, cursor.mask.buf);
+ } catch (rdr::Exception& e) {
+ vlog.error(e.str());
+ }
+}
+
+
+void
+DeviceFrameBuffer::updateColourMap() {
+ if (!format.trueColour)
+ copyDevicePaletteToDIB(device, this);
+}
+
+}; // namespace win32
+
+}; // namespace rfb
diff --git a/rfb_win32/DeviceFrameBuffer.h b/rfb_win32/DeviceFrameBuffer.h
new file mode 100644
index 00000000..5e97b222
--- /dev/null
+++ b/rfb_win32/DeviceFrameBuffer.h
@@ -0,0 +1,121 @@
+/* Copyright (C) 2002-2003 RealVNC Ltd. All Rights Reserved.
+ *
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+ * USA.
+ */
+
+// -=- DeviceFrameBuffer.h
+//
+// The DeviceFrameBuffer class encapsulates the pixel data of a supplied
+// Device Context Handle (HDC)
+
+// *** THIS INTERFACE NEEDS TIDYING TO SEPARATE COORDINATE SYSTEMS BETTER ***
+
+#ifndef __RFB_WIN32_DEVICE_FRAME_BUFFER_H__
+#define __RFB_WIN32_DEVICE_FRAME_BUFFER_H__
+
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+
+#include <rfb_win32/DIBSectionBuffer.h>
+#include <rfb/Cursor.h>
+#include <rfb/Region.h>
+#include <rfb/Exception.h>
+
+namespace rfb {
+
+ class VNCServer;
+
+ namespace win32 {
+
+ // -=- DeviceFrameBuffer interface
+
+ // DeviceFrameBuffer is passed an HDC referring to a window or to
+ // the entire display. It may also be passed a rectangle specifying
+ // the Device-relative coordinates of the actual rectangle to treat
+ // as the desktop.
+
+ // Coordinate systems start getting really annoying here. There are
+ // three different "origins" to which coordinates might be relative:
+ //
+ // Desktop - VNC coordinates, top-left always (0,0)
+ // Device - DC coordinates. Top-left *usually (0,0) but could be other.
+ // Window - coordinates relative to the specified sub-rectangle within
+ // the supplied DC.
+ // Screen - Coordinates relative to the entire Windows virtual screen.
+ // The virtual screen includes all monitors that are part of
+ // the Windows desktop.
+
+ // The data member is made to point to an internal mirror of the
+ // current display data. Individual rectangles or regions of the
+ // buffer can be brought up to date by calling the grab functions.
+
+ class DeviceFrameBuffer : public DIBSectionBuffer {
+ public:
+ DeviceFrameBuffer(HDC deviceContext, const Rect& area_=Rect());
+ virtual ~DeviceFrameBuffer();
+
+ // - FrameBuffer overrides
+
+ virtual void grabRect(const Rect &rect);
+ virtual void grabRegion(const Region &region);
+
+ // - DIBSectionBuffer overrides
+
+ virtual void setPF(const PixelFormat& pf);
+ virtual void setSize(int w, int h);
+
+ // - DeviceFrameBuffer specific methods
+
+ void setCursor(HCURSOR c, VNCServer* server);
+ void updateColourMap();
+
+ // Set whether grabRect should ignore errors or throw exceptions
+ // Only set this if you are sure you'll capture the errors some other way!
+ void setIgnoreGrabErrors(bool ie) {ignoreGrabErrors=ie;}
+
+ protected:
+ // Translate supplied Desktop coordinates into Device-relative coordinates
+ // This translation may have been affected at start-time by the supplied sub-rect.
+ Point desktopToDevice(const Point p) const {return p.translate(deviceCoords.tl);}
+
+ HDC device;
+ DIBSectionBuffer cursorBm;
+ Cursor cursor;
+ Rect deviceCoords;
+ bool ignoreGrabErrors;
+ };
+
+ // -=- createDisplayDeviceFrameBuffer
+ // createDisplayDeviceFrameBuffer must be passed the name of a display device,
+ // and will return a new FrameBuffer object attached to that display
+ // device.
+ // If the device name is not specified then the default display is
+ // returned.
+
+ DeviceFrameBuffer *createDisplayDeviceFrameBuffer(const char *device=0);
+
+ // -=- createDisplayFrameBuffers
+ // Creates a set of framebuffers, one for each available display
+ // device.
+
+ typedef std::vector<DeviceFrameBuffer *> DeviceFrameBuffers;
+ void createDisplayDeviceFrameBuffers(DeviceFrameBuffers *fbs);
+
+ };
+
+};
+
+#endif // __RFB_WIN32_DEVICE_FRAME_BUFFER_H__
diff --git a/rfb_win32/Dialog.cxx b/rfb_win32/Dialog.cxx
new file mode 100644
index 00000000..157cf5fe
--- /dev/null
+++ b/rfb_win32/Dialog.cxx
@@ -0,0 +1,353 @@
+/* Copyright (C) 2002-2003 RealVNC Ltd. All Rights Reserved.
+ *
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+ * USA.
+ */
+
+// -=- Dialog.cxx
+
+// Base-class for any Dialog classes we might require
+
+#include <rfb_win32/Dialog.h>
+#include <rfb_win32/TCharArray.h>
+#include <rfb/LogWriter.h>
+#include <rdr/Exception.h>
+#include <rfb_win32/Win32Util.h>
+#ifdef _DIALOG_CAPTURE
+#include <rfb_win32/DeviceFrameBuffer.h>
+#include <extra/LoadBMP.cxx>
+#endif
+
+using namespace rfb;
+using namespace rfb::win32;
+
+static LogWriter dlog("Dialog");
+static LogWriter plog("PropSheet");
+
+
+Dialog::Dialog(HINSTANCE inst_)
+: inst(inst_), alreadyShowing(false), handle(0)
+{
+}
+
+Dialog::~Dialog()
+{
+}
+
+
+bool Dialog::showDialog(const TCHAR* resource, HWND owner)
+{
+ if (alreadyShowing) return false;
+ handle = 0;
+ alreadyShowing = true;
+ INT_PTR result = DialogBoxParam(inst, resource, owner,
+ staticDialogProc, (LPARAM)this);
+ if (result<0)
+ throw rdr::SystemException("DialogBoxParam failed", GetLastError());
+ alreadyShowing = false;
+ return (result == 1);
+}
+
+
+bool Dialog::isItemChecked(int id) {
+ return SendMessage(GetDlgItem(handle, id), BM_GETCHECK, 0, 0) == BST_CHECKED;
+}
+int Dialog::getItemInt(int id) {
+ BOOL trans;
+ int result = GetDlgItemInt(handle, id, &trans, TRUE);
+ if (!trans)
+ throw rdr::Exception("unable to read dialog Int");
+ return result;
+}
+TCHAR* Dialog::getItemString(int id) {
+ TCharArray tmp(256);
+ if (!GetDlgItemText(handle, id, tmp.buf, 256))
+ tmp.buf[0] = 0;
+ return tmp.takeBuf();
+}
+
+void Dialog::setItemChecked(int id, bool state) {
+ dlog.debug("bool[%d]=%d", id, (int)state);
+ SendMessage(GetDlgItem(handle, id), BM_SETCHECK, state ? BST_CHECKED : BST_UNCHECKED, 0);
+}
+void Dialog::setItemInt(int id, int value) {
+ dlog.debug("int[%d]=%d", id, value);
+ SetDlgItemInt(handle, id, value, TRUE);
+}
+void Dialog::setItemString(int id, const TCHAR* s) {
+ dlog.debug("string[%d]=%s", id, (const char*)CStr(s));
+ SetDlgItemText(handle, id, s);
+}
+
+
+void Dialog::enableItem(int id, bool state) {
+ dlog.debug("enable[%d]=%d", id, (int)state);
+ EnableWindow(GetDlgItem(handle, id), state);
+}
+
+
+
+
+BOOL CALLBACK Dialog::staticDialogProc(HWND hwnd, UINT msg,
+ WPARAM wParam, LPARAM lParam)
+{
+ if (msg == WM_INITDIALOG)
+ SetWindowLong(hwnd, GWL_USERDATA, (LONG)lParam);
+
+ LONG self = GetWindowLong(hwnd, GWL_USERDATA);
+ if (!self) return FALSE;
+
+ return ((Dialog*)self)->dialogProc(hwnd, msg, wParam, lParam);
+}
+
+BOOL Dialog::dialogProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ switch (msg) {
+
+ case WM_INITDIALOG:
+ handle = hwnd;
+ initDialog();
+ return TRUE;
+
+ case WM_COMMAND:
+ switch (LOWORD(wParam)) {
+ case IDOK:
+ if (onOk()) {
+ EndDialog(hwnd, 1);
+ return TRUE;
+ }
+ return FALSE;
+ case IDCANCEL:
+ EndDialog(hwnd, 0);
+ return TRUE;
+ default:
+ return onCommand(LOWORD(wParam), HIWORD(wParam));
+ };
+
+ case WM_HELP:
+ return onHelp(((HELPINFO*)lParam)->iCtrlId);
+
+ }
+
+ return FALSE;
+}
+
+
+PropSheetPage::PropSheetPage(HINSTANCE inst, const TCHAR* id) : Dialog(inst), propSheet(0) {
+ page.dwSize = sizeof(page);
+ page.dwFlags = 0; // PSP_USECALLBACK;
+ page.hInstance = inst;
+ page.pszTemplate = id;
+ page.pfnDlgProc = staticPageProc;
+ page.lParam = (LPARAM)this;
+ page.pfnCallback = 0; // staticPageProc;
+}
+
+PropSheetPage::~PropSheetPage() {
+}
+
+
+BOOL CALLBACK PropSheetPage::staticPageProc(HWND hwnd, UINT msg,
+ WPARAM wParam, LPARAM lParam)
+{
+ if (msg == WM_INITDIALOG)
+ SetWindowLong(hwnd, GWL_USERDATA, ((PROPSHEETPAGE*)lParam)->lParam);
+
+ LONG self = GetWindowLong(hwnd, GWL_USERDATA);
+ if (!self) return FALSE;
+
+ return ((PropSheetPage*)self)->dialogProc(hwnd, msg, wParam, lParam);
+}
+
+BOOL PropSheetPage::dialogProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ switch (msg) {
+
+ case WM_INITDIALOG:
+ handle = hwnd;
+ initDialog();
+ return TRUE;
+
+ case WM_NOTIFY:
+ switch (((NMHDR*)lParam)->code) {
+ case PSN_APPLY:
+ onOk();
+ return FALSE;
+ };
+ return FALSE;
+
+ case WM_COMMAND:
+ return onCommand(LOWORD(wParam), HIWORD(wParam));
+
+ case WM_HELP:
+ return onHelp(((HELPINFO*)lParam)->iCtrlId);
+
+ }
+
+ return FALSE;
+}
+
+
+PropSheet::PropSheet(HINSTANCE inst_, const TCHAR* title_, std::list<PropSheetPage*> pages_, HICON icon_)
+: title(tstrDup(title_)), inst(inst_), pages(pages_), alreadyShowing(0), handle(0), icon(icon_) {
+}
+
+PropSheet::~PropSheet() {
+}
+
+
+bool PropSheet::showPropSheet(HWND owner, bool showApply, bool showCtxtHelp, bool capture) {
+ if (alreadyShowing) return false;
+ alreadyShowing = true;
+ int count = pages.size();
+
+ HPROPSHEETPAGE* hpages = new HPROPSHEETPAGE[count];
+ try {
+ // Create the PropertSheet page GDI objects.
+ std::list<PropSheetPage*>::iterator pspi;
+ int i = 0;
+ for (pspi=pages.begin(); pspi!=pages.end(); pspi++) {
+ hpages[i] = CreatePropertySheetPage(&((*pspi)->page));
+ (*pspi)->setPropSheet(this);
+ i++;
+ }
+
+ // Initialise and create the PropertySheet itself
+ PROPSHEETHEADER header;
+ header.dwSize = PROPSHEETHEADER_V1_SIZE;
+ header.dwFlags = PSH_MODELESS | (showApply ? 0 : PSH_NOAPPLYNOW) /*| (showCtxtHelp ? 0 : PSH_NOCONTEXTHELP)*/;
+ header.hwndParent = owner;
+ header.hInstance = inst;
+ header.pszCaption = title.buf;
+ header.nPages = count;
+ header.nStartPage = 0;
+ header.phpage = hpages;
+ if (icon) {
+ header.hIcon = icon;
+ header.dwFlags |= PSH_USEHICON;
+ }
+
+ handle = (HWND)PropertySheet(&header);
+ if ((handle == 0) || (handle == (HWND)-1))
+ throw rdr::SystemException("PropertySheet failed", GetLastError());
+ centerWindow(handle, owner);
+ plog.info("created %lx", handle);
+
+#if (WINVER >= 0x0500)
+#ifdef _DIALOG_CAPTURE
+ // *** NOT TESTED
+ if (capture) {
+ plog.info("capturing \"%s\"", (const char*)CStr(title.buf));
+ char* tmpdir = getenv("TEMP");
+ HDC dc = GetWindowDC(handle);
+ DeviceFrameBuffer fb(dc);
+ int i=0;
+ while (true) {
+ int id = PropSheet_IndexToId(handle, i);
+ if (!id) break;
+ PropSheet_SetCurSelByID(handle, id);
+ MSG msg;
+ while (PeekMessage(&msg, handle, 0, 0, PM_REMOVE)) {
+ if (!PropSheet_IsDialogMessage(handle, &msg))
+ DispatchMessage(&msg);
+ }
+ fb.grabRect(fb.getRect());
+ char filename[256];
+ sprintf(filename, "%s\\capture%d.bmp", tmpdir, i);
+ saveBMP(filename, &fb);
+ i++;
+ }
+ ReleaseDC(handle, dc);
+ } else {
+#endif
+#endif
+ try {
+ if (owner)
+ EnableWindow(owner, FALSE);
+ // Run the PropertySheet
+ MSG msg;
+ while (GetMessage(&msg, 0, 0, 0)) {
+ if (!PropSheet_IsDialogMessage(handle, &msg))
+ DispatchMessage(&msg);
+ if (!PropSheet_GetCurrentPageHwnd(handle))
+ break;
+ }
+ if (owner)
+ EnableWindow(owner, TRUE);
+ } catch (...) {
+ if (owner)
+ EnableWindow(owner, TRUE);
+ throw;
+ }
+#if (WINVER >= 0x0500)
+#ifdef _DIALOG_CAPTURE
+ }
+#endif
+#endif
+
+ plog.info("finished %lx", handle);
+
+ DestroyWindow(handle);
+ handle = 0;
+ alreadyShowing = false;
+
+ // Clear up the pages' GDI objects
+ for (pspi=pages.begin(); pspi!=pages.end(); pspi++)
+ (*pspi)->setPropSheet(0);
+ delete [] hpages; hpages = 0;
+
+ return true;
+ } catch (rdr::Exception) {
+ alreadyShowing = false;
+
+ std::list<PropSheetPage*>::iterator pspi;
+ for (pspi=pages.begin(); pspi!=pages.end(); pspi++)
+ (*pspi)->setPropSheet(0);
+ delete [] hpages; hpages = 0;
+
+ throw;
+ }
+}
+
+void PropSheet::reInitPages() {
+ plog.debug("reInitPages %lx", handle);
+ std::list<PropSheetPage*>::iterator pspi;
+ for (pspi=pages.begin(); pspi!=pages.end(); pspi++) {
+ if ((*pspi)->handle)
+ (*pspi)->initDialog();
+ }
+}
+
+bool PropSheet::commitPages() {
+ plog.debug("commitPages %lx", handle);
+ bool result = true;
+ std::list<PropSheetPage*>::iterator pspi;
+ for (pspi=pages.begin(); pspi!=pages.end(); pspi++) {
+ if ((*pspi)->handle)
+ result = result && (*pspi)->onOk();
+ }
+ return result;
+}
+
+
+void PropSheetPage::setChanged(bool changed) {
+ if (propSheet) {
+ plog.debug("setChanged[%lx(%lx)]=%d", handle, propSheet->handle, (int)changed);
+ if (changed)
+ PropSheet_Changed(propSheet->handle, handle);
+ else
+ PropSheet_UnChanged(propSheet->handle, handle);
+ }
+}
diff --git a/rfb_win32/Dialog.h b/rfb_win32/Dialog.h
new file mode 100644
index 00000000..d46133a0
--- /dev/null
+++ b/rfb_win32/Dialog.h
@@ -0,0 +1,159 @@
+/* Copyright (C) 2002-2003 RealVNC Ltd. All Rights Reserved.
+ *
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+ * USA.
+ */
+
+// -=- RegConfig.h
+
+// Class which monitors the registry and reads in the registry settings
+// whenever they change, or are added or removed.
+
+#ifndef __RFB_WIN32_DIALOG_H__
+#define __RFB_WIN32_DIALOG_H__
+
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+#include <prsht.h>
+#include <list>
+#include <rfb_win32/TCharArray.h>
+
+namespace rfb {
+
+ namespace win32 {
+
+ // Dialog - A simple Win32 Dialog box. A derived class of Dialog overrides the
+ // initDialog(), command() and ok() methods to take appropriate action. A
+ // simple dialog box can be displayed by creating a Dialog object and calling
+ // show().
+
+ class Dialog {
+ public:
+
+ Dialog(HINSTANCE inst);
+ virtual ~Dialog();
+
+ // showDialog() displays the dialog box. It returns when it has been dismissed,
+ // returning true if "OK" was pressed, false otherwise. The resource
+ // argument identifies the dialog resource (often a MAKEINTRESOURCE macro
+ // expansion), and owner is an optional window handle - the corresponding
+ // window is disabled while the dialog box is displayed.
+
+ bool showDialog(const TCHAR* resource, HWND owner=0);
+
+ // initDialog() is called upon receipt of the WM_INITDIALOG message.
+
+ virtual void initDialog() {}
+
+ // onCommand() is called upon receipt of a WM_COMMAND message item other than IDOK
+ // or IDCANCEL. It should return true if the command has been handled.
+
+ virtual bool onCommand(int item, int cmd) { return false; }
+
+ // onHelp() is called upon receipt of a WM_MENU message. This indicates that
+ // context-specific help should be displayed, for a dialog control, for example.
+ // It should return true if the command has been handled.
+
+ virtual bool onHelp(int item) { return false; }
+
+ // onOk() is called when the OK button is pressed. The hwnd argument is the
+ // dialog box's window handle.
+
+ virtual bool onOk() { return true; }
+
+ // Read the states of items
+ bool isItemChecked(int id);
+ int getItemInt(int id);
+ TCHAR* getItemString(int id); // Recipient owns string storage
+
+ // Set the states of items
+ void setItemChecked(int id, bool state);
+ void setItemInt(int id, int value);
+ void setItemString(int id, const TCHAR* s);
+
+ // enableItem is used to grey out an item, making it inaccessible, or to
+ // re-enable it.
+ void enableItem(int id, bool state);
+
+ protected:
+ static BOOL CALLBACK staticDialogProc(HWND hwnd, UINT msg,
+ WPARAM wParam, LPARAM lParam);
+ virtual BOOL dialogProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam);
+ HINSTANCE inst;
+ HWND handle;
+ bool alreadyShowing;
+ };
+
+ // PropertySheetPage
+ // Class used to define property pages within a PropertySheet.
+ // Each page is associated with a particular dialog resource, indicated by
+ // the "id" parameter supplied to the constructor.
+
+ class PropSheetPage;
+
+ class PropSheet {
+ public:
+ PropSheet(HINSTANCE inst, const TCHAR* title, std::list<PropSheetPage*> pages, HICON icon=0);
+ virtual ~PropSheet();
+
+ // Display the PropertySheet
+ bool showPropSheet(HWND owner, bool showApply = false, bool showCtxtHelp = false, bool capture=false);
+
+ // Calls initDialog again for each page that has already had it called.
+ // Note: If a page hasn't been seen yet, it won't have been called.
+ // Note: This must only be called while the property sheet is visible.
+ void reInitPages();
+
+ // Calls onOk for each page that has had initDialog called, and returns
+ // false if any one of them returns false, or true otherwise. ALL the
+ // onOk() methods will be called, even if one of them fails.
+ // Note: If a page hasn't been seen yet, it won't have been called.
+ // Note: This must only be called while the property sheet is visible.
+ bool commitPages();
+
+ friend class PropSheetPage;
+
+ protected:
+ HWND owner;
+ HICON icon;
+ std::list<PropSheetPage*> pages;
+ HINSTANCE inst;
+ TCharArray title;
+ HWND handle;
+ bool alreadyShowing;
+ };
+
+ class PropSheetPage : public Dialog {
+ public:
+ PropSheetPage(HINSTANCE inst, const TCHAR* id);
+ virtual ~PropSheetPage();
+
+ void setChanged(bool changed);
+
+ friend class PropSheet;
+
+ protected:
+ void setPropSheet(PropSheet* ps) {propSheet = ps;};
+ static BOOL CALLBACK staticPageProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam);
+ virtual BOOL dialogProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam);
+ PROPSHEETPAGE page;
+ PropSheet* propSheet;
+ };
+
+ };
+
+};
+
+#endif // __RFB_WIN32_DIALOG_H__
diff --git a/rfb_win32/IntervalTimer.h b/rfb_win32/IntervalTimer.h
new file mode 100644
index 00000000..645d0ee9
--- /dev/null
+++ b/rfb_win32/IntervalTimer.h
@@ -0,0 +1,70 @@
+/* Copyright (C) 2002-2003 RealVNC Ltd. All Rights Reserved.
+ *
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+ * USA.
+ */
+
+// -=- IntervalTimer.h
+//
+// Simple wrapper for standard Win32 timers
+
+#ifndef __RFB_WIN32_INTERVAL_TIMER_H__
+#define __RFB_WIN32_INTERVAL_TIMER_H__
+
+namespace rfb {
+
+ namespace win32 {
+
+ struct IntervalTimer {
+ IntervalTimer(HWND hwnd_, int id_)
+ : active(false), hwnd(hwnd_), id(id_) {
+ }
+ IntervalTimer() : active(false), hwnd(0), id(0) {
+ }
+ ~IntervalTimer() {
+ stop();
+ }
+
+ void start(int interval_) {
+ if (!active || interval_ != interval) {
+ interval = interval_;
+ if (!SetTimer(hwnd, id, interval, 0))
+ throw rdr::SystemException("SetTimer", GetLastError());
+ active = true;
+ }
+ }
+ void stop() {
+ if (active)
+ KillTimer(hwnd, id);
+ active = false;
+ }
+
+ void setHWND(HWND hwnd_) {hwnd=hwnd_;}
+ void setId(int id_) {id = id_;}
+ int getId() const {return id;}
+ bool isActive() const {return active;}
+
+ private:
+ HWND hwnd;
+ int id;
+ bool active;
+ int interval;
+ };
+
+ }; // win32
+
+}; // rfb
+
+#endif // __RFB_WIN32_INTERVAL_TIMER_H__
diff --git a/rfb_win32/LaunchProcess.cxx b/rfb_win32/LaunchProcess.cxx
new file mode 100644
index 00000000..0a48d60c
--- /dev/null
+++ b/rfb_win32/LaunchProcess.cxx
@@ -0,0 +1,92 @@
+/* Copyright (C) 2002-2004 RealVNC Ltd. All Rights Reserved.
+ *
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+ * USA.
+ */
+
+// -=- LaunchProcess.cxx
+
+#include <rfb_win32/LaunchProcess.h>
+#include <rfb_win32/Win32Util.h>
+
+#include <rfb/util.h>
+
+using namespace rfb;
+using namespace win32;
+
+
+LaunchProcess::LaunchProcess(const TCHAR* exeName_, const TCHAR* params_)
+: exeName(tstrDup(exeName_)), params(tstrDup(params_)) {
+ memset(&procInfo, 0, sizeof(procInfo));
+}
+
+LaunchProcess::~LaunchProcess() {
+ await();
+}
+
+
+void LaunchProcess::start(HANDLE userToken) {
+ if (procInfo.hProcess && (WaitForSingleObject(procInfo.hProcess, 0) != WAIT_OBJECT_0))
+ return;
+ await();
+
+ // - Create storage for the process startup information
+ STARTUPINFO sinfo;
+ memset(&sinfo, 0, sizeof(sinfo));
+ sinfo.cb = sizeof(sinfo);
+
+ // - Concoct a suitable command-line
+ TCharArray exePath;
+ if (!tstrContains(exeName.buf, _T('\\'))) {
+ ModuleFileName filename;
+ TCharArray path; splitPath(filename.buf, &path.buf, 0);
+ exePath.buf = new TCHAR[_tcslen(path.buf) + _tcslen(exeName.buf) + 2];
+ _stprintf(exePath.buf, _T("%s\\%s"), path.buf, exeName.buf);
+ } else {
+ exePath.buf = tstrDup(exeName.buf);
+ }
+
+ // - Start the VNC server
+ // Note: We specify the exe's precise path in the ApplicationName parameter,
+ // AND include the name as the first part of the CommandLine parameter,
+ // because CreateProcess doesn't make ApplicationName argv[0] in C programs.
+ TCharArray cmdLine(_tcslen(exeName.buf) + 3 + _tcslen(params.buf) + 1);
+ _stprintf(cmdLine.buf, _T("\"%s\" %s"), exeName.buf, params.buf);
+#ifdef _DEBUG
+ DWORD flags = CREATE_NEW_CONSOLE;
+#else
+ DWORD flags = CREATE_NO_WINDOW;
+#endif
+ BOOL success;
+ if (userToken)
+ success = CreateProcessAsUser(userToken, exePath.buf, cmdLine.buf, 0, 0, FALSE, flags, 0, 0, &sinfo, &procInfo);
+ else
+ success = CreateProcess(exePath.buf, cmdLine.buf, 0, 0, FALSE, flags, 0, 0, &sinfo, &procInfo);
+ if (!success)
+ throw rdr::SystemException("unable to launch process", GetLastError());
+
+ // Wait for it to finish initialising
+ WaitForInputIdle(procInfo.hProcess, 15000);
+}
+
+void LaunchProcess::await() {
+ if (!procInfo.hProcess)
+ return;
+ WaitForSingleObject(procInfo.hProcess, INFINITE);
+ CloseHandle(procInfo.hProcess);
+ CloseHandle(procInfo.hThread);
+ memset(&procInfo, 0, sizeof(procInfo));
+}
+
diff --git a/rfb_win32/LaunchProcess.h b/rfb_win32/LaunchProcess.h
new file mode 100644
index 00000000..6fd34e9c
--- /dev/null
+++ b/rfb_win32/LaunchProcess.h
@@ -0,0 +1,59 @@
+/* Copyright (C) 2002-2003 RealVNC Ltd. All Rights Reserved.
+ *
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+ * USA.
+ */
+
+// -=- LaunchProcess.h
+
+// Helper class to launch a names process from the same directory as
+// the current process executable resides in.
+
+#ifndef __RFB_WIN32_LAUNCHPROCESS_H__
+#define __RFB_WIN32_LAUNCHPROCESS_H__
+
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+
+#include <rfb_win32/TCharArray.h>
+
+namespace rfb {
+
+ namespace win32 {
+
+ class LaunchProcess {
+ public:
+ LaunchProcess(const TCHAR* exeName_, const TCHAR* params);
+ ~LaunchProcess();
+
+ // If userToken is 0 then starts as current user, otherwise
+ // starts as the specified user. userToken must be a primary token.
+ void start(HANDLE userToken);
+
+ // Wait for the process to quit, and close the handles to it.
+ void await();
+
+ PROCESS_INFORMATION procInfo;
+ protected:
+ TCharArray exeName;
+ TCharArray params;
+ };
+
+
+ };
+
+};
+
+#endif
diff --git a/rfb_win32/MsgWindow.cxx b/rfb_win32/MsgWindow.cxx
new file mode 100644
index 00000000..519d6ab9
--- /dev/null
+++ b/rfb_win32/MsgWindow.cxx
@@ -0,0 +1,116 @@
+/* Copyright (C) 2002-2004 RealVNC Ltd. All Rights Reserved.
+ *
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+ * USA.
+ */
+
+// -=- MsgWindow.cxx
+
+#include <rfb_win32/MsgWindow.h>
+#include <rfb_win32/WMShatter.h>
+#include <rfb/LogWriter.h>
+#include <rdr/Exception.h>
+#include <malloc.h>
+#include <tchar.h>
+
+using namespace rfb;
+using namespace rfb::win32;
+
+static LogWriter vlog("MsgWindow");
+
+//
+// -=- MsgWindowClass
+//
+
+class MsgWindowClass {
+public:
+ MsgWindowClass();
+ ~MsgWindowClass();
+ ATOM classAtom;
+ HINSTANCE instance;
+};
+
+LRESULT CALLBACK MsgWindowProc(HWND wnd, UINT msg, WPARAM wParam, LPARAM lParam) {
+ LRESULT result;
+
+ if (msg == WM_CREATE)
+ SetWindowLong(wnd, GWL_USERDATA, (long)((CREATESTRUCT*)lParam)->lpCreateParams);
+ else if (msg == WM_DESTROY)
+ SetWindowLong(wnd, GWL_USERDATA, 0);
+ MsgWindow* _this = (MsgWindow*) GetWindowLong(wnd, GWL_USERDATA);
+ if (!_this) {
+ vlog.info("null _this in %x, message %x", wnd, msg);
+ return SafeDefWindowProc(wnd, msg, wParam, lParam);
+ }
+
+ try {
+ result = _this->processMessage(msg, wParam, lParam);
+ } catch (rdr::Exception& e) {
+ vlog.error("untrapped: %s", e.str());
+ }
+
+ return result;
+};
+
+MsgWindowClass::MsgWindowClass() : classAtom(0) {
+ WNDCLASS wndClass;
+ wndClass.style = 0;
+ wndClass.lpfnWndProc = MsgWindowProc;
+ wndClass.cbClsExtra = 0;
+ wndClass.cbWndExtra = 0;
+ wndClass.hInstance = instance = GetModuleHandle(0);
+ wndClass.hIcon = 0;
+ wndClass.hCursor = 0;
+ wndClass.hbrBackground = 0;
+ wndClass.lpszMenuName = 0;
+ wndClass.lpszClassName = _T("rfb::win32::MsgWindowClass");
+ classAtom = RegisterClass(&wndClass);
+ if (!classAtom) {
+ throw rdr::SystemException("unable to register MsgWindow window class", GetLastError());
+ }
+}
+
+MsgWindowClass::~MsgWindowClass() {
+ if (classAtom) {
+ UnregisterClass((const TCHAR*)classAtom, instance);
+ }
+}
+
+MsgWindowClass baseClass;
+
+//
+// -=- MsgWindow
+//
+
+MsgWindow::MsgWindow(const TCHAR* name_) : name(tstrDup(name_)), handle(0) {
+ vlog.debug("creating window \"%s\"", (const char*)CStr(name.buf));
+ handle = CreateWindow((const TCHAR*)baseClass.classAtom, name.buf, WS_OVERLAPPED,
+ 0, 0, 10, 10, 0, 0, baseClass.instance, this);
+ if (!handle) {
+ throw rdr::SystemException("unable to create WMNotifier window instance", GetLastError());
+ }
+ vlog.debug("created window \"%s\" (%x)", (const char*)CStr(name.buf), handle);
+}
+
+MsgWindow::~MsgWindow() {
+ if (handle)
+ DestroyWindow(handle);
+ vlog.debug("destroyed window \"%s\" (%x)", (const char*)CStr(name.buf), handle);
+}
+
+LRESULT
+MsgWindow::processMessage(UINT msg, WPARAM wParam, LPARAM lParam) {
+ return SafeDefWindowProc(getHandle(), msg, wParam, lParam);
+} \ No newline at end of file
diff --git a/rfb_win32/MsgWindow.h b/rfb_win32/MsgWindow.h
new file mode 100644
index 00000000..94baca38
--- /dev/null
+++ b/rfb_win32/MsgWindow.h
@@ -0,0 +1,55 @@
+/* Copyright (C) 2002-2003 RealVNC Ltd. All Rights Reserved.
+ *
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+ * USA.
+ */
+
+// -=- MsgWindow.h
+
+// Base-class for any hidden message-handling windows used in the rfb::win32
+// implementation.
+
+#ifndef __RFB_WIN32_MSG_WINDOW_H__
+#define __RFB_WIN32_MSG_WINDOW_H__
+
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+
+#include <rfb_win32/TCharArray.h>
+
+namespace rfb {
+
+ namespace win32 {
+
+ class MsgWindow {
+ public:
+ MsgWindow(const TCHAR* _name);
+ virtual ~MsgWindow();
+
+ const TCHAR* getName() {return name.buf;}
+ HWND getHandle() const {return handle;}
+
+ virtual LRESULT processMessage(UINT msg, WPARAM wParam, LPARAM lParam);
+
+ protected:
+ TCharArray name;
+ HWND handle;
+ };
+
+ };
+
+};
+
+#endif // __RFB_WIN32_MSG_WINDOW_H__
diff --git a/rfb_win32/OSVersion.cxx b/rfb_win32/OSVersion.cxx
new file mode 100644
index 00000000..1976098f
--- /dev/null
+++ b/rfb_win32/OSVersion.cxx
@@ -0,0 +1,47 @@
+/* Copyright (C) 2002-2003 RealVNC Ltd. All Rights Reserved.
+ *
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+ * USA.
+ */
+
+// -=- OSVersion.cxx
+
+#include <rfb_win32/OSVersion.h>
+#include <rdr/Exception.h>
+#include <tchar.h>
+
+using namespace rfb;
+using namespace win32;
+
+
+OSVersionInfo::OSVersionInfo() {
+ // Get OS Version Info
+ ZeroMemory(static_cast<OSVERSIONINFO*>(this), sizeof(this));
+ dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
+ if (!GetVersionEx(this))
+ throw rdr::SystemException("unable to get system version info", GetLastError());
+
+ // Set the special extra flags
+ isPlatformNT = dwPlatformId == VER_PLATFORM_WIN32_NT;
+ isPlatformWindows = dwPlatformId == VER_PLATFORM_WIN32_WINDOWS;
+
+ cannotSwitchDesktop = isPlatformNT && (dwMajorVersion==4) &&
+ ((_tcscmp(szCSDVersion, _T("")) == 0) ||
+ (_tcscmp(szCSDVersion, _T("Service Pack 1")) == 0) ||
+ (_tcscmp(szCSDVersion, _T("Service Pack 2")) == 0));
+
+}
+
+OSVersionInfo rfb::win32::osVersion;
diff --git a/rfb_win32/OSVersion.h b/rfb_win32/OSVersion.h
new file mode 100644
index 00000000..1d529431
--- /dev/null
+++ b/rfb_win32/OSVersion.h
@@ -0,0 +1,54 @@
+/* Copyright (C) 2002-2003 RealVNC Ltd. All Rights Reserved.
+ *
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+ * USA.
+ */
+
+// -=- OSVersion.h
+
+// Operating system version info.
+// GetVersionInfo is called once at process initialisation, and any
+// extra flags (such as isWinNT) are calculated and saved at that
+// point. It is assumed that the OS Version seldom changes during a
+// program's execution...
+
+#ifndef __RFB_WIN32_OS_VERSION_H__
+#define __RFB_WIN32_OS_VERSION_H__
+
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+
+namespace rfb {
+
+ namespace win32 {
+
+ extern struct OSVersionInfo : OSVERSIONINFO {
+ OSVersionInfo();
+
+ // Is the OS one of the NT family (NT 3.51, NT4.0, 2K, XP, etc.)?
+ bool isPlatformNT;
+ // Is one of the Windows family?
+ bool isPlatformWindows;
+
+ // Is this OS one of those that blue-screens when grabbing another desktop (NT4 pre SP3)?
+ bool cannotSwitchDesktop;
+
+ } osVersion;
+
+ };
+
+};
+
+#endif // __RFB_WIN32_OS_VERSION_H__
diff --git a/rfb_win32/RegConfig.cxx b/rfb_win32/RegConfig.cxx
new file mode 100644
index 00000000..fcb309b5
--- /dev/null
+++ b/rfb_win32/RegConfig.cxx
@@ -0,0 +1,151 @@
+/* Copyright (C) 2002-2003 RealVNC Ltd. All Rights Reserved.
+ *
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+ * USA.
+ */
+
+// -=- RegConfig.cxx
+
+#include <malloc.h>
+
+#include <rfb_win32/RegConfig.h>
+#include <rfb/LogWriter.h>
+#include <rfb/util.h>
+#include <rdr/HexOutStream.h>
+
+using namespace rfb;
+using namespace rfb::win32;
+
+
+static LogWriter vlog("RegConfig");
+
+
+class rfb::win32::RegReaderThread : public Thread {
+public:
+ RegReaderThread(RegistryReader& reader, const HKEY key);
+ ~RegReaderThread();
+ virtual void run();
+ virtual Thread* join();
+protected:
+ RegistryReader& reader;
+ RegKey key;
+ HANDLE event;
+};
+
+RegReaderThread::RegReaderThread(RegistryReader& reader_, const HKEY key_) : Thread("RegConfig"), reader(reader_), key(key_) {
+}
+
+RegReaderThread::~RegReaderThread() {
+}
+
+void
+RegReaderThread::run() {
+ vlog.debug("RegReaderThread started");
+ while (key) {
+ // - Wait for changes
+ vlog.debug("waiting for changes");
+ key.awaitChange(true, REG_NOTIFY_CHANGE_NAME | REG_NOTIFY_CHANGE_LAST_SET);
+
+ // - Load settings
+ RegistryReader::loadRegistryConfig(key);
+
+ // - Notify specified thread of changes
+ if (reader.notifyThread)
+ PostThreadMessage(reader.notifyThread->getThreadId(),
+ reader.notifyMsg.message,
+ reader.notifyMsg.wParam,
+ reader.notifyMsg.lParam);
+ else if (reader.notifyWindow)
+ PostMessage(reader.notifyWindow,
+ reader.notifyMsg.message,
+ reader.notifyMsg.wParam,
+ reader.notifyMsg.lParam);
+ }
+}
+
+Thread*
+RegReaderThread::join() {
+ RegKey old_key = key;
+ key.close();
+ if ((HKEY)old_key) {
+ // *** Closing the key doesn't always seem to work
+ // Writing to it always will, instead...
+ vlog.debug("closing key");
+ old_key.setString(_T("dummy"), _T(""));
+ }
+ return Thread::join();
+}
+
+
+RegistryReader::RegistryReader() : thread(0), notifyThread(0) {
+ memset(&notifyMsg, 0, sizeof(notifyMsg));
+}
+
+RegistryReader::~RegistryReader() {
+ if (thread) delete thread->join();
+}
+
+bool RegistryReader::setKey(const HKEY rootkey, const TCHAR* keyname) {
+ if (thread) delete thread->join();
+ thread = 0;
+
+ RegKey key;
+ try {
+ key.createKey(rootkey, keyname);
+ loadRegistryConfig(key);
+ } catch (rdr::Exception& e) {
+ vlog.debug(e.str());
+ return false;
+ }
+ thread = new RegReaderThread(*this, key);
+ if (thread) thread->start();
+ return true;
+}
+
+void
+RegistryReader::loadRegistryConfig(RegKey& key) {
+ DWORD i = 0;
+ try {
+ while (1) {
+ TCharArray name = tstrDup(key.getValueName(i++));
+ if (!name.buf) break;
+ TCharArray value = key.getRepresentation(name.buf);
+ if (!value.buf || !Configuration::setParam(CStr(name.buf), CStr(value.buf)))
+ vlog.info("unable to process %s", CStr(name.buf));
+ }
+ } catch (rdr::SystemException& e) {
+ if (e.err != 6)
+ vlog.error(e.str());
+ }
+}
+
+bool RegistryReader::setNotifyThread(Thread* thread, UINT winMsg, WPARAM wParam, LPARAM lParam) {
+ notifyMsg.message = winMsg;
+ notifyMsg.wParam = wParam;
+ notifyMsg.lParam = lParam;
+ notifyThread = thread;
+ notifyWindow = 0;
+ return true;
+}
+
+bool RegistryReader::setNotifyWindow(HWND window, UINT winMsg, WPARAM wParam, LPARAM lParam) {
+ notifyMsg.message = winMsg;
+ notifyMsg.wParam = wParam;
+ notifyMsg.lParam = lParam;
+ notifyWindow = window;
+ notifyThread = 0;
+ return true;
+}
+
diff --git a/rfb_win32/RegConfig.h b/rfb_win32/RegConfig.h
new file mode 100644
index 00000000..3fced85e
--- /dev/null
+++ b/rfb_win32/RegConfig.h
@@ -0,0 +1,56 @@
+/* Copyright (C) 2002-2003 RealVNC Ltd. All Rights Reserved.
+ *
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+ * USA.
+ */
+
+// -=- RegConfig.h
+
+// Class which monitors the registry and reads in the registry settings
+// whenever they change, or are added or removed.
+
+#ifndef __RFB_WIN32_REG_CONFIG_H__
+#define __RFB_WIN32_REG_CONFIG_H__
+
+#include <rfb/Threading.h>
+#include <rfb/Configuration.h>
+
+#include <rfb_win32/Registry.h>
+
+namespace rfb {
+
+ namespace win32 {
+
+ class RegistryReader {
+ public:
+ RegistryReader();
+ ~RegistryReader();
+ bool setKey(const HKEY rootkey, const TCHAR* keyname);
+ bool setNotifyThread(Thread* thread, UINT winMsg, WPARAM wParam=0, LPARAM lParam=0);
+ bool setNotifyWindow(HWND window, UINT winMsg, WPARAM wParam=0, LPARAM lParam=0);
+ static void loadRegistryConfig(RegKey& key);
+ protected:
+ friend class RegReaderThread;
+ Thread* thread;
+ Thread* notifyThread;
+ HWND notifyWindow;
+ MSG notifyMsg;
+ };
+
+ };
+
+};
+
+#endif // __RFB_WIN32_REG_CONFIG_H__
diff --git a/rfb_win32/Registry.cxx b/rfb_win32/Registry.cxx
new file mode 100644
index 00000000..de9238f7
--- /dev/null
+++ b/rfb_win32/Registry.cxx
@@ -0,0 +1,272 @@
+/* Copyright (C) 2002-2004 RealVNC Ltd. All Rights Reserved.
+ *
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+ * USA.
+ */
+
+// -=- Registry.cxx
+
+#include <rfb/LogWriter.h>
+#include <rfb_win32/Registry.h>
+#include <rdr/MemOutStream.h>
+#include <rdr/HexOutstream.h>
+#include <rdr/HexInStream.h>
+#include <rfb_win32/Security.h>
+
+#include <stdlib.h>
+
+// These flags are required to control access control inheritance,
+// but are not defined by VC6's headers. These definitions comes
+// from the Microsoft Platform SDK.
+#ifndef PROTECTED_DACL_SECURITY_INFORMATION
+#define PROTECTED_DACL_SECURITY_INFORMATION (0x80000000L)
+#endif
+#ifndef UNPROTECTED_DACL_SECURITY_INFORMATION
+#define UNPROTECTED_DACL_SECURITY_INFORMATION (0x20000000L)
+#endif
+
+
+using namespace rfb;
+using namespace rfb::win32;
+
+
+static LogWriter vlog("Registry");
+
+
+RegKey::RegKey() : key(0), freeKey(false), valueNameBufLen(0) {}
+
+RegKey::RegKey(const HKEY k) : key(0), freeKey(false), valueNameBufLen(0) {
+ LONG result = RegOpenKeyEx(k, 0, 0, KEY_ALL_ACCESS, &key);
+ if (result != ERROR_SUCCESS)
+ throw rdr::SystemException("RegOpenKeyEx(HKEY)", result);
+ vlog.debug("duplicated %x to %x", k, key);
+ freeKey = true;
+}
+
+RegKey::RegKey(const RegKey& k) : key(0), freeKey(false), valueNameBufLen(0) {
+ LONG result = RegOpenKeyEx(k.key, 0, 0, KEY_ALL_ACCESS, &key);
+ if (result != ERROR_SUCCESS)
+ throw rdr::SystemException("RegOpenKeyEx(RegKey&)", result);
+ vlog.debug("duplicated %x to %x", k.key, key);
+ freeKey = true;
+}
+
+RegKey::~RegKey() {
+ close();
+}
+
+
+void RegKey::setHKEY(HKEY k, bool fK) {
+ close();
+ freeKey = fK;
+ key = k;
+}
+
+
+bool RegKey::createKey(const RegKey& root, const TCHAR* name) {
+ close();
+ LONG result = RegCreateKey(root.key, name, &key);
+ if (result != ERROR_SUCCESS) {
+ vlog.error("RegCreateKey(%x, %s): %x", root.key, name, result);
+ throw rdr::SystemException("RegCreateKeyEx", result);
+ }
+ freeKey = true;
+ return true;
+}
+
+void RegKey::openKey(const RegKey& root, const TCHAR* name, bool readOnly) {
+ close();
+ LONG result = RegOpenKeyEx(root.key, name, 0, readOnly ? KEY_READ : KEY_ALL_ACCESS, &key);
+ if (result != ERROR_SUCCESS)
+ throw rdr::SystemException("RegOpenKeyEx (open)", result);
+ freeKey = true;
+}
+
+void RegKey::setDACL(const PACL acl, bool inherit) {
+ DWORD result;
+ typedef DWORD (WINAPI *_SetSecurityInfo_proto) (HANDLE, SE_OBJECT_TYPE, SECURITY_INFORMATION, PSID, PSID, PACL, PACL);
+ DynamicFn<_SetSecurityInfo_proto> _SetSecurityInfo(_T("advapi32.dll"), "SetSecurityInfo");
+ if (!_SetSecurityInfo.isValid())
+ throw rdr::SystemException("RegKey::setDACL failed", ERROR_CALL_NOT_IMPLEMENTED);
+ if ((result = (*_SetSecurityInfo)(key, SE_REGISTRY_KEY,
+ DACL_SECURITY_INFORMATION |
+ (inherit ? UNPROTECTED_DACL_SECURITY_INFORMATION : PROTECTED_DACL_SECURITY_INFORMATION),
+ 0, 0, acl, 0)) != ERROR_SUCCESS)
+ throw rdr::SystemException("RegKey::setDACL failed", result);
+}
+
+void RegKey::close() {
+ if (freeKey) {
+ RegCloseKey(key);
+ key = 0;
+ }
+}
+
+void RegKey::deleteKey(const TCHAR* name) const {
+ LONG result = RegDeleteKey(key, name);
+ if (result != ERROR_SUCCESS)
+ throw rdr::SystemException("RegDeleteKey", result);
+}
+
+void RegKey::deleteValue(const TCHAR* name) const {
+ LONG result = RegDeleteValue(key, name);
+ if (result != ERROR_SUCCESS)
+ throw rdr::SystemException("RegDeleteValue", result);
+}
+
+void RegKey::awaitChange(bool watchSubTree, DWORD filter) const {
+ LONG result = RegNotifyChangeKeyValue(key, watchSubTree, filter, 0, FALSE);
+ if (result != ERROR_SUCCESS)
+ throw rdr::SystemException("RegNotifyChangeKeyValue", result);
+}
+
+
+RegKey::operator HKEY() const {return key;}
+
+
+void RegKey::setExpandString(const TCHAR* valname, const TCHAR* value) const {
+ LONG result = RegSetValueEx(key, valname, 0, REG_EXPAND_SZ, (const BYTE*)value, (_tcslen(value)+1)*sizeof(TCHAR));
+ if (result != ERROR_SUCCESS) throw rdr::SystemException("setExpandString", result);
+}
+
+void RegKey::setString(const TCHAR* valname, const TCHAR* value) const {
+ LONG result = RegSetValueEx(key, valname, 0, REG_SZ, (const BYTE*)value, (_tcslen(value)+1)*sizeof(TCHAR));
+ if (result != ERROR_SUCCESS) throw rdr::SystemException("setString", result);
+}
+
+void RegKey::setBinary(const TCHAR* valname, const void* value, int length) const {
+ LONG result = RegSetValueEx(key, valname, 0, REG_BINARY, (const BYTE*)value, length);
+ if (result != ERROR_SUCCESS) throw rdr::SystemException("setBinary", result);
+}
+
+void RegKey::setInt(const TCHAR* valname, int value) const {
+ LONG result = RegSetValueEx(key, valname, 0, REG_DWORD, (const BYTE*)&value, sizeof(value));
+ if (result != ERROR_SUCCESS) throw rdr::SystemException("setInt", result);
+}
+
+void RegKey::setBool(const TCHAR* valname, bool value) const {
+ setInt(valname, value ? 1 : 0);
+}
+
+TCHAR* RegKey::getString(const TCHAR* valname) const {return getRepresentation(valname);}
+TCHAR* RegKey::getString(const TCHAR* valname, const TCHAR* def) const {
+ try {
+ return getString(valname);
+ } catch(rdr::Exception) {
+ return tstrDup(def);
+ }
+}
+
+void RegKey::getBinary(const TCHAR* valname, void** data, int* length) const {
+ TCharArray hex = getRepresentation(valname);
+ if (!rdr::HexInStream::hexStrToBin(CStr(hex.buf), (char**)data, length))
+ throw rdr::Exception("getBinary failed");
+}
+void RegKey::getBinary(const TCHAR* valname, void** data, int* length, void* def, int deflen) const {
+ try {
+ getBinary(valname, data, length);
+ } catch(rdr::Exception) {
+ if (deflen) {
+ *data = new char[deflen];
+ memcpy(*data, def, deflen);
+ } else
+ *data = 0;
+ *length = deflen;
+ }
+}
+
+int RegKey::getInt(const TCHAR* valname) const {
+ TCharArray tmp = getRepresentation(valname);
+ return _ttoi(tmp.buf);
+}
+int RegKey::getInt(const TCHAR* valname, int def) const {
+ try {
+ return getInt(valname);
+ } catch(rdr::Exception) {
+ return def;
+ }
+}
+
+bool RegKey::getBool(const TCHAR* valname) const {
+ return getInt(valname) > 0;
+}
+bool RegKey::getBool(const TCHAR* valname, bool def) const {
+ return getInt(valname, def ? 1 : 0) > 0;
+}
+
+TCHAR* RegKey::getRepresentation(const TCHAR* valname) const {
+ DWORD type, length;
+ LONG result = RegQueryValueEx(key, valname, 0, &type, 0, &length);
+ if (result != ERROR_SUCCESS)
+ throw rdr::SystemException("get registry value length", result);
+ CharArray data(length);
+ result = RegQueryValueEx(key, valname, 0, &type, (BYTE*)data.buf, &length);
+ if (result != ERROR_SUCCESS)
+ throw rdr::SystemException("get registry value", result);
+
+ switch (type) {
+ case REG_BINARY:
+ {
+ TCharArray hex = rdr::HexOutStream::binToHexStr(data.buf, length);
+ return hex.takeBuf();
+ }
+ case REG_SZ:
+ if (length) {
+ // We must terminate the string, just to be sure. Stupid Win32...
+ int len = length/sizeof(TCHAR);
+ TCharArray str(len+1);
+ memcpy(str.buf, data.buf, length);
+ str.buf[len] = 0;
+ return str.takeBuf();
+ } else {
+ return tstrDup(_T(""));
+ }
+ case REG_DWORD:
+ {
+ TCharArray tmp(16);
+ _stprintf(tmp.buf, _T("%u"), *((DWORD*)data.buf));
+ return tmp.takeBuf();
+ }
+ default:
+ throw rdr::Exception("unsupported registry type");
+ }
+}
+
+bool RegKey::isValue(const TCHAR* valname) const {
+ try {
+ TCharArray tmp = getRepresentation(valname);
+ return true;
+ } catch(rdr::Exception) {
+ return false;
+ }
+}
+
+const TCHAR* RegKey::getValueName(int i) {
+ DWORD maxValueNameLen;
+ LONG result = RegQueryInfoKey(key, 0, 0, 0, 0, 0, 0, 0, &maxValueNameLen, 0, 0, 0);
+ if (result != ERROR_SUCCESS)
+ throw rdr::SystemException("RegQueryInfoKey", result);
+ if (valueNameBufLen < maxValueNameLen + 1) {
+ valueNameBufLen = maxValueNameLen + 1;
+ delete [] valueName.buf;
+ valueName.buf = new TCHAR[valueNameBufLen];
+ }
+ DWORD length = valueNameBufLen;
+ result = RegEnumValue(key, i, valueName.buf, &length, NULL, 0, 0, 0);
+ if (result == ERROR_NO_MORE_ITEMS) return 0;
+ if (result != ERROR_SUCCESS)
+ throw rdr::SystemException("RegEnumValue", result);
+ return valueName.buf;
+}
diff --git a/rfb_win32/Registry.h b/rfb_win32/Registry.h
new file mode 100644
index 00000000..1998c497
--- /dev/null
+++ b/rfb_win32/Registry.h
@@ -0,0 +1,111 @@
+/* Copyright (C) 2002-2004 RealVNC Ltd. All Rights Reserved.
+ *
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+ * USA.
+ */
+// -=- Registry.h
+
+// C++ wrappers around the Win32 Registry APIs
+
+#ifndef __RFB_WIN32_REGISTRY_H__
+#define __RFB_WIN32_REGISTRY_H__
+
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+
+#include <rfb_win32/Security.h>
+#include <rfb/util.h>
+
+namespace rfb {
+
+ namespace win32 {
+
+ class RegKey {
+ public:
+ // No key open
+ RegKey();
+
+ // Duplicate the specified existing key
+ RegKey(const HKEY k);
+ RegKey(const RegKey& k);
+
+ // Calls close() internally
+ ~RegKey();
+
+ void setHKEY(HKEY key, bool freeKey);
+ protected:
+ HKEY operator=(const RegKey& k);
+ HKEY operator=(HKEY k);
+ public:
+
+ // Returns true if key was created, false if already existed
+ bool createKey(const RegKey& root, const TCHAR* name);
+
+ // Opens key if it exists, or raises an exception if not
+ void openKey(const RegKey& root, const TCHAR* name, bool readOnly=false);
+
+ // Set the (discretionary) access control list for the key
+ void setDACL(const PACL acl, bool inheritFromParent=true);
+
+ // Closes current key, if required
+ void close();
+
+ // Delete a subkey/value
+ void deleteKey(const TCHAR* name) const;
+ void deleteValue(const TCHAR* name) const;
+
+
+ void awaitChange(bool watchSubTree, DWORD filter) const;
+
+ void setExpandString(const TCHAR* valname, const TCHAR* s) const;
+ void setString(const TCHAR* valname, const TCHAR* s) const;
+ void setBinary(const TCHAR* valname, const void* data, int length) const;
+ void setInt(const TCHAR* valname, int i) const;
+ void setBool(const TCHAR* valname, bool b) const;
+
+ TCHAR* getString(const TCHAR* valname) const;
+ TCHAR* getString(const TCHAR* valname, const TCHAR* def) const;
+
+ void getBinary(const TCHAR* valname, void** data, int* length) const;
+ void getBinary(const TCHAR* valname, void** data, int* length, void* def, int deflength) const;
+
+ int getInt(const TCHAR* valname) const;
+ int getInt(const TCHAR* valname, int def) const;
+
+ bool getBool(const TCHAR* valname) const;
+ bool getBool(const TCHAR* valname, bool def) const;
+
+ TCHAR* getRepresentation(const TCHAR* valname) const;
+
+ bool isValue(const TCHAR* valname) const;
+
+ // Get the name of value number "i"
+ // If there are fewer than "i" values then return 0
+ // NAME IS OWNED BY RegKey OBJECT!
+ const TCHAR* getValueName(int i);
+
+ operator HKEY() const;
+ protected:
+ HKEY key;
+ bool freeKey;
+ TCharArray valueName;
+ DWORD valueNameBufLen;
+ };
+
+ };
+
+};
+
+#endif // __RFB_WIN32_REG_CONFIG_H__
diff --git a/rfb_win32/SDisplay.cxx b/rfb_win32/SDisplay.cxx
new file mode 100644
index 00000000..6fa3ff0e
--- /dev/null
+++ b/rfb_win32/SDisplay.cxx
@@ -0,0 +1,612 @@
+/* Copyright (C) 2002-2004 RealVNC Ltd. All Rights Reserved.
+ *
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+ * USA.
+ */
+
+// -=- SDisplay.cxx
+//
+// The SDisplay class encapsulates a particular system display.
+
+#include <assert.h>
+
+#include <rfb_win32/SDisplay.h>
+#include <rfb_win32/Service.h>
+#include <rfb_win32/WMShatter.h>
+#include <rfb_win32/osVersion.h>
+#include <rfb_win32/Win32Util.h>
+#include <rfb_win32/IntervalTimer.h>
+#include <rfb_win32/CleanDesktop.h>
+
+#include <rfb/util.h>
+#include <rfb/LogWriter.h>
+#include <rfb/Exception.h>
+
+#include <rfb/Configuration.h>
+
+using namespace rdr;
+using namespace rfb;
+using namespace rfb::win32;
+
+static LogWriter vlog("SDisplay");
+
+// - SDisplay-specific configuration options
+
+BoolParameter rfb::win32::SDisplay::use_hooks("UseHooks",
+ "Set hooks in the operating system to capture display updates more efficiently", true);
+BoolParameter rfb::win32::SDisplay::disableLocalInputs("DisableLocalInputs",
+ "Disable local keyboard and pointer input while the server is in use", false);
+StringParameter rfb::win32::SDisplay::disconnectAction("DisconnectAction",
+ "Action to perform when all clients have disconnected. (None, Lock, Logoff)", "None");
+
+BoolParameter rfb::win32::SDisplay::removeWallpaper("RemoveWallpaper",
+ "Remove the desktop wallpaper when the server in in use.", false);
+BoolParameter rfb::win32::SDisplay::removePattern("RemovePattern",
+ "Remove the desktop background pattern when the server in in use.", false);
+BoolParameter rfb::win32::SDisplay::disableEffects("DisableEffects",
+ "Disable desktop user interface effects when the server is in use.", false);
+
+
+// - WM_TIMER ID values
+
+#define TIMER_CURSOR 1
+#define TIMER_UPDATE 2
+#define TIMER_UPDATE_AND_POLL 3
+
+
+// -=- Polling settings
+
+const int POLLING_SEGMENTS = 16;
+
+const int FG_POLLING_FPS = 20;
+const int FG_POLLING_FS_INTERVAL = 1000 / FG_POLLING_FPS;
+const int FG_POLLING_INTERVAL = FG_POLLING_FS_INTERVAL / POLLING_SEGMENTS;
+
+const int BG_POLLING_FS_INTERVAL = 5000;
+const int BG_POLLING_INTERVAL = BG_POLLING_FS_INTERVAL / POLLING_SEGMENTS;
+
+
+//////////////////////////////////////////////////////////////////////////////
+//
+// SDisplayCore
+//
+
+// The SDisplay Core object is created by SDisplay's start() method
+// and deleted by its stop() method.
+// The Core must be created in the current input desktop in order
+// to operate - SDisplay is responsible for ensuring that.
+// The structures contained in the Core are manipulated directly
+// by the SDisplay, which is also responsible for detecting when
+// a desktop-switch is required.
+
+class rfb::win32::SDisplayCore : public MsgWindow {
+public:
+ SDisplayCore(SDisplay* display);
+ ~SDisplayCore();
+
+ void setPixelBuffer(DeviceFrameBuffer* pb_);
+
+ bool isRestartRequired();
+
+ virtual LRESULT processMessage(UINT msg, WPARAM wParam, LPARAM lParam);
+
+ // -=- Timers
+ IntervalTimer pollTimer;
+ IntervalTimer cursorTimer;
+
+ // -=- Input handling
+ rfb::win32::SPointer ptr;
+ rfb::win32::SKeyboard kbd;
+ rfb::win32::Clipboard clipboard;
+
+ // -=- Hook handling objects used outside thread run() method
+ WMCopyRect wm_copyrect;
+ WMPoller wm_poller;
+ WMCursor cursor;
+ WMMonitor wm_monitor;
+ WMHooks wm_hooks;
+ WMBlockInput wm_input;
+
+ // -=- Tidying the desktop
+ CleanDesktop cleanDesktop;
+ bool isWallpaperRemoved;
+ bool isPatternRemoved;
+ bool areEffectsDisabled;
+
+ // -=- Full screen polling
+ int poll_next_y;
+ int poll_y_increment;
+
+ // Are we using hooks?
+ bool use_hooks;
+ bool using_hooks;
+
+ // State of the display object
+ SDisplay* display;
+};
+
+SDisplayCore::SDisplayCore(SDisplay* display_)
+: MsgWindow(_T("SDisplayCore")), display(display_),
+ using_hooks(0), use_hooks(rfb::win32::SDisplay::use_hooks),
+ isWallpaperRemoved(rfb::win32::SDisplay::removeWallpaper),
+ isPatternRemoved(rfb::win32::SDisplay::removePattern),
+ areEffectsDisabled(rfb::win32::SDisplay::disableEffects),
+ pollTimer(getHandle(), TIMER_UPDATE_AND_POLL),
+ cursorTimer(getHandle(), TIMER_CURSOR) {
+ setPixelBuffer(display->pb);
+}
+
+SDisplayCore::~SDisplayCore() {
+}
+
+void SDisplayCore::setPixelBuffer(DeviceFrameBuffer* pb) {
+ poll_y_increment = (display->pb->height()+POLLING_SEGMENTS-1)/POLLING_SEGMENTS;
+ poll_next_y = display->screenRect.tl.y;
+ wm_hooks.setClipRect(display->screenRect);
+ wm_copyrect.setClipRect(display->screenRect);
+ wm_poller.setClipRect(display->screenRect);
+}
+
+
+bool SDisplayCore::isRestartRequired() {
+ // - We must restart the SDesktop if:
+ // 1. We are no longer in the input desktop.
+ // 2. The use_hooks setting has changed.
+
+ // - Check that we are in the input desktop
+ if (rfb::win32::desktopChangeRequired())
+ return true;
+
+ // - Check that the hooks setting hasn't changed
+ // NB: We can't just check using_hooks because that can be false
+ // because they failed, even though use_hooks is true!
+ if (use_hooks != rfb::win32::SDisplay::use_hooks)
+ return true;
+
+ // - Check that the desktop optimisation settings haven't changed
+ // This isn't very efficient, but it shouldn't change very often!
+ if ((isWallpaperRemoved != rfb::win32::SDisplay::removeWallpaper) ||
+ (isPatternRemoved != rfb::win32::SDisplay::removePattern) ||
+ (areEffectsDisabled != rfb::win32::SDisplay::disableEffects))
+ return true;
+
+ return false;
+}
+
+LRESULT SDisplayCore::processMessage(UINT msg, WPARAM wParam, LPARAM lParam) {
+ switch (msg) {
+
+ case WM_TIMER:
+
+ if (display->server && display->server->clientsReadyForUpdate()) {
+
+ // - Check that the SDesktop doesn't need restarting
+ if (isRestartRequired()) {
+ display->restart();
+ return 0;
+ }
+
+ // - Action depends on the timer message type
+ switch (wParam) {
+
+ // POLL THE SCREEN
+ case TIMER_UPDATE_AND_POLL:
+ // Handle window dragging, polling of consoles, etc.
+ while (wm_poller.processEvent()) {}
+
+ // Poll the next strip of the screen (in Screen coordinates)
+ {
+ Rect pollrect = display->screenRect;
+ if (poll_next_y >= pollrect.br.y) {
+ // Yes. Reset the counter and return
+ poll_next_y = pollrect.tl.y;
+ } else {
+ // No. Poll the next section
+ pollrect.tl.y = poll_next_y;
+ poll_next_y += poll_y_increment;
+ pollrect.br.y = min(poll_next_y, pollrect.br.y);
+ display->add_changed(pollrect);
+ }
+ }
+ break;
+
+ case TIMER_CURSOR:
+ display->triggerUpdate();
+ break;
+
+ };
+
+ }
+ return 0;
+
+ };
+
+ return MsgWindow::processMessage(msg, wParam, lParam);
+}
+
+//////////////////////////////////////////////////////////////////////////////
+//
+// SDisplay
+//
+
+// -=- Constructor/Destructor
+
+SDisplay::SDisplay(const TCHAR* devName)
+ : server(0), change_tracker(true), pb(0),
+ deviceName(tstrDup(devName)), device(0), releaseDevice(false),
+ core(0), statusLocation(0)
+{
+ updateEvent.h = CreateEvent(0, TRUE, FALSE, 0);
+}
+
+SDisplay::~SDisplay()
+{
+ // XXX when the VNCServer has been deleted with clients active, stop()
+ // doesn't get called - this ought to be fixed in VNCServerST. In any event,
+ // we should never call any methods on VNCServer once we're being deleted.
+ // This is because it is supposed to be guaranteed that the SDesktop exists
+ // throughout the lifetime of the VNCServer. So if we're being deleted, then
+ // the VNCServer ought not to exist and therefore we shouldn't invoke any
+ // methods on it. Setting server to zero here ensures that stop() doesn't
+ // call setPixelBuffer(0) on the server.
+ server = 0;
+ if (core) stop();
+}
+
+
+// -=- SDesktop interface
+
+void SDisplay::start(VNCServer* vs)
+{
+ vlog.debug("starting");
+ server = vs;
+
+ // Switch to the current input desktop
+ // ***
+ if (rfb::win32::desktopChangeRequired()) {
+ if (!rfb::win32::changeDesktop())
+ throw rdr::Exception("unable to switch into input desktop");
+ }
+
+ // Clear the change tracker
+ change_tracker.clear();
+
+ // Create the framebuffer object
+ recreatePixelBuffer();
+
+ // Create the SDisplayCore
+ core = new SDisplayCore(this);
+ assert(core);
+
+ // Start display monitor and clipboard handler
+ core->wm_monitor.setNotifier(this);
+ core->clipboard.setNotifier(this);
+
+ // Apply desktop optimisations
+ if (removePattern)
+ core->cleanDesktop.disablePattern();
+ if (removeWallpaper)
+ core->cleanDesktop.disableWallpaper();
+ if (disableEffects)
+ core->cleanDesktop.disableEffects();
+
+ // Start hooks
+ core->wm_hooks.setClipRect(screenRect);
+ if (core->use_hooks) {
+ // core->wm_hooks.setDiagnosticRange(0, 0x400-1);
+ core->using_hooks = core->wm_hooks.setUpdateTracker(this);
+ if (!core->using_hooks)
+ vlog.debug("hook subsystem failed to initialise");
+ }
+
+ // Set up timers
+ core->pollTimer.start(core->using_hooks ? BG_POLLING_INTERVAL : FG_POLLING_INTERVAL);
+ core->cursorTimer.start(10);
+
+ // Register an interest in faked copyrect events
+ core->wm_copyrect.setUpdateTracker(&change_tracker);
+ core->wm_copyrect.setClipRect(screenRect);
+
+ // Polling of particular windows on the desktop
+ core->wm_poller.setUpdateTracker(&change_tracker);
+ core->wm_poller.setClipRect(screenRect);
+
+ vlog.debug("started");
+
+ if (statusLocation) *statusLocation = true;
+}
+
+void SDisplay::stop()
+{
+ vlog.debug("stopping");
+ if (core) {
+ // If SDisplay was actually active then perform the disconnect action
+ CharArray action = disconnectAction.getData();
+ if (stricmp(action.buf, "Logoff") == 0) {
+ ExitWindowsEx(EWX_LOGOFF, 0);
+ } else if (stricmp(action.buf, "Lock") == 0) {
+ typedef BOOL (WINAPI *_LockWorkStation_proto)();
+ DynamicFn<_LockWorkStation_proto> _LockWorkStation(_T("user32.dll"), "LockWorkStation");
+ if (_LockWorkStation.isValid())
+ (*_LockWorkStation)();
+ else
+ ExitWindowsEx(EWX_LOGOFF, 0);
+ }
+ }
+ delete core;
+ core = 0;
+ delete pb;
+ pb = 0;
+ if (device) {
+ if (releaseDevice)
+ ReleaseDC(0, device);
+ else
+ DeleteDC(device);
+ }
+ device = 0;
+ if (server)
+ server->setPixelBuffer(0);
+
+ server = 0;
+ vlog.debug("stopped");
+
+ if (statusLocation) *statusLocation = false;
+}
+
+void SDisplay::restart() {
+ vlog.debug("restarting");
+ // Close down the hooks
+ delete core;
+ core = 0;
+ try {
+ // Re-start the hooks if possible
+ start(server);
+ vlog.debug("restarted");
+ } catch (rdr::Exception& e) {
+ // If start() fails then we MUST disconnect all clients,
+ // to cause the server to stop using the desktop.
+ // Otherwise, the SDesktop is in an inconsistent state
+ // and the server will crash
+ server->closeClients(e.str());
+ }
+}
+
+
+void SDisplay::pointerEvent(const Point& pos, rdr::U8 buttonmask) {
+ if (pb->getRect().contains(pos)) {
+ Point screenPos = pos.translate(screenRect.tl);
+ core->ptr.pointerEvent(screenPos, buttonmask);
+ }
+}
+
+void SDisplay::keyEvent(rdr::U32 key, bool down) {
+ core->kbd.keyEvent(key, down);
+}
+
+void SDisplay::clientCutText(const char* text, int len) {
+ CharArray clip_sz(len+1);
+ memcpy(clip_sz.buf, text, len);
+ clip_sz.buf[len] = 0;
+ core->clipboard.setClipText(clip_sz.buf);
+}
+
+
+void SDisplay::framebufferUpdateRequest()
+{
+ triggerUpdate();
+}
+
+Point SDisplay::getFbSize() {
+ bool startAndStop = !core;
+ // If not started, do minimal initialisation to get desktop size.
+ if (startAndStop) recreatePixelBuffer();
+ Point result = Point(pb->width(), pb->height());
+ // Destroy the initialised structures.
+ if (startAndStop) stop();
+ return result;
+}
+
+
+void
+SDisplay::add_changed(const Region& rgn) {
+ change_tracker.add_changed(rgn);
+ triggerUpdate();
+}
+
+void
+SDisplay::add_copied(const Region& dest, const Point& delta) {
+ change_tracker.add_copied(dest, delta);
+ triggerUpdate();
+}
+
+
+void
+SDisplay::notifyClipboardChanged(const char* text, int len) {
+ vlog.debug("clipboard text changed");
+ if (server)
+ server->serverCutText(text, len);
+}
+
+
+void
+SDisplay::notifyDisplayEvent(WMMonitor::Notifier::DisplayEventType evt) {
+ switch (evt) {
+ case WMMonitor::Notifier::DisplaySizeChanged:
+ vlog.debug("desktop size changed");
+ recreatePixelBuffer();
+ break;
+ case WMMonitor::Notifier::DisplayPixelFormatChanged:
+ vlog.debug("desktop format changed");
+ recreatePixelBuffer();
+ break;
+ case WMMonitor::Notifier::DisplayColourMapChanged:
+ vlog.debug("desktop colourmap changed");
+ pb->updateColourMap();
+ if (server)
+ server->setColourMapEntries();
+ break;
+ default:
+ vlog.error("unknown display event received");
+ }
+}
+
+bool
+SDisplay::processEvent(HANDLE event) {
+ if (event == updateEvent) {
+ vlog.info("processEvent");
+ ResetEvent(updateEvent);
+
+ // - If the SDisplay isn't even started then quit now
+ if (!core) {
+ vlog.error("not start()ed");
+ return true;
+ }
+
+ // - Ensure that the disableLocalInputs flag is respected
+ core->wm_input.blockInputs(SDisplay::disableLocalInputs);
+
+ // - Only process updates if the server is ready
+ if (server && server->clientsReadyForUpdate()) {
+ bool try_update = false;
+
+ // - Check that the SDesktop doesn't need restarting
+ if (core->isRestartRequired()) {
+ restart();
+ return true;
+ }
+
+ // *** window dragging can be improved - more frequent, more cunning about updates
+ while (core->wm_copyrect.processEvent()) {}
+
+ // Ensure the cursor is up to date
+ WMCursor::Info info = core->cursor.getCursorInfo();
+ if (old_cursor != info) {
+ // Update the cursor shape if the visibility has changed
+ bool set_cursor = info.visible != old_cursor.visible;
+ // OR if the cursor is visible and the shape has changed.
+ set_cursor |= info.visible && (old_cursor.cursor != info.cursor);
+
+ // Update the cursor shape
+ if (set_cursor)
+ pb->setCursor(info.visible ? info.cursor : 0, server);
+
+ // Update the cursor position
+ // NB: First translate from Screen coordinates to Desktop
+ Point desktopPos = info.position.translate(screenRect.tl.negate());
+ server->setCursorPos(desktopPos.x, desktopPos.y);
+ try_update = true;
+
+ old_cursor = info;
+ }
+
+ // Flush any changes to the server
+ try_update = flushChangeTracker() || try_update;
+ if (try_update)
+ server->tryUpdate();
+ }
+ } else {
+ CloseHandle(event);
+ return false;
+ }
+ return true;
+}
+
+
+// -=- Protected methods
+
+void
+SDisplay::recreatePixelBuffer() {
+ vlog.debug("attaching to device %s", deviceName);
+
+ // Open the specified display device
+ HDC new_device;
+ if (deviceName.buf) {
+ new_device = ::CreateDC(_T("DISPLAY"), deviceName.buf, NULL, NULL);
+ releaseDevice = false;
+ } else {
+ // If no device is specified, open entire screen.
+ // Doing this with CreateDC creates problems on multi-monitor systems.
+ new_device = ::GetDC(0);
+ releaseDevice = true;
+ }
+ if (!new_device)
+ throw SystemException("cannot open the display", GetLastError());
+
+ // Get the coordinates of the entire virtual display
+ Rect newScreenRect;
+ {
+ WindowDC rootDC(0);
+ RECT r;
+ if (!GetClipBox(rootDC, &r))
+ throw rdr::SystemException("GetClipBox", GetLastError());
+ newScreenRect = Rect(r.left, r.top, r.right, r.bottom);
+ }
+
+ // Create a DeviceFrameBuffer attached to it
+ DeviceFrameBuffer* new_buffer = new DeviceFrameBuffer(new_device);
+
+ // Has anything actually changed about the screen or the buffer?
+ if (!pb ||
+ (!newScreenRect.equals(screenRect)) ||
+ (!new_buffer->getPF().equal(pb->getPF())))
+ {
+ // Yes. Update the buffer state.
+ screenRect = newScreenRect;
+ vlog.debug("creating pixel buffer for device");
+
+ // Flush any existing changes to the server
+ flushChangeTracker();
+
+ // Replace the old PixelBuffer
+ if (pb) delete pb;
+ if (device) DeleteDC(device);
+ pb = new_buffer;
+ device = new_device;
+
+ // Initialise the pixels
+ pb->grabRegion(pb->getRect());
+
+ // Prevent future grabRect operations from throwing exceptions
+ pb->setIgnoreGrabErrors(true);
+
+ // Update the SDisplayCore if required
+ if (core)
+ core->setPixelBuffer(pb);
+
+ // Inform the server of the changes
+ if (server)
+ server->setPixelBuffer(pb);
+
+ } else {
+ delete new_buffer;
+ DeleteDC(new_device);
+ }
+}
+
+bool SDisplay::flushChangeTracker() {
+ if (change_tracker.is_empty())
+ return false;
+ // Translate the update coordinates from Screen coords to Desktop
+ change_tracker.translate(screenRect.tl.negate());
+ // Flush the updates through
+ change_tracker.get_update(*server);
+ change_tracker.clear();
+ return true;
+}
+
+void SDisplay::triggerUpdate() {
+ if (core)
+ SetEvent(updateEvent);
+}
diff --git a/rfb_win32/SDisplay.h b/rfb_win32/SDisplay.h
new file mode 100644
index 00000000..c4c08bf3
--- /dev/null
+++ b/rfb_win32/SDisplay.h
@@ -0,0 +1,149 @@
+/* Copyright (C) 2002-2004 RealVNC Ltd. All Rights Reserved.
+ *
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+ * USA.
+ */
+
+// -=- SDisplay.h
+//
+// The SDisplay class encapsulates a system display.
+
+// *** THIS INTERFACE NEEDS TIDYING TO SEPARATE COORDINATE SYSTEMS BETTER ***
+
+#ifndef __RFB_SDISPLAY_H__
+#define __RFB_SDISPLAY_H__
+
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+
+#include <rfb/SDesktop.h>
+#include <rfb/UpdateTracker.h>
+#include <rfb/Configuration.h>
+#include <rfb/util.h>
+
+#include <winsock2.h>
+#include <rfb_win32/Win32Util.h>
+#include <rfb_win32/SocketManager.h>
+#include <rfb_win32/DeviceFrameBuffer.h>
+#include <rfb_win32/SInput.h>
+#include <rfb_win32/Clipboard.h>
+#include <rfb_win32/MsgWindow.h>
+#include <rfb_win32/WMCursor.h>
+#include <rfb_win32/WMHooks.h>
+#include <rfb_win32/WMNotifier.h>
+#include <rfb_win32/WMWindowCopyRect.h>
+#include <rfb_win32/WMPoller.h>
+
+namespace rfb {
+
+ namespace win32 {
+
+ //
+ // -=- SDisplay
+ //
+
+ class SDisplayCore;
+
+ class SDisplay : public SDesktop,
+ WMMonitor::Notifier,
+ Clipboard::Notifier,
+ UpdateTracker,
+ public SocketManager::EventHandler
+ {
+ public:
+ SDisplay(const TCHAR* device=0);
+ virtual ~SDisplay();
+
+ // -=- SDesktop interface
+
+ virtual void start(VNCServer* vs);
+ virtual void stop();
+ virtual void pointerEvent(const Point& pos, rdr::U8 buttonmask);
+ virtual void keyEvent(rdr::U32 key, bool down);
+ virtual void clientCutText(const char* str, int len);
+ virtual void framebufferUpdateRequest();
+ virtual Point getFbSize();
+
+ // -=- UpdateTracker
+
+ virtual void add_changed(const Region& rgn);
+ virtual void add_copied(const Region& dest, const Point& delta);
+
+ // -=- Clipboard
+
+ virtual void notifyClipboardChanged(const char* text, int len);
+
+ // -=- Display events
+
+ virtual void notifyDisplayEvent(WMMonitor::Notifier::DisplayEventType evt);
+
+ // -=- EventHandler interface
+
+ HANDLE getUpdateEvent() {return updateEvent;}
+ virtual bool processEvent(HANDLE event);
+
+ // -=- Notification of whether or not SDisplay is started
+
+ void setStatusLocation(bool* status) {statusLocation = status;}
+
+ friend class SDisplayCore;
+
+ static BoolParameter use_hooks;
+ static BoolParameter disableLocalInputs;
+ static StringParameter disconnectAction;
+ static BoolParameter removeWallpaper;
+ static BoolParameter removePattern;
+ static BoolParameter disableEffects;
+
+ protected:
+ void restart();
+ void recreatePixelBuffer();
+ bool flushChangeTracker(); // true if flushed, false if empty
+
+ void triggerUpdate();
+
+ VNCServer* server;
+
+ // -=- Display pixel buffer
+ DeviceFrameBuffer* pb;
+ TCharArray deviceName;
+ HDC device;
+ bool releaseDevice;
+
+ // -=- The coordinates of Window's entire virtual Screen
+ Rect screenRect;
+
+ // -=- All changes are collected in Display coords and merged
+ SimpleUpdateTracker change_tracker;
+
+ // -=- Internal SDisplay implementation
+ SDisplayCore* core;
+
+ // -=- Cursor
+ WMCursor::Info old_cursor;
+ Region old_cursor_region;
+ Point cursor_renderpos;
+
+ // -=- Event signalled to trigger an update to be flushed
+ Handle updateEvent;
+
+ // -=- Where to write the active/inactive indicator to
+ bool* statusLocation;
+ };
+
+ }
+}
+
+#endif // __RFB_SDISPLAY_H__
diff --git a/rfb_win32/SInput.cxx b/rfb_win32/SInput.cxx
new file mode 100644
index 00000000..457a8619
--- /dev/null
+++ b/rfb_win32/SInput.cxx
@@ -0,0 +1,459 @@
+/* Copyright (C) 2002-2003 RealVNC Ltd. All Rights Reserved.
+ *
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+ * USA.
+ */
+
+// -=- SInput.cxx
+//
+// A number of routines that accept VNC input event data and perform
+// the appropriate actions under Win32
+
+#define XK_MISCELLANY
+#define XK_LATIN1
+#define XK_CURRENCY
+#include <rfb/keysymdef.h>
+
+// * Force the windows headers to include all the SendInput stuff
+#define _WIN32_WINNT 0x401
+
+#include <rfb_win32/SInput.h>
+#include <rfb_win32/Service.h>
+#include <rfb/LogWriter.h>
+#include <rfb_win32/OSVersion.h>
+#include <rfb_win32/Win32Util.h>
+#include "keymap.h"
+
+using namespace rfb;
+
+static LogWriter vlog("SInput");
+
+
+typedef UINT (WINAPI *_SendInput_proto)(UINT, LPINPUT, int);
+static win32::DynamicFn<_SendInput_proto> _SendInput(_T("user32.dll"), "SendInput");
+
+//
+// -=- Pointer implementation for Win32
+//
+
+static DWORD buttonDownMapping[8] = {
+ MOUSEEVENTF_LEFTDOWN, MOUSEEVENTF_MIDDLEDOWN, MOUSEEVENTF_RIGHTDOWN,
+ MOUSEEVENTF_WHEEL, MOUSEEVENTF_WHEEL, 0, 0, 0
+};
+
+static DWORD buttonUpMapping[8] = {
+ MOUSEEVENTF_LEFTUP, MOUSEEVENTF_MIDDLEUP, MOUSEEVENTF_RIGHTUP,
+ MOUSEEVENTF_WHEEL, MOUSEEVENTF_WHEEL, 0, 0, 0
+};
+
+static DWORD buttonDataMapping[8] = {
+ 0, 0, 0, 120, -120, 0, 0, 0
+};
+
+win32::SPointer::SPointer()
+ : last_buttonmask(0)
+{
+}
+
+void
+win32::SPointer::pointerEvent(const Point& pos, rdr::U8 buttonmask)
+{
+ // - We are specifying absolute coordinates
+ DWORD flags = MOUSEEVENTF_ABSOLUTE;
+
+ // - Has the pointer moved since the last event?
+ if (!last_position.equals(pos))
+ flags |= MOUSEEVENTF_MOVE;
+
+ // - If the system swaps left and right mouse buttons then we must
+ // swap them here to negate the effect, so that we do the actual
+ // action we mean to do
+ if (::GetSystemMetrics(SM_SWAPBUTTON)) {
+ bool leftDown = buttonmask & 1;
+ bool rightDown = buttonmask & 4;
+ buttonmask = (buttonmask & ~(1 | 4));
+ if (leftDown) buttonmask |= 4;
+ if (rightDown) buttonmask |= 1;
+ }
+
+ DWORD data = 0;
+ for (int i = 0; i < 8; i++) {
+ if ((buttonmask & (1<<i)) != (last_buttonmask & (1<<i))) {
+ if (buttonmask & (1<<i)) {
+ flags |= buttonDownMapping[i];
+ if (buttonDataMapping[i]) {
+ if (data) vlog.info("warning - two buttons set mouse_event data field");
+ data = buttonDataMapping[i];
+ }
+ } else {
+ flags |= buttonUpMapping[i];
+ }
+ }
+ }
+
+ last_position = pos;
+ last_buttonmask = buttonmask;
+
+ Rect primaryDisplay(0,0,GetSystemMetrics(SM_CXSCREEN),GetSystemMetrics(SM_CYSCREEN));
+ if (primaryDisplay.contains(pos)) {
+ // mouse_event wants coordinates specified as a proportion of the
+ // primary display's size, scaled to the range 0 to 65535
+ Point scaled;
+ scaled.x = (pos.x * 65535) / (primaryDisplay.width()-1);
+ scaled.y = (pos.y * 65535) / (primaryDisplay.height()-1);
+ ::mouse_event(flags, scaled.x, scaled.y, data, 0);
+ } else {
+ // The event lies outside the primary monitor. Under Win2K, we can just use
+ // SendInput, which allows us to provide coordinates scaled to the virtual desktop.
+ // SendInput is available on all multi-monitor-aware platforms.
+#ifdef SM_CXVIRTUALSCREEN
+ if (osVersion.isPlatformNT) {
+ if (!_SendInput.isValid())
+ throw rdr::Exception("SendInput not available");
+ INPUT evt;
+ evt.type = INPUT_MOUSE;
+ Point vPos(pos.x-GetSystemMetrics(SM_XVIRTUALSCREEN),
+ pos.y-GetSystemMetrics(SM_YVIRTUALSCREEN));
+ evt.mi.dx = (vPos.x * 65535) / (GetSystemMetrics(SM_CXVIRTUALSCREEN)-1);
+ evt.mi.dy = (vPos.y * 65535) / (GetSystemMetrics(SM_CYVIRTUALSCREEN)-1);
+ evt.mi.dwFlags = flags | MOUSEEVENTF_VIRTUALDESK;
+ evt.mi.dwExtraInfo = 0;
+ evt.mi.mouseData = data;
+ evt.mi.time = 0;
+ if ((*_SendInput)(1, &evt, sizeof(evt)) != 1)
+ throw rdr::SystemException("SendInput", GetLastError());
+ } else {
+ // Under Win9x, this is not addressable by either mouse_event or SendInput
+ // *** STUPID KLUDGY HACK ***
+ POINT cursorPos; GetCursorPos(&cursorPos);
+ ULONG oldSpeed, newSpeed = 10;
+ ULONG mouseInfo[3];
+ if (flags & MOUSEEVENTF_MOVE) {
+ flags &= ~MOUSEEVENTF_ABSOLUTE;
+ SystemParametersInfo(SPI_GETMOUSE, 0, &mouseInfo, 0);
+ SystemParametersInfo(SPI_GETMOUSESPEED, 0, &oldSpeed, 0);
+ vlog.debug("SPI_GETMOUSE %d, %d, %d, speed %d", mouseInfo[0], mouseInfo[1], mouseInfo[2], oldSpeed);
+ ULONG idealMouseInfo[] = {10, 0, 0};
+ SystemParametersInfo(SPI_SETMOUSESPEED, 0, &newSpeed, 0);
+ SystemParametersInfo(SPI_SETMOUSE, 0, &idealMouseInfo, 0);
+ }
+ ::mouse_event(flags, pos.x-cursorPos.x, pos.y-cursorPos.y, data, 0);
+ if (flags & MOUSEEVENTF_MOVE) {
+ SystemParametersInfo(SPI_SETMOUSE, 0, &mouseInfo, 0);
+ SystemParametersInfo(SPI_SETMOUSESPEED, 0, &oldSpeed, 0);
+ }
+ }
+#endif
+ }
+}
+
+//
+// -=- Keyboard implementation
+//
+
+BoolParameter rfb::win32::SKeyboard::deadKeyAware("DeadKeyAware",
+ "Whether to assume the viewer has already interpreted dead key sequences "
+ "into latin-1 characters", true);
+
+static bool oneShift;
+
+// The keysymToAscii table transforms a couple of awkward keysyms into their
+// ASCII equivalents.
+struct keysymToAscii_t {
+ rdr::U32 keysym;
+ rdr::U8 ascii;
+};
+
+keysymToAscii_t keysymToAscii[] = {
+ { XK_KP_Space, ' ' },
+ { XK_KP_Equal, '=' },
+};
+
+rdr::U8 latin1DeadChars[] = {
+ XK_grave, XK_acute, XK_asciicircum, XK_diaeresis, XK_degree, XK_cedilla,
+ XK_asciitilde
+};
+
+struct latin1ToDeadChars_t {
+ rdr::U8 latin1Char;
+ rdr::U8 deadChar;
+ rdr::U8 baseChar;
+};
+
+latin1ToDeadChars_t latin1ToDeadChars[] = {
+
+ { XK_Agrave, XK_grave, XK_A },
+ { XK_Egrave, XK_grave, XK_E },
+ { XK_Igrave, XK_grave, XK_I },
+ { XK_Ograve, XK_grave, XK_O },
+ { XK_Ugrave, XK_grave, XK_U },
+ { XK_agrave, XK_grave, XK_a },
+ { XK_egrave, XK_grave, XK_e },
+ { XK_igrave, XK_grave, XK_i },
+ { XK_ograve, XK_grave, XK_o},
+ { XK_ugrave, XK_grave, XK_u },
+
+ { XK_Aacute, XK_acute, XK_A },
+ { XK_Eacute, XK_acute, XK_E },
+ { XK_Iacute, XK_acute, XK_I },
+ { XK_Oacute, XK_acute, XK_O },
+ { XK_Uacute, XK_acute, XK_U },
+ { XK_Yacute, XK_acute, XK_Y },
+ { XK_aacute, XK_acute, XK_a },
+ { XK_eacute, XK_acute, XK_e },
+ { XK_iacute, XK_acute, XK_i },
+ { XK_oacute, XK_acute, XK_o},
+ { XK_uacute, XK_acute, XK_u },
+ { XK_yacute, XK_acute, XK_y },
+
+ { XK_Acircumflex, XK_asciicircum, XK_A },
+ { XK_Ecircumflex, XK_asciicircum, XK_E },
+ { XK_Icircumflex, XK_asciicircum, XK_I },
+ { XK_Ocircumflex, XK_asciicircum, XK_O },
+ { XK_Ucircumflex, XK_asciicircum, XK_U },
+ { XK_acircumflex, XK_asciicircum, XK_a },
+ { XK_ecircumflex, XK_asciicircum, XK_e },
+ { XK_icircumflex, XK_asciicircum, XK_i },
+ { XK_ocircumflex, XK_asciicircum, XK_o},
+ { XK_ucircumflex, XK_asciicircum, XK_u },
+
+ { XK_Adiaeresis, XK_diaeresis, XK_A },
+ { XK_Ediaeresis, XK_diaeresis, XK_E },
+ { XK_Idiaeresis, XK_diaeresis, XK_I },
+ { XK_Odiaeresis, XK_diaeresis, XK_O },
+ { XK_Udiaeresis, XK_diaeresis, XK_U },
+ { XK_adiaeresis, XK_diaeresis, XK_a },
+ { XK_ediaeresis, XK_diaeresis, XK_e },
+ { XK_idiaeresis, XK_diaeresis, XK_i },
+ { XK_odiaeresis, XK_diaeresis, XK_o},
+ { XK_udiaeresis, XK_diaeresis, XK_u },
+ { XK_ydiaeresis, XK_diaeresis, XK_y },
+
+ { XK_Aring, XK_degree, XK_A },
+ { XK_aring, XK_degree, XK_a },
+
+ { XK_Ccedilla, XK_cedilla, XK_C },
+ { XK_ccedilla, XK_cedilla, XK_c },
+
+ { XK_Atilde, XK_asciitilde, XK_A },
+ { XK_Ntilde, XK_asciitilde, XK_N },
+ { XK_Otilde, XK_asciitilde, XK_O },
+ { XK_atilde, XK_asciitilde, XK_a },
+ { XK_ntilde, XK_asciitilde, XK_n },
+ { XK_otilde, XK_asciitilde, XK_o },
+};
+
+// doKeyboardEvent wraps the system keybd_event function and attempts to find
+// the appropriate scancode corresponding to the supplied virtual keycode.
+
+inline void doKeyboardEvent(BYTE vkCode, DWORD flags) {
+ vlog.debug("vkCode 0x%x flags 0x%x", vkCode, flags);
+ keybd_event(vkCode, MapVirtualKey(vkCode, 0), flags, 0);
+}
+
+// KeyStateModifier is a class which helps simplify generating a "fake" press
+// or release of shift, ctrl, alt, etc. An instance of the class is created
+// for every key which may need to be pressed or released. Then either press()
+// or release() may be called to make sure that the corresponding key is in the
+// right state. The destructor of the class automatically reverts to the
+// previous state.
+
+class KeyStateModifier {
+public:
+ KeyStateModifier(int vkCode_, int flags_=0)
+ : vkCode(vkCode_), flags(flags_), pressed(false), released(false)
+ {}
+ void press() {
+ if (!(GetAsyncKeyState(vkCode) & 0x8000)) {
+ doKeyboardEvent(vkCode, flags);
+ pressed = true;
+ }
+ }
+ void release() {
+ if (GetAsyncKeyState(vkCode) & 0x8000) {
+ doKeyboardEvent(vkCode, flags | KEYEVENTF_KEYUP);
+ released = true;
+ }
+ }
+ ~KeyStateModifier() {
+ if (pressed) {
+ doKeyboardEvent(vkCode, flags | KEYEVENTF_KEYUP);
+ } else if (released) {
+ doKeyboardEvent(vkCode, flags);
+ }
+ }
+ int vkCode;
+ int flags;
+ bool pressed;
+ bool released;
+};
+
+
+// doKeyEventWithModifiers() generates a key event having first "pressed" or
+// "released" the shift, ctrl or alt modifiers if necessary.
+
+void doKeyEventWithModifiers(BYTE vkCode, BYTE modifierState, bool down)
+{
+ KeyStateModifier ctrl(VK_CONTROL);
+ KeyStateModifier alt(VK_MENU);
+ KeyStateModifier shift(VK_SHIFT);
+
+ if (down) {
+ if (modifierState & 2) ctrl.press();
+ if (modifierState & 4) alt.press();
+ if (modifierState & 1) {
+ shift.press();
+ } else {
+ shift.release();
+ }
+ }
+ doKeyboardEvent(vkCode, down ? 0 : KEYEVENTF_KEYUP);
+}
+
+
+win32::SKeyboard::SKeyboard()
+{
+ oneShift = rfb::win32::osVersion.isPlatformWindows;
+ for (int i = 0; i < sizeof(keymap) / sizeof(keymap_t); i++) {
+ vkMap[keymap[i].keysym] = keymap[i].vk;
+ extendedMap[keymap[i].keysym] = keymap[i].extended;
+ }
+
+ // Find dead characters for the current keyboard layout
+ // XXX how could we handle the keyboard layout changing?
+ BYTE keystate[256];
+ memset(keystate, 0, 256);
+ for (int j = 0; j < sizeof(latin1DeadChars); j++) {
+ SHORT s = VkKeyScan(latin1DeadChars[j]);
+ if (s != -1) {
+ BYTE vkCode = LOBYTE(s);
+ BYTE modifierState = HIBYTE(s);
+ keystate[VK_SHIFT] = (modifierState & 1) ? 0x80 : 0;
+ keystate[VK_CONTROL] = (modifierState & 2) ? 0x80 : 0;
+ keystate[VK_MENU] = (modifierState & 4) ? 0x80 : 0;
+ rdr::U8 chars[2];
+ int nchars = ToAscii(vkCode, 0, keystate, (WORD*)&chars, 0);
+ if (nchars < 0) {
+ vlog.debug("Found dead key 0x%x '%c'",
+ latin1DeadChars[j], latin1DeadChars[j]);
+ deadChars.push_back(latin1DeadChars[j]);
+ ToAscii(vkCode, 0, keystate, (WORD*)&chars, 0);
+ }
+ }
+ }
+}
+
+
+void win32::SKeyboard::keyEvent(rdr::U32 keysym, bool down)
+{
+ for (int i = 0; i < sizeof(keysymToAscii) / sizeof(keysymToAscii_t); i++) {
+ if (keysymToAscii[i].keysym == keysym) {
+ keysym = keysymToAscii[i].ascii;
+ break;
+ }
+ }
+
+ if ((keysym >= 32 && keysym <= 126) ||
+ (keysym >= 160 && keysym <= 255))
+ {
+ // ordinary Latin-1 character
+
+ if (deadKeyAware) {
+ // Detect dead chars and generate the dead char followed by space so
+ // that we'll end up with the original char.
+ for (int i = 0; i < deadChars.size(); i++) {
+ if (keysym == deadChars[i]) {
+ SHORT dc = VkKeyScan(keysym);
+ if (dc != -1) {
+ if (down) {
+ vlog.info("latin-1 dead key: 0x%x vkCode 0x%x mod 0x%x "
+ "followed by space", keysym, LOBYTE(dc), HIBYTE(dc));
+ doKeyEventWithModifiers(LOBYTE(dc), HIBYTE(dc), true);
+ doKeyEventWithModifiers(LOBYTE(dc), HIBYTE(dc), false);
+ doKeyEventWithModifiers(VK_SPACE, 0, true);
+ doKeyEventWithModifiers(VK_SPACE, 0, false);
+ }
+ return;
+ }
+ }
+ }
+ }
+
+ SHORT s = VkKeyScan(keysym);
+ if (s == -1) {
+ if (down) {
+ // not a single keypress - try synthesizing dead chars.
+ for (int j = 0;
+ j < sizeof(latin1ToDeadChars) / sizeof(latin1ToDeadChars_t);
+ j++) {
+ if (keysym == latin1ToDeadChars[j].latin1Char) {
+ for (int i = 0; i < deadChars.size(); i++) {
+ if (deadChars[i] == latin1ToDeadChars[j].deadChar) {
+ SHORT dc = VkKeyScan(latin1ToDeadChars[j].deadChar);
+ SHORT bc = VkKeyScan(latin1ToDeadChars[j].baseChar);
+ if (dc != -1 && bc != -1) {
+ vlog.info("latin-1 key: 0x%x dead key vkCode 0x%x mod 0x%x "
+ "followed by vkCode 0x%x mod 0x%x",
+ keysym, LOBYTE(dc), HIBYTE(dc),
+ LOBYTE(bc), HIBYTE(bc));
+ doKeyEventWithModifiers(LOBYTE(dc), HIBYTE(dc), true);
+ doKeyEventWithModifiers(LOBYTE(dc), HIBYTE(dc), false);
+ doKeyEventWithModifiers(LOBYTE(bc), HIBYTE(bc), true);
+ doKeyEventWithModifiers(LOBYTE(bc), HIBYTE(bc), false);
+ return;
+ }
+ break;
+ }
+ }
+ break;
+ }
+ }
+ vlog.info("ignoring unrecognised Latin-1 keysym 0x%x",keysym);
+ }
+ return;
+ }
+
+ BYTE vkCode = LOBYTE(s);
+ BYTE modifierState = HIBYTE(s);
+ vlog.debug("latin-1 key: 0x%x vkCode 0x%x mod 0x%x down %d",
+ keysym, vkCode, modifierState, down);
+ doKeyEventWithModifiers(vkCode, modifierState, down);
+
+ } else {
+
+ // see if it's a recognised keyboard key, otherwise ignore it
+
+ if (vkMap.find(keysym) == vkMap.end()) {
+ vlog.info("ignoring unknown keysym 0x%x",keysym);
+ return;
+ }
+ BYTE vkCode = vkMap[keysym];
+ DWORD flags = 0;
+ if (extendedMap[keysym]) flags |= KEYEVENTF_EXTENDEDKEY;
+ if (!down) flags |= KEYEVENTF_KEYUP;
+
+ vlog.debug("keyboard key: keysym 0x%x vkCode 0x%x ext %d down %d",
+ keysym, vkCode, extendedMap[keysym], down);
+ if (down && (vkCode == VK_DELETE) &&
+ ((GetAsyncKeyState(VK_CONTROL) & 0x8000) != 0) &&
+ ((GetAsyncKeyState(VK_MENU) & 0x8000) != 0))
+ {
+ rfb::win32::emulateCtrlAltDel();
+ return;
+ }
+
+ doKeyboardEvent(vkCode, flags);
+ }
+}
diff --git a/rfb_win32/SInput.h b/rfb_win32/SInput.h
new file mode 100644
index 00000000..dcd779ef
--- /dev/null
+++ b/rfb_win32/SInput.h
@@ -0,0 +1,70 @@
+/* Copyright (C) 2002-2003 RealVNC Ltd. All Rights Reserved.
+ *
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+ * USA.
+ */
+
+// -=- Input.h
+//
+// A number of routines that accept VNC-style input event data and perform
+// the appropriate actions under Win32
+
+#ifndef __RFB_WIN32_INPUT_H__
+#define __RFB_WIN32_INPUT_H__
+
+#include <rfb/Rect.h>
+#include <rfb/Configuration.h>
+#include <rdr/types.h>
+#include <map>
+#include <vector>
+
+namespace rfb {
+
+ class CMsgWriter;
+
+ namespace win32 {
+
+ // -=- Pointer event handling
+
+ class SPointer {
+ public:
+ SPointer();
+ // - Create a pointer event at a the given coordinates, with the
+ // specified button state. The event must be specified using
+ // Screen coordinates.
+ void pointerEvent(const Point& pos, rdr::U8 buttonmask);
+ protected:
+ Point last_position;
+ rdr::U8 last_buttonmask;
+ };
+
+ // -=- Keyboard event handling
+
+ class SKeyboard {
+ public:
+ SKeyboard();
+ void keyEvent(rdr::U32 key, bool down);
+ static BoolParameter deadKeyAware;
+ private:
+ std::map<rdr::U32,rdr::U8> vkMap;
+ std::map<rdr::U32,bool> extendedMap;
+ std::vector<rdr::U8> deadChars;
+ };
+
+ }; // win32
+
+}; // rfb
+
+#endif // __RFB_WIN32_INPUT_H__
diff --git a/rfb_win32/Security.h b/rfb_win32/Security.h
new file mode 100644
index 00000000..d92e314f
--- /dev/null
+++ b/rfb_win32/Security.h
@@ -0,0 +1,198 @@
+/* Copyright (C) 2002-2004 RealVNC Ltd. All Rights Reserved.
+ *
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+ * USA.
+ */
+
+// Security.h
+
+// Wrapper classes for a few Windows NT security structures/functions
+// that are used by VNC
+
+#ifndef __RFB_WIN32_SECURITY_H__
+#define __RFB_WIN32_SECURITY_H__
+
+#include <rdr/types.h>
+#include <rdr/Exception.h>
+#include <rfb_win32/Win32Util.h>
+#include <rfb_win32/TCharArray.h>
+
+#include <lmcons.h>
+#include <Accctrl.h>
+#include <aclapi.h>
+
+#include <list>
+
+namespace rfb {
+
+ namespace win32 {
+
+ struct Trustee : public TRUSTEE {
+ Trustee(const TCHAR* name,
+ TRUSTEE_FORM form=TRUSTEE_IS_NAME,
+ TRUSTEE_TYPE type=TRUSTEE_IS_UNKNOWN)
+ {
+ pMultipleTrustee = 0;
+ MultipleTrusteeOperation = NO_MULTIPLE_TRUSTEE;
+ TrusteeForm = form;
+ TrusteeType = type;
+ ptstrName = (TCHAR*)name;
+ }
+ };
+
+ struct ExplicitAccess : public EXPLICIT_ACCESS {
+ ExplicitAccess(const TCHAR* name,
+ TRUSTEE_FORM type,
+ DWORD perms,
+ ACCESS_MODE mode,
+ DWORD inherit=0)
+ {
+ Trustee = rfb::win32::Trustee(name, type);
+ grfAccessPermissions = perms;
+ grfAccessMode = mode;
+ grfInheritance = inherit;
+ }
+ };
+
+ // Helper class for building access control lists
+ struct AccessEntries {
+ AccessEntries() : entries(0), entry_count(0) {}
+ ~AccessEntries() {delete [] entries;}
+ void allocMinEntries(int count) {
+ if (count > entry_count) {
+ EXPLICIT_ACCESS* new_entries = new EXPLICIT_ACCESS[entry_count+1];
+ if (entries) {
+ memcpy(new_entries, entries, sizeof(EXPLICIT_ACCESS) * entry_count);
+ delete entries;
+ }
+ entries = new_entries;
+ }
+ }
+ void addEntry(const TCHAR* trusteeName,
+ DWORD permissions,
+ ACCESS_MODE mode)
+ {
+ allocMinEntries(entry_count+1);
+ ZeroMemory(&entries[entry_count], sizeof(EXPLICIT_ACCESS));
+ entries[entry_count] = ExplicitAccess(trusteeName, TRUSTEE_IS_NAME, permissions, mode);
+ entry_count++;
+ }
+ void addEntry(const PSID sid, DWORD permissions, ACCESS_MODE mode) {
+ allocMinEntries(entry_count+1);
+ ZeroMemory(&entries[entry_count], sizeof(EXPLICIT_ACCESS));
+ entries[entry_count] = ExplicitAccess((TCHAR*)sid, TRUSTEE_IS_SID, permissions, mode);
+ entry_count++;
+ }
+
+ EXPLICIT_ACCESS* entries;
+ int entry_count;
+ };
+
+ // Helper class for handling SIDs
+ struct Sid {
+ Sid() : sid(0) {}
+ Sid(PSID sid_) : sid(sid_) {}
+ ~Sid() {
+ if (sid) FreeSid(sid);
+ }
+ operator PSID() const {return sid;}
+ PSID operator=(const PSID sid_) {
+ if (sid) FreeSid(sid);
+ sid = sid_;
+ }
+
+ static PSID Administrators() {
+ PSID sid = 0;
+ SID_IDENTIFIER_AUTHORITY ntAuth = SECURITY_NT_AUTHORITY;
+ if (!AllocateAndInitializeSid(&ntAuth, 2,
+ SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_ADMINS,
+ 0, 0, 0, 0, 0, 0,
+ &sid))
+ throw rdr::SystemException("Sid::Administrators", GetLastError());
+ return sid;
+ }
+ static PSID SYSTEM() {
+ PSID sid = 0;
+ SID_IDENTIFIER_AUTHORITY ntAuth = SECURITY_NT_AUTHORITY;
+ if (!AllocateAndInitializeSid(&ntAuth, 1,
+ SECURITY_LOCAL_SYSTEM_RID, 0, 0, 0, 0, 0, 0, 0,
+ &sid))
+ throw rdr::SystemException("Sid::SYSTEM", GetLastError());
+ return sid;
+ }
+
+ protected:
+ PSID sid;
+ };
+
+ // Helper class for handling & freeing ACLs
+ struct AccessControlList : public LocalMem {
+ AccessControlList(int size) : LocalMem(size) {}
+ AccessControlList(PACL acl_=0) : LocalMem(acl_) {}
+ operator PACL() {return (PACL)ptr;}
+ };
+
+ // Create a new ACL based on supplied entries and, if supplied, existing ACL
+ static PACL CreateACL(const AccessEntries& ae, PACL existing_acl=0) {
+ typedef DWORD (WINAPI *_SetEntriesInAcl_proto) (ULONG, PEXPLICIT_ACCESS, PACL, PACL*);
+#ifdef UNICODE
+ const char* fnName = "SetEntriesInAclW";
+#else
+ const char* fnName = "SetEntriesInAclA";
+#endif
+ DynamicFn<_SetEntriesInAcl_proto> _SetEntriesInAcl(_T("advapi32.dll"), fnName);
+ if (!_SetEntriesInAcl.isValid())
+ throw rdr::SystemException("CreateACL failed; no SetEntriesInAcl", ERROR_CALL_NOT_IMPLEMENTED);
+ PACL new_dacl;
+ DWORD result;
+ if ((result = (*_SetEntriesInAcl)(ae.entry_count, ae.entries, existing_acl, &new_dacl)) != ERROR_SUCCESS)
+ throw rdr::SystemException("SetEntriesInAcl", result);
+ return new_dacl;
+ }
+
+ // Helper class for memory-management of self-relative SecurityDescriptors
+ struct SecurityDescriptorPtr : LocalMem {
+ SecurityDescriptorPtr(int size) : LocalMem(size) {}
+ SecurityDescriptorPtr(PSECURITY_DESCRIPTOR sd_=0) : LocalMem(sd_) {}
+ PSECURITY_DESCRIPTOR takeSD() {return takePtr();}
+ };
+
+ // Create a new self-relative Security Descriptor, owned by SYSTEM/Administrators,
+ // with the supplied DACL and no SACL. The returned value can be assigned
+ // to a SecurityDescriptorPtr to be managed.
+ static PSECURITY_DESCRIPTOR CreateSdWithDacl(const PACL dacl) {
+ SECURITY_DESCRIPTOR absSD;
+ if (!InitializeSecurityDescriptor(&absSD, SECURITY_DESCRIPTOR_REVISION))
+ throw rdr::SystemException("InitializeSecurityDescriptor", GetLastError());
+ Sid owner(Sid::SYSTEM());
+ if (!SetSecurityDescriptorOwner(&absSD, owner, FALSE))
+ throw rdr::SystemException("SetSecurityDescriptorOwner", GetLastError());
+ Sid group(Sid::Administrators());
+ if (!SetSecurityDescriptorGroup(&absSD, group, FALSE))
+ throw rdr::SystemException("SetSecurityDescriptorGroupp", GetLastError());
+ if (!SetSecurityDescriptorDacl(&absSD, TRUE, dacl, FALSE))
+ throw rdr::SystemException("SetSecurityDescriptorDacl", GetLastError());
+ DWORD sdSize = GetSecurityDescriptorLength(&absSD);
+ SecurityDescriptorPtr sd(sdSize);
+ if (!MakeSelfRelativeSD(&absSD, sd, &sdSize))
+ throw rdr::SystemException("MakeSelfRelativeSD", GetLastError());
+ return sd.takeSD();
+ }
+
+ }
+
+}
+
+#endif
diff --git a/rfb_win32/Service.cxx b/rfb_win32/Service.cxx
new file mode 100644
index 00000000..b00c2900
--- /dev/null
+++ b/rfb_win32/Service.cxx
@@ -0,0 +1,638 @@
+/* Copyright (C) 2002-2003 RealVNC Ltd. All Rights Reserved.
+ *
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+ * USA.
+ */
+
+// -=- Service.cxx
+
+#include <rfb_win32/Service.h>
+#include <rfb_win32/MsgWindow.h>
+#include <rfb_win32/Registry.h>
+#include <rfb_win32/Win32Util.h>
+#include <rfb_win32/OSVersion.h>
+#include <rfb/Threading.h>
+#include <rfb/LogWriter.h>
+#include <rfb/util.h>
+#include <rdr/Exception.h>
+
+#include <logmessages/messages.h>
+
+using namespace rdr;
+using namespace rfb;
+using namespace win32;
+
+static LogWriter vlog("Service");
+
+
+// - Internal service implementation functions
+
+Service* service = 0;
+
+VOID WINAPI serviceHandler(DWORD control) {
+ vlog.debug("service control %u", control);
+ switch (control) {
+ case SERVICE_CONTROL_INTERROGATE:
+ service->setStatus();
+ break;
+ case SERVICE_CONTROL_PARAMCHANGE:
+ service->readParams();
+ break;
+ case SERVICE_CONTROL_SHUTDOWN:
+ service->osShuttingDown();
+ break;
+ case SERVICE_CONTROL_STOP:
+ service->setStatus(SERVICE_STOP_PENDING);
+ service->stop();
+ break;
+ }
+}
+
+
+// -=- Message window derived class used under Win9x to implement stopService
+
+#define WM_SMSG_SERVICE_STOP WM_USER
+
+class ServiceMsgWindow : public MsgWindow {
+public:
+ ServiceMsgWindow(const TCHAR* name) : MsgWindow(name) {}
+ LRESULT processMessage(UINT msg, WPARAM wParam, LPARAM lParam) {
+ switch (msg) {
+ case WM_SMSG_SERVICE_STOP:
+ service->stop();
+ return TRUE;
+ }
+ return MsgWindow::processMessage(msg, wParam, lParam);
+ }
+
+ static const TCHAR* baseName;
+};
+
+const TCHAR* ServiceMsgWindow::baseName = _T("ServiceWindow:");
+
+
+// -=- Service main procedure, used under WinNT/2K/XP by the SCM
+
+VOID WINAPI serviceProc(DWORD dwArgc, LPTSTR* lpszArgv) {
+ vlog.debug("entering %s serviceProc", service->getName());
+ service->status_handle = RegisterServiceCtrlHandler(service->getName(), serviceHandler);
+ if (!service->status_handle) {
+ vlog.error("unable to register service control handler");
+ return;
+ }
+ service->setStatus(SERVICE_START_PENDING);
+ vlog.debug("entering %s serviceMain", service->getName());
+ service->status.dwWin32ExitCode = service->serviceMain(dwArgc, lpszArgv);
+ vlog.debug("leaving %s serviceMain", service->getName());
+ service->setStatus(SERVICE_STOPPED);
+}
+
+
+// -=- Service
+
+Service::Service(const TCHAR* name_) : name(name_) {
+ vlog.debug("Service");
+ status_handle = 0;
+ status.dwControlsAccepted = SERVICE_CONTROL_INTERROGATE | SERVICE_ACCEPT_SHUTDOWN | SERVICE_ACCEPT_STOP;
+ status.dwServiceType = SERVICE_WIN32_OWN_PROCESS | SERVICE_INTERACTIVE_PROCESS;
+ status.dwWin32ExitCode = NO_ERROR;
+ status.dwServiceSpecificExitCode = 0;
+ status.dwCheckPoint = 0;
+ status.dwWaitHint = 30000;
+ status.dwCurrentState = SERVICE_STOPPED;
+}
+
+void
+Service::start() {
+ if (osVersion.isPlatformNT) {
+ SERVICE_TABLE_ENTRY entry[2];
+ entry[0].lpServiceName = (TCHAR*)name;
+ entry[0].lpServiceProc = serviceProc;
+ entry[1].lpServiceName = NULL;
+ entry[1].lpServiceProc = NULL;
+ vlog.debug("entering dispatcher");
+ if (!SetProcessShutdownParameters(0x100, 0))
+ vlog.error("unable to set shutdown parameters: %d", GetLastError());
+ service = this;
+ if (!StartServiceCtrlDispatcher(entry))
+ throw SystemException("unable to start service", GetLastError());
+ } else {
+
+ // - Create the service window, so the service can be stopped
+ TCharArray wndName(_tcslen(getName()) + _tcslen(ServiceMsgWindow::baseName) + 1);
+ _tcscpy(wndName.buf, ServiceMsgWindow::baseName);
+ _tcscat(wndName.buf, getName());
+ ServiceMsgWindow service_window(wndName.buf);
+
+ // - Locate the RegisterServiceProcess function
+ typedef DWORD (WINAPI * _RegisterServiceProcess_proto)(DWORD, DWORD);
+ DynamicFn<_RegisterServiceProcess_proto> _RegisterServiceProcess(_T("kernel32.dll"), "RegisterServiceProcess");
+ if (!_RegisterServiceProcess.isValid())
+ throw Exception("unable to find RegisterServiceProcess");
+
+ // - Run the service
+ (*_RegisterServiceProcess)(NULL, 1);
+ service = this;
+ serviceMain(0, 0);
+ (*_RegisterServiceProcess)(NULL, 0);
+ }
+}
+
+void
+Service::setStatus() {
+ setStatus(status.dwCurrentState);
+}
+
+void
+Service::setStatus(DWORD state) {
+ if (!osVersion.isPlatformNT)
+ return;
+ if (status_handle == 0) {
+ vlog.debug("warning - cannot setStatus");
+ return;
+ }
+ status.dwCurrentState = state;
+ status.dwCheckPoint++;
+ if (!SetServiceStatus(status_handle, &status)) {
+ status.dwWin32ExitCode = GetLastError();
+ vlog.error("unable to set service status:%u", status.dwWin32ExitCode);
+ stop();
+ }
+ vlog.debug("set status to %u(%u)", state, status.dwCheckPoint);
+}
+
+Service::~Service() {
+ vlog.debug("~Service");
+ service = 0;
+}
+
+
+// Find out whether this process is running as the WinVNC service
+bool thisIsService() {
+ return service && (service->status.dwCurrentState != SERVICE_STOPPED);
+}
+
+
+// -=- Desktop handling code
+
+// Switch the current thread to the specified desktop
+static bool
+switchToDesktop(HDESK desktop) {
+ HDESK old_desktop = GetThreadDesktop(GetCurrentThreadId());
+ if (!SetThreadDesktop(desktop)) {
+ vlog.debug("switchToDesktop failed:%u", GetLastError());
+ return false;
+ }
+ if (!CloseDesktop(old_desktop))
+ vlog.debug("unable to close old desktop:%u", GetLastError());
+ return true;
+}
+
+// Determine whether the thread's current desktop is the input one
+static bool
+inputDesktopSelected() {
+ HDESK current = GetThreadDesktop(GetCurrentThreadId());
+ HDESK input = OpenInputDesktop(0, FALSE,
+ DESKTOP_CREATEMENU | DESKTOP_CREATEWINDOW |
+ DESKTOP_ENUMERATE | DESKTOP_HOOKCONTROL |
+ DESKTOP_WRITEOBJECTS | DESKTOP_READOBJECTS |
+ DESKTOP_SWITCHDESKTOP | GENERIC_WRITE);
+ if (!input) {
+ vlog.debug("unable to OpenInputDesktop(1):%u", GetLastError());
+ return false;
+ }
+
+ DWORD size;
+ char currentname[256];
+ char inputname[256];
+
+ if (!GetUserObjectInformation(current, UOI_NAME, currentname, 256, &size)) {
+ vlog.debug("unable to GetUserObjectInformation(1):%u", GetLastError());
+ CloseDesktop(input);
+ return false;
+ }
+ if (!GetUserObjectInformation(input, UOI_NAME, inputname, 256, &size)) {
+ vlog.debug("unable to GetUserObjectInformation(2):%u", GetLastError());
+ CloseDesktop(input);
+ return false;
+ }
+ if (!CloseDesktop(input))
+ vlog.debug("unable to close input desktop:%u", GetLastError());
+
+ // *** vlog.debug("current=%s, input=%s", currentname, inputname);
+ bool result = strcmp(currentname, inputname) == 0;
+ return result;
+}
+
+// Switch the current thread into the input desktop
+static bool
+selectInputDesktop() {
+ // - Open the input desktop
+ HDESK desktop = OpenInputDesktop(0, FALSE,
+ DESKTOP_CREATEMENU | DESKTOP_CREATEWINDOW |
+ DESKTOP_ENUMERATE | DESKTOP_HOOKCONTROL |
+ DESKTOP_WRITEOBJECTS | DESKTOP_READOBJECTS |
+ DESKTOP_SWITCHDESKTOP | GENERIC_WRITE);
+ if (!desktop) {
+ vlog.debug("unable to OpenInputDesktop(2):%u", GetLastError());
+ return false;
+ }
+
+ // - Switch into it
+ if (!switchToDesktop(desktop)) {
+ CloseDesktop(desktop);
+ return false;
+ }
+
+ // ***
+ DWORD size = 256;
+ char currentname[256];
+ if (GetUserObjectInformation(desktop, UOI_NAME, currentname, 256, &size)) {
+ vlog.debug("switched to %s", currentname);
+ }
+ // ***
+
+ vlog.debug("switched to input desktop");
+
+ return true;
+}
+
+
+// -=- Access points to desktop-switching routines
+
+bool
+rfb::win32::desktopChangeRequired() {
+ if (!osVersion.isPlatformNT)
+ return false;
+
+ return !inputDesktopSelected();
+}
+
+bool
+rfb::win32::changeDesktop() {
+ if (!osVersion.isPlatformNT)
+ return true;
+ if (osVersion.cannotSwitchDesktop)
+ return false;
+
+ return selectInputDesktop();
+}
+
+
+// -=- Ctrl-Alt-Del emulation
+
+class CADThread : public Thread {
+public:
+ CADThread() : Thread("CtrlAltDel Emulator"), result(false) {}
+ virtual void run() {
+ HDESK old_desktop = GetThreadDesktop(GetCurrentThreadId());
+
+ if (switchToDesktop(OpenDesktop(_T("Winlogon"), 0, FALSE, DESKTOP_CREATEMENU | DESKTOP_CREATEWINDOW |
+ DESKTOP_ENUMERATE | DESKTOP_HOOKCONTROL |
+ DESKTOP_WRITEOBJECTS | DESKTOP_READOBJECTS |
+ DESKTOP_SWITCHDESKTOP | GENERIC_WRITE))) {
+ PostMessage(HWND_BROADCAST, WM_HOTKEY, 0, MAKELONG(MOD_ALT | MOD_CONTROL, VK_DELETE));
+ switchToDesktop(old_desktop);
+ result = true;
+ }
+ }
+ bool result;
+};
+
+bool
+rfb::win32::emulateCtrlAltDel() {
+ if (!osVersion.isPlatformNT)
+ return false;
+
+ CADThread* cad_thread = new CADThread();
+ vlog.debug("emulate Ctrl-Alt-Del");
+ if (cad_thread) {
+ cad_thread->start();
+ cad_thread->join();
+ bool result = cad_thread->result;
+ delete cad_thread;
+ return result;
+ }
+ return false;
+}
+
+
+// -=- Application Event Log target Logger class
+
+class Logger_EventLog : public Logger {
+public:
+ Logger_EventLog(const TCHAR* srcname) : Logger("EventLog") {
+ eventlog = RegisterEventSource(NULL, srcname);
+ if (!eventlog)
+ printf("Unable to open event log:%ld\n", GetLastError());
+ }
+ ~Logger_EventLog() {
+ if (eventlog)
+ DeregisterEventSource(eventlog);
+ }
+
+ virtual void write(int level, const char *logname, const char *message) {
+ if (!eventlog) return;
+ TStr log(logname), msg(message);
+ const TCHAR* strings[] = {log, msg};
+ WORD type = EVENTLOG_INFORMATION_TYPE;
+ if (level == 0) type = EVENTLOG_ERROR_TYPE;
+ if (!ReportEvent(eventlog, type, 0, VNC4LogMessage, NULL, 2, 0, strings, NULL)) {
+ // *** It's not at all clear what is the correct behaviour if this fails...
+ printf("ReportEvent failed:%ld\n", GetLastError());
+ }
+ }
+
+protected:
+ HANDLE eventlog;
+};
+
+static Logger_EventLog* logger = 0;
+
+bool rfb::win32::initEventLogLogger(const TCHAR* srcname) {
+ if (logger)
+ return false;
+ if (osVersion.isPlatformNT) {
+ logger = new Logger_EventLog(srcname);
+ logger->registerLogger();
+ return true;
+ } else {
+ return false;
+ }
+}
+
+
+// -=- Registering and unregistering the service
+
+bool rfb::win32::registerService(const TCHAR* name, const TCHAR* desc,
+ int argc, const char* argv[]) {
+
+ // - Initialise the default service parameters
+ const TCHAR* defaultcmdline;
+ if (osVersion.isPlatformNT)
+ defaultcmdline = _T("-service");
+ else
+ defaultcmdline = _T("-noconsole -service");
+
+ // - Get the full pathname of our executable
+ ModuleFileName buffer;
+
+ // - Calculate the command-line length
+ int cmdline_len = _tcslen(buffer.buf) + 4;
+ int i;
+ for (i=0; i<argc; i++) {
+ cmdline_len += strlen(argv[i]) + 3;
+ }
+
+ // - Add the supplied extra parameters to the command line
+ TCharArray cmdline(cmdline_len+_tcslen(defaultcmdline));
+ _stprintf(cmdline.buf, _T("\"%s\" %s"), buffer.buf, defaultcmdline);
+ for (i=0; i<argc; i++) {
+ _tcscat(cmdline.buf, _T(" \""));
+ _tcscat(cmdline.buf, TStr(argv[i]));
+ _tcscat(cmdline.buf, _T("\""));
+ }
+
+ // - Register the service
+
+ if (osVersion.isPlatformNT) {
+
+ // - Open the SCM
+ ServiceHandle scm = OpenSCManager(NULL, NULL, SC_MANAGER_CREATE_SERVICE);
+ if (!scm)
+ throw rdr::SystemException("unable to open Service Control Manager", GetLastError());
+
+
+ ServiceHandle service = CreateService(scm,
+ name, desc, SC_MANAGER_ALL_ACCESS,
+ SERVICE_WIN32_OWN_PROCESS | SERVICE_INTERACTIVE_PROCESS,
+ SERVICE_AUTO_START, SERVICE_ERROR_IGNORE,
+ cmdline.buf, NULL, NULL, NULL, NULL, NULL);
+ if (!service)
+ throw rdr::SystemException("unable to create service", GetLastError());
+
+ // - Register the event log source
+ RegKey hk, hk2;
+
+ hk2.createKey(HKEY_LOCAL_MACHINE, _T("SYSTEM\\CurrentControlSet\\Services\\EventLog\\Application"));
+ hk.createKey(hk2, name);
+
+ for (i=_tcslen(buffer.buf); i>0; i--) {
+ if (buffer.buf[i] == _T('\\')) {
+ buffer.buf[i+1] = 0;
+ break;
+ }
+ }
+
+ const TCHAR* dllFilename = _T("logmessages.dll");
+ TCharArray dllPath(_tcslen(buffer.buf) + _tcslen(dllFilename) + 1);
+ _tcscpy(dllPath.buf, buffer.buf);
+ _tcscat(dllPath.buf, dllFilename);
+
+ hk.setExpandString(_T("EventMessageFile"), dllPath.buf);
+ hk.setInt(_T("TypesSupported"), EVENTLOG_ERROR_TYPE | EVENTLOG_INFORMATION_TYPE);
+
+ } else {
+
+ RegKey services;
+ services.createKey(HKEY_LOCAL_MACHINE, _T("Software\\Microsoft\\Windows\\CurrentVersion\\RunServices"));
+ services.setString(name, cmdline.buf);
+
+ }
+
+ Sleep(500);
+
+ return true;
+}
+
+bool rfb::win32::unregisterService(const TCHAR* name) {
+ if (osVersion.isPlatformNT) {
+
+ // - Open the SCM
+ ServiceHandle scm = OpenSCManager(NULL, NULL, SC_MANAGER_CREATE_SERVICE);
+ if (!scm)
+ throw rdr::SystemException("unable to open Service Control Manager", GetLastError());
+
+ // - Create the service
+ ServiceHandle service = OpenService(scm, name, SC_MANAGER_ALL_ACCESS);
+ if (!service)
+ throw rdr::SystemException("unable to locate the service", GetLastError());
+ if (!DeleteService(service))
+ throw rdr::SystemException("unable to remove the service", GetLastError());
+
+ // - Register the event log source
+ RegKey hk;
+ hk.openKey(HKEY_LOCAL_MACHINE, _T("SYSTEM\\CurrentControlSet\\Services\\EventLog\\Application"));
+ hk.deleteKey(name);
+
+ } else {
+
+ RegKey services;
+ services.openKey(HKEY_LOCAL_MACHINE, _T("Software\\Microsoft\\Windows\\CurrentVersion\\RunServices"));
+ services.deleteValue(name);
+
+ }
+
+ Sleep(500);
+
+ return true;
+}
+
+
+// -=- Starting and stopping the service
+
+HWND findServiceWindow(const TCHAR* name) {
+ TCharArray wndName(_tcslen(ServiceMsgWindow::baseName)+_tcslen(name)+1);
+ _tcscpy(wndName.buf, ServiceMsgWindow::baseName);
+ _tcscat(wndName.buf, name);
+ vlog.debug("searching for %s window", CStr(wndName.buf));
+ return FindWindow(0, wndName.buf);
+}
+
+bool rfb::win32::startService(const TCHAR* name) {
+
+ if (osVersion.isPlatformNT) {
+ // - Open the SCM
+ ServiceHandle scm = OpenSCManager(NULL, NULL, SC_MANAGER_CONNECT);
+ if (!scm)
+ throw rdr::SystemException("unable to open Service Control Manager", GetLastError());
+
+ // - Locate the service
+ ServiceHandle service = OpenService(scm, name, SERVICE_START);
+ if (!service)
+ throw rdr::SystemException("unable to open the service", GetLastError());
+
+ // - Start the service
+ if (!StartService(service, 0, NULL))
+ throw rdr::SystemException("unable to start the service", GetLastError());
+ } else {
+ // - Check there is no service window
+ if (findServiceWindow(name))
+ throw rdr::Exception("the service is already running");
+
+ // - Find the RunServices registry key
+ RegKey services;
+ services.openKey(HKEY_LOCAL_MACHINE, _T("Software\\Microsoft\\Windows\\CurrentVersion\\RunServices"));
+
+ // - Read the command-line from it
+ TCharArray cmdLine = services.getString(name);
+
+ // - Start the service
+ PROCESS_INFORMATION proc_info;
+ STARTUPINFO startup_info;
+ ZeroMemory(&startup_info, sizeof(startup_info));
+ startup_info.cb = sizeof(startup_info);
+ if (!CreateProcess(0, cmdLine.buf, 0, 0, FALSE, CREATE_NEW_CONSOLE, 0, 0, &startup_info, &proc_info)) {
+ throw SystemException("unable to start service", GetLastError());
+ }
+ }
+
+ Sleep(500);
+
+ return true;
+}
+
+bool rfb::win32::stopService(const TCHAR* name) {
+ if (osVersion.isPlatformNT) {
+ // - Open the SCM
+ ServiceHandle scm = OpenSCManager(NULL, NULL, SC_MANAGER_CONNECT);
+ if (!scm)
+ throw rdr::SystemException("unable to open Service Control Manager", GetLastError());
+
+ // - Locate the service
+ ServiceHandle service = OpenService(scm, name, SERVICE_STOP);
+ if (!service)
+ throw rdr::SystemException("unable to open the service", GetLastError());
+
+ // - Start the service
+ SERVICE_STATUS status;
+ if (!ControlService(service, SERVICE_CONTROL_STOP, &status))
+ throw rdr::SystemException("unable to stop the service", GetLastError());
+
+ } else {
+ // - Find the service window
+ HWND service_window = findServiceWindow(name);
+ if (!service_window)
+ throw Exception("unable to locate running service");
+
+ // Tell it to quit
+ vlog.debug("sending service stop request");
+ if (!SendMessage(service_window, WM_SMSG_SERVICE_STOP, 0, 0))
+ throw Exception("unable to stop service");
+
+ // Check it's quitting...
+ DWORD process_id = 0;
+ HANDLE process = 0;
+ if (!GetWindowThreadProcessId(service_window, &process_id))
+ throw SystemException("unable to verify service has quit", GetLastError());
+ process = OpenProcess(SYNCHRONIZE | PROCESS_TERMINATE, FALSE, process_id);
+ if (!process)
+ throw SystemException("unable to obtain service handle", GetLastError());
+ int retries = 5;
+ vlog.debug("checking status");
+ while (retries-- && (WaitForSingleObject(process, 1000) != WAIT_OBJECT_0)) {}
+ if (!retries) {
+ vlog.debug("failed to quit - terminating");
+ // May not have quit because of silly Win9x registry watching bug..
+ if (!TerminateProcess(process, 1))
+ throw SystemException("unable to terminate process!", GetLastError());
+ throw Exception("service failed to quit - called TerminateProcess");
+ }
+ }
+
+ Sleep(500);
+
+ return true;
+}
+
+void rfb::win32::printServiceStatus(const TCHAR* name) {
+ if (osVersion.isPlatformNT) {
+ // - Open the SCM
+ ServiceHandle scm = OpenSCManager(NULL, NULL, SC_MANAGER_CONNECT);
+ if (!scm)
+ throw rdr::SystemException("unable to open Service Control Manager", GetLastError());
+
+ // - Locate the service
+ ServiceHandle service = OpenService(scm, name, SERVICE_INTERROGATE);
+ if (!service)
+ throw rdr::SystemException("unable to open the service", GetLastError());
+
+ // - Get the service status
+ SERVICE_STATUS status;
+ if (!ControlService(service, SERVICE_CONTROL_INTERROGATE, (SERVICE_STATUS*)&status))
+ throw rdr::SystemException("unable to query the service", GetLastError());
+
+ printf("Service is in the ");
+ switch (status.dwCurrentState) {
+ case SERVICE_RUNNING: printf("running"); break;
+ case SERVICE_STOPPED: printf("stopped"); break;
+ case SERVICE_STOP_PENDING: printf("stop pending"); break;
+ default: printf("unknown (%lu)", status.dwCurrentState); break;
+ };
+ printf(" state.\n");
+
+ } else {
+ HWND service_window = findServiceWindow(name);
+ printf("Service is in the ");
+ if (!service_window) printf("stopped");
+ else printf("running");
+ printf(" state.\n");
+ }
+}
+
+
+bool rfb::win32::isServiceProcess() {
+ return service != 0;
+} \ No newline at end of file
diff --git a/rfb_win32/Service.h b/rfb_win32/Service.h
new file mode 100644
index 00000000..164381a1
--- /dev/null
+++ b/rfb_win32/Service.h
@@ -0,0 +1,123 @@
+/* Copyright (C) 2002-2003 RealVNC Ltd. All Rights Reserved.
+ *
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+ * USA.
+ */
+
+// -=- Service.h
+//
+// Win32 service-mode code.
+// Derive your service from this code and let it handle the annoying Win32
+// service API.
+// The underlying implementation takes care of the differences between
+// Windows NT and Windows 95 based systems
+
+#ifndef __RFB_WIN32_SERVICE_H__
+#define __RFB_WIN32_SERVICE_H__
+
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+
+namespace rfb {
+
+ namespace win32 {
+
+ //
+ // -=- Service
+ //
+
+ // Application base-class for services.
+
+ class Service {
+ public:
+
+ Service(const TCHAR* name_);
+ virtual ~Service();
+
+ const TCHAR* getName() {return name;}
+ SERVICE_STATUS& getStatus() {return status;}
+
+ void setStatus(DWORD status);
+ void setStatus();
+
+ // - Start the service, having initialised it
+ void start();
+
+ // - Service main procedure - override to implement a service
+ virtual DWORD serviceMain(int argc, TCHAR* argv[]) = 0;
+
+ // - Service control notifications
+
+ // To get notified when the OS is shutting down
+ virtual void osShuttingDown() = 0;
+
+ // To get notified when the service parameters change
+ virtual void readParams() = 0;
+
+ // To cause the serviceMain() routine to return
+ virtual void stop() = 0;
+
+ public:
+ SERVICE_STATUS_HANDLE status_handle;
+ SERVICE_STATUS status;
+ protected:
+ const TCHAR* name;
+ };
+
+ class ServiceHandle {
+ public:
+ ServiceHandle(SC_HANDLE h) : handle(h) {}
+ ~ServiceHandle() {CloseServiceHandle(handle);}
+ operator SC_HANDLE() const {return handle;}
+ protected:
+ SC_HANDLE handle;
+ };
+
+ // -=- Routines used by desktop back-end code to manage desktops/window stations
+
+ // Returns false under Win9x
+ bool desktopChangeRequired();
+
+ // Returns true under Win9x
+ bool changeDesktop();
+
+ // -=- Routines used by the SInput Keyboard class to emulate Ctrl-Alt-Del
+ // Returns false under Win9x
+ bool emulateCtrlAltDel();
+
+ // -=- Routines to initialise the Event Log target Logger
+ // Returns false under Win9x
+ bool initEventLogLogger(const TCHAR* srcname);
+
+ // -=- Routines to register/unregister the service
+ // These routines also take care of registering the required
+ // event source information, etc.
+ // *** should really accept TCHAR argv
+
+ bool registerService(const TCHAR* name, const TCHAR* desc, int argc, const char* argv[]);
+ bool unregisterService(const TCHAR* name);
+
+ bool startService(const TCHAR* name);
+ bool stopService(const TCHAR* name);
+ void printServiceStatus(const TCHAR* name);
+
+ // -=- Routine to determine whether the host process is running a service
+ bool isServiceProcess();
+
+ };
+
+};
+
+#endif // __RFB_WIN32_SERVICE_NT_H__
diff --git a/rfb_win32/SocketManager.cxx b/rfb_win32/SocketManager.cxx
new file mode 100644
index 00000000..6ebd5c0a
--- /dev/null
+++ b/rfb_win32/SocketManager.cxx
@@ -0,0 +1,246 @@
+/* Copyright (C) 2002-2004 RealVNC Ltd. All Rights Reserved.
+ *
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+ * USA.
+ */
+
+// -=- SocketManager.cxx
+
+#define WIN32_LEAN_AND_MEAN
+#include <winsock2.h>
+#include <assert.h>
+
+#include <rfb/LogWriter.h>
+#include <rfb_win32/SocketManager.h>
+
+using namespace rfb;
+using namespace rfb::win32;
+
+static LogWriter vlog("SocketManager");
+
+
+// -=- SocketManager
+
+SocketManager::SocketManager() : sockets(0), events(0), nSockets(0), nAvail(0) {
+}
+
+SocketManager::~SocketManager() {
+ for (int i=0; i<nSockets; i++) {
+ if (!sockets[i].is_event)
+ WSACloseEvent(events[i]);
+ }
+ delete [] events;
+ delete [] sockets;
+}
+
+
+void SocketManager::addListener(network::SocketListener* sock_, network::SocketServer* srvr) {
+ WSAEVENT event = WSACreateEvent();
+ assert(event != WSA_INVALID_EVENT);
+ addListener(sock_, event, srvr);
+}
+
+void SocketManager::addSocket(network::Socket* sock_, network::SocketServer* srvr) {
+ WSAEVENT event = WSACreateEvent();
+ assert(event != WSA_INVALID_EVENT);
+ addSocket(sock_, event, srvr);
+}
+
+
+BOOL SocketManager::getMessage(MSG* msg, HWND hwnd, UINT minMsg, UINT maxMsg) {
+ while (true) {
+ // First check for idle timeout
+
+ network::SocketServer* server = 0;
+ int timeout = 0;
+ for (int i=0; i<nSockets; i++) {
+ if (!sockets[i].is_event &&
+ sockets[i].server != server) {
+ server = sockets[i].server;
+ int t = server->checkTimeouts();
+ if (t > 0 && (timeout == 0 || t < timeout))
+ timeout = t;
+ }
+ }
+ if (timeout == 0)
+ timeout = INFINITE;
+
+ // - Network IO is less common than messages - process it first
+ DWORD result;
+ if (nSockets) {
+ result = WaitForMultipleObjects(nSockets, events, FALSE, 0);
+ if (result == WAIT_TIMEOUT) {
+ if (PeekMessage(msg, hwnd, minMsg, maxMsg, PM_REMOVE))
+ return msg->message != WM_QUIT;
+
+ result = MsgWaitForMultipleObjects(nSockets, events, FALSE, timeout,
+ QS_ALLINPUT);
+ if (result == WAIT_OBJECT_0 + nSockets) {
+ if (PeekMessage(msg, hwnd, minMsg, maxMsg, PM_REMOVE))
+ return msg->message != WM_QUIT;
+ continue;
+ }
+ }
+ } else
+ return GetMessage(msg, hwnd, minMsg, maxMsg);
+
+ if ((result >= WAIT_OBJECT_0) && (result < (WAIT_OBJECT_0 + nSockets))) {
+ int index = result - WAIT_OBJECT_0;
+
+ // - Process a socket event
+
+ if (sockets[index].is_event) {
+ // Process a general Win32 event
+ // NB: The handler must reset the event!
+
+ if (!sockets[index].handler->processEvent(events[index])) {
+ removeSocket(index);
+ continue;
+ }
+ } else if (sockets[index].is_conn) {
+ // Process data from an active connection
+
+ // Cancel event notification for this socket
+ if (WSAEventSelect(sockets[index].fd, events[index], 0) == SOCKET_ERROR)
+ vlog.info("unable to disable WSAEventSelect:%u", WSAGetLastError());
+
+ // Reset the event object
+ WSAResetEvent(events[index]);
+
+ // Call the socket server to process the event
+ if (!sockets[index].server->processSocketEvent(sockets[index].sock.conn)) {
+ removeSocket(index);
+ continue;
+ }
+
+ // Re-instate the required socket event
+ // If the read event is still valid, the event object gets set here
+ if (WSAEventSelect(sockets[index].fd, events[index], FD_READ | FD_CLOSE) == SOCKET_ERROR)
+ throw rdr::SystemException("unable to re-enable WSAEventSelect:%u", WSAGetLastError());
+
+ } else {
+ // Accept an incoming connection
+ vlog.debug("accepting incoming connection");
+
+ // What kind of event is this?
+ WSANETWORKEVENTS network_events;
+ WSAEnumNetworkEvents(sockets[index].fd, events[index], &network_events);
+ if (network_events.lNetworkEvents & FD_ACCEPT) {
+ network::Socket* new_sock = sockets[index].sock.listener->accept();
+ if (new_sock) {
+ sockets[index].server->addClient(new_sock);
+ addSocket(new_sock, sockets[index].server);
+ }
+ } else if (network_events.lNetworkEvents & FD_CLOSE) {
+ vlog.info("deleting listening socket");
+ network::SocketListener* s = sockets[index].sock.listener;
+ removeSocket(index);
+ delete s;
+ } else {
+ vlog.error("unknown network event for listener");
+ }
+
+ }
+ } else if (result == WAIT_FAILED) {
+ throw rdr::SystemException("unable to wait for events", GetLastError());
+ }
+ }
+}
+
+
+void SocketManager::resizeArrays(int numSockets) {
+ if (nAvail >= numSockets) return;
+ while (nAvail < numSockets)
+ nAvail = max(16, nAvail*2);
+
+ SocketInfo* newinfo = new SocketInfo[nAvail];
+ HANDLE* newevents = new HANDLE[nAvail];
+ for (int i=0; i<nSockets; i++) {
+ newinfo[i] = sockets[i];
+ newevents[i] = events[i];
+ }
+ delete [] sockets;
+ delete [] events;
+ sockets = newinfo;
+ events = newevents;
+}
+
+void SocketManager::addSocket(network::Socket* sock, HANDLE event, network::SocketServer* server) {
+ resizeArrays(nSockets+1);
+
+ sockets[nSockets].sock.conn = sock;
+ sockets[nSockets].fd = sock->getFd();
+ sockets[nSockets].server = server;
+ events[nSockets] = event;
+ sockets[nSockets].is_conn = true;
+ sockets[nSockets].is_event = false;
+
+ if (WSAEventSelect(sock->getFd(), event, FD_READ | FD_CLOSE) == SOCKET_ERROR)
+ throw rdr::SystemException("unable to select on socket", WSAGetLastError());
+ nSockets++;
+}
+
+void SocketManager::addListener(network::SocketListener* sock, HANDLE event, network::SocketServer* server) {
+ resizeArrays(nSockets+1);
+
+ sockets[nSockets].sock.listener = sock;
+ sockets[nSockets].fd = sock->getFd();
+ sockets[nSockets].server = server;
+ events[nSockets] = event;
+ sockets[nSockets].is_conn = false;
+ sockets[nSockets].is_event = false;
+
+ if (WSAEventSelect(sock->getFd(), event, FD_ACCEPT | FD_CLOSE) == SOCKET_ERROR)
+ throw rdr::SystemException("unable to select on listener", WSAGetLastError());
+ nSockets++;
+}
+
+void SocketManager::remListener(network::SocketListener* sock) {
+ for (int index=0; index<nSockets; index++) {
+ if (!sockets[index].is_conn &&
+ !sockets[index].is_event) {
+ vlog.debug("removing listening socket");
+ removeSocket(index);
+ delete sock;
+ }
+ }
+}
+
+void SocketManager::addEvent(HANDLE event, EventHandler* ecb) {
+ resizeArrays(nSockets+1);
+
+ sockets[nSockets].handler = ecb;
+ events[nSockets] = event;
+ sockets[nSockets].is_conn = false;
+ sockets[nSockets].is_event = true;
+
+ nSockets++;
+}
+
+void SocketManager::removeSocket(int index) {
+ if (index >= nSockets)
+ throw rdr::Exception("attempting to remove unregistered socket");
+
+ if (!sockets[index].is_event)
+ WSACloseEvent(events[index]);
+
+ for (int i=index; i<nSockets-1; i++) {
+ sockets[i] = sockets[i+1];
+ events[i] = events[i+1];
+ }
+
+ nSockets--;
+}
+
diff --git a/rfb_win32/SocketManager.h b/rfb_win32/SocketManager.h
new file mode 100644
index 00000000..791370f2
--- /dev/null
+++ b/rfb_win32/SocketManager.h
@@ -0,0 +1,107 @@
+/* Copyright (C) 2002-2004 RealVNC Ltd. All Rights Reserved.
+ *
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+ * USA.
+ */
+
+// -=- SocketManager.h
+
+// Socket manager class for Win32.
+// Passed a network::SocketListener and a network::SocketServer when
+// constructed. Uses WSAAsyncSelect to get notifications of network
+// connection attempts. When an incoming connection is received,
+// the manager will call network::SocketServer::addClient(). If
+// addClient returns true then the manager registers interest in
+// network events on that socket, and calls
+// network::SocketServer::processSocketEvent().
+
+#ifndef __RFB_WIN32_SOCKET_MGR_H__
+#define __RFB_WIN32_SOCKET_MGR_H__
+
+#include <list>
+
+#include <network/Socket.h>
+#include <rfb_win32/MsgWindow.h>
+
+namespace rfb {
+
+ namespace win32 {
+
+ class SocketManager {
+ public:
+ SocketManager();
+ virtual ~SocketManager();
+
+ // Add a listening socket. Incoming connections will be added to the supplied
+ // SocketServer.
+ void addListener(network::SocketListener* sock_, network::SocketServer* srvr);
+
+ // Remove and delete a listening socket.
+ void remListener(network::SocketListener* sock);
+
+ // Add an already-connected socket. Socket events will cause the supplied
+ // SocketServer to be called. The socket must ALREADY BE REGISTERED with
+ // the SocketServer.
+ void addSocket(network::Socket* sock_, network::SocketServer* srvr);
+
+ // Add a Win32 event & handler for it to the SocketManager
+ // This event will be blocked on along with the registered Sockets, and the
+ // handler called whenever it is discovered to be set.
+ // NB: SocketManager does NOT call ResetEvent on the event!
+ // NB: If processEvent returns false then the event is no longer registered,
+ // and the event object is assumed to have been closed by processEvent()
+ struct EventHandler {
+ virtual ~EventHandler() {}
+ virtual bool processEvent(HANDLE event) = 0;
+ };
+ void addEvent(HANDLE event, EventHandler* ecb);
+
+ // getMessage
+ //
+ // Either return a message from the thread's message queue or process a socket
+ // event.
+ // Returns whenever a message needs processing. Returns false if message is
+ // WM_QUIT, true for all other messages.
+ BOOL getMessage(MSG* msg, HWND hwnd, UINT minMsg, UINT maxMsg);
+
+ protected:
+ void addListener(network::SocketListener* sock, HANDLE event, network::SocketServer* server);
+ void addSocket(network::Socket* sock, HANDLE event, network::SocketServer* server);
+ void resizeArrays(int numSockets);
+ void removeSocket(int index);
+ struct SocketInfo {
+ union {
+ network::Socket* conn;
+ network::SocketListener* listener;
+ } sock;
+ SOCKET fd;
+ bool is_conn;
+ bool is_event;
+ union {
+ network::SocketServer* server;
+ EventHandler* handler;
+ };
+ };
+ SocketInfo* sockets;
+ HANDLE* events;
+ int nSockets;
+ int nAvail;
+ };
+
+ }
+
+}
+
+#endif
diff --git a/rfb_win32/TCharArray.cxx b/rfb_win32/TCharArray.cxx
new file mode 100644
index 00000000..f8f03a69
--- /dev/null
+++ b/rfb_win32/TCharArray.cxx
@@ -0,0 +1,85 @@
+/* Copyright (C) 2002-2003 RealVNC Ltd. All Rights Reserved.
+ *
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+ * USA.
+ */
+
+#include <rfb_win32/TCharArray.h>
+
+namespace rfb {
+
+ WCHAR* wstrDup(const WCHAR* s) {
+ if (!s) return 0;
+ WCHAR* t = new WCHAR[wcslen(s)+1];
+ memcpy(t, s, sizeof(WCHAR)*(wcslen(s)+1));
+ return t;
+ }
+ void wstrFree(WCHAR* s) {delete [] s;}
+
+ char* strDup(const WCHAR* s) {
+ if (!s) return 0;
+ int len = wcslen(s);
+ char* t = new char[len+1];
+ t[WideCharToMultiByte(CP_ACP, 0, s, len, t, len, 0, 0)] = 0;
+ return t;
+ }
+
+ WCHAR* wstrDup(const char* s) {
+ if (!s) return 0;
+ int len = strlen(s);
+ WCHAR* t = new WCHAR[len+1];
+ t[MultiByteToWideChar(CP_ACP, 0, s, len, t, len)] = 0;
+ return t;
+ }
+
+
+ bool wstrSplit(const WCHAR* src, const WCHAR limiter, WCHAR** out1, WCHAR** out2, bool fromEnd) {
+ WCharArray out1old, out2old;
+ if (out1) out1old.buf = *out1;
+ if (out2) out2old.buf = *out2;
+ int len = wcslen(src);
+ int i=0, increment=1, limit=len;
+ if (fromEnd) {
+ i=len-1; increment = -1; limit = -1;
+ }
+ while (i!=limit) {
+ if (src[i] == limiter) {
+ if (out1) {
+ *out1 = new WCHAR[i+1];
+ if (i) memcpy(*out1, src, sizeof(WCHAR)*i);
+ (*out1)[i] = 0;
+ }
+ if (out2) {
+ *out2 = new WCHAR[len-i];
+ if (len-i-1) memcpy(*out2, &src[i+1], sizeof(WCHAR)*(len-i-1));
+ (*out2)[len-i-1] = 0;
+ }
+ return true;
+ }
+ i+=increment;
+ }
+ if (out1) *out1 = wstrDup(src);
+ if (out2) *out2 = 0;
+ return false;
+ }
+
+ bool wstrContains(const WCHAR* src, WCHAR c) {
+ int l=wcslen(src);
+ for (int i=0; i<l; i++)
+ if (src[i] == c) return true;
+ return false;
+ }
+
+};
diff --git a/rfb_win32/TCharArray.h b/rfb_win32/TCharArray.h
new file mode 100644
index 00000000..399e00a7
--- /dev/null
+++ b/rfb_win32/TCharArray.h
@@ -0,0 +1,119 @@
+/* Copyright (C) 2002-2004 RealVNC Ltd. All Rights Reserved.
+ *
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+ * USA.
+ */
+
+// -=- TCharArray.h
+
+// This library contains the wide-character equivalent of CharArray, named
+// WCharArray. In addition to providing wide-character equivalents of
+// the char* string manipulation functions (strDup, strFree, etc), special
+// versions of those functions are provided which attempt to convert from
+// one format to the other.
+// e.g. char* t = "hello world"; WCHAR* w = wstrDup(t);
+// Results in w containing the wide-character text "hello world".
+// For convenience, the WStr and CStr classes are also provided. These
+// accept an existing (const) WCHAR* or char* null-terminated string and
+// create a read-only copy of that in the desired format. The new copy
+// will actually be the original copy if the format has not changed, otherwise
+// it will be a new buffer owned by the WStr/CStr.
+
+// In addition to providing wide character functions, this header defines
+// TCHAR* handling classes & functions. TCHAR is defined at compile time to
+// either char or WCHAR. Programs can treat this as a third data type and
+// call TStr() whenever a TCHAR* is required but a char* or WCHAR* is supplied,
+// and TStr will do the right thing.
+
+#ifndef __RFB_WIN32_TCHARARRAY_H__
+#define __RFB_WIN32_TCHARARRAY_H__
+
+#define WIN32_LEAN_AND_MEAN
+#include <tchar.h>
+
+#include <rfb/util.h>
+
+namespace rfb {
+
+ // -=- String duplication and cleanup functions.
+ // These routines also handle conversion between WCHAR* and char*
+
+ char* strDup(const WCHAR* s);
+ WCHAR* wstrDup(const WCHAR* s);
+ WCHAR* wstrDup(const char* s);
+ void wstrFree(WCHAR* s);
+
+ bool wstrSplit(const WCHAR* src, const WCHAR limiter, WCHAR** out1, WCHAR** out2, bool fromEnd=false);
+ bool wstrContains(const WCHAR* src, WCHAR c);
+
+ // -=- Temporary format conversion classes
+ // CStr accepts WCHAR* or char* and behaves like a char*
+ // WStr accepts WCHAR* or char* and behaves like a WCHAR*
+
+ struct WStr {
+ WStr(const char* s) : buf(wstrDup(s)), free_(true) {}
+ WStr(const WCHAR* s) : buf(s), free_(false) {}
+ ~WStr() {if (free_) wstrFree((WCHAR*)buf);}
+ operator const WCHAR*() {return buf;}
+ const WCHAR* buf;
+ bool free_;
+ };
+
+ struct CStr {
+ CStr(const char* s) : buf(s), free_(false) {}
+ CStr(const WCHAR* s) : buf(strDup(s)), free_(true) {}
+ ~CStr() {if (free_) strFree((char*)buf);}
+ operator const char*() {return buf;}
+ const char* buf;
+ bool free_;
+ };
+
+ // -=- Class to handle cleanup of arrays of native Win32 characters
+ class WCharArray {
+ public:
+ WCharArray() : buf(0) {}
+ WCharArray(char* str) : buf(wstrDup(str)) {strFree(str);} // note: assumes ownership
+ WCharArray(WCHAR* str) : buf(str) {} // note: assumes ownership
+ WCharArray(int len) {
+ buf = new WCHAR[len];
+ }
+ ~WCharArray() {
+ delete [] buf;
+ }
+ // Get the buffer pointer & clear it (i.e. caller takes ownership)
+ WCHAR* takeBuf() {WCHAR* tmp = buf; buf = 0; return tmp;}
+ void replaceBuf(WCHAR* str) {delete [] buf; buf = str;}
+ WCHAR* buf;
+ };
+
+#ifdef _UNICODE
+#define tstrDup wstrDup
+#define tstrFree wstrFree
+#define tstrSplit wstrSplit
+#define tstrContains wstrContains
+ typedef WCharArray TCharArray;
+ typedef WStr TStr;
+#else
+#define tstrDup strDup
+#define tstrFree strFree
+#define tstrSplit strSplit
+#define tstrContains strContains
+ typedef CharArray TCharArray;
+ typedef CStr TStr;
+#endif
+
+};
+
+#endif \ No newline at end of file
diff --git a/rfb_win32/TrayIcon.h b/rfb_win32/TrayIcon.h
new file mode 100644
index 00000000..85680f3f
--- /dev/null
+++ b/rfb_win32/TrayIcon.h
@@ -0,0 +1,90 @@
+/* Copyright (C) 2002-2003 RealVNC Ltd. All Rights Reserved.
+ *
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+ * USA.
+ */
+
+// -=- CView.h
+
+// An instance of the CView class is created for each VNC Viewer connection.
+
+#ifndef __RFB_WIN32_TRAY_ICON_H__
+#define __RFB_WIN32_TRAY_ICON_H__
+
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+#include <shellapi.h>
+#include <rfb_win32/MsgWindow.h>
+#include <rdr/Exception.h>
+
+namespace rfb {
+
+ namespace win32 {
+
+ class TrayIcon : public MsgWindow {
+ public:
+ TrayIcon() : MsgWindow(_T("VNCTray")) {
+#ifdef NOTIFYICONDATA_V1_SIZE
+ nid.cbSize = NOTIFYICONDATA_V1_SIZE;
+#else
+ nid.cbSize = sizeof(NOTIFYICONDATA);
+#endif
+
+ nid.hWnd = getHandle();
+ nid.uID = 0;
+ nid.hIcon = 0;
+ nid.uFlags = NIF_ICON | NIF_MESSAGE;
+ nid.uCallbackMessage = WM_USER;
+ }
+ virtual ~TrayIcon() {
+ remove();
+ }
+ bool setIcon(UINT icon) {
+ if (icon == 0) {
+ return remove();
+ } else {
+ nid.hIcon = (HICON)LoadImage(GetModuleHandle(0), MAKEINTRESOURCE(icon),
+ IMAGE_ICON, 0, 0, LR_DEFAULTSIZE | LR_SHARED);
+ return refresh();
+ }
+ }
+ bool setToolTip(const TCHAR* text) {
+ if (text == 0) {
+ nid.uFlags &= ~NIF_TIP;
+ } else {
+ const int tipLen = sizeof(nid.szTip)/sizeof(TCHAR);
+ _tcsncpy(nid.szTip, text, tipLen);
+ nid.szTip[tipLen-1] = 0;
+ nid.uFlags |= NIF_TIP;
+ }
+ return refresh();
+ }
+ bool remove() {
+ return Shell_NotifyIcon(NIM_DELETE, &nid) != 0;
+ }
+ bool refresh() {
+ return Shell_NotifyIcon(NIM_MODIFY, &nid) || Shell_NotifyIcon(NIM_ADD, &nid);
+ }
+ protected:
+ NOTIFYICONDATA nid;
+ };
+
+ };
+
+};
+
+#endif
+
+
diff --git a/rfb_win32/WMCursor.cxx b/rfb_win32/WMCursor.cxx
new file mode 100644
index 00000000..871d9376
--- /dev/null
+++ b/rfb_win32/WMCursor.cxx
@@ -0,0 +1,98 @@
+/* Copyright (C) 2002-2003 RealVNC Ltd. All Rights Reserved.
+ *
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+ * USA.
+ */
+
+// -=- WMCursor.cxx
+
+// *** DOESN'T SEEM TO WORK WITH GetCursorInfo POS CODE BUILT-IN UNDER NT4SP6
+// *** INSTEAD, WE LOOK FOR Win2000/Win98 OR ABOVE
+#define WINVER 0x0500
+
+#include <rfb_win32/WMCursor.h>
+#include <rfb_win32/OSVersion.h>
+#include <rfb_win32/Win32Util.h>
+#include <rfb/Exception.h>
+#include <rfb/LogWriter.h>
+
+using namespace rdr;
+using namespace rfb;
+using namespace rfb::win32;
+
+static LogWriter vlog("WMCursor");
+
+
+typedef BOOL (WINAPI *_GetCursorInfo_proto)(PCURSORINFO pci);
+DynamicFn<_GetCursorInfo_proto> _GetCursorInfo(_T("user32.dll"), "GetCursorInfo");
+
+
+WMCursor::WMCursor() : hooks(0), library(0), use_getCursorInfo(false), cursor(0) {
+#if (WINVER >= 0x0500)
+ // Check the OS version
+ bool is_win98 = (osVersion.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS) &&
+ (osVersion.dwMajorVersion > 4) || ((osVersion.dwMajorVersion == 4) && (osVersion.dwMinorVersion > 0));
+ bool is_win2K = (osVersion.dwPlatformId == VER_PLATFORM_WIN32_NT) && (osVersion.dwMajorVersion >= 5);
+
+ // Use GetCursorInfo if OS version is sufficient
+ use_getCursorInfo = (is_win98 || is_win2K) && _GetCursorInfo.isValid();
+#else
+#pragma message ("not building in GetCursorInfo support")
+#endif
+ if (!use_getCursorInfo) {
+ hooks = new WMCursorHooks();
+ if (hooks && hooks->start()) {
+ vlog.info("falling back to cursor hooking");
+ } else {
+ delete hooks;
+ hooks = 0;
+ vlog.error("unable to monitor cursor shape");
+ }
+ } else {
+ vlog.info("using GetCursorInfo");
+ }
+ cursor = (HCURSOR)LoadImage(0, IDC_ARROW, IMAGE_CURSOR, 0, 0, LR_DEFAULTSIZE | LR_SHARED);
+}
+
+WMCursor::~WMCursor() {
+ if (hooks) delete hooks;
+ if (library) FreeLibrary(library);
+}
+
+WMCursor::Info
+WMCursor::getCursorInfo() {
+ Info result;
+#if (WINVER >= 0x0500)
+ if (use_getCursorInfo) {
+ CURSORINFO info;
+ info.cbSize = sizeof(CURSORINFO);
+ if ((*_GetCursorInfo)(&info)) {
+ result.cursor = info.hCursor;
+ result.position = Point(info.ptScreenPos.x, info.ptScreenPos.y);
+ result.visible = info.flags & CURSOR_SHOWING;
+ return result;
+ }
+ }
+#endif
+ // Fall back to the old way of doing things
+ POINT pos;
+ if (hooks) cursor = hooks->getCursor();
+ result.cursor = cursor;
+ result.visible = cursor != 0;
+ GetCursorPos(&pos);
+ result.position.x = pos.x;
+ result.position.y = pos.y;
+ return result;
+}
diff --git a/rfb_win32/WMCursor.h b/rfb_win32/WMCursor.h
new file mode 100644
index 00000000..a96822a7
--- /dev/null
+++ b/rfb_win32/WMCursor.h
@@ -0,0 +1,65 @@
+/* Copyright (C) 2002-2003 RealVNC Ltd. All Rights Reserved.
+ *
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+ * USA.
+ */
+
+// -=- WMCursor.h
+
+// WMCursor provides a single API through which the cursor state can be obtained
+// The underlying implementation will use either GetCursorInfo, or use the
+// wm_hooks library if GetCursorInfo is not available.
+
+#ifndef __RFB_WIN32_WM_CURSOR_H__
+#define __RFB_WIN32_WM_CURSOR_H__
+
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+#include <rfb_win32/WMHooks.h>
+
+namespace rfb {
+
+ namespace win32 {
+
+ class WMCursor {
+ public:
+ WMCursor();
+ ~WMCursor();
+
+ struct Info {
+ HCURSOR cursor;
+ Point position;
+ bool visible;
+ Info() : cursor(0), visible(false) {}
+ bool operator!=(const Info& info) {
+ return ((cursor != info.cursor) ||
+ (!position.equals(info.position)) ||
+ (visible != info.visible));
+ }
+ };
+
+ Info getCursorInfo();
+ protected:
+ WMCursorHooks* hooks;
+ HMODULE library;
+ bool use_getCursorInfo;
+ HCURSOR cursor;
+ };
+
+ };
+
+};
+
+#endif // __RFB_WIN32_WM_CURSOR_H__
diff --git a/rfb_win32/WMHooks.cxx b/rfb_win32/WMHooks.cxx
new file mode 100644
index 00000000..26a2363c
--- /dev/null
+++ b/rfb_win32/WMHooks.cxx
@@ -0,0 +1,324 @@
+/* Copyright (C) 2002-2003 RealVNC Ltd. All Rights Reserved.
+ *
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+ * USA.
+ */
+
+// -=- WMHooks.cxx
+
+#include <wm_hooks/wm_hooks.h>
+
+#include <rfb_win32/WMHooks.h>
+#include <rfb_win32/Service.h>
+#include <rfb/Threading.h>
+#include <rfb/LogWriter.h>
+
+#include <list>
+
+using namespace rfb;
+using namespace rfb::win32;
+
+static LogWriter vlog("WMHooks");
+
+class WMHooksThread : public Thread {
+public:
+ WMHooksThread() : Thread("WMHookThread"), active(true) {}
+ virtual void run();
+ virtual Thread* join();
+protected:
+ bool active;
+};
+
+WMHooksThread* hook_mgr = 0;
+std::list<WMHooks*> hooks;
+std::list<WMCursorHooks*> cursor_hooks;
+Mutex hook_mgr_lock;
+HCURSOR hook_cursor = (HCURSOR)LoadImage(0, IDC_ARROW, IMAGE_CURSOR, 0, 0, LR_DEFAULTSIZE | LR_SHARED);
+
+
+bool
+StartHookThread() {
+ if (hook_mgr) return true;
+ vlog.debug("opening hook thread");
+ hook_mgr = new WMHooksThread();
+ if (!WM_Hooks_Install(hook_mgr->getThreadId(), 0)) {
+ vlog.error("failed to initialise hooks");
+ delete hook_mgr->join();
+ hook_mgr = 0;
+ return false;
+ }
+ hook_mgr->start();
+ return true;
+}
+
+void
+StopHookThread() {
+ if (!hook_mgr) return;
+ if (!hooks.empty() || !cursor_hooks.empty()) return;
+ vlog.debug("closing hook thread");
+ delete hook_mgr->join();
+ hook_mgr = 0;
+}
+
+
+bool
+AddHook(WMHooks* hook) {
+ vlog.debug("adding hook");
+ Lock l(hook_mgr_lock);
+ if (!StartHookThread()) return false;
+ hooks.push_back(hook);
+ return true;
+}
+
+bool
+AddCursorHook(WMCursorHooks* hook) {
+ vlog.debug("adding cursor hook");
+ Lock l(hook_mgr_lock);
+ if (cursor_hooks.empty()) WM_Hooks_EnableCursorShape(TRUE);
+ if (!StartHookThread()) return false;
+ cursor_hooks.push_back(hook);
+ return true;
+}
+
+bool
+RemHook(WMHooks* hook) {
+ {
+ vlog.debug("removing hook");
+ Lock l(hook_mgr_lock);
+ hooks.remove(hook);
+ }
+ StopHookThread();
+ return true;
+}
+
+bool
+RemCursorHook(WMCursorHooks* hook) {
+ {
+ vlog.debug("removing cursor hook");
+ Lock l(hook_mgr_lock);
+ cursor_hooks.remove(hook);
+ }
+ StopHookThread();
+ if (cursor_hooks.empty()) WM_Hooks_EnableCursorShape(FALSE);
+ return true;
+}
+
+void
+NotifyHooksRegion(const Region& r) {
+ Lock l(hook_mgr_lock);
+ std::list<WMHooks*>::iterator i;
+ for (i=hooks.begin(); i!=hooks.end(); i++) {
+ (*i)->new_changes.add_changed(r);
+ if (!(*i)->notified) {
+ (*i)->notified = true;
+ PostMessage((*i)->getHandle(), WM_USER, 0, 0);
+ }
+ }
+}
+
+void
+NotifyHooksCursor(HCURSOR c) {
+ Lock l(hook_mgr_lock);
+ hook_cursor = c;
+}
+
+void
+WMHooksThread::run() {
+ UINT windowMsg = WM_Hooks_WindowChanged();
+ UINT clientAreaMsg = WM_Hooks_WindowClientAreaChanged();
+ UINT borderMsg = WM_Hooks_WindowBorderChanged();
+ UINT rectangleMsg = WM_Hooks_RectangleChanged();
+ UINT cursorMsg = WM_Hooks_CursorChanged();
+#ifdef _DEBUG
+ UINT diagnosticMsg = WM_Hooks_Diagnostic();
+#endif
+ MSG msg;
+ RECT wrect;
+ HWND hwnd;
+ int count = 0;
+
+ vlog.debug("starting hook thread");
+
+ while (active && GetMessage(&msg, NULL, 0, 0)) {
+ count++;
+ if (msg.message == windowMsg) {
+ hwnd = (HWND) msg.lParam;
+ if (IsWindow(hwnd) && IsWindowVisible(hwnd) && !IsIconic(hwnd) &&
+ GetWindowRect(hwnd, &wrect) && !IsRectEmpty(&wrect))
+ {
+ NotifyHooksRegion(Rect(wrect.left, wrect.top,
+ wrect.right, wrect.bottom));
+
+ }
+ } else if (msg.message == clientAreaMsg) {
+ hwnd = (HWND) msg.lParam;
+ if (IsWindow(hwnd) && IsWindowVisible(hwnd) && !IsIconic(hwnd) &&
+ GetClientRect(hwnd, &wrect) && !IsRectEmpty(&wrect))
+ {
+ POINT pt = {0,0};
+ if (ClientToScreen(hwnd, &pt)) {
+ NotifyHooksRegion(Rect(wrect.left+pt.x, wrect.top+pt.y,
+ wrect.right+pt.x, wrect.bottom+pt.y));
+ }
+ }
+ } else if (msg.message == borderMsg) {
+ hwnd = (HWND) msg.lParam;
+ if (IsWindow(hwnd) && IsWindowVisible(hwnd) && !IsIconic(hwnd) &&
+ GetWindowRect(hwnd, &wrect) && !IsRectEmpty(&wrect))
+ {
+ Region changed(Rect(wrect.left, wrect.top, wrect.right, wrect.bottom));
+ RECT crect;
+ POINT pt = {0,0};
+ if (GetClientRect(hwnd, &crect) && ClientToScreen(hwnd, &pt) &&
+ !IsRectEmpty(&crect))
+ {
+ changed.assign_subtract(Rect(crect.left+pt.x, crect.top+pt.y,
+ crect.right+pt.x, crect.bottom+pt.y));
+ }
+ NotifyHooksRegion(changed);
+ }
+ } else if (msg.message == rectangleMsg) {
+ Rect r = Rect(LOWORD(msg.wParam), HIWORD(msg.wParam),
+ LOWORD(msg.lParam), HIWORD(msg.lParam));
+ if (!r.is_empty()) {
+ NotifyHooksRegion(r);
+ }
+ } else if (msg.message == cursorMsg) {
+ NotifyHooksCursor((HCURSOR)msg.lParam);
+#ifdef _DEBUG
+ } else if (msg.message == diagnosticMsg) {
+ vlog.info("DIAG msg=%x(%d) wnd=%lx", msg.wParam, msg.wParam, msg.lParam);
+#endif
+ }
+ }
+
+ vlog.debug("stopping hook thread - processed %d events", count);
+ WM_Hooks_Remove(getThreadId());
+}
+
+Thread*
+WMHooksThread::join() {
+ vlog.debug("stopping WMHooks thread");
+ active = false;
+ PostThreadMessage(thread_id, WM_QUIT, 0, 0);
+ vlog.debug("joining WMHooks thread");
+ return Thread::join();
+}
+
+// -=- WMHooks class
+
+rfb::win32::WMHooks::WMHooks()
+ : clipper(0), new_changes(true), fg_window(0),
+ notified(false), MsgWindow(_T("WMHooks")) {
+}
+
+rfb::win32::WMHooks::~WMHooks() {
+ RemHook(this);
+ if (clipper) delete clipper;
+}
+
+LRESULT
+rfb::win32::WMHooks::processMessage(UINT msg, WPARAM wParam, LPARAM lParam) {
+ switch (msg) {
+ case WM_USER:
+ {
+ // *** Yield, to allow the triggering update event to be processed
+ // BEFORE we try to grab the resulting changes.
+ // *** IMPROVES THINGS NOTICABLY ON WinXP
+ Sleep(0);
+ // ***
+
+ Lock l(hook_mgr_lock);
+ notified = false;
+ new_changes.get_update(*clipper);
+ new_changes.clear();
+ }
+ break;
+ }
+ return MsgWindow::processMessage(msg, wParam, lParam);
+}
+
+bool
+rfb::win32::WMHooks::setClipRect(const Rect& r) {
+ clip_region = r;
+ if (clipper) clipper->set_clip_region(clip_region);
+ return true;
+}
+
+bool
+rfb::win32::WMHooks::setUpdateTracker(UpdateTracker* ut) {
+ if (clipper) delete clipper;
+ clipper = new ClippedUpdateTracker(*ut);
+ clipper->set_clip_region(clip_region);
+ return AddHook(this);
+}
+
+#ifdef _DEBUG
+void
+rfb::win32::WMHooks::setDiagnosticRange(UINT min, UINT max) {
+ WM_Hooks_SetDiagnosticRange(min, max);
+}
+#endif
+
+
+// -=- WMBlockInput class
+
+Mutex blockMutex;
+int blockCount = 0;
+
+rfb::win32::WMBlockInput::WMBlockInput() : active(false) {
+}
+
+rfb::win32::WMBlockInput::~WMBlockInput() {
+ blockInputs(false);
+}
+
+bool rfb::win32::WMBlockInput::blockInputs(bool on) {
+ if (on == active) return true;
+ vlog.debug("blockInput changed");
+ Lock l(blockMutex);
+ int newCount = blockCount;
+ if (on)
+ newCount++;
+ else
+ newCount--;
+ if (WM_Hooks_EnableRealInputs(newCount==0, newCount==0)) {
+ vlog.debug("set blocking to %d", newCount);
+ blockCount = newCount;
+ active = on;
+ return true;
+ }
+ return false;
+}
+
+
+// -=- WMCursorHooks class
+
+rfb::win32::WMCursorHooks::WMCursorHooks() {
+}
+
+rfb::win32::WMCursorHooks::~WMCursorHooks() {
+ RemCursorHook(this);
+}
+
+bool
+rfb::win32::WMCursorHooks::start() {
+ return AddCursorHook(this);
+}
+
+HCURSOR
+rfb::win32::WMCursorHooks::getCursor() const {
+ return hook_cursor;
+}
diff --git a/rfb_win32/WMHooks.h b/rfb_win32/WMHooks.h
new file mode 100644
index 00000000..791df763
--- /dev/null
+++ b/rfb_win32/WMHooks.h
@@ -0,0 +1,85 @@
+/* Copyright (C) 2002-2003 RealVNC Ltd. All Rights Reserved.
+ *
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+ * USA.
+ */
+
+// -=- WMHooks.h
+
+#ifndef __RFB_WIN32_WM_HOOKS_H__
+#define __RFB_WIN32_WM_HOOKS_H__
+
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+#include <rfb/UpdateTracker.h>
+#include <rdr/Exception.h>
+#include <rfb_win32/MsgWindow.h>
+
+namespace rfb {
+
+ namespace win32 {
+
+ class WMHooks : public MsgWindow {
+ public:
+ WMHooks();
+ ~WMHooks();
+
+ bool setClipRect(const Rect& cr);
+ bool setUpdateTracker(UpdateTracker* ut);
+
+ virtual LRESULT processMessage(UINT msg, WPARAM wParam, LPARAM lParam);
+
+#ifdef _DEBUG
+ // Get notifications of any messages in the given range, to any hooked window
+ void setDiagnosticRange(UINT min, UINT max);
+#endif
+
+ protected:
+ ClippedUpdateTracker* clipper;
+ Region clip_region;
+
+ void* fg_window;
+ Rect fg_window_rect;
+
+ public:
+ SimpleUpdateTracker new_changes;
+ bool notified;
+ };
+
+ class WMBlockInput {
+ public:
+ WMBlockInput();
+ ~WMBlockInput();
+ bool blockInputs(bool block);
+ protected:
+ bool active;
+ };
+
+ // - Legacy cursor handling support
+ class WMCursorHooks {
+ public:
+ WMCursorHooks();
+ ~WMCursorHooks();
+
+ bool start();
+
+ HCURSOR getCursor() const;
+ };
+
+ };
+
+};
+
+#endif // __RFB_WIN32_WM_HOOKS_H__
diff --git a/rfb_win32/WMNotifier.cxx b/rfb_win32/WMNotifier.cxx
new file mode 100644
index 00000000..9773abf5
--- /dev/null
+++ b/rfb_win32/WMNotifier.cxx
@@ -0,0 +1,57 @@
+/* Copyright (C) 2002-2003 RealVNC Ltd. All Rights Reserved.
+ *
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+ * USA.
+ */
+
+// -=- WMNotifier.cxx
+
+#include <rfb_win32/WMNotifier.h>
+#include <rfb_win32/WMShatter.h>
+#include <rfb_win32/MsgWindow.h>
+
+#include <rfb/LogWriter.h>
+
+using namespace rfb;
+using namespace rfb::win32;
+
+static LogWriter vlog("WMMonitor");
+
+
+WMMonitor::WMMonitor() : MsgWindow(_T("WMMonitor")) {
+}
+
+WMMonitor::~WMMonitor() {
+}
+
+
+LRESULT
+WMMonitor::processMessage(UINT msg, WPARAM wParam, LPARAM lParam) {
+ switch (msg) {
+ case WM_DISPLAYCHANGE:
+ if (notifier) {
+ notifier->notifyDisplayEvent(Notifier::DisplaySizeChanged);
+ notifier->notifyDisplayEvent(Notifier::DisplayPixelFormatChanged);
+ }
+ break;
+ case WM_SYSCOLORCHANGE:
+ case WM_PALETTECHANGED:
+ if (notifier) {
+ notifier->notifyDisplayEvent(Notifier::DisplayColourMapChanged);
+ }
+ break;
+ };
+ return MsgWindow::processMessage(msg, wParam, lParam);
+}
diff --git a/rfb_win32/WMNotifier.h b/rfb_win32/WMNotifier.h
new file mode 100644
index 00000000..564d176f
--- /dev/null
+++ b/rfb_win32/WMNotifier.h
@@ -0,0 +1,68 @@
+/* Copyright (C) 2002-2003 RealVNC Ltd. All Rights Reserved.
+ *
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+ * USA.
+ */
+
+// -=- WMNotifier.h
+//
+// The WMNotifier is used to get callbacks indicating changes in the state
+// of the system, for instance in the size/format/palette of the display.
+// The WMNotifier contains a Win32 window, which receives notifications of
+// system events and stores them. Whenever processEvent is called, any
+// incoming events are processed and the appropriate notifier called.
+
+#ifndef __RFB_WIN32_NOTIFIER_H__
+#define __RFB_WIN32_NOTIFIER_H__
+
+#include <rfb/SDesktop.h>
+#include <rfb/Threading.h>
+#include <rfb_win32/MsgWindow.h>
+#include <rfb_win32/DeviceFrameBuffer.h>
+#include <rfb_win32/SInput.h>
+
+namespace rfb {
+
+ namespace win32 {
+
+ // -=- Window Message Monitor implementation
+
+ class WMMonitor : MsgWindow {
+ public:
+
+ class Notifier {
+ public:
+ typedef enum {DisplaySizeChanged, DisplayColourMapChanged,
+ DisplayPixelFormatChanged} DisplayEventType;
+ virtual void notifyDisplayEvent(DisplayEventType evt) = 0;
+ };
+
+ WMMonitor();
+ virtual ~WMMonitor();
+
+ void setNotifier(Notifier* wmn) {notifier=wmn;}
+
+ protected:
+ // - Internal MsgWindow callback
+ virtual LRESULT processMessage(UINT msg, WPARAM wParam, LPARAM lParam);
+
+ Notifier* notifier;
+ };
+
+ };
+
+};
+
+#endif // __RFB_WIN32_WMNOTIFIER_H__
diff --git a/rfb_win32/WMPoller.cxx b/rfb_win32/WMPoller.cxx
new file mode 100644
index 00000000..f568b211
--- /dev/null
+++ b/rfb_win32/WMPoller.cxx
@@ -0,0 +1,101 @@
+/* Copyright (C) 2002-2003 RealVNC Ltd. All Rights Reserved.
+ *
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+ * USA.
+ */
+
+// -=- WMPoller.cxx
+
+#include <rfb_win32/WMPoller.h>
+#include <rfb/Exception.h>
+#include <rfb/LogWriter.h>
+#include <rfb/Configuration.h>
+
+#include <tchar.h>
+
+using namespace rfb;
+using namespace rfb::win32;
+
+static LogWriter vlog("WMPoller");
+
+BoolParameter rfb::win32::WMPoller::poll_console_windows("PollConsoleWindows",
+ "Server should poll console windows for updates", true);
+
+// -=- WMPoller class
+
+rfb::win32::WMPoller::WMPoller() : clipper(0) {
+}
+
+rfb::win32::WMPoller::~WMPoller() {
+ if (clipper) delete clipper;
+}
+
+bool
+rfb::win32::WMPoller::processEvent() {
+ PollInfo info;
+ if (clipper && poll_console_windows) {
+ ::EnumWindows(WMPoller::enumWindowProc, (LPARAM) &info);
+ clipper->add_changed(info.poll_include);
+ }
+ return false;
+}
+
+bool
+rfb::win32::WMPoller::setClipRect(const Rect& r) {
+ clip_region = r;
+ if (clipper) clipper->set_clip_region(clip_region);
+ return true;
+}
+
+bool
+rfb::win32::WMPoller::setUpdateTracker(UpdateTracker* ut) {
+ if (clipper) delete clipper;
+ clipper = new ClippedUpdateTracker(*ut);
+ clipper->set_clip_region(clip_region);
+ return true;
+}
+
+bool
+rfb::win32::WMPoller::checkPollWindow(HWND w) {
+ TCHAR buffer[128];
+ if (!GetClassName(w, buffer, 128))
+ throw rdr::SystemException("unable to get window class:%u", GetLastError());
+ if ((_tcscmp(buffer, _T("tty")) != 0) &&
+ (_tcscmp(buffer, _T("ConsoleWindowClass")) != 0)) {
+ return false;
+ }
+ return true;
+}
+
+void
+rfb::win32::WMPoller::pollWindow(HWND w, PollInfo* i) {
+ RECT r;
+ if (IsWindowVisible(w) && GetWindowRect(w, &r)) {
+ if (IsRectEmpty(&r)) return;
+ Region wrgn(Rect(r.left, r.top, r.right, r.bottom));
+ if (checkPollWindow(w)) {
+ wrgn.assign_subtract(i->poll_exclude);
+ i->poll_include.assign_union(wrgn);
+ } else {
+ i->poll_exclude.assign_union(wrgn);
+ }
+ }
+}
+
+BOOL CALLBACK
+rfb::win32::WMPoller::enumWindowProc(HWND w, LPARAM lp) {
+ pollWindow(w, (PollInfo*)lp);
+ return TRUE;
+}
diff --git a/rfb_win32/WMPoller.h b/rfb_win32/WMPoller.h
new file mode 100644
index 00000000..3f3f402a
--- /dev/null
+++ b/rfb_win32/WMPoller.h
@@ -0,0 +1,67 @@
+/* Copyright (C) 2002-2003 RealVNC Ltd. All Rights Reserved.
+ *
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+ * USA.
+ */
+
+// -=- WMPoller.h
+//
+// Polls the foreground window. If the pollOnlyConsoles flag is set,
+// then checks the window class of the foreground window first and
+// only polls it if it's a console.
+// If the pollAllWindows flag is set then iterates through visible
+// windows, and polls the visible bits. If pollOnlyConsoles is also
+// set then only visible parts of console windows will be polled.
+
+#ifndef __RFB_WIN32_WM_POLLER_H__
+#define __RFB_WIN32_WM_POLLER_H__
+
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+#include <rfb/UpdateTracker.h>
+#include <rfb/Configuration.h>
+
+namespace rfb {
+
+ namespace win32 {
+
+ class WMPoller {
+ public:
+ WMPoller();
+ ~WMPoller();
+
+ bool processEvent();
+ bool setClipRect(const Rect& cr);
+ bool setUpdateTracker(UpdateTracker* ut);
+
+ static BoolParameter poll_console_windows;
+ protected:
+ struct PollInfo {
+ Region poll_include;
+ Region poll_exclude;
+ };
+ static bool checkPollWindow(HWND w);
+ static void pollWindow(HWND w, PollInfo* info);
+ static BOOL CALLBACK enumWindowProc(HWND w, LPARAM lp);
+
+ ClippedUpdateTracker* clipper;
+ Region clip_region;
+ };
+
+ };
+
+};
+
+#endif // __RFB_WIN32_WM_POLLER_H__
diff --git a/rfb_win32/WMShatter.cxx b/rfb_win32/WMShatter.cxx
new file mode 100644
index 00000000..f6a74848
--- /dev/null
+++ b/rfb_win32/WMShatter.cxx
@@ -0,0 +1,57 @@
+/* Copyright (C) 2002-2003 RealVNC Ltd. All Rights Reserved.
+ *
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+ * USA.
+ */
+
+// -=- WMShatter.cxx
+
+#include <rfb_win32/WMShatter.h>
+
+#include <rfb/LogWriter.h>
+
+using namespace rfb;
+using namespace rfb::win32;
+
+static LogWriter vlog("WMShatter");
+
+bool
+rfb::win32::IsSafeWM(HWND window, UINT msg, WPARAM wParam, LPARAM lParam) {
+ bool result = true;
+ switch (msg) {
+ // - UNSAFE MESSAGES
+ case WM_TIMER:
+ result = lParam == 0;
+ break;
+ };
+ if (!result) {
+ vlog.info("IsSafeWM: 0x%x received 0x%x(%u, %lu) - not safe", window, msg, wParam, lParam);
+ }
+ return result;
+}
+
+LRESULT
+rfb::win32::SafeDefWindowProc(HWND window, UINT msg, WPARAM wParam, LPARAM lParam) {
+ if (IsSafeWM(window, msg, wParam, lParam))
+ return DefWindowProc(window, msg, wParam, lParam);
+ return 0;
+}
+
+LRESULT
+rfb::win32::SafeDispatchMessage(const MSG* msg) {
+ if (IsSafeWM(msg->hwnd, msg->message, msg->wParam, msg->lParam))
+ return DispatchMessage(msg);
+ return 0;
+}
diff --git a/rfb_win32/WMShatter.h b/rfb_win32/WMShatter.h
new file mode 100644
index 00000000..7b81678f
--- /dev/null
+++ b/rfb_win32/WMShatter.h
@@ -0,0 +1,53 @@
+/* Copyright (C) 2002-2003 RealVNC Ltd. All Rights Reserved.
+ *
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+ * USA.
+ */
+
+// -=- WMShatter.h
+//
+// WMShatter provides the IsSafeWM routine, which returns true iff the
+// supplied window message is safe to pass to DispatchMessage, or to
+// process in the window procedure.
+//
+// This is only required, of course, to avoid so-called "shatter" attacks
+// to be made against the VNC server, which take advantage of the noddy
+// design of the Win32 window messaging system.
+//
+// The API here is designed to hopefully be future proof, so that if they
+// ever come up with a proper way to determine whether a message is safe
+// or not then it can just be reimplemented here...
+
+#ifndef __RFB_WIN32_SHATTER_H__
+#define __RFB_WIN32_SHATTER_H__
+
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+
+namespace rfb {
+
+ namespace win32 {
+
+ bool IsSafeWM(HWND window, UINT msg, WPARAM wParam, LPARAM lParam);
+
+ LRESULT SafeDefWindowProc(HWND wnd, UINT msg, WPARAM wParam, LPARAM lParam);
+
+ LRESULT SafeDispatchMessage(const MSG* msg);
+
+ };
+
+};
+
+#endif // __RFB_WIN32_SHATTER_H__
diff --git a/rfb_win32/WMWindowCopyRect.cxx b/rfb_win32/WMWindowCopyRect.cxx
new file mode 100644
index 00000000..46d85eac
--- /dev/null
+++ b/rfb_win32/WMWindowCopyRect.cxx
@@ -0,0 +1,83 @@
+/* Copyright (C) 2002-2003 RealVNC Ltd. All Rights Reserved.
+ *
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+ * USA.
+ */
+
+// -=- WMCopyRect.cxx
+
+#include <rfb_win32/WMWindowCopyRect.h>
+#include <rfb/LogWriter.h>
+#include <windows.h>
+
+using namespace rfb;
+using namespace rfb::win32;
+
+static LogWriter vlog("WMCopyRect");
+
+// -=- WMHooks class
+
+rfb::win32::WMCopyRect::WMCopyRect() : clipper(0), fg_window(0) {
+}
+
+rfb::win32::WMCopyRect::~WMCopyRect() {
+ if (clipper) delete clipper;
+}
+
+bool
+rfb::win32::WMCopyRect::processEvent() {
+ if (clipper) {
+ // See if the foreground window has moved
+ HWND window = GetForegroundWindow();
+ if (window) {
+ RECT wrect;
+ if (IsWindow(window) && IsWindowVisible(window) && GetWindowRect(window, &wrect)) {
+ Rect winrect(wrect.left, wrect.top, wrect.right, wrect.bottom);
+ if (fg_window == window) {
+
+ if (!fg_window_rect.tl.equals(winrect.tl)) {
+ // Window has moved - send a copyrect event to the client
+ Point delta = Point(winrect.tl.x-fg_window_rect.tl.x, winrect.tl.y-fg_window_rect.tl.y);
+ Region copy_dest = winrect;
+ clipper->add_copied(copy_dest, delta);
+ clipper->add_changed(Region(fg_window_rect).subtract(copy_dest));
+ }
+ }
+ fg_window = window;
+ fg_window_rect = winrect;
+ } else {
+ fg_window = 0;
+ }
+ } else {
+ fg_window = 0;
+ }
+ }
+ return false;
+}
+
+bool
+rfb::win32::WMCopyRect::setClipRect(const Rect& r) {
+ clip_region = r;
+ if (clipper) clipper->set_clip_region(clip_region);
+ return true;
+}
+
+bool
+rfb::win32::WMCopyRect::setUpdateTracker(UpdateTracker* ut) {
+ if (clipper) delete clipper;
+ clipper = new ClippedUpdateTracker(*ut);
+ clipper->set_clip_region(clip_region);
+ return true;
+}
diff --git a/rfb_win32/WMWindowCopyRect.h b/rfb_win32/WMWindowCopyRect.h
new file mode 100644
index 00000000..0750d86e
--- /dev/null
+++ b/rfb_win32/WMWindowCopyRect.h
@@ -0,0 +1,56 @@
+/* Copyright (C) 2002-2003 RealVNC Ltd. All Rights Reserved.
+ *
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+ * USA.
+ */
+
+// -=- WMWindowCopyRect.h
+//
+// Helper class which produces copyRect actions by monitoring the location
+// of the current foreground window.
+// Whenever processEvent is called, the foreground window's position is
+// recalculated and a copy event flushed to the supplied UpdateTracker
+// if appropriate.
+
+#ifndef __RFB_WIN32_WM_WINDOW_COPYRECT_H__
+#define __RFB_WIN32_WM_WINDOW_COPYRECT_H__
+
+#include <rfb/UpdateTracker.h>
+
+namespace rfb {
+
+ namespace win32 {
+
+ class WMCopyRect {
+ public:
+ WMCopyRect();
+ ~WMCopyRect();
+
+ bool processEvent();
+ bool setClipRect(const Rect& cr);
+ bool setUpdateTracker(UpdateTracker* ut);
+
+ protected:
+ ClippedUpdateTracker* clipper;
+ Region clip_region;
+ void* fg_window;
+ Rect fg_window_rect;
+ };
+
+ };
+
+};
+
+#endif // __RFB_WIN32_WM_WINDOW_COPYRECT_H__
diff --git a/rfb_win32/Win32Util.cxx b/rfb_win32/Win32Util.cxx
new file mode 100644
index 00000000..e25f43ad
--- /dev/null
+++ b/rfb_win32/Win32Util.cxx
@@ -0,0 +1,447 @@
+/* Copyright (C) 2002-2004 RealVNC Ltd. All Rights Reserved.
+ *
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+ * USA.
+ */
+
+// Win32Util.cxx
+
+#include <rfb_win32/Win32Util.h>
+#include <rdr/Exception.h>
+#include <rdr/HexOutStream.h>
+
+
+namespace rfb {
+namespace win32 {
+
+LogicalPalette::LogicalPalette() : palette(0), numEntries(0) {
+ BYTE buf[sizeof(LOGPALETTE)+256*sizeof(PALETTEENTRY)];
+ LOGPALETTE* logpal = (LOGPALETTE*)buf;
+ logpal->palVersion = 0x300;
+ logpal->palNumEntries = 256;
+ for (int i=0; i<256;i++) {
+ logpal->palPalEntry[i].peRed = 0;
+ logpal->palPalEntry[i].peGreen = 0;
+ logpal->palPalEntry[i].peBlue = 0;
+ logpal->palPalEntry[i].peFlags = 0;
+ }
+ palette = CreatePalette(logpal);
+ if (!palette)
+ throw rdr::SystemException("failed to CreatePalette", GetLastError());
+}
+
+LogicalPalette::~LogicalPalette() {
+ if (palette)
+ if (!DeleteObject(palette))
+ throw rdr::SystemException("del palette failed", GetLastError());
+}
+
+void LogicalPalette::setEntries(int start, int count, const Colour* cols) {
+ if (numEntries < count) {
+ ResizePalette(palette, start+count);
+ numEntries = start+count;
+ }
+ PALETTEENTRY* logpal = new PALETTEENTRY[count];
+ for (int i=0; i<count; i++) {
+ logpal[i].peRed = cols[i].r >> 8;
+ logpal[i].peGreen = cols[i].g >> 8;
+ logpal[i].peBlue = cols[i].b >> 8;
+ logpal[i].peFlags = 0;
+ }
+ UnrealizeObject(palette);
+ SetPaletteEntries(palette, start, count, logpal);
+ delete [] logpal;
+}
+
+
+static LogWriter dcLog("DeviceContext");
+
+PixelFormat DeviceContext::getPF() const {
+ return getPF(dc);
+}
+
+PixelFormat DeviceContext::getPF(HDC dc) {
+ PixelFormat format;
+ CompatibleBitmap bitmap(dc, 1, 1);
+
+ // -=- Get the bitmap format information
+ BitmapInfo bi;
+ memset(&bi, 0, sizeof(bi));
+ bi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
+ bi.bmiHeader.biBitCount = 0;
+
+ if (!::GetDIBits(dc, bitmap, 0, 1, NULL, (BITMAPINFO*)&bi, DIB_RGB_COLORS)) {
+ throw rdr::SystemException("unable to determine device pixel format", GetLastError());
+ }
+ if (!::GetDIBits(dc, bitmap, 0, 1, NULL, (BITMAPINFO*)&bi, DIB_RGB_COLORS)) {
+ throw rdr::SystemException("unable to determine pixel shifts/palette", GetLastError());
+ }
+
+ // -=- Munge the bitmap info here
+ switch (bi.bmiHeader.biBitCount) {
+ case 1:
+ case 4:
+ bi.bmiHeader.biBitCount = 8;
+ break;
+ case 24:
+ bi.bmiHeader.biBitCount = 32;
+ break;
+ }
+ bi.bmiHeader.biPlanes = 1;
+
+ format.trueColour = bi.bmiHeader.biBitCount > 8;
+ format.bigEndian = 0;
+ format.bpp = format.depth = bi.bmiHeader.biBitCount;
+
+ if (format.trueColour) {
+ DWORD rMask=0, gMask=0, bMask=0;
+
+ // Which true colour format is the DIB section using?
+ switch (bi.bmiHeader.biCompression) {
+ case BI_RGB:
+ // Default RGB layout
+ switch (bi.bmiHeader.biBitCount) {
+ case 16:
+ // RGB 555 - High Colour
+ dcLog.info("16-bit High Colour");
+ rMask = 0x7c00;
+ bMask = 0x001f;
+ gMask = 0x03e0;
+ format.depth = 15;
+ break;
+ case 24:
+ case 32:
+ // RGB 888 - True Colour
+ dcLog.info("24/32-bit High Colour");
+ rMask = 0xff0000;
+ gMask = 0x00ff00;
+ bMask = 0x0000ff;
+ format.depth = 24;
+ break;
+ default:
+ dcLog.error("bits per pixel %u not supported", bi.bmiHeader.biBitCount);
+ throw rdr::Exception("unknown bits per pixel specified");
+ };
+ break;
+ case BI_BITFIELDS:
+ // Custom RGB layout
+ rMask = bi.mask.red;
+ gMask = bi.mask.green;
+ bMask = bi.mask.blue;
+ dcLog.info("BitFields format: %lu, (%lx, %lx, %lx)",
+ bi.bmiHeader.biBitCount, rMask, gMask, bMask);
+ if (format.bpp == 32)
+ format.depth = 24; // ...probably
+ break;
+ };
+
+ // Convert the data we just retrieved
+ initMaxAndShift(rMask, &format.redMax, &format.redShift);
+ initMaxAndShift(gMask, &format.greenMax, &format.greenShift);
+ initMaxAndShift(bMask, &format.blueMax, &format.blueShift);
+ }
+
+ return format;
+}
+
+
+WindowDC::WindowDC(HWND wnd) : hwnd(wnd) {
+ dc = GetDC(wnd);
+ if (!dc)
+ throw rdr::SystemException("GetDC failed", GetLastError());
+}
+WindowDC::~WindowDC() {
+ if (dc)
+ ReleaseDC(hwnd, dc);
+}
+
+
+CompatibleDC::CompatibleDC(HDC existing) {
+ dc = CreateCompatibleDC(existing);
+ if (!dc)
+ throw rdr::SystemException("CreateCompatibleDC failed", GetLastError());
+}
+CompatibleDC::~CompatibleDC() {
+ if (dc)
+ DeleteDC(dc);
+}
+
+
+BitmapDC::BitmapDC(HDC hdc, HBITMAP hbitmap) : CompatibleDC(hdc){
+ oldBitmap = (HBITMAP)SelectObject(dc, hbitmap);
+ if (!oldBitmap)
+ throw rdr::SystemException("SelectObject to CompatibleDC failed",
+ GetLastError());
+}
+BitmapDC::~BitmapDC() {
+ SelectObject(dc, oldBitmap);
+}
+
+
+CompatibleBitmap::CompatibleBitmap(HDC hdc, int width, int height) {
+ hbmp = CreateCompatibleBitmap(hdc, width, height);
+ if (!hbmp)
+ throw rdr::SystemException("CreateCompatibleBitmap() failed",
+ GetLastError());
+}
+CompatibleBitmap::~CompatibleBitmap() {
+ if (hbmp) DeleteObject(hbmp);
+}
+
+
+PaletteSelector::PaletteSelector(HDC dc, HPALETTE pal) : device(dc), redrawRequired(false) {
+ oldPal = SelectPalette(dc, pal, FALSE);
+ redrawRequired = RealizePalette(dc) > 0;
+}
+PaletteSelector::~PaletteSelector() {
+ if (oldPal) SelectPalette(device, oldPal, TRUE);
+}
+
+
+IconInfo::IconInfo(HICON icon) {
+ if (!GetIconInfo(icon, this))
+ throw rdr::SystemException("GetIconInfo() failed", GetLastError());
+}
+IconInfo::~IconInfo() {
+ if (hbmColor)
+ DeleteObject(hbmColor);
+ if (hbmMask)
+ DeleteObject(hbmMask);
+}
+
+
+ModuleFileName::ModuleFileName(HMODULE module) : TCharArray(MAX_PATH) {
+ if (!module) module = GetModuleHandle(0);
+ if (!GetModuleFileName(module, buf, MAX_PATH))
+ buf[0] = 0;
+}
+
+
+FileVersionInfo::FileVersionInfo(const TCHAR* filename) {
+ // Get executable name
+ ModuleFileName exeName;
+ if (!filename) filename = exeName.buf;
+
+ // Get version info size
+ DWORD handle;
+ int size = GetFileVersionInfoSize((TCHAR*)filename, &handle);
+ if (!size)
+ throw rdr::SystemException("GetVersionInfoSize failed", GetLastError());
+
+ // Get version info
+ buf = new TCHAR[size];
+ if (!GetFileVersionInfo((TCHAR*)filename, handle, size, buf))
+ throw rdr::SystemException("GetVersionInfo failed", GetLastError());
+}
+
+const TCHAR* FileVersionInfo::getVerString(const TCHAR* name, DWORD langId) {
+ char langIdBuf[sizeof(langId)];
+ for (int i=sizeof(langIdBuf)-1; i>=0; i--) {
+ langIdBuf[i] = langId & 0xff;
+ langId = langId >> 8;
+ }
+
+ TCharArray langIdStr = rdr::HexOutStream::binToHexStr(langIdBuf, sizeof(langId));
+ TCharArray infoName(_tcslen(_T("StringFileInfo")) + 4 + _tcslen(name) + _tcslen(langIdStr.buf));
+ _stprintf(infoName.buf, _T("\\StringFileInfo\\%s\\%s"), langIdStr.buf, name);
+
+ // Locate the required version string within the version info
+ TCHAR* buffer = 0;
+ UINT length = 0;
+ if (!VerQueryValue(buf, infoName.buf, (void**)&buffer, &length)) {
+ printf("unable to find %s version string", CStr(infoName.buf));
+ throw rdr::Exception("VerQueryValue failed");
+ }
+ return buffer;
+}
+
+
+bool splitPath(const TCHAR* path, TCHAR** dir, TCHAR** file) {
+ return tstrSplit(path, '\\', dir, file, true);
+}
+
+
+static LogWriter dfbLog("DynamicFn");
+
+DynamicFnBase::DynamicFnBase(const TCHAR* dllName, const char* fnName) : dllHandle(0), fnPtr(0) {
+ dllHandle = LoadLibrary(dllName);
+ if (!dllHandle) {
+ dfbLog.info("DLL %s not found (%d)", (const char*)CStr(dllName), GetLastError());
+ return;
+ }
+ fnPtr = GetProcAddress(dllHandle, fnName);
+ if (!fnPtr)
+ dfbLog.info("proc %s not found in %s (%d)", fnName, (const char*)CStr(dllName), GetLastError());
+}
+
+DynamicFnBase::~DynamicFnBase() {
+ if (dllHandle)
+ FreeLibrary(dllHandle);
+}
+
+
+static LogWriter miLog("MonitorInfo");
+
+MonitorInfo::MonitorInfo(HWND window) {
+#if (WINVER >= 0x0500)
+ typedef HMONITOR (WINAPI *_MonitorFromWindow_proto)(HWND,DWORD);
+ rfb::win32::DynamicFn<_MonitorFromWindow_proto> _MonitorFromWindow(_T("user32.dll"), "MonitorFromWindow");
+ typedef BOOL (WINAPI *_GetMonitorInfo_proto)(HMONITOR,LPMONITORINFO);
+ rfb::win32::DynamicFn<_GetMonitorInfo_proto> _GetMonitorInfo(_T("user32.dll"), "GetMonitorInfoA");
+
+ // Can we dynamically link to the monitor functions?
+ if (_MonitorFromWindow.isValid()) {
+ if (_GetMonitorInfo.isValid()) {
+ HMONITOR monitor = (*_MonitorFromWindow)(window, MONITOR_DEFAULTTONEAREST);
+ miLog.debug("monitor=%lx", monitor);
+ if (monitor) {
+ memset(this, 0, sizeof(MONITORINFOEXA));
+ cbSize = sizeof(MONITORINFOEXA);
+ if ((*_GetMonitorInfo)(monitor, this)) {
+ miLog.debug("monitor is %d,%d-%d,%d", rcMonitor.left, rcMonitor.top, rcMonitor.right, rcMonitor.bottom);
+ miLog.debug("work area is %d,%d-%d,%d", rcWork.left, rcWork.top, rcWork.right, rcWork.bottom);
+ miLog.debug("device is \"%s\"", szDevice);
+ return;
+ }
+ miLog.error("failed to get monitor info: %ld", GetLastError());
+ }
+ } else {
+ miLog.debug("GetMonitorInfo not found");
+ }
+ } else {
+ miLog.debug("MonitorFromWindow not found");
+ }
+#else
+#pragma message ("not building in GetMonitorInfo")
+ cbSize = sizeof(MonitorInfo);
+ szDevice[0] = 0;
+#endif
+
+ // Legacy fallbacks - just return the desktop settings
+ miLog.debug("using legacy fall-backs");
+ HWND desktop = GetDesktopWindow();
+ GetWindowRect(desktop, &rcMonitor);
+ SystemParametersInfo(SPI_GETWORKAREA, 0, &rcWork, 0);
+ dwFlags = 0;
+}
+
+
+#if (WINVER >= 0x0500)
+
+struct moveToMonitorData {
+ HWND window;
+ const char* monitorName;
+};
+
+typedef BOOL (WINAPI *_GetMonitorInfo_proto)(HMONITOR,LPMONITORINFO);
+static rfb::win32::DynamicFn<_GetMonitorInfo_proto> _GetMonitorInfo(_T("user32.dll"), "GetMonitorInfoA");
+
+static BOOL CALLBACK moveToMonitorEnumProc(HMONITOR monitor,
+ HDC dc,
+ LPRECT pos,
+ LPARAM d) {
+ moveToMonitorData* data = (moveToMonitorData*)d;
+ MONITORINFOEXA info;
+ memset(&info, 0, sizeof(info));
+ info.cbSize = sizeof(info);
+
+ if ((*_GetMonitorInfo)(monitor, &info)) {
+ if (stricmp(data->monitorName, info.szDevice) == 0) {
+ SetWindowPos(data->window, 0,
+ info.rcMonitor.left, info.rcMonitor.top,
+ info.rcMonitor.right, info.rcMonitor.bottom,
+ SWP_NOZORDER | SWP_NOSIZE | SWP_NOACTIVATE | SWP_NOOWNERZORDER);
+ return FALSE;
+ }
+ }
+
+ return TRUE;
+}
+
+#endif
+
+void moveToMonitor(HWND handle, const char* device) {
+ miLog.debug("moveToMonitor %s", device);
+
+#if (WINVER >= 0x500)
+ typedef BOOL (WINAPI *_EnumDisplayMonitors_proto)(HDC, LPCRECT, MONITORENUMPROC, LPARAM);
+ rfb::win32::DynamicFn<_EnumDisplayMonitors_proto> _EnumDisplayMonitors(_T("user32.dll"), "EnumDisplayMonitors");
+ if (!_EnumDisplayMonitors.isValid()) {
+ miLog.debug("EnumDisplayMonitors not found");
+ return;
+ }
+
+ moveToMonitorData data;
+ data.window = handle;
+ data.monitorName = device;
+
+ (*_EnumDisplayMonitors)(0, 0, &moveToMonitorEnumProc, (LPARAM)&data);
+#endif
+}
+
+
+void centerWindow(HWND handle, HWND parent, bool clipToParent) {
+ RECT r;
+ if (parent && IsWindowVisible(parent)) {
+ if (!GetWindowRect(parent, &r)) return;
+ } else {
+ MonitorInfo mi(handle);
+ r=mi.rcWork;
+ }
+ centerWindow(handle, r, clipToParent);
+}
+
+void centerWindow(HWND handle, const RECT& r, bool clipToRect) {
+ RECT wr;
+ if (!GetWindowRect(handle, &wr)) return;
+ int w = wr.right-wr.left;
+ int h = wr.bottom-wr.top;
+ if (clipToRect) {
+ w = min(r.right-r.left, w);
+ h = min(r.bottom-r.top, h);
+ }
+ int x = (r.left + r.right - w)/2;
+ int y = (r.top + r.bottom - h)/2;
+ UINT flags = SWP_NOACTIVATE | SWP_NOOWNERZORDER | SWP_NOZORDER | (clipToRect ? 0 : SWP_NOSIZE);
+ SetWindowPos(handle, 0, x, y, w, h, flags);
+}
+
+
+int MsgBox(HWND parent, const TCHAR* msg, UINT flags) {
+ const TCHAR* msgType = 0;
+ UINT tflags = flags & 0x70;
+ if (tflags == MB_ICONHAND)
+ msgType = _T("Error");
+ else if (tflags == MB_ICONQUESTION)
+ msgType = _T("Question");
+ else if (tflags == MB_ICONEXCLAMATION)
+ msgType = _T("Warning");
+ else if (tflags == MB_ICONASTERISK)
+ msgType = _T("Information");
+ flags |= MB_TOPMOST | MB_SETFOREGROUND;
+ int len = _tcslen(AppName.buf) + 1;
+ if (msgType) len += _tcslen(msgType) + 3;
+ TCharArray title = new TCHAR[len];
+ _tcscpy(title.buf, AppName.buf);
+ if (msgType) {
+ _tcscat(title.buf, _T(" : "));
+ _tcscat(title.buf, msgType);
+ }
+ return MessageBox(parent, msg, title.buf, flags);
+}
+
+
+};
+};
diff --git a/rfb_win32/Win32Util.h b/rfb_win32/Win32Util.h
new file mode 100644
index 00000000..5f0ab5a0
--- /dev/null
+++ b/rfb_win32/Win32Util.h
@@ -0,0 +1,217 @@
+/* Copyright (C) 2002-2004 RealVNC Ltd. All Rights Reserved.
+ *
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+ * USA.
+ */
+
+// -=- Win32Util.h
+
+// Miscellaneous but useful Win32 API utility functions & classes.
+// In particular, a set of classes which wrap GDI objects,
+// and some to handle palettes.
+
+#ifndef __RFB_WIN32_GDIUTIL_H__
+#define __RFB_WIN32_GDIUTIL_H__
+
+#include <rfb/ColourMap.h>
+#include <rfb/PixelFormat.h>
+#include <rfb/Rect.h>
+#include <rfb_win32/TCharArray.h>
+
+namespace rfb {
+
+ namespace win32 {
+
+ class LogicalPalette {
+ public:
+ LogicalPalette();
+ ~LogicalPalette();
+ void setEntries(int start, int count, const Colour* cols);
+ HPALETTE getHandle() {return palette;}
+ protected:
+ HPALETTE palette;
+ int numEntries;
+ };
+
+ class DeviceContext {
+ public:
+ DeviceContext() : dc(0) {}
+ virtual ~DeviceContext() {}
+ operator HDC() const {return dc;}
+ PixelFormat getPF() const;
+ static PixelFormat getPF(HDC dc);
+ protected:
+ HDC dc;
+ };
+
+ class WindowDC : public DeviceContext {
+ public:
+ WindowDC(HWND wnd);
+ virtual ~WindowDC();
+ protected:
+ HWND hwnd;
+ };
+
+ class CompatibleDC : public DeviceContext {
+ public:
+ CompatibleDC(HDC existing);
+ virtual ~CompatibleDC();
+ };
+
+ class BitmapDC : public CompatibleDC {
+ public:
+ BitmapDC(HDC hdc, HBITMAP hbitmap);
+ ~BitmapDC();
+ protected:
+ HBITMAP oldBitmap;
+ };
+
+ class CompatibleBitmap {
+ public:
+ CompatibleBitmap(HDC hdc, int width, int height);
+ virtual ~CompatibleBitmap();
+ operator HBITMAP() const {return hbmp;}
+ protected:
+ HBITMAP hbmp;
+ };
+
+ struct BitmapInfo {
+ BITMAPINFOHEADER bmiHeader;
+ union {
+ struct {
+ DWORD red;
+ DWORD green;
+ DWORD blue;
+ } mask;
+ RGBQUAD color[256];
+ };
+ };
+
+ inline void initMaxAndShift(DWORD mask, int* max, int* shift) {
+ for ((*shift) = 0; (mask & 1) == 0; (*shift)++) mask >>= 1;
+ (*max) = (rdr::U16)mask;
+ }
+
+ class PaletteSelector {
+ public:
+ PaletteSelector(HDC dc, HPALETTE pal);
+ ~PaletteSelector();
+ bool isRedrawRequired() {return redrawRequired;}
+ protected:
+ HPALETTE oldPal;
+ HDC device;
+ bool redrawRequired;
+ };
+
+ struct IconInfo : public ICONINFO {
+ IconInfo(HICON icon);
+ ~IconInfo();
+ };
+
+ struct ModuleFileName : public TCharArray {
+ ModuleFileName(HMODULE module=0);
+ };
+
+ struct FileVersionInfo : public TCharArray {
+ FileVersionInfo(const TCHAR* filename=0);
+ const TCHAR* getVerString(const TCHAR* name, DWORD langId = 0x080904b0);
+ };
+
+ bool splitPath(const TCHAR* path, TCHAR** dir, TCHAR** file);
+
+ class DynamicFnBase {
+ public:
+ DynamicFnBase(const TCHAR* dllName, const char* fnName);
+ ~DynamicFnBase();
+ bool isValid() const {return fnPtr != 0;}
+ protected:
+ void* fnPtr;
+ HMODULE dllHandle;
+ };
+
+ template<class T> class DynamicFn : public DynamicFnBase {
+ public:
+ DynamicFn(const TCHAR* dllName, const char* fnName) : DynamicFnBase(dllName, fnName) {}
+ T operator *() const {return (T)fnPtr;};
+ };
+
+ // Structure containing info on the monitor nearest the window.
+ // Copes with multi-monitor OSes and older ones.
+#if (WINVER >= 0x0500)
+ struct MonitorInfo : MONITORINFOEXA {
+ MonitorInfo(HWND hwnd);
+ };
+#else
+ struct MonitorInfo {
+ MonitorInfo(HWND hwnd);
+ DWORD cbSize;
+ RECT rcMonitor;
+ RECT rcWork;
+ DWORD dwFlags;
+ char szDevice[1]; // Always null...
+ };
+#endif
+ void moveToMonitor(HWND handle, const char* device);
+
+ class Handle {
+ public:
+ Handle(HANDLE h_=0) : h(h_) {}
+ ~Handle() {
+ if (h) CloseHandle(h);
+ }
+ operator HANDLE() {return h;}
+ HANDLE h;
+ };
+
+ // Center the window to a rectangle, or to a parent window.
+ // Optionally, resize the window to lay within the rect or parent window
+ // If the parent window is NULL then the working area if the window's
+ // current monitor is used instead.
+ void centerWindow(HWND handle, const RECT& r, bool clipToRect=false);
+ void centerWindow(HWND handle, HWND parent, bool clipToRect=false);
+
+ // MsgBox helper function. Define rfb::win32::AppName somewhere in your
+ // code and MsgBox will use its value in informational messages.
+ extern TStr AppName;
+ int MsgBox(HWND parent, const TCHAR* message, UINT flags);
+
+ // Get the computer name
+ struct ComputerName : TCharArray {
+ ComputerName() : TCharArray(MAX_COMPUTERNAME_LENGTH+1) {
+ ULONG namelength = MAX_COMPUTERNAME_LENGTH+1;
+ if (!GetComputerName(buf, &namelength))
+ _tcscpy(buf, _T(""));
+ }
+ };
+
+ // Allocate and/or manage LocalAlloc memory.
+ struct LocalMem {
+ LocalMem(int size) : ptr(LocalAlloc(LMEM_FIXED, size)) {
+ if (!ptr) throw rdr::SystemException("LocalAlloc", GetLastError());
+ }
+ LocalMem(void* p) : ptr(p) {}
+ ~LocalMem() {LocalFree(ptr);}
+ operator void*() {return ptr;}
+ void* takePtr() {
+ void* t = ptr; ptr = 0; return t;
+ }
+ void* ptr;
+ };
+
+ };
+
+};
+
+#endif
diff --git a/rfb_win32/keymap.h b/rfb_win32/keymap.h
new file mode 100644
index 00000000..69ce66f2
--- /dev/null
+++ b/rfb_win32/keymap.h
@@ -0,0 +1,149 @@
+/* Copyright (C) 2002-2004 RealVNC Ltd. All Rights Reserved.
+ *
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+ * USA.
+ */
+
+// keymap.h - this file is shared between SInput.cxx and CKeyboard.cxx
+//
+// Mapping of X keysyms to and from Windows VK codes. Ordering here must be
+// such that when we look up a Windows VK code we get the preferred X keysym.
+// Going the other way there is no problem because an X keysym always maps to
+// exactly one Windows VK code. This map only contain keys which are not the
+// normal keys for printable ASCII characters. For example it does not contain
+// VK_SPACE (note that things like VK_ADD are for the plus key on the keypad,
+// not on the main keyboard).
+
+struct keymap_t {
+ rdr::U32 keysym;
+ rdr::U8 vk;
+ bool extended;
+};
+
+static keymap_t keymap[] = {
+
+ { XK_BackSpace, VK_BACK, 0 },
+ { XK_Tab, VK_TAB, 0 },
+ { XK_Clear, VK_CLEAR, 0 },
+ { XK_Return, VK_RETURN, 0 },
+ { XK_Pause, VK_PAUSE, 0 },
+ { XK_Escape, VK_ESCAPE, 0 },
+ { XK_Delete, VK_DELETE, 1 },
+
+ // Cursor control & motion
+
+ { XK_Home, VK_HOME, 1 },
+ { XK_Left, VK_LEFT, 1 },
+ { XK_Up, VK_UP, 1 },
+ { XK_Right, VK_RIGHT, 1 },
+ { XK_Down, VK_DOWN, 1 },
+ { XK_Page_Up, VK_PRIOR, 1 },
+ { XK_Page_Down, VK_NEXT, 1 },
+ { XK_End, VK_END, 1 },
+
+ // Misc functions
+
+ { XK_Select, VK_SELECT, 0 },
+ { XK_Print, VK_SNAPSHOT, 0 },
+ { XK_Execute, VK_EXECUTE, 0 },
+ { XK_Insert, VK_INSERT, 1 },
+ { XK_Help, VK_HELP, 0 },
+ { XK_Break, VK_CANCEL, 1 },
+
+ // Auxilliary Functions - must come before XK_KP_F1, etc
+
+ { XK_F1, VK_F1, 0 },
+ { XK_F2, VK_F2, 0 },
+ { XK_F3, VK_F3, 0 },
+ { XK_F4, VK_F4, 0 },
+ { XK_F5, VK_F5, 0 },
+ { XK_F6, VK_F6, 0 },
+ { XK_F7, VK_F7, 0 },
+ { XK_F8, VK_F8, 0 },
+ { XK_F9, VK_F9, 0 },
+ { XK_F10, VK_F10, 0 },
+ { XK_F11, VK_F11, 0 },
+ { XK_F12, VK_F12, 0 },
+ { XK_F13, VK_F13, 0 },
+ { XK_F14, VK_F14, 0 },
+ { XK_F15, VK_F15, 0 },
+ { XK_F16, VK_F16, 0 },
+ { XK_F17, VK_F17, 0 },
+ { XK_F18, VK_F18, 0 },
+ { XK_F19, VK_F19, 0 },
+ { XK_F20, VK_F20, 0 },
+ { XK_F21, VK_F21, 0 },
+ { XK_F22, VK_F22, 0 },
+ { XK_F23, VK_F23, 0 },
+ { XK_F24, VK_F24, 0 },
+
+ // Keypad Functions, keypad numbers
+
+ { XK_KP_Tab, VK_TAB, 0 },
+ { XK_KP_Enter, VK_RETURN, 1 },
+ { XK_KP_F1, VK_F1, 0 },
+ { XK_KP_F2, VK_F2, 0 },
+ { XK_KP_F3, VK_F3, 0 },
+ { XK_KP_F4, VK_F4, 0 },
+ { XK_KP_Home, VK_HOME, 0 },
+ { XK_KP_Left, VK_LEFT, 0 },
+ { XK_KP_Up, VK_UP, 0 },
+ { XK_KP_Right, VK_RIGHT, 0 },
+ { XK_KP_Down, VK_DOWN, 0 },
+ { XK_KP_End, VK_END, 0 },
+ { XK_KP_Page_Up, VK_PRIOR, 0 },
+ { XK_KP_Page_Down, VK_NEXT, 0 },
+ { XK_KP_Begin, VK_CLEAR, 0 },
+ { XK_KP_Insert, VK_INSERT, 0 },
+ { XK_KP_Delete, VK_DELETE, 0 },
+ { XK_KP_Multiply, VK_MULTIPLY, 0 },
+ { XK_KP_Add, VK_ADD, 0 },
+ { XK_KP_Separator, VK_SEPARATOR, 0 },
+ { XK_KP_Subtract, VK_SUBTRACT, 0 },
+ { XK_KP_Decimal, VK_DECIMAL, 0 },
+ { XK_KP_Divide, VK_DIVIDE, 1 },
+
+ { XK_KP_0, VK_NUMPAD0, 0 },
+ { XK_KP_1, VK_NUMPAD1, 0 },
+ { XK_KP_2, VK_NUMPAD2, 0 },
+ { XK_KP_3, VK_NUMPAD3, 0 },
+ { XK_KP_4, VK_NUMPAD4, 0 },
+ { XK_KP_5, VK_NUMPAD5, 0 },
+ { XK_KP_6, VK_NUMPAD6, 0 },
+ { XK_KP_7, VK_NUMPAD7, 0 },
+ { XK_KP_8, VK_NUMPAD8, 0 },
+ { XK_KP_9, VK_NUMPAD9, 0 },
+
+ // Modifiers
+
+ { XK_Shift_L, VK_SHIFT, 0 },
+ { XK_Shift_R, VK_SHIFT, 0 },
+ { XK_Control_L, VK_CONTROL, 0 },
+ { XK_Control_R, VK_CONTROL, 1 },
+ { XK_Alt_L, VK_MENU, 0 },
+ { XK_Alt_R, VK_MENU, 1 },
+
+ // Left & Right Windows keys & Windows Menu Key
+
+ { XK_Super_L, VK_LWIN, 0 },
+ { XK_Super_R, VK_RWIN, 0 },
+ { XK_Menu, VK_APPS, 0 },
+
+ // Japanese stuff - almost certainly wrong...
+
+ { XK_Kanji, VK_KANJI, 0 },
+ { XK_Kana_Shift, VK_KANA, 0 },
+
+};
diff --git a/rfb_win32/msvcwarning.h b/rfb_win32/msvcwarning.h
new file mode 100644
index 00000000..e50d9c1a
--- /dev/null
+++ b/rfb_win32/msvcwarning.h
@@ -0,0 +1,20 @@
+/* Copyright (C) 2002-2003 RealVNC Ltd. All Rights Reserved.
+ *
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+ * USA.
+ */
+#pragma warning( disable : 4244 ) // loss of data e.g. int to char
+#pragma warning( disable : 4800 ) // forcing bool 'true' or 'false'
+#pragma warning( disable : 4786 ) // debug info truncated
diff --git a/rfb_win32/rfb_win32.dsp b/rfb_win32/rfb_win32.dsp
new file mode 100644
index 00000000..2118bcb2
--- /dev/null
+++ b/rfb_win32/rfb_win32.dsp
@@ -0,0 +1,341 @@
+# Microsoft Developer Studio Project File - Name="rfb_win32" - Package Owner=<4>
+# Microsoft Developer Studio Generated Build File, Format Version 6.00
+# ** DO NOT EDIT **
+
+# TARGTYPE "Win32 (x86) Static Library" 0x0104
+
+CFG=rfb_win32 - Win32 Debug Unicode
+!MESSAGE This is not a valid makefile. To build this project using NMAKE,
+!MESSAGE use the Export Makefile command and run
+!MESSAGE
+!MESSAGE NMAKE /f "rfb_win32.mak".
+!MESSAGE
+!MESSAGE You can specify a configuration when running NMAKE
+!MESSAGE by defining the macro CFG on the command line. For example:
+!MESSAGE
+!MESSAGE NMAKE /f "rfb_win32.mak" CFG="rfb_win32 - Win32 Debug Unicode"
+!MESSAGE
+!MESSAGE Possible choices for configuration are:
+!MESSAGE
+!MESSAGE "rfb_win32 - Win32 Release" (based on "Win32 (x86) Static Library")
+!MESSAGE "rfb_win32 - Win32 Debug" (based on "Win32 (x86) Static Library")
+!MESSAGE "rfb_win32 - Win32 Debug Unicode" (based on "Win32 (x86) Static Library")
+!MESSAGE
+
+# Begin Project
+# PROP AllowPerConfigDependencies 0
+# PROP Scc_ProjName ""
+# PROP Scc_LocalPath ""
+CPP=cl.exe
+RSC=rc.exe
+
+!IF "$(CFG)" == "rfb_win32 - Win32 Release"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 0
+# PROP BASE Output_Dir "Release"
+# PROP BASE Intermediate_Dir "Release"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 0
+# PROP Output_Dir "Release"
+# PROP Intermediate_Dir "Release"
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /YX /FD /c
+# ADD CPP /nologo /MT /W3 /GX /O2 /I ".." /FI"msvcwarning.h" /D "NDEBUG" /D "_LIB" /D "WIN32" /D "_MBCS" /YX /FD /c
+# ADD BASE RSC /l 0x809 /d "NDEBUG"
+# ADD RSC /l 0x809 /d "NDEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LIB32=link.exe -lib
+# ADD BASE LIB32 /nologo
+# ADD LIB32 /nologo
+
+!ELSEIF "$(CFG)" == "rfb_win32 - Win32 Debug"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 1
+# PROP BASE Output_Dir "Debug"
+# PROP BASE Intermediate_Dir "Debug"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 1
+# PROP Output_Dir "Debug"
+# PROP Intermediate_Dir "Debug"
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /YX /FD /GZ /c
+# ADD CPP /nologo /MTd /W3 /Gm /GX /Zi /Od /I ".." /FI"msvcwarning.h" /D "_DEBUG" /D "_LIB" /D "WIN32" /D "_MBCS" /YX /FD /GZ /c
+# ADD BASE RSC /l 0x809 /d "_DEBUG"
+# ADD RSC /l 0x809 /d "_DEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LIB32=link.exe -lib
+# ADD BASE LIB32 /nologo
+# ADD LIB32 /nologo
+
+!ELSEIF "$(CFG)" == "rfb_win32 - Win32 Debug Unicode"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 1
+# PROP BASE Output_Dir "rfb_win32___Win32_Debug_Unicode"
+# PROP BASE Intermediate_Dir "rfb_win32___Win32_Debug_Unicode"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 1
+# PROP Output_Dir "Debug_Unicode"
+# PROP Intermediate_Dir "Debug_Unicode"
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /MTd /W3 /Gm /GX /Zi /Od /I ".." /FI"msvcwarning.h" /D "_DEBUG" /D "_LIB" /D "WIN32" /D "_MBCS" /YX /FD /GZ /c
+# ADD CPP /nologo /MTd /W3 /Gm /GX /Zi /Od /I ".." /FI"msvcwarning.h" /D "_LIB" /D "_DEBUG" /D "WIN32" /D "_UNICODE" /D "UNICODE" /YX /FD /GZ /c
+# ADD BASE RSC /l 0x809 /d "_DEBUG"
+# ADD RSC /l 0x809 /d "_DEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LIB32=link.exe -lib
+# ADD BASE LIB32 /nologo
+# ADD LIB32 /nologo
+
+!ENDIF
+
+# Begin Target
+
+# Name "rfb_win32 - Win32 Release"
+# Name "rfb_win32 - Win32 Debug"
+# Name "rfb_win32 - Win32 Debug Unicode"
+# Begin Group "Source Files"
+
+# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat"
+# Begin Source File
+
+SOURCE=.\AboutDialog.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\CKeyboard.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\CleanDesktop.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\Clipboard.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\CPointer.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\CurrentUser.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\DeviceFrameBuffer.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\Dialog.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\DIBSectionBuffer.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\LaunchProcess.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\MsgWindow.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\OSVersion.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\RegConfig.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\Registry.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\SDisplay.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\Service.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\SInput.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\SocketManager.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\TCharArray.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\Win32Util.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\WMCursor.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\WMHooks.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\WMNotifier.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\WMPoller.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\WMShatter.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\WMWindowCopyRect.cxx
+# End Source File
+# End Group
+# Begin Group "Header Files"
+
+# PROP Default_Filter "h;hpp;hxx;hm;inl"
+# Begin Source File
+
+SOURCE=.\AboutDialog.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\CKeyboard.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\CleanDesktop.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\Clipboard.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\CPointer.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\CurrentUser.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\DeviceFrameBuffer.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\Dialog.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\DIBSectionBuffer.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\IntervalTimer.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\keymap.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\LaunchProcess.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\MsgWindow.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\OSVersion.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\RegConfig.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\Registry.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\SDisplay.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\Security.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\Service.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\SInput.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\SocketManager.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\TCharArray.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\TrayIcon.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\Win32Util.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\WMCursor.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\WMHooks.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\WMNotifier.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\WMPoller.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\WMShatter.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\WMWindowCopyRect.h
+# End Source File
+# End Group
+# End Target
+# End Project