Migrating to new directory structure adopted from the RealVNC's source tree. More changes will follow.
git-svn-id: svn://svn.code.sf.net/p/tigervnc/code/trunk@591 3789f03b-4d11-0410-bbf8-ca57d06f2519
diff --git a/win/rfb_win32/AboutDialog.cxx b/win/rfb_win32/AboutDialog.cxx
new file mode 100644
index 0000000..030be1b
--- /dev/null
+++ b/win/rfb_win32/AboutDialog.cxx
@@ -0,0 +1,49 @@
+/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
+ *
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+ * USA.
+ */
+
+#include <rfb_win32/AboutDialog.h>
+#include <rfb_win32/Win32Util.h>
+#include <rfb_win32/TCharArray.h>
+#include <rfb/LogWriter.h>
+
+using namespace rfb;
+using namespace rfb::win32;
+
+static LogWriter vlog("AboutDialog");
+
+AboutDialog AboutDialog::instance;
+
+
+AboutDialog::AboutDialog() : Dialog(GetModuleHandle(0)) {
+}
+
+bool AboutDialog::showDialog() {
+ return Dialog::showDialog(MAKEINTRESOURCE(DialogId));
+}
+
+void AboutDialog::initDialog() {
+ // Set the build time field
+ SetWindowText(GetDlgItem(handle, BuildTime), TStr(buildTime));
+
+ // Get our executable's version info
+ FileVersionInfo verInfo;
+
+ SetWindowText(GetDlgItem(handle, Version), verInfo.getVerString(_T("ProductVersion")));
+ SetWindowText(GetDlgItem(handle, Copyright), verInfo.getVerString(_T("LegalCopyright")));
+ SetWindowText(GetDlgItem(handle, Description), verInfo.getVerString(_T("ProductName")));
+}
diff --git a/win/rfb_win32/AboutDialog.h b/win/rfb_win32/AboutDialog.h
new file mode 100644
index 0000000..0dd9d49
--- /dev/null
+++ b/win/rfb_win32/AboutDialog.h
@@ -0,0 +1,55 @@
+/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
+ *
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+ * USA.
+ */
+
+// -=- AboutDialog.h
+
+#ifndef __RFB_WIN32_ABOUT_DIALOG_H__
+#define __RFB_WIN32_ABOUT_DIALOG_H__
+
+#include <rfb_win32/Dialog.h>
+#include <rfb/util.h>
+
+extern const char* buildTime;
+
+namespace rfb {
+
+ namespace win32 {
+
+ class AboutDialog : Dialog {
+ public:
+ AboutDialog();
+ virtual bool showDialog();
+ virtual void initDialog();
+
+ static AboutDialog instance;
+
+ typedef WORD LabelId;
+ static const LabelId DialogId; // Resource ID of the About dialog
+ static const LabelId BuildTime; // Resource ID of the BuildTime label in the dialog
+ static const LabelId Version; // etc...
+ static const LabelId Copyright;
+ static const LabelId Description;
+ protected:
+ WORD dialogId;
+ };
+
+ };
+
+};
+
+#endif
diff --git a/win/rfb_win32/BitmapInfo.h b/win/rfb_win32/BitmapInfo.h
new file mode 100644
index 0000000..6a6f0d2
--- /dev/null
+++ b/win/rfb_win32/BitmapInfo.h
@@ -0,0 +1,48 @@
+/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
+ *
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+ * USA.
+ */
+
+#ifndef __RFB_WIN32_BITMAP_INFO_H__
+#define __RFB_WIN32_BITMAP_INFO_H__
+
+#include <windows.h>
+#include <rdr/types.h>
+
+namespace rfb {
+ namespace win32 {
+
+ struct BitmapInfo {
+ BITMAPINFOHEADER bmiHeader;
+ union {
+ struct {
+ DWORD red;
+ DWORD green;
+ DWORD blue;
+ } mask;
+ RGBQUAD color[256];
+ };
+ };
+
+ inline void initMaxAndShift(DWORD mask, int* max, int* shift) {
+ for ((*shift) = 0; (mask & 1) == 0; (*shift)++) mask >>= 1;
+ (*max) = (rdr::U16)mask;
+ }
+
+ };
+};
+
+#endif
diff --git a/win/rfb_win32/CKeyboard.cxx b/win/rfb_win32/CKeyboard.cxx
new file mode 100644
index 0000000..28aceab
--- /dev/null
+++ b/win/rfb_win32/CKeyboard.cxx
@@ -0,0 +1,258 @@
+/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
+ *
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+ * USA.
+ */
+
+#include <map>
+
+#define XK_MISCELLANY
+#define XK_LATIN1
+#define XK_CURRENCY
+#include <rfb/keysymdef.h>
+
+#include <rfb_win32/CKeyboard.h>
+#include <rfb/LogWriter.h>
+#include <rfb_win32/OSVersion.h>
+#include "keymap.h"
+
+using namespace rfb;
+
+static LogWriter vlog("CKeyboard");
+
+
+// Client-side RFB keyboard event sythesis
+
+class CKeymapper {
+
+public:
+ CKeymapper()
+ {
+ for (int i = 0; i < sizeof(keymap) / sizeof(keymap_t); i++) {
+ int extendedVkey = keymap[i].vk + (keymap[i].extended ? 256 : 0);
+ if (keysymMap.find(extendedVkey) == keysymMap.end()) {
+ keysymMap[extendedVkey] = keymap[i].keysym;
+ }
+ }
+ }
+
+ // lookup() tries to find a match for vkey with the extended flag. We check
+ // first for an exact match including the extended flag, then try without the
+ // extended flag.
+ rdr::U32 lookup(int extendedVkey) {
+ if (keysymMap.find(extendedVkey) != keysymMap.end())
+ return keysymMap[extendedVkey];
+ if (keysymMap.find(extendedVkey ^ 256) != keysymMap.end())
+ return keysymMap[extendedVkey ^ 256];
+ return 0;
+ }
+
+private:
+ std::map<int,rdr::U32> keysymMap;
+} ckeymapper;
+
+
+class ModifierKeyReleaser {
+public:
+ ModifierKeyReleaser(InputHandler* writer_, int vkCode, bool extended)
+ : writer(writer_), extendedVkey(vkCode + (extended ? 256 : 0)),
+ keysym(0)
+ {}
+ void release(std::map<int,rdr::U32>* downKeysym) {
+ if (downKeysym->find(extendedVkey) != downKeysym->end()) {
+ keysym = (*downKeysym)[extendedVkey];
+ vlog.debug("fake release extendedVkey 0x%x, keysym 0x%x",
+ extendedVkey, keysym);
+ writer->keyEvent(keysym, false);
+ }
+ }
+ ~ModifierKeyReleaser() {
+ if (keysym) {
+ vlog.debug("fake press extendedVkey 0x%x, keysym 0x%x",
+ extendedVkey, keysym);
+ writer->keyEvent(keysym, true);
+ }
+ }
+ InputHandler* writer;
+ int extendedVkey;
+ rdr::U32 keysym;
+};
+
+// IS_PRINTABLE_LATIN1 tests if a character is either a printable latin1
+// character, or 128, which is the Euro symbol on Windows.
+#define IS_PRINTABLE_LATIN1(c) (((c) >= 32 && (c) <= 126) || (c) == 128 || \
+ ((c) >= 160 && (c) <= 255))
+
+void win32::CKeyboard::keyEvent(InputHandler* writer, rdr::U8 vkey,
+ rdr::U32 flags, bool down)
+{
+ bool extended = (flags & 0x1000000);
+ int extendedVkey = vkey + (extended ? 256 : 0);
+
+ // If it's a release, just release whichever keysym corresponded to the same
+ // key being pressed, regardless of how it would be interpreted in the
+ // current keyboard state.
+ if (!down) {
+ releaseKey(writer, extendedVkey);
+ return;
+ }
+
+ // We should always pass every down event to ToAscii() otherwise it can get
+ // out of sync.
+
+ // XXX should we pass CapsLock, ScrollLock or NumLock to ToAscii - they
+ // actually alter the lock state on the keyboard?
+
+ BYTE keystate[256];
+ GetKeyboardState(keystate);
+ rdr::U8 chars[2];
+
+ int nchars = ToAscii(vkey, 0, keystate, (WORD*)&chars, 0);
+
+ // See if it's in the Windows VK code -> X keysym map. We do this before
+ // looking at the result of ToAscii so that e.g. we recognise that it's
+ // XK_KP_Add rather than '+'.
+
+ rdr::U32 keysym = ckeymapper.lookup(extendedVkey);
+ if (keysym) {
+ vlog.debug("mapped key: extendedVkey 0x%x", extendedVkey);
+ pressKey(writer, extendedVkey, keysym);
+ return;
+ }
+
+ if (nchars < 0) {
+ // Dead key - the next call to ToAscii() will give us either the accented
+ // character or two characters.
+ vlog.debug("ToAscii dead key (1): extendedVkey 0x%x", extendedVkey);
+ return;
+ }
+
+ if (nchars > 0 && IS_PRINTABLE_LATIN1(chars[0])) {
+ // Got a printable latin1 character. We must release Control and Alt
+ // (AltGr) if they were both pressed, so that the latin1 character is seen
+ // without them by the VNC server.
+ ModifierKeyReleaser lctrl(writer, VK_CONTROL, 0);
+ ModifierKeyReleaser rctrl(writer, VK_CONTROL, 1);
+ ModifierKeyReleaser lalt(writer, VK_MENU, 0);
+ ModifierKeyReleaser ralt(writer, VK_MENU, 1);
+
+ if ((keystate[VK_CONTROL] & 0x80) && (keystate[VK_MENU] & 0x80)) {
+ lctrl.release(&downKeysym);
+ rctrl.release(&downKeysym);
+ lalt.release(&downKeysym);
+ ralt.release(&downKeysym);
+ }
+
+ for (int i = 0; i < nchars; i++) {
+ vlog.debug("ToAscii key (1): extendedVkey 0x%x", extendedVkey);
+ if (chars[i] == 128) { // special hack for euro!
+ pressKey(writer, extendedVkey, XK_EuroSign);
+ } else {
+ pressKey(writer, extendedVkey, chars[i]);
+ }
+ }
+ return;
+ }
+
+ // Either no chars were generated, or something outside the printable
+ // character range. Try ToAscii() without the Control and Alt keys down to
+ // see if that yields an ordinary character.
+
+ keystate[VK_CONTROL] = keystate[VK_LCONTROL] = keystate[VK_RCONTROL] = 0;
+ keystate[VK_MENU] = keystate[VK_LMENU] = keystate[VK_RMENU] = 0;
+
+ nchars = ToAscii(vkey, 0, keystate, (WORD*)&chars, 0);
+
+ if (nchars < 0) {
+ // So it would be a dead key if neither control nor alt were pressed.
+ // However, we know that at least one of control and alt must be pressed.
+ // We can't leave it at this stage otherwise the next call to ToAscii()
+ // with a valid character will get wrongly interpreted in the context of
+ // this bogus dead key. Working on the assumption that a dead key followed
+ // by space usually returns the dead character itself, try calling ToAscii
+ // with VK_SPACE.
+ vlog.debug("ToAscii dead key (2): extendedVkey 0x%x", extendedVkey);
+ nchars = ToAscii(VK_SPACE, 0, keystate, (WORD*)&chars, 0);
+ if (nchars < 0) {
+ vlog.debug("ToAscii dead key (3): extendedVkey 0x%x - giving up!",
+ extendedVkey);
+ return;
+ }
+ }
+
+ if (nchars > 0 && IS_PRINTABLE_LATIN1(chars[0])) {
+ for (int i = 0; i < nchars; i++) {
+ vlog.debug("ToAscii key (2) (no ctrl/alt): extendedVkey 0x%x",
+ extendedVkey);
+ if (chars[i] == 128) { // special hack for euro!
+ pressKey(writer, extendedVkey, XK_EuroSign);
+ } else {
+ pressKey(writer, extendedVkey, chars[i]);
+ }
+ }
+ return;
+ }
+
+ vlog.debug("no chars regardless of control and alt: extendedVkey 0x%x",
+ extendedVkey);
+}
+
+// releaseAllKeys() - write key release events to the server for all keys
+// that are currently regarded as being down.
+void win32::CKeyboard::releaseAllKeys(InputHandler* writer) {
+ std::map<int,rdr::U32>::iterator i, next_i;
+ for (i=downKeysym.begin(); i!=downKeysym.end(); i=next_i) {
+ next_i = i; next_i++;
+ writer->keyEvent((*i).second, false);
+ downKeysym.erase(i);
+ }
+}
+
+// releaseKey() - write a key up event to the server, but only if we've
+// actually sent a key down event for the given key. The key up event always
+// contains the same keysym we used in the key down event, regardless of what
+// it would look up as using the current keyboard state.
+void win32::CKeyboard::releaseKey(InputHandler* writer, int extendedVkey)
+{
+ if (downKeysym.find(extendedVkey) != downKeysym.end()) {
+ vlog.debug("release extendedVkey 0x%x, keysym 0x%x",
+ extendedVkey, downKeysym[extendedVkey]);
+ writer->keyEvent(downKeysym[extendedVkey], false);
+ downKeysym.erase(extendedVkey);
+ }
+}
+
+// pressKey() - write a key down event to the server, and record which keysym
+// was sent as corresponding to the given extendedVkey. The only tricky bit is
+// that if we are trying to press an extendedVkey which is already marked as
+// down but with a different keysym, then we need to release the old keysym
+// first. This can happen in two cases: (a) when a single key press results in
+// more than one character, and (b) when shift is released while another key is
+// autorepeating.
+void win32::CKeyboard::pressKey(InputHandler* writer, int extendedVkey,
+ rdr::U32 keysym)
+{
+ if (downKeysym.find(extendedVkey) != downKeysym.end()) {
+ if (downKeysym[extendedVkey] != keysym) {
+ vlog.debug("release extendedVkey 0x%x, keysym 0x%x",
+ extendedVkey, downKeysym[extendedVkey]);
+ writer->keyEvent(downKeysym[extendedVkey], false);
+ }
+ }
+ vlog.debug("press extendedVkey 0x%x, keysym 0x%x",
+ extendedVkey, keysym);
+ writer->keyEvent(keysym, true);
+ downKeysym[extendedVkey] = keysym;
+}
diff --git a/win/rfb_win32/CKeyboard.h b/win/rfb_win32/CKeyboard.h
new file mode 100644
index 0000000..666ebce
--- /dev/null
+++ b/win/rfb_win32/CKeyboard.h
@@ -0,0 +1,51 @@
+/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
+ *
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+ * USA.
+ */
+
+// -=- CKeyboard.h
+//
+// Client-side keyboard handling for Win32
+
+#ifndef __RFB_WIN32_CKEYBOARD_H__
+#define __RFB_WIN32_CKEYBOARD_H__
+
+#include <rfb/InputHandler.h>
+#include <map>
+
+namespace rfb {
+
+ namespace win32 {
+
+ class CKeyboard {
+ public:
+ void keyEvent(InputHandler* writer, rdr::U8 vkey, rdr::U32 flags,
+ bool down);
+ void releaseAllKeys(InputHandler* writer);
+ const std::map<int,rdr::U32>& pressedKeys() const {return downKeysym;};
+ bool keyPressed(int k) const {return downKeysym.find(k)!=downKeysym.end();}
+ private:
+ void win32::CKeyboard::releaseKey(InputHandler* writer, int extendedVkey);
+ void win32::CKeyboard::pressKey(InputHandler* writer, int extendedVkey,
+ rdr::U32 keysym);
+ std::map<int,rdr::U32> downKeysym;
+ };
+
+ }; // win32
+
+}; // rfb
+
+#endif // __RFB_WIN32_CKEYBOARD_H__
diff --git a/win/rfb_win32/CPointer.cxx b/win/rfb_win32/CPointer.cxx
new file mode 100644
index 0000000..3d0d934
--- /dev/null
+++ b/win/rfb_win32/CPointer.cxx
@@ -0,0 +1,186 @@
+/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
+ *
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+ * USA.
+ */
+
+#include <windows.h>
+#include <rfb/LogWriter.h>
+#include <rfb_win32/CPointer.h>
+
+using namespace rfb;
+using namespace win32;
+
+static LogWriter vlog("CPointer");
+
+
+CPointer::CPointer() : currButtonMask(0), intervalQueued(false), threeEmulating(false) {
+}
+
+CPointer::~CPointer() {
+ intervalTimer.stop();
+ threeTimer.stop();
+}
+
+
+void CPointer::pointerEvent(InputHandler* writer, const Point& pos, int buttonMask) {
+ //
+ // - Duplicate Event Filtering
+ //
+
+ bool maskChanged = buttonMask != currButtonMask;
+ bool posChanged = !pos.equals(currPos);
+ if (!(posChanged || maskChanged))
+ return;
+
+ // Pass on the event to the event-interval handler
+ threePointerEvent(writer, pos, buttonMask);
+
+ // Save the position and mask
+ currPos = pos;
+ currButtonMask = buttonMask;
+}
+
+
+inline abs(int x) {return x>0 ? x : 0;}
+
+int emulate3Mask(int buttonMask) {
+ // - Release left & right and press middle
+ vlog.debug("emulate3: active");
+ buttonMask &= ~5;
+ buttonMask |= 2;
+ return buttonMask;
+}
+
+void CPointer::threePointerEvent(InputHandler* writer, const Point& pos, int buttonMask) {
+ //
+ // - 3-Button Mouse Emulation
+ //
+
+ if (emulate3) {
+
+ bool leftChanged = (buttonMask & 1) != (currButtonMask & 1);
+ bool rightChanged = (buttonMask & 4) != (currButtonMask & 4);
+
+ if (leftChanged || rightChanged) {
+ // - One of left or right have changed
+
+ if ((buttonMask & 5) == 1 || (buttonMask & 5) == 4) {
+ // - One is up, one is down. Start a timer, so that if it
+ // expires then we know we should actually send this event
+ vlog.debug("emulate3: start timer");
+ threeTimer.start(100);
+ threePos = pos;
+ threeMask = buttonMask;
+ return;
+
+ } else if (threeTimer.isActive()) {
+ // - Both are up or both are down, and we were timing for an emulation event
+ // Stop the timer and flush the stored event
+ vlog.debug("emulate3: stop timer (state)");
+ threeTimer.stop();
+ if (threeEmulating == ((buttonMask & 5) == 5))
+ intervalPointerEvent(writer, threePos, threeMask);
+ else
+ threeEmulating = ((buttonMask & 5) == 5);
+ }
+
+ } else {
+
+ if (threeTimer.isActive()) {
+ // - We are timing for an emulation event
+
+ if (abs(threePos.x - pos.x) <= 4 || abs(threePos.y - pos.y) <= 4) {
+ // If the mouse has moved too far since the button-change event then flush
+ vlog.debug("emulate3: stop timer (moved)");
+ threeTimer.stop();
+ intervalPointerEvent(writer, threePos, threeMask);
+
+ } else {
+ // Otherwise, we ignore the new event
+ return;
+ }
+ }
+
+ }
+
+ // - If neither left nor right are down, stop emulating
+ if ((buttonMask & 5) == 0)
+ threeEmulating = false;
+
+ // - If emulating, release left & right and press middle
+ if (threeEmulating)
+ buttonMask = emulate3Mask(buttonMask);
+
+ }
+
+ // - Let the event pass through to the next stage of processing
+ intervalPointerEvent(writer, pos, buttonMask);
+}
+
+void CPointer::intervalPointerEvent(InputHandler* writer, const Point& pos, int buttonMask) {
+ //
+ // - Pointer Event Interval
+ //
+ vlog.write(101, "ptrEvent: %d,%d (%lx)", pos.x, pos.y, buttonMask);
+
+ // Send the event immediately if we haven't sent one for a while
+ bool sendNow = !intervalTimer.isActive();
+
+ if (intervalMask != buttonMask) {
+ // If the buttons have changed then flush queued events and send now
+ sendNow = true;
+ if (intervalQueued)
+ writer->pointerEvent(intervalPos, intervalMask);
+ intervalQueued = false;
+ }
+
+ if (!sendNow) {
+ // If we're not sending now then just queue the event
+ intervalQueued = true;
+ intervalPos = pos;
+ intervalMask = buttonMask;
+ } else {
+ // Start the interval timer if required, and send the event
+ intervalQueued = false;
+ intervalMask = buttonMask;
+ if (pointerEventInterval)
+ intervalTimer.start(pointerEventInterval);
+ writer->pointerEvent(pos, buttonMask);
+ }
+}
+
+void CPointer::handleTimer(InputHandler* writer, int timerId) {
+ if (timerId == intervalTimer.getId()) {
+ // Pointer interval has expired - send any queued events
+ if (intervalQueued) {
+ writer->pointerEvent(intervalPos, intervalMask);
+ intervalQueued = false;
+ } else {
+ intervalTimer.stop();
+ }
+
+ } else if (timerId = threeTimer.getId()) {
+ // 3-Button emulation timer has expired - send what we've got
+ vlog.debug("emulate3: timeout");
+ threeTimer.stop();
+
+ // If emulating, release left & right and press middle
+ if (threeEmulating)
+ threeMask = emulate3Mask(threeMask);
+
+ intervalPointerEvent(writer, threePos, threeMask);
+ }
+}
diff --git a/win/rfb_win32/CPointer.h b/win/rfb_win32/CPointer.h
new file mode 100644
index 0000000..b591601
--- /dev/null
+++ b/win/rfb_win32/CPointer.h
@@ -0,0 +1,74 @@
+/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
+ *
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+ * USA.
+ */
+
+// -=- CPointer.h
+//
+// Client-side pointer event handling for Win32
+
+#ifndef __RFB_WIN32_CPOINTER_H__
+#define __RFB_WIN32_CPOINTER_H__
+
+#include <rdr/Exception.h>
+#include <rfb/Configuration.h>
+#include <rfb/InputHandler.h>
+#include <rfb/Rect.h>
+#include <rfb_win32/IntervalTimer.h>
+
+namespace rfb {
+
+ namespace win32 {
+
+ class CPointer {
+ public:
+ CPointer();
+ ~CPointer();
+
+ void pointerEvent(InputHandler* writer, const Point& pos, int buttonMask);
+ void handleTimer(InputHandler* writer, int timerId);
+
+ void setHWND(HWND w) {intervalTimer.setHWND(w); threeTimer.setHWND(w);}
+ void setIntervalTimerId(int id) {intervalTimer.setId(id);}
+ void set3ButtonTimerId(int id) {threeTimer.setId(id);}
+
+ void enableEmulate3(bool enable) {emulate3 = enable;}
+ void enableInterval(int millis) {pointerEventInterval = millis;}
+ private:
+ Point currPos;
+ int currButtonMask;
+
+ bool emulate3;
+ int pointerEventInterval;
+
+ void intervalPointerEvent(InputHandler* writer, const Point& pos, int buttonMask);
+ IntervalTimer intervalTimer;
+ bool intervalQueued;
+ Point intervalPos;
+ int intervalMask;
+
+ void threePointerEvent(InputHandler* writer, const Point& pos, int buttonMask);
+ IntervalTimer threeTimer;
+ Point threePos;
+ int threeMask;
+ bool threeEmulating;
+ };
+
+ }; // win32
+
+}; // rfb
+
+#endif // __RFB_WIN32_CPOINTER_H__
diff --git a/win/rfb_win32/CleanDesktop.cxx b/win/rfb_win32/CleanDesktop.cxx
new file mode 100644
index 0000000..39cca11
--- /dev/null
+++ b/win/rfb_win32/CleanDesktop.cxx
@@ -0,0 +1,321 @@
+/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
+ *
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+ * USA.
+ */
+
+// -=- CleanDesktop.cxx
+
+#include <windows.h>
+#include <wininet.h>
+#include <shlobj.h>
+#include <rfb_win32/CleanDesktop.h>
+#include <rfb_win32/CurrentUser.h>
+#include <rfb_win32/Registry.h>
+#include <rfb_win32/OSVersion.h>
+#include <rfb/LogWriter.h>
+#include <rdr/Exception.h>
+#include <set>
+
+#ifdef SPI_GETUIEFFECTS
+#define RFB_HAVE_SPI_UIEFFECTS
+#else
+#pragma message(" NOTE: Not building Get/Set UI Effects support.")
+#endif
+
+using namespace rfb;
+using namespace rfb::win32;
+
+static LogWriter vlog("CleanDesktop");
+
+
+struct ActiveDesktop {
+ ActiveDesktop() : handle(0) {
+ // - Contact Active Desktop
+ HRESULT result = CoCreateInstance(CLSID_ActiveDesktop, NULL, CLSCTX_INPROC_SERVER,
+ IID_IActiveDesktop, (PVOID*)&handle);
+ if (result != S_OK)
+ throw rdr::SystemException("failed to contact Active Desktop", result);
+ }
+ ~ActiveDesktop() {
+ if (handle)
+ handle->Release();
+ }
+
+ // enableItem
+ // enables or disables the Nth Active Desktop item
+ bool enableItem(int i, bool enable_) {
+ COMPONENT item;
+ memset(&item, 0, sizeof(item));
+ item.dwSize = sizeof(item);
+
+ HRESULT hr = handle->GetDesktopItem(i, &item, 0);
+ if (hr != S_OK) {
+ vlog.error("unable to GetDesktopItem %d: %ld", i, hr);
+ return false;
+ }
+ item.fChecked = enable_;
+ vlog.debug("%sbling %d: \"%s\"", enable_ ? "ena" : "disa", i, (const char*)CStr(item.wszFriendlyName));
+
+ hr = handle->ModifyDesktopItem(&item, COMP_ELEM_CHECKED);
+ return hr == S_OK;
+ }
+
+ // enable
+ // Attempts to enable/disable Active Desktop, returns true if the setting changed,
+ // false otherwise.
+ // If Active Desktop *can* be enabled/disabled then that is done.
+ // If Active Desktop is always on (XP/2K3) then instead the individual items are
+ // disabled, and true is returned to indicate that they need to be restored later.
+ bool enable(bool enable_) {
+ bool modifyComponents = false;
+
+ vlog.debug("ActiveDesktop::enable");
+
+ // - Firstly, try to disable Active Desktop entirely
+ HRESULT hr;
+ COMPONENTSOPT adOptions;
+ memset(&adOptions, 0, sizeof(adOptions));
+ adOptions.dwSize = sizeof(adOptions);
+
+ // Attempt to actually disable/enable AD
+ hr = handle->GetDesktopItemOptions(&adOptions, 0);
+ if (hr == S_OK) {
+ // If Active Desktop is already in the desired state then return false (no change)
+ // NB: If AD is enabled AND restoreItems is set then we regard it as disabled...
+ if (((adOptions.fActiveDesktop==0) && restoreItems.empty()) == (enable_==false))
+ return false;
+ adOptions.fActiveDesktop = enable_;
+ hr = handle->SetDesktopItemOptions(&adOptions, 0);
+ }
+ // Apply the change, then test whether it actually took effect
+ if (hr == S_OK)
+ hr = handle->ApplyChanges(AD_APPLY_REFRESH);
+ if (hr == S_OK)
+ hr = handle->GetDesktopItemOptions(&adOptions, 0);
+ if (hr == S_OK)
+ modifyComponents = (adOptions.fActiveDesktop==0) != (enable_==false);
+ if (hr != S_OK) {
+ vlog.error("failed to get/set Active Desktop options: %ld", hr);
+ return false;
+ }
+
+ if (enable_) {
+ // - We are re-enabling Active Desktop. If there are components in restoreItems
+ // then restore them!
+ std::set<int>::const_iterator i;
+ for (i=restoreItems.begin(); i!=restoreItems.end(); i++) {
+ enableItem(*i, true);
+ }
+ restoreItems.clear();
+ } else if (modifyComponents) {
+ // - Disable all currently enabled items, and add the disabled ones to restoreItems
+ int itemCount = 0;
+ hr = handle->GetDesktopItemCount(&itemCount, 0);
+ if (hr != S_OK) {
+ vlog.error("failed to get desktop item count: %ld", hr);
+ return false;
+ }
+ for (unsigned int i=0; i<itemCount; i++) {
+ if (enableItem(i, false))
+ restoreItems.insert(i);
+ }
+ }
+
+ // - Apply whatever changes we have made, but DON'T save them!
+ hr = handle->ApplyChanges(AD_APPLY_REFRESH);
+ return hr == S_OK;
+ }
+ IActiveDesktop* handle;
+ std::set<int> restoreItems;
+};
+
+
+DWORD SysParamsInfo(UINT action, UINT param, PVOID ptr, UINT ini) {
+ DWORD r = ERROR_SUCCESS;
+ if (!SystemParametersInfo(action, param, ptr, ini)) {
+ r = GetLastError();
+ vlog.info("SPI error: %d", r);
+ }
+ return r;
+}
+
+
+CleanDesktop::CleanDesktop() : restoreActiveDesktop(false), restoreWallpaper(false),
+ restorePattern(false), restoreEffects(false) {
+ CoInitialize(0);
+}
+
+CleanDesktop::~CleanDesktop() {
+ enableEffects();
+ enablePattern();
+ enableWallpaper();
+ CoUninitialize();
+}
+
+void CleanDesktop::disableWallpaper() {
+ try {
+ ImpersonateCurrentUser icu;
+
+ vlog.debug("disable desktop wallpaper/Active Desktop");
+
+ // -=- First attempt to remove the wallpaper using Active Desktop
+ try {
+ ActiveDesktop ad;
+ if (ad.enable(false))
+ restoreActiveDesktop = true;
+ } catch (rdr::Exception& e) {
+ vlog.error(e.str());
+ }
+
+ // -=- Switch of normal wallpaper and notify apps
+ SysParamsInfo(SPI_SETDESKWALLPAPER, 0, "", SPIF_SENDCHANGE);
+ restoreWallpaper = true;
+
+ } catch (rdr::Exception& e) {
+ vlog.info(e.str());
+ }
+}
+
+void CleanDesktop::enableWallpaper() {
+ try {
+ ImpersonateCurrentUser icu;
+
+ if (restoreActiveDesktop) {
+ vlog.debug("restore Active Desktop");
+
+ // -=- First attempt to re-enable Active Desktop
+ try {
+ ActiveDesktop ad;
+ ad.enable(true);
+ restoreActiveDesktop = false;
+ } catch (rdr::Exception& e) {
+ vlog.error(e.str());
+ }
+ }
+
+ if (restoreWallpaper) {
+ vlog.debug("restore desktop wallpaper");
+
+ // -=- Then restore the standard wallpaper if required
+ SysParamsInfo(SPI_SETDESKWALLPAPER, 0, NULL, SPIF_SENDCHANGE);
+ restoreWallpaper = false;
+ }
+
+ } catch (rdr::Exception& e) {
+ vlog.info(e.str());
+ }
+}
+
+
+void CleanDesktop::disablePattern() {
+ try {
+ ImpersonateCurrentUser icu;
+
+ vlog.debug("disable desktop pattern");
+ SysParamsInfo(SPI_SETDESKPATTERN, 0, "", SPIF_SENDCHANGE);
+ restorePattern = true;
+
+ } catch (rdr::Exception& e) {
+ vlog.info(e.str());
+ }
+}
+
+void CleanDesktop::enablePattern() {
+ try {
+ if (restorePattern) {
+ ImpersonateCurrentUser icu;
+
+ vlog.debug("restoring pattern...");
+
+ TCharArray pattern;
+ if (osVersion.isPlatformWindows) {
+ RegKey cfgKey;
+ cfgKey.openKey(HKEY_CURRENT_USER, _T("Control Panel\\Desktop"));
+ pattern.buf = cfgKey.getString(_T("Pattern"));
+ }
+ SysParamsInfo(SPI_SETDESKPATTERN, 0, pattern.buf, SPIF_SENDCHANGE);
+ restorePattern = false;
+ }
+
+ } catch (rdr::Exception& e) {
+ vlog.info(e.str());
+ }
+}
+
+
+void CleanDesktop::disableEffects() {
+ try {
+ ImpersonateCurrentUser icu;
+
+ vlog.debug("disable desktop effects");
+
+ SysParamsInfo(SPI_SETFONTSMOOTHING, FALSE, 0, SPIF_SENDCHANGE);
+#ifdef RFB_HAVE_SPI_UIEFFECTS
+ if (SysParamsInfo(SPI_GETUIEFFECTS, 0, &uiEffects, 0) == ERROR_CALL_NOT_IMPLEMENTED) {
+ SysParamsInfo(SPI_GETCOMBOBOXANIMATION, 0, &comboBoxAnim, 0);
+ SysParamsInfo(SPI_GETGRADIENTCAPTIONS, 0, &gradientCaptions, 0);
+ SysParamsInfo(SPI_GETHOTTRACKING, 0, &hotTracking, 0);
+ SysParamsInfo(SPI_GETLISTBOXSMOOTHSCROLLING, 0, &listBoxSmoothScroll, 0);
+ SysParamsInfo(SPI_GETMENUANIMATION, 0, &menuAnim, 0);
+ SysParamsInfo(SPI_SETCOMBOBOXANIMATION, 0, FALSE, SPIF_SENDCHANGE);
+ SysParamsInfo(SPI_SETGRADIENTCAPTIONS, 0, FALSE, SPIF_SENDCHANGE);
+ SysParamsInfo(SPI_SETHOTTRACKING, 0, FALSE, SPIF_SENDCHANGE);
+ SysParamsInfo(SPI_SETLISTBOXSMOOTHSCROLLING, 0, FALSE, SPIF_SENDCHANGE);
+ SysParamsInfo(SPI_SETMENUANIMATION, 0, FALSE, SPIF_SENDCHANGE);
+ } else {
+ SysParamsInfo(SPI_SETUIEFFECTS, 0, FALSE, SPIF_SENDCHANGE);
+
+ // We *always* restore UI effects overall, since there is no Windows GUI to do it
+ uiEffects = TRUE;
+ }
+#else
+ vlog.debug(" not supported");
+#endif
+ restoreEffects = true;
+
+ } catch (rdr::Exception& e) {
+ vlog.info(e.str());
+ }
+}
+
+void CleanDesktop::enableEffects() {
+ try {
+ if (restoreEffects) {
+ ImpersonateCurrentUser icu;
+
+ vlog.debug("restore desktop effects");
+
+ RegKey desktopCfg;
+ desktopCfg.openKey(HKEY_CURRENT_USER, _T("Control Panel\\Desktop"));
+ SysParamsInfo(SPI_SETFONTSMOOTHING, desktopCfg.getInt(_T("FontSmoothing"), 0) != 0, 0, SPIF_SENDCHANGE);
+#ifdef RFB_HAVE_SPI_UIEFFECTS
+ if (SysParamsInfo(SPI_SETUIEFFECTS, 0, (void*)uiEffects, SPIF_SENDCHANGE) == ERROR_CALL_NOT_IMPLEMENTED) {
+ SysParamsInfo(SPI_SETCOMBOBOXANIMATION, 0, (void*)comboBoxAnim, SPIF_SENDCHANGE);
+ SysParamsInfo(SPI_SETGRADIENTCAPTIONS, 0, (void*)gradientCaptions, SPIF_SENDCHANGE);
+ SysParamsInfo(SPI_SETHOTTRACKING, 0, (void*)hotTracking, SPIF_SENDCHANGE);
+ SysParamsInfo(SPI_SETLISTBOXSMOOTHSCROLLING, 0, (void*)listBoxSmoothScroll, SPIF_SENDCHANGE);
+ SysParamsInfo(SPI_SETMENUANIMATION, 0, (void*)menuAnim, SPIF_SENDCHANGE);
+ }
+ restoreEffects = false;
+#else
+ vlog.info(" not supported");
+#endif
+ }
+
+ } catch (rdr::Exception& e) {
+ vlog.info(e.str());
+ }
+}
diff --git a/win/rfb_win32/CleanDesktop.h b/win/rfb_win32/CleanDesktop.h
new file mode 100644
index 0000000..22e246f
--- /dev/null
+++ b/win/rfb_win32/CleanDesktop.h
@@ -0,0 +1,57 @@
+/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
+ *
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+ * USA.
+ */
+
+// -=- CleanDesktop.h
+
+#ifndef __RFB_WIN32_CLEANDESKTOP_H__
+#define __RFB_WIN32_CLEANDESKTOP_H__
+
+#include <rfb_win32/TCharArray.h>
+
+namespace rfb {
+
+ namespace win32 {
+
+ class CleanDesktop {
+ public:
+ CleanDesktop();
+ ~CleanDesktop();
+
+ void disableWallpaper();
+ void enableWallpaper();
+
+ void disablePattern();
+ void enablePattern();
+
+ void disableEffects();
+ void enableEffects();
+
+ private:
+ bool restoreActiveDesktop;
+ bool restoreWallpaper;
+ bool restorePattern;
+ bool restoreEffects;
+ BOOL uiEffects;
+ BOOL comboBoxAnim, gradientCaptions, hotTracking, listBoxSmoothScroll, menuAnim;
+ };
+
+ }; // win32
+
+}; // rfb
+
+#endif // __RFB_WIN32_CLEANDESKTOP_H__
diff --git a/win/rfb_win32/Clipboard.cxx b/win/rfb_win32/Clipboard.cxx
new file mode 100644
index 0000000..a4c43f0
--- /dev/null
+++ b/win/rfb_win32/Clipboard.cxx
@@ -0,0 +1,200 @@
+/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
+ *
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+ * USA.
+ */
+
+// -=- Clipboard.cxx
+
+#include <rfb_win32/Clipboard.h>
+#include <rfb_win32/WMShatter.h>
+#include <rfb/util.h>
+
+#include <rfb/LogWriter.h>
+
+using namespace rfb;
+using namespace rfb::win32;
+
+static LogWriter vlog("Clipboard");
+
+
+//
+// -=- CR/LF handlers
+//
+
+char*
+dos2unix(const char* text) {
+ int len = strlen(text)+1;
+ char* unix = new char[strlen(text)+1];
+ int i, j=0;
+ for (i=0; i<len; i++) {
+ if (text[i] != '\x0d')
+ unix[j++] = text[i];
+ }
+ return unix;
+}
+
+char*
+unix2dos(const char* text) {
+ int len = strlen(text)+1;
+ char* dos = new char[strlen(text)*2+1];
+ int i, j=0;
+ for (i=0; i<len; i++) {
+ if (text[i] == '\x0a')
+ dos[j++] = '\x0d';
+ dos[j++] = text[i];
+ }
+ return dos;
+}
+
+
+//
+// -=- ISO-8859-1 (Latin 1) filter (in-place)
+//
+
+void
+removeNonISOLatin1Chars(char* text) {
+ int len = strlen(text);
+ int i=0, j=0;
+ for (; i<len; i++) {
+ if (((text[i] >= 1) && (text[i] <= 127)) ||
+ ((text[i] >= 160) && (text[i] <= 255)))
+ text[j++] = text[i];
+ }
+ text[j] = 0;
+}
+
+//
+// -=- Clipboard object
+//
+
+Clipboard::Clipboard()
+ : MsgWindow(_T("Clipboard")), notifier(0), next_window(0) {
+ next_window = SetClipboardViewer(getHandle());
+ vlog.debug("registered clipboard handler");
+}
+
+Clipboard::~Clipboard() {
+ vlog.debug("removing %x from chain (next is %x)", getHandle(), next_window);
+ ChangeClipboardChain(getHandle(), next_window);
+}
+
+LRESULT
+Clipboard::processMessage(UINT msg, WPARAM wParam, LPARAM lParam) {
+ switch (msg) {
+
+ case WM_CHANGECBCHAIN:
+ vlog.debug("change clipboard chain (%x, %x)", wParam, lParam);
+ if ((HWND) wParam == next_window)
+ next_window = (HWND) lParam;
+ else if (next_window != 0)
+ SendMessage(next_window, msg, wParam, lParam);
+ else
+ vlog.error("bad clipboard chain change!");
+ break;
+
+ case WM_DRAWCLIPBOARD:
+ {
+ HWND owner = GetClipboardOwner();
+ if (owner == getHandle()) {
+ vlog.debug("local clipboard changed by me");
+ } else {
+ vlog.debug("local clipboard changed by %x", owner);
+
+ // Open the clipboard
+ if (OpenClipboard(getHandle())) {
+ // Get the clipboard data
+ HGLOBAL cliphandle = GetClipboardData(CF_TEXT);
+ if (cliphandle) {
+ char* clipdata = (char*) GlobalLock(cliphandle);
+
+ // Notify clients
+ if (notifier) {
+ if (!clipdata) {
+ notifier->notifyClipboardChanged(0, 0);
+ } else {
+ CharArray unix_text;
+ unix_text.buf = dos2unix(clipdata);
+ removeNonISOLatin1Chars(unix_text.buf);
+ notifier->notifyClipboardChanged(unix_text.buf, strlen(unix_text.buf));
+ }
+ } else {
+ vlog.debug("no clipboard notifier registered");
+ }
+
+ // Release the buffer and close the clipboard
+ GlobalUnlock(cliphandle);
+ }
+
+ CloseClipboard();
+ }
+ }
+ }
+ if (next_window)
+ SendMessage(next_window, msg, wParam, lParam);
+ return 0;
+
+ };
+ return MsgWindow::processMessage(msg, wParam, lParam);
+};
+
+void
+Clipboard::setClipText(const char* text) {
+ HANDLE clip_handle = 0;
+
+ try {
+
+ // - Firstly, we must open the clipboard
+ if (!OpenClipboard(getHandle()))
+ throw rdr::SystemException("unable to open Win32 clipboard", GetLastError());
+
+ // - Pre-process the supplied clipboard text into DOS format
+ CharArray dos_text;
+ dos_text.buf = unix2dos(text);
+ removeNonISOLatin1Chars(dos_text.buf);
+ int dos_text_len = strlen(dos_text.buf);
+
+ // - Allocate global memory for the data
+ clip_handle = ::GlobalAlloc(GMEM_MOVEABLE, dos_text_len+1);
+
+ char* data = (char*) GlobalLock(clip_handle);
+ memcpy(data, dos_text.buf, dos_text_len+1);
+ data[dos_text_len] = 0;
+ GlobalUnlock(clip_handle);
+
+ // - Next, we must clear out any existing data
+ if (!EmptyClipboard())
+ throw rdr::SystemException("unable to empty Win32 clipboard", GetLastError());
+
+ // - Set the new clipboard data
+ if (!SetClipboardData(CF_TEXT, clip_handle))
+ throw rdr::SystemException("unable to set Win32 clipboard", GetLastError());
+ clip_handle = 0;
+
+ vlog.debug("set clipboard");
+ } catch (rdr::Exception& e) {
+ vlog.debug(e.str());
+ }
+
+ // - Close the clipboard
+ if (!CloseClipboard())
+ vlog.debug("unable to close Win32 clipboard: %u", GetLastError());
+ else
+ vlog.debug("closed clipboard");
+ if (clip_handle) {
+ vlog.debug("freeing clipboard handle");
+ GlobalFree(clip_handle);
+ }
+}
diff --git a/win/rfb_win32/Clipboard.h b/win/rfb_win32/Clipboard.h
new file mode 100644
index 0000000..b79768f
--- /dev/null
+++ b/win/rfb_win32/Clipboard.h
@@ -0,0 +1,66 @@
+/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
+ *
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+ * USA.
+ */
+
+// -=- Clipboard.h
+//
+// The Clipboard is used to set the system clipboard, and to get callbacks
+// when the system clipboard has changed.
+
+#ifndef __RFB_WIN32_CLIPBOARD_H__
+#define __RFB_WIN32_CLIPBOARD_H__
+
+#include <rfb/SDesktop.h>
+#include <rfb/Threading.h>
+#include <rfb_win32/MsgWindow.h>
+#include <rfb_win32/DeviceFrameBuffer.h>
+
+namespace rfb {
+
+ namespace win32 {
+
+ class Clipboard : MsgWindow {
+ public:
+
+ // -=- Abstract base class for callback recipients
+ class Notifier {
+ public:
+ virtual void notifyClipboardChanged(const char* text, int len) = 0;
+ };
+
+ Clipboard();
+ ~Clipboard();
+
+ // - Set the notifier to use
+ void setNotifier(Notifier* cbn) {notifier = cbn;}
+
+ // - Set the clipboard contents
+ void setClipText(const char* text);
+
+ protected:
+ // - Internal MsgWindow callback
+ virtual LRESULT processMessage(UINT msg, WPARAM wParam, LPARAM lParam);
+
+ Notifier* notifier;
+ HWND next_window;
+ };
+
+ };
+
+};
+
+#endif // __RFB_WIN32_CLIPBOARD_H__
diff --git a/win/rfb_win32/CompatibleBitmap.h b/win/rfb_win32/CompatibleBitmap.h
new file mode 100644
index 0000000..4beed8d
--- /dev/null
+++ b/win/rfb_win32/CompatibleBitmap.h
@@ -0,0 +1,46 @@
+/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
+ *
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+ * USA.
+ */
+
+#ifndef __RFB_WIN32_COMPAT_BITMAP_H__
+#define __RFB_WIN32_COMPAT_BITMAP_H__
+
+#include <windows.h>
+#include <rdr/Exception.h>
+
+namespace rfb {
+ namespace win32 {
+
+ class CompatibleBitmap {
+ public:
+ CompatibleBitmap(HDC hdc, int width, int height) {
+ hbmp = CreateCompatibleBitmap(hdc, width, height);
+ if (!hbmp)
+ throw rdr::SystemException("CreateCompatibleBitmap() failed", GetLastError());
+ }
+ virtual ~CompatibleBitmap() {
+ if (hbmp) DeleteObject(hbmp);
+ }
+ operator HBITMAP() const {return hbmp;}
+ protected:
+ HBITMAP hbmp;
+ };
+
+ };
+};
+
+#endif
diff --git a/win/rfb_win32/ComputerName.h b/win/rfb_win32/ComputerName.h
new file mode 100644
index 0000000..110caa5
--- /dev/null
+++ b/win/rfb_win32/ComputerName.h
@@ -0,0 +1,40 @@
+/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
+ *
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+ * USA.
+ */
+
+#ifndef __RFB_WIN32_COMPUTERNAME_H__
+#define __RFB_WIN32_COMPUTERNAME_H__
+
+#include <windows.h>
+#include <rfb_win32/TCharArray.h>
+
+namespace rfb {
+ namespace win32 {
+
+ // Get the computer name
+ struct ComputerName : TCharArray {
+ ComputerName() : TCharArray(MAX_COMPUTERNAME_LENGTH+1) {
+ ULONG namelength = MAX_COMPUTERNAME_LENGTH+1;
+ if (!GetComputerName(buf, &namelength))
+ _tcscpy(buf, _T(""));
+ }
+ };
+
+ };
+};
+
+#endif
diff --git a/win/rfb_win32/CurrentUser.cxx b/win/rfb_win32/CurrentUser.cxx
new file mode 100644
index 0000000..7562d29
--- /dev/null
+++ b/win/rfb_win32/CurrentUser.cxx
@@ -0,0 +1,152 @@
+/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
+ *
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+ * USA.
+ */
+
+// -=- Currentuser.cxx
+
+#include <stdlib.h>
+#include <rfb/LogWriter.h>
+#include <rfb_win32/CurrentUser.h>
+#include <rfb_win32/DynamicFn.h>
+#include <rfb_win32/Service.h>
+#include <rfb_win32/OSVersion.h>
+#include <lmcons.h>
+
+using namespace rfb;
+using namespace win32;
+
+static LogWriter vlog("CurrentUser");
+
+
+const TCHAR* shellIconClass = _T("Shell_TrayWnd");
+
+BOOL CALLBACK enumWindows(HWND hwnd, LPARAM lParam) {
+ TCHAR className[16];
+ if (GetClassName(hwnd, className, sizeof(className)) &&
+ (_tcscmp(className, shellIconClass) == 0)) {
+ vlog.debug("located tray icon window (%s)", (const char*)CStr(className));
+ DWORD processId = 0;
+ GetWindowThreadProcessId(hwnd, &processId);
+ if (!processId)
+ return TRUE;
+ Handle process = OpenProcess(MAXIMUM_ALLOWED, FALSE, processId);
+ if (!process.h)
+ return TRUE;
+ if (!OpenProcessToken(process, MAXIMUM_ALLOWED, (HANDLE*)lParam))
+ return TRUE;
+ vlog.debug("obtained user token");
+ return FALSE;
+ }
+ return TRUE;
+}
+
+BOOL CALLBACK enumDesktops(LPTSTR lpszDesktop, LPARAM lParam) {
+ HDESK desktop = OpenDesktop(lpszDesktop, 0, FALSE, DESKTOP_ENUMERATE);
+ vlog.debug("opening \"%s\"", lpszDesktop);
+ if (!desktop) {
+ vlog.info("desktop \"%s\" inaccessible", (const char*)CStr(lpszDesktop));
+ return TRUE;
+ }
+ BOOL result = EnumDesktopWindows(desktop, enumWindows, lParam);
+ if (!CloseDesktop(desktop))
+ vlog.info("unable to close desktop: %ld", GetLastError());
+ return result;
+}
+
+
+CurrentUserToken::CurrentUserToken() : isSafe_(false) {
+ if (isServiceProcess()) {
+ // If the platform is Windows 95/98/Me then we must fake the token's presence
+ if (osVersion.isPlatformWindows) {
+ try {
+ UserName un;
+ h = INVALID_HANDLE_VALUE;
+ } catch (rdr::SystemException& e) {
+ if (e.err != ERROR_NOT_LOGGED_ON)
+ throw;
+ if (FindWindow(shellIconClass, 0))
+ h = INVALID_HANDLE_VALUE;
+ }
+ isSafe_ = (h != 0);
+ return;
+ }
+
+ // Try to get the user token using the Terminal Services APIs
+ // NB: This will only work under XP/2003 and later
+ typedef BOOL (WINAPI *WTSQueryUserToken_proto)(ULONG, PHANDLE);
+ DynamicFn<WTSQueryUserToken_proto> _WTSQueryUserToken(_T("wtsapi32.dll"), "WTSQueryUserToken");
+ if (_WTSQueryUserToken.isValid()) {
+ (*_WTSQueryUserToken)(-1, &h);
+ isSafe_ = true;
+ return;
+ }
+
+ // Try to find the Shell Tray Icon window and take its token
+ // NB: This will only work under NT/2K (and later, but they're dealt with above)
+ // NB: If the shell is not running then this will return an Unsafe Null token.
+ EnumDesktops(GetProcessWindowStation(), enumDesktops, (LONG)&h);
+ isSafe_ = (h != 0);
+ } else {
+ // Try to open the security token for the User-Mode process
+ if (!OpenProcessToken(GetCurrentProcess(), GENERIC_ALL, &h)) {
+ DWORD err = GetLastError();
+ if (err != ERROR_CALL_NOT_IMPLEMENTED)
+ throw rdr::SystemException("OpenProcessToken failed", err);
+ // Under Windows 95/98/Me, we fake the handle value...
+ h = INVALID_HANDLE_VALUE;
+ }
+ isSafe_ = true;
+ }
+}
+
+
+ImpersonateCurrentUser::ImpersonateCurrentUser() {
+ RegCloseKey(HKEY_CURRENT_USER);
+ if (!isServiceProcess())
+ return;
+ if (!token.canImpersonate())
+ throw rdr::Exception("Cannot impersonate unsafe or null token");
+ if (!ImpersonateLoggedOnUser(token)) {
+ DWORD err = GetLastError();
+ if (err != ERROR_CALL_NOT_IMPLEMENTED)
+ throw rdr::SystemException("Failed to impersonate user", GetLastError());
+ }
+}
+
+ImpersonateCurrentUser::~ImpersonateCurrentUser() {
+ if (!RevertToSelf()) {
+ DWORD err = GetLastError();
+ if (err != ERROR_CALL_NOT_IMPLEMENTED)
+ exit(err);
+ }
+ RegCloseKey(HKEY_CURRENT_USER);
+}
+
+
+UserName::UserName() : TCharArray(UNLEN+1) {
+ DWORD len = UNLEN+1;
+ if (!GetUserName(buf, &len))
+ throw rdr::SystemException("GetUserName failed", GetLastError());
+}
+
+
+UserSID::UserSID() {
+ CurrentUserToken token;
+ if (!token.canImpersonate())
+ return;
+ setSID(Sid::FromToken(token.h));
+}
diff --git a/win/rfb_win32/CurrentUser.h b/win/rfb_win32/CurrentUser.h
new file mode 100644
index 0000000..794f27c
--- /dev/null
+++ b/win/rfb_win32/CurrentUser.h
@@ -0,0 +1,100 @@
+/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
+ *
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+ * USA.
+ */
+
+// CurrentUser.h
+
+// Helper class providing the session's logged on username, if
+// a user is logged on. Also allows processes running under
+// XP/2K3 etc to masquerade as the logged on user for security
+// purposes
+
+#ifndef __RFB_WIN32_CURRENT_USER_H__
+#define __RFB_WIN32_CURRENT_USER_H__
+
+#include <rfb_win32/Handle.h>
+#include <rfb_win32/Security.h>
+
+namespace rfb {
+
+ namespace win32 {
+
+ // CurrentUserToken
+ // CurrentUserToken is a Handle containing the security token
+ // for the currently logged-on user, or null if no user is
+ // logged on.
+ //
+ // Under Windows 95/98/Me, which don't support security tokens,
+ // the token will be INVALID_HANDLE_VALUE if a user is logged on.
+ //
+ // Under Windows NT/2K, it may be the case that the token is
+ // null even when a user *is* logged on, because we use some hacks
+ // to detect the user's token and sometimes they fail. On these
+ // platforms, isSafe() will return False if the token is null.
+ //
+ // Under Windows XP, etc, isSafe() will always be True, and the token
+ // will always be set to the currently logged on user's token.
+ //
+ // canImpersonate() tests whether there is a user token that is safe
+ // to impersonate.
+ //
+ // noUserLoggedOn() tests whether there is *definitely* no user logged on.
+
+ struct CurrentUserToken : public Handle {
+ CurrentUserToken();
+ bool isSafe() const { return isSafe_; };
+ bool canImpersonate() const { return h && isSafe(); }
+ bool noUserLoggedOn() const { return !h && isSafe(); }
+ private:
+ bool isSafe_;
+ };
+
+ // ImpersonateCurrentUser
+ // Throws an exception on failure.
+ // Succeeds (trivially) if process is not running as service.
+ // Fails if CurrentUserToken is not valid.
+ // Fails if platform is NT AND cannot impersonate token.
+ // Succeeds otherwise.
+
+ struct ImpersonateCurrentUser {
+ ImpersonateCurrentUser();
+ ~ImpersonateCurrentUser();
+ CurrentUserToken token;
+ };
+
+ // UserName
+ // Returns the name of the user the thread is currently running as.
+ // Raises a SystemException in case of error.
+ // NB: Raises a SystemException with err == ERROR_NOT_LOGGED_ON if
+ // running under Windows 9x/95/Me and no user is logged on.
+
+ struct UserName : public TCharArray {
+ UserName();
+ };
+
+ // UserSID
+ // Returns the SID of the currently logged-on user (i.e. the session user)
+
+ struct UserSID : public Sid {
+ UserSID();
+ };
+
+ }
+
+}
+
+#endif
diff --git a/win/rfb_win32/DIBSectionBuffer.cxx b/win/rfb_win32/DIBSectionBuffer.cxx
new file mode 100644
index 0000000..18ce3ea
--- /dev/null
+++ b/win/rfb_win32/DIBSectionBuffer.cxx
@@ -0,0 +1,222 @@
+/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
+ *
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+ * USA.
+ */
+
+#include <rfb_win32/DIBSectionBuffer.h>
+#include <rfb_win32/DeviceContext.h>
+#include <rfb_win32/BitmapInfo.h>
+#include <rfb/LogWriter.h>
+
+using namespace rfb;
+using namespace win32;
+
+static LogWriter vlog("DIBSectionBuffer");
+
+
+DIBSectionBuffer::DIBSectionBuffer(HWND window_)
+ : bitmap(0), device(0), window(window_) {
+ memset(&format, 0, sizeof(format));
+ memset(palette, 0, sizeof(palette));
+}
+
+DIBSectionBuffer::DIBSectionBuffer(HDC device_)
+ : bitmap(0), window(0), device(device_) {
+ memset(&format, 0, sizeof(format));
+ memset(palette, 0, sizeof(palette));
+}
+
+DIBSectionBuffer::~DIBSectionBuffer() {
+ if (bitmap)
+ DeleteObject(bitmap);
+}
+
+
+void DIBSectionBuffer::setPF(const PixelFormat& pf) {
+ if (memcmp(&getPF(), &pf, sizeof(pf)) == 0) {
+ vlog.debug("pixel format unchanged by setPF()");
+ return;
+ }
+ format = pf;
+ recreateBuffer();
+ if ((pf.bpp <= 8) && pf.trueColour) {
+ vlog.info("creating %d-bit TrueColour palette", pf.depth);
+ for (int i=0; i < (1<<(pf.depth)); i++) {
+ palette[i].b = ((((i >> pf.blueShift) & pf.blueMax) * 65535) + pf.blueMax/2) / pf.blueMax;
+ palette[i].g = ((((i >> pf.greenShift) & pf.greenMax) * 65535) + pf.greenMax/2) / pf.greenMax;
+ palette[i].r = ((((i >> pf.redShift) & pf.redMax) * 65535) + pf.redMax/2) / pf.redMax;
+ }
+ refreshPalette();
+ }
+}
+
+void DIBSectionBuffer::setSize(int w, int h) {
+ if (width_ == w && height_ == h) {
+ vlog.debug("size unchanged by setSize()");
+ return;
+ }
+ width_ = w;
+ height_ = h;
+ recreateBuffer();
+}
+
+
+// * copyPaletteToDIB MUST NEVER be called on a truecolour DIB! *
+
+void copyPaletteToDIB(Colour palette[256], HDC wndDC, HBITMAP dib) {
+ BitmapDC dibDC(wndDC, dib);
+ RGBQUAD rgb[256];
+ for (unsigned int i=0;i<256;i++) {
+ rgb[i].rgbRed = palette[i].r >> 8;
+ rgb[i].rgbGreen = palette[i].g >> 8;
+ rgb[i].rgbBlue = palette[i].b >> 8;
+ }
+ if (!SetDIBColorTable(dibDC, 0, 256, (RGBQUAD*) rgb))
+ throw rdr::SystemException("unable to SetDIBColorTable", GetLastError());
+}
+
+inline void initMaxAndShift(DWORD mask, int* max, int* shift) {
+ for ((*shift) = 0; (mask & 1) == 0; (*shift)++) mask >>= 1;
+ (*max) = (rdr::U16)mask;
+}
+
+void DIBSectionBuffer::recreateBuffer() {
+ HBITMAP new_bitmap = 0;
+ rdr::U8* new_data = 0;
+
+ if (width_ && height_ && (format.depth != 0)) {
+ BitmapInfo bi;
+ memset(&bi, 0, sizeof(bi));
+ // *** wrong?
+ UINT iUsage = format.trueColour ? DIB_RGB_COLORS : DIB_PAL_COLORS;
+ // ***
+ bi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
+ bi.bmiHeader.biBitCount = format.bpp;
+ bi.bmiHeader.biSizeImage = (format.bpp / 8) * width_ * height_;
+ bi.bmiHeader.biPlanes = 1;
+ bi.bmiHeader.biWidth = width_;
+ bi.bmiHeader.biHeight = -height_;
+ bi.bmiHeader.biCompression = (format.bpp > 8) ? BI_BITFIELDS : BI_RGB;
+ bi.mask.red = format.redMax << format.redShift;
+ bi.mask.green = format.greenMax << format.greenShift;
+ bi.mask.blue = format.blueMax << format.blueShift;
+
+ // Create a DIBSection to draw into
+ if (device)
+ new_bitmap = ::CreateDIBSection(device, (BITMAPINFO*)&bi.bmiHeader, iUsage,
+ (void**)&new_data, NULL, 0);
+ else
+ new_bitmap = ::CreateDIBSection(WindowDC(window), (BITMAPINFO*)&bi.bmiHeader, iUsage,
+ (void**)&new_data, NULL, 0);
+
+ if (!new_bitmap) {
+ int err = GetLastError();
+ throw rdr::SystemException("unable to create DIB section", err);
+ }
+
+ vlog.debug("recreateBuffer()");
+ } else {
+ vlog.debug("one of area or format not set");
+ }
+
+ if (new_bitmap && bitmap) {
+ vlog.debug("preserving bitmap contents");
+
+ // Copy the contents across
+ if (device) {
+ if (format.bpp <= 8)
+ copyPaletteToDIB(palette, device, new_bitmap);
+ BitmapDC src_dev(device, bitmap);
+ BitmapDC dest_dev(device, new_bitmap);
+ BitBlt(dest_dev, 0, 0, width_, height_, src_dev, 0, 0, SRCCOPY);
+ } else {
+ WindowDC wndDC(window);
+ if (format.bpp <= 8)
+ copyPaletteToDIB(palette, wndDC, new_bitmap);
+ BitmapDC src_dev(wndDC, bitmap);
+ BitmapDC dest_dev(wndDC, new_bitmap);
+ BitBlt(dest_dev, 0, 0, width_, height_, src_dev, 0, 0, SRCCOPY);
+ }
+ }
+
+ if (bitmap) {
+ // Delete the old bitmap
+ DeleteObject(bitmap);
+ bitmap = 0;
+ data = 0;
+ }
+
+ if (new_bitmap) {
+ // Set up the new bitmap
+ bitmap = new_bitmap;
+ data = new_data;
+
+ // Determine the *actual* DIBSection format
+ DIBSECTION ds;
+ if (!GetObject(bitmap, sizeof(ds), &ds))
+ throw rdr::SystemException("GetObject", GetLastError());
+
+ // Correct the "stride" of the DIB
+ // *** This code DWORD aligns each row - is that right???
+ stride = width_;
+ int bytesPerRow = stride * format.bpp/8;
+ if (bytesPerRow % 4) {
+ bytesPerRow += 4 - (bytesPerRow % 4);
+ stride = (bytesPerRow * 8) / format.bpp;
+ vlog.info("adjusting DIB stride: %d to %d", width_, stride);
+ }
+
+ // Calculate the PixelFormat for the DIB
+ format.bigEndian = 0;
+ format.bpp = format.depth = ds.dsBm.bmBitsPixel;
+ format.trueColour = format.trueColour || format.bpp > 8;
+ if (format.bpp > 8) {
+
+ // Get the truecolour format used by the DIBSection
+ initMaxAndShift(ds.dsBitfields[0], &format.redMax, &format.redShift);
+ initMaxAndShift(ds.dsBitfields[1], &format.greenMax, &format.greenShift);
+ initMaxAndShift(ds.dsBitfields[2], &format.blueMax, &format.blueShift);
+
+ // Calculate the effective depth
+ format.depth = 0;
+ Pixel bits = ds.dsBitfields[0] | ds.dsBitfields[1] | ds.dsBitfields[2];
+ while (bits) {
+ format.depth++;
+ bits = bits >> 1;
+ }
+ if (format.depth > format.bpp)
+ throw Exception("Bad DIBSection format (depth exceeds bpp)");
+ } else {
+ // Set the DIBSection's palette
+ refreshPalette();
+ }
+
+ }
+}
+
+void DIBSectionBuffer::refreshPalette() {
+ if (format.bpp > 8) {
+ vlog.error("refresh palette called for truecolor DIB");
+ return;
+ }
+ vlog.debug("refreshing palette");
+ if (device)
+ copyPaletteToDIB(palette, device, bitmap);
+ else
+ copyPaletteToDIB(palette, WindowDC(window), bitmap);
+}
+
+
diff --git a/win/rfb_win32/DIBSectionBuffer.h b/win/rfb_win32/DIBSectionBuffer.h
new file mode 100644
index 0000000..ad1a310
--- /dev/null
+++ b/win/rfb_win32/DIBSectionBuffer.h
@@ -0,0 +1,85 @@
+/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
+ *
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+ * USA.
+ */
+
+// -=- DIBSectionBuffer.h
+
+// A DIBSectionBuffer acts much like a standard PixelBuffer, but is associated
+// with a particular window on-screen and can be drawn into that window if
+// required, using the standard Win32 drawing operations.
+
+#ifndef __RFB_WIN32_DIB_SECTION_BUFFER_H__
+#define __RFB_WIN32_DIB_SECTION_BUFFER_H__
+
+#include <windows.h>
+#include <rfb/PixelBuffer.h>
+#include <rfb/Region.h>
+#include <rfb/ColourMap.h>
+#include <rfb/Exception.h>
+
+namespace rfb {
+
+ namespace win32 {
+
+ //
+ // -=- DIBSectionBuffer
+ //
+
+ class DIBSectionBuffer : public FullFramePixelBuffer, ColourMap {
+ public:
+ DIBSectionBuffer(HWND window);
+ DIBSectionBuffer(HDC device);
+ virtual ~DIBSectionBuffer();
+
+ virtual void setPF(const PixelFormat &pf);
+ virtual void setSize(int w, int h);
+
+ virtual int getStride() const {return stride;}
+
+ virtual ColourMap* getColourMap() const {return (ColourMap*)this;}
+
+ // - ColourMap interface
+ virtual void lookup(int index, int* r, int *g, int* b) {
+ *r = palette[index].r;
+ *g = palette[index].g;
+ *b = palette[index].b;
+ }
+
+ // Custom colourmap interface
+ void setColour(int index, int r, int g, int b) {
+ palette[index].r = r;
+ palette[index].g = g;
+ palette[index].b = b;
+ }
+ void refreshPalette();
+
+ // *** virtual void copyRect(const Rect &dest, const Point &move_by_delta);
+ public:
+ HBITMAP bitmap;
+ protected:
+ void recreateBuffer();
+ Colour palette[256];
+ int stride;
+ HWND window;
+ HDC device;
+ };
+
+ };
+
+};
+
+#endif // __RFB_WIN32_DIB_SECTION_BUFFER_H__
diff --git a/win/rfb_win32/DeviceContext.cxx b/win/rfb_win32/DeviceContext.cxx
new file mode 100644
index 0000000..4f70a1b
--- /dev/null
+++ b/win/rfb_win32/DeviceContext.cxx
@@ -0,0 +1,188 @@
+/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
+ *
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+ * USA.
+ */
+
+#include <rfb_win32/DeviceContext.h>
+#include <rfb_win32/CompatibleBitmap.h>
+#include <rfb_win32/BitmapInfo.h>
+#include <rdr/Exception.h>
+#include <rfb/LogWriter.h>
+
+using namespace rfb;
+using namespace win32;
+
+
+static LogWriter vlog("DeviceContext");
+
+PixelFormat DeviceContext::getPF() const {
+ return getPF(dc);
+}
+
+PixelFormat DeviceContext::getPF(HDC dc) {
+ PixelFormat format;
+ CompatibleBitmap bitmap(dc, 1, 1);
+
+ // -=- Get the bitmap format information
+ BitmapInfo bi;
+ memset(&bi, 0, sizeof(bi));
+ bi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
+ bi.bmiHeader.biBitCount = 0;
+ if (!::GetDIBits(dc, bitmap, 0, 1, NULL, (BITMAPINFO*)&bi, DIB_RGB_COLORS)) {
+ throw rdr::SystemException("unable to determine device pixel format", GetLastError());
+ }
+ if (!::GetDIBits(dc, bitmap, 0, 1, NULL, (BITMAPINFO*)&bi, DIB_RGB_COLORS)) {
+ throw rdr::SystemException("unable to determine pixel shifts/palette", GetLastError());
+ }
+
+ // Set the initial format information
+ format.trueColour = bi.bmiHeader.biBitCount > 8;
+ format.bigEndian = 0;
+ format.bpp = bi.bmiHeader.biBitCount;
+
+ if (format.trueColour) {
+ DWORD rMask=0, gMask=0, bMask=0;
+
+ // Which true colour format is the DIB section using?
+ switch (bi.bmiHeader.biCompression) {
+ case BI_RGB:
+ // Default RGB layout
+ switch (bi.bmiHeader.biBitCount) {
+ case 16:
+ // RGB 555 - High Colour
+ vlog.info("16-bit High Colour");
+ rMask = 0x7c00;
+ bMask = 0x001f;
+ gMask = 0x03e0;
+ break;
+ case 24:
+ case 32:
+ // RGB 888 - True Colour
+ vlog.info("24/32-bit High Colour");
+ rMask = 0xff0000;
+ gMask = 0x00ff00;
+ bMask = 0x0000ff;
+ break;
+ default:
+ vlog.error("bits per pixel %u not supported", bi.bmiHeader.biBitCount);
+ throw rdr::Exception("unknown bits per pixel specified");
+ };
+ break;
+ case BI_BITFIELDS:
+ // Custom RGB layout
+ rMask = bi.mask.red;
+ gMask = bi.mask.green;
+ bMask = bi.mask.blue;
+ vlog.info("%lu-bit BitFields: (%lx, %lx, %lx)",
+ bi.bmiHeader.biBitCount, rMask, gMask, bMask);
+ break;
+ };
+
+ // Convert the data we just retrieved
+ initMaxAndShift(rMask, &format.redMax, &format.redShift);
+ initMaxAndShift(gMask, &format.greenMax, &format.greenShift);
+ initMaxAndShift(bMask, &format.blueMax, &format.blueShift);
+
+ // Calculate the depth from the colour shifts
+ format.depth = 0;
+ Pixel bits = rMask | gMask | bMask;
+ while (bits) {
+ format.depth++;
+ bits = bits >> 1;
+ }
+
+ // Check that the depth & bpp are valid
+ if (format.depth > format.bpp) {
+ vlog.error("depth exceeds bits per pixel!");
+ format.bpp = format.depth;
+ }
+
+ // Correct the bits-per-pixel to something we're happy with
+ if (format.bpp <= 16)
+ format.bpp = 16;
+ else if (format.bpp <= 32)
+ format.bpp = 32;
+ } else {
+ // Palettised format - depth reflects number of colours,
+ // but bits-per-pixel is ALWAYS 8
+ format.depth = format.bpp;
+ if (format.bpp < 8)
+ format.bpp = 8;
+ vlog.info("%d-colour palettised", 1<<format.depth);
+ }
+
+ return format;
+}
+
+Rect DeviceContext::getClipBox() const {
+ return getClipBox(dc);
+}
+
+Rect DeviceContext::getClipBox(HDC dc) {
+ // Get the display dimensions
+ RECT cr;
+ if (!GetClipBox(dc, &cr))
+ throw rdr::SystemException("GetClipBox", GetLastError());
+ return Rect(cr.left, cr.top, cr.right, cr.bottom);
+}
+
+
+DeviceDC::DeviceDC(const TCHAR* deviceName) {
+ dc = ::CreateDC(_T("DISPLAY"), deviceName, NULL, NULL);
+ if (!dc)
+ throw rdr::SystemException("failed to create DeviceDC", GetLastError());
+}
+
+DeviceDC::~DeviceDC() {
+ if (dc)
+ DeleteDC(dc);
+}
+
+
+WindowDC::WindowDC(HWND wnd) : hwnd(wnd) {
+ dc = GetDC(wnd);
+ if (!dc)
+ throw rdr::SystemException("GetDC failed", GetLastError());
+}
+
+WindowDC::~WindowDC() {
+ if (dc)
+ ReleaseDC(hwnd, dc);
+}
+
+
+CompatibleDC::CompatibleDC(HDC existing) {
+ dc = CreateCompatibleDC(existing);
+ if (!dc)
+ throw rdr::SystemException("CreateCompatibleDC failed", GetLastError());
+}
+
+CompatibleDC::~CompatibleDC() {
+ if (dc)
+ DeleteDC(dc);
+}
+
+
+BitmapDC::BitmapDC(HDC hdc, HBITMAP hbitmap) : CompatibleDC(hdc){
+ oldBitmap = (HBITMAP)SelectObject(dc, hbitmap);
+ if (!oldBitmap)
+ throw rdr::SystemException("SelectObject to CompatibleDC failed",
+ GetLastError());
+}
+
+BitmapDC::~BitmapDC() {
+ SelectObject(dc, oldBitmap);
+}
diff --git a/win/rfb_win32/DeviceContext.h b/win/rfb_win32/DeviceContext.h
new file mode 100644
index 0000000..9d91cec
--- /dev/null
+++ b/win/rfb_win32/DeviceContext.h
@@ -0,0 +1,86 @@
+/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
+ *
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+ * USA.
+ */
+
+// DeviceContext base class, wrapping Windows HDC, plus some
+// helper classes tailored to particular types of DC, such as
+// window and device DCs.
+
+#ifndef __RFB_WIN32_DEVICECONTEXT_H__
+#define __RFB_WIN32_DEVICECONTEXT_H__
+
+#include <windows.h>
+#include <rfb/PixelFormat.h>
+#include <rfb/Rect.h>
+#include <rfb_win32/TCharArray.h>
+
+namespace rfb {
+
+ namespace win32 {
+
+ // Base class, providing methods to get the bounding (clip) box,
+ // and the pixel format, and access to the HDC itself.
+ class DeviceContext {
+ public:
+ DeviceContext() : dc(0) {}
+ virtual ~DeviceContext() {}
+ operator HDC() const {return dc;}
+ PixelFormat getPF() const;
+ static PixelFormat getPF(HDC dc);
+ Rect getClipBox() const;
+ static Rect getClipBox(HDC dc);
+ protected:
+ HDC dc;
+ };
+
+ // -=- DeviceContext that opens a specific display device
+ class DeviceDC : public DeviceContext {
+ public:
+ DeviceDC(const TCHAR* deviceName);
+ ~DeviceDC();
+ };
+
+ // Get a DC for a particular window's client area.
+ class WindowDC : public DeviceContext {
+ public:
+ WindowDC(HWND wnd);
+ virtual ~WindowDC();
+ protected:
+ HWND hwnd;
+ };
+
+ // Create a new DC, compatible with an existing one.
+ class CompatibleDC : public DeviceContext {
+ public:
+ CompatibleDC(HDC existing);
+ virtual ~CompatibleDC();
+ };
+
+ // Create a new DC, compatible with an existing one, and
+ // select the specified bitmap into it.
+ class BitmapDC : public CompatibleDC {
+ public:
+ BitmapDC(HDC hdc, HBITMAP hbitmap);
+ ~BitmapDC();
+ protected:
+ HBITMAP oldBitmap;
+ };
+
+ };
+};
+
+#endif
diff --git a/win/rfb_win32/DeviceFrameBuffer.cxx b/win/rfb_win32/DeviceFrameBuffer.cxx
new file mode 100644
index 0000000..8da894e
--- /dev/null
+++ b/win/rfb_win32/DeviceFrameBuffer.cxx
@@ -0,0 +1,289 @@
+/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
+ *
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+ * USA.
+ */
+
+// -=- DeviceFrameBuffer.cxx
+//
+// The DeviceFrameBuffer class encapsulates the pixel data of the system
+// display.
+
+#include <vector>
+#include <rfb_win32/DeviceFrameBuffer.h>
+#include <rfb_win32/DeviceContext.h>
+#include <rfb_win32/OSVersion.h>
+#include <rfb_win32/IconInfo.h>
+#include <rfb/VNCServer.h>
+#include <rfb/LogWriter.h>
+
+using namespace rfb;
+using namespace win32;
+
+static LogWriter vlog("DeviceFrameBuffer");
+
+BoolParameter DeviceFrameBuffer::useCaptureBlt("UseCaptureBlt",
+ "Use a slower capture method that ensures that alpha blended windows appear correctly",
+ true);
+
+
+// -=- DeviceFrameBuffer class
+
+DeviceFrameBuffer::DeviceFrameBuffer(HDC deviceContext, const Rect& wRect)
+ : DIBSectionBuffer(deviceContext), device(deviceContext), cursorBm(deviceContext),
+ ignoreGrabErrors(false)
+{
+
+ // -=- Firstly, let's check that the device has suitable capabilities
+
+ int capabilities = GetDeviceCaps(device, RASTERCAPS);
+ if (!(capabilities & RC_BITBLT)) {
+ throw Exception("device does not support BitBlt");
+ }
+ if (!(capabilities & RC_DI_BITMAP)) {
+ throw Exception("device does not support GetDIBits");
+ }
+ /*
+ if (GetDeviceCaps(device, PLANES) != 1) {
+ throw Exception("device does not support planar displays");
+ }
+ */
+
+ // -=- Get the display dimensions and pixel format
+
+ // Get the display dimensions
+ deviceCoords = DeviceContext::getClipBox(device);
+ if (!wRect.is_empty())
+ deviceCoords = wRect.translate(deviceCoords.tl);
+ int w = deviceCoords.width();
+ int h = deviceCoords.height();
+
+ // We can't handle uneven widths :(
+ if (w % 2) w--;
+
+ // Configure the underlying DIB to match the device
+ DIBSectionBuffer::setPF(DeviceContext::getPF(device));
+ DIBSectionBuffer::setSize(w, h);
+
+ // Configure the cursor buffer
+ cursorBm.setPF(format);
+
+ // Set up a palette if required
+ if (!format.trueColour)
+ updateColourMap();
+}
+
+DeviceFrameBuffer::~DeviceFrameBuffer() {
+}
+
+
+void
+DeviceFrameBuffer::setPF(const PixelFormat &pf) {
+ throw Exception("setPF not supported");
+}
+
+void
+DeviceFrameBuffer::setSize(int w, int h) {
+ throw Exception("setSize not supported");
+}
+
+
+#ifndef CAPTUREBLT
+#define CAPTUREBLT 0x40000000
+#endif
+
+void
+DeviceFrameBuffer::grabRect(const Rect &rect) {
+ BitmapDC tmpDC(device, bitmap);
+
+ // Map the rectangle coords from VNC Desktop-relative to device relative - usually (0,0)
+ Point src = desktopToDevice(rect.tl);
+
+ // Note: Microsoft's documentation lies directly about CAPTUREBLT and claims it works on 98/ME
+ // If you try CAPTUREBLT on 98 then you get blank output...
+ if (!::BitBlt(tmpDC, rect.tl.x, rect.tl.y, rect.width(), rect.height(), device, src.x, src.y,
+ (osVersion.isPlatformNT && useCaptureBlt) ? (CAPTUREBLT | SRCCOPY) : SRCCOPY)) {
+ if (ignoreGrabErrors)
+ vlog.error("BitBlt failed:%ld", GetLastError());
+ else
+ throw rdr::SystemException("BitBlt failed", GetLastError());
+ }
+}
+
+void
+DeviceFrameBuffer::grabRegion(const Region &rgn) {
+ std::vector<Rect> rects;
+ std::vector<Rect>::const_iterator i;
+ rgn.get_rects(&rects);
+ for(i=rects.begin(); i!=rects.end(); i++) {
+ grabRect(*i);
+ }
+ ::GdiFlush();
+}
+
+
+void copyDevicePaletteToDIB(HDC dc, DIBSectionBuffer* dib) {
+ // - Fetch the system palette for the framebuffer
+ PALETTEENTRY syspalette[256];
+ UINT entries = ::GetSystemPaletteEntries(dc, 0, 256, syspalette);
+
+ if (entries == 0) {
+ vlog.info("resorting to standard 16 color palette");
+ for (unsigned int i=0;i<256;i++) {
+ int v = (i%16) >= 8 ? 127 : 255;
+ syspalette[i].peRed = i & 1 ? v : 0;
+ syspalette[i].peGreen = i & 2 ? v : 0;
+ syspalette[i].peBlue = i & 4 ? v : 0;
+ }
+ } else {
+ vlog.info("framebuffer has %u palette entries", entries);
+ }
+
+ // - Update the bitmap's stored copy of the palette
+ for (unsigned int i=0;i<256;i++) {
+ int r, g, b;
+ r = (syspalette[i].peRed << 8) + 0x80;
+ g = (syspalette[i].peGreen << 8) + 0x80;
+ b = (syspalette[i].peBlue << 8) + 0x80;
+ dib->setColour(i, r, g, b);
+ }
+
+ // - Update the DIB section to use the palette
+ dib->refreshPalette();
+}
+
+
+void DeviceFrameBuffer::setCursor(HCURSOR hCursor, VNCServer* server)
+{
+ // - If hCursor is null then there is no cursor - clear the old one
+
+ if (hCursor == 0) {
+ server->setCursor(0, 0, Point(), 0, 0);
+ return;
+ }
+
+ try {
+
+ // - Get the size and other details about the cursor.
+
+ IconInfo iconInfo((HICON)hCursor);
+
+ BITMAP maskInfo;
+ if (!GetObject(iconInfo.hbmMask, sizeof(BITMAP), &maskInfo))
+ throw rdr::SystemException("GetObject() failed", GetLastError());
+ if (maskInfo.bmPlanes != 1)
+ throw rdr::Exception("unsupported multi-plane cursor");
+ if (maskInfo.bmBitsPixel != 1)
+ throw rdr::Exception("unsupported cursor mask format");
+
+ // - Create the cursor pixel buffer and mask storage
+ // NB: The cursor pixel buffer is NOT used here. Instead, we
+ // pass the cursorBm.data pointer directly, to save overhead.
+
+ cursor.setSize(maskInfo.bmWidth, maskInfo.bmHeight);
+ cursor.setPF(format);
+ cursor.hotspot = Point(iconInfo.xHotspot, iconInfo.yHotspot);
+
+ // - Get the AND and XOR masks. There is only an XOR mask if this is not a
+ // colour cursor.
+
+ if (!iconInfo.hbmColor)
+ cursor.setSize(cursor.width(), cursor.height() / 2);
+ rdr::U8Array mask(maskInfo.bmWidthBytes * maskInfo.bmHeight);
+ rdr::U8* xorMask = mask.buf + cursor.height() * maskInfo.bmWidthBytes;
+
+ if (!GetBitmapBits(iconInfo.hbmMask,
+ maskInfo.bmWidthBytes * maskInfo.bmHeight, mask.buf))
+ throw rdr::SystemException("GetBitmapBits failed", GetLastError());
+
+ // Configure the cursor bitmap
+ cursorBm.setSize(cursor.width(), cursor.height());
+
+ // Copy the palette into it if required
+ if (format.bpp <= 8)
+ copyDevicePaletteToDIB(device, &cursorBm);
+
+ // Draw the cursor into the bitmap
+ BitmapDC dc(device, cursorBm.bitmap);
+ if (!DrawIconEx(dc, 0, 0, hCursor, 0, 0, 0, NULL, DI_NORMAL | DI_COMPAT))
+ throw rdr::SystemException("unable to render cursor", GetLastError());
+
+ // Replace any XORed pixels with xorColour, because RFB doesn't support
+ // XORing of cursors. XORing is used for the I-beam cursor, which is most
+ // often used over a white background, but also sometimes over a black
+ // background. We set the XOR'd pixels to black, then draw a white outline
+ // around the whole cursor.
+
+ // *** should we replace any pixels not set in mask to zero, to ensure
+ // that irrelevant data doesn't screw compression?
+
+ bool doOutline = false;
+ if (!iconInfo.hbmColor) {
+ Pixel xorColour = format.pixelFromRGB(0, 0, 0, cursorBm.getColourMap());
+ for (int y = 0; y < cursor.height(); y++) {
+ bool first = true;
+ for (int x = 0; x < cursor.width(); x++) {
+ int byte = y * maskInfo.bmWidthBytes + x / 8;
+ int bit = 7 - x % 8;
+ if ((mask.buf[byte] & (1 << bit)) && (xorMask[byte] & (1 << bit)))
+ {
+ mask.buf[byte] &= ~(1 << bit);
+
+ switch (format.bpp) {
+ case 8:
+ ((rdr::U8*)cursorBm.data)[y * cursor.width() + x] = xorColour; break;
+ case 16:
+ ((rdr::U16*)cursorBm.data)[y * cursor.width() + x] = xorColour; break;
+ case 32:
+ ((rdr::U32*)cursorBm.data)[y * cursor.width() + x] = xorColour; break;
+ }
+
+ doOutline = true;
+ }
+ }
+ }
+ }
+
+ // Finally invert the AND mask so it's suitable for RFB and pack it into
+ // the minimum number of bytes per row.
+
+ int maskBytesPerRow = (cursor.width() + 7) / 8;
+
+ for (int j = 0; j < cursor.height(); j++) {
+ for (int i = 0; i < maskBytesPerRow; i++)
+ cursor.mask.buf[j * maskBytesPerRow + i]
+ = ~mask.buf[j * maskInfo.bmWidthBytes + i];
+ }
+
+ if (doOutline) {
+ vlog.debug("drawing cursor outline!");
+ memcpy(cursor.data, cursorBm.data, cursor.dataLen());
+ cursor.drawOutline(format.pixelFromRGB(0xffff, 0xffff, 0xffff, cursorBm.getColourMap()));
+ memcpy(cursorBm.data, cursor.data, cursor.dataLen());
+ }
+
+ server->setCursor(cursor.width(), cursor.height(), cursor.hotspot,
+ cursorBm.data, cursor.mask.buf);
+ } catch (rdr::Exception& e) {
+ vlog.error(e.str());
+ }
+}
+
+
+void
+DeviceFrameBuffer::updateColourMap() {
+ if (!format.trueColour)
+ copyDevicePaletteToDIB(device, this);
+}
diff --git a/win/rfb_win32/DeviceFrameBuffer.h b/win/rfb_win32/DeviceFrameBuffer.h
new file mode 100644
index 0000000..7718c33
--- /dev/null
+++ b/win/rfb_win32/DeviceFrameBuffer.h
@@ -0,0 +1,106 @@
+/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
+ *
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+ * USA.
+ */
+
+// -=- DeviceFrameBuffer.h
+//
+// The DeviceFrameBuffer class encapsulates the pixel data of a supplied
+// Device Context Handle (HDC)
+
+// *** THIS INTERFACE NEEDS TIDYING TO SEPARATE COORDINATE SYSTEMS BETTER ***
+
+#ifndef __RFB_WIN32_DEVICE_FRAME_BUFFER_H__
+#define __RFB_WIN32_DEVICE_FRAME_BUFFER_H__
+
+#include <windows.h>
+#include <rfb_win32/DIBSectionBuffer.h>
+#include <rfb/Cursor.h>
+#include <rfb/Region.h>
+#include <rfb/Exception.h>
+#include <rfb/Configuration.h>
+
+namespace rfb {
+
+ class VNCServer;
+
+ namespace win32 {
+
+ // -=- DeviceFrameBuffer interface
+
+ // DeviceFrameBuffer is passed an HDC referring to a window or to
+ // the entire display. It may also be passed a rectangle specifying
+ // the Device-relative coordinates of the actual rectangle to treat
+ // as the desktop.
+
+ // Coordinate systems start getting really annoying here. There are
+ // three different "origins" to which coordinates might be relative:
+ //
+ // Desktop - VNC coordinates, top-left always (0,0)
+ // Device - DC coordinates. Top-left *usually (0,0) but could be other.
+ // Window - coordinates relative to the specified sub-rectangle within
+ // the supplied DC.
+ // Screen - Coordinates relative to the entire Windows virtual screen.
+ // The virtual screen includes all monitors that are part of
+ // the Windows desktop.
+
+ // The data member is made to point to an internal mirror of the
+ // current display data. Individual rectangles or regions of the
+ // buffer can be brought up to date by calling the grab functions.
+
+ class DeviceFrameBuffer : public DIBSectionBuffer {
+ public:
+ DeviceFrameBuffer(HDC deviceContext, const Rect& area_=Rect());
+ virtual ~DeviceFrameBuffer();
+
+ // - FrameBuffer overrides
+
+ virtual void grabRect(const Rect &rect);
+ virtual void grabRegion(const Region ®ion);
+
+ // - DIBSectionBuffer overrides
+
+ virtual void setPF(const PixelFormat& pf);
+ virtual void setSize(int w, int h);
+
+ // - DeviceFrameBuffer specific methods
+
+ void setCursor(HCURSOR c, VNCServer* server);
+ void updateColourMap();
+
+ // Set whether grabRect should ignore errors or throw exceptions
+ // Only set this if you are sure you'll capture the errors some other way!
+ void setIgnoreGrabErrors(bool ie) {ignoreGrabErrors=ie;}
+
+ static BoolParameter useCaptureBlt;
+
+ protected:
+ // Translate supplied Desktop coordinates into Device-relative coordinates
+ // This translation may have been affected at start-time by the supplied sub-rect.
+ Point desktopToDevice(const Point p) const {return p.translate(deviceCoords.tl);}
+
+ HDC device;
+ DIBSectionBuffer cursorBm;
+ Cursor cursor;
+ Rect deviceCoords;
+ bool ignoreGrabErrors;
+ };
+
+ };
+
+};
+
+#endif // __RFB_WIN32_DEVICE_FRAME_BUFFER_H__
diff --git a/win/rfb_win32/Dialog.cxx b/win/rfb_win32/Dialog.cxx
new file mode 100644
index 0000000..398334f
--- /dev/null
+++ b/win/rfb_win32/Dialog.cxx
@@ -0,0 +1,391 @@
+/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
+ *
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+ * USA.
+ */
+
+// -=- Dialog.cxx
+
+// Base-class for any Dialog classes we might require
+
+#include <rfb_win32/Dialog.h>
+#include <rfb_win32/TCharArray.h>
+#include <rfb/LogWriter.h>
+#include <rdr/Exception.h>
+#include <rfb_win32/Win32Util.h>
+
+#ifdef _DIALOG_CAPTURE
+#ifdef PropSheet_IndexToId
+#include <rfb_win32/DeviceFrameBuffer.h>
+#include <extra/LoadBMP.cxx>
+#else
+#undef _DIALOG_CAPTURE
+#pragma message(" NOTE: Not building Dialog Capture support.")
+#endif
+#endif
+
+using namespace rfb;
+using namespace rfb::win32;
+
+static LogWriter dlog("Dialog");
+static LogWriter plog("PropSheet");
+
+
+Dialog::Dialog(HINSTANCE inst_)
+: inst(inst_), alreadyShowing(false), handle(0)
+{
+}
+
+Dialog::~Dialog()
+{
+}
+
+
+bool Dialog::showDialog(const TCHAR* resource, HWND owner)
+{
+ if (alreadyShowing) return false;
+ handle = 0;
+ alreadyShowing = true;
+ INT_PTR result = DialogBoxParam(inst, resource, owner,
+ staticDialogProc, (LPARAM)this);
+ if (result<0)
+ throw rdr::SystemException("DialogBoxParam failed", GetLastError());
+ alreadyShowing = false;
+ return (result == 1);
+}
+
+
+bool Dialog::isItemChecked(int id) {
+ return SendMessage(GetDlgItem(handle, id), BM_GETCHECK, 0, 0) == BST_CHECKED;
+}
+int Dialog::getItemInt(int id) {
+ BOOL trans;
+ int result = GetDlgItemInt(handle, id, &trans, TRUE);
+ if (!trans)
+ throw rdr::Exception("unable to read dialog Int");
+ return result;
+}
+TCHAR* Dialog::getItemString(int id) {
+ TCharArray tmp(256);
+ if (!GetDlgItemText(handle, id, tmp.buf, 256))
+ tmp.buf[0] = 0;
+ return tmp.takeBuf();
+}
+
+void Dialog::setItemChecked(int id, bool state) {
+ dlog.debug("bool[%d]=%d", id, (int)state);
+ SendMessage(GetDlgItem(handle, id), BM_SETCHECK, state ? BST_CHECKED : BST_UNCHECKED, 0);
+}
+void Dialog::setItemInt(int id, int value) {
+ dlog.debug("int[%d]=%d", id, value);
+ SetDlgItemInt(handle, id, value, TRUE);
+}
+void Dialog::setItemString(int id, const TCHAR* s) {
+ dlog.debug("string[%d]=%s", id, (const char*)CStr(s));
+ SetDlgItemText(handle, id, s);
+}
+
+
+void Dialog::enableItem(int id, bool state) {
+ dlog.debug("enable[%d]=%d", id, (int)state);
+ EnableWindow(GetDlgItem(handle, id), state);
+}
+
+
+
+
+BOOL CALLBACK Dialog::staticDialogProc(HWND hwnd, UINT msg,
+ WPARAM wParam, LPARAM lParam)
+{
+ if (msg == WM_INITDIALOG)
+ SetWindowLong(hwnd, GWL_USERDATA, (LONG)lParam);
+
+ LONG self = GetWindowLong(hwnd, GWL_USERDATA);
+ if (!self) return FALSE;
+
+ return ((Dialog*)self)->dialogProc(hwnd, msg, wParam, lParam);
+}
+
+BOOL Dialog::dialogProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ switch (msg) {
+
+ case WM_INITDIALOG:
+ handle = hwnd;
+ initDialog();
+ return TRUE;
+
+ case WM_COMMAND:
+ switch (LOWORD(wParam)) {
+ case IDOK:
+ if (onOk()) {
+ EndDialog(hwnd, 1);
+ return TRUE;
+ }
+ return FALSE;
+ case IDCANCEL:
+ EndDialog(hwnd, 0);
+ return TRUE;
+ default:
+ return onCommand(LOWORD(wParam), HIWORD(wParam));
+ };
+
+ case WM_HELP:
+ return onHelp(((HELPINFO*)lParam)->iCtrlId);
+
+ }
+
+ return FALSE;
+}
+
+
+PropSheetPage::PropSheetPage(HINSTANCE inst, const TCHAR* id) : Dialog(inst), propSheet(0) {
+ page.dwSize = sizeof(page);
+ page.dwFlags = 0; // PSP_USECALLBACK;
+ page.hInstance = inst;
+ page.pszTemplate = id;
+ page.pfnDlgProc = staticPageProc;
+ page.lParam = (LPARAM)this;
+ page.pfnCallback = 0; // staticPageProc;
+}
+
+PropSheetPage::~PropSheetPage() {
+}
+
+
+BOOL CALLBACK PropSheetPage::staticPageProc(HWND hwnd, UINT msg,
+ WPARAM wParam, LPARAM lParam)
+{
+ if (msg == WM_INITDIALOG)
+ SetWindowLong(hwnd, GWL_USERDATA, ((PROPSHEETPAGE*)lParam)->lParam);
+
+ LONG self = GetWindowLong(hwnd, GWL_USERDATA);
+ if (!self) return FALSE;
+
+ return ((PropSheetPage*)self)->dialogProc(hwnd, msg, wParam, lParam);
+}
+
+BOOL PropSheetPage::dialogProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ switch (msg) {
+
+ case WM_INITDIALOG:
+ handle = hwnd;
+ initDialog();
+ return TRUE;
+
+ case WM_NOTIFY:
+ switch (((NMHDR*)lParam)->code) {
+ case PSN_APPLY:
+ onOk();
+ return FALSE;
+ };
+ return FALSE;
+
+ case WM_COMMAND:
+ return onCommand(LOWORD(wParam), HIWORD(wParam));
+
+ case WM_HELP:
+ return onHelp(((HELPINFO*)lParam)->iCtrlId);
+
+ }
+
+ return FALSE;
+}
+
+
+PropSheet::PropSheet(HINSTANCE inst_, const TCHAR* title_, std::list<PropSheetPage*> pages_, HICON icon_)
+: title(tstrDup(title_)), inst(inst_), pages(pages_), alreadyShowing(0), handle(0), icon(icon_) {
+}
+
+PropSheet::~PropSheet() {
+}
+
+
+// For some reason, DLGTEMPLATEEX isn't defined in the Windows headers - go figure...
+struct DLGTEMPLATEEX {
+ WORD dlgVer;
+ WORD signature;
+ DWORD helpID;
+ DWORD exStyle;
+ DWORD style;
+ WORD cDlgItems;
+ short x;
+ short y;
+ short cx;
+ short cy;
+};
+
+static int CALLBACK removeCtxtHelp(HWND hwnd, UINT message, LPARAM lParam) {
+ if (message == PSCB_PRECREATE) {
+ // Remove the context-help style, to remove the titlebar ? button
+ // *** Nasty hack to cope with new & old dialog template formats...
+ if (((DLGTEMPLATEEX*)lParam)->signature == 0xffff)
+ ((DLGTEMPLATEEX*)lParam)->style &= ~DS_CONTEXTHELP;
+ else
+ ((LPDLGTEMPLATE)lParam)->style &= ~DS_CONTEXTHELP;
+ }
+ return TRUE;
+}
+
+
+bool PropSheet::showPropSheet(HWND owner, bool showApply, bool showCtxtHelp, bool capture) {
+ if (alreadyShowing) return false;
+ alreadyShowing = true;
+ int count = pages.size();
+
+ HPROPSHEETPAGE* hpages = new HPROPSHEETPAGE[count];
+ try {
+ // Create the PropertSheet page GDI objects.
+ std::list<PropSheetPage*>::iterator pspi;
+ int i = 0;
+ for (pspi=pages.begin(); pspi!=pages.end(); pspi++) {
+ hpages[i] = CreatePropertySheetPage(&((*pspi)->page));
+ (*pspi)->setPropSheet(this);
+ i++;
+ }
+
+ // Initialise and create the PropertySheet itself
+ PROPSHEETHEADER header;
+ header.dwSize = PROPSHEETHEADER_V1_SIZE;
+ header.dwFlags = PSH_MODELESS | (showApply ? 0 : PSH_NOAPPLYNOW) | (showCtxtHelp ? 0 : PSH_USECALLBACK);
+ header.pfnCallback = removeCtxtHelp;
+ header.hwndParent = owner;
+ header.hInstance = inst;
+ header.pszCaption = title.buf;
+ header.nPages = count;
+ header.nStartPage = 0;
+ header.phpage = hpages;
+ if (icon) {
+ header.hIcon = icon;
+ header.dwFlags |= PSH_USEHICON;
+ }
+
+ handle = (HWND)PropertySheet(&header);
+ if ((handle == 0) || (handle == (HWND)-1))
+ throw rdr::SystemException("PropertySheet failed", GetLastError());
+ centerWindow(handle, owner);
+ plog.info("created %lx", handle);
+
+#ifdef _DIALOG_CAPTURE
+ if (capture) {
+ plog.info("capturing \"%s\"", (const char*)CStr(title.buf));
+ char* tmpdir = getenv("TEMP");
+ HDC dc = GetWindowDC(handle);
+ DeviceFrameBuffer fb(dc);
+ int i=0;
+ while (true) {
+ int id = PropSheet_IndexToId(handle, i);
+ if (!id) break;
+ PropSheet_SetCurSelByID(handle, id);
+ MSG msg;
+ while (PeekMessage(&msg, handle, 0, 0, PM_REMOVE)) {
+ if (!PropSheet_IsDialogMessage(handle, &msg))
+ DispatchMessage(&msg);
+ }
+ fb.grabRect(fb.getRect());
+ TCHAR title[128];
+ if (!GetWindowText(PropSheet_GetCurrentPageHwnd(handle), title, sizeof(title)))
+ _stprintf(title, _T("capture%d"), i);
+ CharArray pageTitle(strDup(title));
+ for (int j=0; j<strlen(pageTitle.buf); j++) {
+ if (pageTitle.buf[j] == '/' || pageTitle.buf[j] == '\\' || pageTitle.buf[j] == ':')
+ pageTitle.buf[j] = '-';
+ }
+ char filename[256];
+ sprintf(filename, "%s\\%s.bmp", tmpdir, pageTitle.buf);
+ vlog.debug("writing to %s", filename);
+ saveBMP(filename, &fb);
+ i++;
+ }
+ ReleaseDC(handle, dc);
+ } else {
+#endif
+ try {
+ if (owner)
+ EnableWindow(owner, FALSE);
+ // Run the PropertySheet
+ MSG msg;
+ while (GetMessage(&msg, 0, 0, 0)) {
+ if (!PropSheet_IsDialogMessage(handle, &msg))
+ DispatchMessage(&msg);
+ if (!PropSheet_GetCurrentPageHwnd(handle))
+ break;
+ }
+ if (owner)
+ EnableWindow(owner, TRUE);
+ } catch (...) {
+ if (owner)
+ EnableWindow(owner, TRUE);
+ throw;
+ }
+#ifdef _DIALOG_CAPTURE
+ }
+#endif
+
+ plog.info("finished %lx", handle);
+
+ DestroyWindow(handle);
+ handle = 0;
+ alreadyShowing = false;
+
+ // Clear up the pages' GDI objects
+ for (pspi=pages.begin(); pspi!=pages.end(); pspi++)
+ (*pspi)->setPropSheet(0);
+ delete [] hpages; hpages = 0;
+
+ return true;
+ } catch (rdr::Exception) {
+ alreadyShowing = false;
+
+ std::list<PropSheetPage*>::iterator pspi;
+ for (pspi=pages.begin(); pspi!=pages.end(); pspi++)
+ (*pspi)->setPropSheet(0);
+ delete [] hpages; hpages = 0;
+
+ throw;
+ }
+}
+
+void PropSheet::reInitPages() {
+ plog.debug("reInitPages %lx", handle);
+ std::list<PropSheetPage*>::iterator pspi;
+ for (pspi=pages.begin(); pspi!=pages.end(); pspi++) {
+ if ((*pspi)->handle)
+ (*pspi)->initDialog();
+ }
+}
+
+bool PropSheet::commitPages() {
+ plog.debug("commitPages %lx", handle);
+ bool result = true;
+ std::list<PropSheetPage*>::iterator pspi;
+ for (pspi=pages.begin(); pspi!=pages.end(); pspi++) {
+ if ((*pspi)->handle)
+ result = result && (*pspi)->onOk();
+ }
+ return result;
+}
+
+
+void PropSheetPage::setChanged(bool changed) {
+ if (propSheet) {
+ plog.debug("setChanged[%lx(%lx)]=%d", handle, propSheet->handle, (int)changed);
+ if (changed)
+ PropSheet_Changed(propSheet->handle, handle);
+ else
+ PropSheet_UnChanged(propSheet->handle, handle);
+ }
+}
diff --git a/win/rfb_win32/Dialog.h b/win/rfb_win32/Dialog.h
new file mode 100644
index 0000000..9784ba4
--- /dev/null
+++ b/win/rfb_win32/Dialog.h
@@ -0,0 +1,158 @@
+/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
+ *
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+ * USA.
+ */
+
+// -=- RegConfig.h
+
+// Class which monitors the registry and reads in the registry settings
+// whenever they change, or are added or removed.
+
+#ifndef __RFB_WIN32_DIALOG_H__
+#define __RFB_WIN32_DIALOG_H__
+
+#include <windows.h>
+#include <prsht.h>
+#include <list>
+#include <rfb_win32/TCharArray.h>
+
+namespace rfb {
+
+ namespace win32 {
+
+ // Dialog - A simple Win32 Dialog box. A derived class of Dialog overrides the
+ // initDialog(), command() and ok() methods to take appropriate action. A
+ // simple dialog box can be displayed by creating a Dialog object and calling
+ // show().
+
+ class Dialog {
+ public:
+
+ Dialog(HINSTANCE inst);
+ virtual ~Dialog();
+
+ // showDialog() displays the dialog box. It returns when it has been dismissed,
+ // returning true if "OK" was pressed, false otherwise. The resource
+ // argument identifies the dialog resource (often a MAKEINTRESOURCE macro
+ // expansion), and owner is an optional window handle - the corresponding
+ // window is disabled while the dialog box is displayed.
+
+ bool showDialog(const TCHAR* resource, HWND owner=0);
+
+ // initDialog() is called upon receipt of the WM_INITDIALOG message.
+
+ virtual void initDialog() {}
+
+ // onCommand() is called upon receipt of a WM_COMMAND message item other than IDOK
+ // or IDCANCEL. It should return true if the command has been handled.
+
+ virtual bool onCommand(int item, int cmd) { return false; }
+
+ // onHelp() is called upon receipt of a WM_MENU message. This indicates that
+ // context-specific help should be displayed, for a dialog control, for example.
+ // It should return true if the command has been handled.
+
+ virtual bool onHelp(int item) { return false; }
+
+ // onOk() is called when the OK button is pressed. The hwnd argument is the
+ // dialog box's window handle.
+
+ virtual bool onOk() { return true; }
+
+ // Read the states of items
+ bool isItemChecked(int id);
+ int getItemInt(int id);
+ TCHAR* getItemString(int id); // Recipient owns string storage
+
+ // Set the states of items
+ void setItemChecked(int id, bool state);
+ void setItemInt(int id, int value);
+ void setItemString(int id, const TCHAR* s);
+
+ // enableItem is used to grey out an item, making it inaccessible, or to
+ // re-enable it.
+ void enableItem(int id, bool state);
+
+ protected:
+ static BOOL CALLBACK staticDialogProc(HWND hwnd, UINT msg,
+ WPARAM wParam, LPARAM lParam);
+ virtual BOOL dialogProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam);
+ HINSTANCE inst;
+ HWND handle;
+ bool alreadyShowing;
+ };
+
+ // PropertySheetPage
+ // Class used to define property pages within a PropertySheet.
+ // Each page is associated with a particular dialog resource, indicated by
+ // the "id" parameter supplied to the constructor.
+
+ class PropSheetPage;
+
+ class PropSheet {
+ public:
+ PropSheet(HINSTANCE inst, const TCHAR* title, std::list<PropSheetPage*> pages, HICON icon=0);
+ virtual ~PropSheet();
+
+ // Display the PropertySheet
+ bool showPropSheet(HWND owner, bool showApply = false, bool showCtxtHelp = false, bool capture=false);
+
+ // Calls initDialog again for each page that has already had it called.
+ // Note: If a page hasn't been seen yet, it won't have been called.
+ // Note: This must only be called while the property sheet is visible.
+ void reInitPages();
+
+ // Calls onOk for each page that has had initDialog called, and returns
+ // false if any one of them returns false, or true otherwise. ALL the
+ // onOk() methods will be called, even if one of them fails.
+ // Note: If a page hasn't been seen yet, it won't have been called.
+ // Note: This must only be called while the property sheet is visible.
+ bool commitPages();
+
+ friend class PropSheetPage;
+
+ protected:
+ HWND owner;
+ HICON icon;
+ std::list<PropSheetPage*> pages;
+ HINSTANCE inst;
+ TCharArray title;
+ HWND handle;
+ bool alreadyShowing;
+ };
+
+ class PropSheetPage : public Dialog {
+ public:
+ PropSheetPage(HINSTANCE inst, const TCHAR* id);
+ virtual ~PropSheetPage();
+
+ void setChanged(bool changed);
+
+ friend class PropSheet;
+
+ protected:
+ void setPropSheet(PropSheet* ps) {propSheet = ps;};
+ static BOOL CALLBACK staticPageProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam);
+ virtual BOOL dialogProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam);
+ PROPSHEETPAGE page;
+ PropSheet* propSheet;
+ };
+
+ };
+
+};
+
+#endif // __RFB_WIN32_DIALOG_H__
diff --git a/win/rfb_win32/DynamicFn.cxx b/win/rfb_win32/DynamicFn.cxx
new file mode 100644
index 0000000..e933f24
--- /dev/null
+++ b/win/rfb_win32/DynamicFn.cxx
@@ -0,0 +1,45 @@
+/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
+ *
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+ * USA.
+ */
+
+#include <rfb_win32/DynamicFn.h>
+#include <rfb_win32/TCharArray.h>
+#include <rfb/LogWriter.h>
+
+using namespace rfb;
+using namespace win32;
+
+static LogWriter vlog("DynamicFn");
+
+
+DynamicFnBase::DynamicFnBase(const TCHAR* dllName, const char* fnName) : dllHandle(0), fnPtr(0) {
+ dllHandle = LoadLibrary(dllName);
+ if (!dllHandle) {
+ vlog.info("DLL %s not found (%d)", (const char*)CStr(dllName), GetLastError());
+ return;
+ }
+ fnPtr = GetProcAddress(dllHandle, fnName);
+ if (!fnPtr)
+ vlog.info("proc %s not found in %s (%d)", fnName, (const char*)CStr(dllName), GetLastError());
+}
+
+DynamicFnBase::~DynamicFnBase() {
+ if (dllHandle)
+ FreeLibrary(dllHandle);
+}
+
+
diff --git a/win/rfb_win32/DynamicFn.h b/win/rfb_win32/DynamicFn.h
new file mode 100644
index 0000000..57fdbec
--- /dev/null
+++ b/win/rfb_win32/DynamicFn.h
@@ -0,0 +1,51 @@
+/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
+ *
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+ * USA.
+ */
+
+// Helper class managing dynamic linkage to DLL functions.
+
+#ifndef __RFB_WIN32_DYNAMICFN_H__
+#define __RFB_WIN32_DYNAMICFN_H__
+
+#include <windows.h>
+
+namespace rfb {
+ namespace win32 {
+
+ class DynamicFnBase {
+ public:
+ DynamicFnBase(const TCHAR* dllName, const char* fnName);
+ ~DynamicFnBase();
+ bool isValid() const {return fnPtr != 0;}
+ protected:
+ void* fnPtr;
+ HMODULE dllHandle;
+ private:
+ DynamicFnBase(const DynamicFnBase&);
+ DynamicFnBase operator=(const DynamicFnBase&);
+ };
+
+ template<class T> class DynamicFn : public DynamicFnBase {
+ public:
+ DynamicFn(const TCHAR* dllName, const char* fnName) : DynamicFnBase(dllName, fnName) {}
+ T operator *() const {return (T)fnPtr;};
+ };
+
+ };
+};
+
+#endif
diff --git a/win/rfb_win32/EventManager.cxx b/win/rfb_win32/EventManager.cxx
new file mode 100644
index 0000000..0f9993b
--- /dev/null
+++ b/win/rfb_win32/EventManager.cxx
@@ -0,0 +1,103 @@
+/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
+ *
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+ * USA.
+ */
+
+#include <rfb_win32/EventManager.h>
+#include <rdr/Exception.h>
+#include <rfb/LogWriter.h>
+
+using namespace rfb;
+using namespace rfb::win32;
+
+static LogWriter vlog("EventManager");
+
+
+EventManager::EventManager() : eventCount(0) {
+}
+
+EventManager::~EventManager() {
+}
+
+
+bool EventManager::addEvent(HANDLE event, EventHandler* ecb) {
+ if (eventCount >= MAXIMUM_WAIT_OBJECTS-1)
+ return false;
+ events[eventCount] = event;
+ handlers[eventCount] = ecb;
+ eventCount++;
+ return true;
+}
+
+void EventManager::removeEvent(HANDLE event) {
+ for (int i=0; i<eventCount; i++) {
+ if (events[i] == event) {
+ for (int j=i; j<eventCount-1; j++) {
+ events[j] = events[j+1];
+ handlers[j] = handlers[j+1];
+ }
+ eventCount--;
+ return;
+ }
+ }
+ throw rdr::Exception("Event not registered");
+}
+
+
+int EventManager::checkTimeouts() {
+ return 0;
+}
+
+BOOL EventManager::getMessage(MSG* msg, HWND hwnd, UINT minMsg, UINT maxMsg) {
+ while (true) {
+ // - Process any pending timeouts
+ DWORD timeout = checkTimeouts();
+ if (timeout == 0)
+ timeout = INFINITE;
+
+ // - Events take precedence over messages
+ DWORD result;
+ if (eventCount) {
+ // - Check whether any events are set
+ result = WaitForMultipleObjects(eventCount, events, FALSE, 0);
+ if (result == WAIT_TIMEOUT) {
+ // - No events are set, so check for messages
+ if (PeekMessage(msg, hwnd, minMsg, maxMsg, PM_REMOVE))
+ return msg->message != WM_QUIT;
+
+ // - Block waiting for an event to be set, or a message
+ result = MsgWaitForMultipleObjects(eventCount, events, FALSE, timeout,
+ QS_ALLINPUT);
+ if (result == WAIT_OBJECT_0 + eventCount) {
+ // - Return the message, if any
+ if (PeekMessage(msg, hwnd, minMsg, maxMsg, PM_REMOVE))
+ return msg->message != WM_QUIT;
+ continue;
+ }
+ }
+ } else
+ return GetMessage(msg, hwnd, minMsg, maxMsg);
+
+ if ((result >= WAIT_OBJECT_0) && (result < (WAIT_OBJECT_0 + eventCount))) {
+ // - An event was set - call the handler
+ int index = result - WAIT_OBJECT_0;
+ handlers[index]->processEvent(events[index]);
+ } else if (result == WAIT_FAILED) {
+ // - An error has occurred, so return the error status code
+ return -1;
+ }
+ }
+}
diff --git a/win/rfb_win32/EventManager.h b/win/rfb_win32/EventManager.h
new file mode 100644
index 0000000..bb66e34
--- /dev/null
+++ b/win/rfb_win32/EventManager.h
@@ -0,0 +1,77 @@
+/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
+ *
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+ * USA.
+ */
+
+// -=- EventManager.h
+
+// Win32 event manager. Caller supplies event & handler pairs and
+// then uses getMessage() in place of ::GetMessage() in the main
+// loop. EventManager calls the event handler whenever the event
+// is set.
+// Ownership of events remains with the caller.
+// It is the responsibility of handlers to reset events.
+
+#ifndef __RFB_WIN32_EVENT_MGR_H__
+#define __RFB_WIN32_EVENT_MGR_H__
+
+#include <rfb_win32/Win32Util.h>
+
+namespace rfb {
+ namespace win32 {
+
+ class EventHandler {
+ public:
+ virtual ~EventHandler() {}
+ virtual void processEvent(HANDLE event) = 0;
+ };
+
+ class EventManager {
+ public:
+ EventManager();
+ virtual ~EventManager();
+
+ // Add a Win32 event & handler for it
+ // NB: The handler must call ResetEvent on the event.
+ // NB: The caller retains ownership of the event.
+ virtual bool addEvent(HANDLE event, EventHandler* ecb);
+
+ // Remove a Win32 event
+ virtual void removeEvent(HANDLE event);
+
+ // getMessage
+ // Waits for a message to become available on the thread's message queue,
+ // and returns it. If any registered events become set while waiting then
+ // their handlers are called before returning.
+ // Returns zero if the message is WM_QUIT, -1 in case of error, >0 otherwise.
+ virtual BOOL getMessage(MSG* msg, HWND hwnd, UINT minMsg, UINT maxMsg);
+
+ protected:
+ // checkTimeouts
+ // Derived classes should override this to perform any extra processing,
+ // returning the maximum number of milliseconds after which the callback
+ // should be called again.
+ virtual int checkTimeouts();
+
+ HANDLE events[MAXIMUM_WAIT_OBJECTS];
+ EventHandler* handlers[MAXIMUM_WAIT_OBJECTS-1];
+ int eventCount;
+ };
+
+ };
+};
+
+#endif
diff --git a/win/rfb_win32/FolderManager.cxx b/win/rfb_win32/FolderManager.cxx
new file mode 100644
index 0000000..a2fa08d
--- /dev/null
+++ b/win/rfb_win32/FolderManager.cxx
@@ -0,0 +1,279 @@
+/* Copyright (C) 2005 TightVNC Team. All Rights Reserved.
+ *
+ * Developed by Dennis Syrovatsky.
+ *
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+ * USA.
+ *
+ * TightVNC distribution homepage on the Web: http://www.tightvnc.com/
+ *
+ */
+
+// -=- FolderManager.cxx
+
+#include <rfb_win32/FolderManager.h>
+
+using namespace rfb;
+using namespace rfb::win32;
+
+FolderManager::FolderManager()
+{
+
+}
+
+FolderManager::~FolderManager()
+{
+
+}
+
+bool
+FolderManager::createDir(char *pFullPath)
+{
+ if (CreateDirectory(pFullPath, NULL) == 0) return false;
+
+ return true;
+}
+
+bool
+FolderManager::renameIt(char *pPath, char *pOldName, char *pNewName)
+{
+ char fullOldName[FT_FILENAME_SIZE];
+ char fullNewName[FT_FILENAME_SIZE];
+
+ sprintf(fullOldName, "%s\\%s", pPath, pOldName);
+ sprintf(fullNewName, "%s\\%s", pPath, pNewName);
+
+ return renameIt(fullOldName, fullNewName);
+}
+
+bool
+FolderManager::renameIt(char *pOldName, char *pNewName)
+{
+ if (MoveFile(pOldName, pNewName)) return true;
+
+ return false;
+}
+
+bool
+FolderManager::deleteIt(char *pFullPath)
+{
+ FileInfo fileInfo;
+
+ FILEINFO FIStruct;
+ if (!getInfo(pFullPath, &FIStruct)) return false;
+
+ fileInfo.add(&FIStruct);
+
+ return deleteIt(&fileInfo);
+}
+
+bool
+FolderManager::deleteIt(char *pPrefix, FileInfo *pFI)
+{
+ char buf[FT_FILENAME_SIZE];
+ for (unsigned int i = 0; i < pFI->getNumEntries(); i++) {
+ sprintf(buf, "%s\\%s", pPrefix, pFI->getNameAt(i));
+ pFI->setNameAt(i,buf);
+ }
+ return deleteIt(pFI);
+}
+
+bool
+FolderManager::deleteIt(FileInfo *pFI)
+{
+ unsigned int num = pFI->getNumEntries();
+ unsigned int last = num - 1;
+
+ while (num > 0) {
+ if (pFI->getFlagsAt(last) & FT_ATTR_DIR) {
+ if (RemoveDirectory(pFI->getNameAt(last)) == 0) {
+ if (GetLastError() == ERROR_DIR_NOT_EMPTY) {
+ if (!getFolderInfoWithPrefix(pFI->getNameAt(last), pFI)) {
+ pFI->free();
+ return false;
+ }
+ }
+ } else {
+ pFI->deleteAt(last);
+ }
+ } else {
+ if (DeleteFile(pFI->getNameAt(last)) == 0) {
+ pFI->free();
+ return false;
+ } else {
+ pFI->deleteAt(last);
+ }
+ }
+ num = pFI->getNumEntries();
+ last = num - 1;
+ }
+
+ return true;
+}
+
+bool
+FolderManager::getFolderInfoWithPrefix(char *pPrefix, FileInfo *pFileInfo)
+{
+ char prefix[FT_FILENAME_SIZE];
+ strcpy(prefix, pPrefix);
+
+ FileInfo tmpFileInfo;
+ if (!getDirInfo(prefix, &tmpFileInfo, 0)) {
+ tmpFileInfo.free();
+ return false;
+ } else {
+ char buf[FT_FILENAME_SIZE];
+ for (unsigned int i = 0; i < tmpFileInfo.getNumEntries(); i++) {
+ sprintf(buf, "%s\\%s", prefix, tmpFileInfo.getNameAt(i));
+ pFileInfo->add(buf, tmpFileInfo.getSizeAt(i), tmpFileInfo.getDataAt(i), tmpFileInfo.getFlagsAt(i));
+ }
+ }
+ tmpFileInfo.free();
+ return true;
+}
+
+bool
+FolderManager::getDirInfo(char *pPath, FileInfo *pFileInfo, unsigned int dirOnly)
+{
+ if (strlen(pPath) == 0) return getDrivesInfo(pFileInfo);
+
+ char path[FT_FILENAME_SIZE];
+ sprintf(path, "%s\\*", pPath);
+
+ WIN32_FIND_DATA FindFileData;
+ SetErrorMode(SEM_FAILCRITICALERRORS);
+ HANDLE handle = FindFirstFile(path, &FindFileData);
+ DWORD lastError = GetLastError();
+ SetErrorMode(0);
+
+ if (handle != INVALID_HANDLE_VALUE) {
+ do {
+ if (strcmp(FindFileData.cFileName, ".") != 0 &&
+ strcmp(FindFileData.cFileName, "..") != 0) {
+ unsigned int lastWriteTime = getTime70(FindFileData.ftLastWriteTime);
+ if ((FindFileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) {
+ pFileInfo->add(FindFileData.cFileName, 0, lastWriteTime, FT_ATTR_DIR);
+ } else {
+ if (!dirOnly)
+ pFileInfo->add(FindFileData.cFileName, FindFileData.nFileSizeLow, lastWriteTime, FT_ATTR_FILE);
+ }
+ }
+
+ } while (FindNextFile(handle, &FindFileData));
+ } else {
+ return false;
+ }
+ FindClose(handle);
+ return true;
+}
+
+bool
+FolderManager::getDrivesInfo(FileInfo *pFileInfo)
+{
+ TCHAR szDrivesList[256];
+ if (GetLogicalDriveStrings(255, szDrivesList) == 0)
+ return false;
+
+ int i = 0;
+ while (szDrivesList[i] != '\0') {
+ char *drive = strdup(&szDrivesList[i]);
+ char *backslash = strrchr(drive, '\\');
+ if (backslash != NULL)
+ *backslash = '\0';
+ pFileInfo->add(drive, 0, 0, FT_ATTR_DIR);
+ free(drive);
+ i += strcspn(&szDrivesList[i], "\0") + 1;
+ }
+ return true;
+}
+
+bool
+FolderManager::getInfo(char *pFullPath, FILEINFO *pFIStruct)
+{
+ WIN32_FIND_DATA FindFileData;
+ SetErrorMode(SEM_FAILCRITICALERRORS);
+ HANDLE hFile = FindFirstFile(pFullPath, &FindFileData);
+ DWORD lastError = GetLastError();
+ SetErrorMode(0);
+ if (hFile != INVALID_HANDLE_VALUE) {
+ FindClose(hFile);
+ strcpy(pFIStruct->name, pFullPath);
+ pFIStruct->info.data = getTime70(FindFileData.ftLastWriteTime);
+ if (FindFileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
+ pFIStruct->info.size = 0;
+ pFIStruct->info.flags = FT_ATTR_DIR;
+ return true;
+ } else {
+ pFIStruct->info.size = FindFileData.nFileSizeLow;
+ pFIStruct->info.flags = FT_ATTR_FILE;
+ return true;
+ }
+ }
+ return false;
+}
+
+unsigned int
+FolderManager::getTime70(FILETIME ftime)
+{
+ LARGE_INTEGER uli;
+ uli.LowPart = ftime.dwLowDateTime;
+ uli.HighPart = ftime.dwHighDateTime;
+ uli.QuadPart = (uli.QuadPart - 116444736000000000) / 10000000;
+ return uli.LowPart;
+}
+
+void
+FolderManager::getFiletime(unsigned int time70, FILETIME *pftime)
+{
+ LONGLONG ll = Int32x32To64(time70, 10000000) + 116444736000000000;
+ pftime->dwLowDateTime = (DWORD) ll;
+ pftime->dwHighDateTime = (DWORD) (ll >> 32);
+}
+
+bool
+FolderManager::getDirSize(char *pFullPath, DWORD64 *dirSize)
+{
+ char fullPath[FT_FILENAME_SIZE];
+ FileInfo fi;
+ fi.add(pFullPath, 0, 0, FT_ATTR_DIR);
+ DWORD64 dirFileSize64 = 0;
+ do {
+ sprintf(fullPath, "%s\\*", fi.getNameAt(0));
+ WIN32_FIND_DATA FindFileData;
+ SetErrorMode(SEM_FAILCRITICALERRORS);
+ HANDLE hFile = FindFirstFile(fullPath, &FindFileData);
+ SetErrorMode(0);
+
+ if (hFile != INVALID_HANDLE_VALUE) {
+ do {
+ if (strcmp(FindFileData.cFileName, ".") != 0 &&
+ strcmp(FindFileData.cFileName, "..") != 0) {
+ char buff[MAX_PATH];
+ if ((FindFileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) {
+ sprintf(buff, "%s\\%s", fi.getNameAt(0), FindFileData.cFileName);
+ fi.add(buff, 0, 0, FT_ATTR_DIR);
+ } else {
+ dirFileSize64 += FindFileData.nFileSizeLow;
+ }
+ }
+ } while (FindNextFile(hFile, &FindFileData));
+ FindClose(hFile);
+ }
+ fi.deleteAt(0);
+ } while (fi.getNumEntries() > 0);
+
+ *dirSize = dirFileSize64;
+ return true;
+}
\ No newline at end of file
diff --git a/win/rfb_win32/FolderManager.h b/win/rfb_win32/FolderManager.h
new file mode 100644
index 0000000..24923dd
--- /dev/null
+++ b/win/rfb_win32/FolderManager.h
@@ -0,0 +1,66 @@
+/* Copyright (C) 2005 TightVNC Team. All Rights Reserved.
+ *
+ * Developed by Dennis Syrovatsky.
+ *
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+ * USA.
+ *
+ * TightVNC distribution homepage on the Web: http://www.tightvnc.com/
+ *
+ */
+
+// -=- FolderManager.h
+
+#ifndef __RFB_WIN32_FOLDERMANAGER_H__
+#define __RFB_WIN32_FOLDERMANAGER_H__
+
+#include <windows.h>
+
+#include <rfb/FileInfo.h>
+#include <rfb/DirManager.h>
+
+namespace rfb {
+ namespace win32{
+ class FolderManager : public DirManager {
+ public:
+ FolderManager();
+ ~FolderManager();
+
+ bool createDir(char *pFullPath);
+
+ bool renameIt(char *pOldName, char *pNewName);
+ bool renameIt(char *pPath, char *pOldName, char *pNewName);
+
+ bool deleteIt(char *pPrefix, FileInfo *pFI);
+ bool deleteIt(char *pFullPath);
+ bool deleteIt(FileInfo *pFI);
+
+ bool getInfo(char *pFullPath, FILEINFO *pFIStruct);
+
+ bool getDirInfo(char *pPath, FileInfo *pFileInfo, unsigned int dirOnly);
+ bool getDrivesInfo(FileInfo *pFI);
+
+ unsigned int getTime70(FILETIME ftime);
+ void getFiletime(unsigned int time70, FILETIME *pftime);
+
+ bool getDirSize(char *pFullPath, DWORD64 *dirSize);
+
+ private:
+ bool getFolderInfoWithPrefix(char *pPrefix, FileInfo *pFileInfo);
+ };
+ }
+}
+
+#endif // __RFB_WIN32_FOLDERMANAGER_H__
\ No newline at end of file
diff --git a/win/rfb_win32/Handle.h b/win/rfb_win32/Handle.h
new file mode 100644
index 0000000..d3baa58
--- /dev/null
+++ b/win/rfb_win32/Handle.h
@@ -0,0 +1,43 @@
+/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
+ *
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+ * USA.
+ */
+
+// Wrapper for Win32 HANDLEs that can/must be CloseHandle()d.
+
+#ifndef __RFB_WIN32_HANDLE_H__
+#define __RFB_WIN32_HANDLE_H__
+
+#include <windows.h>
+
+namespace rfb {
+ namespace win32 {
+
+
+ class Handle {
+ public:
+ Handle(HANDLE h_=0) : h(h_) {}
+ ~Handle() {
+ if (h) CloseHandle(h);
+ }
+ operator HANDLE() {return h;}
+ HANDLE h;
+ };
+
+ };
+};
+
+#endif
diff --git a/win/rfb_win32/IconInfo.h b/win/rfb_win32/IconInfo.h
new file mode 100644
index 0000000..cb33a42
--- /dev/null
+++ b/win/rfb_win32/IconInfo.h
@@ -0,0 +1,44 @@
+/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
+ *
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+ * USA.
+ */
+
+#ifndef __RFB_WIN32_ICONINFO_H__
+#define __RFB_WIN32_ICONINFO_H__
+
+#include <windows.h>
+#include <rdr/Exception.h>
+
+namespace rfb {
+ namespace win32 {
+
+ struct IconInfo : public ICONINFO {
+ IconInfo(HICON icon) {
+ if (!GetIconInfo(icon, this))
+ throw rdr::SystemException("GetIconInfo() failed", GetLastError());
+ }
+ ~IconInfo() {
+ if (hbmColor)
+ DeleteObject(hbmColor);
+ if (hbmMask)
+ DeleteObject(hbmMask);
+ }
+ };
+
+ };
+};
+
+#endif
diff --git a/win/rfb_win32/IntervalTimer.h b/win/rfb_win32/IntervalTimer.h
new file mode 100644
index 0000000..ddfae49
--- /dev/null
+++ b/win/rfb_win32/IntervalTimer.h
@@ -0,0 +1,70 @@
+/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
+ *
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+ * USA.
+ */
+
+// -=- IntervalTimer.h
+//
+// Simple wrapper for standard Win32 timers
+
+#ifndef __RFB_WIN32_INTERVAL_TIMER_H__
+#define __RFB_WIN32_INTERVAL_TIMER_H__
+
+namespace rfb {
+
+ namespace win32 {
+
+ struct IntervalTimer {
+ IntervalTimer(HWND hwnd_, int id_)
+ : active(false), hwnd(hwnd_), id(id_) {
+ }
+ IntervalTimer() : active(false), hwnd(0), id(0) {
+ }
+ ~IntervalTimer() {
+ stop();
+ }
+
+ void start(int interval_) {
+ if (!active || interval_ != interval) {
+ interval = interval_;
+ if (!SetTimer(hwnd, id, interval, 0))
+ throw rdr::SystemException("SetTimer", GetLastError());
+ active = true;
+ }
+ }
+ void stop() {
+ if (active)
+ KillTimer(hwnd, id);
+ active = false;
+ }
+
+ void setHWND(HWND hwnd_) {hwnd=hwnd_;}
+ void setId(int id_) {id = id_;}
+ int getId() const {return id;}
+ bool isActive() const {return active;}
+
+ private:
+ HWND hwnd;
+ int id;
+ bool active;
+ int interval;
+ };
+
+ }; // win32
+
+}; // rfb
+
+#endif // __RFB_WIN32_INTERVAL_TIMER_H__
diff --git a/win/rfb_win32/LaunchProcess.cxx b/win/rfb_win32/LaunchProcess.cxx
new file mode 100644
index 0000000..56a712e
--- /dev/null
+++ b/win/rfb_win32/LaunchProcess.cxx
@@ -0,0 +1,103 @@
+/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
+ *
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+ * USA.
+ */
+
+// -=- LaunchProcess.cxx
+
+#include <rfb_win32/LaunchProcess.h>
+#include <rfb_win32/ModuleFileName.h>
+#include <rfb_win32/Win32Util.h>
+#include <rdr/Exception.h>
+#include <stdio.h>
+
+using namespace rfb;
+using namespace win32;
+
+
+LaunchProcess::LaunchProcess(const TCHAR* exeName_, const TCHAR* params_)
+: exeName(tstrDup(exeName_)), params(tstrDup(params_)) {
+ memset(&procInfo, 0, sizeof(procInfo));
+}
+
+LaunchProcess::~LaunchProcess() {
+ await();
+}
+
+
+void LaunchProcess::start(HANDLE userToken, bool createConsole) {
+ if (procInfo.hProcess && (WaitForSingleObject(procInfo.hProcess, 0) != WAIT_OBJECT_0))
+ return;
+ await();
+ returnCode = STILL_ACTIVE;
+
+ // - Create storage for the process startup information
+ STARTUPINFO sinfo;
+ memset(&sinfo, 0, sizeof(sinfo));
+ sinfo.cb = sizeof(sinfo);
+
+ // - Concoct a suitable command-line
+ TCharArray exePath;
+ if (!tstrContains(exeName.buf, _T('\\'))) {
+ ModuleFileName filename;
+ TCharArray path; splitPath(filename.buf, &path.buf, 0);
+ exePath.buf = new TCHAR[_tcslen(path.buf) + _tcslen(exeName.buf) + 2];
+ _stprintf(exePath.buf, _T("%s\\%s"), path.buf, exeName.buf);
+ } else {
+ exePath.buf = tstrDup(exeName.buf);
+ }
+
+ // - Start the process
+ // Note: We specify the exe's precise path in the ApplicationName parameter,
+ // AND include the name as the first part of the CommandLine parameter,
+ // because CreateProcess doesn't make ApplicationName argv[0] in C programs.
+ TCharArray cmdLine(_tcslen(exeName.buf) + 3 + _tcslen(params.buf) + 1);
+ _stprintf(cmdLine.buf, _T("\"%s\" %s"), exeName.buf, params.buf);
+ DWORD flags = createConsole ? CREATE_NEW_CONSOLE : CREATE_NO_WINDOW;
+ BOOL success;
+ if (userToken != INVALID_HANDLE_VALUE)
+ success = CreateProcessAsUser(userToken, exePath.buf, cmdLine.buf, 0, 0, FALSE, flags, 0, 0, &sinfo, &procInfo);
+ else
+ success = CreateProcess(exePath.buf, cmdLine.buf, 0, 0, FALSE, flags, 0, 0, &sinfo, &procInfo);
+ if (!success)
+ throw rdr::SystemException("unable to launch process", GetLastError());
+
+ // Wait for it to finish initialising
+ WaitForInputIdle(procInfo.hProcess, 15000);
+}
+
+void LaunchProcess::detach()
+{
+ if (!procInfo.hProcess)
+ return;
+ CloseHandle(procInfo.hProcess);
+ CloseHandle(procInfo.hThread);
+ memset(&procInfo, 0, sizeof(procInfo));
+}
+
+bool LaunchProcess::await(DWORD timeoutMs) {
+ if (!procInfo.hProcess)
+ return true;
+ DWORD result = WaitForSingleObject(procInfo.hProcess, timeoutMs);
+ if (result == WAIT_OBJECT_0) {
+ GetExitCodeProcess(procInfo.hProcess, &returnCode);
+ detach();
+ return true;
+ } else if (result == WAIT_FAILED) {
+ throw rdr::SystemException("await() failed", GetLastError());
+ }
+ return false;
+}
diff --git a/win/rfb_win32/LaunchProcess.h b/win/rfb_win32/LaunchProcess.h
new file mode 100644
index 0000000..38521dc
--- /dev/null
+++ b/win/rfb_win32/LaunchProcess.h
@@ -0,0 +1,71 @@
+/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
+ *
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+ * USA.
+ */
+
+// -=- LaunchProcess.h
+
+// Helper class to launch a names process from the same directory as
+// the current process executable resides in.
+
+#ifndef __RFB_WIN32_LAUNCHPROCESS_H__
+#define __RFB_WIN32_LAUNCHPROCESS_H__
+
+#include <windows.h>
+#include <rfb_win32/TCharArray.h>
+
+namespace rfb {
+
+ namespace win32 {
+
+ class LaunchProcess {
+ public:
+ LaunchProcess(const TCHAR* exeName_, const TCHAR* params);
+ ~LaunchProcess();
+
+ // start() starts the specified process with the supplied
+ // command-line.
+ // If userToken is INVALID_HANDLE_VALUE then starts the process
+ // as the current user, otherwise as the specified user.
+ // If createConsole is true then CREATE_CONSOLE_WINDOW is passed
+ // as an extra flag to the process creation call.
+ void start(HANDLE userToken, bool createConsole=false);
+
+ // Detatch from the child process. After detatching from a child
+ // process, no other methods should be called on the object
+ // that started it
+ void detach();
+
+ // Wait for the process to quit, up to the specified timeout, and
+ // close the handles to it once it has quit.
+ // If the process quits within the timeout then true is returned
+ // and returnCode is set. If it has not quit then false is returned.
+ // If an error occurs then an exception will be thrown.
+ bool await(DWORD timeoutMs=INFINITE);
+
+ PROCESS_INFORMATION procInfo;
+ DWORD returnCode;
+ protected:
+ TCharArray exeName;
+ TCharArray params;
+ };
+
+
+ };
+
+};
+
+#endif
diff --git a/win/rfb_win32/ListViewControl.cxx b/win/rfb_win32/ListViewControl.cxx
new file mode 100644
index 0000000..12e0400
--- /dev/null
+++ b/win/rfb_win32/ListViewControl.cxx
@@ -0,0 +1,103 @@
+// ListViewControl.cxx: implementation of the ListViewControl class.
+//
+//////////////////////////////////////////////////////////////////////
+#include <tchar.h>
+#include "ListViewControl.h"
+#include "commctrl.h"
+//////////////////////////////////////////////////////////////////////
+// Construction/Destruction
+//////////////////////////////////////////////////////////////////////
+using namespace rfb;
+using namespace rfb::win32;
+
+ListViewControl::ListViewControl()
+{
+}
+
+bool ListViewControl::IsSelectedLVItem(DWORD idListView,
+ HWND hDlg, int numberItem)
+{
+ return (ListView_GetItemState(GetDlgItem(hDlg, idListView),
+ numberItem, LVIS_SELECTED) == LVIS_SELECTED);
+}
+
+void ListViewControl::SelectLVItem(DWORD idListView, HWND hDlg, int numberItem)
+{
+ ListView_SetItemState(GetDlgItem(hDlg, idListView),
+ numberItem, LVIS_SELECTED, LVIS_SELECTED);
+}
+
+BOOL ListViewControl::InitLVColumns(DWORD idListView, HWND hDlg, int width, int columns,
+ TCHAR *title[], DWORD mask, DWORD LVStyle, DWORD format)
+{
+ ListView_SetExtendedListViewStyle(GetDlgItem(hDlg, idListView), LVStyle);
+ TCHAR szText[256];
+ LVCOLUMN lvc;
+ int iCol;
+
+ lvc.mask = mask;
+
+ for (iCol = 0; iCol < columns; iCol++) {
+ lvc.iSubItem = iCol;
+ lvc.pszText = szText;
+ lvc.cx = width;
+ lvc.fmt = format;
+
+ _tcscpy(szText, title[iCol]);
+ if (ListView_InsertColumn(GetDlgItem(hDlg, idListView), iCol, &lvc) == -1)
+ return FALSE;
+ }
+ return TRUE;
+}
+
+BOOL ListViewControl::InsertLVItem(DWORD idListView, HWND hDlg, int number, TCHAR * texts[],
+ int columns)
+{
+ int i;
+ LVITEM lvI;
+ lvI.mask = LVIF_TEXT| LVIF_STATE;
+ lvI.state = 0;
+ lvI.stateMask = 0;
+ lvI.iItem = number;
+ lvI.iSubItem = 0;
+ lvI.pszText = texts[0];
+
+ if(ListView_InsertItem(GetDlgItem(hDlg, idListView), &lvI) == -1)
+ return NULL;
+
+ for (i =1; i < columns; i++) {
+ SetLVItemText(
+ idListView, hDlg,
+ number, i, texts[i]);
+ }
+ return TRUE;
+}
+
+void ListViewControl::SetLVItemText(DWORD idListView, HWND hDlg, int numberItem,
+ int namberColumn, TCHAR * text)
+{
+ ListView_SetItemText(
+ GetDlgItem(hDlg, idListView),
+ numberItem, namberColumn, text);
+}
+
+void ListViewControl::GetLVItemText(DWORD idListView, HWND hDlg, int numberItem,
+ int namberColumn, TCHAR * text)
+{
+ ListView_GetItemText(GetDlgItem(hDlg, idListView), numberItem,
+ namberColumn, text, 256);
+}
+
+void ListViewControl::DeleteLVItem(DWORD idListView, HWND hDlg, int number)
+{
+ ListView_DeleteItem(GetDlgItem(hDlg, idListView), number);
+}
+
+void ListViewControl::DeleteAllLVItem(DWORD idListView, HWND hDlg)
+{
+ ListView_DeleteAllItems(GetDlgItem(hDlg, idListView));
+}
+
+ListViewControl::~ListViewControl()
+{
+}
diff --git a/win/rfb_win32/ListViewControl.h b/win/rfb_win32/ListViewControl.h
new file mode 100644
index 0000000..8a16373
--- /dev/null
+++ b/win/rfb_win32/ListViewControl.h
@@ -0,0 +1,35 @@
+// ListViewControl.h: interface for the ListViewControl class.
+//
+//////////////////////////////////////////////////////////////////////
+
+#ifndef AFX_LISTVIEWCONTROL_H__
+#define AFX_LISTVIEWCONTROL_H__
+
+#include <windows.h>
+#include "commctrl.h"
+
+namespace rfb {
+
+ namespace win32 {
+ class ListViewControl
+ {
+ public:
+ ListViewControl();
+ bool IsSelectedLVItem(DWORD idListView, HWND hDlg, int numberItem);
+ void SelectLVItem(DWORD idListView, HWND hDlg, int numberItem);
+ BOOL InitLVColumns(DWORD idListView, HWND hDlg, int width, int columns,
+ TCHAR * title[], DWORD mask, DWORD style, DWORD format);
+ BOOL InsertLVItem(DWORD idListView, HWND hDlg, int number, TCHAR * texts[],
+ int columns);
+ void SetLVItemText(DWORD idListView, HWND hDlg, int numberItem,
+ int namberColumn, TCHAR * text);
+ void GetLVItemText(DWORD idListView, HWND hDlg, int numberItem,
+ int namberColumn, TCHAR * text);
+ void DeleteLVItem(DWORD idListView, HWND hDlg, int number);
+ void DeleteAllLVItem(DWORD idListView, HWND hDlg);
+ virtual ~ListViewControl();
+ };
+ };
+};
+
+#endif;
\ No newline at end of file
diff --git a/win/rfb_win32/LocalMem.h b/win/rfb_win32/LocalMem.h
new file mode 100644
index 0000000..a99d324
--- /dev/null
+++ b/win/rfb_win32/LocalMem.h
@@ -0,0 +1,45 @@
+/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
+ *
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+ * USA.
+ */
+
+#ifndef __RFB_WIN32_LOCALMEM_H__
+#define __RFB_WIN32_LOCALMEM_H__
+
+#include <windows.h>
+#include <rdr/Exception.h>
+
+namespace rfb {
+ namespace win32 {
+
+ // Allocate and/or manage LocalAlloc memory.
+ struct LocalMem {
+ LocalMem(int size) : ptr(LocalAlloc(LMEM_FIXED, size)) {
+ if (!ptr) throw rdr::SystemException("LocalAlloc", GetLastError());
+ }
+ LocalMem(void* p) : ptr(p) {}
+ ~LocalMem() {LocalFree(ptr);}
+ operator void*() {return ptr;}
+ void* takePtr() {
+ void* t = ptr; ptr = 0; return t;
+ }
+ void* ptr;
+ };
+
+ };
+};
+
+#endif
diff --git a/win/rfb_win32/LogicalPalette.h b/win/rfb_win32/LogicalPalette.h
new file mode 100644
index 0000000..204f108
--- /dev/null
+++ b/win/rfb_win32/LogicalPalette.h
@@ -0,0 +1,90 @@
+/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
+ *
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+ * USA.
+ */
+
+#ifndef __RFB_WIN32_LOGPALETTE_H__
+#define __RFB_WIN32_LOGPALETTE_H__
+
+#include <windows.h>
+#include <rdr/Exception.h>
+
+namespace rfb {
+ namespace win32 {
+
+ class LogicalPalette {
+ public:
+ LogicalPalette() {
+ BYTE buf[sizeof(LOGPALETTE)+256*sizeof(PALETTEENTRY)];
+ LOGPALETTE* logpal = (LOGPALETTE*)buf;
+ logpal->palVersion = 0x300;
+ logpal->palNumEntries = 256;
+ for (int i=0; i<256;i++) {
+ logpal->palPalEntry[i].peRed = 0;
+ logpal->palPalEntry[i].peGreen = 0;
+ logpal->palPalEntry[i].peBlue = 0;
+ logpal->palPalEntry[i].peFlags = 0;
+ }
+ palette = CreatePalette(logpal);
+ if (!palette)
+ throw rdr::SystemException("failed to CreatePalette", GetLastError());
+ }
+ ~LogicalPalette() {
+ if (palette && !DeleteObject(palette))
+ throw rdr::SystemException("del palette failed", GetLastError());
+ }
+ void setEntries(int start, int count, const Colour* cols) {
+ if (numEntries < count) {
+ ResizePalette(palette, start+count);
+ numEntries = start+count;
+ }
+ PALETTEENTRY* logpal = new PALETTEENTRY[count];
+ for (int i=0; i<count; i++) {
+ logpal[i].peRed = cols[i].r >> 8;
+ logpal[i].peGreen = cols[i].g >> 8;
+ logpal[i].peBlue = cols[i].b >> 8;
+ logpal[i].peFlags = 0;
+ }
+ UnrealizeObject(palette);
+ SetPaletteEntries(palette, start, count, logpal);
+ delete [] logpal;
+ }
+ HPALETTE getHandle() {return palette;}
+ protected:
+ HPALETTE palette;
+ int numEntries;
+ };
+
+ class PaletteSelector {
+ public:
+ PaletteSelector(HDC dc, HPALETTE pal) : device(dc), redrawRequired(false) {
+ oldPal = SelectPalette(dc, pal, FALSE);
+ redrawRequired = RealizePalette(dc) > 0;
+ }
+ ~PaletteSelector() {
+ if (oldPal) SelectPalette(device, oldPal, TRUE);
+ }
+ bool isRedrawRequired() {return redrawRequired;}
+ protected:
+ HPALETTE oldPal;
+ HDC device;
+ bool redrawRequired;
+ };
+
+ };
+};
+
+#endif
diff --git a/win/rfb_win32/LowLevelKeyEvents.cxx b/win/rfb_win32/LowLevelKeyEvents.cxx
new file mode 100644
index 0000000..322d1f4
--- /dev/null
+++ b/win/rfb_win32/LowLevelKeyEvents.cxx
@@ -0,0 +1,96 @@
+/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
+ *
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+ * USA.
+ */
+
+#include <windows.h>
+#include <rfb_win32/LowLevelKeyEvents.h>
+#include <rfb/Threading.h>
+#include <rfb/LogWriter.h>
+#include <list>
+
+using namespace rfb;
+using namespace win32;
+
+static LogWriter vlog("LowLevelKeyEvents");
+
+
+HHOOK hook = 0;
+std::list<HWND> windows;
+Mutex windowLock;
+
+
+static bool filterKeyEvent(int vkCode) {
+ switch (vkCode) {
+ case VK_LWIN:
+ case VK_RWIN:
+ case VK_SNAPSHOT:
+ return true;
+ case VK_TAB:
+ if (GetAsyncKeyState(VK_MENU) & 0x8000)
+ return true;
+ case VK_ESCAPE:
+ if (GetAsyncKeyState(VK_MENU) & 0x8000)
+ return true;
+ if (GetAsyncKeyState(VK_CONTROL) & 0x8000)
+ return true;
+ }
+ return false;
+}
+
+LRESULT CALLBACK LowLevelKeyEventProc(int nCode,
+ WPARAM wParam,
+ LPARAM lParam) {
+ if (nCode >= 0) {
+ Lock l(windowLock);
+ HWND foreground = GetForegroundWindow();
+ std::list<HWND>::iterator i;
+ for (i=windows.begin(); i!=windows.end(); i++) {
+ if (*i == foreground) {
+ UINT msgType = wParam;
+ KBDLLHOOKSTRUCT* msgInfo = (KBDLLHOOKSTRUCT*)lParam;
+ if (filterKeyEvent(msgInfo->vkCode)) {
+ vlog.debug("filtered event %lx(%lu) %lu", msgInfo->vkCode, msgInfo->vkCode, wParam);
+ PostMessage(*i, wParam, msgInfo->vkCode, (msgInfo->scanCode & 0xff) << 16);
+ return 1;
+ }
+ }
+ }
+ }
+ return CallNextHookEx(hook, nCode, wParam, lParam);
+}
+
+
+bool rfb::win32::enableLowLevelKeyEvents(HWND hwnd) {
+// *** return false; // *** THIS CODE IS EXPERIMENTAL, SO DISABLED BY DEFAULT!
+ Lock l(windowLock);
+ if (windows.empty() && !hook)
+ hook = SetWindowsHookEx(WH_KEYBOARD_LL, &LowLevelKeyEventProc, GetModuleHandle(0), 0);
+ if (hook)
+ windows.push_back(hwnd);
+ vlog.debug("enable %p -> %s", hwnd, hook ? "success" : "failure");
+ return hook != 0;
+}
+
+void rfb::win32::disableLowLevelKeyEvents(HWND hwnd) {
+ vlog.debug("disable %p", hwnd);
+ Lock l(windowLock);
+ windows.remove(hwnd);
+ if (windows.empty() && hook) {
+ UnhookWindowsHookEx(hook);
+ hook = 0;
+ }
+}
diff --git a/win/rfb_win32/LowLevelKeyEvents.h b/win/rfb_win32/LowLevelKeyEvents.h
new file mode 100644
index 0000000..40d2ecf
--- /dev/null
+++ b/win/rfb_win32/LowLevelKeyEvents.h
@@ -0,0 +1,49 @@
+/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
+ *
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+ * USA.
+ */
+
+// -=- LowLevelKeyEvents.h
+//
+// This interface allows keyboard events destined for a particular window
+// to be intercepted early in the keyboard message queue and posted directly
+// to the window. This is used to avoid having the operating system process
+// keys such as VK_LWIN, VK_RWIN, etc.
+//
+
+#ifndef __RFB_WIN32_LOW_LEVEL_KEY_EVENTS_H__
+#define __RFB_WIN32_LOW_LEVEL_KEY_EVENTS_H__
+
+namespace rfb {
+
+ namespace win32 {
+
+ // enableLowLevelKeyEvents
+ // Specifies that keyboard events destined for the specified window should
+ // be posted directly to the window, rather than being passed via the normal
+ // Windows keyboard message queue.
+ bool enableLowLevelKeyEvents(HWND hwnd);
+
+ // disableLowLevelKeyEvents
+ // Causes the specified window to revert to the normal Windows keyboard
+ // event processing mechanism.
+ void disableLowLevelKeyEvents(HWND hwnd);
+
+ };
+
+};
+
+#endif // __RFB_WIN32_LOW_LEVEL_KEY_EVENTS_H__
diff --git a/win/rfb_win32/ModuleFileName.h b/win/rfb_win32/ModuleFileName.h
new file mode 100644
index 0000000..2264e89
--- /dev/null
+++ b/win/rfb_win32/ModuleFileName.h
@@ -0,0 +1,40 @@
+/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
+ *
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+ * USA.
+ */
+
+#ifndef __RFB_WIN32_MODULE_FILENAME_H__
+#define __RFB_WIN32_MODULE_FILENAME_H__
+
+#include <windows.h>
+#include <rfb_win32/TCharArray.h>
+
+namespace rfb {
+ namespace win32 {
+
+ struct ModuleFileName : public TCharArray {
+ ModuleFileName(HMODULE module=0) : TCharArray(MAX_PATH) {
+ if (!module)
+ module = GetModuleHandle(0);
+ if (!GetModuleFileName(module, buf, MAX_PATH))
+ buf[0] = 0;
+ }
+ };
+
+ };
+};
+
+#endif
diff --git a/win/rfb_win32/MonitorInfo.cxx b/win/rfb_win32/MonitorInfo.cxx
new file mode 100644
index 0000000..03772e9
--- /dev/null
+++ b/win/rfb_win32/MonitorInfo.cxx
@@ -0,0 +1,205 @@
+/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
+ *
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+ * USA.
+ */
+
+#include <rfb_win32/DynamicFn.h>
+#include <rfb_win32/MonitorInfo.h>
+#include <rfb_win32/Win32Util.h>
+#include <rdr/Exception.h>
+#include <rfb/LogWriter.h>
+
+using namespace rfb;
+using namespace win32;
+
+static LogWriter vlog("MonitorInfo");
+
+
+// If we are building in multi-monitor support (i.e. the headers support it)
+// then do dynamic imports of the required system calls, and provide any
+// other code that wouldn't otherwise compile.
+#ifdef RFB_HAVE_MONITORINFO
+#include <tchar.h>
+typedef HMONITOR (WINAPI *_MonitorFromWindow_proto)(HWND,DWORD);
+static rfb::win32::DynamicFn<_MonitorFromWindow_proto> _MonitorFromWindow(_T("user32.dll"), "MonitorFromWindow");
+typedef HMONITOR (WINAPI *_MonitorFromRect_proto)(LPCRECT,DWORD);
+static rfb::win32::DynamicFn<_MonitorFromRect_proto> _MonitorFromRect(_T("user32.dll"), "MonitorFromRect");
+typedef BOOL (WINAPI *_GetMonitorInfo_proto)(HMONITOR,LPMONITORINFO);
+static rfb::win32::DynamicFn<_GetMonitorInfo_proto> _GetMonitorInfo(_T("user32.dll"), "GetMonitorInfoA");
+typedef BOOL (WINAPI *_EnumDisplayMonitors_proto)(HDC, LPCRECT, MONITORENUMPROC, LPARAM);
+static rfb::win32::DynamicFn<_EnumDisplayMonitors_proto> _EnumDisplayMonitors(_T("user32.dll"), "EnumDisplayMonitors");
+static void fillMonitorInfo(HMONITOR monitor, MonitorInfo* mi) {
+ vlog.debug("monitor=%lx", monitor);
+ if (!_GetMonitorInfo.isValid())
+ throw rdr::Exception("no GetMonitorInfo");
+ memset(mi, 0, sizeof(MONITORINFOEXA));
+ mi->cbSize = sizeof(MONITORINFOEXA);
+ if (!(*_GetMonitorInfo)(monitor, mi))
+ throw rdr::SystemException("failed to GetMonitorInfo", GetLastError());
+ vlog.debug("monitor is %d,%d-%d,%d", mi->rcMonitor.left, mi->rcMonitor.top, mi->rcMonitor.right, mi->rcMonitor.bottom);
+ vlog.debug("work area is %d,%d-%d,%d", mi->rcWork.left, mi->rcWork.top, mi->rcWork.right, mi->rcWork.bottom);
+ vlog.debug("device is \"%s\"", mi->szDevice);
+}
+#else
+#pragma message(" NOTE: Not building Multi-Monitor support.")
+#endif
+
+
+MonitorInfo::MonitorInfo(HWND window) {
+ cbSize = sizeof(MonitorInfo);
+ szDevice[0] = 0;
+
+#ifdef RFB_HAVE_MONITORINFO
+ try {
+ if (_MonitorFromWindow.isValid()) {
+ HMONITOR monitor = (*_MonitorFromWindow)(window, MONITOR_DEFAULTTONEAREST);
+ if (!monitor)
+ throw rdr::SystemException("failed to get monitor", GetLastError());
+ fillMonitorInfo(monitor, this);
+ return;
+ }
+ } catch (rdr::Exception& e) {
+ vlog.error(e.str());
+ }
+#endif
+
+ // Legacy fallbacks - just return the desktop settings
+ vlog.debug("using legacy fall-backs");
+ HWND desktop = GetDesktopWindow();
+ GetWindowRect(desktop, &rcMonitor);
+ SystemParametersInfo(SPI_GETWORKAREA, 0, &rcWork, 0);
+ dwFlags = 0;
+}
+
+MonitorInfo::MonitorInfo(const RECT& r) {
+ cbSize = sizeof(MonitorInfo);
+ szDevice[0] = 0;
+
+#ifdef RFB_HAVE_MONITORINFO
+ try {
+ if (_MonitorFromRect.isValid()) {
+ HMONITOR monitor = (*_MonitorFromRect)(&r, MONITOR_DEFAULTTONEAREST);
+ if (!monitor)
+ throw rdr::SystemException("failed to get monitor", GetLastError());
+ fillMonitorInfo(monitor, this);
+ return;
+ }
+ } catch (rdr::Exception& e) {
+ vlog.error(e.str());
+ }
+#endif
+
+ // Legacy fallbacks - just return the desktop settings
+ vlog.debug("using legacy fall-backs");
+ HWND desktop = GetDesktopWindow();
+ GetWindowRect(desktop, &rcMonitor);
+ SystemParametersInfo(SPI_GETWORKAREA, 0, &rcWork, 0);
+ dwFlags = 0;
+}
+
+
+#ifdef RFB_HAVE_MONITORINFO
+
+struct monitorByNameData {
+ MonitorInfo* info;
+ const char* monitorName;
+};
+
+static BOOL CALLBACK monitorByNameEnumProc(HMONITOR monitor,
+ HDC dc,
+ LPRECT pos,
+ LPARAM d) {
+ monitorByNameData* data = (monitorByNameData*)d;
+ memset(data->info, 0, sizeof(MONITORINFOEXA));
+ data->info->cbSize = sizeof(MONITORINFOEXA);
+ if ((*_GetMonitorInfo)(monitor, data->info)) {
+ if (stricmp(data->monitorName, data->info->szDevice) == 0)
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+#endif
+
+MonitorInfo::MonitorInfo(const char* devName) {
+#ifdef RFB_HAVE_MONITORINFO
+ if (!_EnumDisplayMonitors.isValid()) {
+ vlog.debug("EnumDisplayMonitors not found");
+ } else {
+ monitorByNameData data;
+ data.info = this;
+ data.monitorName = devName;
+
+ (*_EnumDisplayMonitors)(0, 0, &monitorByNameEnumProc, (LPARAM)&data);
+ if (stricmp(data.monitorName, szDevice) == 0)
+ return;
+ }
+#endif
+ // If multi-monitor is not built, or not supported by the OS,
+ // or if the named monitor is not found, revert to the primary monitor.
+ vlog.debug("reverting to primary monitor");
+ cbSize = sizeof(MonitorInfo);
+ szDevice[0] = 0;
+
+ HWND desktop = GetDesktopWindow();
+ GetWindowRect(desktop, &rcMonitor);
+ SystemParametersInfo(SPI_GETWORKAREA, 0, &rcWork, 0);
+ dwFlags = 0;
+}
+
+void MonitorInfo::moveTo(HWND handle) {
+ vlog.debug("moveTo monitor=%s", szDevice);
+
+#ifdef RFB_HAVE_MONITORINFO
+ MonitorInfo mi(handle);
+ if (strcmp(szDevice, mi.szDevice) != 0) {
+ centerWindow(handle, rcWork);
+ clipTo(handle);
+ }
+#endif
+}
+
+void MonitorInfo::clipTo(RECT* r) {
+ vlog.debug("clipTo monitor=%s", szDevice);
+
+ if (r->top < rcWork.top) {
+ r->bottom += rcWork.top - r->top; r->top = rcWork.top;
+ }
+ if (r->left < rcWork.left) {
+ r->right += rcWork.left - r->left; r->left = rcWork.left;
+ }
+ if (r->bottom > rcWork.bottom) {
+ r->top += rcWork.bottom - r->bottom; r->bottom = rcWork.bottom;
+ }
+ if (r->right > rcWork.right) {
+ r->left += rcWork.right - r->right; r->right = rcWork.right;
+ }
+ r->left = max(r->left, rcWork.left);
+ r->right = min(r->right, rcWork.right);
+ r->top = max(r->top, rcWork.top);
+ r->bottom = min(r->bottom, rcWork.bottom);
+}
+
+void MonitorInfo::clipTo(HWND handle) {
+ RECT r;
+ GetWindowRect(handle, &r);
+ clipTo(&r);
+ SetWindowPos(handle, 0, r.left, r.top, r.right-r.left, r.bottom-r.top,
+ SWP_NOZORDER | SWP_NOACTIVATE | SWP_NOOWNERZORDER);
+}
+
+
diff --git a/win/rfb_win32/MonitorInfo.h b/win/rfb_win32/MonitorInfo.h
new file mode 100644
index 0000000..acf2775
--- /dev/null
+++ b/win/rfb_win32/MonitorInfo.h
@@ -0,0 +1,72 @@
+/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
+ *
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+ * USA.
+ */
+
+// Helper class used to obtain information about a particular monitor.
+// This class wraps the Windows MONITORINFOEX ASCII structure, providing
+// methods that can safely be called on both multi-monitor aware systems
+// and older "legacy" systems.
+
+
+#ifndef __RFB_WIN32_MONITORINFO_H__
+#define __RFB_WIN32_MONITORINFO_H__
+
+#include <windows.h>
+#ifdef MONITOR_DEFAULTTONULL
+#define RFB_HAVE_MONITORINFO
+#endif
+
+namespace rfb {
+ namespace win32 {
+
+ // Structure containing info on the monitor nearest the window.
+ // Copes with multi-monitor OSes and older ones.
+#ifdef RFB_HAVE_MONITORINFO
+ struct MonitorInfo : MONITORINFOEXA {
+#else
+ struct MonitorInfo {
+ DWORD cbSize;
+ RECT rcMonitor;
+ RECT rcWork;
+ DWORD dwFlags;
+ char szDevice[1]; // Always null...
+#endif
+
+ // Constructor: Obtains monitor info for the monitor that has the
+ // greatest overlap with the supplied window or rectangle.
+ MonitorInfo(HWND hwnd);
+ MonitorInfo(const RECT& r);
+
+ // Constructor: Obtains monitor info for the name monitor. Monitor
+ // names should be those obtained from the MonitorInfo
+ // szDevice field, and usually look like "\\.\DISPLAY<n>"
+ MonitorInfo(const char* devName);
+
+ // Move the specified window to reside on the monitor.
+ void moveTo(HWND handle);
+
+ // Clip the specified rectangle or window to the monitor's working area.
+ // The rectangle/window is moved so that as much as possible resides
+ // on the working area of the monitor, and is then intersected with it.
+ void clipTo(HWND handle);
+ void clipTo(RECT* r);
+ };
+
+ };
+};
+
+#endif
diff --git a/win/rfb_win32/MsgBox.h b/win/rfb_win32/MsgBox.h
new file mode 100644
index 0000000..5957139
--- /dev/null
+++ b/win/rfb_win32/MsgBox.h
@@ -0,0 +1,63 @@
+/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
+ *
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+ * USA.
+ */
+
+#ifndef __RFB_WIN32_MSGBOX_H__
+#define __RFB_WIN32_MSGBOX_H__
+
+#include <windows.h>
+#include <rfb_win32/TCharArray.h>
+
+namespace rfb {
+ namespace win32 {
+
+ // Define rfb::win32::AppName somewhere in the application.
+ // The MsgBox function will use the specified application name
+ // as the prefix for the message box title.
+ // Message box titles are based on the (standard Win32) flags
+ // passed to the MsgBox helper function.
+
+ extern TStr AppName;
+
+ // Wrapper around Win32 MessageBox()
+ static int MsgBox(HWND parent, const TCHAR* msg, UINT flags) {
+ const TCHAR* msgType = 0;
+ UINT tflags = flags & 0x70;
+ if (tflags == MB_ICONHAND)
+ msgType = _T("Error");
+ else if (tflags == MB_ICONQUESTION)
+ msgType = _T("Question");
+ else if (tflags == MB_ICONEXCLAMATION)
+ msgType = _T("Warning");
+ else if (tflags == MB_ICONASTERISK)
+ msgType = _T("Information");
+ flags |= MB_TOPMOST | MB_SETFOREGROUND;
+ int len = _tcslen(AppName.buf) + 1;
+ if (msgType) len += _tcslen(msgType) + 3;
+ TCharArray title = new TCHAR[len];
+ _tcscpy(title.buf, AppName.buf);
+ if (msgType) {
+ _tcscat(title.buf, _T(" : "));
+ _tcscat(title.buf, msgType);
+ }
+ return MessageBox(parent, msg, title.buf, flags);
+ }
+
+ };
+};
+
+#endif
diff --git a/win/rfb_win32/MsgWindow.cxx b/win/rfb_win32/MsgWindow.cxx
new file mode 100644
index 0000000..95bd523
--- /dev/null
+++ b/win/rfb_win32/MsgWindow.cxx
@@ -0,0 +1,116 @@
+/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
+ *
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+ * USA.
+ */
+
+// -=- MsgWindow.cxx
+
+#include <rfb_win32/MsgWindow.h>
+#include <rfb_win32/WMShatter.h>
+#include <rfb/LogWriter.h>
+#include <rdr/Exception.h>
+#include <malloc.h>
+#include <tchar.h>
+
+using namespace rfb;
+using namespace rfb::win32;
+
+static LogWriter vlog("MsgWindow");
+
+//
+// -=- MsgWindowClass
+//
+
+class MsgWindowClass {
+public:
+ MsgWindowClass();
+ ~MsgWindowClass();
+ ATOM classAtom;
+ HINSTANCE instance;
+};
+
+LRESULT CALLBACK MsgWindowProc(HWND wnd, UINT msg, WPARAM wParam, LPARAM lParam) {
+ LRESULT result;
+
+ if (msg == WM_CREATE)
+ SetWindowLong(wnd, GWL_USERDATA, (long)((CREATESTRUCT*)lParam)->lpCreateParams);
+ else if (msg == WM_DESTROY)
+ SetWindowLong(wnd, GWL_USERDATA, 0);
+ MsgWindow* _this = (MsgWindow*) GetWindowLong(wnd, GWL_USERDATA);
+ if (!_this) {
+ vlog.info("null _this in %x, message %x", wnd, msg);
+ return SafeDefWindowProc(wnd, msg, wParam, lParam);
+ }
+
+ try {
+ result = _this->processMessage(msg, wParam, lParam);
+ } catch (rdr::Exception& e) {
+ vlog.error("untrapped: %s", e.str());
+ }
+
+ return result;
+};
+
+MsgWindowClass::MsgWindowClass() : classAtom(0) {
+ WNDCLASS wndClass;
+ wndClass.style = 0;
+ wndClass.lpfnWndProc = MsgWindowProc;
+ wndClass.cbClsExtra = 0;
+ wndClass.cbWndExtra = 0;
+ wndClass.hInstance = instance = GetModuleHandle(0);
+ wndClass.hIcon = 0;
+ wndClass.hCursor = 0;
+ wndClass.hbrBackground = 0;
+ wndClass.lpszMenuName = 0;
+ wndClass.lpszClassName = _T("rfb::win32::MsgWindowClass");
+ classAtom = RegisterClass(&wndClass);
+ if (!classAtom) {
+ throw rdr::SystemException("unable to register MsgWindow window class", GetLastError());
+ }
+}
+
+MsgWindowClass::~MsgWindowClass() {
+ if (classAtom) {
+ UnregisterClass((const TCHAR*)classAtom, instance);
+ }
+}
+
+MsgWindowClass baseClass;
+
+//
+// -=- MsgWindow
+//
+
+MsgWindow::MsgWindow(const TCHAR* name_) : name(tstrDup(name_)), handle(0) {
+ vlog.debug("creating window \"%s\"", (const char*)CStr(name.buf));
+ handle = CreateWindow((const TCHAR*)baseClass.classAtom, name.buf, WS_OVERLAPPED,
+ 0, 0, 10, 10, 0, 0, baseClass.instance, this);
+ if (!handle) {
+ throw rdr::SystemException("unable to create WMNotifier window instance", GetLastError());
+ }
+ vlog.debug("created window \"%s\" (%x)", (const char*)CStr(name.buf), handle);
+}
+
+MsgWindow::~MsgWindow() {
+ if (handle)
+ DestroyWindow(handle);
+ vlog.debug("destroyed window \"%s\" (%x)", (const char*)CStr(name.buf), handle);
+}
+
+LRESULT
+MsgWindow::processMessage(UINT msg, WPARAM wParam, LPARAM lParam) {
+ return SafeDefWindowProc(getHandle(), msg, wParam, lParam);
+}
diff --git a/win/rfb_win32/MsgWindow.h b/win/rfb_win32/MsgWindow.h
new file mode 100644
index 0000000..92b6cf2
--- /dev/null
+++ b/win/rfb_win32/MsgWindow.h
@@ -0,0 +1,53 @@
+/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
+ *
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+ * USA.
+ */
+
+// -=- MsgWindow.h
+
+// Base-class for any hidden message-handling windows used in the rfb::win32
+// implementation.
+
+#ifndef __RFB_WIN32_MSG_WINDOW_H__
+#define __RFB_WIN32_MSG_WINDOW_H__
+
+#include <windows.h>
+#include <rfb_win32/TCharArray.h>
+
+namespace rfb {
+
+ namespace win32 {
+
+ class MsgWindow {
+ public:
+ MsgWindow(const TCHAR* _name);
+ virtual ~MsgWindow();
+
+ const TCHAR* getName() {return name.buf;}
+ HWND getHandle() const {return handle;}
+
+ virtual LRESULT processMessage(UINT msg, WPARAM wParam, LPARAM lParam);
+
+ protected:
+ TCharArray name;
+ HWND handle;
+ };
+
+ };
+
+};
+
+#endif // __RFB_WIN32_MSG_WINDOW_H__
diff --git a/win/rfb_win32/OSVersion.cxx b/win/rfb_win32/OSVersion.cxx
new file mode 100644
index 0000000..3d74c95
--- /dev/null
+++ b/win/rfb_win32/OSVersion.cxx
@@ -0,0 +1,47 @@
+/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
+ *
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+ * USA.
+ */
+
+// -=- OSVersion.cxx
+
+#include <rfb_win32/OSVersion.h>
+#include <rdr/Exception.h>
+#include <tchar.h>
+
+using namespace rfb;
+using namespace win32;
+
+
+OSVersionInfo::OSVersionInfo() {
+ // Get OS Version Info
+ ZeroMemory(static_cast<OSVERSIONINFO*>(this), sizeof(this));
+ dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
+ if (!GetVersionEx(this))
+ throw rdr::SystemException("unable to get system version info", GetLastError());
+
+ // Set the special extra flags
+ isPlatformNT = dwPlatformId == VER_PLATFORM_WIN32_NT;
+ isPlatformWindows = dwPlatformId == VER_PLATFORM_WIN32_WINDOWS;
+
+ cannotSwitchDesktop = isPlatformNT && (dwMajorVersion==4) &&
+ ((_tcscmp(szCSDVersion, _T("")) == 0) ||
+ (_tcscmp(szCSDVersion, _T("Service Pack 1")) == 0) ||
+ (_tcscmp(szCSDVersion, _T("Service Pack 2")) == 0));
+
+}
+
+OSVersionInfo rfb::win32::osVersion;
diff --git a/win/rfb_win32/OSVersion.h b/win/rfb_win32/OSVersion.h
new file mode 100644
index 0000000..18ec003
--- /dev/null
+++ b/win/rfb_win32/OSVersion.h
@@ -0,0 +1,53 @@
+/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
+ *
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+ * USA.
+ */
+
+// -=- OSVersion.h
+
+// Operating system version info.
+// GetVersionInfo is called once at process initialisation, and any
+// extra flags (such as isWinNT) are calculated and saved at that
+// point. It is assumed that the OS Version seldom changes during a
+// program's execution...
+
+#ifndef __RFB_WIN32_OS_VERSION_H__
+#define __RFB_WIN32_OS_VERSION_H__
+
+#include <windows.h>
+
+namespace rfb {
+
+ namespace win32 {
+
+ extern struct OSVersionInfo : OSVERSIONINFO {
+ OSVersionInfo();
+
+ // Is the OS one of the NT family (NT 3.51, NT4.0, 2K, XP, etc.)?
+ bool isPlatformNT;
+ // Is one of the Windows family?
+ bool isPlatformWindows;
+
+ // Is this OS one of those that blue-screens when grabbing another desktop (NT4 pre SP3)?
+ bool cannotSwitchDesktop;
+
+ } osVersion;
+
+ };
+
+};
+
+#endif // __RFB_WIN32_OS_VERSION_H__
diff --git a/win/rfb_win32/ProgressControl.cxx b/win/rfb_win32/ProgressControl.cxx
new file mode 100644
index 0000000..85bd15f
--- /dev/null
+++ b/win/rfb_win32/ProgressControl.cxx
@@ -0,0 +1,97 @@
+/* Copyright (C) 2005 TightVNC Team. All Rights Reserved.
+ *
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+ * USA.
+ *
+ * TightVNC distribution homepage on the Web: http://www.tightvnc.com/
+ *
+ */
+
+// -=- ProgressControl.cxx
+
+#include <rfb_win32/ProgressControl.h>
+
+using namespace rfb;
+using namespace rfb::win32;
+
+#define MAX_RANGE 0xFFFF
+
+ProgressControl::ProgressControl(HWND hwndProgress)
+{
+ m_hwndProgress = hwndProgress;
+
+ m_dw64MaxValue = 0;
+ m_dw64CurrentValue = 0;
+}
+
+ProgressControl::~ProgressControl()
+{
+}
+
+bool
+ProgressControl::init(DWORD64 maxValue, DWORD64 position)
+{
+ if (m_dw64CurrentValue > m_dw64MaxValue) return false;
+
+ m_dw64CurrentValue = position;
+ m_dw64MaxValue = maxValue;
+
+ if (!SendMessage(m_hwndProgress, PBM_SETRANGE, (WPARAM) 0, MAKELPARAM(0, MAX_RANGE)))
+ return false;
+
+ return true;
+}
+
+bool
+ProgressControl::clear()
+{
+ m_dw64CurrentValue = 0;
+ return show();
+}
+
+bool
+ProgressControl::increase(DWORD64 value)
+{
+ if ((m_dw64MaxValue - m_dw64CurrentValue) > value) {
+ m_dw64CurrentValue += value;
+ } else {
+ m_dw64CurrentValue = m_dw64MaxValue;
+ }
+ return show();
+}
+
+bool
+ProgressControl::show()
+{
+ DWORD curPos;
+ if (m_dw64MaxValue != 0) {
+ curPos = (DWORD) ((m_dw64CurrentValue * MAX_RANGE) / m_dw64MaxValue);
+ } else {
+ curPos = 0;
+ }
+
+ if (!SendMessage(m_hwndProgress, PBM_SETPOS, (WPARAM) curPos, (LPARAM) 0))
+ return false;
+
+ return true;
+}
+
+int
+ProgressControl::getCurrentPercent()
+{
+ if (m_dw64MaxValue == 0) return 0;
+
+ return ((int) ((m_dw64CurrentValue * 100) / m_dw64MaxValue));
+}
\ No newline at end of file
diff --git a/win/rfb_win32/ProgressControl.h b/win/rfb_win32/ProgressControl.h
new file mode 100644
index 0000000..ceeb153
--- /dev/null
+++ b/win/rfb_win32/ProgressControl.h
@@ -0,0 +1,56 @@
+/* Copyright (C) 2005 TightVNC Team. All Rights Reserved.
+ *
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+ * USA.
+ *
+ * TightVNC distribution homepage on the Web: http://www.tightvnc.com/
+ *
+ */
+
+// -=- ProgressControl.h
+
+#ifndef __RFB_WIN32_PROGRESSCONTROL_H__
+#define __RFB_WIN32_PROGRESSCONTROL_H__
+
+#include <windows.h>
+#include <commctrl.h>
+
+namespace rfb {
+ namespace win32 {
+ class ProgressControl
+ {
+ public:
+ ProgressControl(HWND hwndProgress);
+ ~ProgressControl();
+
+ bool init(DWORD64 maxValue, DWORD64 position);
+
+ bool increase(DWORD64 value);
+ bool clear();
+
+ int getCurrentPercent();
+
+ private:
+ HWND m_hwndProgress;
+
+ DWORD64 m_dw64MaxValue;
+ DWORD64 m_dw64CurrentValue;
+
+ bool show();
+ };
+ }
+}
+
+#endif // __RFB_WIN32_PROGRESSCONTROL_H__
diff --git a/win/rfb_win32/RegConfig.cxx b/win/rfb_win32/RegConfig.cxx
new file mode 100644
index 0000000..dd1c3b0
--- /dev/null
+++ b/win/rfb_win32/RegConfig.cxx
@@ -0,0 +1,114 @@
+/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
+ *
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+ * USA.
+ */
+
+// -=- RegConfig.cxx
+
+#include <malloc.h>
+
+#include <rfb_win32/RegConfig.h>
+#include <rfb/LogWriter.h>
+#include <rfb/util.h>
+//#include <rdr/HexOutStream.h>
+
+using namespace rfb;
+using namespace rfb::win32;
+
+
+static LogWriter vlog("RegConfig");
+
+
+RegConfig::RegConfig(EventManager* em) : eventMgr(em), event(CreateEvent(0, TRUE, FALSE, 0)), callback(0) {
+ if (em->addEvent(event, this))
+ eventMgr = em;
+}
+
+RegConfig::~RegConfig() {
+ if (eventMgr)
+ eventMgr->removeEvent(event);
+}
+
+bool RegConfig::setKey(const HKEY rootkey, const TCHAR* keyname) {
+ try {
+ key.createKey(rootkey, keyname);
+ processEvent(event);
+ return true;
+ } catch (rdr::Exception& e) {
+ vlog.debug(e.str());
+ return false;
+ }
+}
+
+void RegConfig::loadRegistryConfig(RegKey& key) {
+ DWORD i = 0;
+ try {
+ while (1) {
+ TCharArray name = tstrDup(key.getValueName(i++));
+ if (!name.buf) break;
+ TCharArray value = key.getRepresentation(name.buf);
+ if (!value.buf || !Configuration::setParam(CStr(name.buf), CStr(value.buf)))
+ vlog.info("unable to process %s", CStr(name.buf));
+ }
+ } catch (rdr::SystemException& e) {
+ if (e.err != 6)
+ vlog.error(e.str());
+ }
+}
+
+void RegConfig::processEvent(HANDLE event_) {
+ vlog.info("registry changed");
+
+ // Reinstate the registry change notifications
+ ResetEvent(event);
+ key.awaitChange(true, REG_NOTIFY_CHANGE_NAME | REG_NOTIFY_CHANGE_LAST_SET, event);
+
+ // Load settings
+ loadRegistryConfig(key);
+
+ // Notify the callback, if supplied
+ if (callback)
+ callback->regConfigChanged();
+}
+
+
+RegConfigThread::RegConfigThread() : Thread("RegConfigThread"), config(&eventMgr) {
+}
+
+RegConfigThread::~RegConfigThread() {
+ join();
+}
+
+bool RegConfigThread::start(const HKEY rootKey, const TCHAR* keyname) {
+ if (config.setKey(rootKey, keyname)) {
+ Thread::start();
+ return true;
+ }
+ return false;
+}
+
+void RegConfigThread::run() {
+ DWORD result = 0;
+ MSG msg;
+ while ((result = eventMgr.getMessage(&msg, 0, 0, 0)) > 0) {}
+ if (result < 0)
+ throw rdr::SystemException("RegConfigThread failed", GetLastError());
+}
+
+Thread* RegConfigThread::join() {
+ PostThreadMessage(getThreadId(), WM_QUIT, 0, 0);
+ return Thread::join();
+}
diff --git a/win/rfb_win32/RegConfig.h b/win/rfb_win32/RegConfig.h
new file mode 100644
index 0000000..e9c01b1
--- /dev/null
+++ b/win/rfb_win32/RegConfig.h
@@ -0,0 +1,84 @@
+/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
+ *
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+ * USA.
+ */
+
+// -=- RegConfig.h
+
+// Class which monitors the registry and reads in the registry settings
+// whenever they change, or are added or removed.
+
+#ifndef __RFB_WIN32_REG_CONFIG_H__
+#define __RFB_WIN32_REG_CONFIG_H__
+
+#include <rfb/Threading.h>
+#include <rfb/Configuration.h>
+#include <rfb_win32/Registry.h>
+#include <rfb_win32/EventManager.h>
+#include <rfb_win32/Handle.h>
+
+namespace rfb {
+
+ namespace win32 {
+
+ class RegConfig : EventHandler {
+ public:
+ RegConfig(EventManager* em);
+ ~RegConfig();
+
+ // Specify the registry key to read Configuration items from
+ bool setKey(const HKEY rootkey, const TCHAR* keyname);
+
+ // Support for a callback, run in the RegConfig host thread whenever
+ // the registry configuration changes
+ class Callback {
+ public:
+ virtual ~Callback() {}
+ virtual void regConfigChanged() = 0;
+ };
+ void setCallback(Callback* cb) { callback = cb; }
+
+ // Read entries from the specified key into the Configuration
+ static void loadRegistryConfig(RegKey& key);
+ protected:
+ // EventHandler interface and trigger event
+ virtual void processEvent(HANDLE event);
+
+ EventManager* eventMgr;
+ Handle event;
+ Callback* callback;
+ RegKey key;
+ };
+
+ class RegConfigThread : Thread {
+ public:
+ RegConfigThread();
+ ~RegConfigThread();
+
+ // Start the thread, reading from the specified key
+ bool start(const HKEY rootkey, const TCHAR* keyname);
+ protected:
+ void run();
+ Thread* join();
+ EventManager eventMgr;
+ RegConfig config;
+ };
+
+ };
+
+};
+
+#endif // __RFB_WIN32_REG_CONFIG_H__
diff --git a/win/rfb_win32/Registry.cxx b/win/rfb_win32/Registry.cxx
new file mode 100644
index 0000000..4ece4ba
--- /dev/null
+++ b/win/rfb_win32/Registry.cxx
@@ -0,0 +1,316 @@
+/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
+ *
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+ * USA.
+ */
+
+// -=- Registry.cxx
+
+#include <rfb_win32/Registry.h>
+#include <rfb_win32/Security.h>
+#include <rfb_win32/DynamicFn.h>
+#include <rdr/MemOutStream.h>
+#include <rdr/HexOutstream.h>
+#include <rdr/HexInStream.h>
+#include <stdlib.h>
+#include <rfb/LogWriter.h>
+
+// These flags are required to control access control inheritance,
+// but are not defined by VC6's headers. These definitions comes
+// from the Microsoft Platform SDK.
+#ifndef PROTECTED_DACL_SECURITY_INFORMATION
+#define PROTECTED_DACL_SECURITY_INFORMATION (0x80000000L)
+#endif
+#ifndef UNPROTECTED_DACL_SECURITY_INFORMATION
+#define UNPROTECTED_DACL_SECURITY_INFORMATION (0x20000000L)
+#endif
+
+
+using namespace rfb;
+using namespace rfb::win32;
+
+
+static LogWriter vlog("Registry");
+
+
+RegKey::RegKey() : key(0), freeKey(false), valueNameBufLen(0) {}
+
+RegKey::RegKey(const HKEY k) : key(0), freeKey(false), valueNameBufLen(0) {
+ LONG result = RegOpenKeyEx(k, 0, 0, KEY_ALL_ACCESS, &key);
+ if (result != ERROR_SUCCESS)
+ throw rdr::SystemException("RegOpenKeyEx(HKEY)", result);
+ vlog.debug("duplicated %x to %x", k, key);
+ freeKey = true;
+}
+
+RegKey::RegKey(const RegKey& k) : key(0), freeKey(false), valueNameBufLen(0) {
+ LONG result = RegOpenKeyEx(k.key, 0, 0, KEY_ALL_ACCESS, &key);
+ if (result != ERROR_SUCCESS)
+ throw rdr::SystemException("RegOpenKeyEx(RegKey&)", result);
+ vlog.debug("duplicated %x to %x", k.key, key);
+ freeKey = true;
+}
+
+RegKey::~RegKey() {
+ close();
+}
+
+
+void RegKey::setHKEY(HKEY k, bool fK) {
+ vlog.debug("setHKEY(%x,%d)", k, (int)fK);
+ close();
+ freeKey = fK;
+ key = k;
+}
+
+
+bool RegKey::createKey(const RegKey& root, const TCHAR* name) {
+ close();
+ LONG result = RegCreateKey(root.key, name, &key);
+ if (result != ERROR_SUCCESS) {
+ vlog.error("RegCreateKey(%x, %s): %x", root.key, name, result);
+ throw rdr::SystemException("RegCreateKeyEx", result);
+ }
+ vlog.debug("createKey(%x,%s) = %x", root.key, (const char*)CStr(name), key);
+ freeKey = true;
+ return true;
+}
+
+void RegKey::openKey(const RegKey& root, const TCHAR* name, bool readOnly) {
+ close();
+ LONG result = RegOpenKeyEx(root.key, name, 0, readOnly ? KEY_READ : KEY_ALL_ACCESS, &key);
+ if (result != ERROR_SUCCESS)
+ throw rdr::SystemException("RegOpenKeyEx (open)", result);
+ vlog.debug("openKey(%x,%s,%s) = %x", root.key, (const char*)CStr(name),
+ readOnly ? "ro" : "rw", key);
+ freeKey = true;
+}
+
+void RegKey::setDACL(const PACL acl, bool inherit) {
+ DWORD result;
+ typedef DWORD (WINAPI *_SetSecurityInfo_proto) (HANDLE, SE_OBJECT_TYPE, SECURITY_INFORMATION, PSID, PSID, PACL, PACL);
+ DynamicFn<_SetSecurityInfo_proto> _SetSecurityInfo(_T("advapi32.dll"), "SetSecurityInfo");
+ if (!_SetSecurityInfo.isValid())
+ throw rdr::SystemException("RegKey::setDACL failed", ERROR_CALL_NOT_IMPLEMENTED);
+ if ((result = (*_SetSecurityInfo)(key, SE_REGISTRY_KEY,
+ DACL_SECURITY_INFORMATION |
+ (inherit ? UNPROTECTED_DACL_SECURITY_INFORMATION : PROTECTED_DACL_SECURITY_INFORMATION),
+ 0, 0, acl, 0)) != ERROR_SUCCESS)
+ throw rdr::SystemException("RegKey::setDACL failed", result);
+}
+
+void RegKey::close() {
+ if (freeKey) {
+ vlog.debug("RegCloseKey(%x)", key);
+ RegCloseKey(key);
+ key = 0;
+ }
+}
+
+void RegKey::deleteKey(const TCHAR* name) const {
+ LONG result = RegDeleteKey(key, name);
+ if (result != ERROR_SUCCESS)
+ throw rdr::SystemException("RegDeleteKey", result);
+}
+
+void RegKey::deleteValue(const TCHAR* name) const {
+ LONG result = RegDeleteValue(key, name);
+ if (result != ERROR_SUCCESS)
+ throw rdr::SystemException("RegDeleteValue", result);
+}
+
+void RegKey::awaitChange(bool watchSubTree, DWORD filter, HANDLE event) const {
+ LONG result = RegNotifyChangeKeyValue(key, watchSubTree, filter, event, event != 0);
+ if (result != ERROR_SUCCESS)
+ throw rdr::SystemException("RegNotifyChangeKeyValue", result);
+}
+
+
+RegKey::operator HKEY() const {return key;}
+
+
+void RegKey::setExpandString(const TCHAR* valname, const TCHAR* value) const {
+ LONG result = RegSetValueEx(key, valname, 0, REG_EXPAND_SZ, (const BYTE*)value, (_tcslen(value)+1)*sizeof(TCHAR));
+ if (result != ERROR_SUCCESS) throw rdr::SystemException("setExpandString", result);
+}
+
+void RegKey::setString(const TCHAR* valname, const TCHAR* value) const {
+ LONG result = RegSetValueEx(key, valname, 0, REG_SZ, (const BYTE*)value, (_tcslen(value)+1)*sizeof(TCHAR));
+ if (result != ERROR_SUCCESS) throw rdr::SystemException("setString", result);
+}
+
+void RegKey::setBinary(const TCHAR* valname, const void* value, int length) const {
+ LONG result = RegSetValueEx(key, valname, 0, REG_BINARY, (const BYTE*)value, length);
+ if (result != ERROR_SUCCESS) throw rdr::SystemException("setBinary", result);
+}
+
+void RegKey::setInt(const TCHAR* valname, int value) const {
+ LONG result = RegSetValueEx(key, valname, 0, REG_DWORD, (const BYTE*)&value, sizeof(value));
+ if (result != ERROR_SUCCESS) throw rdr::SystemException("setInt", result);
+}
+
+void RegKey::setBool(const TCHAR* valname, bool value) const {
+ setInt(valname, value ? 1 : 0);
+}
+
+TCHAR* RegKey::getString(const TCHAR* valname) const {return getRepresentation(valname);}
+TCHAR* RegKey::getString(const TCHAR* valname, const TCHAR* def) const {
+ try {
+ return getString(valname);
+ } catch(rdr::Exception) {
+ return tstrDup(def);
+ }
+}
+
+void RegKey::getBinary(const TCHAR* valname, void** data, int* length) const {
+ TCharArray hex = getRepresentation(valname);
+ if (!rdr::HexInStream::hexStrToBin(CStr(hex.buf), (char**)data, length))
+ throw rdr::Exception("getBinary failed");
+}
+void RegKey::getBinary(const TCHAR* valname, void** data, int* length, void* def, int deflen) const {
+ try {
+ getBinary(valname, data, length);
+ } catch(rdr::Exception) {
+ if (deflen) {
+ *data = new char[deflen];
+ memcpy(*data, def, deflen);
+ } else
+ *data = 0;
+ *length = deflen;
+ }
+}
+
+int RegKey::getInt(const TCHAR* valname) const {
+ TCharArray tmp = getRepresentation(valname);
+ return _ttoi(tmp.buf);
+}
+int RegKey::getInt(const TCHAR* valname, int def) const {
+ try {
+ return getInt(valname);
+ } catch(rdr::Exception) {
+ return def;
+ }
+}
+
+bool RegKey::getBool(const TCHAR* valname) const {
+ return getInt(valname) > 0;
+}
+bool RegKey::getBool(const TCHAR* valname, bool def) const {
+ return getInt(valname, def ? 1 : 0) > 0;
+}
+
+static inline TCHAR* terminateData(char* data, int length)
+{
+ // We must terminate the string, just to be sure. Stupid Win32...
+ int len = length/sizeof(TCHAR);
+ TCharArray str(len+1);
+ memcpy(str.buf, data, length);
+ str.buf[len] = 0;
+ return str.takeBuf();
+}
+
+TCHAR* RegKey::getRepresentation(const TCHAR* valname) const {
+ DWORD type, length;
+ LONG result = RegQueryValueEx(key, valname, 0, &type, 0, &length);
+ if (result != ERROR_SUCCESS)
+ throw rdr::SystemException("get registry value length", result);
+ CharArray data(length);
+ result = RegQueryValueEx(key, valname, 0, &type, (BYTE*)data.buf, &length);
+ if (result != ERROR_SUCCESS)
+ throw rdr::SystemException("get registry value", result);
+
+ switch (type) {
+ case REG_BINARY:
+ {
+ TCharArray hex = rdr::HexOutStream::binToHexStr(data.buf, length);
+ return hex.takeBuf();
+ }
+ case REG_SZ:
+ if (length) {
+ return terminateData(data.buf, length);
+ } else {
+ return tstrDup(_T(""));
+ }
+ case REG_DWORD:
+ {
+ TCharArray tmp(16);
+ _stprintf(tmp.buf, _T("%u"), *((DWORD*)data.buf));
+ return tmp.takeBuf();
+ }
+ case REG_EXPAND_SZ:
+ {
+ if (length) {
+ TCharArray str(terminateData(data.buf, length));
+ DWORD required = ExpandEnvironmentStrings(str.buf, 0, 0);
+ if (required==0)
+ throw rdr::SystemException("ExpandEnvironmentStrings", GetLastError());
+ TCharArray result(required);
+ length = ExpandEnvironmentStrings(str.buf, result.buf, required);
+ if (required<length)
+ rdr::Exception("unable to expand environment strings");
+ return result.takeBuf();
+ } else {
+ return tstrDup(_T(""));
+ }
+ }
+ default:
+ throw rdr::Exception("unsupported registry type");
+ }
+}
+
+bool RegKey::isValue(const TCHAR* valname) const {
+ try {
+ TCharArray tmp = getRepresentation(valname);
+ return true;
+ } catch(rdr::Exception) {
+ return false;
+ }
+}
+
+const TCHAR* RegKey::getValueName(int i) {
+ DWORD maxValueNameLen;
+ LONG result = RegQueryInfoKey(key, 0, 0, 0, 0, 0, 0, 0, &maxValueNameLen, 0, 0, 0);
+ if (result != ERROR_SUCCESS)
+ throw rdr::SystemException("RegQueryInfoKey", result);
+ if (valueNameBufLen < maxValueNameLen + 1) {
+ valueNameBufLen = maxValueNameLen + 1;
+ delete [] valueName.buf;
+ valueName.buf = new TCHAR[valueNameBufLen];
+ }
+ DWORD length = valueNameBufLen;
+ result = RegEnumValue(key, i, valueName.buf, &length, NULL, 0, 0, 0);
+ if (result == ERROR_NO_MORE_ITEMS) return 0;
+ if (result != ERROR_SUCCESS)
+ throw rdr::SystemException("RegEnumValue", result);
+ return valueName.buf;
+}
+
+const TCHAR* RegKey::getKeyName(int i) {
+ DWORD maxValueNameLen;
+ LONG result = RegQueryInfoKey(key, 0, 0, 0, 0, &maxValueNameLen, 0, 0, 0, 0, 0, 0);
+ if (result != ERROR_SUCCESS)
+ throw rdr::SystemException("RegQueryInfoKey", result);
+ if (valueNameBufLen < maxValueNameLen + 1) {
+ valueNameBufLen = maxValueNameLen + 1;
+ delete [] valueName.buf;
+ valueName.buf = new TCHAR[valueNameBufLen];
+ }
+ DWORD length = valueNameBufLen;
+ result = RegEnumKeyEx(key, i, valueName.buf, &length, NULL, 0, 0, 0);
+ if (result == ERROR_NO_MORE_ITEMS) return 0;
+ if (result != ERROR_SUCCESS)
+ throw rdr::SystemException("RegEnumKey", result);
+ return valueName.buf;
+}
diff --git a/win/rfb_win32/Registry.h b/win/rfb_win32/Registry.h
new file mode 100644
index 0000000..68d535c
--- /dev/null
+++ b/win/rfb_win32/Registry.h
@@ -0,0 +1,112 @@
+/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
+ *
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+ * USA.
+ */
+// -=- Registry.h
+
+// C++ wrappers around the Win32 Registry APIs
+
+#ifndef __RFB_WIN32_REGISTRY_H__
+#define __RFB_WIN32_REGISTRY_H__
+
+#include <windows.h>
+#include <rfb_win32/Security.h>
+#include <rfb/util.h>
+
+namespace rfb {
+
+ namespace win32 {
+
+ class RegKey {
+ public:
+ // No key open
+ RegKey();
+
+ // Duplicate the specified existing key
+ RegKey(const HKEY k);
+ RegKey(const RegKey& k);
+
+ // Calls close() internally
+ ~RegKey();
+
+ void setHKEY(HKEY key, bool freeKey);
+ private:
+ RegKey& operator=(const RegKey& k);
+ HKEY& operator=(const HKEY& k);
+ public:
+
+ // Returns true if key was created, false if already existed
+ bool createKey(const RegKey& root, const TCHAR* name);
+
+ // Opens key if it exists, or raises an exception if not
+ void openKey(const RegKey& root, const TCHAR* name, bool readOnly=false);
+
+ // Set the (discretionary) access control list for the key
+ void setDACL(const PACL acl, bool inheritFromParent=true);
+
+ // Closes current key, if required
+ void close();
+
+ // Delete a subkey/value
+ void deleteKey(const TCHAR* name) const;
+ void deleteValue(const TCHAR* name) const;
+
+
+ // Block waiting for a registry change, OR return immediately and notify the
+ // event when there is a change, if specified
+ void awaitChange(bool watchSubTree, DWORD filter, HANDLE event=0) const;
+
+ void setExpandString(const TCHAR* valname, const TCHAR* s) const;
+ void setString(const TCHAR* valname, const TCHAR* s) const;
+ void setBinary(const TCHAR* valname, const void* data, int length) const;
+ void setInt(const TCHAR* valname, int i) const;
+ void setBool(const TCHAR* valname, bool b) const;
+
+ TCHAR* getString(const TCHAR* valname) const;
+ TCHAR* getString(const TCHAR* valname, const TCHAR* def) const;
+
+ void getBinary(const TCHAR* valname, void** data, int* length) const;
+ void getBinary(const TCHAR* valname, void** data, int* length, void* def, int deflength) const;
+
+ int getInt(const TCHAR* valname) const;
+ int getInt(const TCHAR* valname, int def) const;
+
+ bool getBool(const TCHAR* valname) const;
+ bool getBool(const TCHAR* valname, bool def) const;
+
+ TCHAR* getRepresentation(const TCHAR* valname) const;
+
+ bool isValue(const TCHAR* valname) const;
+
+ // Get the name of value/key number "i"
+ // If there are fewer than "i" values then return 0
+ // NAME IS OWNED BY RegKey OBJECT!
+ const TCHAR* getValueName(int i);
+ const TCHAR* getKeyName(int i);
+
+ operator HKEY() const;
+ protected:
+ HKEY key;
+ bool freeKey;
+ TCharArray valueName;
+ DWORD valueNameBufLen;
+ };
+
+ };
+
+};
+
+#endif // __RFB_WIN32_REG_CONFIG_H__
diff --git a/win/rfb_win32/SDisplay.cxx b/win/rfb_win32/SDisplay.cxx
new file mode 100644
index 0000000..0af5064
--- /dev/null
+++ b/win/rfb_win32/SDisplay.cxx
@@ -0,0 +1,524 @@
+/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
+ *
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+ * USA.
+ */
+
+// -=- SDisplay.cxx
+//
+// The SDisplay class encapsulates a particular system display.
+
+#include <rfb_win32/SDisplay.h>
+#include <rfb_win32/Service.h>
+#include <rfb_win32/TsSessions.h>
+#include <rfb_win32/CleanDesktop.h>
+#include <rfb_win32/CurrentUser.h>
+#include <rfb_win32/DynamicFn.h>
+#include <rfb_win32/MonitorInfo.h>
+#include <rfb_win32/SDisplayCorePolling.h>
+#include <rfb_win32/SDisplayCoreWMHooks.h>
+#include <rfb_win32/SDisplayCoreDriver.h>
+#include <rfb/Exception.h>
+#include <rfb/LogWriter.h>
+
+
+using namespace rdr;
+using namespace rfb;
+using namespace rfb::win32;
+
+static LogWriter vlog("SDisplay");
+
+// - SDisplay-specific configuration options
+
+IntParameter rfb::win32::SDisplay::updateMethod("UpdateMethod",
+ "How to discover desktop updates; 0 - Polling, 1 - Application hooking, 2 - Driver hooking.", 1);
+BoolParameter rfb::win32::SDisplay::disableLocalInputs("DisableLocalInputs",
+ "Disable local keyboard and pointer input while the server is in use", false);
+StringParameter rfb::win32::SDisplay::disconnectAction("DisconnectAction",
+ "Action to perform when all clients have disconnected. (None, Lock, Logoff)", "None");
+StringParameter displayDevice("DisplayDevice",
+ "Display device name of the monitor to be remoted, or empty to export the whole desktop.", "");
+BoolParameter rfb::win32::SDisplay::removeWallpaper("RemoveWallpaper",
+ "Remove the desktop wallpaper when the server is in use.", false);
+BoolParameter rfb::win32::SDisplay::removePattern("RemovePattern",
+ "Remove the desktop background pattern when the server is in use.", false);
+BoolParameter rfb::win32::SDisplay::disableEffects("DisableEffects",
+ "Disable desktop user interface effects when the server is in use.", false);
+
+
+//////////////////////////////////////////////////////////////////////////////
+//
+// SDisplay
+//
+
+typedef BOOL (WINAPI *_LockWorkStation_proto)();
+DynamicFn<_LockWorkStation_proto> _LockWorkStation(_T("user32.dll"), "LockWorkStation");
+
+// -=- Constructor/Destructor
+
+SDisplay::SDisplay()
+ : server(0), pb(0), device(0),
+ core(0), ptr(0), kbd(0), clipboard(0),
+ inputs(0), monitor(0), cleanDesktop(0), cursor(0),
+ statusLocation(0)
+{
+ updateEvent.h = CreateEvent(0, TRUE, FALSE, 0);
+}
+
+SDisplay::~SDisplay()
+{
+ // XXX when the VNCServer has been deleted with clients active, stop()
+ // doesn't get called - this ought to be fixed in VNCServerST. In any event,
+ // we should never call any methods on VNCServer once we're being deleted.
+ // This is because it is supposed to be guaranteed that the SDesktop exists
+ // throughout the lifetime of the VNCServer. So if we're being deleted, then
+ // the VNCServer ought not to exist and therefore we shouldn't invoke any
+ // methods on it. Setting server to zero here ensures that stop() doesn't
+ // call setPixelBuffer(0) on the server.
+ server = 0;
+ if (core) stop();
+}
+
+
+// -=- SDesktop interface
+
+void SDisplay::start(VNCServer* vs)
+{
+ vlog.debug("starting");
+
+ // Try to make session zero the console session
+ if (!inConsoleSession())
+ setConsoleSession();
+
+ // Start the SDisplay core
+ server = vs;
+ startCore();
+
+ vlog.debug("started");
+
+ if (statusLocation) *statusLocation = true;
+}
+
+void SDisplay::stop()
+{
+ vlog.debug("stopping");
+
+ // If we successfully start()ed then perform the DisconnectAction
+ if (core) {
+ CurrentUserToken cut;
+ CharArray action = disconnectAction.getData();
+ if (stricmp(action.buf, "Logoff") == 0) {
+ if (!cut.h)
+ vlog.info("ignoring DisconnectAction=Logoff - no current user");
+ else
+ ExitWindowsEx(EWX_LOGOFF, 0);
+ } else if (stricmp(action.buf, "Lock") == 0) {
+ if (!cut.h) {
+ vlog.info("ignoring DisconnectAction=Lock - no current user");
+ } else {
+ if (_LockWorkStation.isValid())
+ (*_LockWorkStation)();
+ else
+ ExitWindowsEx(EWX_LOGOFF, 0);
+ }
+ }
+ }
+
+ // Stop the SDisplayCore
+ if (server)
+ server->setPixelBuffer(0);
+ stopCore();
+ server = 0;
+
+ vlog.debug("stopped");
+
+ if (statusLocation) *statusLocation = false;
+}
+
+
+void SDisplay::startCore() {
+
+ // Currently, we just check whether we're in the console session, and
+ // fail if not
+ if (!inConsoleSession())
+ throw rdr::Exception("Console is not session zero - oreconnect to restore Console sessin");
+
+ // Switch to the current input desktop
+ if (rfb::win32::desktopChangeRequired()) {
+ if (!rfb::win32::changeDesktop())
+ throw rdr::Exception("unable to switch into input desktop");
+ }
+
+ // Initialise the change tracker and clipper
+ updates.clear();
+ clipper.setUpdateTracker(server);
+
+ // Create the framebuffer object
+ recreatePixelBuffer(true);
+
+ // Create the SDisplayCore
+ updateMethod_ = updateMethod;
+ int tryMethod = updateMethod_;
+ while (!core) {
+ try {
+ if (tryMethod == 2)
+ core = new SDisplayCoreDriver(this, &updates);
+ else if (tryMethod == 1)
+ core = new SDisplayCoreWMHooks(this, &updates);
+ else
+ core = new SDisplayCorePolling(this, &updates);
+ core->setScreenRect(screenRect);
+ } catch (rdr::Exception& e) {
+ delete core; core = 0;
+ if (tryMethod == 0)
+ throw rdr::Exception("unable to access desktop");
+ tryMethod--;
+ vlog.error(e.str());
+ }
+ }
+ vlog.info("Started %s", core->methodName());
+
+ // Start display monitor, clipboard handler and input handlers
+ monitor = new WMMonitor;
+ monitor->setNotifier(this);
+ clipboard = new Clipboard;
+ clipboard->setNotifier(this);
+ ptr = new SPointer;
+ kbd = new SKeyboard;
+ inputs = new WMBlockInput;
+ cursor = new WMCursor;
+
+ // Apply desktop optimisations
+ cleanDesktop = new CleanDesktop;
+ if (removePattern)
+ cleanDesktop->disablePattern();
+ if (removeWallpaper)
+ cleanDesktop->disableWallpaper();
+ if (disableEffects)
+ cleanDesktop->disableEffects();
+ isWallpaperRemoved = removeWallpaper;
+ isPatternRemoved = removePattern;
+ areEffectsDisabled = disableEffects;
+}
+
+void SDisplay::stopCore() {
+ if (core)
+ vlog.info("Stopping %s", core->methodName());
+ delete core; core = 0;
+ delete pb; pb = 0;
+ delete device; device = 0;
+ delete monitor; monitor = 0;
+ delete clipboard; clipboard = 0;
+ delete inputs; inputs = 0;
+ delete ptr; ptr = 0;
+ delete kbd; kbd = 0;
+ delete cleanDesktop; cleanDesktop = 0;
+ delete cursor; cursor = 0;
+ ResetEvent(updateEvent);
+}
+
+
+bool SDisplay::areHooksAvailable() {
+ return WMHooks::areAvailable();
+}
+
+bool SDisplay::isDriverAvailable() {
+ return SDisplayCoreDriver::isAvailable();
+}
+
+
+bool SDisplay::isRestartRequired() {
+ // - We must restart the SDesktop if:
+ // 1. We are no longer in the input desktop.
+ // 2. The any setting has changed.
+
+ // - Check that our session is the Console
+ if (!inConsoleSession())
+ return true;
+
+ // - Check that we are in the input desktop
+ if (rfb::win32::desktopChangeRequired())
+ return true;
+
+ // - Check that the update method setting hasn't changed
+ // NB: updateMethod reflects the *selected* update method, not
+ // necessarily the one in use, since we fall back to simpler
+ // methods if more advanced ones fail!
+ if (updateMethod_ != updateMethod)
+ return true;
+
+ // - Check that the desktop optimisation settings haven't changed
+ // This isn't very efficient, but it shouldn't change very often!
+ if ((isWallpaperRemoved != removeWallpaper) ||
+ (isPatternRemoved != removePattern) ||
+ (areEffectsDisabled != disableEffects))
+ return true;
+
+ return false;
+}
+
+
+void SDisplay::restartCore() {
+ vlog.info("restarting");
+
+ // Stop the existing Core related resources
+ stopCore();
+ try {
+ // Start a new Core if possible
+ startCore();
+ vlog.info("restarted");
+ } catch (rdr::Exception& e) {
+ // If startCore() fails then we MUST disconnect all clients,
+ // to cause the server to stop() the desktop.
+ // Otherwise, the SDesktop is in an inconsistent state
+ // and the server will crash.
+ server->closeClients(e.str());
+ }
+}
+
+
+void SDisplay::pointerEvent(const Point& pos, int buttonmask) {
+ if (pb->getRect().contains(pos)) {
+ Point screenPos = pos.translate(screenRect.tl);
+ // - Check that the SDesktop doesn't need restarting
+ if (isRestartRequired())
+ restartCore();
+ if (ptr)
+ ptr->pointerEvent(screenPos, buttonmask);
+ }
+}
+
+void SDisplay::keyEvent(rdr::U32 key, bool down) {
+ // - Check that the SDesktop doesn't need restarting
+ if (isRestartRequired())
+ restartCore();
+ if (kbd)
+ kbd->keyEvent(key, down);
+}
+
+void SDisplay::clientCutText(const char* text, int len) {
+ CharArray clip_sz(len+1);
+ memcpy(clip_sz.buf, text, len);
+ clip_sz.buf[len] = 0;
+ clipboard->setClipText(clip_sz.buf);
+}
+
+
+void SDisplay::framebufferUpdateRequest()
+{
+ SetEvent(updateEvent);
+}
+
+Point SDisplay::getFbSize() {
+ bool startAndStop = !core;
+
+ // If not started, do minimal initialisation to get desktop size.
+ if (startAndStop)
+ recreatePixelBuffer();
+ Point result = Point(pb->width(), pb->height());
+
+ // Destroy the initialised structures.
+ if (startAndStop)
+ stopCore();
+ return result;
+}
+
+
+void
+SDisplay::notifyClipboardChanged(const char* text, int len) {
+ vlog.debug("clipboard text changed");
+ if (server)
+ server->serverCutText(text, len);
+}
+
+
+void
+SDisplay::notifyDisplayEvent(WMMonitor::Notifier::DisplayEventType evt) {
+ switch (evt) {
+ case WMMonitor::Notifier::DisplaySizeChanged:
+ vlog.debug("desktop size changed");
+ recreatePixelBuffer();
+ break;
+ case WMMonitor::Notifier::DisplayPixelFormatChanged:
+ vlog.debug("desktop format changed");
+ recreatePixelBuffer();
+ break;
+ case WMMonitor::Notifier::DisplayColourMapChanged:
+ vlog.debug("desktop colormap changed");
+ pb->updateColourMap();
+ if (server)
+ server->setColourMapEntries();
+ break;
+ default:
+ vlog.error("unknown display event received");
+ }
+}
+
+void
+SDisplay::processEvent(HANDLE event) {
+ if (event == updateEvent) {
+ vlog.write(120, "processEvent");
+ ResetEvent(updateEvent);
+
+ // - If the SDisplay isn't even started then quit now
+ if (!core) {
+ vlog.error("not start()ed");
+ return;
+ }
+
+ // - Ensure that the disableLocalInputs flag is respected
+ inputs->blockInputs(disableLocalInputs);
+
+ // - Only process updates if the server is ready
+ if (server && server->clientsReadyForUpdate()) {
+ bool try_update = false;
+
+ // - Check that the SDesktop doesn't need restarting
+ if (isRestartRequired()) {
+ restartCore();
+ return;
+ }
+
+ // - Flush any updates from the core
+ try {
+ core->flushUpdates();
+ } catch (rdr::Exception& e) {
+ vlog.error(e.str());
+ restartCore();
+ return;
+ }
+
+ // Ensure the cursor is up to date
+ WMCursor::Info info = cursor->getCursorInfo();
+ if (old_cursor != info) {
+ // Update the cursor shape if the visibility has changed
+ bool set_cursor = info.visible != old_cursor.visible;
+ // OR if the cursor is visible and the shape has changed.
+ set_cursor |= info.visible && (old_cursor.cursor != info.cursor);
+
+ // Update the cursor shape
+ if (set_cursor)
+ pb->setCursor(info.visible ? info.cursor : 0, server);
+
+ // Update the cursor position
+ // NB: First translate from Screen coordinates to Desktop
+ Point desktopPos = info.position.translate(screenRect.tl.negate());
+ server->setCursorPos(desktopPos);
+ try_update = true;
+
+ old_cursor = info;
+ }
+
+ // Flush any changes to the server
+ try_update = flushChangeTracker() || try_update;
+ if (try_update) {
+ server->tryUpdate();
+ }
+ }
+ return;
+ }
+ throw rdr::Exception("No such event");
+}
+
+
+// -=- Protected methods
+
+void
+SDisplay::recreatePixelBuffer(bool force) {
+ // Open the specified display device
+ // If no device is specified, open entire screen using GetDC().
+ // Opening the whole display with CreateDC doesn't work on multi-monitor
+ // systems for some reason.
+ DeviceContext* new_device = 0;
+ TCharArray deviceName(displayDevice.getData());
+ if (deviceName.buf[0]) {
+ vlog.info("Attaching to device %s", (const char*)CStr(deviceName.buf));
+ new_device = new DeviceDC(deviceName.buf);
+ }
+ if (!new_device) {
+ vlog.info("Attaching to virtual desktop");
+ new_device = new WindowDC(0);
+ }
+
+ // Get the coordinates of the specified dispay device
+ Rect newScreenRect;
+ if (deviceName.buf[0]) {
+ MonitorInfo info(CStr(deviceName.buf));
+ newScreenRect = Rect(info.rcMonitor.left, info.rcMonitor.top,
+ info.rcMonitor.right, info.rcMonitor.bottom);
+ } else {
+ newScreenRect = new_device->getClipBox();
+ }
+
+ // If nothing has changed & a recreate has not been forced, delete
+ // the new device context and return
+ if (pb && !force &&
+ newScreenRect.equals(screenRect) &&
+ new_device->getPF().equal(pb->getPF())) {
+ delete new_device;
+ return;
+ }
+
+ // Flush any existing changes to the server
+ flushChangeTracker();
+
+ // Delete the old pixelbuffer and device context
+ vlog.debug("deleting old pixel buffer & device");
+ if (pb)
+ delete pb;
+ if (device)
+ delete device;
+
+ // Create a DeviceFrameBuffer attached to the new device
+ vlog.debug("creating pixel buffer");
+ DeviceFrameBuffer* new_buffer = new DeviceFrameBuffer(*new_device);
+
+ // Replace the old PixelBuffer
+ screenRect = newScreenRect;
+ pb = new_buffer;
+ device = new_device;
+
+ // Initialise the pixels
+ pb->grabRegion(pb->getRect());
+
+ // Prevent future grabRect operations from throwing exceptions
+ pb->setIgnoreGrabErrors(true);
+
+ // Update the clipping update tracker
+ clipper.setClipRect(pb->getRect());
+
+ // Inform the core of the changes
+ if (core)
+ core->setScreenRect(screenRect);
+
+ // Inform the server of the changes
+ if (server)
+ server->setPixelBuffer(pb);
+}
+
+bool SDisplay::flushChangeTracker() {
+ if (updates.is_empty())
+ return false;
+
+ vlog.write(120, "flushChangeTracker");
+
+ // Translate the update coordinates from Screen coords to Desktop
+ updates.translate(screenRect.tl.negate());
+
+ // Clip the updates & flush them to the server
+ updates.copyTo(&clipper);
+ updates.clear();
+ return true;
+}
diff --git a/win/rfb_win32/SDisplay.h b/win/rfb_win32/SDisplay.h
new file mode 100644
index 0000000..6dbb50a
--- /dev/null
+++ b/win/rfb_win32/SDisplay.h
@@ -0,0 +1,163 @@
+/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
+ *
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+ * USA.
+ */
+
+// -=- SDisplay.h
+//
+// The SDisplay class encapsulates a system display.
+
+#ifndef __RFB_SDISPLAY_H__
+#define __RFB_SDISPLAY_H__
+
+#include <rfb/SDesktop.h>
+#include <rfb/UpdateTracker.h>
+#include <rfb/Configuration.h>
+#include <rfb_win32/Handle.h>
+#include <rfb_win32/EventManager.h>
+#include <rfb_win32/SInput.h>
+#include <rfb_win32/Clipboard.h>
+#include <rfb_win32/CleanDesktop.h>
+#include <rfb_win32/WMCursor.h>
+#include <rfb_win32/WMNotifier.h>
+#include <rfb_win32/DeviceFrameBuffer.h>
+#include <rfb_win32/DeviceContext.h>
+
+namespace rfb {
+
+ namespace win32 {
+
+ //
+ // -=- SDisplay
+ //
+
+ class SDisplayCore {
+ public:
+ virtual ~SDisplayCore() {};
+ virtual void setScreenRect(const Rect& screenRect_) = 0;
+ virtual void flushUpdates() = 0;
+ virtual const char* methodName() const = 0;
+ };
+
+ class SDisplay : public SDesktop,
+ WMMonitor::Notifier,
+ Clipboard::Notifier,
+ public EventHandler
+ {
+ public:
+ SDisplay();
+ virtual ~SDisplay();
+
+ // -=- SDesktop interface
+
+ virtual void start(VNCServer* vs);
+ virtual void stop();
+ virtual void pointerEvent(const Point& pos, int buttonmask);
+ virtual void keyEvent(rdr::U32 key, bool down);
+ virtual void clientCutText(const char* str, int len);
+ virtual void framebufferUpdateRequest();
+ virtual Point getFbSize();
+
+ // -=- Clipboard
+
+ virtual void notifyClipboardChanged(const char* text, int len);
+
+ // -=- Display events
+
+ virtual void notifyDisplayEvent(WMMonitor::Notifier::DisplayEventType evt);
+
+ // -=- EventHandler interface
+
+ HANDLE getUpdateEvent() {return updateEvent;}
+ virtual void processEvent(HANDLE event);
+
+ // -=- Notification of whether or not SDisplay is started
+
+ void setStatusLocation(bool* status) {statusLocation = status;}
+
+ friend class SDisplayCore;
+
+ static IntParameter updateMethod;
+ static BoolParameter disableLocalInputs;
+ static StringParameter disconnectAction;
+ static BoolParameter removeWallpaper;
+ static BoolParameter removePattern;
+ static BoolParameter disableEffects;
+
+ // -=- Use by VNC Config to determine whether hooks, driver, etc are available
+ static bool areHooksAvailable();
+ static bool isDriverAvailable();
+
+
+ protected:
+ bool isRestartRequired();
+ void startCore();
+ void stopCore();
+ void restartCore();
+ void recreatePixelBuffer(bool force=false);
+ bool flushChangeTracker(); // true if flushed, false if empty
+
+ VNCServer* server;
+
+ // -=- Display pixel buffer
+ DeviceFrameBuffer* pb;
+ DeviceContext* device;
+
+ // -=- The coordinates of Window's entire virtual Screen
+ Rect screenRect;
+
+ // -=- All changes are collected in UN-CLIPPED Display coords and merged
+ // When they are to be flushed to the VNCServer, they are changed
+ // to server coords and clipped appropriately.
+ SimpleUpdateTracker updates;
+ ClippingUpdateTracker clipper;
+
+ // -=- Internal SDisplay implementation
+ SDisplayCore* core;
+ int updateMethod_;
+
+ // Inputs
+ SPointer* ptr;
+ SKeyboard* kbd;
+ Clipboard* clipboard;
+ WMBlockInput* inputs;
+
+ // Desktop state
+ WMMonitor* monitor;
+
+ // Desktop optimisation
+ CleanDesktop* cleanDesktop;
+ bool isWallpaperRemoved;
+ bool isPatternRemoved;
+ bool areEffectsDisabled;
+
+ // Cursor
+ WMCursor* cursor;
+ WMCursor::Info old_cursor;
+ Region old_cursor_region;
+ Point cursor_renderpos;
+
+ // -=- Event signalled to trigger an update to be flushed
+ Handle updateEvent;
+
+ // -=- Where to write the active/inactive indicator to
+ bool* statusLocation;
+ };
+
+ }
+}
+
+#endif // __RFB_SDISPLAY_H__
diff --git a/win/rfb_win32/SDisplayCoreDriver.h b/win/rfb_win32/SDisplayCoreDriver.h
new file mode 100644
index 0000000..5fea75c
--- /dev/null
+++ b/win/rfb_win32/SDisplayCoreDriver.h
@@ -0,0 +1,52 @@
+/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
+ *
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+ * USA.
+ */
+
+// -=- SDisplayCoreDriver.h
+//
+// Placeholder for SDisplayCore mirror-driver implementation.
+
+#ifndef __RFB_SDISPLAY_CORE_DRIVER_H__
+#define __RFB_SDISPLAY_CORE_DRIVER_H__
+
+#include <rfb_win32/SDisplay.h>
+
+namespace rfb {
+ namespace win32 {
+
+ class SDisplayCoreDriver: public SDisplayCore {
+ public:
+ SDisplayCoreDriver(SDisplay* display, UpdateTracker* ut) {
+ throw rdr::Exception("Not supported");
+ }
+
+ // - Called by SDisplay to inform Core of the screen size
+ virtual void setScreenRect(const Rect& screenRect_) {}
+
+ // - Called by SDisplay to flush updates to the specified tracker
+ virtual void flushUpdates() {}
+
+ virtual const char* methodName() const { return "VNC Mirror Driver"; }
+
+ // - Determine whether the display driver is installed & usable
+ static bool isAvailable() { return false; }
+ };
+
+ };
+};
+
+#endif
diff --git a/win/rfb_win32/SDisplayCorePolling.cxx b/win/rfb_win32/SDisplayCorePolling.cxx
new file mode 100644
index 0000000..fc57ecd
--- /dev/null
+++ b/win/rfb_win32/SDisplayCorePolling.cxx
@@ -0,0 +1,81 @@
+/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
+ *
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+ * USA.
+ */
+
+// -=- SDisplayCorePolling.cxx
+
+#include <rfb_win32/SDisplayCorePolling.h>
+#include <rfb/LogWriter.h>
+
+using namespace rfb;
+using namespace rfb::win32;
+
+static LogWriter vlog("SDisplayCorePolling");
+
+const int POLLING_SEGMENTS = 16;
+
+const int SDisplayCorePolling::pollTimerId = 1;
+
+SDisplayCorePolling::SDisplayCorePolling(SDisplay* d, UpdateTracker* ut, int pollInterval_)
+ : MsgWindow(_T("rfb::win32::SDisplayCorePolling")), updateTracker(ut),
+ pollTimer(getHandle(), pollTimerId), pollNextStrip(false), display(d) {
+ pollInterval = max(10, (pollInterval_ / POLLING_SEGMENTS));
+ copyrect.setUpdateTracker(ut);
+}
+
+SDisplayCorePolling::~SDisplayCorePolling() {
+}
+
+LRESULT SDisplayCorePolling::processMessage(UINT msg, WPARAM wParam, LPARAM lParam) {
+ if (msg == WM_TIMER && wParam == pollTimerId) {
+ pollNextStrip = true;
+ SetEvent(display->getUpdateEvent());
+ return 0;
+ }
+ return MsgWindow::processMessage(msg, wParam, lParam);
+}
+
+void SDisplayCorePolling::setScreenRect(const Rect& screenRect_) {
+ vlog.info("setScreenRect");
+ screenRect = screenRect_;
+ pollIncrementY = (screenRect.height()+POLLING_SEGMENTS-1)/POLLING_SEGMENTS;
+ pollNextY = screenRect.tl.y;
+ pollTimer.start(pollInterval);
+}
+
+void SDisplayCorePolling::flushUpdates() {
+ vlog.write(120, "flushUpdates");
+
+ // Check for window movement
+ while (copyrect.processEvent()) {}
+
+ if (pollNextStrip) {
+ // Poll the next strip of the screen (in Screen coordinates)
+ pollNextStrip = false;
+ Rect pollrect = screenRect;
+ if (pollNextY >= pollrect.br.y) {
+ // Yes. Reset the counter and return
+ pollNextY = pollrect.tl.y;
+ } else {
+ // No. Poll the next section
+ pollrect.tl.y = pollNextY;
+ pollNextY += pollIncrementY;
+ pollrect.br.y = min(pollNextY, pollrect.br.y);
+ updateTracker->add_changed(pollrect);
+ }
+ }
+}
diff --git a/win/rfb_win32/SDisplayCorePolling.h b/win/rfb_win32/SDisplayCorePolling.h
new file mode 100644
index 0000000..9e1b5ad
--- /dev/null
+++ b/win/rfb_win32/SDisplayCorePolling.h
@@ -0,0 +1,75 @@
+/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
+ *
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+ * USA.
+ */
+
+// -=- SDisplayCorePolling.h
+//
+// SDisplayCore implementation that simply polls the screen, in sections,
+// in order to detect changes. This Core will signal the SDisplay's
+// updateEvent regularly, causing it to call the Core back to propagate
+// changes to the VNC Server.
+
+
+#ifndef __RFB_SDISPLAY_CORE_POLLING_H__
+#define __RFB_SDISPLAY_CORE_POLLING_H__
+
+#include <rfb_win32/SDisplay.h>
+#include <rfb_win32/IntervalTimer.h>
+#include <rfb_win32/WMWindowCopyRect.h>
+
+namespace rfb {
+ namespace win32 {
+
+ class SDisplayCorePolling : public SDisplayCore, protected MsgWindow {
+ public:
+ SDisplayCorePolling(SDisplay* display, UpdateTracker* ut, int pollIntervalMs=50);
+ ~SDisplayCorePolling();
+
+ // - Called by SDisplay to inform Core of the screen size
+ virtual void setScreenRect(const Rect& screenRect_);
+
+ // - Called by SDisplay to flush updates to the specified tracker
+ virtual void flushUpdates();
+
+ virtual const char* methodName() const { return "Polling"; }
+
+ protected:
+ // - MsgWindow overrides
+ // processMessage is used to service the cursor & polling timers
+ virtual LRESULT processMessage(UINT msg, WPARAM wParam, LPARAM lParam);
+
+ // - Hooking subcomponents used to track the desktop state
+ WMCopyRect copyrect;
+
+ // - Background full screen polling fields
+ IntervalTimer pollTimer;
+ static const int pollTimerId;
+ Rect screenRect;
+ int pollInterval;
+ int pollNextY;
+ int pollIncrementY;
+ bool pollNextStrip;
+
+ // - Handle back to the owning SDisplay, and to the UpdateTracker to flush to
+ SDisplay* display;
+ UpdateTracker* updateTracker;
+ };
+
+ };
+};
+
+#endif
\ No newline at end of file
diff --git a/win/rfb_win32/SDisplayCoreWMHooks.cxx b/win/rfb_win32/SDisplayCoreWMHooks.cxx
new file mode 100644
index 0000000..10b88e0
--- /dev/null
+++ b/win/rfb_win32/SDisplayCoreWMHooks.cxx
@@ -0,0 +1,74 @@
+/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
+ *
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+ * USA.
+ */
+
+// -=- SDisplayCoreWMHooks.cxx
+
+#include <rfb_win32/SDisplayCoreWMHooks.h>
+#include <rfb/LogWriter.h>
+
+using namespace rfb;
+using namespace rfb::win32;
+
+static LogWriter vlog("SDisplayCoreWMHooks");
+
+const int SDisplayCoreWMHooks::cursorTimerId = 2;
+const int SDisplayCoreWMHooks::consolePollTimerId = 3;
+
+
+SDisplayCoreWMHooks::SDisplayCoreWMHooks(SDisplay* d, UpdateTracker* ut)
+ : SDisplayCorePolling(d, ut, 5000),
+ cursorTimer(getHandle(), cursorTimerId),
+ consolePollTimer(getHandle(), consolePollTimerId),
+ pollConsoles(false) {
+ if (!hooks.setEvent(display->getUpdateEvent()))
+ throw rdr::Exception("hook subsystem failed to initialise");
+ poller.setUpdateTracker(updateTracker);
+ cursorTimer.start(20);
+ consolePollTimer.start(200);
+}
+
+SDisplayCoreWMHooks::~SDisplayCoreWMHooks() {
+}
+
+LRESULT SDisplayCoreWMHooks::processMessage(UINT msg, WPARAM wParam, LPARAM lParam) {
+ if (msg == WM_TIMER) {
+ if (wParam == cursorTimerId) {
+ SetEvent(display->getUpdateEvent());
+ return 0;
+ } else if (wParam == consolePollTimerId) {
+ pollConsoles = true;
+ SetEvent(display->getUpdateEvent());
+ return 0;
+ }
+ }
+ return SDisplayCorePolling::processMessage(msg, wParam, lParam);
+}
+
+void SDisplayCoreWMHooks::flushUpdates() {
+ // Poll any visible console windows
+ if (pollConsoles) {
+ pollConsoles = false;
+ poller.processEvent();
+ }
+
+ // Check for updates from the hooks
+ hooks.getUpdates(updateTracker);
+
+ // Check for updates from the polling Core
+ SDisplayCorePolling::flushUpdates();
+}
diff --git a/win/rfb_win32/SDisplayCoreWMHooks.h b/win/rfb_win32/SDisplayCoreWMHooks.h
new file mode 100644
index 0000000..24fa5cd
--- /dev/null
+++ b/win/rfb_win32/SDisplayCoreWMHooks.h
@@ -0,0 +1,68 @@
+/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
+ *
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+ * USA.
+ */
+
+// -=- SDisplayCoreWMHooks.h
+//
+// SDisplayCore implementation that uses WMHooks to capture changes to
+// the display.
+// Whenever changes are detected, the SDisplay's updateEvent is signalled,
+// so that it can perform housekeeping tasks (like ensuring the currently
+// active desktop is the correct one), before flushing changes from the
+// Core to the VNC Server. The SDisplay will clip the changes before they
+// reach the VNC Server.
+
+
+#ifndef __RFB_SDISPLAY_CORE_WMHOOKS_H__
+#define __RFB_SDISPLAY_CORE_WMHOOKS_H__
+
+#include <rfb_win32/SDisplayCorePolling.h>
+#include <rfb_win32/WMHooks.h>
+#include <rfb_win32/WMPoller.h>
+
+namespace rfb {
+ namespace win32 {
+
+ class SDisplayCoreWMHooks : public SDisplayCorePolling {
+ public:
+ SDisplayCoreWMHooks(SDisplay* display, UpdateTracker* ut);
+ ~SDisplayCoreWMHooks();
+
+ // - Called by SDisplay to flush updates to the specified tracker
+ virtual void flushUpdates();
+
+ virtual const char* methodName() const { return "VNC Hooks"; }
+
+ protected:
+ // - MsgWindow overrides
+ // processMessage is used to service the cursor & polling timers
+ virtual LRESULT processMessage(UINT msg, WPARAM wParam, LPARAM lParam);
+
+ // - Hooking subcomponents used to track the desktop state
+ WMHooks hooks;
+ WMPoller poller;
+ IntervalTimer cursorTimer;
+ IntervalTimer consolePollTimer;
+ bool pollConsoles;
+ static const int consolePollTimerId;
+ static const int cursorTimerId;
+ };
+
+ };
+};
+
+#endif
diff --git a/win/rfb_win32/SFileTransferManagerWin32.cxx b/win/rfb_win32/SFileTransferManagerWin32.cxx
new file mode 100644
index 0000000..edc898b
--- /dev/null
+++ b/win/rfb_win32/SFileTransferManagerWin32.cxx
@@ -0,0 +1,71 @@
+/* Copyright (C) 2006 TightVNC Team. All Rights Reserved.
+ *
+ * Developed by Dennis Syrovatsky.
+ *
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+ * USA.
+ *
+ * TightVNC distribution homepage on the Web: http://www.tightvnc.com/
+ *
+ */
+
+// -=- SFileTransferManagerWin32.cxx
+
+#include <rfb_win32/SFileTransferManagerWin32.h>
+
+using namespace rfb;
+using namespace win32;
+
+SFileTransferManagerWin32::SFileTransferManagerWin32()
+{
+
+}
+
+SFileTransferManagerWin32::~SFileTransferManagerWin32()
+{
+
+}
+
+SFileTransfer *
+SFileTransferManagerWin32::createObject(network::Socket *sock)
+{
+ rfb::SFileTransfer *pFT = 0;
+ rfb::win32::SFileTransferWin32 *pFTWin32 = 0;
+
+ pFTWin32 = new SFileTransferWin32(sock);
+ if (pFTWin32 == NULL) return NULL;
+
+ pFT = (SFileTransfer *) pFTWin32;
+
+ m_lstFTObjects.push_front(pFT);
+
+ return pFT;
+}
+
+void
+SFileTransferManagerWin32::processDownloadMsg(MSG msg)
+{
+ SFileTransfer *pFT = (SFileTransfer *)msg.lParam;
+
+ if (pFT != NULL) {
+ std::list<SFileTransfer*>::iterator i;
+ for (i=m_lstFTObjects.begin(); i!=m_lstFTObjects.end(); i++) {
+ if ((*i) == pFT) {
+ (*i)->sendFileDownloadPortion();
+ return;
+ }
+ }
+ }
+}
diff --git a/win/rfb_win32/SFileTransferManagerWin32.h b/win/rfb_win32/SFileTransferManagerWin32.h
new file mode 100644
index 0000000..ed1f997
--- /dev/null
+++ b/win/rfb_win32/SFileTransferManagerWin32.h
@@ -0,0 +1,48 @@
+/* Copyright (C) 2006 TightVNC Team. All Rights Reserved.
+ *
+ * Developed by Dennis Syrovatsky.
+ *
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+ * USA.
+ *
+ * TightVNC distribution homepage on the Web: http://www.tightvnc.com/
+ *
+ */
+
+// -=- SFileTransferManagerWin32.h
+
+#ifndef __RFB_WIN32_SFILETRANSFERMANAGERWIN32_H__
+#define __RFB_WIN32_SFILETRANSFERMANAGERWIN32_H__
+
+#include <rfb/SFileTransfer.h>
+#include <rfb/SFileTransferManager.h>
+#include <rfb_win32/SFileTransferWin32.h>
+
+namespace rfb {
+ namespace win32 {
+ class SFileTransferManagerWin32 : public rfb::SFileTransferManager
+ {
+ public:
+ SFileTransferManagerWin32();
+ virtual ~SFileTransferManagerWin32();
+
+ void processDownloadMsg(MSG msg);
+
+ virtual SFileTransfer *createObject(network::Socket *sock);
+ };
+ };
+}
+
+#endif // __RFB_WIN32_SFILETRANSFERMANAGERWIN32_H__
diff --git a/win/rfb_win32/SFileTransferWin32.cxx b/win/rfb_win32/SFileTransferWin32.cxx
new file mode 100644
index 0000000..5aea412
--- /dev/null
+++ b/win/rfb_win32/SFileTransferWin32.cxx
@@ -0,0 +1,125 @@
+/* Copyright (C) 2006 TightVNC Team. All Rights Reserved.
+ *
+ * Developed by Dennis Syrovatsky.
+ *
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+ * USA.
+ *
+ * TightVNC distribution homepage on the Web: http://www.tightvnc.com/
+ *
+ */
+
+// -=- SFileTransferWin32.cxx
+
+#include <rfb/msgTypes.h>
+#include <rfb_win32/FolderManager.h>
+#include <rfb_win32/SFileTransferWin32.h>
+
+using namespace rfb;
+using namespace rfb::win32;
+
+SFileTransferWin32::SFileTransferWin32(network::Socket *sock) : SFileTransfer(sock)
+{
+}
+
+SFileTransferWin32::~SFileTransferWin32()
+{
+}
+
+bool
+SFileTransferWin32::initDownloadCallback()
+{
+ PostThreadMessage(GetCurrentThreadId(), VNCM_FT_DOWNLOAD, (WPARAM) 0, (LPARAM) this);
+ return true;
+}
+
+bool
+SFileTransferWin32::processDownloadCallback()
+{
+ return sendFileDownloadPortion();
+}
+
+bool
+SFileTransferWin32::convertPathFromNet(char *pszPath)
+{
+ int len = strlen(pszPath);
+ if (pszPath[0] == '/') {
+ if (len == 1) {
+ pszPath[0] = '\0';
+ return true;
+ }
+ } else {
+ return false;
+ }
+
+ for(int i = 0; i < (len - 1); i++) {
+ if(pszPath[i+1] == '/') pszPath[i+1] = '\\';
+ pszPath[i] = pszPath[i+1];
+ }
+
+ pszPath[len-1] = '\0';
+ return true;
+}
+
+bool
+SFileTransferWin32::makeFileList(char *pszPath, FileInfo *pFI, bool bDirOnly)
+{
+ FolderManager fm;
+ if (fm.getDirInfo(pszPath, pFI, bDirOnly))
+ return true;
+ else
+ return false;
+}
+
+bool
+SFileTransferWin32::deleteIt(char *pszPath)
+{
+ FolderManager fm;
+
+ return fm.deleteIt(pszPath);
+}
+
+bool
+SFileTransferWin32::renameIt(char *pszOldPath, char *pszNewPath)
+{
+ FolderManager fm;
+
+ return fm.renameIt(pszOldPath, pszNewPath);
+}
+
+bool
+SFileTransferWin32::createDir(char *pszPath)
+{
+ FolderManager fm;
+
+ return fm.createDir(pszPath);
+}
+
+bool
+SFileTransferWin32::getDirSize(char *pszName, unsigned short *pHighSize16,
+ unsigned int *pLowSize32)
+{
+ FolderManager fm;
+ DWORD64 dw64DirSize = 0;
+
+ if (!fm.getDirSize(pszName, &dw64DirSize)) return false;
+
+ if (dw64DirSize & 0xFFFF000000000000) return false;
+
+ *pHighSize16 = ((dw64DirSize & 0x0000FFFF00000000) >> 32);
+ *pLowSize32 = (dw64DirSize & 0x00000000FFFFFFFF);
+
+ return true;
+}
diff --git a/win/rfb_win32/SFileTransferWin32.h b/win/rfb_win32/SFileTransferWin32.h
new file mode 100644
index 0000000..5f682a4
--- /dev/null
+++ b/win/rfb_win32/SFileTransferWin32.h
@@ -0,0 +1,59 @@
+/* Copyright (C) 2006 TightVNC Team. All Rights Reserved.
+ *
+ * Developed by Dennis Syrovatsky.
+ *
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+ * USA.
+ *
+ * TightVNC distribution homepage on the Web: http://www.tightvnc.com/
+ *
+ */
+
+// -=- SFileTransferWin32.h
+
+#ifndef __RFB_SFILETRANSFERWIN32_H__
+#define __RFB_SFILETRANSFERWIN32_H__
+
+#include <windows.h>
+
+#include <rfb/SFileTransfer.h>
+
+const UINT VNCM_FT_DOWNLOAD = WM_USER + 2;
+
+namespace rfb {
+ namespace win32 {
+ class SFileTransferWin32 : public rfb::SFileTransfer
+ {
+ public:
+ SFileTransferWin32(network::Socket *sock);
+ virtual ~SFileTransferWin32();
+
+ bool processDownloadCallback();
+ virtual bool initDownloadCallback();
+
+ virtual bool convertPathFromNet(char *pszPath);
+ virtual bool makeFileList(char *pszPath, FileInfo *pFI, bool bDirOnly);
+
+ virtual bool deleteIt(char *pszPath);
+ virtual bool renameIt(char *pszOldPath, char *pszNewPath);
+ virtual bool createDir(char *pszPath);
+
+ virtual bool getDirSize(char *pszName, unsigned short *pHighSize16, unsigned int *pLowSize32);
+
+ };
+ };
+}
+
+#endif // __RFB_SFILETRANSFERWIN32_H__
diff --git a/win/rfb_win32/SInput.cxx b/win/rfb_win32/SInput.cxx
new file mode 100644
index 0000000..db59287
--- /dev/null
+++ b/win/rfb_win32/SInput.cxx
@@ -0,0 +1,466 @@
+/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
+ *
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+ * USA.
+ */
+
+// -=- SInput.cxx
+//
+// A number of routines that accept VNC input event data and perform
+// the appropriate actions under Win32
+
+#define XK_MISCELLANY
+#define XK_LATIN1
+#define XK_CURRENCY
+#include <rfb/keysymdef.h>
+
+#include <tchar.h>
+#include <rfb_win32/SInput.h>
+#include <rfb_win32/MonitorInfo.h>
+#include <rfb_win32/Service.h>
+#include <rfb_win32/OSVersion.h>
+#include <rfb_win32/DynamicFn.h>
+#include <rfb_win32/keymap.h>
+#include <rdr/Exception.h>
+#include <rfb/LogWriter.h>
+
+#if(defined(INPUT_MOUSE) && defined(RFB_HAVE_MONITORINFO))
+#define RFB_HAVE_SENDINPUT
+#else
+#pragma message(" NOTE: Not building SendInput support.")
+#endif
+
+using namespace rfb;
+
+static LogWriter vlog("SInput");
+
+#ifdef RFB_HAVE_SENDINPUT
+typedef UINT (WINAPI *_SendInput_proto)(UINT, LPINPUT, int);
+static win32::DynamicFn<_SendInput_proto> _SendInput(_T("user32.dll"), "SendInput");
+#endif
+
+//
+// -=- Pointer implementation for Win32
+//
+
+static DWORD buttonDownMapping[8] = {
+ MOUSEEVENTF_LEFTDOWN, MOUSEEVENTF_MIDDLEDOWN, MOUSEEVENTF_RIGHTDOWN,
+ MOUSEEVENTF_WHEEL, MOUSEEVENTF_WHEEL, 0, 0, 0
+};
+
+static DWORD buttonUpMapping[8] = {
+ MOUSEEVENTF_LEFTUP, MOUSEEVENTF_MIDDLEUP, MOUSEEVENTF_RIGHTUP,
+ MOUSEEVENTF_WHEEL, MOUSEEVENTF_WHEEL, 0, 0, 0
+};
+
+static DWORD buttonDataMapping[8] = {
+ 0, 0, 0, 120, -120, 0, 0, 0
+};
+
+win32::SPointer::SPointer()
+ : last_buttonmask(0)
+{
+}
+
+void
+win32::SPointer::pointerEvent(const Point& pos, int buttonmask)
+{
+ // - We are specifying absolute coordinates
+ DWORD flags = MOUSEEVENTF_ABSOLUTE;
+
+ // - Has the pointer moved since the last event?
+ if (!last_position.equals(pos))
+ flags |= MOUSEEVENTF_MOVE;
+
+ // - If the system swaps left and right mouse buttons then we must
+ // swap them here to negate the effect, so that we do the actual
+ // action we mean to do
+ if (::GetSystemMetrics(SM_SWAPBUTTON)) {
+ bool leftDown = buttonmask & 1;
+ bool rightDown = buttonmask & 4;
+ buttonmask = (buttonmask & ~(1 | 4));
+ if (leftDown) buttonmask |= 4;
+ if (rightDown) buttonmask |= 1;
+ }
+
+ DWORD data = 0;
+ for (int i = 0; i < 8; i++) {
+ if ((buttonmask & (1<<i)) != (last_buttonmask & (1<<i))) {
+ if (buttonmask & (1<<i)) {
+ flags |= buttonDownMapping[i];
+ if (buttonDataMapping[i]) {
+ if (data) vlog.info("warning - two buttons set mouse_event data field");
+ data = buttonDataMapping[i];
+ }
+ } else {
+ flags |= buttonUpMapping[i];
+ }
+ }
+ }
+
+ last_position = pos;
+ last_buttonmask = buttonmask;
+
+ Rect primaryDisplay(0,0,GetSystemMetrics(SM_CXSCREEN),GetSystemMetrics(SM_CYSCREEN));
+ if (primaryDisplay.contains(pos)) {
+ // mouse_event wants coordinates specified as a proportion of the
+ // primary display's size, scaled to the range 0 to 65535
+ Point scaled;
+ scaled.x = (pos.x * 65535) / (primaryDisplay.width()-1);
+ scaled.y = (pos.y * 65535) / (primaryDisplay.height()-1);
+ ::mouse_event(flags, scaled.x, scaled.y, data, 0);
+ } else {
+ // The event lies outside the primary monitor. Under Win2K, we can just use
+ // SendInput, which allows us to provide coordinates scaled to the virtual desktop.
+ // SendInput is available on all multi-monitor-aware platforms.
+#ifdef RFB_HAVE_SENDINPUT
+ if (osVersion.isPlatformNT) {
+ if (!_SendInput.isValid())
+ throw rdr::Exception("SendInput not available");
+ INPUT evt;
+ evt.type = INPUT_MOUSE;
+ Point vPos(pos.x-GetSystemMetrics(SM_XVIRTUALSCREEN),
+ pos.y-GetSystemMetrics(SM_YVIRTUALSCREEN));
+ evt.mi.dx = (vPos.x * 65535) / (GetSystemMetrics(SM_CXVIRTUALSCREEN)-1);
+ evt.mi.dy = (vPos.y * 65535) / (GetSystemMetrics(SM_CYVIRTUALSCREEN)-1);
+ evt.mi.dwFlags = flags | MOUSEEVENTF_VIRTUALDESK;
+ evt.mi.dwExtraInfo = 0;
+ evt.mi.mouseData = data;
+ evt.mi.time = 0;
+ if ((*_SendInput)(1, &evt, sizeof(evt)) != 1)
+ throw rdr::SystemException("SendInput", GetLastError());
+ } else {
+ // Under Win9x, this is not addressable by either mouse_event or SendInput
+ // *** STUPID KLUDGY HACK ***
+ POINT cursorPos; GetCursorPos(&cursorPos);
+ ULONG oldSpeed, newSpeed = 10;
+ ULONG mouseInfo[3];
+ if (flags & MOUSEEVENTF_MOVE) {
+ flags &= ~MOUSEEVENTF_ABSOLUTE;
+ SystemParametersInfo(SPI_GETMOUSE, 0, &mouseInfo, 0);
+ SystemParametersInfo(SPI_GETMOUSESPEED, 0, &oldSpeed, 0);
+ vlog.debug("SPI_GETMOUSE %d, %d, %d, speed %d", mouseInfo[0], mouseInfo[1], mouseInfo[2], oldSpeed);
+ ULONG idealMouseInfo[] = {10, 0, 0};
+ SystemParametersInfo(SPI_SETMOUSESPEED, 0, &newSpeed, 0);
+ SystemParametersInfo(SPI_SETMOUSE, 0, &idealMouseInfo, 0);
+ }
+ ::mouse_event(flags, pos.x-cursorPos.x, pos.y-cursorPos.y, data, 0);
+ if (flags & MOUSEEVENTF_MOVE) {
+ SystemParametersInfo(SPI_SETMOUSE, 0, &mouseInfo, 0);
+ SystemParametersInfo(SPI_SETMOUSESPEED, 0, &oldSpeed, 0);
+ }
+ }
+#endif
+ }
+}
+
+//
+// -=- Keyboard implementation
+//
+
+BoolParameter rfb::win32::SKeyboard::deadKeyAware("DeadKeyAware",
+ "Whether to assume the viewer has already interpreted dead key sequences "
+ "into latin-1 characters", true);
+
+static bool oneShift;
+
+// The keysymToAscii table transforms a couple of awkward keysyms into their
+// ASCII equivalents.
+struct keysymToAscii_t {
+ rdr::U32 keysym;
+ rdr::U8 ascii;
+};
+
+keysymToAscii_t keysymToAscii[] = {
+ { XK_KP_Space, ' ' },
+ { XK_KP_Equal, '=' },
+};
+
+rdr::U8 latin1DeadChars[] = {
+ XK_grave, XK_acute, XK_asciicircum, XK_diaeresis, XK_degree, XK_cedilla,
+ XK_asciitilde
+};
+
+struct latin1ToDeadChars_t {
+ rdr::U8 latin1Char;
+ rdr::U8 deadChar;
+ rdr::U8 baseChar;
+};
+
+latin1ToDeadChars_t latin1ToDeadChars[] = {
+
+ { XK_Agrave, XK_grave, XK_A },
+ { XK_Egrave, XK_grave, XK_E },
+ { XK_Igrave, XK_grave, XK_I },
+ { XK_Ograve, XK_grave, XK_O },
+ { XK_Ugrave, XK_grave, XK_U },
+ { XK_agrave, XK_grave, XK_a },
+ { XK_egrave, XK_grave, XK_e },
+ { XK_igrave, XK_grave, XK_i },
+ { XK_ograve, XK_grave, XK_o},
+ { XK_ugrave, XK_grave, XK_u },
+
+ { XK_Aacute, XK_acute, XK_A },
+ { XK_Eacute, XK_acute, XK_E },
+ { XK_Iacute, XK_acute, XK_I },
+ { XK_Oacute, XK_acute, XK_O },
+ { XK_Uacute, XK_acute, XK_U },
+ { XK_Yacute, XK_acute, XK_Y },
+ { XK_aacute, XK_acute, XK_a },
+ { XK_eacute, XK_acute, XK_e },
+ { XK_iacute, XK_acute, XK_i },
+ { XK_oacute, XK_acute, XK_o},
+ { XK_uacute, XK_acute, XK_u },
+ { XK_yacute, XK_acute, XK_y },
+
+ { XK_Acircumflex, XK_asciicircum, XK_A },
+ { XK_Ecircumflex, XK_asciicircum, XK_E },
+ { XK_Icircumflex, XK_asciicircum, XK_I },
+ { XK_Ocircumflex, XK_asciicircum, XK_O },
+ { XK_Ucircumflex, XK_asciicircum, XK_U },
+ { XK_acircumflex, XK_asciicircum, XK_a },
+ { XK_ecircumflex, XK_asciicircum, XK_e },
+ { XK_icircumflex, XK_asciicircum, XK_i },
+ { XK_ocircumflex, XK_asciicircum, XK_o},
+ { XK_ucircumflex, XK_asciicircum, XK_u },
+
+ { XK_Adiaeresis, XK_diaeresis, XK_A },
+ { XK_Ediaeresis, XK_diaeresis, XK_E },
+ { XK_Idiaeresis, XK_diaeresis, XK_I },
+ { XK_Odiaeresis, XK_diaeresis, XK_O },
+ { XK_Udiaeresis, XK_diaeresis, XK_U },
+ { XK_adiaeresis, XK_diaeresis, XK_a },
+ { XK_ediaeresis, XK_diaeresis, XK_e },
+ { XK_idiaeresis, XK_diaeresis, XK_i },
+ { XK_odiaeresis, XK_diaeresis, XK_o},
+ { XK_udiaeresis, XK_diaeresis, XK_u },
+ { XK_ydiaeresis, XK_diaeresis, XK_y },
+
+ { XK_Aring, XK_degree, XK_A },
+ { XK_aring, XK_degree, XK_a },
+
+ { XK_Ccedilla, XK_cedilla, XK_C },
+ { XK_ccedilla, XK_cedilla, XK_c },
+
+ { XK_Atilde, XK_asciitilde, XK_A },
+ { XK_Ntilde, XK_asciitilde, XK_N },
+ { XK_Otilde, XK_asciitilde, XK_O },
+ { XK_atilde, XK_asciitilde, XK_a },
+ { XK_ntilde, XK_asciitilde, XK_n },
+ { XK_otilde, XK_asciitilde, XK_o },
+};
+
+// doKeyboardEvent wraps the system keybd_event function and attempts to find
+// the appropriate scancode corresponding to the supplied virtual keycode.
+
+inline void doKeyboardEvent(BYTE vkCode, DWORD flags) {
+ vlog.debug("vkCode 0x%x flags 0x%x", vkCode, flags);
+ keybd_event(vkCode, MapVirtualKey(vkCode, 0), flags, 0);
+}
+
+// KeyStateModifier is a class which helps simplify generating a "fake" press
+// or release of shift, ctrl, alt, etc. An instance of the class is created
+// for every key which may need to be pressed or released. Then either press()
+// or release() may be called to make sure that the corresponding key is in the
+// right state. The destructor of the class automatically reverts to the
+// previous state.
+
+class KeyStateModifier {
+public:
+ KeyStateModifier(int vkCode_, int flags_=0)
+ : vkCode(vkCode_), flags(flags_), pressed(false), released(false)
+ {}
+ void press() {
+ if (!(GetAsyncKeyState(vkCode) & 0x8000)) {
+ doKeyboardEvent(vkCode, flags);
+ pressed = true;
+ }
+ }
+ void release() {
+ if (GetAsyncKeyState(vkCode) & 0x8000) {
+ doKeyboardEvent(vkCode, flags | KEYEVENTF_KEYUP);
+ released = true;
+ }
+ }
+ ~KeyStateModifier() {
+ if (pressed) {
+ doKeyboardEvent(vkCode, flags | KEYEVENTF_KEYUP);
+ } else if (released) {
+ doKeyboardEvent(vkCode, flags);
+ }
+ }
+ int vkCode;
+ int flags;
+ bool pressed;
+ bool released;
+};
+
+
+// doKeyEventWithModifiers() generates a key event having first "pressed" or
+// "released" the shift, ctrl or alt modifiers if necessary.
+
+void doKeyEventWithModifiers(BYTE vkCode, BYTE modifierState, bool down)
+{
+ KeyStateModifier ctrl(VK_CONTROL);
+ KeyStateModifier alt(VK_MENU);
+ KeyStateModifier shift(VK_SHIFT);
+
+ if (down) {
+ if (modifierState & 2) ctrl.press();
+ if (modifierState & 4) alt.press();
+ if (modifierState & 1) {
+ shift.press();
+ } else {
+ shift.release();
+ }
+ }
+ doKeyboardEvent(vkCode, down ? 0 : KEYEVENTF_KEYUP);
+}
+
+
+win32::SKeyboard::SKeyboard()
+{
+ oneShift = rfb::win32::osVersion.isPlatformWindows;
+ for (int i = 0; i < sizeof(keymap) / sizeof(keymap_t); i++) {
+ vkMap[keymap[i].keysym] = keymap[i].vk;
+ extendedMap[keymap[i].keysym] = keymap[i].extended;
+ }
+
+ // Find dead characters for the current keyboard layout
+ // XXX how could we handle the keyboard layout changing?
+ BYTE keystate[256];
+ memset(keystate, 0, 256);
+ for (int j = 0; j < sizeof(latin1DeadChars); j++) {
+ SHORT s = VkKeyScan(latin1DeadChars[j]);
+ if (s != -1) {
+ BYTE vkCode = LOBYTE(s);
+ BYTE modifierState = HIBYTE(s);
+ keystate[VK_SHIFT] = (modifierState & 1) ? 0x80 : 0;
+ keystate[VK_CONTROL] = (modifierState & 2) ? 0x80 : 0;
+ keystate[VK_MENU] = (modifierState & 4) ? 0x80 : 0;
+ rdr::U8 chars[2];
+ int nchars = ToAscii(vkCode, 0, keystate, (WORD*)&chars, 0);
+ if (nchars < 0) {
+ vlog.debug("Found dead key 0x%x '%c'",
+ latin1DeadChars[j], latin1DeadChars[j]);
+ deadChars.push_back(latin1DeadChars[j]);
+ ToAscii(vkCode, 0, keystate, (WORD*)&chars, 0);
+ }
+ }
+ }
+}
+
+
+void win32::SKeyboard::keyEvent(rdr::U32 keysym, bool down)
+{
+ for (int i = 0; i < sizeof(keysymToAscii) / sizeof(keysymToAscii_t); i++) {
+ if (keysymToAscii[i].keysym == keysym) {
+ keysym = keysymToAscii[i].ascii;
+ break;
+ }
+ }
+
+ if ((keysym >= 32 && keysym <= 126) ||
+ (keysym >= 160 && keysym <= 255))
+ {
+ // ordinary Latin-1 character
+
+ if (deadKeyAware) {
+ // Detect dead chars and generate the dead char followed by space so
+ // that we'll end up with the original char.
+ for (int i = 0; i < deadChars.size(); i++) {
+ if (keysym == deadChars[i]) {
+ SHORT dc = VkKeyScan(keysym);
+ if (dc != -1) {
+ if (down) {
+ vlog.info("latin-1 dead key: 0x%x vkCode 0x%x mod 0x%x "
+ "followed by space", keysym, LOBYTE(dc), HIBYTE(dc));
+ doKeyEventWithModifiers(LOBYTE(dc), HIBYTE(dc), true);
+ doKeyEventWithModifiers(LOBYTE(dc), HIBYTE(dc), false);
+ doKeyEventWithModifiers(VK_SPACE, 0, true);
+ doKeyEventWithModifiers(VK_SPACE, 0, false);
+ }
+ return;
+ }
+ }
+ }
+ }
+
+ SHORT s = VkKeyScan(keysym);
+ if (s == -1) {
+ if (down) {
+ // not a single keypress - try synthesizing dead chars.
+ for (int j = 0;
+ j < sizeof(latin1ToDeadChars) / sizeof(latin1ToDeadChars_t);
+ j++) {
+ if (keysym == latin1ToDeadChars[j].latin1Char) {
+ for (int i = 0; i < deadChars.size(); i++) {
+ if (deadChars[i] == latin1ToDeadChars[j].deadChar) {
+ SHORT dc = VkKeyScan(latin1ToDeadChars[j].deadChar);
+ SHORT bc = VkKeyScan(latin1ToDeadChars[j].baseChar);
+ if (dc != -1 && bc != -1) {
+ vlog.info("latin-1 key: 0x%x dead key vkCode 0x%x mod 0x%x "
+ "followed by vkCode 0x%x mod 0x%x",
+ keysym, LOBYTE(dc), HIBYTE(dc),
+ LOBYTE(bc), HIBYTE(bc));
+ doKeyEventWithModifiers(LOBYTE(dc), HIBYTE(dc), true);
+ doKeyEventWithModifiers(LOBYTE(dc), HIBYTE(dc), false);
+ doKeyEventWithModifiers(LOBYTE(bc), HIBYTE(bc), true);
+ doKeyEventWithModifiers(LOBYTE(bc), HIBYTE(bc), false);
+ return;
+ }
+ break;
+ }
+ }
+ break;
+ }
+ }
+ vlog.info("ignoring unrecognised Latin-1 keysym 0x%x",keysym);
+ }
+ return;
+ }
+
+ BYTE vkCode = LOBYTE(s);
+ BYTE modifierState = HIBYTE(s);
+ vlog.debug("latin-1 key: 0x%x vkCode 0x%x mod 0x%x down %d",
+ keysym, vkCode, modifierState, down);
+ doKeyEventWithModifiers(vkCode, modifierState, down);
+
+ } else {
+
+ // see if it's a recognised keyboard key, otherwise ignore it
+
+ if (vkMap.find(keysym) == vkMap.end()) {
+ vlog.info("ignoring unknown keysym 0x%x",keysym);
+ return;
+ }
+ BYTE vkCode = vkMap[keysym];
+ DWORD flags = 0;
+ if (extendedMap[keysym]) flags |= KEYEVENTF_EXTENDEDKEY;
+ if (!down) flags |= KEYEVENTF_KEYUP;
+
+ vlog.debug("keyboard key: keysym 0x%x vkCode 0x%x ext %d down %d",
+ keysym, vkCode, extendedMap[keysym], down);
+ if (down && (vkCode == VK_DELETE) &&
+ ((GetAsyncKeyState(VK_CONTROL) & 0x8000) != 0) &&
+ ((GetAsyncKeyState(VK_MENU) & 0x8000) != 0))
+ {
+ rfb::win32::emulateCtrlAltDel();
+ return;
+ }
+
+ doKeyboardEvent(vkCode, flags);
+ }
+}
diff --git a/win/rfb_win32/SInput.h b/win/rfb_win32/SInput.h
new file mode 100644
index 0000000..2a0b3e6
--- /dev/null
+++ b/win/rfb_win32/SInput.h
@@ -0,0 +1,68 @@
+/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
+ *
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+ * USA.
+ */
+
+// -=- Input.h
+//
+// A number of routines that accept VNC-style input event data and perform
+// the appropriate actions under Win32
+
+#ifndef __RFB_WIN32_INPUT_H__
+#define __RFB_WIN32_INPUT_H__
+
+#include <rfb/Rect.h>
+#include <rfb/Configuration.h>
+#include <rdr/types.h>
+#include <map>
+#include <vector>
+
+namespace rfb {
+
+ namespace win32 {
+
+ // -=- Pointer event handling
+
+ class SPointer {
+ public:
+ SPointer();
+ // - Create a pointer event at a the given coordinates, with the
+ // specified button state. The event must be specified using
+ // Screen coordinates.
+ void pointerEvent(const Point& pos, int buttonmask);
+ protected:
+ Point last_position;
+ rdr::U8 last_buttonmask;
+ };
+
+ // -=- Keyboard event handling
+
+ class SKeyboard {
+ public:
+ SKeyboard();
+ void keyEvent(rdr::U32 key, bool down);
+ static BoolParameter deadKeyAware;
+ private:
+ std::map<rdr::U32,rdr::U8> vkMap;
+ std::map<rdr::U32,bool> extendedMap;
+ std::vector<rdr::U8> deadChars;
+ };
+
+ }; // win32
+
+}; // rfb
+
+#endif // __RFB_WIN32_INPUT_H__
diff --git a/win/rfb_win32/ScaledDIBSectionBuffer.cxx b/win/rfb_win32/ScaledDIBSectionBuffer.cxx
new file mode 100644
index 0000000..e6c15b8
--- /dev/null
+++ b/win/rfb_win32/ScaledDIBSectionBuffer.cxx
@@ -0,0 +1,124 @@
+/* Copyright (C) 2006 TightVNC Team. All Rights Reserved.
+ *
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+ * USA.
+ *
+ * TightVNC distribution homepage on the Web: http://www.tightvnc.com/
+ *
+ */
+
+// -=- ScaledDIBSectionBuffer.cxx
+
+#include <math.h>
+
+#include <rfb_win32/ScaledDIBSectionBuffer.h>
+
+using namespace rfb;
+using namespace win32;
+
+ScaledDIBSectionBuffer::ScaledDIBSectionBuffer(HWND window)
+ : src_buffer(0), scaling(false), DIBSectionBuffer(window) {
+ scaled_data = data;
+}
+
+ScaledDIBSectionBuffer::~ScaledDIBSectionBuffer() {
+ if (src_buffer) delete src_buffer;
+}
+
+void ScaledDIBSectionBuffer::setScale(int scale_) {
+ if (scale_ == getScale()) return;
+
+ if (src_buffer) {
+ delete src_buffer;
+ src_buffer = 0;
+ }
+ if (scale_ != 100) {
+ scaling = true;
+ src_buffer = new ManagedPixelBuffer(format, src_width, src_height);
+ src_data = &(src_buffer->data);
+ } else {
+ scaling = false;
+ }
+ ScaledPixelBuffer::setScale(scale_);
+}
+
+void ScaledDIBSectionBuffer::setPF(const PixelFormat &pf) {
+ if (scaling) src_buffer->setPF(pf);
+ DIBSectionBuffer::setPF(pf);
+ scaled_data = data;
+}
+
+void ScaledDIBSectionBuffer::setSize(int src_width_, int src_height_) {
+ src_width = src_width_;
+ src_height = src_height_;
+ if (scaling) {
+ src_buffer->setSize(src_width, src_height);
+ }
+ calculateScaledBufferSize();
+ recreateScaledBuffer();
+ scaled_data = data;
+}
+
+void ScaledDIBSectionBuffer::recreateScaledBuffer() {
+ width_ = scaled_width;
+ height_ = scaled_height;
+ DIBSectionBuffer::recreateBuffer();
+ scaled_data = data;
+}
+
+void ScaledDIBSectionBuffer::fillRect(const Rect &dest, Pixel pix) {
+ if (scaling) {
+ src_buffer->fillRect(dest, pix);
+ scaleRect(dest);
+ } else {
+ DIBSectionBuffer::fillRect(dest, pix);
+ }
+}
+
+void ScaledDIBSectionBuffer::imageRect(const Rect &dest, const void* pixels, int stride) {
+ if (scaling) {
+ src_buffer->imageRect(dest, pixels, stride);
+ scaleRect(dest);
+ } else {
+ DIBSectionBuffer::imageRect(dest, pixels, stride);
+ }
+}
+
+void ScaledDIBSectionBuffer::copyRect(const Rect &dest, const Point &move_by_delta) {
+ if (scaling) {
+ src_buffer->copyRect(dest, move_by_delta);
+ scaleRect(dest);
+ } else {
+ DIBSectionBuffer::copyRect(dest, move_by_delta);
+ }
+}
+
+void ScaledDIBSectionBuffer::maskRect(const Rect& r, const void* pixels, const void* mask_) {
+ if (scaling) {
+ src_buffer->maskRect(r, pixels, mask_);
+ scaleRect(r);
+ } else {
+ DIBSectionBuffer::maskRect(r, pixels, mask_);
+ }
+}
+
+void ScaledDIBSectionBuffer::maskRect(const Rect& r, Pixel pixel, const void* mask_) {
+ if (scaling) {
+ src_buffer->maskRect(r, pixel, mask_);
+ scaleRect(r);
+ } else {
+ DIBSectionBuffer::maskRect(r, pixel, mask_);
+ }
+}
diff --git a/win/rfb_win32/ScaledDIBSectionBuffer.h b/win/rfb_win32/ScaledDIBSectionBuffer.h
new file mode 100644
index 0000000..3cc267b
--- /dev/null
+++ b/win/rfb_win32/ScaledDIBSectionBuffer.h
@@ -0,0 +1,78 @@
+/* Copyright (C) 2006 TightVNC Team. All Rights Reserved.
+ *
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+ * USA.
+ *
+ * TightVNC distribution homepage on the Web: http://www.tightvnc.com/
+ *
+ */
+
+// -=- ScaledDIBSectionBuffer.h
+
+#ifndef __RFB_WIN32_SCALED_DIB_SECTION_BUFFER_H__
+#define __RFB_WIN32_SCALED_DIB_SECTION_BUFFER_H__
+
+#include <rfb/ScaledPixelBuffer.h>
+
+#include <rfb_win32/DIBSectionBuffer.h>
+
+namespace rfb {
+
+ namespace win32 {
+
+ //
+ // -=- ScaledDIBSectionBuffer
+ //
+
+ class ScaledDIBSectionBuffer : public ScaledPixelBuffer, public DIBSectionBuffer {
+ public:
+ ScaledDIBSectionBuffer(HWND window);
+ virtual ~ScaledDIBSectionBuffer();
+
+ int width() const { return scaled_width; }
+ int height() const { return scaled_height; }
+ bool isScaling() const { return scaling; }
+
+ virtual void setPF(const PixelFormat &pf);
+ virtual void setSize(int w, int h);
+ virtual void setScale(int scale);
+
+ Rect getRect() const { return ScaledPixelBuffer::getRect(); }
+ Rect getRect(const Point& pos) const { return ScaledPixelBuffer::getRect(pos); }
+
+ // -=- Overrides basic rendering operations of
+ // FullFramePixelBuffer class
+
+ virtual void fillRect(const Rect &dest, Pixel pix);
+ virtual void imageRect(const Rect &dest, const void* pixels, int stride=0);
+ virtual void copyRect(const Rect &dest, const Point &move_by_delta);
+ virtual void maskRect(const Rect& r, const void* pixels, const void* mask_);
+ virtual void maskRect(const Rect& r, Pixel pixel, const void* mask_);
+
+ protected:
+ virtual void recreateScaledBuffer();
+ virtual void recreateBuffer() {
+ recreateScaledBuffer();
+ };
+
+ ManagedPixelBuffer *src_buffer;
+ bool scaling;
+ };
+
+ };
+
+};
+
+#endif // __RFB_WIN32_SCALED_DIB_SECTION_BUFFER_H__
diff --git a/win/rfb_win32/Security.cxx b/win/rfb_win32/Security.cxx
new file mode 100644
index 0000000..985f00c
--- /dev/null
+++ b/win/rfb_win32/Security.cxx
@@ -0,0 +1,192 @@
+/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
+ *
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+ * USA.
+ */
+
+// -=- Security.cxx
+
+#include <rfb_win32/Security.h>
+#include <rfb_win32/DynamicFn.h>
+#include <rfb/LogWriter.h>
+
+#include <lmcons.h>
+#include <Accctrl.h>
+#include <list>
+
+using namespace rfb;
+using namespace rfb::win32;
+
+static LogWriter vlog("SecurityWin32");
+
+
+Trustee::Trustee(const TCHAR* name,
+ TRUSTEE_FORM form,
+ TRUSTEE_TYPE type) {
+ pMultipleTrustee = 0;
+ MultipleTrusteeOperation = NO_MULTIPLE_TRUSTEE;
+ TrusteeForm = form;
+ TrusteeType = type;
+ ptstrName = (TCHAR*)name;
+}
+
+
+ExplicitAccess::ExplicitAccess(const TCHAR* name,
+ TRUSTEE_FORM type,
+ DWORD perms,
+ ACCESS_MODE mode,
+ DWORD inherit) {
+ Trustee = rfb::win32::Trustee(name, type);
+ grfAccessPermissions = perms;
+ grfAccessMode = mode;
+ grfInheritance = inherit;
+}
+
+
+AccessEntries::AccessEntries() : entries(0), entry_count(0) {}
+
+AccessEntries::~AccessEntries() {
+ delete [] entries;
+}
+
+void AccessEntries::allocMinEntries(int count) {
+ if (count > entry_count) {
+ EXPLICIT_ACCESS* new_entries = new EXPLICIT_ACCESS[entry_count+1];
+ if (entries) {
+ memcpy(new_entries, entries, sizeof(EXPLICIT_ACCESS) * entry_count);
+ delete entries;
+ }
+ entries = new_entries;
+ }
+}
+
+void AccessEntries::addEntry(const TCHAR* trusteeName,
+ DWORD permissions,
+ ACCESS_MODE mode) {
+ allocMinEntries(entry_count+1);
+ ZeroMemory(&entries[entry_count], sizeof(EXPLICIT_ACCESS));
+ entries[entry_count] = ExplicitAccess(trusteeName, TRUSTEE_IS_NAME, permissions, mode);
+ entry_count++;
+}
+
+void AccessEntries::addEntry(const PSID sid,
+ DWORD permissions,
+ ACCESS_MODE mode) {
+ allocMinEntries(entry_count+1);
+ ZeroMemory(&entries[entry_count], sizeof(EXPLICIT_ACCESS));
+ entries[entry_count] = ExplicitAccess((TCHAR*)sid, TRUSTEE_IS_SID, permissions, mode);
+ entry_count++;
+}
+
+
+PSID Sid::copySID(const PSID sid) {
+ if (!IsValidSid(sid))
+ throw rdr::Exception("invalid SID in copyPSID");
+ PSID buf = (PSID)new rdr::U8[GetLengthSid(sid)];
+ if (!CopySid(GetLengthSid(sid), buf, sid))
+ throw rdr::SystemException("CopySid failed", GetLastError());
+ return buf;
+}
+
+void Sid::setSID(const PSID sid) {
+ delete [] buf;
+ buf = (rdr::U8*)copySID(sid);
+}
+
+void Sid::getUserNameAndDomain(TCHAR** name, TCHAR** domain) {
+ DWORD nameLen = 0;
+ DWORD domainLen = 0;
+ SID_NAME_USE use;
+ LookupAccountSid(0, (PSID)buf, 0, &nameLen, 0, &domainLen, &use);
+ if (GetLastError() != ERROR_INSUFFICIENT_BUFFER)
+ throw rdr::SystemException("Unable to determine SID name lengths", GetLastError());
+ vlog.info("nameLen=%d, domainLen=%d, use=%d", nameLen, domainLen, use);
+ *name = new TCHAR[nameLen];
+ *domain = new TCHAR[domainLen];
+ if (!LookupAccountSid(0, (PSID)buf, *name, &nameLen, *domain, &domainLen, &use))
+ throw rdr::SystemException("Unable to lookup account SID", GetLastError());
+}
+
+
+Sid::Administrators::Administrators() {
+ PSID sid = 0;
+ SID_IDENTIFIER_AUTHORITY ntAuth = SECURITY_NT_AUTHORITY;
+ if (!AllocateAndInitializeSid(&ntAuth, 2,
+ SECURITY_BUILTIN_DOMAIN_RID,
+ DOMAIN_ALIAS_RID_ADMINS,
+ 0, 0, 0, 0, 0, 0, &sid))
+ throw rdr::SystemException("Sid::Administrators", GetLastError());
+ setSID(sid);
+ FreeSid(sid);
+}
+
+Sid::SYSTEM::SYSTEM() {
+ PSID sid = 0;
+ SID_IDENTIFIER_AUTHORITY ntAuth = SECURITY_NT_AUTHORITY;
+ if (!AllocateAndInitializeSid(&ntAuth, 1,
+ SECURITY_LOCAL_SYSTEM_RID,
+ 0, 0, 0, 0, 0, 0, 0, &sid))
+ throw rdr::SystemException("Sid::SYSTEM", GetLastError());
+ setSID(sid);
+ FreeSid(sid);
+}
+
+Sid::FromToken::FromToken(HANDLE h) {
+ DWORD required = 0;
+ GetTokenInformation(h, TokenUser, 0, 0, &required);
+ rdr::U8Array tmp(required);
+ if (!GetTokenInformation(h, TokenUser, tmp.buf, required, &required))
+ throw rdr::SystemException("GetTokenInformation", GetLastError());
+ TOKEN_USER* tokenUser = (TOKEN_USER*)tmp.buf;
+ setSID(tokenUser->User.Sid);
+}
+
+
+PACL rfb::win32::CreateACL(const AccessEntries& ae, PACL existing_acl) {
+ typedef DWORD (WINAPI *_SetEntriesInAcl_proto) (ULONG, PEXPLICIT_ACCESS, PACL, PACL*);
+#ifdef UNICODE
+ const char* fnName = "SetEntriesInAclW";
+#else
+ const char* fnName = "SetEntriesInAclA";
+#endif
+ DynamicFn<_SetEntriesInAcl_proto> _SetEntriesInAcl(_T("advapi32.dll"), fnName);
+ if (!_SetEntriesInAcl.isValid())
+ throw rdr::SystemException("CreateACL failed; no SetEntriesInAcl", ERROR_CALL_NOT_IMPLEMENTED);
+ PACL new_dacl;
+ DWORD result;
+ if ((result = (*_SetEntriesInAcl)(ae.entry_count, ae.entries, existing_acl, &new_dacl)) != ERROR_SUCCESS)
+ throw rdr::SystemException("SetEntriesInAcl", result);
+ return new_dacl;
+}
+
+
+PSECURITY_DESCRIPTOR rfb::win32::CreateSdWithDacl(const PACL dacl) {
+ SECURITY_DESCRIPTOR absSD;
+ if (!InitializeSecurityDescriptor(&absSD, SECURITY_DESCRIPTOR_REVISION))
+ throw rdr::SystemException("InitializeSecurityDescriptor", GetLastError());
+ Sid::SYSTEM owner;
+ if (!SetSecurityDescriptorOwner(&absSD, owner, FALSE))
+ throw rdr::SystemException("SetSecurityDescriptorOwner", GetLastError());
+ Sid::Administrators group;
+ if (!SetSecurityDescriptorGroup(&absSD, group, FALSE))
+ throw rdr::SystemException("SetSecurityDescriptorGroupp", GetLastError());
+ if (!SetSecurityDescriptorDacl(&absSD, TRUE, dacl, FALSE))
+ throw rdr::SystemException("SetSecurityDescriptorDacl", GetLastError());
+ DWORD sdSize = GetSecurityDescriptorLength(&absSD);
+ SecurityDescriptorPtr sd(sdSize);
+ if (!MakeSelfRelativeSD(&absSD, sd, &sdSize))
+ throw rdr::SystemException("MakeSelfRelativeSD", GetLastError());
+ return sd.takeSD();
+}
diff --git a/win/rfb_win32/Security.h b/win/rfb_win32/Security.h
new file mode 100644
index 0000000..1e2e906
--- /dev/null
+++ b/win/rfb_win32/Security.h
@@ -0,0 +1,123 @@
+/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
+ *
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+ * USA.
+ */
+
+// Security.h
+
+// Wrapper classes for a few Windows NT security structures/functions
+// that are used by VNC
+
+#ifndef __RFB_WIN32_SECURITY_H__
+#define __RFB_WIN32_SECURITY_H__
+
+#include <rdr/types.h>
+#include <rfb_win32/LocalMem.h>
+#include <rfb_win32/TCharArray.h>
+#include <aclapi.h>
+
+namespace rfb {
+
+ namespace win32 {
+
+ struct Trustee : public TRUSTEE {
+ Trustee(const TCHAR* name,
+ TRUSTEE_FORM form=TRUSTEE_IS_NAME,
+ TRUSTEE_TYPE type=TRUSTEE_IS_UNKNOWN);
+ };
+
+ struct ExplicitAccess : public EXPLICIT_ACCESS {
+ ExplicitAccess(const TCHAR* name,
+ TRUSTEE_FORM type,
+ DWORD perms,
+ ACCESS_MODE mode,
+ DWORD inherit=0);
+ };
+
+ // Helper class for building access control lists
+ struct AccessEntries {
+ AccessEntries();
+ ~AccessEntries();
+ void allocMinEntries(int count);
+ void addEntry(const TCHAR* trusteeName,
+ DWORD permissions,
+ ACCESS_MODE mode);
+ void addEntry(const PSID sid,
+ DWORD permissions,
+ ACCESS_MODE mode);
+
+ EXPLICIT_ACCESS* entries;
+ int entry_count;
+ };
+
+ // Helper class for handling SIDs
+ struct Sid : rdr::U8Array {
+ Sid() {}
+ operator PSID() const {return (PSID)buf;}
+ PSID takePSID() {PSID r = (PSID)buf; buf = 0; return r;}
+
+ static PSID copySID(const PSID sid);
+
+ void setSID(const PSID sid);
+
+ void getUserNameAndDomain(TCHAR** name, TCHAR** domain);
+
+ struct Administrators;
+ struct SYSTEM;
+ struct FromToken;
+
+ private:
+ Sid(const Sid&);
+ Sid& operator=(const Sid&);
+ };
+
+ struct Sid::Administrators : public Sid {
+ Administrators();
+ };
+ struct Sid::SYSTEM : public Sid {
+ SYSTEM();
+ };
+ struct Sid::FromToken : public Sid {
+ FromToken(HANDLE h);
+ };
+
+ // Helper class for handling & freeing ACLs
+ struct AccessControlList : public LocalMem {
+ AccessControlList(int size) : LocalMem(size) {}
+ AccessControlList(PACL acl_=0) : LocalMem(acl_) {}
+ operator PACL() {return (PACL)ptr;}
+ };
+
+ // Create a new ACL based on supplied entries and, if supplied, existing ACL
+ PACL CreateACL(const AccessEntries& ae, PACL existing_acl=0);
+
+ // Helper class for memory-management of self-relative SecurityDescriptors
+ struct SecurityDescriptorPtr : LocalMem {
+ SecurityDescriptorPtr(int size) : LocalMem(size) {}
+ SecurityDescriptorPtr(PSECURITY_DESCRIPTOR sd_=0) : LocalMem(sd_) {}
+ PSECURITY_DESCRIPTOR takeSD() {return takePtr();}
+ };
+
+ // Create a new self-relative Security Descriptor, owned by SYSTEM/Administrators,
+ // with the supplied DACL and no SACL. The returned value can be assigned
+ // to a SecurityDescriptorPtr to be managed.
+ PSECURITY_DESCRIPTOR CreateSdWithDacl(const PACL dacl);
+
+ }
+
+}
+
+#endif
diff --git a/win/rfb_win32/Service.cxx b/win/rfb_win32/Service.cxx
new file mode 100644
index 0000000..2b11a22
--- /dev/null
+++ b/win/rfb_win32/Service.cxx
@@ -0,0 +1,645 @@
+/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
+ *
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+ * USA.
+ */
+
+// -=- Service.cxx
+
+#include <rfb_win32/Service.h>
+#include <rfb_win32/MsgWindow.h>
+#include <rfb_win32/DynamicFn.h>
+#include <rfb_win32/ModuleFileName.h>
+#include <rfb_win32/Registry.h>
+#include <rfb_win32/OSVersion.h>
+#include <rfb/Threading.h>
+#include <logmessages/messages.h>
+#include <rdr/Exception.h>
+#include <rfb/LogWriter.h>
+
+
+using namespace rdr;
+using namespace rfb;
+using namespace win32;
+
+static LogWriter vlog("Service");
+
+
+// - Internal service implementation functions
+
+Service* service = 0;
+
+VOID WINAPI serviceHandler(DWORD control) {
+ switch (control) {
+ case SERVICE_CONTROL_INTERROGATE:
+ vlog.info("cmd: report status");
+ service->setStatus();
+ return;
+ case SERVICE_CONTROL_PARAMCHANGE:
+ vlog.info("cmd: param change");
+ service->readParams();
+ return;
+ case SERVICE_CONTROL_SHUTDOWN:
+ vlog.info("cmd: OS shutdown");
+ service->osShuttingDown();
+ return;
+ case SERVICE_CONTROL_STOP:
+ vlog.info("cmd: stop");
+ service->setStatus(SERVICE_STOP_PENDING);
+ service->stop();
+ return;
+ };
+ vlog.debug("cmd: unknown %lu", control);
+}
+
+
+// -=- Message window derived class used under Win9x to implement stopService
+
+#define WM_SMSG_SERVICE_STOP WM_USER
+
+class ServiceMsgWindow : public MsgWindow {
+public:
+ ServiceMsgWindow(const TCHAR* name) : MsgWindow(name) {}
+ LRESULT processMessage(UINT msg, WPARAM wParam, LPARAM lParam) {
+ switch (msg) {
+ case WM_SMSG_SERVICE_STOP:
+ service->stop();
+ return TRUE;
+ }
+ return MsgWindow::processMessage(msg, wParam, lParam);
+ }
+
+ static const TCHAR* baseName;
+};
+
+const TCHAR* ServiceMsgWindow::baseName = _T("ServiceWindow:");
+
+
+// -=- Service main procedure, used under WinNT/2K/XP by the SCM
+
+VOID WINAPI serviceProc(DWORD dwArgc, LPTSTR* lpszArgv) {
+ vlog.debug("entering %s serviceProc", service->getName());
+ vlog.info("registering handler...");
+ service->status_handle = RegisterServiceCtrlHandler(service->getName(), serviceHandler);
+ if (!service->status_handle) {
+ DWORD err = GetLastError();
+ vlog.error("failed to register handler: %lu", err);
+ ExitProcess(err);
+ }
+ vlog.debug("registered handler (%lx)", service->status_handle);
+ service->setStatus(SERVICE_START_PENDING);
+ vlog.debug("entering %s serviceMain", service->getName());
+ service->status.dwWin32ExitCode = service->serviceMain(dwArgc, lpszArgv);
+ vlog.debug("leaving %s serviceMain", service->getName());
+ service->setStatus(SERVICE_STOPPED);
+}
+
+
+// -=- Service
+
+Service::Service(const TCHAR* name_) : name(name_) {
+ vlog.debug("Service");
+ status_handle = 0;
+ status.dwControlsAccepted = SERVICE_CONTROL_INTERROGATE | SERVICE_ACCEPT_SHUTDOWN | SERVICE_ACCEPT_STOP;
+ status.dwServiceType = SERVICE_WIN32_OWN_PROCESS | SERVICE_INTERACTIVE_PROCESS;
+ status.dwWin32ExitCode = NO_ERROR;
+ status.dwServiceSpecificExitCode = 0;
+ status.dwCheckPoint = 0;
+ status.dwWaitHint = 30000;
+ status.dwCurrentState = SERVICE_STOPPED;
+}
+
+void
+Service::start() {
+ if (osVersion.isPlatformNT) {
+ SERVICE_TABLE_ENTRY entry[2];
+ entry[0].lpServiceName = (TCHAR*)name;
+ entry[0].lpServiceProc = serviceProc;
+ entry[1].lpServiceName = NULL;
+ entry[1].lpServiceProc = NULL;
+ vlog.debug("entering dispatcher");
+ if (!SetProcessShutdownParameters(0x100, 0))
+ vlog.error("unable to set shutdown parameters: %d", GetLastError());
+ service = this;
+ if (!StartServiceCtrlDispatcher(entry))
+ throw SystemException("unable to start service", GetLastError());
+ } else {
+
+ // - Create the service window, so the service can be stopped
+ TCharArray wndName(_tcslen(getName()) + _tcslen(ServiceMsgWindow::baseName) + 1);
+ _tcscpy(wndName.buf, ServiceMsgWindow::baseName);
+ _tcscat(wndName.buf, getName());
+ ServiceMsgWindow service_window(wndName.buf);
+
+ // - Locate the RegisterServiceProcess function
+ typedef DWORD (WINAPI * _RegisterServiceProcess_proto)(DWORD, DWORD);
+ DynamicFn<_RegisterServiceProcess_proto> _RegisterServiceProcess(_T("kernel32.dll"), "RegisterServiceProcess");
+ if (!_RegisterServiceProcess.isValid())
+ throw Exception("unable to find RegisterServiceProcess");
+
+ // - Run the service
+ (*_RegisterServiceProcess)(NULL, 1);
+ service = this;
+ serviceMain(0, 0);
+ (*_RegisterServiceProcess)(NULL, 0);
+ }
+}
+
+void
+Service::setStatus() {
+ setStatus(status.dwCurrentState);
+}
+
+void
+Service::setStatus(DWORD state) {
+ if (!osVersion.isPlatformNT)
+ return;
+ if (status_handle == 0) {
+ vlog.debug("warning - cannot setStatus");
+ return;
+ }
+ status.dwCurrentState = state;
+ status.dwCheckPoint++;
+ if (!SetServiceStatus(status_handle, &status)) {
+ status.dwCurrentState = SERVICE_STOPPED;
+ status.dwWin32ExitCode = GetLastError();
+ vlog.error("unable to set service status:%u", status.dwWin32ExitCode);
+ }
+ vlog.debug("set status to %u(%u)", state, status.dwCheckPoint);
+}
+
+Service::~Service() {
+ vlog.debug("~Service");
+ service = 0;
+}
+
+
+// Find out whether this process is running as the WinVNC service
+bool thisIsService() {
+ return service && (service->status.dwCurrentState != SERVICE_STOPPED);
+}
+
+
+// -=- Desktop handling code
+
+// Switch the current thread to the specified desktop
+static bool
+switchToDesktop(HDESK desktop) {
+ HDESK old_desktop = GetThreadDesktop(GetCurrentThreadId());
+ if (!SetThreadDesktop(desktop)) {
+ vlog.debug("switchToDesktop failed:%u", GetLastError());
+ return false;
+ }
+ if (!CloseDesktop(old_desktop))
+ vlog.debug("unable to close old desktop:%u", GetLastError());
+ return true;
+}
+
+// Determine whether the thread's current desktop is the input one
+static bool
+inputDesktopSelected() {
+ HDESK current = GetThreadDesktop(GetCurrentThreadId());
+ HDESK input = OpenInputDesktop(0, FALSE,
+ DESKTOP_CREATEMENU | DESKTOP_CREATEWINDOW |
+ DESKTOP_ENUMERATE | DESKTOP_HOOKCONTROL |
+ DESKTOP_WRITEOBJECTS | DESKTOP_READOBJECTS |
+ DESKTOP_SWITCHDESKTOP | GENERIC_WRITE);
+ if (!input) {
+ vlog.debug("unable to OpenInputDesktop(1):%u", GetLastError());
+ return false;
+ }
+
+ DWORD size;
+ char currentname[256];
+ char inputname[256];
+
+ if (!GetUserObjectInformation(current, UOI_NAME, currentname, 256, &size)) {
+ vlog.debug("unable to GetUserObjectInformation(1):%u", GetLastError());
+ CloseDesktop(input);
+ return false;
+ }
+ if (!GetUserObjectInformation(input, UOI_NAME, inputname, 256, &size)) {
+ vlog.debug("unable to GetUserObjectInformation(2):%u", GetLastError());
+ CloseDesktop(input);
+ return false;
+ }
+ if (!CloseDesktop(input))
+ vlog.debug("unable to close input desktop:%u", GetLastError());
+
+ // *** vlog.debug("current=%s, input=%s", currentname, inputname);
+ bool result = strcmp(currentname, inputname) == 0;
+ return result;
+}
+
+// Switch the current thread into the input desktop
+static bool
+selectInputDesktop() {
+ // - Open the input desktop
+ HDESK desktop = OpenInputDesktop(0, FALSE,
+ DESKTOP_CREATEMENU | DESKTOP_CREATEWINDOW |
+ DESKTOP_ENUMERATE | DESKTOP_HOOKCONTROL |
+ DESKTOP_WRITEOBJECTS | DESKTOP_READOBJECTS |
+ DESKTOP_SWITCHDESKTOP | GENERIC_WRITE);
+ if (!desktop) {
+ vlog.debug("unable to OpenInputDesktop(2):%u", GetLastError());
+ return false;
+ }
+
+ // - Switch into it
+ if (!switchToDesktop(desktop)) {
+ CloseDesktop(desktop);
+ return false;
+ }
+
+ // ***
+ DWORD size = 256;
+ char currentname[256];
+ if (GetUserObjectInformation(desktop, UOI_NAME, currentname, 256, &size)) {
+ vlog.debug("switched to %s", currentname);
+ }
+ // ***
+
+ vlog.debug("switched to input desktop");
+
+ return true;
+}
+
+
+// -=- Access points to desktop-switching routines
+
+bool
+rfb::win32::desktopChangeRequired() {
+ if (!osVersion.isPlatformNT)
+ return false;
+
+ return !inputDesktopSelected();
+}
+
+bool
+rfb::win32::changeDesktop() {
+ if (!osVersion.isPlatformNT)
+ return true;
+ if (osVersion.cannotSwitchDesktop)
+ return false;
+
+ return selectInputDesktop();
+}
+
+
+// -=- Ctrl-Alt-Del emulation
+
+class CADThread : public Thread {
+public:
+ CADThread() : Thread("CtrlAltDel Emulator"), result(false) {}
+ virtual void run() {
+ HDESK old_desktop = GetThreadDesktop(GetCurrentThreadId());
+
+ if (switchToDesktop(OpenDesktop(_T("Winlogon"), 0, FALSE, DESKTOP_CREATEMENU | DESKTOP_CREATEWINDOW |
+ DESKTOP_ENUMERATE | DESKTOP_HOOKCONTROL |
+ DESKTOP_WRITEOBJECTS | DESKTOP_READOBJECTS |
+ DESKTOP_SWITCHDESKTOP | GENERIC_WRITE))) {
+ PostMessage(HWND_BROADCAST, WM_HOTKEY, 0, MAKELONG(MOD_ALT | MOD_CONTROL, VK_DELETE));
+ switchToDesktop(old_desktop);
+ result = true;
+ }
+ }
+ bool result;
+};
+
+bool
+rfb::win32::emulateCtrlAltDel() {
+ if (!osVersion.isPlatformNT)
+ return false;
+
+ CADThread* cad_thread = new CADThread();
+ vlog.debug("emulate Ctrl-Alt-Del");
+ if (cad_thread) {
+ cad_thread->start();
+ cad_thread->join();
+ bool result = cad_thread->result;
+ delete cad_thread;
+ return result;
+ }
+ return false;
+}
+
+
+// -=- Application Event Log target Logger class
+
+class Logger_EventLog : public Logger {
+public:
+ Logger_EventLog(const TCHAR* srcname) : Logger("EventLog") {
+ eventlog = RegisterEventSource(NULL, srcname);
+ if (!eventlog)
+ printf("Unable to open event log:%ld\n", GetLastError());
+ }
+ ~Logger_EventLog() {
+ if (eventlog)
+ DeregisterEventSource(eventlog);
+ }
+
+ virtual void write(int level, const char *logname, const char *message) {
+ if (!eventlog) return;
+ TStr log(logname), msg(message);
+ const TCHAR* strings[] = {log, msg};
+ WORD type = EVENTLOG_INFORMATION_TYPE;
+ if (level == 0) type = EVENTLOG_ERROR_TYPE;
+ if (!ReportEvent(eventlog, type, 0, VNC4LogMessage, NULL, 2, 0, strings, NULL)) {
+ // *** It's not at all clear what is the correct behaviour if this fails...
+ printf("ReportEvent failed:%ld\n", GetLastError());
+ }
+ }
+
+protected:
+ HANDLE eventlog;
+};
+
+static Logger_EventLog* logger = 0;
+
+bool rfb::win32::initEventLogLogger(const TCHAR* srcname) {
+ if (logger)
+ return false;
+ if (osVersion.isPlatformNT) {
+ logger = new Logger_EventLog(srcname);
+ logger->registerLogger();
+ return true;
+ } else {
+ return false;
+ }
+}
+
+
+// -=- Registering and unregistering the service
+
+bool rfb::win32::registerService(const TCHAR* name, const TCHAR* desc,
+ int argc, const char* argv[]) {
+
+ // - Initialise the default service parameters
+ const TCHAR* defaultcmdline;
+ if (osVersion.isPlatformNT)
+ defaultcmdline = _T("-service");
+ else
+ defaultcmdline = _T("-noconsole -service");
+
+ // - Get the full pathname of our executable
+ ModuleFileName buffer;
+
+ // - Calculate the command-line length
+ int cmdline_len = _tcslen(buffer.buf) + 4;
+ int i;
+ for (i=0; i<argc; i++) {
+ cmdline_len += strlen(argv[i]) + 3;
+ }
+
+ // - Add the supplied extra parameters to the command line
+ TCharArray cmdline(cmdline_len+_tcslen(defaultcmdline));
+ _stprintf(cmdline.buf, _T("\"%s\" %s"), buffer.buf, defaultcmdline);
+ for (i=0; i<argc; i++) {
+ _tcscat(cmdline.buf, _T(" \""));
+ _tcscat(cmdline.buf, TStr(argv[i]));
+ _tcscat(cmdline.buf, _T("\""));
+ }
+
+ // - Register the service
+
+ if (osVersion.isPlatformNT) {
+
+ // - Open the SCM
+ ServiceHandle scm = OpenSCManager(NULL, NULL, SC_MANAGER_CREATE_SERVICE);
+ if (!scm)
+ throw rdr::SystemException("unable to open Service Control Manager", GetLastError());
+
+
+ ServiceHandle service = CreateService(scm,
+ name, desc, SC_MANAGER_ALL_ACCESS,
+ SERVICE_WIN32_OWN_PROCESS | SERVICE_INTERACTIVE_PROCESS,
+ SERVICE_AUTO_START, SERVICE_ERROR_IGNORE,
+ cmdline.buf, NULL, NULL, NULL, NULL, NULL);
+ if (!service)
+ throw rdr::SystemException("unable to create service", GetLastError());
+
+ // - Register the event log source
+ RegKey hk, hk2;
+
+ hk2.createKey(HKEY_LOCAL_MACHINE, _T("SYSTEM\\CurrentControlSet\\Services\\EventLog\\Application"));
+ hk.createKey(hk2, name);
+
+ for (i=_tcslen(buffer.buf); i>0; i--) {
+ if (buffer.buf[i] == _T('\\')) {
+ buffer.buf[i+1] = 0;
+ break;
+ }
+ }
+
+ const TCHAR* dllFilename = _T("logmessages.dll");
+ TCharArray dllPath(_tcslen(buffer.buf) + _tcslen(dllFilename) + 1);
+ _tcscpy(dllPath.buf, buffer.buf);
+ _tcscat(dllPath.buf, dllFilename);
+
+ hk.setExpandString(_T("EventMessageFile"), dllPath.buf);
+ hk.setInt(_T("TypesSupported"), EVENTLOG_ERROR_TYPE | EVENTLOG_INFORMATION_TYPE);
+
+ } else {
+
+ RegKey services;
+ services.createKey(HKEY_LOCAL_MACHINE, _T("Software\\Microsoft\\Windows\\CurrentVersion\\RunServices"));
+ services.setString(name, cmdline.buf);
+
+ }
+
+ Sleep(500);
+
+ return true;
+}
+
+bool rfb::win32::unregisterService(const TCHAR* name) {
+ if (osVersion.isPlatformNT) {
+
+ // - Open the SCM
+ ServiceHandle scm = OpenSCManager(NULL, NULL, SC_MANAGER_CREATE_SERVICE);
+ if (!scm)
+ throw rdr::SystemException("unable to open Service Control Manager", GetLastError());
+
+ // - Create the service
+ ServiceHandle service = OpenService(scm, name, SC_MANAGER_ALL_ACCESS);
+ if (!service)
+ throw rdr::SystemException("unable to locate the service", GetLastError());
+ if (!DeleteService(service))
+ throw rdr::SystemException("unable to remove the service", GetLastError());
+
+ // - Register the event log source
+ RegKey hk;
+ hk.openKey(HKEY_LOCAL_MACHINE, _T("SYSTEM\\CurrentControlSet\\Services\\EventLog\\Application"));
+ hk.deleteKey(name);
+
+ } else {
+
+ RegKey services;
+ services.openKey(HKEY_LOCAL_MACHINE, _T("Software\\Microsoft\\Windows\\CurrentVersion\\RunServices"));
+ services.deleteValue(name);
+
+ }
+
+ Sleep(500);
+
+ return true;
+}
+
+
+// -=- Starting and stopping the service
+
+HWND findServiceWindow(const TCHAR* name) {
+ TCharArray wndName(_tcslen(ServiceMsgWindow::baseName)+_tcslen(name)+1);
+ _tcscpy(wndName.buf, ServiceMsgWindow::baseName);
+ _tcscat(wndName.buf, name);
+ vlog.debug("searching for %s window", CStr(wndName.buf));
+ return FindWindow(0, wndName.buf);
+}
+
+bool rfb::win32::startService(const TCHAR* name) {
+
+ if (osVersion.isPlatformNT) {
+ // - Open the SCM
+ ServiceHandle scm = OpenSCManager(NULL, NULL, SC_MANAGER_CONNECT);
+ if (!scm)
+ throw rdr::SystemException("unable to open Service Control Manager", GetLastError());
+
+ // - Locate the service
+ ServiceHandle service = OpenService(scm, name, SERVICE_START);
+ if (!service)
+ throw rdr::SystemException("unable to open the service", GetLastError());
+
+ // - Start the service
+ if (!StartService(service, 0, NULL))
+ throw rdr::SystemException("unable to start the service", GetLastError());
+ } else {
+ // - Check there is no service window
+ if (findServiceWindow(name))
+ throw rdr::Exception("the service is already running");
+
+ // - Find the RunServices registry key
+ RegKey services;
+ services.openKey(HKEY_LOCAL_MACHINE, _T("Software\\Microsoft\\Windows\\CurrentVersion\\RunServices"));
+
+ // - Read the command-line from it
+ TCharArray cmdLine = services.getString(name);
+
+ // - Start the service
+ PROCESS_INFORMATION proc_info;
+ STARTUPINFO startup_info;
+ ZeroMemory(&startup_info, sizeof(startup_info));
+ startup_info.cb = sizeof(startup_info);
+ if (!CreateProcess(0, cmdLine.buf, 0, 0, FALSE, CREATE_NEW_CONSOLE, 0, 0, &startup_info, &proc_info)) {
+ throw SystemException("unable to start service", GetLastError());
+ }
+ }
+
+ Sleep(500);
+
+ return true;
+}
+
+bool rfb::win32::stopService(const TCHAR* name) {
+ if (osVersion.isPlatformNT) {
+ // - Open the SCM
+ ServiceHandle scm = OpenSCManager(NULL, NULL, SC_MANAGER_CONNECT);
+ if (!scm)
+ throw rdr::SystemException("unable to open Service Control Manager", GetLastError());
+
+ // - Locate the service
+ ServiceHandle service = OpenService(scm, name, SERVICE_STOP);
+ if (!service)
+ throw rdr::SystemException("unable to open the service", GetLastError());
+
+ // - Start the service
+ SERVICE_STATUS status;
+ if (!ControlService(service, SERVICE_CONTROL_STOP, &status))
+ throw rdr::SystemException("unable to stop the service", GetLastError());
+
+ } else {
+ // - Find the service window
+ HWND service_window = findServiceWindow(name);
+ if (!service_window)
+ throw Exception("unable to locate running service");
+
+ // Tell it to quit
+ vlog.debug("sending service stop request");
+ if (!SendMessage(service_window, WM_SMSG_SERVICE_STOP, 0, 0))
+ throw Exception("unable to stop service");
+
+ // Check it's quitting...
+ DWORD process_id = 0;
+ HANDLE process = 0;
+ if (!GetWindowThreadProcessId(service_window, &process_id))
+ throw SystemException("unable to verify service has quit", GetLastError());
+ process = OpenProcess(SYNCHRONIZE | PROCESS_TERMINATE, FALSE, process_id);
+ if (!process)
+ throw SystemException("unable to obtain service handle", GetLastError());
+ int retries = 5;
+ vlog.debug("checking status");
+ while (retries-- && (WaitForSingleObject(process, 1000) != WAIT_OBJECT_0)) {}
+ if (!retries) {
+ vlog.debug("failed to quit - terminating");
+ // May not have quit because of silly Win9x registry watching bug..
+ if (!TerminateProcess(process, 1))
+ throw SystemException("unable to terminate process!", GetLastError());
+ throw Exception("service failed to quit - called TerminateProcess");
+ }
+ }
+
+ Sleep(500);
+
+ return true;
+}
+
+DWORD rfb::win32::getServiceState(const TCHAR* name) {
+ if (osVersion.isPlatformNT) {
+ // - Open the SCM
+ ServiceHandle scm = OpenSCManager(NULL, NULL, SC_MANAGER_CONNECT);
+ if (!scm)
+ throw rdr::SystemException("unable to open Service Control Manager", GetLastError());
+
+ // - Locate the service
+ ServiceHandle service = OpenService(scm, name, SERVICE_INTERROGATE);
+ if (!service)
+ throw rdr::SystemException("unable to open the service", GetLastError());
+
+ // - Get the service status
+ SERVICE_STATUS status;
+ if (!ControlService(service, SERVICE_CONTROL_INTERROGATE, (SERVICE_STATUS*)&status))
+ throw rdr::SystemException("unable to query the service", GetLastError());
+
+ return status.dwCurrentState;
+ } else {
+ HWND service_window = findServiceWindow(name);
+ return service_window ? SERVICE_RUNNING : SERVICE_STOPPED;
+ }
+}
+
+char* rfb::win32::serviceStateName(DWORD state) {
+ switch (state) {
+ case SERVICE_RUNNING: return strDup("Running");
+ case SERVICE_STOPPED: return strDup("Stopped");
+ case SERVICE_STOP_PENDING: return strDup("Stopping");
+ };
+ CharArray tmp(32);
+ sprintf(tmp.buf, "Unknown (%lu)", state);
+ return tmp.takeBuf();
+}
+
+
+bool rfb::win32::isServiceProcess() {
+ return service != 0;
+}
diff --git a/win/rfb_win32/Service.h b/win/rfb_win32/Service.h
new file mode 100644
index 0000000..00abe10
--- /dev/null
+++ b/win/rfb_win32/Service.h
@@ -0,0 +1,128 @@
+/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
+ *
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+ * USA.
+ */
+
+// -=- Service.h
+//
+// Win32 service-mode code.
+// Derive your service from this code and let it handle the annoying Win32
+// service API.
+// The underlying implementation takes care of the differences between
+// Windows NT and Windows 95 based systems
+
+#ifndef __RFB_WIN32_SERVICE_H__
+#define __RFB_WIN32_SERVICE_H__
+
+#include <windows.h>
+
+namespace rfb {
+
+ namespace win32 {
+
+ //
+ // -=- Service
+ //
+
+ // Application base-class for services.
+
+ class Service {
+ public:
+
+ Service(const TCHAR* name_);
+ virtual ~Service();
+
+ const TCHAR* getName() {return name;}
+ SERVICE_STATUS& getStatus() {return status;}
+
+ void setStatus(DWORD status);
+ void setStatus();
+
+ // - Start the service, having initialised it
+ void start();
+
+ // - Service main procedure - override to implement a service
+ virtual DWORD serviceMain(int argc, TCHAR* argv[]) = 0;
+
+ // - Service control notifications
+
+ // To get notified when the OS is shutting down
+ virtual void osShuttingDown() {};
+
+ // To get notified when the service parameters change
+ virtual void readParams() {};
+
+ // To cause the serviceMain() routine to return
+ virtual void stop() {};
+
+ public:
+ SERVICE_STATUS_HANDLE status_handle;
+ SERVICE_STATUS status;
+ protected:
+ const TCHAR* name;
+ };
+
+ class ServiceHandle {
+ public:
+ ServiceHandle(SC_HANDLE h) : handle(h) {}
+ ~ServiceHandle() {CloseServiceHandle(handle);}
+ operator SC_HANDLE() const {return handle;}
+ protected:
+ SC_HANDLE handle;
+ };
+
+ // -=- Routines used by desktop back-end code to manage desktops/window stations
+
+ // Returns false under Win9x
+ bool desktopChangeRequired();
+
+ // Returns true under Win9x
+ bool changeDesktop();
+
+ // -=- Routines used by the SInput Keyboard class to emulate Ctrl-Alt-Del
+ // Returns false under Win9x
+ bool emulateCtrlAltDel();
+
+ // -=- Routines to initialise the Event Log target Logger
+ // Returns false under Win9x
+ bool initEventLogLogger(const TCHAR* srcname);
+
+ // -=- Routines to register/unregister the service
+ // These routines also take care of registering the required
+ // event source information, etc.
+ // *** should really accept TCHAR argv
+
+ bool registerService(const TCHAR* name, const TCHAR* desc, int argc, const char* argv[]);
+ bool unregisterService(const TCHAR* name);
+
+ bool startService(const TCHAR* name);
+ bool stopService(const TCHAR* name);
+
+ // -=- Get the state of the named service (one of the NT service state values)
+ DWORD getServiceState(const TCHAR* name);
+
+ // -=- Convert a supplied service state value to a printable string e.g. Running, Stopped...
+ // The caller must delete the returned string buffer
+ char* serviceStateName(DWORD state);
+
+ // -=- Routine to determine whether the host process is running a service
+ bool isServiceProcess();
+
+ };
+
+};
+
+#endif // __RFB_WIN32_SERVICE_NT_H__
diff --git a/win/rfb_win32/SocketManager.cxx b/win/rfb_win32/SocketManager.cxx
new file mode 100644
index 0000000..1d52bc8
--- /dev/null
+++ b/win/rfb_win32/SocketManager.cxx
@@ -0,0 +1,213 @@
+/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
+ *
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+ * USA.
+ */
+
+// -=- SocketManager.cxx
+
+#include <winsock2.h>
+#include <list>
+#include <rfb/LogWriter.h>
+#include <rfb_win32/SocketManager.h>
+
+using namespace rfb;
+using namespace rfb::win32;
+
+static LogWriter vlog("SocketManager");
+
+
+// -=- SocketManager
+
+SocketManager::SocketManager() {
+}
+
+SocketManager::~SocketManager() {
+}
+
+
+static requestAddressChangeEvents(network::SocketListener* sock_) {
+ DWORD dummy = 0;
+ if (WSAIoctl(sock_->getFd(), SIO_ADDRESS_LIST_CHANGE, 0, 0, 0, 0, &dummy, 0, 0) == SOCKET_ERROR) {
+ DWORD err = WSAGetLastError();
+ if (err != WSAEWOULDBLOCK)
+ vlog.error("Unable to track address changes", err);
+ }
+}
+
+
+void SocketManager::addListener(network::SocketListener* sock_,
+ network::SocketServer* srvr,
+ AddressChangeNotifier* acn) {
+ WSAEVENT event = WSACreateEvent();
+ long flags = FD_ACCEPT | FD_CLOSE;
+ if (acn)
+ flags |= FD_ADDRESS_LIST_CHANGE;
+ try {
+ if (event && (WSAEventSelect(sock_->getFd(), event, flags) == SOCKET_ERROR))
+ throw rdr::SystemException("Unable to select on listener", WSAGetLastError());
+
+ // requestAddressChangeEvents MUST happen after WSAEventSelect, so that the socket is non-blocking
+ if (acn)
+ requestAddressChangeEvents(sock_);
+
+ // addEvent is the last thing we do, so that the event is NOT registered if previous steps fail
+ if (!event || !addEvent(event, this))
+ throw rdr::Exception("Unable to add listener");
+ } catch (rdr::Exception& e) {
+ if (event)
+ WSACloseEvent(event);
+ delete sock_;
+ vlog.error(e.str());
+ throw;
+ }
+
+ ListenInfo li;
+ li.sock = sock_;
+ li.server = srvr;
+ li.notifier = acn;
+ listeners[event] = li;
+}
+
+void SocketManager::remListener(network::SocketListener* sock) {
+ std::map<HANDLE,ListenInfo>::iterator i;
+ for (i=listeners.begin(); i!=listeners.end(); i++) {
+ if (i->second.sock == sock) {
+ removeEvent(i->first);
+ WSACloseEvent(i->first);
+ delete sock;
+ listeners.erase(i);
+ return;
+ }
+ }
+ throw rdr::Exception("Listener not registered");
+}
+
+
+void SocketManager::addSocket(network::Socket* sock_, network::SocketServer* srvr, bool outgoing) {
+ WSAEVENT event = WSACreateEvent();
+ if (!event || !addEvent(event, this) ||
+ (WSAEventSelect(sock_->getFd(), event, FD_READ | FD_CLOSE) == SOCKET_ERROR)) {
+ if (event)
+ WSACloseEvent(event);
+ delete sock_;
+ vlog.error("Unable to add connection");
+ return;
+ }
+ ConnInfo ci;
+ ci.sock = sock_;
+ ci.server = srvr;
+ connections[event] = ci;
+ srvr->addSocket(sock_, outgoing);
+}
+
+void SocketManager::remSocket(network::Socket* sock_) {
+ std::map<HANDLE,ConnInfo>::iterator i;
+ for (i=connections.begin(); i!=connections.end(); i++) {
+ if (i->second.sock == sock_) {
+ i->second.server->removeSocket(sock_);
+ removeEvent(i->first);
+ WSACloseEvent(i->first);
+ delete sock_;
+ connections.erase(i);
+ return;
+ }
+ }
+ throw rdr::Exception("Socket not registered");
+}
+
+
+int SocketManager::checkTimeouts() {
+ network::SocketServer* server = 0;
+ int timeout = EventManager::checkTimeouts();
+
+ std::map<HANDLE,ListenInfo>::iterator i;
+ for (i=listeners.begin(); i!=listeners.end(); i++)
+ soonestTimeout(&timeout, i->second.server->checkTimeouts());
+
+ std::list<network::Socket*> shutdownSocks;
+ std::map<HANDLE,ConnInfo>::iterator j, j_next;
+ for (j=connections.begin(); j!=connections.end(); j=j_next) {
+ j_next = j; j_next++;
+ if (j->second.sock->isShutdown())
+ shutdownSocks.push_back(j->second.sock);
+ }
+
+ std::list<network::Socket*>::iterator k;
+ for (k=shutdownSocks.begin(); k!=shutdownSocks.end(); k++)
+ remSocket(*k);
+
+ return timeout;
+}
+
+
+void SocketManager::processEvent(HANDLE event) {
+ if (listeners.count(event)) {
+ ListenInfo li = listeners[event];
+
+ // Accept an incoming connection
+ vlog.debug("accepting incoming connection");
+
+ // What kind of event is this?
+ WSANETWORKEVENTS network_events;
+ WSAEnumNetworkEvents(li.sock->getFd(), event, &network_events);
+ if (network_events.lNetworkEvents & FD_ACCEPT) {
+ network::Socket* new_sock = li.sock->accept();
+ if (new_sock && li.server->getDisable()) {
+ delete new_sock;
+ new_sock = 0;
+ }
+ if (new_sock)
+ addSocket(new_sock, li.server, false);
+ } else if (network_events.lNetworkEvents & FD_CLOSE) {
+ vlog.info("deleting listening socket");
+ remListener(li.sock);
+ } else if (network_events.lNetworkEvents & FD_ADDRESS_LIST_CHANGE) {
+ li.notifier->processAddressChange(li.sock);
+ DWORD dummy = 0;
+ requestAddressChangeEvents(li.sock);
+ } else {
+ vlog.error("unknown listener event: %lx", network_events.lNetworkEvents);
+ }
+ } else if (connections.count(event)) {
+ ConnInfo ci = connections[event];
+
+ try {
+ // Process data from an active connection
+
+ // Cancel event notification for this socket
+ if (WSAEventSelect(ci.sock->getFd(), event, 0) == SOCKET_ERROR)
+ throw rdr::SystemException("unable to disable WSAEventSelect:%u", WSAGetLastError());
+
+ // Reset the event object
+ WSAResetEvent(event);
+
+ // Call the socket server to process the event
+ ci.server->processSocketEvent(ci.sock);
+ if (ci.sock->isShutdown()) {
+ remSocket(ci.sock);
+ return;
+ }
+
+ // Re-instate the required socket event
+ // If the read event is still valid, the event object gets set here
+ if (WSAEventSelect(ci.sock->getFd(), event, FD_READ | FD_CLOSE) == SOCKET_ERROR)
+ throw rdr::SystemException("unable to re-enable WSAEventSelect:%u", WSAGetLastError());
+ } catch (rdr::Exception& e) {
+ vlog.error(e.str());
+ remSocket(ci.sock);
+ }
+ }
+}
diff --git a/win/rfb_win32/SocketManager.h b/win/rfb_win32/SocketManager.h
new file mode 100644
index 0000000..ef35974
--- /dev/null
+++ b/win/rfb_win32/SocketManager.h
@@ -0,0 +1,90 @@
+/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
+ *
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+ * USA.
+ */
+
+// -=- SocketManager.h
+
+// Socket manager class for Win32.
+// Passed a network::SocketListener and a network::SocketServer when
+// constructed. Uses WSAAsyncSelect to get notifications of network
+// connection attempts. When an incoming connection is received,
+// the manager will call network::SocketServer::addClient(). If
+// addClient returns true then the manager registers interest in
+// network events on that socket, and calls
+// network::SocketServer::processSocketEvent().
+
+#ifndef __RFB_WIN32_SOCKET_MGR_H__
+#define __RFB_WIN32_SOCKET_MGR_H__
+
+#include <map>
+#include <network/Socket.h>
+#include <rfb_win32/EventManager.h>
+
+namespace rfb {
+ namespace win32 {
+
+ class SocketManager : public EventManager, EventHandler {
+ public:
+ SocketManager();
+ virtual ~SocketManager();
+
+ // AddressChangeNotifier callback interface
+ // If an object implementing this is passed to addListener then it will be
+ // called whenever the SocketListener's address list changes
+ class AddressChangeNotifier {
+ public:
+ virtual ~AddressChangeNotifier() {}
+ virtual void processAddressChange(network::SocketListener* sl) = 0;
+ };
+
+ // Add a listening socket. Incoming connections will be added to the supplied
+ // SocketServer.
+ void addListener(network::SocketListener* sock_,
+ network::SocketServer* srvr,
+ AddressChangeNotifier* acn = 0);
+
+ // Remove and delete a listening socket.
+ void remListener(network::SocketListener* sock);
+
+ // Add an already-connected socket. Socket events will cause the supplied
+ // SocketServer to be called. The socket must ALREADY BE REGISTERED with
+ // the SocketServer.
+ void addSocket(network::Socket* sock_, network::SocketServer* srvr, bool outgoing=true);
+
+ protected:
+ virtual int checkTimeouts();
+ virtual void processEvent(HANDLE event);
+ virtual void remSocket(network::Socket* sock);
+
+ struct ConnInfo {
+ network::Socket* sock;
+ network::SocketServer* server;
+ };
+ struct ListenInfo {
+ network::SocketListener* sock;
+ network::SocketServer* server;
+ AddressChangeNotifier* notifier;
+ };
+ std::map<HANDLE, ListenInfo> listeners;
+ std::map<HANDLE, ConnInfo> connections;
+ };
+
+ }
+
+}
+
+#endif
diff --git a/win/rfb_win32/TCharArray.cxx b/win/rfb_win32/TCharArray.cxx
new file mode 100644
index 0000000..fd4c078
--- /dev/null
+++ b/win/rfb_win32/TCharArray.cxx
@@ -0,0 +1,85 @@
+/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
+ *
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+ * USA.
+ */
+
+#include <rfb_win32/TCharArray.h>
+
+namespace rfb {
+
+ WCHAR* wstrDup(const WCHAR* s) {
+ if (!s) return 0;
+ WCHAR* t = new WCHAR[wcslen(s)+1];
+ memcpy(t, s, sizeof(WCHAR)*(wcslen(s)+1));
+ return t;
+ }
+ void wstrFree(WCHAR* s) {delete [] s;}
+
+ char* strDup(const WCHAR* s) {
+ if (!s) return 0;
+ int len = wcslen(s);
+ char* t = new char[len+1];
+ t[WideCharToMultiByte(CP_ACP, 0, s, len, t, len, 0, 0)] = 0;
+ return t;
+ }
+
+ WCHAR* wstrDup(const char* s) {
+ if (!s) return 0;
+ int len = strlen(s);
+ WCHAR* t = new WCHAR[len+1];
+ t[MultiByteToWideChar(CP_ACP, 0, s, len, t, len)] = 0;
+ return t;
+ }
+
+
+ bool wstrSplit(const WCHAR* src, const WCHAR limiter, WCHAR** out1, WCHAR** out2, bool fromEnd) {
+ WCharArray out1old, out2old;
+ if (out1) out1old.buf = *out1;
+ if (out2) out2old.buf = *out2;
+ int len = wcslen(src);
+ int i=0, increment=1, limit=len;
+ if (fromEnd) {
+ i=len-1; increment = -1; limit = -1;
+ }
+ while (i!=limit) {
+ if (src[i] == limiter) {
+ if (out1) {
+ *out1 = new WCHAR[i+1];
+ if (i) memcpy(*out1, src, sizeof(WCHAR)*i);
+ (*out1)[i] = 0;
+ }
+ if (out2) {
+ *out2 = new WCHAR[len-i];
+ if (len-i-1) memcpy(*out2, &src[i+1], sizeof(WCHAR)*(len-i-1));
+ (*out2)[len-i-1] = 0;
+ }
+ return true;
+ }
+ i+=increment;
+ }
+ if (out1) *out1 = wstrDup(src);
+ if (out2) *out2 = 0;
+ return false;
+ }
+
+ bool wstrContains(const WCHAR* src, WCHAR c) {
+ int l=wcslen(src);
+ for (int i=0; i<l; i++)
+ if (src[i] == c) return true;
+ return false;
+ }
+
+};
diff --git a/win/rfb_win32/TCharArray.h b/win/rfb_win32/TCharArray.h
new file mode 100644
index 0000000..dde63b7
--- /dev/null
+++ b/win/rfb_win32/TCharArray.h
@@ -0,0 +1,135 @@
+/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
+ *
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+ * USA.
+ */
+
+// -=- TCharArray.h
+
+// This library contains the wide-character equivalent of CharArray, named
+// WCharArray. In addition to providing wide-character equivalents of
+// the char* string manipulation functions (strDup, strFree, etc), special
+// versions of those functions are provided which attempt to convert from
+// one format to the other.
+// e.g. char* t = "hello world"; WCHAR* w = wstrDup(t);
+// Results in w containing the wide-character text "hello world".
+// For convenience, the WStr and CStr classes are also provided. These
+// accept an existing (const) WCHAR* or char* null-terminated string and
+// create a read-only copy of that in the desired format. The new copy
+// will actually be the original copy if the format has not changed, otherwise
+// it will be a new buffer owned by the WStr/CStr.
+
+// In addition to providing wide character functions, this header defines
+// TCHAR* handling classes & functions. TCHAR is defined at compile time to
+// either char or WCHAR. Programs can treat this as a third data type and
+// call TStr() whenever a TCHAR* is required but a char* or WCHAR* is supplied,
+// and TStr will do the right thing.
+
+#ifndef __RFB_WIN32_TCHARARRAY_H__
+#define __RFB_WIN32_TCHARARRAY_H__
+
+#include <windows.h>
+#include <tchar.h>
+#include <rfb/util.h>
+#include <rfb/Password.h>
+
+namespace rfb {
+
+ // -=- String duplication and cleanup functions.
+ // These routines also handle conversion between WCHAR* and char*
+
+ char* strDup(const WCHAR* s);
+ WCHAR* wstrDup(const WCHAR* s);
+ WCHAR* wstrDup(const char* s);
+ void wstrFree(WCHAR* s);
+
+ bool wstrSplit(const WCHAR* src, const WCHAR limiter, WCHAR** out1, WCHAR** out2, bool fromEnd=false);
+ bool wstrContains(const WCHAR* src, WCHAR c);
+
+ // -=- Temporary format conversion classes
+ // CStr accepts WCHAR* or char* and behaves like a char*
+ // WStr accepts WCHAR* or char* and behaves like a WCHAR*
+
+ struct WStr {
+ WStr(const char* s) : buf(wstrDup(s)), free_(true) {}
+ WStr(const WCHAR* s) : buf(s), free_(false) {}
+ ~WStr() {if (free_) wstrFree((WCHAR*)buf);}
+ operator const WCHAR*() {return buf;}
+ const WCHAR* buf;
+ bool free_;
+ };
+
+ struct CStr {
+ CStr(const char* s) : buf(s), free_(false) {}
+ CStr(const WCHAR* s) : buf(strDup(s)), free_(true) {}
+ ~CStr() {if (free_) strFree((char*)buf);}
+ operator const char*() {return buf;}
+ const char* buf;
+ bool free_;
+ };
+
+ // -=- Class to handle cleanup of arrays of native Win32 characters
+ class WCharArray {
+ public:
+ WCharArray() : buf(0) {}
+ WCharArray(char* str) : buf(wstrDup(str)) {strFree(str);} // note: assumes ownership
+ WCharArray(WCHAR* str) : buf(str) {} // note: assumes ownership
+ WCharArray(int len) {
+ buf = new WCHAR[len];
+ }
+ ~WCharArray() {
+ delete [] buf;
+ }
+ // Get the buffer pointer & clear it (i.e. caller takes ownership)
+ WCHAR* takeBuf() {WCHAR* tmp = buf; buf = 0; return tmp;}
+ void replaceBuf(WCHAR* str) {delete [] buf; buf = str;}
+ WCHAR* buf;
+ };
+
+ // -=- Wide-character-based password-buffer handler. Zeroes the password
+ // buffer when deleted or replaced.
+ class WPlainPasswd : public WCharArray {
+ public:
+ WPlainPasswd() {}
+ WPlainPasswd(WCHAR* str) : WCharArray(str) {}
+ ~WPlainPasswd() {replaceBuf(0);}
+ void replaceBuf(WCHAR* str) {
+ if (buf)
+ memset(buf, 0, sizeof(WCHAR)*wcslen(buf));
+ WCharArray::replaceBuf(str);
+ }
+ };
+
+#ifdef _UNICODE
+#define tstrDup wstrDup
+#define tstrFree wstrFree
+#define tstrSplit wstrSplit
+#define tstrContains wstrContains
+ typedef WCharArray TCharArray;
+ typedef WStr TStr;
+ typedef WPlainPasswd TPlainPasswd;
+#else
+#define tstrDup strDup
+#define tstrFree strFree
+#define tstrSplit strSplit
+#define tstrContains strContains
+ typedef CharArray TCharArray;
+ typedef CStr TStr;
+ typedef PlainPasswd TPlainPasswd;
+#endif
+
+};
+
+#endif
diff --git a/win/rfb_win32/Threading.cxx b/win/rfb_win32/Threading.cxx
new file mode 100644
index 0000000..c41ac38
--- /dev/null
+++ b/win/rfb_win32/Threading.cxx
@@ -0,0 +1,151 @@
+/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
+ *
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+ * USA.
+ */
+
+// -=- Threading.cxx
+// Win32 Threading interface implementation
+
+#include <malloc.h>
+
+#include <rdr/Exception.h>
+#include <rfb/LogWriter.h>
+#include <rfb/util.h>
+#include <rfb_win32/Threading.h>
+
+using namespace rfb;
+
+static LogWriter vlog("Threading");
+
+static DWORD threadStorage = TlsAlloc();
+
+
+inline void logAction(Thread* t, const char* action) {
+ vlog.debug("%-16.16s %s(%lx)", action, t->getName(), t);
+}
+
+inline void logError(Thread* t, const char* err) {
+ vlog.error("%-16.16s %s(%lx):%s", "failed", t->getName(), t, err);
+}
+
+
+DWORD WINAPI
+Thread::threadProc(LPVOID lpParameter) {
+ Thread* thread = (Thread*) lpParameter;
+ TlsSetValue(threadStorage, thread);
+ logAction(thread, "started");
+ try {
+ thread->run();
+ logAction(thread, "stopped");
+ } catch (rdr::Exception& e) {
+ logError(thread, e.str());
+ }
+ bool deleteThread = false;
+ {
+ Lock l(thread->mutex);
+ thread->state = ThreadStopped;
+ thread->sig->signal();
+ deleteThread = thread->deleteAfterRun;
+ }
+ if (deleteThread)
+ delete thread;
+ return 0;
+}
+
+Thread::Thread(const char* name_) : name(strDup(name_ ? name_ : "Unnamed")), sig(0), deleteAfterRun(false) {
+ sig = new Condition(mutex);
+ cond_event.h = CreateEvent(NULL, TRUE, FALSE, NULL);
+ thread.h = CreateThread(NULL, 0, threadProc, this, CREATE_SUSPENDED, &thread_id);
+ state = ThreadCreated;
+ logAction(this, "created");
+}
+
+Thread::Thread(HANDLE thread_, DWORD thread_id_) : name(strDup("Native")), sig(0), deleteAfterRun(false),
+ thread(thread_), thread_id(thread_id_) {
+ cond_event.h = CreateEvent(NULL, TRUE, FALSE, NULL);
+ state = ThreadNative;
+ logAction(this, "created");
+}
+
+Thread::~Thread() {
+ logAction(this, "destroying");
+ if (!deleteAfterRun && state != ThreadNative)
+ this->join();
+ if (sig)
+ delete sig;
+ logAction(this, "destroyed");
+}
+
+void
+Thread::run() {
+}
+
+void
+Thread::start() {
+ Lock l(mutex);
+ if (state == ThreadCreated) {
+ state = ThreadStarted;
+ sig->signal();
+ ResumeThread(thread);
+ }
+}
+
+Thread*
+Thread::join() {
+ if (deleteAfterRun)
+ throw rdr::Exception("attempt to join() with deleteAfterRun thread");
+ Lock l(mutex);
+ if (state == ThreadJoined) {
+ logAction(this, "already joined");
+ } else {
+ logAction(this, "joining");
+ while (state == ThreadStarted) {
+ sig->wait();
+ logAction(this, "checking");
+ }
+ state = ThreadJoined;
+ logAction(this, "joined");
+ }
+ return this;
+}
+
+const char*
+Thread::getName() const {
+ return name.buf;
+}
+
+ThreadState
+Thread::getState() const {
+ return state;
+}
+
+unsigned long
+Thread::getThreadId() const {
+ return thread_id;
+}
+
+
+Thread*
+Thread::self() {
+ Thread* thread = (Thread*) TlsGetValue(threadStorage);
+ if (!thread) {
+ // *** memory leak - could use GetExitCodeThread to lazily detect when
+ // to clean up native thread objects
+ thread = new Thread(GetCurrentThread(), GetCurrentThreadId());
+ TlsSetValue(threadStorage, thread);
+ }
+ return thread;
+}
diff --git a/win/rfb_win32/Threading.h b/win/rfb_win32/Threading.h
new file mode 100644
index 0000000..850f04d
--- /dev/null
+++ b/win/rfb_win32/Threading.h
@@ -0,0 +1,154 @@
+/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
+ *
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+ * USA.
+ */
+
+// -=- Threading_win32.h
+// Win32 Threading interface implementation
+
+#ifndef __RFB_THREADING_IMPL_WIN32
+#define __RFB_THREADING_IMPL_WIN32
+
+#define __RFB_THREADING_IMPL WIN32
+
+#include <rfb_win32/Handle.h>
+#include <rfb/util.h>
+#include <rdr/Exception.h>
+//#include <stdio.h>
+
+
+namespace rfb {
+
+ class Mutex {
+ public:
+ Mutex() {
+ InitializeCriticalSection(&crit);
+ }
+ ~Mutex() {
+ DeleteCriticalSection(&crit);
+ }
+ friend class Lock;
+ friend class Condition;
+ protected:
+ void enter() {EnterCriticalSection(&crit);}
+ void exit() {LeaveCriticalSection(&crit);}
+ CRITICAL_SECTION crit;
+ };
+
+ class Lock {
+ public:
+ Lock(Mutex& m) : mutex(m) {m.enter();}
+ ~Lock() {mutex.exit();}
+ protected:
+ Mutex& mutex;
+ };
+
+ enum ThreadState {ThreadCreated, ThreadStarted, ThreadStopped, ThreadJoined, ThreadNative};
+
+ class Thread {
+ public:
+ Thread(const char* name_=0);
+ virtual ~Thread();
+
+ virtual void run();
+
+ virtual void start();
+ virtual Thread* join();
+
+ const char* getName() const;
+ ThreadState getState() const;
+
+ // Determines whether the thread should delete itself when run() returns
+ // If you set this, you must NEVER call join()!
+ void setDeleteAfterRun() {deleteAfterRun = true;};
+
+ unsigned long getThreadId() const;
+
+ static Thread* self();
+
+ friend class Condition;
+
+ protected:
+ Thread(HANDLE thread_, DWORD thread_id_);
+ static DWORD WINAPI threadProc(LPVOID lpParameter);
+
+ win32::Handle thread;
+ DWORD thread_id;
+ CharArray name;
+ ThreadState state;
+ Condition* sig;
+ Mutex mutex;
+
+ win32::Handle cond_event;
+ Thread* cond_next;
+
+ bool deleteAfterRun;
+ };
+
+ class Condition {
+ public:
+ Condition(Mutex& m) : mutex(m), waiting(0) {
+ }
+ ~Condition() {
+ }
+
+ // Wake up the specified number of threads that are waiting
+ // on this Condition, or all of them if -1 is specified.
+ void signal(int howMany=1) {
+ Lock l(cond_lock);
+ while (waiting && howMany!=0) {
+ SetEvent(waiting->cond_event);
+ waiting = waiting->cond_next;
+ if (howMany>0) --howMany;
+ }
+ }
+
+ // NB: Must hold "mutex" to call wait()
+ // Wait until either the Condition is signalled or the timeout
+ // expires.
+ void wait(DWORD timeout=INFINITE) {
+ Thread* self = Thread::self();
+ ResetEvent(self->cond_event);
+ { Lock l(cond_lock);
+ self->cond_next = waiting;
+ waiting = self;
+ }
+ mutex.exit();
+ DWORD result = WaitForSingleObject(self->cond_event, timeout);
+ mutex.enter();
+ if (result == WAIT_TIMEOUT) {
+ Lock l(cond_lock);
+ // Remove this thread from the Condition
+ for (Thread** removeFrom = &waiting; *removeFrom; removeFrom = &(*removeFrom)->cond_next) {
+ if (*removeFrom == self) {
+ *removeFrom = self->cond_next;
+ break;
+ }
+ }
+ } else if (result == WAIT_FAILED) {
+ throw rdr::SystemException("failed to wait on Condition", GetLastError());
+ }
+ }
+
+ protected:
+ Mutex& mutex;
+ Mutex cond_lock;
+ Thread* waiting;
+ };
+
+};
+
+#endif // __RFB_THREADING_IMPL
diff --git a/win/rfb_win32/ToolBar.cxx b/win/rfb_win32/ToolBar.cxx
new file mode 100644
index 0000000..6392ebd
--- /dev/null
+++ b/win/rfb_win32/ToolBar.cxx
@@ -0,0 +1,216 @@
+/* Copyright (C) 2004 TightVNC Team. All Rights Reserved.
+ *
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+ * USA.
+ */
+
+// -=- ToolBar control class.
+
+#include "ToolBar.h"
+
+using namespace rfb::win32;
+
+ToolBar::ToolBar() : hwndToolBar(0), tbID(-1) {
+ INITCOMMONCONTROLSEX icex;
+
+ // Ensure that the common control DLL is loaded
+ icex.dwSize = sizeof(INITCOMMONCONTROLSEX);
+ icex.dwICC = ICC_BAR_CLASSES;
+ InitCommonControlsEx(&icex);
+}
+
+ToolBar::~ToolBar() {
+ DestroyWindow(getHandle());
+}
+
+bool ToolBar::create(int _tbID, HWND _parentHwnd, DWORD dwStyle) {
+ parentHwnd = _parentHwnd;
+ dwStyle |= WS_CHILD;
+
+ // Create the ToolBar window
+ hwndToolBar = CreateWindowEx(0, TOOLBARCLASSNAME, 0, dwStyle,
+ 0, 0, 25, 25, parentHwnd, (HMENU)_tbID, GetModuleHandle(0), 0);
+
+ if (hwndToolBar) {
+ tbID = _tbID;
+
+ // It's required for backward compatibility
+ SendMessage(hwndToolBar, TB_BUTTONSTRUCTSIZE, (WPARAM)sizeof(TBBUTTON), 0);
+ }
+ return (hwndToolBar ? true : false);
+};
+
+int ToolBar::addBitmap(int nButtons, UINT bitmapID) {
+ assert(nButtons > 0);
+ TBADDBITMAP resBitmap;
+ resBitmap.hInst = GetModuleHandle(0);
+ resBitmap.nID = bitmapID;
+ return SendMessage(getHandle(), TB_ADDBITMAP, nButtons, (LPARAM)&resBitmap);
+}
+
+int ToolBar::addSystemBitmap(UINT stdBitmapID) {
+ TBADDBITMAP resBitmap;
+ resBitmap.hInst = HINST_COMMCTRL;
+ resBitmap.nID = stdBitmapID;
+ return SendMessage(getHandle(), TB_ADDBITMAP, 0, (LPARAM)&resBitmap);
+}
+
+bool ToolBar::setBitmapSize(int width, int height) {
+ int result = SendMessage(getHandle(), TB_SETBITMAPSIZE,
+ 0, MAKELONG(width, height));
+ return (result ? true : false);
+}
+
+bool ToolBar::addButton(int iBitmap, int idCommand, BYTE state, BYTE style, UINT dwData, int iString) {
+ TBBUTTON tbb;
+ tbb.iBitmap = iBitmap;
+ tbb.idCommand = idCommand;
+ tbb.fsState = state;
+ tbb.fsStyle = style;
+ tbb.dwData = dwData;
+ tbb.iString = iString;
+
+ int result = SendMessage(getHandle(), TB_ADDBUTTONS, 1, (LPARAM)&tbb);
+ if (result) {
+ SendMessage(getHandle(), TB_AUTOSIZE, 0, 0);
+ }
+ return (result ? true : false);
+}
+
+bool ToolBar::addNButton(int nButtons, LPTBBUTTON tbb) {
+ assert(nButtons > 0);
+ assert(tbb > 0);
+ int result = SendMessage(getHandle(), TB_ADDBUTTONS, nButtons, (LPARAM)tbb);
+ if (result) {
+ SendMessage(getHandle(), TB_AUTOSIZE, 0, 0);
+ }
+ return (result ? true : false);
+}
+
+bool ToolBar::deleteButton(int indexButton) {
+ assert(indexButton >= 0);
+ int result = SendMessage(getHandle(), TB_DELETEBUTTON, indexButton, 0);
+
+ if (result) {
+ SendMessage(getHandle(), TB_AUTOSIZE, 0, 0);
+ }
+ return (result ? true : false);
+}
+
+bool ToolBar::insertButton(int indexButton, LPTBBUTTON tbb) {
+ assert(indexButton >= 0);
+ assert(tbb > 0);
+ int result = SendMessage(getHandle(), TB_INSERTBUTTON,
+ indexButton, (LPARAM)tbb);
+
+ if (result) {
+ SendMessage(getHandle(), TB_AUTOSIZE, 0, 0);
+ }
+ return (result ? true : false);
+}
+
+int ToolBar::getButtonInfo(int idButton, TBBUTTONINFO *btnInfo) {
+ assert(idButton >= 0);
+ assert(btnInfo > 0);
+ return SendMessage(getHandle(), TB_GETBUTTONINFO, idButton, (LPARAM)btnInfo);
+}
+
+int ToolBar::getButtonsHeight() {
+ return HIWORD(SendMessage(getHandle(), TB_GETBUTTONSIZE, 0, 0));
+}
+
+int ToolBar::getButtonsWidth() {
+ return LOWORD(SendMessage(getHandle(), TB_GETBUTTONSIZE, 0, 0));
+}
+
+bool ToolBar::setButtonInfo(int idButton, TBBUTTONINFO* btnInfo) {
+ assert(idButton >= 0);
+ assert(btnInfo > 0);
+ int result = SendMessage(getHandle(), TB_SETBUTTONINFO,
+ idButton, (LPARAM)(LPTBBUTTONINFO)btnInfo);
+ return (result ? true : false);
+}
+
+bool ToolBar::checkButton(int idButton, bool check) {
+ assert(idButton >= 0);
+ int result = SendMessage(getHandle(), TB_CHECKBUTTON,
+ idButton, MAKELONG(check, 0));
+ return (result ? true : false);
+}
+
+bool ToolBar::enableButton(int idButton, bool enable) {
+ assert(idButton >= 0);
+ int result = SendMessage(getHandle(), TB_ENABLEBUTTON,
+ idButton, MAKELONG(enable, 0));
+ return (result ? true : false);
+}
+
+bool ToolBar::pressButton(int idButton, bool press) {
+ assert(idButton >= 0);
+ int result = SendMessage(getHandle(), TB_PRESSBUTTON,
+ idButton, MAKELONG(press, 0));
+ return (result ? true : false);
+}
+
+bool ToolBar::getButtonRect(int nIndex, LPRECT buttonRect) {
+ int result = SendMessage(getHandle(), TB_GETITEMRECT,
+ nIndex, (LPARAM)buttonRect);
+ return (result ? true : false);
+}
+
+bool ToolBar::setButtonSize(int width, int height) {
+ assert(width > 0);
+ assert(height > 0);
+ int result = SendMessage(getHandle(), TB_SETBUTTONSIZE,
+ 0, MAKELONG(width, height));
+ if (result) {
+ SendMessage(getHandle(), TB_AUTOSIZE, 0, 0);
+ return true;
+ }
+ return false;
+}
+
+void ToolBar::autoSize() {
+ DWORD style = SendMessage(getHandle(), TB_GETSTYLE, 0, 0);
+ if (style & CCS_NORESIZE) {
+ RECT r, btnRect;
+ GetClientRect(parentHwnd, &r);
+ getButtonRect(0, &btnRect);
+ int height = getButtonsHeight() + btnRect.top * 2 + 2;
+ SetWindowPos(getHandle(), HWND_TOP, 0, 0, r.right - r.left, height,
+ SWP_NOMOVE);
+ } else {
+ SendMessage(getHandle(), TB_AUTOSIZE, 0, 0);
+ }
+}
+
+int ToolBar::getHeight() {
+ RECT r;
+ GetWindowRect(getHandle(), &r);
+ return r.bottom - r.top;
+}
+
+void ToolBar::show() {
+ ShowWindow(getHandle(), SW_SHOW);
+}
+
+void ToolBar::hide() {
+ ShowWindow(getHandle(), SW_HIDE);
+}
+
+bool ToolBar::isVisible() {
+ DWORD style = GetWindowLong(getHandle(), GWL_STYLE);
+ return (bool)(style & WS_VISIBLE);
+}
diff --git a/win/rfb_win32/ToolBar.h b/win/rfb_win32/ToolBar.h
new file mode 100644
index 0000000..2242c2a
--- /dev/null
+++ b/win/rfb_win32/ToolBar.h
@@ -0,0 +1,132 @@
+/* Copyright (C) 2004 TightVNC Team. All Rights Reserved.
+ *
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+ * USA.
+ */
+
+// -=- ToolBar control class.
+
+#include <windows.h>
+#include <commctrl.h>
+#include <assert.h>
+
+namespace rfb {
+
+ namespace win32 {
+
+ class ToolBar {
+ public:
+ ToolBar();
+ virtual ~ToolBar();
+
+ // create() creates a windows toolbar. dwStyle is a combination of
+ // the toolbar control and button styles. It returns TRUE if successful,
+ // or FALSE otherwise.
+ bool create(int tbID, HWND parentHwnd,
+ DWORD dwStyle = WS_CHILD | WS_VISIBLE | TBSTYLE_FLAT);
+
+ // -=- Button images operations
+
+ // addBitmap() adds one or more images from resources to
+ // the list of button images available for a toolbar.
+ // Returns the index of the first new image if successful,
+ // or -1 otherwise.
+ int addBitmap(int nButtons, UINT bitmapID);
+
+ // addSystemBitmap() adds the system-defined button bitmaps to the list
+ // of the toolbar button specifying by stdBitmapID. Returns the index of
+ // the first new image if successful, or -1 otherwise.
+ int addSystemBitmap(UINT stdBitmapID);
+
+ // setBitmapSize() sets the size of the bitmapped images to be added
+ // to a toolbar. It returns TRUE if successful, or FALSE otherwise.
+ // You must call it before addBitmap().
+ bool setBitmapSize(int width, int height);
+
+ // -=- Button operations
+
+ // addButton() adds one button.
+ bool addButton(int iBitmap, int idCommand, BYTE state=TBSTATE_ENABLED,
+ BYTE style=TBSTYLE_BUTTON, UINT dwData=0, int iString=0);
+
+ // addNButton() adds nButtons buttons to a toolbar.
+ bool addNButton(int nButtons, LPTBBUTTON tbb);
+
+ // deleteButton() removes a button from the toolbar.
+ bool deleteButton(int nIndex);
+
+ // insertButton() inserts a button in a toolbar control by index.
+ bool insertButton(int nIndex, LPTBBUTTON tbb);
+
+ // getButtonInfo() retrieves extended information about a toolbar's
+ // button. It returns index of the button if successful, or -1 otherwise.
+ int getButtonInfo(int idButton, TBBUTTONINFO *btnInfo);
+
+ // getButtonsHeight() retrieves the height of the toolbar buttons.
+ int getButtonsHeight();
+
+ // getButtonsWidth() retrieves the width of the toolbar buttons.
+ int getButtonsWidth();
+
+ // setButtonInfo() sets the information for an existing button
+ // in a toolbar.
+ bool setButtonInfo(int idButton, TBBUTTONINFO* ptbbi);
+
+ // checkButton() checks or unchecks a given button in a toolbar control.
+ bool checkButton(int idButton, bool check);
+
+ // enableButton() enables or disables the specified button
+ // in the toolbar.
+ bool enableButton(int idButton, bool enable);
+
+ // pressButton() presses or releases the specified button in the toolbar.
+ bool pressButton(int idButton, bool press);
+
+ // getButtonRect() gets the bounding rectangle of a button in a toolbar.
+ bool getButtonRect(int nIndex, LPRECT buttonRect);
+
+ // setButtonSize() sets the size of the buttons to be added to a toolbar.
+ // Button size must be largen the button bitmap.
+ bool setButtonSize(int width, int height);
+
+ // -=- ToolBar operations
+
+ // autoSize() resizes the toolbar window.
+ void autoSize();
+
+ // getHandle() returns handle to a toolbar window.
+ HWND getHandle() { return hwndToolBar; }
+
+ // getHeight() returns the toolbar window height.
+ int getHeight();
+
+ // show() displays the toolbar window.
+ void show();
+
+ // hide() hides the toolbar window.
+ void hide();
+
+ // isVisible() check the toolbar window on visible.
+ bool isVisible();
+
+ protected:
+ HWND hwndToolBar;
+ HWND parentHwnd;
+ int tbID;
+ };
+
+ }; // win32
+
+}; // rfb
diff --git a/win/rfb_win32/TrayIcon.h b/win/rfb_win32/TrayIcon.h
new file mode 100644
index 0000000..dc5102a
--- /dev/null
+++ b/win/rfb_win32/TrayIcon.h
@@ -0,0 +1,89 @@
+/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
+ *
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+ * USA.
+ */
+
+// -=- CView.h
+
+// An instance of the CView class is created for each VNC Viewer connection.
+
+#ifndef __RFB_WIN32_TRAY_ICON_H__
+#define __RFB_WIN32_TRAY_ICON_H__
+
+#include <windows.h>
+#include <shellapi.h>
+#include <rfb_win32/MsgWindow.h>
+#include <rdr/Exception.h>
+
+namespace rfb {
+
+ namespace win32 {
+
+ class TrayIcon : public MsgWindow {
+ public:
+ TrayIcon() : MsgWindow(_T("VNCTray")) {
+#ifdef NOTIFYICONDATA_V1_SIZE
+ nid.cbSize = NOTIFYICONDATA_V1_SIZE;
+#else
+ nid.cbSize = sizeof(NOTIFYICONDATA);
+#endif
+
+ nid.hWnd = getHandle();
+ nid.uID = 0;
+ nid.hIcon = 0;
+ nid.uFlags = NIF_ICON | NIF_MESSAGE;
+ nid.uCallbackMessage = WM_USER;
+ }
+ virtual ~TrayIcon() {
+ remove();
+ }
+ bool setIcon(UINT icon) {
+ if (icon == 0) {
+ return remove();
+ } else {
+ nid.hIcon = (HICON)LoadImage(GetModuleHandle(0), MAKEINTRESOURCE(icon),
+ IMAGE_ICON, 0, 0, LR_DEFAULTSIZE | LR_SHARED);
+ return refresh();
+ }
+ }
+ bool setToolTip(const TCHAR* text) {
+ if (text == 0) {
+ nid.uFlags &= ~NIF_TIP;
+ } else {
+ const int tipLen = sizeof(nid.szTip)/sizeof(TCHAR);
+ _tcsncpy(nid.szTip, text, tipLen);
+ nid.szTip[tipLen-1] = 0;
+ nid.uFlags |= NIF_TIP;
+ }
+ return refresh();
+ }
+ bool remove() {
+ return Shell_NotifyIcon(NIM_DELETE, &nid) != 0;
+ }
+ bool refresh() {
+ return Shell_NotifyIcon(NIM_MODIFY, &nid) || Shell_NotifyIcon(NIM_ADD, &nid);
+ }
+ protected:
+ NOTIFYICONDATA nid;
+ };
+
+ };
+
+};
+
+#endif
+
+
diff --git a/win/rfb_win32/TsSessions.cxx b/win/rfb_win32/TsSessions.cxx
new file mode 100644
index 0000000..efe7564
--- /dev/null
+++ b/win/rfb_win32/TsSessions.cxx
@@ -0,0 +1,93 @@
+/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
+ *
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+ * USA.
+ */
+
+#include <rfb_win32/TsSessions.h>
+#include <rfb_win32/DynamicFn.h>
+#include <rfb/LogWriter.h>
+#include <rdr/Exception.h>
+#include <tchar.h>
+
+#ifdef ERROR_CTX_WINSTATION_BUSY
+#define RFB_HAVE_WINSTATION_CONNECT
+#else
+#pragma message(" NOTE: Not building WinStationConnect support.")
+#endif
+
+static rfb::LogWriter vlog("TsSessions");
+
+namespace rfb {
+namespace win32 {
+
+ // Windows XP (and later) functions used to handle session Ids
+ typedef BOOLEAN (WINAPI *_WinStationConnect_proto) (HANDLE,ULONG,ULONG,PCWSTR,ULONG);
+ DynamicFn<_WinStationConnect_proto> _WinStationConnect(_T("winsta.dll"), "WinStationConnectW");
+ typedef DWORD (WINAPI *_WTSGetActiveConsoleSessionId_proto) ();
+ DynamicFn<_WTSGetActiveConsoleSessionId_proto> _WTSGetActiveConsoleSessionId(_T("kernel32.dll"), "WTSGetActiveConsoleSessionId");
+ typedef BOOL (WINAPI *_ProcessIdToSessionId_proto) (DWORD, DWORD*);
+ DynamicFn<_ProcessIdToSessionId_proto> _ProcessIdToSessionId(_T("kernel32.dll"), "ProcessIdToSessionId");
+ typedef BOOL (WINAPI *_LockWorkStation_proto)();
+ DynamicFn<_LockWorkStation_proto> _LockWorkStation(_T("user32.dll"), "LockWorkStation");
+
+
+ ProcessSessionId::ProcessSessionId(DWORD processId) {
+ id = 0;
+ if (!_ProcessIdToSessionId.isValid())
+ return;
+ if (processId == -1)
+ processId = GetCurrentProcessId();
+ if (!(*_ProcessIdToSessionId)(GetCurrentProcessId(), &id))
+ throw rdr::SystemException("ProcessIdToSessionId", GetLastError());
+ }
+
+ ProcessSessionId mySessionId;
+
+ ConsoleSessionId::ConsoleSessionId() {
+ if (_WTSGetActiveConsoleSessionId.isValid())
+ id = (*_WTSGetActiveConsoleSessionId)();
+ else
+ id = 0;
+ }
+
+ bool inConsoleSession() {
+ ConsoleSessionId console;
+ return console.id == mySessionId.id;
+ }
+
+ void setConsoleSession(DWORD sessionId) {
+#ifdef RFB_HAVE_WINSTATION_CONNECT
+ if (!_WinStationConnect.isValid())
+ throw rdr::Exception("WinSta APIs missing");
+ if (sessionId == -1)
+ sessionId = mySessionId.id;
+
+ // Try to reconnect our session to the console
+ ConsoleSessionId console;
+ vlog.info("Console session is %d", console.id);
+ if (!(*_WinStationConnect)(0, sessionId, console.id, L"", 0))
+ throw rdr::SystemException("Unable to connect session to Console", GetLastError());
+
+ // Lock the newly connected session, for security
+ if (_LockWorkStation.isValid())
+ (*_LockWorkStation)();
+#else
+ throw rdr::Exception("setConsoleSession not implemented");
+#endif
+ }
+
+};
+};
diff --git a/win/rfb_win32/TsSessions.h b/win/rfb_win32/TsSessions.h
new file mode 100644
index 0000000..b15ada7
--- /dev/null
+++ b/win/rfb_win32/TsSessions.h
@@ -0,0 +1,61 @@
+/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
+ *
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+ * USA.
+ */
+
+// Windows version-independent Terminal Services Session discovery
+// and manipulation API. This code will eventually be replaced
+// by the full TS-compatibility scheme.
+
+#ifndef __RFB_WIN32_TSSESSIONS_H__
+#define __RFB_WIN32_TSSESSIONS_H__
+
+#include <windows.h>
+
+namespace rfb {
+
+ namespace win32 {
+
+ struct SessionId {
+ DWORD id;
+ };
+
+ // Session Id for a given process
+ struct ProcessSessionId : SessionId {
+ ProcessSessionId(DWORD processId = -1);
+ };
+
+ // Session Id for current process
+ extern ProcessSessionId mySessionId;
+
+ // Current console Session Id
+ struct ConsoleSessionId : SessionId {
+ ConsoleSessionId();
+ };
+
+ // Check whether the process is in the Console session at present
+ bool inConsoleSession();
+
+ // Make the specified session the Console session.
+ // If sessionId is -1 then the process' session is
+ // made the Console session.
+ void setConsoleSession(DWORD sessionId = -1);
+
+ };
+
+};
+
+#endif
diff --git a/win/rfb_win32/WMCursor.cxx b/win/rfb_win32/WMCursor.cxx
new file mode 100644
index 0000000..4d696cb
--- /dev/null
+++ b/win/rfb_win32/WMCursor.cxx
@@ -0,0 +1,104 @@
+/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
+ *
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+ * USA.
+ */
+
+// -=- WMCursor.cxx
+
+// *** DOESN'T SEEM TO WORK WITH GetCursorInfo POS CODE BUILT-IN UNDER NT4SP6
+// *** INSTEAD, WE LOOK FOR Win2000/Win98 OR ABOVE
+
+#include <rfb_win32/WMCursor.h>
+#include <rfb_win32/OSVersion.h>
+#include <rfb_win32/DynamicFn.h>
+#include <rfb/Exception.h>
+#include <rfb/LogWriter.h>
+
+using namespace rdr;
+using namespace rfb;
+using namespace rfb::win32;
+
+static LogWriter vlog("WMCursor");
+
+
+#ifdef CURSOR_SHOWING
+#define RFB_HAVE_GETCURSORINFO
+#else
+#pragma message(" NOTE: Not building GetCursorInfo support.")
+#endif
+
+#ifdef RFB_HAVE_GETCURSORINFO
+typedef BOOL (WINAPI *_GetCursorInfo_proto)(PCURSORINFO pci);
+DynamicFn<_GetCursorInfo_proto> _GetCursorInfo(_T("user32.dll"), "GetCursorInfo");
+#endif
+
+WMCursor::WMCursor() : hooks(0), use_getCursorInfo(false), cursor(0) {
+#ifdef RFB_HAVE_GETCURSORINFO
+ // Check the OS version
+ bool is_win98 = (osVersion.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS) &&
+ (osVersion.dwMajorVersion > 4) || ((osVersion.dwMajorVersion == 4) && (osVersion.dwMinorVersion > 0));
+ bool is_win2K = (osVersion.dwPlatformId == VER_PLATFORM_WIN32_NT) && (osVersion.dwMajorVersion >= 5);
+
+ // Use GetCursorInfo if OS version is sufficient
+ use_getCursorInfo = (is_win98 || is_win2K) && _GetCursorInfo.isValid();
+#endif
+ cursor = (HCURSOR)LoadImage(0, IDC_ARROW, IMAGE_CURSOR, 0, 0, LR_DEFAULTSIZE | LR_SHARED);
+ if (!use_getCursorInfo) {
+ hooks = new WMCursorHooks();
+ if (hooks && hooks->start()) {
+ vlog.info("falling back to cursor hooking: %p", hooks);
+ } else {
+ delete hooks;
+ hooks = 0;
+ vlog.error("unable to monitor cursor shape");
+ }
+ } else {
+ vlog.info("using GetCursorInfo");
+ }
+}
+
+WMCursor::~WMCursor() {
+ vlog.debug("deleting WMCursorHooks (%p)", hooks);
+ if (hooks)
+ delete hooks;
+}
+
+WMCursor::Info
+WMCursor::getCursorInfo() {
+ Info result;
+#ifdef RFB_HAVE_GETCURSORINFO
+ if (use_getCursorInfo) {
+ CURSORINFO info;
+ info.cbSize = sizeof(CURSORINFO);
+ if ((*_GetCursorInfo)(&info)) {
+ result.cursor = info.hCursor;
+ result.position = Point(info.ptScreenPos.x, info.ptScreenPos.y);
+ result.visible = info.flags & CURSOR_SHOWING;
+ return result;
+ }
+ }
+#endif
+ // Fall back to the old way of doing things
+ POINT pos;
+ if (hooks)
+ cursor = hooks->getCursor();
+ result.cursor = cursor;
+ result.visible = cursor != 0;
+ GetCursorPos(&pos);
+ result.position.x = pos.x;
+ result.position.y = pos.y;
+ return result;
+}
diff --git a/win/rfb_win32/WMCursor.h b/win/rfb_win32/WMCursor.h
new file mode 100644
index 0000000..41f9ee8
--- /dev/null
+++ b/win/rfb_win32/WMCursor.h
@@ -0,0 +1,61 @@
+/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
+ *
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+ * USA.
+ */
+
+// -=- WMCursor.h
+
+// WMCursor provides a single API through which the cursor state can be obtained
+// The underlying implementation will use either GetCursorInfo, or use the
+// wm_hooks library if GetCursorInfo is not available.
+
+#ifndef __RFB_WIN32_WM_CURSOR_H__
+#define __RFB_WIN32_WM_CURSOR_H__
+
+#include <windows.h>
+#include <rfb_win32/WMHooks.h>
+
+namespace rfb {
+ namespace win32 {
+
+ class WMCursor {
+ public:
+ WMCursor();
+ ~WMCursor();
+
+ struct Info {
+ HCURSOR cursor;
+ Point position;
+ bool visible;
+ Info() : cursor(0), visible(false) {}
+ bool operator!=(const Info& info) {
+ return ((cursor != info.cursor) ||
+ (!position.equals(info.position)) ||
+ (visible != info.visible));
+ }
+ };
+
+ Info getCursorInfo();
+ protected:
+ WMCursorHooks* hooks;
+ bool use_getCursorInfo;
+ HCURSOR cursor;
+ };
+
+ };
+};
+
+#endif // __RFB_WIN32_WM_CURSOR_H__
diff --git a/win/rfb_win32/WMHooks.cxx b/win/rfb_win32/WMHooks.cxx
new file mode 100644
index 0000000..2d69053
--- /dev/null
+++ b/win/rfb_win32/WMHooks.cxx
@@ -0,0 +1,394 @@
+/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
+ *
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+ * USA.
+ */
+
+// -=- WMHooks.cxx
+
+#include <rfb_win32/WMHooks.h>
+#include <rfb_win32/DynamicFn.h>
+#include <rfb_win32/Service.h>
+#include <rfb_win32/MsgWindow.h>
+#include <rfb_win32/IntervalTimer.h>
+#include <rfb/Threading.h>
+#include <rfb/LogWriter.h>
+
+#include <list>
+
+using namespace rfb;
+using namespace rfb::win32;
+
+static LogWriter vlog("WMHooks");
+
+
+typedef UINT (*WM_Hooks_WMVAL_proto)();
+typedef BOOL (*WM_Hooks_Install_proto)(DWORD owner, DWORD thread);
+typedef BOOL (*WM_Hooks_Remove_proto)(DWORD owner);
+typedef BOOL (*WM_Hooks_EnableCursorShape_proto)(BOOL enable);
+#ifdef _DEBUG
+typedef void (*WM_Hooks_SetDiagnosticRange_proto)(UINT min, UINT max);
+DynamicFn<WM_Hooks_SetDiagnosticRange_proto> WM_Hooks_SetDiagnosticRange(_T("wm_hooks.dll"), "WM_Hooks_SetDiagnosticRange");
+#endif
+
+
+class WMHooksThread : public Thread {
+public:
+ WMHooksThread() : Thread("WMHookThread"), active(true),
+ WM_Hooks_Install(_T("wm_hooks.dll"), "WM_Hooks_Install"),
+ WM_Hooks_Remove(_T("wm_hooks.dll"), "WM_Hooks_Remove"),
+ WM_Hooks_EnableCursorShape(_T("wm_hooks.dll"), "WM_Hooks_EnableCursorShape") {
+ }
+ virtual void run();
+ virtual Thread* join();
+ DynamicFn<WM_Hooks_Install_proto> WM_Hooks_Install;;
+ DynamicFn<WM_Hooks_Remove_proto> WM_Hooks_Remove;
+ DynamicFn<WM_Hooks_EnableCursorShape_proto> WM_Hooks_EnableCursorShape;
+protected:
+ bool active;
+};
+
+WMHooksThread* hook_mgr = 0;
+std::list<WMHooks*> hooks;
+std::list<WMCursorHooks*> cursor_hooks;
+Mutex hook_mgr_lock;
+HCURSOR hook_cursor = (HCURSOR)LoadImage(0, IDC_ARROW, IMAGE_CURSOR, 0, 0, LR_DEFAULTSIZE | LR_SHARED);
+
+
+static bool StartHookThread() {
+ if (hook_mgr)
+ return true;
+ vlog.debug("creating thread");
+ hook_mgr = new WMHooksThread();
+ if (!hook_mgr->WM_Hooks_Install.isValid() ||
+ !hook_mgr->WM_Hooks_Remove.isValid()) {
+ vlog.debug("hooks not available");
+ return false;
+ }
+ vlog.debug("installing hooks");
+ if (!(*hook_mgr->WM_Hooks_Install)(hook_mgr->getThreadId(), 0)) {
+ vlog.error("failed to initialise hooks");
+ delete hook_mgr->join();
+ hook_mgr = 0;
+ return false;
+ }
+ vlog.debug("starting thread");
+ hook_mgr->start();
+ return true;
+}
+
+static void StopHookThread() {
+ if (!hook_mgr)
+ return;
+ if (!hooks.empty() || !cursor_hooks.empty())
+ return;
+ vlog.debug("closing thread");
+ delete hook_mgr->join();
+ hook_mgr = 0;
+}
+
+
+static bool AddHook(WMHooks* hook) {
+ vlog.debug("adding hook");
+ Lock l(hook_mgr_lock);
+ if (!StartHookThread())
+ return false;
+ hooks.push_back(hook);
+ return true;
+}
+
+static bool AddCursorHook(WMCursorHooks* hook) {
+ vlog.debug("adding cursor hook");
+ Lock l(hook_mgr_lock);
+ if (!StartHookThread())
+ return false;
+ if (!hook_mgr->WM_Hooks_EnableCursorShape.isValid())
+ return false;
+ if (cursor_hooks.empty() && !(*hook_mgr->WM_Hooks_EnableCursorShape)(TRUE))
+ return false;
+ cursor_hooks.push_back(hook);
+ return true;
+}
+
+static bool RemHook(WMHooks* hook) {
+ {
+ vlog.debug("removing hook");
+ Lock l(hook_mgr_lock);
+ hooks.remove(hook);
+ }
+ StopHookThread();
+ return true;
+}
+
+static bool RemCursorHook(WMCursorHooks* hook) {
+ {
+ vlog.debug("removing cursor hook");
+ Lock l(hook_mgr_lock);
+ cursor_hooks.remove(hook);
+ if (hook_mgr->WM_Hooks_EnableCursorShape.isValid() &&
+ cursor_hooks.empty())
+ (*hook_mgr->WM_Hooks_EnableCursorShape)(FALSE);
+ }
+ StopHookThread();
+ return true;
+}
+
+static void NotifyHooksRegion(const Region& r) {
+ Lock l(hook_mgr_lock);
+ std::list<WMHooks*>::iterator i;
+ for (i=hooks.begin(); i!=hooks.end(); i++)
+ (*i)->NotifyHooksRegion(r);
+}
+
+static void NotifyHooksCursor(HCURSOR c) {
+ Lock l(hook_mgr_lock);
+ hook_cursor = c;
+}
+
+
+static UINT GetMsgVal(DynamicFn<WM_Hooks_WMVAL_proto>& fn) {
+ if (fn.isValid())
+ return (*fn)();
+ return WM_NULL;
+}
+
+void
+WMHooksThread::run() {
+ // Obtain message ids for all supported hook messages
+ DynamicFn<WM_Hooks_WMVAL_proto> WM_Hooks_WindowChanged(_T("wm_hooks.dll"), "WM_Hooks_WindowChanged");
+ DynamicFn<WM_Hooks_WMVAL_proto> WM_Hooks_WindowBorderChanged(_T("wm_hooks.dll"), "WM_Hooks_WindowBorderChanged");
+ DynamicFn<WM_Hooks_WMVAL_proto> WM_Hooks_WindowClientAreaChanged(_T("wm_hooks.dll"), "WM_Hooks_WindowClientAreaChanged");
+ DynamicFn<WM_Hooks_WMVAL_proto> WM_Hooks_RectangleChanged(_T("wm_hooks.dll"), "WM_Hooks_RectangleChanged");
+ DynamicFn<WM_Hooks_WMVAL_proto> WM_Hooks_CursorChanged(_T("wm_hooks.dll"), "WM_Hooks_CursorChanged");
+ UINT windowMsg = GetMsgVal(WM_Hooks_WindowChanged);
+ UINT clientAreaMsg = GetMsgVal(WM_Hooks_WindowClientAreaChanged);
+ UINT borderMsg = GetMsgVal(WM_Hooks_WindowBorderChanged);
+ UINT rectangleMsg = GetMsgVal(WM_Hooks_RectangleChanged);
+ UINT cursorMsg = GetMsgVal(WM_Hooks_CursorChanged);
+#ifdef _DEBUG
+ DynamicFn<WM_Hooks_WMVAL_proto> WM_Hooks_Diagnostic(_T("wm_hooks.dll"), "WM_Hooks_Diagnostic");
+ UINT diagnosticMsg = GetMsgVal(WM_Hooks_Diagnostic);
+#endif
+ MSG msg;
+ RECT wrect;
+ HWND hwnd;
+ int count = 0;
+
+ // Update delay handling
+ // We delay updates by 40-80ms, so that the triggering application has time to
+ // actually complete them before we notify the hook callbacks & they go off
+ // capturing screen state.
+ const int updateDelayMs = 40;
+ MsgWindow updateDelayWnd(_T("WMHooks::updateDelay"));
+ IntervalTimer updateDelayTimer(updateDelayWnd.getHandle(), 1);
+ Region updates[2];
+ int activeRgn = 0;
+
+ vlog.debug("starting hook thread");
+
+ while (active && GetMessage(&msg, NULL, 0, 0)) {
+ count++;
+
+ if (msg.message == WM_TIMER) {
+ // Actually notify callbacks of graphical updates
+ NotifyHooksRegion(updates[1-activeRgn]);
+ if (updates[activeRgn].is_empty())
+ updateDelayTimer.stop();
+ activeRgn = 1-activeRgn;
+ updates[activeRgn].clear();
+
+ } else if (msg.message == windowMsg) {
+ // An entire window has (potentially) changed
+ hwnd = (HWND) msg.lParam;
+ if (IsWindow(hwnd) && IsWindowVisible(hwnd) && !IsIconic(hwnd) &&
+ GetWindowRect(hwnd, &wrect) && !IsRectEmpty(&wrect)) {
+ updates[activeRgn].assign_union(Rect(wrect.left, wrect.top,
+ wrect.right, wrect.bottom));
+ updateDelayTimer.start(updateDelayMs);
+ }
+
+ } else if (msg.message == clientAreaMsg) {
+ // The client area of a window has (potentially) changed
+ hwnd = (HWND) msg.lParam;
+ if (IsWindow(hwnd) && IsWindowVisible(hwnd) && !IsIconic(hwnd) &&
+ GetClientRect(hwnd, &wrect) && !IsRectEmpty(&wrect))
+ {
+ POINT pt = {0,0};
+ if (ClientToScreen(hwnd, &pt)) {
+ updates[activeRgn].assign_union(Rect(wrect.left+pt.x, wrect.top+pt.y,
+ wrect.right+pt.x, wrect.bottom+pt.y));
+ updateDelayTimer.start(updateDelayMs);
+ }
+ }
+
+ } else if (msg.message == borderMsg) {
+ hwnd = (HWND) msg.lParam;
+ if (IsWindow(hwnd) && IsWindowVisible(hwnd) && !IsIconic(hwnd) &&
+ GetWindowRect(hwnd, &wrect) && !IsRectEmpty(&wrect))
+ {
+ Region changed(Rect(wrect.left, wrect.top, wrect.right, wrect.bottom));
+ RECT crect;
+ POINT pt = {0,0};
+ if (GetClientRect(hwnd, &crect) && ClientToScreen(hwnd, &pt) &&
+ !IsRectEmpty(&crect))
+ {
+ changed.assign_subtract(Rect(crect.left+pt.x, crect.top+pt.y,
+ crect.right+pt.x, crect.bottom+pt.y));
+ }
+ if (!changed.is_empty()) {
+ updates[activeRgn].assign_union(changed);
+ updateDelayTimer.start(updateDelayMs);
+ }
+ }
+ } else if (msg.message == rectangleMsg) {
+ Rect r = Rect(LOWORD(msg.wParam), HIWORD(msg.wParam),
+ LOWORD(msg.lParam), HIWORD(msg.lParam));
+ if (!r.is_empty()) {
+ updates[activeRgn].assign_union(r);
+ updateDelayTimer.start(updateDelayMs);
+ }
+
+ } else if (msg.message == cursorMsg) {
+ NotifyHooksCursor((HCURSOR)msg.lParam);
+#ifdef _DEBUG
+ } else if (msg.message == diagnosticMsg) {
+ vlog.info("DIAG msg=%x(%d) wnd=%lx", msg.wParam, msg.wParam, msg.lParam);
+#endif
+ }
+ }
+
+ vlog.debug("stopping hook thread - processed %d events", count);
+ (*WM_Hooks_Remove)(getThreadId());
+}
+
+Thread*
+WMHooksThread::join() {
+ vlog.debug("stopping WMHooks thread");
+ active = false;
+ PostThreadMessage(thread_id, WM_QUIT, 0, 0);
+ vlog.debug("joining WMHooks thread");
+ return Thread::join();
+}
+
+// -=- WMHooks class
+
+rfb::win32::WMHooks::WMHooks() : updateEvent(0) {
+}
+
+rfb::win32::WMHooks::~WMHooks() {
+ RemHook(this);
+}
+
+bool rfb::win32::WMHooks::setEvent(HANDLE ue) {
+ if (updateEvent)
+ RemHook(this);
+ updateEvent = ue;
+ return AddHook(this);
+}
+
+bool rfb::win32::WMHooks::getUpdates(UpdateTracker* ut) {
+ if (!updatesReady) return false;
+ Lock l(hook_mgr_lock);
+ updates.copyTo(ut);
+ updates.clear();
+ updatesReady = false;
+ return true;
+}
+
+bool rfb::win32::WMHooks::areAvailable() {
+ WMHooksThread wmht;
+ return wmht.WM_Hooks_Install.isValid();
+}
+
+#ifdef _DEBUG
+void
+rfb::win32::WMHooks::setDiagnosticRange(UINT min, UINT max) {
+ if (WM_Hooks_SetDiagnosticRange.isValid())
+ (*WM_Hooks_SetDiagnosticRange)(min, max);
+}
+#endif
+
+void rfb::win32::WMHooks::NotifyHooksRegion(const Region& r) {
+ // hook_mgr_lock is already held at this point
+ updates.add_changed(r);
+ updatesReady = true;
+ SetEvent(updateEvent);
+}
+
+
+// -=- WMBlockInput class
+
+rfb::win32::WMBlockInput::WMBlockInput() : active(false) {
+}
+
+rfb::win32::WMBlockInput::~WMBlockInput() {
+ blockInputs(false);
+}
+
+typedef BOOL (*WM_Hooks_EnableRealInputs_proto)(BOOL pointer, BOOL keyboard);
+DynamicFn<WM_Hooks_EnableRealInputs_proto>* WM_Hooks_EnableRealInputs = 0;
+static bool blockRealInputs(bool block_) {
+ // NB: Requires blockMutex to be held!
+ if (block_) {
+ if (WM_Hooks_EnableRealInputs)
+ return true;
+ // Enable blocking
+ WM_Hooks_EnableRealInputs = new DynamicFn<WM_Hooks_EnableRealInputs_proto>(_T("wm_hooks.dll"), "WM_Hooks_EnableRealInputs");
+ if (WM_Hooks_EnableRealInputs->isValid() && (**WM_Hooks_EnableRealInputs)(false, false))
+ return true;
+ }
+ if (WM_Hooks_EnableRealInputs) {
+ // Clean up the DynamicFn, either if init failed, or block_ is false
+ if (WM_Hooks_EnableRealInputs->isValid())
+ (**WM_Hooks_EnableRealInputs)(true, true);
+ delete WM_Hooks_EnableRealInputs;
+ WM_Hooks_EnableRealInputs = 0;
+ }
+ return block_ == (WM_Hooks_EnableRealInputs != 0);
+}
+
+Mutex blockMutex;
+int blockCount = 0;
+
+bool rfb::win32::WMBlockInput::blockInputs(bool on) {
+ if (active == on) return true;
+ Lock l(blockMutex);
+ int newCount = on ? blockCount+1 : blockCount-1;
+ if (!blockRealInputs(newCount > 0))
+ return false;
+ blockCount = newCount;
+ active = on;
+ return true;
+}
+
+
+// -=- WMCursorHooks class
+
+rfb::win32::WMCursorHooks::WMCursorHooks() {
+}
+
+rfb::win32::WMCursorHooks::~WMCursorHooks() {
+ RemCursorHook(this);
+}
+
+bool
+rfb::win32::WMCursorHooks::start() {
+ return AddCursorHook(this);
+}
+
+HCURSOR
+rfb::win32::WMCursorHooks::getCursor() const {
+ return hook_cursor;
+}
diff --git a/win/rfb_win32/WMHooks.h b/win/rfb_win32/WMHooks.h
new file mode 100644
index 0000000..4713b41
--- /dev/null
+++ b/win/rfb_win32/WMHooks.h
@@ -0,0 +1,92 @@
+/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
+ *
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+ * USA.
+ */
+
+// -=- WMHooks.h
+
+#ifndef __RFB_WIN32_WM_HOOKS_H__
+#define __RFB_WIN32_WM_HOOKS_H__
+
+#include <windows.h>
+#include <rfb/UpdateTracker.h>
+#include <rdr/Exception.h>
+#include <rfb_win32/Win32Util.h>
+
+namespace rfb {
+
+ namespace win32 {
+
+ // -=- WMHooks
+ // Uses the wm_hooks DLL to intercept window messages, to get _hints_ as
+ // to what may have changed on-screen. Updates are notified via a Win32
+ // event, and retrieved using the getUpdates method, which is thread-safe.
+ class WMHooks {
+ public:
+ WMHooks();
+ ~WMHooks();
+
+ // Specify the event object to notify. Starts the hook subsystem if it is
+ // not already active, and returns false if the hooks fail to start.
+ bool setEvent(HANDLE updateEvent);
+
+ // Copies any new updates to the UpdateTracker. Returns true if new updates
+ // were added, false otherwise.
+ bool getUpdates(UpdateTracker* ut);
+
+ // Determine whether the hooks DLL is installed on the system
+ static bool areAvailable();
+
+#ifdef _DEBUG
+ // Get notifications of any messages in the given range, to any hooked window
+ void setDiagnosticRange(UINT min, UINT max);
+#endif
+
+ // * INTERNAL NOTIFICATION FUNCTION *
+ void NotifyHooksRegion(const Region& r);
+ protected:
+ HANDLE updateEvent;
+ bool updatesReady;
+ SimpleUpdateTracker updates;
+ };
+
+ // -=- Support for filtering out local input events while remote connections are
+ // active. Implemented using SetWindowsHookEx for portability.
+ class WMBlockInput {
+ public:
+ WMBlockInput();
+ ~WMBlockInput();
+ bool blockInputs(bool block);
+ protected:
+ bool active;
+ };
+
+ // - Legacy cursor handling support
+ class WMCursorHooks {
+ public:
+ WMCursorHooks();
+ ~WMCursorHooks();
+
+ bool start();
+
+ HCURSOR getCursor() const;
+ };
+
+ };
+
+};
+
+#endif // __RFB_WIN32_WM_HOOKS_H__
diff --git a/win/rfb_win32/WMNotifier.cxx b/win/rfb_win32/WMNotifier.cxx
new file mode 100644
index 0000000..20a5445
--- /dev/null
+++ b/win/rfb_win32/WMNotifier.cxx
@@ -0,0 +1,57 @@
+/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
+ *
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+ * USA.
+ */
+
+// -=- WMNotifier.cxx
+
+#include <rfb_win32/WMNotifier.h>
+#include <rfb_win32/WMShatter.h>
+#include <rfb_win32/MsgWindow.h>
+
+#include <rfb/LogWriter.h>
+
+using namespace rfb;
+using namespace rfb::win32;
+
+static LogWriter vlog("WMMonitor");
+
+
+WMMonitor::WMMonitor() : MsgWindow(_T("WMMonitor")), notifier(0) {
+}
+
+WMMonitor::~WMMonitor() {
+}
+
+
+LRESULT
+WMMonitor::processMessage(UINT msg, WPARAM wParam, LPARAM lParam) {
+ switch (msg) {
+ case WM_DISPLAYCHANGE:
+ if (notifier) {
+ notifier->notifyDisplayEvent(Notifier::DisplaySizeChanged);
+ notifier->notifyDisplayEvent(Notifier::DisplayPixelFormatChanged);
+ }
+ break;
+ case WM_SYSCOLORCHANGE:
+ case WM_PALETTECHANGED:
+ if (notifier) {
+ notifier->notifyDisplayEvent(Notifier::DisplayColourMapChanged);
+ }
+ break;
+ };
+ return MsgWindow::processMessage(msg, wParam, lParam);
+}
diff --git a/win/rfb_win32/WMNotifier.h b/win/rfb_win32/WMNotifier.h
new file mode 100644
index 0000000..a760964
--- /dev/null
+++ b/win/rfb_win32/WMNotifier.h
@@ -0,0 +1,68 @@
+/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
+ *
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+ * USA.
+ */
+
+// -=- WMNotifier.h
+//
+// The WMNotifier is used to get callbacks indicating changes in the state
+// of the system, for instance in the size/format/palette of the display.
+// The WMNotifier contains a Win32 window, which receives notifications of
+// system events and stores them. Whenever processEvent is called, any
+// incoming events are processed and the appropriate notifier called.
+
+#ifndef __RFB_WIN32_NOTIFIER_H__
+#define __RFB_WIN32_NOTIFIER_H__
+
+#include <rfb/SDesktop.h>
+#include <rfb/Threading.h>
+#include <rfb_win32/MsgWindow.h>
+#include <rfb_win32/DeviceFrameBuffer.h>
+#include <rfb_win32/SInput.h>
+
+namespace rfb {
+
+ namespace win32 {
+
+ // -=- Window Message Monitor implementation
+
+ class WMMonitor : MsgWindow {
+ public:
+
+ class Notifier {
+ public:
+ typedef enum {DisplaySizeChanged, DisplayColourMapChanged,
+ DisplayPixelFormatChanged} DisplayEventType;
+ virtual void notifyDisplayEvent(DisplayEventType evt) = 0;
+ };
+
+ WMMonitor();
+ virtual ~WMMonitor();
+
+ void setNotifier(Notifier* wmn) {notifier=wmn;}
+
+ protected:
+ // - Internal MsgWindow callback
+ virtual LRESULT processMessage(UINT msg, WPARAM wParam, LPARAM lParam);
+
+ Notifier* notifier;
+ };
+
+ };
+
+};
+
+#endif // __RFB_WIN32_WMNOTIFIER_H__
diff --git a/win/rfb_win32/WMPoller.cxx b/win/rfb_win32/WMPoller.cxx
new file mode 100644
index 0000000..f850534
--- /dev/null
+++ b/win/rfb_win32/WMPoller.cxx
@@ -0,0 +1,85 @@
+/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
+ *
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+ * USA.
+ */
+
+// -=- WMPoller.cxx
+
+#include <rfb_win32/WMPoller.h>
+#include <rfb/Exception.h>
+#include <rfb/LogWriter.h>
+#include <rfb/Configuration.h>
+
+#include <tchar.h>
+
+using namespace rfb;
+using namespace rfb::win32;
+
+static LogWriter vlog("WMPoller");
+
+BoolParameter rfb::win32::WMPoller::poll_console_windows("PollConsoleWindows",
+ "Server should poll console windows for updates", true);
+
+// -=- WMPoller class
+
+bool
+rfb::win32::WMPoller::processEvent() {
+ PollInfo info;
+ if (poll_console_windows && ut) {
+ ::EnumWindows(WMPoller::enumWindowProc, (LPARAM) &info);
+ ut->add_changed(info.poll_include);
+ }
+ return false;
+}
+
+bool
+rfb::win32::WMPoller::setUpdateTracker(UpdateTracker* ut_) {
+ ut = ut_;
+ return true;
+}
+
+bool
+rfb::win32::WMPoller::checkPollWindow(HWND w) {
+ TCHAR buffer[128];
+ if (!GetClassName(w, buffer, 128))
+ throw rdr::SystemException("unable to get window class:%u", GetLastError());
+ if ((_tcscmp(buffer, _T("tty")) != 0) &&
+ (_tcscmp(buffer, _T("ConsoleWindowClass")) != 0)) {
+ return false;
+ }
+ return true;
+}
+
+void
+rfb::win32::WMPoller::pollWindow(HWND w, PollInfo* i) {
+ RECT r;
+ if (IsWindowVisible(w) && GetWindowRect(w, &r)) {
+ if (IsRectEmpty(&r)) return;
+ Region wrgn(Rect(r.left, r.top, r.right, r.bottom));
+ if (checkPollWindow(w)) {
+ wrgn.assign_subtract(i->poll_exclude);
+ i->poll_include.assign_union(wrgn);
+ } else {
+ i->poll_exclude.assign_union(wrgn);
+ }
+ }
+}
+
+BOOL CALLBACK
+rfb::win32::WMPoller::enumWindowProc(HWND w, LPARAM lp) {
+ pollWindow(w, (PollInfo*)lp);
+ return TRUE;
+}
diff --git a/win/rfb_win32/WMPoller.h b/win/rfb_win32/WMPoller.h
new file mode 100644
index 0000000..851b69f
--- /dev/null
+++ b/win/rfb_win32/WMPoller.h
@@ -0,0 +1,62 @@
+/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
+ *
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+ * USA.
+ */
+
+// -=- WMPoller.h
+//
+// Polls the foreground window. If the pollOnlyConsoles flag is set,
+// then checks the window class of the foreground window first and
+// only polls it if it's a console.
+// If the pollAllWindows flag is set then iterates through visible
+// windows, and polls the visible bits. If pollOnlyConsoles is also
+// set then only visible parts of console windows will be polled.
+
+#ifndef __RFB_WIN32_WM_POLLER_H__
+#define __RFB_WIN32_WM_POLLER_H__
+
+#include <windows.h>
+#include <rfb/UpdateTracker.h>
+#include <rfb/Configuration.h>
+
+namespace rfb {
+
+ namespace win32 {
+
+ class WMPoller {
+ public:
+ WMPoller() : ut(0) {}
+
+ bool processEvent();
+ bool setUpdateTracker(UpdateTracker* ut);
+
+ static BoolParameter poll_console_windows;
+ protected:
+ struct PollInfo {
+ Region poll_include;
+ Region poll_exclude;
+ };
+ static bool checkPollWindow(HWND w);
+ static void pollWindow(HWND w, PollInfo* info);
+ static BOOL CALLBACK enumWindowProc(HWND w, LPARAM lp);
+ UpdateTracker* ut;
+ };
+
+ };
+
+};
+
+#endif // __RFB_WIN32_WM_POLLER_H__
diff --git a/win/rfb_win32/WMShatter.cxx b/win/rfb_win32/WMShatter.cxx
new file mode 100644
index 0000000..e68abfb
--- /dev/null
+++ b/win/rfb_win32/WMShatter.cxx
@@ -0,0 +1,57 @@
+/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
+ *
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+ * USA.
+ */
+
+// -=- WMShatter.cxx
+
+#include <rfb_win32/WMShatter.h>
+
+#include <rfb/LogWriter.h>
+
+using namespace rfb;
+using namespace rfb::win32;
+
+static LogWriter vlog("WMShatter");
+
+bool
+rfb::win32::IsSafeWM(HWND window, UINT msg, WPARAM wParam, LPARAM lParam) {
+ bool result = true;
+ switch (msg) {
+ // - UNSAFE MESSAGES
+ case WM_TIMER:
+ result = lParam == 0;
+ break;
+ };
+ if (!result) {
+ vlog.info("IsSafeWM: 0x%x received 0x%x(%u, %lu) - not safe", window, msg, wParam, lParam);
+ }
+ return result;
+}
+
+LRESULT
+rfb::win32::SafeDefWindowProc(HWND window, UINT msg, WPARAM wParam, LPARAM lParam) {
+ if (IsSafeWM(window, msg, wParam, lParam))
+ return DefWindowProc(window, msg, wParam, lParam);
+ return 0;
+}
+
+LRESULT
+rfb::win32::SafeDispatchMessage(const MSG* msg) {
+ if (IsSafeWM(msg->hwnd, msg->message, msg->wParam, msg->lParam))
+ return DispatchMessage(msg);
+ return 0;
+}
diff --git a/win/rfb_win32/WMShatter.h b/win/rfb_win32/WMShatter.h
new file mode 100644
index 0000000..3ea63b1
--- /dev/null
+++ b/win/rfb_win32/WMShatter.h
@@ -0,0 +1,50 @@
+/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
+ *
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+ * USA.
+ */
+
+// -=- WMShatter.h
+//
+// WMShatter provides the IsSafeWM routine, which returns true iff the
+// supplied window message is safe to pass to DispatchMessage, or to
+// process in the window procedure.
+//
+// This is only required, of course, to avoid so-called "shatter" attacks
+// to be made against the VNC server, which take advantage of the noddy
+// design of the Win32 window messaging system.
+//
+// The API here is designed to hopefully be future proof, so that if they
+// ever come up with a proper way to determine whether a message is safe
+// or not then it can just be reimplemented here...
+
+#ifndef __RFB_WIN32_SHATTER_H__
+#define __RFB_WIN32_SHATTER_H__
+
+#include <windows.h>
+
+namespace rfb {
+ namespace win32 {
+
+ bool IsSafeWM(HWND window, UINT msg, WPARAM wParam, LPARAM lParam);
+
+ LRESULT SafeDefWindowProc(HWND wnd, UINT msg, WPARAM wParam, LPARAM lParam);
+
+ LRESULT SafeDispatchMessage(const MSG* msg);
+
+ };
+};
+
+#endif // __RFB_WIN32_SHATTER_H__
diff --git a/win/rfb_win32/WMWindowCopyRect.cxx b/win/rfb_win32/WMWindowCopyRect.cxx
new file mode 100644
index 0000000..63c1da2
--- /dev/null
+++ b/win/rfb_win32/WMWindowCopyRect.cxx
@@ -0,0 +1,67 @@
+/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
+ *
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+ * USA.
+ */
+
+// -=- WMCopyRect.cxx
+
+#include <rfb_win32/WMWindowCopyRect.h>
+#include <rfb/LogWriter.h>
+#include <windows.h>
+
+using namespace rfb;
+using namespace rfb::win32;
+
+static LogWriter vlog("WMCopyRect");
+
+// -=- WMHooks class
+
+rfb::win32::WMCopyRect::WMCopyRect() : ut(0), fg_window(0) {
+}
+
+bool
+rfb::win32::WMCopyRect::processEvent() {
+ // See if the foreground window has moved
+ HWND window = GetForegroundWindow();
+ if (window) {
+ RECT wrect;
+ if (IsWindow(window) && IsWindowVisible(window) && GetWindowRect(window, &wrect)) {
+ Rect winrect(wrect.left, wrect.top, wrect.right, wrect.bottom);
+ if (fg_window == window) {
+ if (!fg_window_rect.tl.equals(winrect.tl) && ut) {
+ // Window has moved - send a copyrect event to the client
+ Point delta = Point(winrect.tl.x-fg_window_rect.tl.x, winrect.tl.y-fg_window_rect.tl.y);
+ Region copy_dest = winrect;
+ ut->add_copied(copy_dest, delta);
+ ut->add_changed(Region(fg_window_rect).subtract(copy_dest));
+ }
+ }
+ fg_window = window;
+ fg_window_rect = winrect;
+ } else {
+ fg_window = 0;
+ }
+ } else {
+ fg_window = 0;
+ }
+ return false;
+}
+
+bool
+rfb::win32::WMCopyRect::setUpdateTracker(UpdateTracker* ut_) {
+ ut = ut_;
+ return true;
+}
diff --git a/win/rfb_win32/WMWindowCopyRect.h b/win/rfb_win32/WMWindowCopyRect.h
new file mode 100644
index 0000000..5a0e876
--- /dev/null
+++ b/win/rfb_win32/WMWindowCopyRect.h
@@ -0,0 +1,53 @@
+/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
+ *
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+ * USA.
+ */
+
+// -=- WMWindowCopyRect.h
+//
+// Helper class which produces copyRect actions by monitoring the location
+// of the current foreground window.
+// Whenever processEvent is called, the foreground window's position is
+// recalculated and a copy event flushed to the supplied UpdateTracker
+// if appropriate.
+
+#ifndef __RFB_WIN32_WM_WINDOW_COPYRECT_H__
+#define __RFB_WIN32_WM_WINDOW_COPYRECT_H__
+
+#include <rfb/UpdateTracker.h>
+
+namespace rfb {
+
+ namespace win32 {
+
+ class WMCopyRect {
+ public:
+ WMCopyRect();
+
+ bool processEvent();
+ bool setUpdateTracker(UpdateTracker* ut);
+
+ protected:
+ UpdateTracker* ut;
+ void* fg_window;
+ Rect fg_window_rect;
+ };
+
+ };
+
+};
+
+#endif // __RFB_WIN32_WM_WINDOW_COPYRECT_H__
diff --git a/win/rfb_win32/Win32Util.cxx b/win/rfb_win32/Win32Util.cxx
new file mode 100644
index 0000000..ef8039a
--- /dev/null
+++ b/win/rfb_win32/Win32Util.cxx
@@ -0,0 +1,114 @@
+/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
+ *
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+ * USA.
+ */
+
+// Win32Util.cxx
+
+#include <rfb_win32/ModuleFileName.h>
+#include <rfb_win32/Win32Util.h>
+#include <rfb_win32/MonitorInfo.h>
+#include <rfb_win32/Handle.h>
+#include <rdr/HexOutStream.h>
+#include <rdr/Exception.h>
+
+namespace rfb {
+namespace win32 {
+
+
+FileVersionInfo::FileVersionInfo(const TCHAR* filename) {
+ // Get executable name
+ ModuleFileName exeName;
+ if (!filename)
+ filename = exeName.buf;
+
+ // Attempt to open the file, to cause Access Denied, etc, errors
+ // to be correctly reported, since the GetFileVersionInfoXXX calls lie...
+ {
+ Handle file(CreateFile(filename, GENERIC_READ, FILE_SHARE_READ, 0, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0));
+ if (file.h == INVALID_HANDLE_VALUE)
+ throw rdr::SystemException("Failed to open file", GetLastError());
+ }
+
+ // Get version info size
+ DWORD handle;
+ int size = GetFileVersionInfoSize((TCHAR*)filename, &handle);
+ if (!size)
+ throw rdr::SystemException("GetVersionInfoSize failed", GetLastError());
+
+ // Get version info
+ buf = new TCHAR[size];
+ if (!GetFileVersionInfo((TCHAR*)filename, handle, size, buf))
+ throw rdr::SystemException("GetVersionInfo failed", GetLastError());
+}
+
+const TCHAR* FileVersionInfo::getVerString(const TCHAR* name, DWORD langId) {
+ char langIdBuf[sizeof(langId)];
+ for (int i=sizeof(langIdBuf)-1; i>=0; i--) {
+ langIdBuf[i] = (char) (langId & 0xff);
+ langId = langId >> 8;
+ }
+
+ TCharArray langIdStr = rdr::HexOutStream::binToHexStr(langIdBuf, sizeof(langId));
+ TCharArray infoName(_tcslen(_T("StringFileInfo")) + 4 + _tcslen(name) + _tcslen(langIdStr.buf));
+ _stprintf(infoName.buf, _T("\\StringFileInfo\\%s\\%s"), langIdStr.buf, name);
+
+ // Locate the required version string within the version info
+ TCHAR* buffer = 0;
+ UINT length = 0;
+ if (!VerQueryValue(buf, infoName.buf, (void**)&buffer, &length)) {
+ printf("unable to find %s version string", CStr(infoName.buf));
+ throw rdr::Exception("VerQueryValue failed");
+ }
+ return buffer;
+}
+
+
+bool splitPath(const TCHAR* path, TCHAR** dir, TCHAR** file) {
+ return tstrSplit(path, '\\', dir, file, true);
+}
+
+
+void centerWindow(HWND handle, HWND parent) {
+ RECT r;
+ MonitorInfo mi(parent ? parent : handle);
+ if (!parent || !IsWindowVisible(parent) || !GetWindowRect(parent, &r))
+ r=mi.rcWork;
+ centerWindow(handle, r);
+ mi.clipTo(handle);
+}
+
+void centerWindow(HWND handle, const RECT& r) {
+ RECT wr;
+ if (!GetWindowRect(handle, &wr)) return;
+ int w = wr.right-wr.left;
+ int h = wr.bottom-wr.top;
+ int x = (r.left + r.right - w)/2;
+ int y = (r.top + r.bottom - h)/2;
+ UINT flags = SWP_NOACTIVATE | SWP_NOOWNERZORDER | SWP_NOZORDER | SWP_NOSIZE;
+ SetWindowPos(handle, 0, x, y, 0, 0, flags);
+}
+
+void resizeWindow(HWND handle, int width, int height) {
+ RECT r;
+ GetWindowRect(handle, &r);
+ SetWindowPos(handle, 0, 0, 0, width, height, SWP_NOACTIVATE | SWP_NOOWNERZORDER | SWP_NOZORDER | SWP_NOMOVE);
+ centerWindow(handle, r);
+}
+
+
+};
+};
diff --git a/win/rfb_win32/Win32Util.h b/win/rfb_win32/Win32Util.h
new file mode 100644
index 0000000..8cc1a2b
--- /dev/null
+++ b/win/rfb_win32/Win32Util.h
@@ -0,0 +1,55 @@
+/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
+ *
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+ * USA.
+ */
+
+// -=- Win32Util.h
+
+// Miscellaneous but useful Win32 API utility functions & classes.
+// In particular, a set of classes which wrap GDI objects,
+// and some to handle palettes.
+
+#ifndef __RFB_WIN32_GDIUTIL_H__
+#define __RFB_WIN32_GDIUTIL_H__
+
+#include <rfb_win32/TCharArray.h>
+
+namespace rfb {
+
+ namespace win32 {
+
+ struct FileVersionInfo : public TCharArray {
+ FileVersionInfo(const TCHAR* filename=0);
+ const TCHAR* getVerString(const TCHAR* name, DWORD langId = 0x080904b0);
+ };
+
+ bool splitPath(const TCHAR* path, TCHAR** dir, TCHAR** file);
+
+ // Center the window to a rectangle, or to a parent window.
+ // Optionally, resize the window to lay within the rect or parent window
+ // If the parent window is NULL then the working area if the window's
+ // current monitor is used instead.
+ void centerWindow(HWND handle, const RECT& r);
+ void centerWindow(HWND handle, HWND parent);
+
+ // resizeWindow resizes a window about its center
+ void resizeWindow(HWND handle, int width, int height);
+
+ };
+
+};
+
+#endif
diff --git a/win/rfb_win32/keymap.h b/win/rfb_win32/keymap.h
new file mode 100644
index 0000000..a340d09
--- /dev/null
+++ b/win/rfb_win32/keymap.h
@@ -0,0 +1,149 @@
+/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
+ *
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+ * USA.
+ */
+
+// keymap.h - this file is shared between SInput.cxx and CKeyboard.cxx
+//
+// Mapping of X keysyms to and from Windows VK codes. Ordering here must be
+// such that when we look up a Windows VK code we get the preferred X keysym.
+// Going the other way there is no problem because an X keysym always maps to
+// exactly one Windows VK code. This map only contain keys which are not the
+// normal keys for printable ASCII characters. For example it does not contain
+// VK_SPACE (note that things like VK_ADD are for the plus key on the keypad,
+// not on the main keyboard).
+
+struct keymap_t {
+ rdr::U32 keysym;
+ rdr::U8 vk;
+ bool extended;
+};
+
+static keymap_t keymap[] = {
+
+ { XK_BackSpace, VK_BACK, 0 },
+ { XK_Tab, VK_TAB, 0 },
+ { XK_Clear, VK_CLEAR, 0 },
+ { XK_Return, VK_RETURN, 0 },
+ { XK_Pause, VK_PAUSE, 0 },
+ { XK_Escape, VK_ESCAPE, 0 },
+ { XK_Delete, VK_DELETE, 1 },
+
+ // Cursor control & motion
+
+ { XK_Home, VK_HOME, 1 },
+ { XK_Left, VK_LEFT, 1 },
+ { XK_Up, VK_UP, 1 },
+ { XK_Right, VK_RIGHT, 1 },
+ { XK_Down, VK_DOWN, 1 },
+ { XK_Page_Up, VK_PRIOR, 1 },
+ { XK_Page_Down, VK_NEXT, 1 },
+ { XK_End, VK_END, 1 },
+
+ // Misc functions
+
+ { XK_Select, VK_SELECT, 0 },
+ { XK_Print, VK_SNAPSHOT, 0 },
+ { XK_Execute, VK_EXECUTE, 0 },
+ { XK_Insert, VK_INSERT, 1 },
+ { XK_Help, VK_HELP, 0 },
+ { XK_Break, VK_CANCEL, 1 },
+
+ // Auxilliary Functions - must come before XK_KP_F1, etc
+
+ { XK_F1, VK_F1, 0 },
+ { XK_F2, VK_F2, 0 },
+ { XK_F3, VK_F3, 0 },
+ { XK_F4, VK_F4, 0 },
+ { XK_F5, VK_F5, 0 },
+ { XK_F6, VK_F6, 0 },
+ { XK_F7, VK_F7, 0 },
+ { XK_F8, VK_F8, 0 },
+ { XK_F9, VK_F9, 0 },
+ { XK_F10, VK_F10, 0 },
+ { XK_F11, VK_F11, 0 },
+ { XK_F12, VK_F12, 0 },
+ { XK_F13, VK_F13, 0 },
+ { XK_F14, VK_F14, 0 },
+ { XK_F15, VK_F15, 0 },
+ { XK_F16, VK_F16, 0 },
+ { XK_F17, VK_F17, 0 },
+ { XK_F18, VK_F18, 0 },
+ { XK_F19, VK_F19, 0 },
+ { XK_F20, VK_F20, 0 },
+ { XK_F21, VK_F21, 0 },
+ { XK_F22, VK_F22, 0 },
+ { XK_F23, VK_F23, 0 },
+ { XK_F24, VK_F24, 0 },
+
+ // Keypad Functions, keypad numbers
+
+ { XK_KP_Tab, VK_TAB, 0 },
+ { XK_KP_Enter, VK_RETURN, 1 },
+ { XK_KP_F1, VK_F1, 0 },
+ { XK_KP_F2, VK_F2, 0 },
+ { XK_KP_F3, VK_F3, 0 },
+ { XK_KP_F4, VK_F4, 0 },
+ { XK_KP_Home, VK_HOME, 0 },
+ { XK_KP_Left, VK_LEFT, 0 },
+ { XK_KP_Up, VK_UP, 0 },
+ { XK_KP_Right, VK_RIGHT, 0 },
+ { XK_KP_Down, VK_DOWN, 0 },
+ { XK_KP_End, VK_END, 0 },
+ { XK_KP_Page_Up, VK_PRIOR, 0 },
+ { XK_KP_Page_Down, VK_NEXT, 0 },
+ { XK_KP_Begin, VK_CLEAR, 0 },
+ { XK_KP_Insert, VK_INSERT, 0 },
+ { XK_KP_Delete, VK_DELETE, 0 },
+ { XK_KP_Multiply, VK_MULTIPLY, 0 },
+ { XK_KP_Add, VK_ADD, 0 },
+ { XK_KP_Separator, VK_SEPARATOR, 0 },
+ { XK_KP_Subtract, VK_SUBTRACT, 0 },
+ { XK_KP_Decimal, VK_DECIMAL, 0 },
+ { XK_KP_Divide, VK_DIVIDE, 1 },
+
+ { XK_KP_0, VK_NUMPAD0, 0 },
+ { XK_KP_1, VK_NUMPAD1, 0 },
+ { XK_KP_2, VK_NUMPAD2, 0 },
+ { XK_KP_3, VK_NUMPAD3, 0 },
+ { XK_KP_4, VK_NUMPAD4, 0 },
+ { XK_KP_5, VK_NUMPAD5, 0 },
+ { XK_KP_6, VK_NUMPAD6, 0 },
+ { XK_KP_7, VK_NUMPAD7, 0 },
+ { XK_KP_8, VK_NUMPAD8, 0 },
+ { XK_KP_9, VK_NUMPAD9, 0 },
+
+ // Modifiers
+
+ { XK_Shift_L, VK_SHIFT, 0 },
+ { XK_Shift_R, VK_SHIFT, 0 },
+ { XK_Control_L, VK_CONTROL, 0 },
+ { XK_Control_R, VK_CONTROL, 1 },
+ { XK_Alt_L, VK_MENU, 0 },
+ { XK_Alt_R, VK_MENU, 1 },
+
+ // Left & Right Windows keys & Windows Menu Key
+
+ { XK_Super_L, VK_LWIN, 0 },
+ { XK_Super_R, VK_RWIN, 0 },
+ { XK_Menu, VK_APPS, 0 },
+
+ // Japanese stuff - almost certainly wrong...
+
+ { XK_Kanji, VK_KANJI, 0 },
+ { XK_Kana_Shift, VK_KANA, 0 },
+
+};
diff --git a/win/rfb_win32/rfb_win32.dsp b/win/rfb_win32/rfb_win32.dsp
new file mode 100644
index 0000000..acfb5aa
--- /dev/null
+++ b/win/rfb_win32/rfb_win32.dsp
@@ -0,0 +1,513 @@
+# Microsoft Developer Studio Project File - Name="rfb_win32" - Package Owner=<4>
+# Microsoft Developer Studio Generated Build File, Format Version 6.00
+# ** DO NOT EDIT **
+
+# TARGTYPE "Win32 (x86) Static Library" 0x0104
+
+CFG=rfb_win32 - Win32 Debug Unicode
+!MESSAGE This is not a valid makefile. To build this project using NMAKE,
+!MESSAGE use the Export Makefile command and run
+!MESSAGE
+!MESSAGE NMAKE /f "rfb_win32.mak".
+!MESSAGE
+!MESSAGE You can specify a configuration when running NMAKE
+!MESSAGE by defining the macro CFG on the command line. For example:
+!MESSAGE
+!MESSAGE NMAKE /f "rfb_win32.mak" CFG="rfb_win32 - Win32 Debug Unicode"
+!MESSAGE
+!MESSAGE Possible choices for configuration are:
+!MESSAGE
+!MESSAGE "rfb_win32 - Win32 Release" (based on "Win32 (x86) Static Library")
+!MESSAGE "rfb_win32 - Win32 Debug" (based on "Win32 (x86) Static Library")
+!MESSAGE "rfb_win32 - Win32 Debug Unicode" (based on "Win32 (x86) Static Library")
+!MESSAGE
+
+# Begin Project
+# PROP AllowPerConfigDependencies 0
+# PROP Scc_ProjName ""
+# PROP Scc_LocalPath ""
+CPP=cl.exe
+RSC=rc.exe
+
+!IF "$(CFG)" == "rfb_win32 - Win32 Release"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 0
+# PROP BASE Output_Dir "Release"
+# PROP BASE Intermediate_Dir "Release"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 0
+# PROP Output_Dir "..\Release"
+# PROP Intermediate_Dir "..\Release\rfb_win32"
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /YX /FD /c
+# ADD CPP /nologo /MT /W3 /GX /O2 /I ".." /I "../../common" /FI"rdr/msvcwarning.h" /D "NDEBUG" /D "_MBCS" /D "_LIB" /D "WIN32" /YX /FD /c
+# ADD BASE RSC /l 0x809 /d "NDEBUG"
+# ADD RSC /l 0x809 /d "NDEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LIB32=link.exe -lib
+# ADD BASE LIB32 /nologo
+# ADD LIB32 /nologo
+
+!ELSEIF "$(CFG)" == "rfb_win32 - Win32 Debug"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 1
+# PROP BASE Output_Dir "Debug"
+# PROP BASE Intermediate_Dir "Debug"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 1
+# PROP Output_Dir "..\Debug"
+# PROP Intermediate_Dir "..\Debug\rfb_win32"
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /YX /FD /GZ /c
+# ADD CPP /nologo /MTd /W3 /Gm /GX /Zi /Od /I ".." /I "../../common" /FI"rdr/msvcwarning.h" /D "_DEBUG" /D "_MBCS" /D "_LIB" /D "WIN32" /YX /FD /GZ /c
+# ADD BASE RSC /l 0x809 /d "_DEBUG"
+# ADD RSC /l 0x809 /d "_DEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LIB32=link.exe -lib
+# ADD BASE LIB32 /nologo
+# ADD LIB32 /nologo
+
+!ELSEIF "$(CFG)" == "rfb_win32 - Win32 Debug Unicode"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 1
+# PROP BASE Output_Dir "rfb_win32___Win32_Debug_Unicode"
+# PROP BASE Intermediate_Dir "rfb_win32___Win32_Debug_Unicode"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 1
+# PROP Output_Dir "..\Debug_Unicode"
+# PROP Intermediate_Dir "..\Debug_Unicode\rfb_win32"
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /MTd /W3 /Gm /GX /Zi /Od /I ".." /FI"msvcwarning.h" /D "_DEBUG" /D "_LIB" /D "WIN32" /D "_MBCS" /YX /FD /GZ /c
+# ADD CPP /nologo /MTd /W3 /Gm /GX /Zi /Od /I ".." /I "../../common" /FI"rdr/msvcwarning.h" /D "_DEBUG" /D "_UNICODE" /D "UNICODE" /D "_LIB" /D "WIN32" /YX /FD /GZ /c
+# ADD BASE RSC /l 0x809 /d "_DEBUG"
+# ADD RSC /l 0x809 /d "_DEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LIB32=link.exe -lib
+# ADD BASE LIB32 /nologo
+# ADD LIB32 /nologo
+
+!ENDIF
+
+# Begin Target
+
+# Name "rfb_win32 - Win32 Release"
+# Name "rfb_win32 - Win32 Debug"
+# Name "rfb_win32 - Win32 Debug Unicode"
+# Begin Group "Source Files"
+
+# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat"
+# Begin Source File
+
+SOURCE=.\AboutDialog.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\CKeyboard.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\CleanDesktop.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\Clipboard.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\CPointer.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\CurrentUser.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\DeviceContext.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\DeviceFrameBuffer.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\Dialog.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\DIBSectionBuffer.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\DynamicFn.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\EventManager.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\FolderManager.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\LaunchProcess.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\ListViewControl.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\LowLevelKeyEvents.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\MonitorInfo.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\MsgWindow.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\OSVersion.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\ProgressControl.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\RegConfig.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\Registry.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\ScaledDIBSectionBuffer.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\SDisplay.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\SDisplayCorePolling.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\SDisplayCoreWMHooks.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\Security.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\Service.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\SFileTransferManagerWin32.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\SFileTransferWin32.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\SInput.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\SocketManager.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\TCharArray.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\Threading.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\ToolBar.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\TsSessions.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\Win32Util.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\WMCursor.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\WMHooks.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\WMNotifier.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\WMPoller.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\WMShatter.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\WMWindowCopyRect.cxx
+# End Source File
+# End Group
+# Begin Group "Header Files"
+
+# PROP Default_Filter "h;hpp;hxx;hm;inl"
+# Begin Source File
+
+SOURCE=.\AboutDialog.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\BitmapInfo.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\CKeyboard.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\CleanDesktop.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\Clipboard.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\CompatibleBitmap.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\ComputerName.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\CPointer.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\CurrentUser.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\DeviceContext.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\DeviceFrameBuffer.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\Dialog.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\DIBSectionBuffer.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\DynamicFn.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\EventManager.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\FolderManager.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\Handle.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\IconInfo.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\IntervalTimer.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\keymap.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\LaunchProcess.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\ListViewControl.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\LocalMem.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\LogicalPalette.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\LowLevelKeyEvents.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\ModuleFileName.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\MonitorInfo.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\MsgBox.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\MsgWindow.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\OSVersion.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\ProgressControl.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\RegConfig.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\Registry.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\ScaledDIBSectionBuffer.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\SDisplay.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\SDisplayCoreDriver.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\SDisplayCorePolling.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\SDisplayCoreWMHooks.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\Security.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\Service.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\SFileTransferManagerWin32.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\SFileTransferWin32.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\SInput.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\SocketManager.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\TCharArray.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\Threading.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\ToolBar.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\TrayIcon.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\TsSessions.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\Win32Util.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\WMCursor.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\WMHooks.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\WMNotifier.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\WMPoller.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\WMShatter.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\WMWindowCopyRect.h
+# End Source File
+# End Group
+# End Target
+# End Project