diff options
author | Constantin Kaplinsky <const@tightvnc.com> | 2006-05-18 11:08:21 +0000 |
---|---|---|
committer | Constantin Kaplinsky <const@tightvnc.com> | 2006-05-18 11:08:21 +0000 |
commit | 8d9f5139332ca8509ab84601c53634d361ab4a2d (patch) | |
tree | 499e7ce0e3bfec1a5d374794126ef7b35565e9b2 /rfb_win32 | |
parent | 265d3c8752f2e9799dab2b048dde64994f891cb6 (diff) | |
parent | 590db877c7bbadc2f95751c3a6ea6f6c93bca37a (diff) | |
download | tigervnc-8d9f5139332ca8509ab84601c53634d361ab4a2d.tar.gz tigervnc-8d9f5139332ca8509ab84601c53634d361ab4a2d.zip |
Merged the changes from branch/merge-with-vnc-4.1.1, revisions 520:558.
git-svn-id: svn://svn.code.sf.net/p/tigervnc/code/trunk@559 3789f03b-4d11-0410-bbf8-ca57d06f2519
Diffstat (limited to 'rfb_win32')
86 files changed, 4090 insertions, 1983 deletions
diff --git a/rfb_win32/AboutDialog.cxx b/rfb_win32/AboutDialog.cxx index efb15c00..030be1b3 100644 --- a/rfb_win32/AboutDialog.cxx +++ b/rfb_win32/AboutDialog.cxx @@ -1,5 +1,5 @@ -/* Copyright (C) 2002-2003 RealVNC Ltd. All Rights Reserved. - * +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * * This is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or @@ -43,7 +43,7 @@ void AboutDialog::initDialog() { // Get our executable's version info FileVersionInfo verInfo; - SetWindowText(GetDlgItem(handle, Version), verInfo.getVerString(_T("FileVersion"))); + SetWindowText(GetDlgItem(handle, Version), verInfo.getVerString(_T("ProductVersion"))); SetWindowText(GetDlgItem(handle, Copyright), verInfo.getVerString(_T("LegalCopyright"))); - SetWindowText(GetDlgItem(handle, Description), verInfo.getVerString(_T("FileDescription"))); + SetWindowText(GetDlgItem(handle, Description), verInfo.getVerString(_T("ProductName"))); } diff --git a/rfb_win32/AboutDialog.h b/rfb_win32/AboutDialog.h index cb1713d4..0dd9d494 100644 --- a/rfb_win32/AboutDialog.h +++ b/rfb_win32/AboutDialog.h @@ -1,5 +1,5 @@ -/* Copyright (C) 2002-2003 RealVNC Ltd. All Rights Reserved. - * +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * * This is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or diff --git a/rfb_win32/BitmapInfo.h b/rfb_win32/BitmapInfo.h new file mode 100644 index 00000000..6a6f0d24 --- /dev/null +++ b/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/rfb_win32/CKeyboard.cxx b/rfb_win32/CKeyboard.cxx index ad852a0e..28aceab7 100644 --- a/rfb_win32/CKeyboard.cxx +++ b/rfb_win32/CKeyboard.cxx @@ -1,5 +1,5 @@ -/* Copyright (C) 2002-2003 RealVNC Ltd. All Rights Reserved. - * +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * * This is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or @@ -24,7 +24,6 @@ #include <rfb/keysymdef.h> #include <rfb_win32/CKeyboard.h> -#include <rfb/CMsgWriter.h> #include <rfb/LogWriter.h> #include <rfb_win32/OSVersion.h> #include "keymap.h" @@ -67,7 +66,7 @@ private: class ModifierKeyReleaser { public: - ModifierKeyReleaser(CMsgWriter* writer_, int vkCode, bool extended) + ModifierKeyReleaser(InputHandler* writer_, int vkCode, bool extended) : writer(writer_), extendedVkey(vkCode + (extended ? 256 : 0)), keysym(0) {} @@ -76,17 +75,17 @@ public: keysym = (*downKeysym)[extendedVkey]; vlog.debug("fake release extendedVkey 0x%x, keysym 0x%x", extendedVkey, keysym); - writer->writeKeyEvent(keysym, false); + writer->keyEvent(keysym, false); } } ~ModifierKeyReleaser() { if (keysym) { vlog.debug("fake press extendedVkey 0x%x, keysym 0x%x", extendedVkey, keysym); - writer->writeKeyEvent(keysym, true); + writer->keyEvent(keysym, true); } } - CMsgWriter* writer; + InputHandler* writer; int extendedVkey; rdr::U32 keysym; }; @@ -96,7 +95,7 @@ public: #define IS_PRINTABLE_LATIN1(c) (((c) >= 32 && (c) <= 126) || (c) == 128 || \ ((c) >= 160 && (c) <= 255)) -void win32::CKeyboard::keyEvent(CMsgWriter* writer, rdr::U8 vkey, +void win32::CKeyboard::keyEvent(InputHandler* writer, rdr::U8 vkey, rdr::U32 flags, bool down) { bool extended = (flags & 0x1000000); @@ -212,11 +211,11 @@ void win32::CKeyboard::keyEvent(CMsgWriter* writer, rdr::U8 vkey, // releaseAllKeys() - write key release events to the server for all keys // that are currently regarded as being down. -void win32::CKeyboard::releaseAllKeys(CMsgWriter* writer) { +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->writeKeyEvent((*i).second, false); + writer->keyEvent((*i).second, false); downKeysym.erase(i); } } @@ -225,12 +224,12 @@ void win32::CKeyboard::releaseAllKeys(CMsgWriter* writer) { // actually sent a key down event for the given key. The key up event always // contains the same keysym we used in the key down event, regardless of what // it would look up as using the current keyboard state. -void win32::CKeyboard::releaseKey(CMsgWriter* writer, int extendedVkey) +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->writeKeyEvent(downKeysym[extendedVkey], false); + writer->keyEvent(downKeysym[extendedVkey], false); downKeysym.erase(extendedVkey); } } @@ -242,18 +241,18 @@ void win32::CKeyboard::releaseKey(CMsgWriter* writer, int extendedVkey) // first. This can happen in two cases: (a) when a single key press results in // more than one character, and (b) when shift is released while another key is // autorepeating. -void win32::CKeyboard::pressKey(CMsgWriter* writer, int extendedVkey, +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->writeKeyEvent(downKeysym[extendedVkey], false); + writer->keyEvent(downKeysym[extendedVkey], false); } } vlog.debug("press extendedVkey 0x%x, keysym 0x%x", extendedVkey, keysym); - writer->writeKeyEvent(keysym, true); + writer->keyEvent(keysym, true); downKeysym[extendedVkey] = keysym; } diff --git a/rfb_win32/CKeyboard.h b/rfb_win32/CKeyboard.h index 10346ffc..666ebce5 100644 --- a/rfb_win32/CKeyboard.h +++ b/rfb_win32/CKeyboard.h @@ -1,5 +1,5 @@ -/* Copyright (C) 2002-2003 RealVNC Ltd. All Rights Reserved. - * +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * * This is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or @@ -23,25 +23,23 @@ #ifndef __RFB_WIN32_CKEYBOARD_H__ #define __RFB_WIN32_CKEYBOARD_H__ -#include <rdr/types.h> +#include <rfb/InputHandler.h> #include <map> namespace rfb { - class CMsgWriter; - namespace win32 { class CKeyboard { public: - void keyEvent(CMsgWriter* writer, rdr::U8 vkey, rdr::U32 flags, + void keyEvent(InputHandler* writer, rdr::U8 vkey, rdr::U32 flags, bool down); - void releaseAllKeys(CMsgWriter* writer); + 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(CMsgWriter* writer, int extendedVkey); - void win32::CKeyboard::pressKey(CMsgWriter* writer, int extendedVkey, + 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; }; diff --git a/rfb_win32/CPointer.cxx b/rfb_win32/CPointer.cxx index 1cab662a..3d0d9342 100644 --- a/rfb_win32/CPointer.cxx +++ b/rfb_win32/CPointer.cxx @@ -1,5 +1,5 @@ -/* Copyright (C) 2002-2003 RealVNC Ltd. All Rights Reserved. - * +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * * This is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or @@ -16,9 +16,7 @@ * USA. */ -#define WIN32_LEAN_AND_MEAN #include <windows.h> - #include <rfb/LogWriter.h> #include <rfb_win32/CPointer.h> @@ -37,21 +35,21 @@ CPointer::~CPointer() { } -void CPointer::pointerEvent(CMsgWriter* writer, int x, int y, int buttonMask) { +void CPointer::pointerEvent(InputHandler* writer, const Point& pos, int buttonMask) { // // - Duplicate Event Filtering // bool maskChanged = buttonMask != currButtonMask; - bool posChanged = !Point(x, y).equals(currPos); + bool posChanged = !pos.equals(currPos); if (!(posChanged || maskChanged)) return; // Pass on the event to the event-interval handler - threePointerEvent(writer, x, y, buttonMask); + threePointerEvent(writer, pos, buttonMask); // Save the position and mask - currPos = Point(x, y); + currPos = pos; currButtonMask = buttonMask; } @@ -66,7 +64,7 @@ int emulate3Mask(int buttonMask) { return buttonMask; } -void CPointer::threePointerEvent(CMsgWriter* writer, int x, int y, int buttonMask) { +void CPointer::threePointerEvent(InputHandler* writer, const Point& pos, int buttonMask) { // // - 3-Button Mouse Emulation // @@ -84,7 +82,7 @@ void CPointer::threePointerEvent(CMsgWriter* writer, int x, int y, int buttonMas // expires then we know we should actually send this event vlog.debug("emulate3: start timer"); threeTimer.start(100); - threePos = Point(x, y); + threePos = pos; threeMask = buttonMask; return; @@ -94,7 +92,7 @@ void CPointer::threePointerEvent(CMsgWriter* writer, int x, int y, int buttonMas vlog.debug("emulate3: stop timer (state)"); threeTimer.stop(); if (threeEmulating == ((buttonMask & 5) == 5)) - intervalPointerEvent(writer, threePos.x, threePos.y, threeMask); + intervalPointerEvent(writer, threePos, threeMask); else threeEmulating = ((buttonMask & 5) == 5); } @@ -104,11 +102,11 @@ void CPointer::threePointerEvent(CMsgWriter* writer, int x, int y, int buttonMas if (threeTimer.isActive()) { // - We are timing for an emulation event - if (abs(threePos.x - x) <= 4 || abs(threePos.y - y) <= 4) { + 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.x, threePos.y, threeMask); + intervalPointerEvent(writer, threePos, threeMask); } else { // Otherwise, we ignore the new event @@ -129,14 +127,14 @@ void CPointer::threePointerEvent(CMsgWriter* writer, int x, int y, int buttonMas } // - Let the event pass through to the next stage of processing - intervalPointerEvent(writer, x, y, buttonMask); + intervalPointerEvent(writer, pos, buttonMask); } -void CPointer::intervalPointerEvent(CMsgWriter* writer, int x, int y, int buttonMask) { +void CPointer::intervalPointerEvent(InputHandler* writer, const Point& pos, int buttonMask) { // // - Pointer Event Interval // - vlog.write(101, "ptrEvent: %d,%d (%lx)", x, y, buttonMask); + 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(); @@ -145,14 +143,14 @@ void CPointer::intervalPointerEvent(CMsgWriter* writer, int x, int y, int button // If the buttons have changed then flush queued events and send now sendNow = true; if (intervalQueued) - writer->writePointerEvent(intervalPos.x, intervalPos.y, intervalMask); + writer->pointerEvent(intervalPos, intervalMask); intervalQueued = false; } if (!sendNow) { // If we're not sending now then just queue the event intervalQueued = true; - intervalPos = Point(x, y); + intervalPos = pos; intervalMask = buttonMask; } else { // Start the interval timer if required, and send the event @@ -160,15 +158,15 @@ void CPointer::intervalPointerEvent(CMsgWriter* writer, int x, int y, int button intervalMask = buttonMask; if (pointerEventInterval) intervalTimer.start(pointerEventInterval); - writer->writePointerEvent(x, y, buttonMask); + writer->pointerEvent(pos, buttonMask); } } -void CPointer::handleTimer(CMsgWriter* writer, int timerId) { +void CPointer::handleTimer(InputHandler* writer, int timerId) { if (timerId == intervalTimer.getId()) { // Pointer interval has expired - send any queued events if (intervalQueued) { - writer->writePointerEvent(intervalPos.x, intervalPos.y, intervalMask); + writer->pointerEvent(intervalPos, intervalMask); intervalQueued = false; } else { intervalTimer.stop(); @@ -183,6 +181,6 @@ void CPointer::handleTimer(CMsgWriter* writer, int timerId) { if (threeEmulating) threeMask = emulate3Mask(threeMask); - intervalPointerEvent(writer, threePos.x, threePos.y, threeMask); + intervalPointerEvent(writer, threePos, threeMask); } } diff --git a/rfb_win32/CPointer.h b/rfb_win32/CPointer.h index f111de74..b5916010 100644 --- a/rfb_win32/CPointer.h +++ b/rfb_win32/CPointer.h @@ -1,5 +1,5 @@ -/* Copyright (C) 2002-2003 RealVNC Ltd. All Rights Reserved. - * +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * * This is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or @@ -25,14 +25,12 @@ #include <rdr/Exception.h> #include <rfb/Configuration.h> -#include <rfb/CMsgWriter.h> +#include <rfb/InputHandler.h> #include <rfb/Rect.h> #include <rfb_win32/IntervalTimer.h> namespace rfb { - class CMsgWriter; - namespace win32 { class CPointer { @@ -40,8 +38,8 @@ namespace rfb { CPointer(); ~CPointer(); - void pointerEvent(CMsgWriter* writer, int x, int y, int buttonMask); - void handleTimer(CMsgWriter* writer, int timerId); + 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);} @@ -56,13 +54,13 @@ namespace rfb { bool emulate3; int pointerEventInterval; - void intervalPointerEvent(CMsgWriter* writer, int x, int y, int buttonMask); + void intervalPointerEvent(InputHandler* writer, const Point& pos, int buttonMask); IntervalTimer intervalTimer; bool intervalQueued; Point intervalPos; int intervalMask; - void threePointerEvent(CMsgWriter* writer, int x, int y, int buttonMask); + void threePointerEvent(InputHandler* writer, const Point& pos, int buttonMask); IntervalTimer threeTimer; Point threePos; int threeMask; diff --git a/rfb_win32/CleanDesktop.cxx b/rfb_win32/CleanDesktop.cxx index 9fb83478..39cca119 100644 --- a/rfb_win32/CleanDesktop.cxx +++ b/rfb_win32/CleanDesktop.cxx @@ -1,5 +1,5 @@ -/* Copyright (C) 2002-2004 RealVNC Ltd. All Rights Reserved. - * +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * * This is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or @@ -18,16 +18,22 @@ // -=- CleanDesktop.cxx -#define WIN32_LEAN_AND_MEAN #include <windows.h> #include <wininet.h> #include <shlobj.h> - #include <rfb_win32/CleanDesktop.h> #include <rfb_win32/CurrentUser.h> #include <rfb_win32/Registry.h> +#include <rfb_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; @@ -47,37 +53,93 @@ struct 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_) { - // - Get the current Active Desktop options + 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); - HRESULT result = handle->GetDesktopItemOptions(&adOptions, 0); - if (result != S_OK) { - vlog.error("failed to get Active Desktop options: %d", result); + // 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 Active Desktop is active, disable it - if ((adOptions.fActiveDesktop==0) != (enable_==0)) { - if (enable_) - vlog.debug("enabling Active Desktop"); - else - vlog.debug("disabling Active Desktop"); - - adOptions.fActiveDesktop = enable_; - result = handle->SetDesktopItemOptions(&adOptions, 0); - if (result != S_OK) { - vlog.error("failed to disable ActiveDesktop: %d", result); + 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; } - handle->ApplyChanges(AD_APPLY_REFRESH); - return true; + for (unsigned int i=0; i<itemCount; i++) { + if (enableItem(i, false)) + restoreItems.insert(i); + } } - return false; + + // - 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; }; @@ -91,7 +153,8 @@ DWORD SysParamsInfo(UINT action, UINT param, PVOID ptr, UINT ini) { } -CleanDesktop::CleanDesktop() : restoreActiveDesktop(false), restoreWallpaper(false), restoreEffects(false) { +CleanDesktop::CleanDesktop() : restoreActiveDesktop(false), restoreWallpaper(false), + restorePattern(false), restoreEffects(false) { CoInitialize(0); } @@ -194,13 +257,13 @@ void CleanDesktop::enablePattern() { void CleanDesktop::disableEffects() { -#if (WINVER >= 0x500) 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); @@ -214,21 +277,23 @@ void CleanDesktop::disableEffects() { 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()); } -#else - vlog.info("disableffects not implemented"); -#endif } void CleanDesktop::enableEffects() { try { if (restoreEffects) { -#if (WINVER >= 0x500) ImpersonateCurrentUser icu; vlog.debug("restore desktop effects"); @@ -236,6 +301,7 @@ void CleanDesktop::enableEffects() { 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); @@ -245,7 +311,7 @@ void CleanDesktop::enableEffects() { } restoreEffects = false; #else - vlog.info("enableEffects not implemented"); + vlog.info(" not supported"); #endif } diff --git a/rfb_win32/CleanDesktop.h b/rfb_win32/CleanDesktop.h index 73f41534..22e246fa 100644 --- a/rfb_win32/CleanDesktop.h +++ b/rfb_win32/CleanDesktop.h @@ -1,5 +1,5 @@ -/* Copyright (C) 2002-2003 RealVNC Ltd. All Rights Reserved. - * +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * * This is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or diff --git a/rfb_win32/Clipboard.cxx b/rfb_win32/Clipboard.cxx index 867f885d..a4c43f04 100644 --- a/rfb_win32/Clipboard.cxx +++ b/rfb_win32/Clipboard.cxx @@ -1,5 +1,5 @@ -/* Copyright (C) 2002-2004 RealVNC Ltd. All Rights Reserved. - * +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * * This is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or @@ -61,15 +61,16 @@ unix2dos(const char* text) { // -// -=- ASCII filter (in-place) +// -=- ISO-8859-1 (Latin 1) filter (in-place) // void -removeNonAsciiChars(char* text) { +removeNonISOLatin1Chars(char* text) { int len = strlen(text); int i=0, j=0; for (; i<len; i++) { - if ((text[i] >= 1) && (text[i] <= 127)) + if (((text[i] >= 1) && (text[i] <= 127)) || + ((text[i] >= 160) && (text[i] <= 255))) text[j++] = text[i]; } text[j] = 0; @@ -126,7 +127,7 @@ Clipboard::processMessage(UINT msg, WPARAM wParam, LPARAM lParam) { } else { CharArray unix_text; unix_text.buf = dos2unix(clipdata); - // removeNonAsciiChars(unix_text.buf); + removeNonISOLatin1Chars(unix_text.buf); notifier->notifyClipboardChanged(unix_text.buf, strlen(unix_text.buf)); } } else { @@ -162,7 +163,7 @@ Clipboard::setClipText(const char* text) { // - Pre-process the supplied clipboard text into DOS format CharArray dos_text; dos_text.buf = unix2dos(text); - // removeNonAsciiChars(dos_text.buf); + removeNonISOLatin1Chars(dos_text.buf); int dos_text_len = strlen(dos_text.buf); // - Allocate global memory for the data diff --git a/rfb_win32/Clipboard.h b/rfb_win32/Clipboard.h index 57297e1e..b79768f6 100644 --- a/rfb_win32/Clipboard.h +++ b/rfb_win32/Clipboard.h @@ -1,5 +1,5 @@ -/* Copyright (C) 2002-2003 RealVNC Ltd. All Rights Reserved. - * +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * * This is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or diff --git a/rfb_win32/CompatibleBitmap.h b/rfb_win32/CompatibleBitmap.h new file mode 100644 index 00000000..4beed8dc --- /dev/null +++ b/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/rfb_win32/ComputerName.h b/rfb_win32/ComputerName.h new file mode 100644 index 00000000..110caa59 --- /dev/null +++ b/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/rfb_win32/CurrentUser.cxx b/rfb_win32/CurrentUser.cxx index 1a244853..7562d29b 100644 --- a/rfb_win32/CurrentUser.cxx +++ b/rfb_win32/CurrentUser.cxx @@ -1,5 +1,5 @@ -/* Copyright (C) 2002-2004 RealVNC Ltd. All Rights Reserved. - * +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * * This is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or @@ -18,48 +18,98 @@ // -=- Currentuser.cxx -#include <windows.h> -#include <lmcons.h> +#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"); -CurrentUserToken::CurrentUserToken() : isValid_(false) { - // - If the OS doesn't support security, ignore it - if (!isServiceProcess()) { - // - Running in User-Mode - just get our current token - if (!OpenProcessToken(GetCurrentProcess(), GENERIC_ALL, &h)) { - DWORD err = GetLastError(); - if (err != ERROR_CALL_NOT_IMPLEMENTED) - throw rdr::SystemException("OpenProcessToken failed", GetLastError()); + +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; } - isValid_ = true; - } else { - // - Under XP/2003 and above, we can just ask the operating system + + // 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); - isValid_ = true; - } else { - // - Under NT/2K we have to resort to a nasty hack... - HWND tray = FindWindow(_T("Shell_TrayWnd"), 0); - if (!tray) - return; - DWORD processId = 0; - GetWindowThreadProcessId(tray, &processId); - if (!processId) - return; - Handle process = OpenProcess(MAXIMUM_ALLOWED, FALSE, processId); - if (!process.h) - return; - OpenProcessToken(process, MAXIMUM_ALLOWED, &h); - isValid_ = true; + 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; } } @@ -68,8 +118,8 @@ ImpersonateCurrentUser::ImpersonateCurrentUser() { RegCloseKey(HKEY_CURRENT_USER); if (!isServiceProcess()) return; - if (!token.isValid()) - throw rdr::Exception("CurrentUserToken is not valid"); + if (!token.canImpersonate()) + throw rdr::Exception("Cannot impersonate unsafe or null token"); if (!ImpersonateLoggedOnUser(token)) { DWORD err = GetLastError(); if (err != ERROR_CALL_NOT_IMPLEMENTED) @@ -83,6 +133,7 @@ ImpersonateCurrentUser::~ImpersonateCurrentUser() { if (err != ERROR_CALL_NOT_IMPLEMENTED) exit(err); } + RegCloseKey(HKEY_CURRENT_USER); } @@ -91,3 +142,11 @@ UserName::UserName() : TCharArray(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/rfb_win32/CurrentUser.h b/rfb_win32/CurrentUser.h index 469946b6..794f27c6 100644 --- a/rfb_win32/CurrentUser.h +++ b/rfb_win32/CurrentUser.h @@ -1,5 +1,5 @@ -/* Copyright (C) 2002-2004 RealVNC Ltd. All Rights Reserved. - * +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * * This is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or @@ -26,29 +26,41 @@ #ifndef __RFB_WIN32_CURRENT_USER_H__ #define __RFB_WIN32_CURRENT_USER_H__ -#include <rfb_win32/Service.h> -#include <rfb_win32/OSVersion.h> -#include <rfb_win32/Win32Util.h> +#include <rfb_win32/Handle.h> +#include <rfb_win32/Security.h> namespace rfb { namespace win32 { // CurrentUserToken - // h == 0 - // if platform is not NT *or* unable to get token - // *or* if process is hosting service. - // h != 0 - // if process is hosting service *and* - // if platform is NT *and* able to get token. - // isValid() == true - // if platform is not NT *or* token is valid. + // 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 isValid() const {return isValid_;}; + bool isSafe() const { return isSafe_; }; + bool canImpersonate() const { return h && isSafe(); } + bool noUserLoggedOn() const { return !h && isSafe(); } private: - bool isValid_; + bool isSafe_; }; // ImpersonateCurrentUser @@ -66,11 +78,21 @@ namespace rfb { // 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(); + }; + } } diff --git a/rfb_win32/DIBSectionBuffer.cxx b/rfb_win32/DIBSectionBuffer.cxx index 026e5ed4..18ce3ea4 100644 --- a/rfb_win32/DIBSectionBuffer.cxx +++ b/rfb_win32/DIBSectionBuffer.cxx @@ -1,5 +1,5 @@ -/* Copyright (C) 2002-2004 RealVNC Ltd. All Rights Reserved. - * +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * * This is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or @@ -15,16 +15,16 @@ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, * USA. */ -#include <rfb/LogWriter.h> #include <rfb_win32/DIBSectionBuffer.h> -#include <rfb_win32/Win32Util.h> - +#include <rfb_win32/DeviceContext.h> +#include <rfb_win32/BitmapInfo.h> +#include <rfb/LogWriter.h> using namespace rfb; using namespace win32; -static LogWriter vlog("DIBSection"); +static LogWriter vlog("DIBSectionBuffer"); DIBSectionBuffer::DIBSectionBuffer(HWND window_) @@ -53,7 +53,7 @@ void DIBSectionBuffer::setPF(const PixelFormat& pf) { format = pf; recreateBuffer(); if ((pf.bpp <= 8) && pf.trueColour) { - vlog.debug("creating %d-bit TrueColor palette", pf.depth); + 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; @@ -114,8 +114,6 @@ void DIBSectionBuffer::recreateBuffer() { bi.mask.green = format.greenMax << format.greenShift; bi.mask.blue = format.blueMax << format.blueShift; - vlog.debug("area=%d, bpp=%d", width_ * height_, format.bpp); - // Create a DIBSection to draw into if (device) new_bitmap = ::CreateDIBSection(device, (BITMAPINFO*)&bi.bmiHeader, iUsage, @@ -199,10 +197,13 @@ void DIBSectionBuffer::recreateBuffer() { 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(); } + } } diff --git a/rfb_win32/DIBSectionBuffer.h b/rfb_win32/DIBSectionBuffer.h index 51e2da31..ad1a310c 100644 --- a/rfb_win32/DIBSectionBuffer.h +++ b/rfb_win32/DIBSectionBuffer.h @@ -1,5 +1,5 @@ -/* Copyright (C) 2002-2003 RealVNC Ltd. All Rights Reserved. - * +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * * This is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or @@ -25,9 +25,7 @@ #ifndef __RFB_WIN32_DIB_SECTION_BUFFER_H__ #define __RFB_WIN32_DIB_SECTION_BUFFER_H__ -#define WIN32_LEAN_AND_MEAN #include <windows.h> - #include <rfb/PixelBuffer.h> #include <rfb/Region.h> #include <rfb/ColourMap.h> diff --git a/rfb_win32/DeviceContext.cxx b/rfb_win32/DeviceContext.cxx new file mode 100644 index 00000000..4f70a1bf --- /dev/null +++ b/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/rfb_win32/DeviceContext.h b/rfb_win32/DeviceContext.h new file mode 100644 index 00000000..9d91cec2 --- /dev/null +++ b/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/rfb_win32/DeviceFrameBuffer.cxx b/rfb_win32/DeviceFrameBuffer.cxx index 70975c37..8da894e0 100644 --- a/rfb_win32/DeviceFrameBuffer.cxx +++ b/rfb_win32/DeviceFrameBuffer.cxx @@ -1,5 +1,5 @@ -/* Copyright (C) 2002-2004 RealVNC Ltd. All Rights Reserved. - * +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * * This is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or @@ -21,25 +21,22 @@ // The DeviceFrameBuffer class encapsulates the pixel data of the system // display. -#define WIN32_LEAN_AND_MEAN -#include <windows.h> -#include <assert.h> - #include <vector> - -#include <rfb/LogWriter.h> -#include <rfb/Exception.h> -#include <rdr/types.h> -#include <rfb/VNCServer.h> #include <rfb_win32/DeviceFrameBuffer.h> -#include <rfb_win32/Win32Util.h> +#include <rfb_win32/DeviceContext.h> #include <rfb_win32/OSVersion.h> +#include <rfb_win32/IconInfo.h> +#include <rfb/VNCServer.h> +#include <rfb/LogWriter.h> -namespace rfb { +using namespace rfb; +using namespace win32; -namespace win32 { +static LogWriter vlog("DeviceFrameBuffer"); -static LogWriter vlog("FrameBuffer"); +BoolParameter DeviceFrameBuffer::useCaptureBlt("UseCaptureBlt", + "Use a slower capture method that ensures that alpha blended windows appear correctly", + true); // -=- DeviceFrameBuffer class @@ -67,10 +64,7 @@ DeviceFrameBuffer::DeviceFrameBuffer(HDC deviceContext, const Rect& wRect) // -=- Get the display dimensions and pixel format // Get the display dimensions - RECT cr; - if (!GetClipBox(device, &cr)) - throw rdr::SystemException("GetClipBox", GetLastError()); - deviceCoords = Rect(cr.left, cr.top, cr.right, cr.bottom); + deviceCoords = DeviceContext::getClipBox(device); if (!wRect.is_empty()) deviceCoords = wRect.translate(deviceCoords.tl); int w = deviceCoords.width(); @@ -120,7 +114,7 @@ DeviceFrameBuffer::grabRect(const Rect &rect) { // Note: Microsoft's documentation lies directly about CAPTUREBLT and claims it works on 98/ME // If you try CAPTUREBLT on 98 then you get blank output... if (!::BitBlt(tmpDC, rect.tl.x, rect.tl.y, rect.width(), rect.height(), device, src.x, src.y, - osVersion.isPlatformNT ? CAPTUREBLT | SRCCOPY : SRCCOPY)) { + (osVersion.isPlatformNT && useCaptureBlt) ? (CAPTUREBLT | SRCCOPY) : SRCCOPY)) { if (ignoreGrabErrors) vlog.error("BitBlt failed:%ld", GetLastError()); else @@ -176,7 +170,7 @@ void DeviceFrameBuffer::setCursor(HCURSOR hCursor, VNCServer* server) // - If hCursor is null then there is no cursor - clear the old one if (hCursor == 0) { - server->setCursor(0, 0, 0, 0, 0, 0); + server->setCursor(0, 0, Point(), 0, 0); return; } @@ -189,8 +183,10 @@ void DeviceFrameBuffer::setCursor(HCURSOR hCursor, VNCServer* server) BITMAP maskInfo; if (!GetObject(iconInfo.hbmMask, sizeof(BITMAP), &maskInfo)) throw rdr::SystemException("GetObject() failed", GetLastError()); - - assert(maskInfo.bmPlanes == 1 && maskInfo.bmBitsPixel == 1); + 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 @@ -278,8 +274,7 @@ void DeviceFrameBuffer::setCursor(HCURSOR hCursor, VNCServer* server) memcpy(cursorBm.data, cursor.data, cursor.dataLen()); } - server->setCursor(cursor.width(), cursor.height(), - cursor.hotspot.x, cursor.hotspot.y, + server->setCursor(cursor.width(), cursor.height(), cursor.hotspot, cursorBm.data, cursor.mask.buf); } catch (rdr::Exception& e) { vlog.error(e.str()); @@ -292,7 +287,3 @@ DeviceFrameBuffer::updateColourMap() { if (!format.trueColour) copyDevicePaletteToDIB(device, this); } - -}; // namespace win32 - -}; // namespace rfb diff --git a/rfb_win32/DeviceFrameBuffer.h b/rfb_win32/DeviceFrameBuffer.h index 5e97b222..7718c339 100644 --- a/rfb_win32/DeviceFrameBuffer.h +++ b/rfb_win32/DeviceFrameBuffer.h @@ -1,5 +1,5 @@ -/* Copyright (C) 2002-2003 RealVNC Ltd. All Rights Reserved. - * +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * * This is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or @@ -26,13 +26,12 @@ #ifndef __RFB_WIN32_DEVICE_FRAME_BUFFER_H__ #define __RFB_WIN32_DEVICE_FRAME_BUFFER_H__ -#define WIN32_LEAN_AND_MEAN #include <windows.h> - #include <rfb_win32/DIBSectionBuffer.h> #include <rfb/Cursor.h> #include <rfb/Region.h> #include <rfb/Exception.h> +#include <rfb/Configuration.h> namespace rfb { @@ -85,6 +84,8 @@ namespace rfb { // 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 @@ -98,22 +99,6 @@ namespace rfb { bool ignoreGrabErrors; }; - // -=- createDisplayDeviceFrameBuffer - // createDisplayDeviceFrameBuffer must be passed the name of a display device, - // and will return a new FrameBuffer object attached to that display - // device. - // If the device name is not specified then the default display is - // returned. - - DeviceFrameBuffer *createDisplayDeviceFrameBuffer(const char *device=0); - - // -=- createDisplayFrameBuffers - // Creates a set of framebuffers, one for each available display - // device. - - typedef std::vector<DeviceFrameBuffer *> DeviceFrameBuffers; - void createDisplayDeviceFrameBuffers(DeviceFrameBuffers *fbs); - }; }; diff --git a/rfb_win32/Dialog.cxx b/rfb_win32/Dialog.cxx index 157cf5fe..398334f0 100644 --- a/rfb_win32/Dialog.cxx +++ b/rfb_win32/Dialog.cxx @@ -1,5 +1,5 @@ -/* Copyright (C) 2002-2003 RealVNC Ltd. All Rights Reserved. - * +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * * This is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or @@ -25,9 +25,15 @@ #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; @@ -208,6 +214,33 @@ 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; @@ -227,7 +260,8 @@ bool PropSheet::showPropSheet(HWND owner, bool showApply, bool showCtxtHelp, boo // Initialise and create the PropertySheet itself PROPSHEETHEADER header; header.dwSize = PROPSHEETHEADER_V1_SIZE; - header.dwFlags = PSH_MODELESS | (showApply ? 0 : PSH_NOAPPLYNOW) /*| (showCtxtHelp ? 0 : PSH_NOCONTEXTHELP)*/; + header.dwFlags = PSH_MODELESS | (showApply ? 0 : PSH_NOAPPLYNOW) | (showCtxtHelp ? 0 : PSH_USECALLBACK); + header.pfnCallback = removeCtxtHelp; header.hwndParent = owner; header.hInstance = inst; header.pszCaption = title.buf; @@ -245,9 +279,7 @@ bool PropSheet::showPropSheet(HWND owner, bool showApply, bool showCtxtHelp, boo centerWindow(handle, owner); plog.info("created %lx", handle); -#if (WINVER >= 0x0500) #ifdef _DIALOG_CAPTURE - // *** NOT TESTED if (capture) { plog.info("capturing \"%s\"", (const char*)CStr(title.buf)); char* tmpdir = getenv("TEMP"); @@ -264,15 +296,23 @@ bool PropSheet::showPropSheet(HWND owner, bool showApply, bool showCtxtHelp, boo 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\\capture%d.bmp", tmpdir, i); + sprintf(filename, "%s\\%s.bmp", tmpdir, pageTitle.buf); + vlog.debug("writing to %s", filename); saveBMP(filename, &fb); i++; } ReleaseDC(handle, dc); } else { #endif -#endif try { if (owner) EnableWindow(owner, FALSE); @@ -291,11 +331,9 @@ bool PropSheet::showPropSheet(HWND owner, bool showApply, bool showCtxtHelp, boo EnableWindow(owner, TRUE); throw; } -#if (WINVER >= 0x0500) #ifdef _DIALOG_CAPTURE } #endif -#endif plog.info("finished %lx", handle); diff --git a/rfb_win32/Dialog.h b/rfb_win32/Dialog.h index d46133a0..9784ba46 100644 --- a/rfb_win32/Dialog.h +++ b/rfb_win32/Dialog.h @@ -1,5 +1,5 @@ -/* Copyright (C) 2002-2003 RealVNC Ltd. All Rights Reserved. - * +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * * This is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or @@ -24,7 +24,6 @@ #ifndef __RFB_WIN32_DIALOG_H__ #define __RFB_WIN32_DIALOG_H__ -#define WIN32_LEAN_AND_MEAN #include <windows.h> #include <prsht.h> #include <list> diff --git a/rfb_win32/DynamicFn.cxx b/rfb_win32/DynamicFn.cxx new file mode 100644 index 00000000..e933f249 --- /dev/null +++ b/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/rfb_win32/DynamicFn.h b/rfb_win32/DynamicFn.h new file mode 100644 index 00000000..57fdbec5 --- /dev/null +++ b/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/rfb_win32/EventManager.cxx b/rfb_win32/EventManager.cxx new file mode 100644 index 00000000..0f9993b7 --- /dev/null +++ b/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/rfb_win32/EventManager.h b/rfb_win32/EventManager.h new file mode 100644 index 00000000..bb66e340 --- /dev/null +++ b/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/rfb_win32/msvcwarning.h b/rfb_win32/Handle.h index e50d9c1a..d3baa580 100644 --- a/rfb_win32/msvcwarning.h +++ b/rfb_win32/Handle.h @@ -1,5 +1,5 @@ -/* Copyright (C) 2002-2003 RealVNC Ltd. All Rights Reserved. - * +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * * This is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or @@ -15,6 +15,29 @@ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, * USA. */ -#pragma warning( disable : 4244 ) // loss of data e.g. int to char -#pragma warning( disable : 4800 ) // forcing bool 'true' or 'false' -#pragma warning( disable : 4786 ) // debug info truncated + +// 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/rfb_win32/IconInfo.h b/rfb_win32/IconInfo.h new file mode 100644 index 00000000..cb33a42d --- /dev/null +++ b/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/rfb_win32/IntervalTimer.h b/rfb_win32/IntervalTimer.h index 645d0ee9..ddfae493 100644 --- a/rfb_win32/IntervalTimer.h +++ b/rfb_win32/IntervalTimer.h @@ -1,5 +1,5 @@ -/* Copyright (C) 2002-2003 RealVNC Ltd. All Rights Reserved. - * +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * * This is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or diff --git a/rfb_win32/LaunchProcess.cxx b/rfb_win32/LaunchProcess.cxx index 0a48d60c..56a712e6 100644 --- a/rfb_win32/LaunchProcess.cxx +++ b/rfb_win32/LaunchProcess.cxx @@ -1,5 +1,5 @@ -/* Copyright (C) 2002-2004 RealVNC Ltd. All Rights Reserved. - * +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * * This is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or @@ -19,9 +19,10 @@ // -=- LaunchProcess.cxx #include <rfb_win32/LaunchProcess.h> +#include <rfb_win32/ModuleFileName.h> #include <rfb_win32/Win32Util.h> - -#include <rfb/util.h> +#include <rdr/Exception.h> +#include <stdio.h> using namespace rfb; using namespace win32; @@ -37,10 +38,11 @@ LaunchProcess::~LaunchProcess() { } -void LaunchProcess::start(HANDLE userToken) { +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; @@ -58,19 +60,15 @@ void LaunchProcess::start(HANDLE userToken) { exePath.buf = tstrDup(exeName.buf); } - // - Start the VNC server + // - 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); -#ifdef _DEBUG - DWORD flags = CREATE_NEW_CONSOLE; -#else - DWORD flags = CREATE_NO_WINDOW; -#endif + DWORD flags = createConsole ? CREATE_NEW_CONSOLE : CREATE_NO_WINDOW; BOOL success; - if (userToken) + 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); @@ -81,12 +79,25 @@ void LaunchProcess::start(HANDLE userToken) { WaitForInputIdle(procInfo.hProcess, 15000); } -void LaunchProcess::await() { +void LaunchProcess::detach() +{ if (!procInfo.hProcess) return; - WaitForSingleObject(procInfo.hProcess, INFINITE); 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/rfb_win32/LaunchProcess.h b/rfb_win32/LaunchProcess.h index 6fd34e9c..38521dcd 100644 --- a/rfb_win32/LaunchProcess.h +++ b/rfb_win32/LaunchProcess.h @@ -1,5 +1,5 @@ -/* Copyright (C) 2002-2003 RealVNC Ltd. All Rights Reserved. - * +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * * This is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or @@ -24,9 +24,7 @@ #ifndef __RFB_WIN32_LAUNCHPROCESS_H__ #define __RFB_WIN32_LAUNCHPROCESS_H__ -#define WIN32_LEAN_AND_MEAN #include <windows.h> - #include <rfb_win32/TCharArray.h> namespace rfb { @@ -38,14 +36,28 @@ namespace rfb { LaunchProcess(const TCHAR* exeName_, const TCHAR* params); ~LaunchProcess(); - // If userToken is 0 then starts as current user, otherwise - // starts as the specified user. userToken must be a primary token. - void start(HANDLE userToken); + // 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, and close the handles to it. - void await(); + // 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; diff --git a/rfb_win32/LocalMem.h b/rfb_win32/LocalMem.h new file mode 100644 index 00000000..a99d3241 --- /dev/null +++ b/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/rfb_win32/LogicalPalette.h b/rfb_win32/LogicalPalette.h new file mode 100644 index 00000000..204f1081 --- /dev/null +++ b/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/rfb_win32/LowLevelKeyEvents.cxx b/rfb_win32/LowLevelKeyEvents.cxx new file mode 100644 index 00000000..322d1f40 --- /dev/null +++ b/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/rfb_win32/LowLevelKeyEvents.h b/rfb_win32/LowLevelKeyEvents.h new file mode 100644 index 00000000..40d2ecfa --- /dev/null +++ b/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/rfb_win32/ModuleFileName.h b/rfb_win32/ModuleFileName.h new file mode 100644 index 00000000..2264e89d --- /dev/null +++ b/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/rfb_win32/MonitorInfo.cxx b/rfb_win32/MonitorInfo.cxx new file mode 100644 index 00000000..03772e97 --- /dev/null +++ b/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/rfb_win32/MonitorInfo.h b/rfb_win32/MonitorInfo.h new file mode 100644 index 00000000..acf27755 --- /dev/null +++ b/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/rfb_win32/MsgBox.h b/rfb_win32/MsgBox.h new file mode 100644 index 00000000..59571395 --- /dev/null +++ b/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/rfb_win32/MsgWindow.cxx b/rfb_win32/MsgWindow.cxx index 519d6ab9..95bd5237 100644 --- a/rfb_win32/MsgWindow.cxx +++ b/rfb_win32/MsgWindow.cxx @@ -1,5 +1,5 @@ -/* Copyright (C) 2002-2004 RealVNC Ltd. All Rights Reserved. - * +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * * This is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or @@ -113,4 +113,4 @@ MsgWindow::~MsgWindow() { LRESULT MsgWindow::processMessage(UINT msg, WPARAM wParam, LPARAM lParam) { return SafeDefWindowProc(getHandle(), msg, wParam, lParam); -}
\ No newline at end of file +} diff --git a/rfb_win32/MsgWindow.h b/rfb_win32/MsgWindow.h index 94baca38..92b6cf20 100644 --- a/rfb_win32/MsgWindow.h +++ b/rfb_win32/MsgWindow.h @@ -1,5 +1,5 @@ -/* Copyright (C) 2002-2003 RealVNC Ltd. All Rights Reserved. - * +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * * This is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or @@ -24,9 +24,7 @@ #ifndef __RFB_WIN32_MSG_WINDOW_H__ #define __RFB_WIN32_MSG_WINDOW_H__ -#define WIN32_LEAN_AND_MEAN #include <windows.h> - #include <rfb_win32/TCharArray.h> namespace rfb { diff --git a/rfb_win32/OSVersion.cxx b/rfb_win32/OSVersion.cxx index 1976098f..3d74c956 100644 --- a/rfb_win32/OSVersion.cxx +++ b/rfb_win32/OSVersion.cxx @@ -1,5 +1,5 @@ -/* Copyright (C) 2002-2003 RealVNC Ltd. All Rights Reserved. - * +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * * This is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or diff --git a/rfb_win32/OSVersion.h b/rfb_win32/OSVersion.h index 1d529431..18ec003e 100644 --- a/rfb_win32/OSVersion.h +++ b/rfb_win32/OSVersion.h @@ -1,5 +1,5 @@ -/* Copyright (C) 2002-2003 RealVNC Ltd. All Rights Reserved. - * +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * * This is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or @@ -27,7 +27,6 @@ #ifndef __RFB_WIN32_OS_VERSION_H__ #define __RFB_WIN32_OS_VERSION_H__ -#define WIN32_LEAN_AND_MEAN #include <windows.h> namespace rfb { diff --git a/rfb_win32/RegConfig.cxx b/rfb_win32/RegConfig.cxx index fcb309b5..dd1c3b06 100644 --- a/rfb_win32/RegConfig.cxx +++ b/rfb_win32/RegConfig.cxx @@ -1,5 +1,5 @@ -/* Copyright (C) 2002-2003 RealVNC Ltd. All Rights Reserved. - * +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * * This is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or @@ -23,7 +23,7 @@ #include <rfb_win32/RegConfig.h> #include <rfb/LogWriter.h> #include <rfb/util.h> -#include <rdr/HexOutStream.h> +//#include <rdr/HexOutStream.h> using namespace rfb; using namespace rfb::win32; @@ -32,90 +32,28 @@ using namespace rfb::win32; static LogWriter vlog("RegConfig"); -class rfb::win32::RegReaderThread : public Thread { -public: - RegReaderThread(RegistryReader& reader, const HKEY key); - ~RegReaderThread(); - virtual void run(); - virtual Thread* join(); -protected: - RegistryReader& reader; - RegKey key; - HANDLE event; -}; - -RegReaderThread::RegReaderThread(RegistryReader& reader_, const HKEY key_) : Thread("RegConfig"), reader(reader_), key(key_) { -} - -RegReaderThread::~RegReaderThread() { -} - -void -RegReaderThread::run() { - vlog.debug("RegReaderThread started"); - while (key) { - // - Wait for changes - vlog.debug("waiting for changes"); - key.awaitChange(true, REG_NOTIFY_CHANGE_NAME | REG_NOTIFY_CHANGE_LAST_SET); - - // - Load settings - RegistryReader::loadRegistryConfig(key); - - // - Notify specified thread of changes - if (reader.notifyThread) - PostThreadMessage(reader.notifyThread->getThreadId(), - reader.notifyMsg.message, - reader.notifyMsg.wParam, - reader.notifyMsg.lParam); - else if (reader.notifyWindow) - PostMessage(reader.notifyWindow, - reader.notifyMsg.message, - reader.notifyMsg.wParam, - reader.notifyMsg.lParam); - } -} - -Thread* -RegReaderThread::join() { - RegKey old_key = key; - key.close(); - if ((HKEY)old_key) { - // *** Closing the key doesn't always seem to work - // Writing to it always will, instead... - vlog.debug("closing key"); - old_key.setString(_T("dummy"), _T("")); - } - return Thread::join(); -} - - -RegistryReader::RegistryReader() : thread(0), notifyThread(0) { - memset(¬ifyMsg, 0, sizeof(notifyMsg)); +RegConfig::RegConfig(EventManager* em) : eventMgr(em), event(CreateEvent(0, TRUE, FALSE, 0)), callback(0) { + if (em->addEvent(event, this)) + eventMgr = em; } -RegistryReader::~RegistryReader() { - if (thread) delete thread->join(); +RegConfig::~RegConfig() { + if (eventMgr) + eventMgr->removeEvent(event); } -bool RegistryReader::setKey(const HKEY rootkey, const TCHAR* keyname) { - if (thread) delete thread->join(); - thread = 0; - - RegKey key; +bool RegConfig::setKey(const HKEY rootkey, const TCHAR* keyname) { try { key.createKey(rootkey, keyname); - loadRegistryConfig(key); + processEvent(event); + return true; } catch (rdr::Exception& e) { vlog.debug(e.str()); return false; } - thread = new RegReaderThread(*this, key); - if (thread) thread->start(); - return true; } -void -RegistryReader::loadRegistryConfig(RegKey& key) { +void RegConfig::loadRegistryConfig(RegKey& key) { DWORD i = 0; try { while (1) { @@ -131,21 +69,46 @@ RegistryReader::loadRegistryConfig(RegKey& key) { } } -bool RegistryReader::setNotifyThread(Thread* thread, UINT winMsg, WPARAM wParam, LPARAM lParam) { - notifyMsg.message = winMsg; - notifyMsg.wParam = wParam; - notifyMsg.lParam = lParam; - notifyThread = thread; - notifyWindow = 0; - return true; +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(); } -bool RegistryReader::setNotifyWindow(HWND window, UINT winMsg, WPARAM wParam, LPARAM lParam) { - notifyMsg.message = winMsg; - notifyMsg.wParam = wParam; - notifyMsg.lParam = lParam; - notifyWindow = window; - notifyThread = 0; - return true; + +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/rfb_win32/RegConfig.h b/rfb_win32/RegConfig.h index 3fced85e..e9c01b1d 100644 --- a/rfb_win32/RegConfig.h +++ b/rfb_win32/RegConfig.h @@ -1,5 +1,5 @@ -/* Copyright (C) 2002-2003 RealVNC Ltd. All Rights Reserved. - * +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * * This is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or @@ -26,27 +26,55 @@ #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 RegistryReader { + class RegConfig : EventHandler { public: - RegistryReader(); - ~RegistryReader(); + RegConfig(EventManager* em); + ~RegConfig(); + + // Specify the registry key to read Configuration items from bool setKey(const HKEY rootkey, const TCHAR* keyname); - bool setNotifyThread(Thread* thread, UINT winMsg, WPARAM wParam=0, LPARAM lParam=0); - bool setNotifyWindow(HWND window, UINT winMsg, WPARAM wParam=0, LPARAM lParam=0); + + // 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: - friend class RegReaderThread; - Thread* thread; - Thread* notifyThread; - HWND notifyWindow; - MSG notifyMsg; + // 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; }; }; diff --git a/rfb_win32/Registry.cxx b/rfb_win32/Registry.cxx index de9238f7..4ece4bac 100644 --- a/rfb_win32/Registry.cxx +++ b/rfb_win32/Registry.cxx @@ -1,5 +1,5 @@ -/* Copyright (C) 2002-2004 RealVNC Ltd. All Rights Reserved. - * +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * * This is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or @@ -18,14 +18,14 @@ // -=- Registry.cxx -#include <rfb/LogWriter.h> #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 <rfb_win32/Security.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 @@ -69,6 +69,7 @@ RegKey::~RegKey() { void RegKey::setHKEY(HKEY k, bool fK) { + vlog.debug("setHKEY(%x,%d)", k, (int)fK); close(); freeKey = fK; key = k; @@ -82,6 +83,7 @@ bool RegKey::createKey(const RegKey& root, const TCHAR* name) { 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; } @@ -91,6 +93,8 @@ void RegKey::openKey(const RegKey& root, const TCHAR* name, bool readOnly) { 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; } @@ -109,6 +113,7 @@ void RegKey::setDACL(const PACL acl, bool inherit) { void RegKey::close() { if (freeKey) { + vlog.debug("RegCloseKey(%x)", key); RegCloseKey(key); key = 0; } @@ -126,8 +131,8 @@ void RegKey::deleteValue(const TCHAR* name) const { throw rdr::SystemException("RegDeleteValue", result); } -void RegKey::awaitChange(bool watchSubTree, DWORD filter) const { - LONG result = RegNotifyChangeKeyValue(key, watchSubTree, filter, 0, FALSE); +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); } @@ -206,6 +211,16 @@ 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); @@ -224,12 +239,7 @@ TCHAR* RegKey::getRepresentation(const TCHAR* valname) const { } case REG_SZ: if (length) { - // We must terminate the string, just to be sure. Stupid Win32... - int len = length/sizeof(TCHAR); - TCharArray str(len+1); - memcpy(str.buf, data.buf, length); - str.buf[len] = 0; - return str.takeBuf(); + return terminateData(data.buf, length); } else { return tstrDup(_T("")); } @@ -239,6 +249,22 @@ TCHAR* RegKey::getRepresentation(const TCHAR* valname) const { _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"); } @@ -270,3 +296,21 @@ const TCHAR* RegKey::getValueName(int i) { 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/rfb_win32/Registry.h b/rfb_win32/Registry.h index 1998c497..68d535cd 100644 --- a/rfb_win32/Registry.h +++ b/rfb_win32/Registry.h @@ -1,5 +1,5 @@ -/* Copyright (C) 2002-2004 RealVNC Ltd. All Rights Reserved. - * +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * * This is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or @@ -22,9 +22,7 @@ #ifndef __RFB_WIN32_REGISTRY_H__ #define __RFB_WIN32_REGISTRY_H__ -#define WIN32_LEAN_AND_MEAN #include <windows.h> - #include <rfb_win32/Security.h> #include <rfb/util.h> @@ -45,9 +43,9 @@ namespace rfb { ~RegKey(); void setHKEY(HKEY key, bool freeKey); - protected: - HKEY operator=(const RegKey& k); - HKEY operator=(HKEY k); + private: + RegKey& operator=(const RegKey& k); + HKEY& operator=(const HKEY& k); public: // Returns true if key was created, false if already existed @@ -67,7 +65,9 @@ namespace rfb { void deleteValue(const TCHAR* name) const; - void awaitChange(bool watchSubTree, DWORD filter) 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; @@ -91,10 +91,11 @@ namespace rfb { bool isValue(const TCHAR* valname) const; - // Get the name of value number "i" + // 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: diff --git a/rfb_win32/SDisplay.cxx b/rfb_win32/SDisplay.cxx index 4916c486..0af50649 100644 --- a/rfb_win32/SDisplay.cxx +++ b/rfb_win32/SDisplay.cxx @@ -1,5 +1,5 @@ -/* Copyright (C) 2002-2004 RealVNC Ltd. All Rights Reserved. - * +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * * This is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or @@ -20,21 +20,19 @@ // // The SDisplay class encapsulates a particular system display. -#include <assert.h> - #include <rfb_win32/SDisplay.h> #include <rfb_win32/Service.h> -#include <rfb_win32/WMShatter.h> -#include <rfb_win32/osVersion.h> -#include <rfb_win32/Win32Util.h> -#include <rfb_win32/IntervalTimer.h> +#include <rfb_win32/TsSessions.h> #include <rfb_win32/CleanDesktop.h> - -#include <rfb/util.h> -#include <rfb/LogWriter.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> -#include <rfb/Configuration.h> using namespace rdr; using namespace rfb; @@ -44,209 +42,37 @@ static LogWriter vlog("SDisplay"); // - SDisplay-specific configuration options -BoolParameter rfb::win32::SDisplay::use_hooks("UseHooks", - "Set hooks in the operating system to capture display updates more efficiently", true); +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 in in use.", false); + "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 in in use.", false); + "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); -// - WM_TIMER ID values - -#define TIMER_CURSOR 1 -#define TIMER_UPDATE 2 -#define TIMER_UPDATE_AND_POLL 3 - - -// -=- Polling settings - -const int POLLING_SEGMENTS = 16; - -const int FG_POLLING_FPS = 20; -const int FG_POLLING_FS_INTERVAL = 1000 / FG_POLLING_FPS; -const int FG_POLLING_INTERVAL = FG_POLLING_FS_INTERVAL / POLLING_SEGMENTS; - -const int BG_POLLING_FS_INTERVAL = 5000; -const int BG_POLLING_INTERVAL = BG_POLLING_FS_INTERVAL / POLLING_SEGMENTS; - - -////////////////////////////////////////////////////////////////////////////// -// -// SDisplayCore -// - -// The SDisplay Core object is created by SDisplay's start() method -// and deleted by its stop() method. -// The Core must be created in the current input desktop in order -// to operate - SDisplay is responsible for ensuring that. -// The structures contained in the Core are manipulated directly -// by the SDisplay, which is also responsible for detecting when -// a desktop-switch is required. - -class rfb::win32::SDisplayCore : public MsgWindow { -public: - SDisplayCore(SDisplay* display); - ~SDisplayCore(); - - void setPixelBuffer(DeviceFrameBuffer* pb_); - - bool isRestartRequired(); - - virtual LRESULT processMessage(UINT msg, WPARAM wParam, LPARAM lParam); - - // -=- Timers - IntervalTimer pollTimer; - IntervalTimer cursorTimer; - - // -=- Input handling - rfb::win32::SPointer ptr; - rfb::win32::SKeyboard kbd; - rfb::win32::Clipboard clipboard; - - // -=- Hook handling objects used outside thread run() method - WMCopyRect wm_copyrect; - WMPoller wm_poller; - WMCursor cursor; - WMMonitor wm_monitor; - WMHooks wm_hooks; - WMBlockInput wm_input; - - // -=- Tidying the desktop - CleanDesktop cleanDesktop; - bool isWallpaperRemoved; - bool isPatternRemoved; - bool areEffectsDisabled; - - // -=- Full screen polling - int poll_next_y; - int poll_y_increment; - - // Are we using hooks? - bool use_hooks; - bool using_hooks; - - // State of the display object - SDisplay* display; -}; - -SDisplayCore::SDisplayCore(SDisplay* display_) -: MsgWindow(_T("SDisplayCore")), display(display_), - using_hooks(0), use_hooks(rfb::win32::SDisplay::use_hooks), - isWallpaperRemoved(rfb::win32::SDisplay::removeWallpaper), - isPatternRemoved(rfb::win32::SDisplay::removePattern), - areEffectsDisabled(rfb::win32::SDisplay::disableEffects), - pollTimer(getHandle(), TIMER_UPDATE_AND_POLL), - cursorTimer(getHandle(), TIMER_CURSOR) { - setPixelBuffer(display->pb); -} - -SDisplayCore::~SDisplayCore() { -} - -void SDisplayCore::setPixelBuffer(DeviceFrameBuffer* pb) { - poll_y_increment = (display->pb->height()+POLLING_SEGMENTS-1)/POLLING_SEGMENTS; - poll_next_y = display->screenRect.tl.y; - wm_hooks.setClipRect(display->screenRect); - wm_copyrect.setClipRect(display->screenRect); - wm_poller.setClipRect(display->screenRect); -} - - -bool SDisplayCore::isRestartRequired() { - // - We must restart the SDesktop if: - // 1. We are no longer in the input desktop. - // 2. The use_hooks setting has changed. - - // - Check that we are in the input desktop - if (rfb::win32::desktopChangeRequired()) - return true; - - // - Check that the hooks setting hasn't changed - // NB: We can't just check using_hooks because that can be false - // because they failed, even though use_hooks is true! - if (use_hooks != rfb::win32::SDisplay::use_hooks) - return true; - - // - Check that the desktop optimisation settings haven't changed - // This isn't very efficient, but it shouldn't change very often! - if ((isWallpaperRemoved != rfb::win32::SDisplay::removeWallpaper) || - (isPatternRemoved != rfb::win32::SDisplay::removePattern) || - (areEffectsDisabled != rfb::win32::SDisplay::disableEffects)) - return true; - - return false; -} - -LRESULT SDisplayCore::processMessage(UINT msg, WPARAM wParam, LPARAM lParam) { - switch (msg) { - - case WM_TIMER: - - if (display->server && display->server->clientsReadyForUpdate()) { - - // - Check that the SDesktop doesn't need restarting - if (isRestartRequired()) { - display->restart(); - return 0; - } - - // - Action depends on the timer message type - switch (wParam) { - - // POLL THE SCREEN - case TIMER_UPDATE_AND_POLL: - // Handle window dragging, polling of consoles, etc. - while (wm_poller.processEvent()) {} - - // Poll the next strip of the screen (in Screen coordinates) - { - Rect pollrect = display->screenRect; - if (poll_next_y >= pollrect.br.y) { - // Yes. Reset the counter and return - poll_next_y = pollrect.tl.y; - } else { - // No. Poll the next section - pollrect.tl.y = poll_next_y; - poll_next_y += poll_y_increment; - pollrect.br.y = min(poll_next_y, pollrect.br.y); - display->add_changed(pollrect); - } - } - break; - - case TIMER_CURSOR: - display->triggerUpdate(); - break; - - }; - - } - return 0; - - }; - - return MsgWindow::processMessage(msg, wParam, lParam); -} - ////////////////////////////////////////////////////////////////////////////// // // SDisplay // +typedef BOOL (WINAPI *_LockWorkStation_proto)(); +DynamicFn<_LockWorkStation_proto> _LockWorkStation(_T("user32.dll"), "LockWorkStation"); + // -=- Constructor/Destructor -SDisplay::SDisplay(const TCHAR* devName) - : server(0), change_tracker(true), pb(0), - deviceName(tstrDup(devName)), device(0), releaseDevice(false), - core(0), statusLocation(0) +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); } @@ -271,168 +97,246 @@ SDisplay::~SDisplay() 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"); } - // Clear the change tracker - change_tracker.clear(); + // Initialise the change tracker and clipper + updates.clear(); + clipper.setUpdateTracker(server); // Create the framebuffer object - recreatePixelBuffer(); + recreatePixelBuffer(true); // Create the SDisplayCore - core = new SDisplayCore(this); - assert(core); - - // Start display monitor and clipboard handler - core->wm_monitor.setNotifier(this); - core->clipboard.setNotifier(this); + 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) - core->cleanDesktop.disablePattern(); + cleanDesktop->disablePattern(); if (removeWallpaper) - core->cleanDesktop.disableWallpaper(); + cleanDesktop->disableWallpaper(); if (disableEffects) - core->cleanDesktop.disableEffects(); - - // Start hooks - core->wm_hooks.setClipRect(screenRect); - if (core->use_hooks) { - // core->wm_hooks.setDiagnosticRange(0, 0x400-1); - core->using_hooks = core->wm_hooks.setUpdateTracker(this); - if (!core->using_hooks) - vlog.debug("hook subsystem failed to initialise"); - } - - // Set up timers - core->pollTimer.start(core->using_hooks ? BG_POLLING_INTERVAL : FG_POLLING_INTERVAL); - core->cursorTimer.start(10); + cleanDesktop->disableEffects(); + isWallpaperRemoved = removeWallpaper; + isPatternRemoved = removePattern; + areEffectsDisabled = disableEffects; +} - // Register an interest in faked copyrect events - core->wm_copyrect.setUpdateTracker(&change_tracker); - core->wm_copyrect.setClipRect(screenRect); +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); +} - // Polling of particular windows on the desktop - core->wm_poller.setUpdateTracker(&change_tracker); - core->wm_poller.setClipRect(screenRect); - vlog.debug("started"); +bool SDisplay::areHooksAvailable() { + return WMHooks::areAvailable(); +} - if (statusLocation) *statusLocation = true; +bool SDisplay::isDriverAvailable() { + return SDisplayCoreDriver::isAvailable(); } -void SDisplay::stop() -{ - vlog.debug("stopping"); - if (core) { - // If SDisplay was actually active then perform the disconnect action - CharArray action = disconnectAction.getData(); - if (stricmp(action.buf, "Logoff") == 0) { - ExitWindowsEx(EWX_LOGOFF, 0); - } else if (stricmp(action.buf, "Lock") == 0) { - typedef BOOL (WINAPI *_LockWorkStation_proto)(); - DynamicFn<_LockWorkStation_proto> _LockWorkStation(_T("user32.dll"), "LockWorkStation"); - if (_LockWorkStation.isValid()) - (*_LockWorkStation)(); - else - ExitWindowsEx(EWX_LOGOFF, 0); - } - } - delete core; - core = 0; - delete pb; - pb = 0; - if (device) { - if (releaseDevice) - ReleaseDC(0, device); - else - DeleteDC(device); - } - device = 0; - if (server) - server->setPixelBuffer(0); - server = 0; - vlog.debug("stopped"); +bool SDisplay::isRestartRequired() { + // - We must restart the SDesktop if: + // 1. We are no longer in the input desktop. + // 2. The any setting has changed. - if (statusLocation) *statusLocation = false; + // - 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::restart() { - vlog.debug("restarting"); - // Close down the hooks - delete core; - core = 0; + +void SDisplay::restartCore() { + vlog.info("restarting"); + + // Stop the existing Core related resources + stopCore(); try { - // Re-start the hooks if possible - start(server); - vlog.debug("restarted"); + // Start a new Core if possible + startCore(); + vlog.info("restarted"); } catch (rdr::Exception& e) { - // If start() fails then we MUST disconnect all clients, - // to cause the server to stop using the desktop. + // 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 + // and the server will crash. server->closeClients(e.str()); } } -void SDisplay::pointerEvent(const Point& pos, rdr::U8 buttonmask) { +void SDisplay::pointerEvent(const Point& pos, int buttonmask) { if (pb->getRect().contains(pos)) { Point screenPos = pos.translate(screenRect.tl); - core->ptr.pointerEvent(screenPos, buttonmask); + // - 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) { - core->kbd.keyEvent(key, 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; - core->clipboard.setClipText(clip_sz.buf); + clipboard->setClipText(clip_sz.buf); } void SDisplay::framebufferUpdateRequest() { - triggerUpdate(); + SetEvent(updateEvent); } Point SDisplay::getFbSize() { bool startAndStop = !core; + // If not started, do minimal initialisation to get desktop size. - if (startAndStop) recreatePixelBuffer(); + if (startAndStop) + recreatePixelBuffer(); Point result = Point(pb->width(), pb->height()); + // Destroy the initialised structures. - if (startAndStop) stop(); + if (startAndStop) + stopCore(); return result; } void -SDisplay::add_changed(const Region& rgn) { - change_tracker.add_changed(rgn); - triggerUpdate(); -} - -void -SDisplay::add_copied(const Region& dest, const Point& delta) { - change_tracker.add_copied(dest, delta); - triggerUpdate(); -} - - -void SDisplay::notifyClipboardChanged(const char* text, int len) { vlog.debug("clipboard text changed"); if (server) @@ -462,36 +366,42 @@ SDisplay::notifyDisplayEvent(WMMonitor::Notifier::DisplayEventType evt) { } } -bool +void SDisplay::processEvent(HANDLE event) { if (event == updateEvent) { - vlog.info("processEvent"); + vlog.write(120, "processEvent"); ResetEvent(updateEvent); // - If the SDisplay isn't even started then quit now if (!core) { vlog.error("not start()ed"); - return true; + return; } // - Ensure that the disableLocalInputs flag is respected - core->wm_input.blockInputs(SDisplay::disableLocalInputs); + 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 (core->isRestartRequired()) { - restart(); - return true; + if (isRestartRequired()) { + restartCore(); + return; } - - // *** window dragging can be improved - more frequent, more cunning about updates - while (core->wm_copyrect.processEvent()) {} - + + // - 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 = core->cursor.getCursorInfo(); + 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; @@ -505,7 +415,7 @@ SDisplay::processEvent(HANDLE event) { // Update the cursor position // NB: First translate from Screen coordinates to Desktop Point desktopPos = info.position.translate(screenRect.tl.negate()); - server->setCursorPos(desktopPos.x, desktopPos.y); + server->setCursorPos(desktopPos); try_update = true; old_cursor = info; @@ -513,100 +423,102 @@ SDisplay::processEvent(HANDLE event) { // Flush any changes to the server try_update = flushChangeTracker() || try_update; - if (try_update) + if (try_update) { server->tryUpdate(); + } } - } else { - CloseHandle(event); - return false; + return; } - return true; + throw rdr::Exception("No such event"); } // -=- Protected methods void -SDisplay::recreatePixelBuffer() { - vlog.debug("attaching to device %s", deviceName); - +SDisplay::recreatePixelBuffer(bool force) { // Open the specified display device - HDC new_device; - if (deviceName.buf) { - new_device = ::CreateDC(_T("DISPLAY"), deviceName.buf, NULL, NULL); - releaseDevice = false; - } else { - // If no device is specified, open entire screen. - // Doing this with CreateDC creates problems on multi-monitor systems. - new_device = ::GetDC(0); - releaseDevice = true; + // If 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); } - if (!new_device) - throw SystemException("cannot open the display", GetLastError()); - // Get the coordinates of the entire virtual display + // Get the coordinates of the specified dispay device Rect newScreenRect; - { - WindowDC rootDC(0); - RECT r; - if (!GetClipBox(rootDC, &r)) - throw rdr::SystemException("GetClipBox", GetLastError()); - newScreenRect = Rect(r.left, r.top, r.right, r.bottom); + 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(); } - // Create a DeviceFrameBuffer attached to it - DeviceFrameBuffer* new_buffer = new DeviceFrameBuffer(new_device); + // 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; + } - // Has anything actually changed about the screen or the buffer? - if (!pb || - (!newScreenRect.equals(screenRect)) || - (!new_buffer->getPF().equal(pb->getPF()))) - { - // Yes. Update the buffer state. - screenRect = newScreenRect; - vlog.debug("creating pixel buffer for device"); + // Flush any existing changes to the server + flushChangeTracker(); - // 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; - // Replace the old PixelBuffer - if (pb) delete pb; - if (device) DeleteDC(device); - pb = new_buffer; - device = new_device; + // Create a DeviceFrameBuffer attached to the new device + vlog.debug("creating pixel buffer"); + DeviceFrameBuffer* new_buffer = new DeviceFrameBuffer(*new_device); - // Initialise the pixels - pb->grabRegion(pb->getRect()); + // Replace the old PixelBuffer + screenRect = newScreenRect; + pb = new_buffer; + device = new_device; - // Prevent future grabRect operations from throwing exceptions - pb->setIgnoreGrabErrors(true); + // Initialise the pixels + pb->grabRegion(pb->getRect()); - // Update the SDisplayCore if required - if (core) - core->setPixelBuffer(pb); + // Prevent future grabRect operations from throwing exceptions + pb->setIgnoreGrabErrors(true); - // Inform the server of the changes - if (server) - server->setPixelBuffer(pb); + // Update the clipping update tracker + clipper.setClipRect(pb->getRect()); - } else { - delete new_buffer; - DeleteDC(new_device); - } + // 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 (change_tracker.is_empty()) + if (updates.is_empty()) return false; + + vlog.write(120, "flushChangeTracker"); + // Translate the update coordinates from Screen coords to Desktop - change_tracker.translate(screenRect.tl.negate()); - // Flush the updates through - change_tracker.get_update(*server); - change_tracker.clear(); - return true; -} + updates.translate(screenRect.tl.negate()); -void SDisplay::triggerUpdate() { - if (core) - SetEvent(updateEvent); + // Clip the updates & flush them to the server + updates.copyTo(&clipper); + updates.clear(); + return true; } diff --git a/rfb_win32/SDisplay.h b/rfb_win32/SDisplay.h index c4c08bf3..6dbb50a5 100644 --- a/rfb_win32/SDisplay.h +++ b/rfb_win32/SDisplay.h @@ -1,5 +1,5 @@ -/* Copyright (C) 2002-2004 RealVNC Ltd. All Rights Reserved. - * +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * * This is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or @@ -20,31 +20,21 @@ // // The SDisplay class encapsulates a system display. -// *** THIS INTERFACE NEEDS TIDYING TO SEPARATE COORDINATE SYSTEMS BETTER *** - #ifndef __RFB_SDISPLAY_H__ #define __RFB_SDISPLAY_H__ -#define WIN32_LEAN_AND_MEAN -#include <windows.h> - #include <rfb/SDesktop.h> #include <rfb/UpdateTracker.h> #include <rfb/Configuration.h> -#include <rfb/util.h> - -#include <winsock2.h> -#include <rfb_win32/Win32Util.h> -#include <rfb_win32/SocketManager.h> -#include <rfb_win32/DeviceFrameBuffer.h> +#include <rfb_win32/Handle.h> +#include <rfb_win32/EventManager.h> #include <rfb_win32/SInput.h> #include <rfb_win32/Clipboard.h> -#include <rfb_win32/MsgWindow.h> +#include <rfb_win32/CleanDesktop.h> #include <rfb_win32/WMCursor.h> -#include <rfb_win32/WMHooks.h> #include <rfb_win32/WMNotifier.h> -#include <rfb_win32/WMWindowCopyRect.h> -#include <rfb_win32/WMPoller.h> +#include <rfb_win32/DeviceFrameBuffer.h> +#include <rfb_win32/DeviceContext.h> namespace rfb { @@ -54,33 +44,33 @@ namespace rfb { // -=- SDisplay // - class SDisplayCore; + 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, - UpdateTracker, - public SocketManager::EventHandler + public EventHandler { public: - SDisplay(const TCHAR* device=0); + SDisplay(); virtual ~SDisplay(); // -=- SDesktop interface virtual void start(VNCServer* vs); virtual void stop(); - virtual void pointerEvent(const Point& pos, rdr::U8 buttonmask); + 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(); - // -=- UpdateTracker - - virtual void add_changed(const Region& rgn); - virtual void add_copied(const Region& dest, const Point& delta); - // -=- Clipboard virtual void notifyClipboardChanged(const char* text, int len); @@ -92,7 +82,7 @@ namespace rfb { // -=- EventHandler interface HANDLE getUpdateEvent() {return updateEvent;} - virtual bool processEvent(HANDLE event); + virtual void processEvent(HANDLE event); // -=- Notification of whether or not SDisplay is started @@ -100,38 +90,62 @@ namespace rfb { friend class SDisplayCore; - static BoolParameter use_hooks; + 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: - void restart(); - void recreatePixelBuffer(); + bool isRestartRequired(); + void startCore(); + void stopCore(); + void restartCore(); + void recreatePixelBuffer(bool force=false); bool flushChangeTracker(); // true if flushed, false if empty - void triggerUpdate(); - VNCServer* server; // -=- Display pixel buffer DeviceFrameBuffer* pb; - TCharArray deviceName; - HDC device; - bool releaseDevice; + DeviceContext* device; // -=- The coordinates of Window's entire virtual Screen Rect screenRect; - // -=- All changes are collected in Display coords and merged - SimpleUpdateTracker change_tracker; + // -=- 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 + // Cursor + WMCursor* cursor; WMCursor::Info old_cursor; Region old_cursor_region; Point cursor_renderpos; diff --git a/rfb_win32/SDisplayCoreDriver.h b/rfb_win32/SDisplayCoreDriver.h new file mode 100644 index 00000000..5fea75cc --- /dev/null +++ b/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/rfb_win32/SDisplayCorePolling.cxx b/rfb_win32/SDisplayCorePolling.cxx new file mode 100644 index 00000000..fc57ecd0 --- /dev/null +++ b/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/rfb_win32/SDisplayCorePolling.h b/rfb_win32/SDisplayCorePolling.h new file mode 100644 index 00000000..9e1b5ad1 --- /dev/null +++ b/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/rfb_win32/SDisplayCoreWMHooks.cxx b/rfb_win32/SDisplayCoreWMHooks.cxx new file mode 100644 index 00000000..10b88e08 --- /dev/null +++ b/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/rfb_win32/SDisplayCoreWMHooks.h b/rfb_win32/SDisplayCoreWMHooks.h new file mode 100644 index 00000000..24fa5cdc --- /dev/null +++ b/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/rfb_win32/SInput.cxx b/rfb_win32/SInput.cxx index 457a8619..db59287a 100644 --- a/rfb_win32/SInput.cxx +++ b/rfb_win32/SInput.cxx @@ -1,5 +1,5 @@ -/* Copyright (C) 2002-2003 RealVNC Ltd. All Rights Reserved. - * +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * * This is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or @@ -26,23 +26,30 @@ #define XK_CURRENCY #include <rfb/keysymdef.h> -// * Force the windows headers to include all the SendInput stuff -#define _WIN32_WINNT 0x401 - +#include <tchar.h> #include <rfb_win32/SInput.h> +#include <rfb_win32/MonitorInfo.h> #include <rfb_win32/Service.h> -#include <rfb/LogWriter.h> #include <rfb_win32/OSVersion.h> -#include <rfb_win32/Win32Util.h> -#include "keymap.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 @@ -68,7 +75,7 @@ win32::SPointer::SPointer() } void -win32::SPointer::pointerEvent(const Point& pos, rdr::U8 buttonmask) +win32::SPointer::pointerEvent(const Point& pos, int buttonmask) { // - We are specifying absolute coordinates DWORD flags = MOUSEEVENTF_ABSOLUTE; @@ -118,7 +125,7 @@ win32::SPointer::pointerEvent(const Point& pos, rdr::U8 buttonmask) // The event lies outside the primary monitor. Under Win2K, we can just use // SendInput, which allows us to provide coordinates scaled to the virtual desktop. // SendInput is available on all multi-monitor-aware platforms. -#ifdef SM_CXVIRTUALSCREEN +#ifdef RFB_HAVE_SENDINPUT if (osVersion.isPlatformNT) { if (!_SendInput.isValid()) throw rdr::Exception("SendInput not available"); diff --git a/rfb_win32/SInput.h b/rfb_win32/SInput.h index dcd779ef..2a0b3e67 100644 --- a/rfb_win32/SInput.h +++ b/rfb_win32/SInput.h @@ -1,5 +1,5 @@ -/* Copyright (C) 2002-2003 RealVNC Ltd. All Rights Reserved. - * +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * * This is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or @@ -32,8 +32,6 @@ namespace rfb { - class CMsgWriter; - namespace win32 { // -=- Pointer event handling @@ -44,7 +42,7 @@ namespace rfb { // - Create a pointer event at a the given coordinates, with the // specified button state. The event must be specified using // Screen coordinates. - void pointerEvent(const Point& pos, rdr::U8 buttonmask); + void pointerEvent(const Point& pos, int buttonmask); protected: Point last_position; rdr::U8 last_buttonmask; diff --git a/rfb_win32/Security.cxx b/rfb_win32/Security.cxx new file mode 100644 index 00000000..985f00cb --- /dev/null +++ b/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/rfb_win32/Security.h b/rfb_win32/Security.h index d92e314f..1e2e9068 100644 --- a/rfb_win32/Security.h +++ b/rfb_win32/Security.h @@ -1,5 +1,5 @@ -/* Copyright (C) 2002-2004 RealVNC Ltd. All Rights Reserved. - * +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * * This is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or @@ -25,16 +25,10 @@ #define __RFB_WIN32_SECURITY_H__ #include <rdr/types.h> -#include <rdr/Exception.h> -#include <rfb_win32/Win32Util.h> +#include <rfb_win32/LocalMem.h> #include <rfb_win32/TCharArray.h> - -#include <lmcons.h> -#include <Accctrl.h> #include <aclapi.h> -#include <list> - namespace rfb { namespace win32 { @@ -42,14 +36,7 @@ namespace rfb { struct Trustee : public TRUSTEE { Trustee(const TCHAR* name, TRUSTEE_FORM form=TRUSTEE_IS_NAME, - TRUSTEE_TYPE type=TRUSTEE_IS_UNKNOWN) - { - pMultipleTrustee = 0; - MultipleTrusteeOperation = NO_MULTIPLE_TRUSTEE; - TrusteeForm = form; - TrusteeType = type; - ptstrName = (TCHAR*)name; - } + TRUSTEE_TYPE type=TRUSTEE_IS_UNKNOWN); }; struct ExplicitAccess : public EXPLICIT_ACCESS { @@ -57,86 +44,56 @@ namespace rfb { TRUSTEE_FORM type, DWORD perms, ACCESS_MODE mode, - DWORD inherit=0) - { - Trustee = rfb::win32::Trustee(name, type); - grfAccessPermissions = perms; - grfAccessMode = mode; - grfInheritance = inherit; - } + DWORD inherit=0); }; // Helper class for building access control lists struct AccessEntries { - AccessEntries() : entries(0), entry_count(0) {} - ~AccessEntries() {delete [] entries;} - void allocMinEntries(int count) { - if (count > entry_count) { - EXPLICIT_ACCESS* new_entries = new EXPLICIT_ACCESS[entry_count+1]; - if (entries) { - memcpy(new_entries, entries, sizeof(EXPLICIT_ACCESS) * entry_count); - delete entries; - } - entries = new_entries; - } - } + AccessEntries(); + ~AccessEntries(); + void allocMinEntries(int count); void addEntry(const TCHAR* trusteeName, DWORD permissions, - ACCESS_MODE mode) - { - allocMinEntries(entry_count+1); - ZeroMemory(&entries[entry_count], sizeof(EXPLICIT_ACCESS)); - entries[entry_count] = ExplicitAccess(trusteeName, TRUSTEE_IS_NAME, permissions, mode); - entry_count++; - } - void addEntry(const PSID sid, DWORD permissions, ACCESS_MODE mode) { - allocMinEntries(entry_count+1); - ZeroMemory(&entries[entry_count], sizeof(EXPLICIT_ACCESS)); - entries[entry_count] = ExplicitAccess((TCHAR*)sid, TRUSTEE_IS_SID, permissions, mode); - entry_count++; - } + 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 { - Sid() : sid(0) {} - Sid(PSID sid_) : sid(sid_) {} - ~Sid() { - if (sid) FreeSid(sid); - } - operator PSID() const {return sid;} - PSID operator=(const PSID sid_) { - if (sid) FreeSid(sid); - sid = sid_; - } - - static PSID Administrators() { - PSID sid = 0; - SID_IDENTIFIER_AUTHORITY ntAuth = SECURITY_NT_AUTHORITY; - if (!AllocateAndInitializeSid(&ntAuth, 2, - SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_ADMINS, - 0, 0, 0, 0, 0, 0, - &sid)) - throw rdr::SystemException("Sid::Administrators", GetLastError()); - return sid; - } - static PSID SYSTEM() { - PSID sid = 0; - SID_IDENTIFIER_AUTHORITY ntAuth = SECURITY_NT_AUTHORITY; - if (!AllocateAndInitializeSid(&ntAuth, 1, - SECURITY_LOCAL_SYSTEM_RID, 0, 0, 0, 0, 0, 0, 0, - &sid)) - throw rdr::SystemException("Sid::SYSTEM", GetLastError()); - return sid; - } - - protected: - PSID sid; + 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) {} @@ -145,22 +102,7 @@ namespace rfb { }; // Create a new ACL based on supplied entries and, if supplied, existing ACL - static PACL CreateACL(const AccessEntries& ae, PACL existing_acl=0) { - typedef DWORD (WINAPI *_SetEntriesInAcl_proto) (ULONG, PEXPLICIT_ACCESS, PACL, PACL*); -#ifdef UNICODE - const char* fnName = "SetEntriesInAclW"; -#else - const char* fnName = "SetEntriesInAclA"; -#endif - DynamicFn<_SetEntriesInAcl_proto> _SetEntriesInAcl(_T("advapi32.dll"), fnName); - if (!_SetEntriesInAcl.isValid()) - throw rdr::SystemException("CreateACL failed; no SetEntriesInAcl", ERROR_CALL_NOT_IMPLEMENTED); - PACL new_dacl; - DWORD result; - if ((result = (*_SetEntriesInAcl)(ae.entry_count, ae.entries, existing_acl, &new_dacl)) != ERROR_SUCCESS) - throw rdr::SystemException("SetEntriesInAcl", result); - return new_dacl; - } + PACL CreateACL(const AccessEntries& ae, PACL existing_acl=0); // Helper class for memory-management of self-relative SecurityDescriptors struct SecurityDescriptorPtr : LocalMem { @@ -172,24 +114,7 @@ namespace rfb { // Create a new self-relative Security Descriptor, owned by SYSTEM/Administrators, // with the supplied DACL and no SACL. The returned value can be assigned // to a SecurityDescriptorPtr to be managed. - static PSECURITY_DESCRIPTOR CreateSdWithDacl(const PACL dacl) { - SECURITY_DESCRIPTOR absSD; - if (!InitializeSecurityDescriptor(&absSD, SECURITY_DESCRIPTOR_REVISION)) - throw rdr::SystemException("InitializeSecurityDescriptor", GetLastError()); - Sid owner(Sid::SYSTEM()); - if (!SetSecurityDescriptorOwner(&absSD, owner, FALSE)) - throw rdr::SystemException("SetSecurityDescriptorOwner", GetLastError()); - Sid group(Sid::Administrators()); - if (!SetSecurityDescriptorGroup(&absSD, group, FALSE)) - throw rdr::SystemException("SetSecurityDescriptorGroupp", GetLastError()); - if (!SetSecurityDescriptorDacl(&absSD, TRUE, dacl, FALSE)) - throw rdr::SystemException("SetSecurityDescriptorDacl", GetLastError()); - DWORD sdSize = GetSecurityDescriptorLength(&absSD); - SecurityDescriptorPtr sd(sdSize); - if (!MakeSelfRelativeSD(&absSD, sd, &sdSize)) - throw rdr::SystemException("MakeSelfRelativeSD", GetLastError()); - return sd.takeSD(); - } + PSECURITY_DESCRIPTOR CreateSdWithDacl(const PACL dacl); } diff --git a/rfb_win32/Service.cxx b/rfb_win32/Service.cxx index b00c2900..2b11a22d 100644 --- a/rfb_win32/Service.cxx +++ b/rfb_win32/Service.cxx @@ -1,5 +1,5 @@ -/* Copyright (C) 2002-2003 RealVNC Ltd. All Rights Reserved. - * +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * * This is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or @@ -20,15 +20,15 @@ #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/Win32Util.h> #include <rfb_win32/OSVersion.h> #include <rfb/Threading.h> -#include <rfb/LogWriter.h> -#include <rfb/util.h> +#include <logmessages/messages.h> #include <rdr/Exception.h> +#include <rfb/LogWriter.h> -#include <logmessages/messages.h> using namespace rdr; using namespace rfb; @@ -42,22 +42,26 @@ static LogWriter vlog("Service"); Service* service = 0; VOID WINAPI serviceHandler(DWORD control) { - vlog.debug("service control %u", control); switch (control) { case SERVICE_CONTROL_INTERROGATE: + vlog.info("cmd: report status"); service->setStatus(); - break; + return; case SERVICE_CONTROL_PARAMCHANGE: + vlog.info("cmd: param change"); service->readParams(); - break; + return; case SERVICE_CONTROL_SHUTDOWN: + vlog.info("cmd: OS shutdown"); service->osShuttingDown(); - break; + return; case SERVICE_CONTROL_STOP: + vlog.info("cmd: stop"); service->setStatus(SERVICE_STOP_PENDING); service->stop(); - break; - } + return; + }; + vlog.debug("cmd: unknown %lu", control); } @@ -87,11 +91,14 @@ const TCHAR* ServiceMsgWindow::baseName = _T("ServiceWindow:"); 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) { - vlog.error("unable to register service control handler"); - return; + 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); @@ -166,9 +173,9 @@ Service::setStatus(DWORD state) { 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); - stop(); } vlog.debug("set status to %u(%u)", state, status.dwCheckPoint); } @@ -597,7 +604,7 @@ bool rfb::win32::stopService(const TCHAR* name) { return true; } -void rfb::win32::printServiceStatus(const TCHAR* name) { +DWORD rfb::win32::getServiceState(const TCHAR* name) { if (osVersion.isPlatformNT) { // - Open the SCM ServiceHandle scm = OpenSCManager(NULL, NULL, SC_MANAGER_CONNECT); @@ -614,25 +621,25 @@ void rfb::win32::printServiceStatus(const TCHAR* name) { if (!ControlService(service, SERVICE_CONTROL_INTERROGATE, (SERVICE_STATUS*)&status)) throw rdr::SystemException("unable to query the service", GetLastError()); - printf("Service is in the "); - switch (status.dwCurrentState) { - case SERVICE_RUNNING: printf("running"); break; - case SERVICE_STOPPED: printf("stopped"); break; - case SERVICE_STOP_PENDING: printf("stop pending"); break; - default: printf("unknown (%lu)", status.dwCurrentState); break; - }; - printf(" state.\n"); - + return status.dwCurrentState; } else { HWND service_window = findServiceWindow(name); - printf("Service is in the "); - if (!service_window) printf("stopped"); - else printf("running"); - printf(" state.\n"); + 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; -}
\ No newline at end of file +} diff --git a/rfb_win32/Service.h b/rfb_win32/Service.h index 164381a1..00abe108 100644 --- a/rfb_win32/Service.h +++ b/rfb_win32/Service.h @@ -1,5 +1,5 @@ -/* Copyright (C) 2002-2003 RealVNC Ltd. All Rights Reserved. - * +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * * This is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or @@ -27,7 +27,6 @@ #ifndef __RFB_WIN32_SERVICE_H__ #define __RFB_WIN32_SERVICE_H__ -#define WIN32_LEAN_AND_MEAN #include <windows.h> namespace rfb { @@ -61,13 +60,13 @@ namespace rfb { // - Service control notifications // To get notified when the OS is shutting down - virtual void osShuttingDown() = 0; + virtual void osShuttingDown() {}; // To get notified when the service parameters change - virtual void readParams() = 0; + virtual void readParams() {}; // To cause the serviceMain() routine to return - virtual void stop() = 0; + virtual void stop() {}; public: SERVICE_STATUS_HANDLE status_handle; @@ -111,7 +110,13 @@ namespace rfb { bool startService(const TCHAR* name); bool stopService(const TCHAR* name); - void printServiceStatus(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(); diff --git a/rfb_win32/SocketManager.cxx b/rfb_win32/SocketManager.cxx index 6d1980cc..1d52bc86 100644 --- a/rfb_win32/SocketManager.cxx +++ b/rfb_win32/SocketManager.cxx @@ -1,5 +1,5 @@ -/* Copyright (C) 2002-2004 RealVNC Ltd. All Rights Reserved. - * +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * * This is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or @@ -18,10 +18,8 @@ // -=- SocketManager.cxx -#define WIN32_LEAN_AND_MEAN #include <winsock2.h> -#include <assert.h> - +#include <list> #include <rfb/LogWriter.h> #include <rfb_win32/SocketManager.h> @@ -33,218 +31,183 @@ static LogWriter vlog("SocketManager"); // -=- SocketManager -SocketManager::SocketManager() : sockets(0), events(0), nSockets(0), nAvail(0) { +SocketManager::SocketManager() { } SocketManager::~SocketManager() { - for (int i=0; i<nSockets; i++) { - if (!sockets[i].is_event) - WSACloseEvent(events[i]); - } - delete [] events; - delete [] sockets; } -void SocketManager::addListener(network::SocketListener* sock_, network::SocketServer* srvr) { - WSAEVENT event = WSACreateEvent(); - assert(event != WSA_INVALID_EVENT); - addListener(sock_, event, srvr); +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::addSocket(network::Socket* sock_, network::SocketServer* srvr) { + +void SocketManager::addListener(network::SocketListener* sock_, + network::SocketServer* srvr, + AddressChangeNotifier* acn) { WSAEVENT event = WSACreateEvent(); - assert(event != WSA_INVALID_EVENT); - addSocket(sock_, event, srvr); + 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"); +} -BOOL SocketManager::getMessage(MSG* msg, HWND hwnd, UINT minMsg, UINT maxMsg) { - while (true) { - // First check for idle timeout - network::SocketServer* server = 0; - int timeout = 0; - for (int i=0; i<nSockets; i++) { - if (!sockets[i].is_event && - sockets[i].server != server) { - server = sockets[i].server; - int t = server->checkTimeouts(); - if (t > 0 && (timeout == 0 || t < timeout)) - timeout = t; - } - } - if (timeout == 0) - timeout = INFINITE; - - // - Network IO is less common than messages - process it first - DWORD result; - if (nSockets) { - result = WaitForMultipleObjects(nSockets, events, FALSE, 0); - if (result == WAIT_TIMEOUT) { - if (PeekMessage(msg, hwnd, minMsg, maxMsg, PM_REMOVE)) - return msg->message != WM_QUIT; - - result = MsgWaitForMultipleObjects(nSockets, events, FALSE, timeout, - QS_ALLINPUT); - if (result == WAIT_OBJECT_0 + nSockets) { - if (PeekMessage(msg, hwnd, minMsg, maxMsg, PM_REMOVE)) - return msg->message != WM_QUIT; - continue; - } - } - } else - return GetMessage(msg, hwnd, minMsg, maxMsg); - - if ((result >= WAIT_OBJECT_0) && (result < (WAIT_OBJECT_0 + nSockets))) { - int index = result - WAIT_OBJECT_0; - - // - Process a socket event - - if (sockets[index].is_event) { - // Process a general Win32 event - // NB: The handler must reset the event! - - if (!sockets[index].handler->processEvent(events[index])) { - removeSocket(index); - continue; - } - } else if (sockets[index].is_conn) { - // Process data from an active connection - - // Cancel event notification for this socket - if (WSAEventSelect(sockets[index].fd, events[index], 0) == SOCKET_ERROR) - vlog.info("unable to disable WSAEventSelect:%u", WSAGetLastError()); - - // Reset the event object - WSAResetEvent(events[index]); - - // Call the socket server to process the event - if (!sockets[index].server->processSocketEvent(sockets[index].sock.conn)) { - removeSocket(index); - continue; - } - - // Re-instate the required socket event - // If the read event is still valid, the event object gets set here - if (WSAEventSelect(sockets[index].fd, events[index], FD_READ | FD_CLOSE) == SOCKET_ERROR) - throw rdr::SystemException("unable to re-enable WSAEventSelect:%u", WSAGetLastError()); - - } else { - // Accept an incoming connection - vlog.debug("accepting incoming connection"); - - // What kind of event is this? - WSANETWORKEVENTS network_events; - WSAEnumNetworkEvents(sockets[index].fd, events[index], &network_events); - if (network_events.lNetworkEvents & FD_ACCEPT) { - network::Socket* new_sock = sockets[index].sock.listener->accept(); - if ((sockets[index].server)->getDisable()) { - delete new_sock; - new_sock = 0; - } - if (new_sock) { - sockets[index].server->addClient(new_sock); - addSocket(new_sock, sockets[index].server); - } - } else if (network_events.lNetworkEvents & FD_CLOSE) { - vlog.info("deleting listening socket"); - network::SocketListener* s = sockets[index].sock.listener; - removeSocket(index); - delete s; - } else { - vlog.error("unknown network event for listener"); - } +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); +} - } - } else if (result == WAIT_FAILED) { - throw rdr::SystemException("unable to wait for events", GetLastError()); +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"); } -void SocketManager::resizeArrays(int numSockets) { - if (nAvail >= numSockets) return; - while (nAvail < numSockets) - nAvail = max(16, nAvail*2); +int SocketManager::checkTimeouts() { + network::SocketServer* server = 0; + int timeout = EventManager::checkTimeouts(); - SocketInfo* newinfo = new SocketInfo[nAvail]; - HANDLE* newevents = new HANDLE[nAvail]; - for (int i=0; i<nSockets; i++) { - newinfo[i] = sockets[i]; - newevents[i] = events[i]; - } - delete [] sockets; - delete [] events; - sockets = newinfo; - events = newevents; -} + std::map<HANDLE,ListenInfo>::iterator i; + for (i=listeners.begin(); i!=listeners.end(); i++) + soonestTimeout(&timeout, i->second.server->checkTimeouts()); -void SocketManager::addSocket(network::Socket* sock, HANDLE event, network::SocketServer* server) { - resizeArrays(nSockets+1); + 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); + } - sockets[nSockets].sock.conn = sock; - sockets[nSockets].fd = sock->getFd(); - sockets[nSockets].server = server; - events[nSockets] = event; - sockets[nSockets].is_conn = true; - sockets[nSockets].is_event = false; + std::list<network::Socket*>::iterator k; + for (k=shutdownSocks.begin(); k!=shutdownSocks.end(); k++) + remSocket(*k); - if (WSAEventSelect(sock->getFd(), event, FD_READ | FD_CLOSE) == SOCKET_ERROR) - throw rdr::SystemException("unable to select on socket", WSAGetLastError()); - nSockets++; + return timeout; } -void SocketManager::addListener(network::SocketListener* sock, HANDLE event, network::SocketServer* server) { - resizeArrays(nSockets+1); - sockets[nSockets].sock.listener = sock; - sockets[nSockets].fd = sock->getFd(); - sockets[nSockets].server = server; - events[nSockets] = event; - sockets[nSockets].is_conn = false; - sockets[nSockets].is_event = false; +void SocketManager::processEvent(HANDLE event) { + if (listeners.count(event)) { + ListenInfo li = listeners[event]; - if (WSAEventSelect(sock->getFd(), event, FD_ACCEPT | FD_CLOSE) == SOCKET_ERROR) - throw rdr::SystemException("unable to select on listener", WSAGetLastError()); - nSockets++; -} + // Accept an incoming connection + vlog.debug("accepting incoming connection"); -void SocketManager::remListener(network::SocketListener* sock) { - for (int index=0; index<nSockets; index++) { - if (!sockets[index].is_conn && - !sockets[index].is_event) { - vlog.debug("removing listening socket"); - removeSocket(index); - delete sock; + // 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]; -void SocketManager::addEvent(HANDLE event, EventHandler* ecb) { - resizeArrays(nSockets+1); + try { + // Process data from an active connection - sockets[nSockets].handler = ecb; - events[nSockets] = event; - sockets[nSockets].is_conn = false; - sockets[nSockets].is_event = true; - - nSockets++; -} + // Cancel event notification for this socket + if (WSAEventSelect(ci.sock->getFd(), event, 0) == SOCKET_ERROR) + throw rdr::SystemException("unable to disable WSAEventSelect:%u", WSAGetLastError()); -void SocketManager::removeSocket(int index) { - if (index >= nSockets) - throw rdr::Exception("attempting to remove unregistered socket"); + // Reset the event object + WSAResetEvent(event); - if (!sockets[index].is_event) - WSACloseEvent(events[index]); + // Call the socket server to process the event + ci.server->processSocketEvent(ci.sock); + if (ci.sock->isShutdown()) { + remSocket(ci.sock); + return; + } - for (int i=index; i<nSockets-1; i++) { - sockets[i] = sockets[i+1]; - events[i] = events[i+1]; + // 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); + } } - - nSockets--; } - diff --git a/rfb_win32/SocketManager.h b/rfb_win32/SocketManager.h index 791370f2..ef359749 100644 --- a/rfb_win32/SocketManager.h +++ b/rfb_win32/SocketManager.h @@ -1,5 +1,5 @@ -/* Copyright (C) 2002-2004 RealVNC Ltd. All Rights Reserved. - * +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * * This is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or @@ -30,23 +30,32 @@ #ifndef __RFB_WIN32_SOCKET_MGR_H__ #define __RFB_WIN32_SOCKET_MGR_H__ -#include <list> - +#include <map> #include <network/Socket.h> -#include <rfb_win32/MsgWindow.h> +#include <rfb_win32/EventManager.h> namespace rfb { - namespace win32 { - class SocketManager { + 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); + void addListener(network::SocketListener* sock_, + network::SocketServer* srvr, + AddressChangeNotifier* acn = 0); // Remove and delete a listening socket. void remListener(network::SocketListener* sock); @@ -54,50 +63,24 @@ namespace rfb { // Add an already-connected socket. Socket events will cause the supplied // SocketServer to be called. The socket must ALREADY BE REGISTERED with // the SocketServer. - void addSocket(network::Socket* sock_, network::SocketServer* srvr); - - // Add a Win32 event & handler for it to the SocketManager - // This event will be blocked on along with the registered Sockets, and the - // handler called whenever it is discovered to be set. - // NB: SocketManager does NOT call ResetEvent on the event! - // NB: If processEvent returns false then the event is no longer registered, - // and the event object is assumed to have been closed by processEvent() - struct EventHandler { - virtual ~EventHandler() {} - virtual bool processEvent(HANDLE event) = 0; - }; - void addEvent(HANDLE event, EventHandler* ecb); - - // getMessage - // - // Either return a message from the thread's message queue or process a socket - // event. - // Returns whenever a message needs processing. Returns false if message is - // WM_QUIT, true for all other messages. - BOOL getMessage(MSG* msg, HWND hwnd, UINT minMsg, UINT maxMsg); + void addSocket(network::Socket* sock_, network::SocketServer* srvr, bool outgoing=true); protected: - void addListener(network::SocketListener* sock, HANDLE event, network::SocketServer* server); - void addSocket(network::Socket* sock, HANDLE event, network::SocketServer* server); - void resizeArrays(int numSockets); - void removeSocket(int index); - struct SocketInfo { - union { - network::Socket* conn; - network::SocketListener* listener; - } sock; - SOCKET fd; - bool is_conn; - bool is_event; - union { - network::SocketServer* server; - EventHandler* handler; - }; + 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; }; - SocketInfo* sockets; - HANDLE* events; - int nSockets; - int nAvail; + std::map<HANDLE, ListenInfo> listeners; + std::map<HANDLE, ConnInfo> connections; }; } diff --git a/rfb_win32/TCharArray.cxx b/rfb_win32/TCharArray.cxx index f8f03a69..fd4c0783 100644 --- a/rfb_win32/TCharArray.cxx +++ b/rfb_win32/TCharArray.cxx @@ -1,5 +1,5 @@ -/* Copyright (C) 2002-2003 RealVNC Ltd. All Rights Reserved. - * +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * * This is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or diff --git a/rfb_win32/TCharArray.h b/rfb_win32/TCharArray.h index 399e00a7..dde63b78 100644 --- a/rfb_win32/TCharArray.h +++ b/rfb_win32/TCharArray.h @@ -1,5 +1,5 @@ -/* Copyright (C) 2002-2004 RealVNC Ltd. All Rights Reserved. - * +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * * This is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or @@ -40,10 +40,10 @@ #ifndef __RFB_WIN32_TCHARARRAY_H__ #define __RFB_WIN32_TCHARARRAY_H__ -#define WIN32_LEAN_AND_MEAN +#include <windows.h> #include <tchar.h> - #include <rfb/util.h> +#include <rfb/Password.h> namespace rfb { @@ -98,6 +98,20 @@ namespace rfb { 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 @@ -105,6 +119,7 @@ namespace rfb { #define tstrContains wstrContains typedef WCharArray TCharArray; typedef WStr TStr; + typedef WPlainPasswd TPlainPasswd; #else #define tstrDup strDup #define tstrFree strFree @@ -112,8 +127,9 @@ namespace rfb { #define tstrContains strContains typedef CharArray TCharArray; typedef CStr TStr; + typedef PlainPasswd TPlainPasswd; #endif }; -#endif
\ No newline at end of file +#endif diff --git a/rfb_win32/Threading.cxx b/rfb_win32/Threading.cxx new file mode 100644 index 00000000..c41ac38b --- /dev/null +++ b/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/rfb_win32/Threading.h b/rfb_win32/Threading.h new file mode 100644 index 00000000..850f04dd --- /dev/null +++ b/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/rfb_win32/TrayIcon.h b/rfb_win32/TrayIcon.h index 85680f3f..dc5102a9 100644 --- a/rfb_win32/TrayIcon.h +++ b/rfb_win32/TrayIcon.h @@ -1,5 +1,5 @@ -/* Copyright (C) 2002-2003 RealVNC Ltd. All Rights Reserved. - * +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * * This is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or @@ -23,7 +23,6 @@ #ifndef __RFB_WIN32_TRAY_ICON_H__ #define __RFB_WIN32_TRAY_ICON_H__ -#define WIN32_LEAN_AND_MEAN #include <windows.h> #include <shellapi.h> #include <rfb_win32/MsgWindow.h> diff --git a/rfb_win32/TsSessions.cxx b/rfb_win32/TsSessions.cxx new file mode 100644 index 00000000..efe75640 --- /dev/null +++ b/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/rfb_win32/TsSessions.h b/rfb_win32/TsSessions.h new file mode 100644 index 00000000..b15ada71 --- /dev/null +++ b/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/rfb_win32/WMCursor.cxx b/rfb_win32/WMCursor.cxx index 871d9376..4d696cbc 100644 --- a/rfb_win32/WMCursor.cxx +++ b/rfb_win32/WMCursor.cxx @@ -1,5 +1,5 @@ -/* Copyright (C) 2002-2003 RealVNC Ltd. All Rights Reserved. - * +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * * This is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or @@ -20,11 +20,10 @@ // *** DOESN'T SEEM TO WORK WITH GetCursorInfo POS CODE BUILT-IN UNDER NT4SP6 // *** INSTEAD, WE LOOK FOR Win2000/Win98 OR ABOVE -#define WINVER 0x0500 #include <rfb_win32/WMCursor.h> #include <rfb_win32/OSVersion.h> -#include <rfb_win32/Win32Util.h> +#include <rfb_win32/DynamicFn.h> #include <rfb/Exception.h> #include <rfb/LogWriter.h> @@ -35,12 +34,19 @@ 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), library(0), use_getCursorInfo(false), cursor(0) { -#if (WINVER >= 0x0500) +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)); @@ -48,13 +54,12 @@ WMCursor::WMCursor() : hooks(0), library(0), use_getCursorInfo(false), cursor(0) // Use GetCursorInfo if OS version is sufficient use_getCursorInfo = (is_win98 || is_win2K) && _GetCursorInfo.isValid(); -#else -#pragma message ("not building in GetCursorInfo support") #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"); + vlog.info("falling back to cursor hooking: %p", hooks); } else { delete hooks; hooks = 0; @@ -63,18 +68,18 @@ WMCursor::WMCursor() : hooks(0), library(0), use_getCursorInfo(false), cursor(0) } else { vlog.info("using GetCursorInfo"); } - cursor = (HCURSOR)LoadImage(0, IDC_ARROW, IMAGE_CURSOR, 0, 0, LR_DEFAULTSIZE | LR_SHARED); } WMCursor::~WMCursor() { - if (hooks) delete hooks; - if (library) FreeLibrary(library); + vlog.debug("deleting WMCursorHooks (%p)", hooks); + if (hooks) + delete hooks; } WMCursor::Info WMCursor::getCursorInfo() { Info result; -#if (WINVER >= 0x0500) +#ifdef RFB_HAVE_GETCURSORINFO if (use_getCursorInfo) { CURSORINFO info; info.cbSize = sizeof(CURSORINFO); @@ -88,7 +93,8 @@ WMCursor::getCursorInfo() { #endif // Fall back to the old way of doing things POINT pos; - if (hooks) cursor = hooks->getCursor(); + if (hooks) + cursor = hooks->getCursor(); result.cursor = cursor; result.visible = cursor != 0; GetCursorPos(&pos); diff --git a/rfb_win32/WMCursor.h b/rfb_win32/WMCursor.h index a96822a7..41f9ee84 100644 --- a/rfb_win32/WMCursor.h +++ b/rfb_win32/WMCursor.h @@ -1,5 +1,5 @@ -/* Copyright (C) 2002-2003 RealVNC Ltd. All Rights Reserved. - * +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * * This is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or @@ -25,12 +25,10 @@ #ifndef __RFB_WIN32_WM_CURSOR_H__ #define __RFB_WIN32_WM_CURSOR_H__ -#define WIN32_LEAN_AND_MEAN #include <windows.h> #include <rfb_win32/WMHooks.h> namespace rfb { - namespace win32 { class WMCursor { @@ -53,13 +51,11 @@ namespace rfb { Info getCursorInfo(); protected: WMCursorHooks* hooks; - HMODULE library; bool use_getCursorInfo; HCURSOR cursor; }; }; - }; #endif // __RFB_WIN32_WM_CURSOR_H__ diff --git a/rfb_win32/WMHooks.cxx b/rfb_win32/WMHooks.cxx index 26a2363c..2d690538 100644 --- a/rfb_win32/WMHooks.cxx +++ b/rfb_win32/WMHooks.cxx @@ -1,5 +1,5 @@ -/* Copyright (C) 2002-2003 RealVNC Ltd. All Rights Reserved. - * +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * * This is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or @@ -18,10 +18,11 @@ // -=- WMHooks.cxx -#include <wm_hooks/wm_hooks.h> - #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> @@ -32,11 +33,29 @@ 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) {} + 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; }; @@ -48,52 +67,62 @@ Mutex hook_mgr_lock; HCURSOR hook_cursor = (HCURSOR)LoadImage(0, IDC_ARROW, IMAGE_CURSOR, 0, 0, LR_DEFAULTSIZE | LR_SHARED); -bool -StartHookThread() { - if (hook_mgr) return true; - vlog.debug("opening hook thread"); +static bool StartHookThread() { + if (hook_mgr) + return true; + vlog.debug("creating thread"); hook_mgr = new WMHooksThread(); - if (!WM_Hooks_Install(hook_mgr->getThreadId(), 0)) { + 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; } -void -StopHookThread() { - if (!hook_mgr) return; - if (!hooks.empty() || !cursor_hooks.empty()) return; - vlog.debug("closing hook thread"); +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; } -bool -AddHook(WMHooks* hook) { +static bool AddHook(WMHooks* hook) { vlog.debug("adding hook"); Lock l(hook_mgr_lock); - if (!StartHookThread()) return false; + if (!StartHookThread()) + return false; hooks.push_back(hook); return true; } -bool -AddCursorHook(WMCursorHooks* hook) { +static bool AddCursorHook(WMCursorHooks* hook) { vlog.debug("adding cursor hook"); Lock l(hook_mgr_lock); - if (cursor_hooks.empty()) WM_Hooks_EnableCursorShape(TRUE); - if (!StartHookThread()) return false; + 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; } -bool -RemHook(WMHooks* hook) { +static bool RemHook(WMHooks* hook) { { vlog.debug("removing hook"); Lock l(hook_mgr_lock); @@ -103,76 +132,107 @@ RemHook(WMHooks* hook) { return true; } -bool -RemCursorHook(WMCursorHooks* hook) { +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(); - if (cursor_hooks.empty()) WM_Hooks_EnableCursorShape(FALSE); return true; } -void -NotifyHooksRegion(const Region& r) { +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)->new_changes.add_changed(r); - if (!(*i)->notified) { - (*i)->notified = true; - PostMessage((*i)->getHandle(), WM_USER, 0, 0); - } - } + for (i=hooks.begin(); i!=hooks.end(); i++) + (*i)->NotifyHooksRegion(r); } -void -NotifyHooksCursor(HCURSOR c) { +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() { - UINT windowMsg = WM_Hooks_WindowChanged(); - UINT clientAreaMsg = WM_Hooks_WindowClientAreaChanged(); - UINT borderMsg = WM_Hooks_WindowBorderChanged(); - UINT rectangleMsg = WM_Hooks_RectangleChanged(); - UINT cursorMsg = WM_Hooks_CursorChanged(); + // 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 - UINT diagnosticMsg = WM_Hooks_Diagnostic(); + 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 == windowMsg) { + + 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)) - { - NotifyHooksRegion(Rect(wrect.left, wrect.top, - wrect.right, wrect.bottom)); - + 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)) { - NotifyHooksRegion(Rect(wrect.left+pt.x, wrect.top+pt.y, - wrect.right+pt.x, wrect.bottom+pt.y)); + 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) && @@ -187,14 +247,19 @@ WMHooksThread::run() { changed.assign_subtract(Rect(crect.left+pt.x, crect.top+pt.y, crect.right+pt.x, crect.bottom+pt.y)); } - NotifyHooksRegion(changed); + 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()) { - NotifyHooksRegion(r); + updates[activeRgn].assign_union(r); + updateDelayTimer.start(updateDelayMs); } + } else if (msg.message == cursorMsg) { NotifyHooksCursor((HCURSOR)msg.lParam); #ifdef _DEBUG @@ -205,7 +270,7 @@ WMHooksThread::run() { } vlog.debug("stopping hook thread - processed %d events", count); - WM_Hooks_Remove(getThreadId()); + (*WM_Hooks_Remove)(getThreadId()); } Thread* @@ -219,65 +284,52 @@ WMHooksThread::join() { // -=- WMHooks class -rfb::win32::WMHooks::WMHooks() - : clipper(0), new_changes(true), fg_window(0), - notified(false), MsgWindow(_T("WMHooks")) { +rfb::win32::WMHooks::WMHooks() : updateEvent(0) { } rfb::win32::WMHooks::~WMHooks() { RemHook(this); - if (clipper) delete clipper; } -LRESULT -rfb::win32::WMHooks::processMessage(UINT msg, WPARAM wParam, LPARAM lParam) { - switch (msg) { - case WM_USER: - { - // *** Yield, to allow the triggering update event to be processed - // BEFORE we try to grab the resulting changes. - // *** IMPROVES THINGS NOTICABLY ON WinXP - Sleep(0); - // *** - - Lock l(hook_mgr_lock); - notified = false; - new_changes.get_update(*clipper); - new_changes.clear(); - } - break; - } - return MsgWindow::processMessage(msg, wParam, lParam); +bool rfb::win32::WMHooks::setEvent(HANDLE ue) { + if (updateEvent) + RemHook(this); + updateEvent = ue; + return AddHook(this); } -bool -rfb::win32::WMHooks::setClipRect(const Rect& r) { - clip_region = r; - if (clipper) clipper->set_clip_region(clip_region); +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::setUpdateTracker(UpdateTracker* ut) { - if (clipper) delete clipper; - clipper = new ClippedUpdateTracker(*ut); - clipper->set_clip_region(clip_region); - return AddHook(this); +bool rfb::win32::WMHooks::areAvailable() { + WMHooksThread wmht; + return wmht.WM_Hooks_Install.isValid(); } #ifdef _DEBUG void rfb::win32::WMHooks::setDiagnosticRange(UINT min, UINT max) { - WM_Hooks_SetDiagnosticRange(min, 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 -Mutex blockMutex; -int blockCount = 0; - rfb::win32::WMBlockInput::WMBlockInput() : active(false) { } @@ -285,22 +337,40 @@ 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 (on == active) return true; - vlog.debug("blockInput changed"); + if (active == on) return true; Lock l(blockMutex); - int newCount = blockCount; - if (on) - newCount++; - else - newCount--; - if (WM_Hooks_EnableRealInputs(newCount==0, newCount==0)) { - vlog.debug("set blocking to %d", newCount); - blockCount = newCount; - active = on; - return true; - } - return false; + int newCount = on ? blockCount+1 : blockCount-1; + if (!blockRealInputs(newCount > 0)) + return false; + blockCount = newCount; + active = on; + return true; } diff --git a/rfb_win32/WMHooks.h b/rfb_win32/WMHooks.h index 791df763..4713b416 100644 --- a/rfb_win32/WMHooks.h +++ b/rfb_win32/WMHooks.h @@ -1,5 +1,5 @@ -/* Copyright (C) 2002-2003 RealVNC Ltd. All Rights Reserved. - * +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * * This is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or @@ -21,43 +21,50 @@ #ifndef __RFB_WIN32_WM_HOOKS_H__ #define __RFB_WIN32_WM_HOOKS_H__ -#define WIN32_LEAN_AND_MEAN #include <windows.h> #include <rfb/UpdateTracker.h> #include <rdr/Exception.h> -#include <rfb_win32/MsgWindow.h> +#include <rfb_win32/Win32Util.h> namespace rfb { namespace win32 { - class WMHooks : public MsgWindow { + // -=- 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(); - bool setClipRect(const Rect& cr); - bool setUpdateTracker(UpdateTracker* ut); + // 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); - virtual LRESULT processMessage(UINT msg, WPARAM wParam, LPARAM lParam); + // 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: - ClippedUpdateTracker* clipper; - Region clip_region; - - void* fg_window; - Rect fg_window_rect; - - public: - SimpleUpdateTracker new_changes; - bool notified; + 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(); diff --git a/rfb_win32/WMNotifier.cxx b/rfb_win32/WMNotifier.cxx index 9773abf5..20a5445f 100644 --- a/rfb_win32/WMNotifier.cxx +++ b/rfb_win32/WMNotifier.cxx @@ -1,5 +1,5 @@ -/* Copyright (C) 2002-2003 RealVNC Ltd. All Rights Reserved. - * +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * * This is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or @@ -30,7 +30,7 @@ using namespace rfb::win32; static LogWriter vlog("WMMonitor"); -WMMonitor::WMMonitor() : MsgWindow(_T("WMMonitor")) { +WMMonitor::WMMonitor() : MsgWindow(_T("WMMonitor")), notifier(0) { } WMMonitor::~WMMonitor() { diff --git a/rfb_win32/WMNotifier.h b/rfb_win32/WMNotifier.h index 564d176f..a7609642 100644 --- a/rfb_win32/WMNotifier.h +++ b/rfb_win32/WMNotifier.h @@ -1,5 +1,5 @@ -/* Copyright (C) 2002-2003 RealVNC Ltd. All Rights Reserved. - * +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * * This is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or diff --git a/rfb_win32/WMPoller.cxx b/rfb_win32/WMPoller.cxx index f568b211..f8505342 100644 --- a/rfb_win32/WMPoller.cxx +++ b/rfb_win32/WMPoller.cxx @@ -1,5 +1,5 @@ -/* Copyright (C) 2002-2003 RealVNC Ltd. All Rights Reserved. - * +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * * This is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or @@ -35,35 +35,19 @@ BoolParameter rfb::win32::WMPoller::poll_console_windows("PollConsoleWindows", // -=- WMPoller class -rfb::win32::WMPoller::WMPoller() : clipper(0) { -} - -rfb::win32::WMPoller::~WMPoller() { - if (clipper) delete clipper; -} - bool rfb::win32::WMPoller::processEvent() { PollInfo info; - if (clipper && poll_console_windows) { + if (poll_console_windows && ut) { ::EnumWindows(WMPoller::enumWindowProc, (LPARAM) &info); - clipper->add_changed(info.poll_include); + ut->add_changed(info.poll_include); } return false; } bool -rfb::win32::WMPoller::setClipRect(const Rect& r) { - clip_region = r; - if (clipper) clipper->set_clip_region(clip_region); - return true; -} - -bool -rfb::win32::WMPoller::setUpdateTracker(UpdateTracker* ut) { - if (clipper) delete clipper; - clipper = new ClippedUpdateTracker(*ut); - clipper->set_clip_region(clip_region); +rfb::win32::WMPoller::setUpdateTracker(UpdateTracker* ut_) { + ut = ut_; return true; } diff --git a/rfb_win32/WMPoller.h b/rfb_win32/WMPoller.h index 3f3f402a..851b69f4 100644 --- a/rfb_win32/WMPoller.h +++ b/rfb_win32/WMPoller.h @@ -1,5 +1,5 @@ -/* Copyright (C) 2002-2003 RealVNC Ltd. All Rights Reserved. - * +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * * This is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or @@ -28,7 +28,6 @@ #ifndef __RFB_WIN32_WM_POLLER_H__ #define __RFB_WIN32_WM_POLLER_H__ -#define WIN32_LEAN_AND_MEAN #include <windows.h> #include <rfb/UpdateTracker.h> #include <rfb/Configuration.h> @@ -39,11 +38,9 @@ namespace rfb { class WMPoller { public: - WMPoller(); - ~WMPoller(); + WMPoller() : ut(0) {} bool processEvent(); - bool setClipRect(const Rect& cr); bool setUpdateTracker(UpdateTracker* ut); static BoolParameter poll_console_windows; @@ -55,9 +52,7 @@ namespace rfb { static bool checkPollWindow(HWND w); static void pollWindow(HWND w, PollInfo* info); static BOOL CALLBACK enumWindowProc(HWND w, LPARAM lp); - - ClippedUpdateTracker* clipper; - Region clip_region; + UpdateTracker* ut; }; }; diff --git a/rfb_win32/WMShatter.cxx b/rfb_win32/WMShatter.cxx index f6a74848..e68abfb1 100644 --- a/rfb_win32/WMShatter.cxx +++ b/rfb_win32/WMShatter.cxx @@ -1,5 +1,5 @@ -/* Copyright (C) 2002-2003 RealVNC Ltd. All Rights Reserved. - * +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * * This is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or diff --git a/rfb_win32/WMShatter.h b/rfb_win32/WMShatter.h index 7b81678f..3ea63b1a 100644 --- a/rfb_win32/WMShatter.h +++ b/rfb_win32/WMShatter.h @@ -1,5 +1,5 @@ -/* Copyright (C) 2002-2003 RealVNC Ltd. All Rights Reserved. - * +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * * This is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or @@ -33,11 +33,9 @@ #ifndef __RFB_WIN32_SHATTER_H__ #define __RFB_WIN32_SHATTER_H__ -#define WIN32_LEAN_AND_MEAN #include <windows.h> namespace rfb { - namespace win32 { bool IsSafeWM(HWND window, UINT msg, WPARAM wParam, LPARAM lParam); @@ -47,7 +45,6 @@ namespace rfb { LRESULT SafeDispatchMessage(const MSG* msg); }; - }; #endif // __RFB_WIN32_SHATTER_H__ diff --git a/rfb_win32/WMWindowCopyRect.cxx b/rfb_win32/WMWindowCopyRect.cxx index 46d85eac..63c1da2e 100644 --- a/rfb_win32/WMWindowCopyRect.cxx +++ b/rfb_win32/WMWindowCopyRect.cxx @@ -1,5 +1,5 @@ -/* Copyright (C) 2002-2003 RealVNC Ltd. All Rights Reserved. - * +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * * This is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or @@ -29,55 +29,39 @@ static LogWriter vlog("WMCopyRect"); // -=- WMHooks class -rfb::win32::WMCopyRect::WMCopyRect() : clipper(0), fg_window(0) { -} - -rfb::win32::WMCopyRect::~WMCopyRect() { - if (clipper) delete clipper; +rfb::win32::WMCopyRect::WMCopyRect() : ut(0), fg_window(0) { } bool rfb::win32::WMCopyRect::processEvent() { - if (clipper) { - // See if the foreground window has moved - HWND window = GetForegroundWindow(); - if (window) { - RECT wrect; - if (IsWindow(window) && IsWindowVisible(window) && GetWindowRect(window, &wrect)) { - Rect winrect(wrect.left, wrect.top, wrect.right, wrect.bottom); - if (fg_window == window) { - - if (!fg_window_rect.tl.equals(winrect.tl)) { - // Window has moved - send a copyrect event to the client - Point delta = Point(winrect.tl.x-fg_window_rect.tl.x, winrect.tl.y-fg_window_rect.tl.y); - Region copy_dest = winrect; - clipper->add_copied(copy_dest, delta); - clipper->add_changed(Region(fg_window_rect).subtract(copy_dest)); - } + // 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; } + fg_window = window; + fg_window_rect = winrect; } else { fg_window = 0; } + } else { + fg_window = 0; } return false; } bool -rfb::win32::WMCopyRect::setClipRect(const Rect& r) { - clip_region = r; - if (clipper) clipper->set_clip_region(clip_region); - return true; -} - -bool -rfb::win32::WMCopyRect::setUpdateTracker(UpdateTracker* ut) { - if (clipper) delete clipper; - clipper = new ClippedUpdateTracker(*ut); - clipper->set_clip_region(clip_region); +rfb::win32::WMCopyRect::setUpdateTracker(UpdateTracker* ut_) { + ut = ut_; return true; } diff --git a/rfb_win32/WMWindowCopyRect.h b/rfb_win32/WMWindowCopyRect.h index 0750d86e..5a0e876d 100644 --- a/rfb_win32/WMWindowCopyRect.h +++ b/rfb_win32/WMWindowCopyRect.h @@ -1,5 +1,5 @@ -/* Copyright (C) 2002-2003 RealVNC Ltd. All Rights Reserved. - * +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * * This is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or @@ -36,15 +36,12 @@ namespace rfb { class WMCopyRect { public: WMCopyRect(); - ~WMCopyRect(); bool processEvent(); - bool setClipRect(const Rect& cr); bool setUpdateTracker(UpdateTracker* ut); protected: - ClippedUpdateTracker* clipper; - Region clip_region; + UpdateTracker* ut; void* fg_window; Rect fg_window_rect; }; diff --git a/rfb_win32/Win32Util.cxx b/rfb_win32/Win32Util.cxx index 28fae2e8..ef8039ab 100644 --- a/rfb_win32/Win32Util.cxx +++ b/rfb_win32/Win32Util.cxx @@ -1,5 +1,5 @@ -/* Copyright (C) 2002-2004 RealVNC Ltd. All Rights Reserved. - * +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * * This is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or @@ -18,221 +18,30 @@ // Win32Util.cxx +#include <rfb_win32/ModuleFileName.h> #include <rfb_win32/Win32Util.h> -#include <rdr/Exception.h> +#include <rfb_win32/MonitorInfo.h> +#include <rfb_win32/Handle.h> #include <rdr/HexOutStream.h> - +#include <rdr/Exception.h> namespace rfb { namespace win32 { -LogicalPalette::LogicalPalette() : palette(0), numEntries(0) { - BYTE buf[sizeof(LOGPALETTE)+256*sizeof(PALETTEENTRY)]; - LOGPALETTE* logpal = (LOGPALETTE*)buf; - logpal->palVersion = 0x300; - logpal->palNumEntries = 256; - for (int i=0; i<256;i++) { - logpal->palPalEntry[i].peRed = 0; - logpal->palPalEntry[i].peGreen = 0; - logpal->palPalEntry[i].peBlue = 0; - logpal->palPalEntry[i].peFlags = 0; - } - palette = CreatePalette(logpal); - if (!palette) - throw rdr::SystemException("failed to CreatePalette", GetLastError()); -} - -LogicalPalette::~LogicalPalette() { - if (palette) - if (!DeleteObject(palette)) - throw rdr::SystemException("del palette failed", GetLastError()); -} - -void LogicalPalette::setEntries(int start, int count, const Colour* cols) { - if (numEntries < count) { - ResizePalette(palette, start+count); - numEntries = start+count; - } - PALETTEENTRY* logpal = new PALETTEENTRY[count]; - for (int i=0; i<count; i++) { - logpal[i].peRed = cols[i].r >> 8; - logpal[i].peGreen = cols[i].g >> 8; - logpal[i].peBlue = cols[i].b >> 8; - logpal[i].peFlags = 0; - } - UnrealizeObject(palette); - SetPaletteEntries(palette, start, count, logpal); - delete [] logpal; -} - - -static LogWriter dcLog("DeviceContext"); - -PixelFormat DeviceContext::getPF() const { - return getPF(dc); -} - -PixelFormat DeviceContext::getPF(HDC dc) { - PixelFormat format; - CompatibleBitmap bitmap(dc, 1, 1); - - // -=- Get the bitmap format information - BitmapInfo bi; - memset(&bi, 0, sizeof(bi)); - bi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER); - bi.bmiHeader.biBitCount = 0; - - if (!::GetDIBits(dc, bitmap, 0, 1, NULL, (BITMAPINFO*)&bi, DIB_RGB_COLORS)) { - throw rdr::SystemException("unable to determine device pixel format", GetLastError()); - } - if (!::GetDIBits(dc, bitmap, 0, 1, NULL, (BITMAPINFO*)&bi, DIB_RGB_COLORS)) { - throw rdr::SystemException("unable to determine pixel shifts/palette", GetLastError()); - } - - // -=- Munge the bitmap info here - switch (bi.bmiHeader.biBitCount) { - case 1: - case 4: - bi.bmiHeader.biBitCount = 8; - break; - case 24: - bi.bmiHeader.biBitCount = 32; - break; - } - bi.bmiHeader.biPlanes = 1; - - format.trueColour = bi.bmiHeader.biBitCount > 8; - format.bigEndian = 0; - format.bpp = format.depth = bi.bmiHeader.biBitCount; - - if (format.trueColour) { - DWORD rMask=0, gMask=0, bMask=0; - - // Which true colour format is the DIB section using? - switch (bi.bmiHeader.biCompression) { - case BI_RGB: - // Default RGB layout - switch (bi.bmiHeader.biBitCount) { - case 16: - // RGB 555 - High Colour - dcLog.info("16-bit High Color"); - rMask = 0x7c00; - bMask = 0x001f; - gMask = 0x03e0; - format.depth = 15; - break; - case 24: - case 32: - // RGB 888 - True Colour - dcLog.info("24/32-bit High Color"); - rMask = 0xff0000; - gMask = 0x00ff00; - bMask = 0x0000ff; - format.depth = 24; - break; - default: - dcLog.error("bits per pixel %u not supported", bi.bmiHeader.biBitCount); - throw rdr::Exception("unknown bits per pixel specified"); - }; - break; - case BI_BITFIELDS: - // Custom RGB layout - rMask = bi.mask.red; - gMask = bi.mask.green; - bMask = bi.mask.blue; - dcLog.info("BitFields format: %lu, (%lx, %lx, %lx)", - bi.bmiHeader.biBitCount, rMask, gMask, bMask); - if (format.bpp == 32) - format.depth = 24; // ...probably - break; - }; - - // Convert the data we just retrieved - initMaxAndShift(rMask, &format.redMax, &format.redShift); - initMaxAndShift(gMask, &format.greenMax, &format.greenShift); - initMaxAndShift(bMask, &format.blueMax, &format.blueShift); - } - - return format; -} - - -WindowDC::WindowDC(HWND wnd) : hwnd(wnd) { - dc = GetDC(wnd); - if (!dc) - throw rdr::SystemException("GetDC failed", GetLastError()); -} -WindowDC::~WindowDC() { - if (dc) - ReleaseDC(hwnd, dc); -} - - -CompatibleDC::CompatibleDC(HDC existing) { - dc = CreateCompatibleDC(existing); - if (!dc) - throw rdr::SystemException("CreateCompatibleDC failed", GetLastError()); -} -CompatibleDC::~CompatibleDC() { - if (dc) - DeleteDC(dc); -} - - -BitmapDC::BitmapDC(HDC hdc, HBITMAP hbitmap) : CompatibleDC(hdc){ - oldBitmap = (HBITMAP)SelectObject(dc, hbitmap); - if (!oldBitmap) - throw rdr::SystemException("SelectObject to CompatibleDC failed", - GetLastError()); -} -BitmapDC::~BitmapDC() { - SelectObject(dc, oldBitmap); -} - - -CompatibleBitmap::CompatibleBitmap(HDC hdc, int width, int height) { - hbmp = CreateCompatibleBitmap(hdc, width, height); - if (!hbmp) - throw rdr::SystemException("CreateCompatibleBitmap() failed", - GetLastError()); -} -CompatibleBitmap::~CompatibleBitmap() { - if (hbmp) DeleteObject(hbmp); -} - - -PaletteSelector::PaletteSelector(HDC dc, HPALETTE pal) : device(dc), redrawRequired(false) { - oldPal = SelectPalette(dc, pal, FALSE); - redrawRequired = RealizePalette(dc) > 0; -} -PaletteSelector::~PaletteSelector() { - if (oldPal) SelectPalette(device, oldPal, TRUE); -} - - -IconInfo::IconInfo(HICON icon) { - if (!GetIconInfo(icon, this)) - throw rdr::SystemException("GetIconInfo() failed", GetLastError()); -} -IconInfo::~IconInfo() { - if (hbmColor) - DeleteObject(hbmColor); - if (hbmMask) - DeleteObject(hbmMask); -} - - -ModuleFileName::ModuleFileName(HMODULE module) : TCharArray(MAX_PATH) { - if (!module) module = GetModuleHandle(0); - if (!GetModuleFileName(module, buf, MAX_PATH)) - buf[0] = 0; -} - FileVersionInfo::FileVersionInfo(const TCHAR* filename) { // Get executable name ModuleFileName exeName; - if (!filename) filename = exeName.buf; + 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; @@ -249,7 +58,7 @@ FileVersionInfo::FileVersionInfo(const TCHAR* filename) { const TCHAR* FileVersionInfo::getVerString(const TCHAR* name, DWORD langId) { char langIdBuf[sizeof(langId)]; for (int i=sizeof(langIdBuf)-1; i>=0; i--) { - langIdBuf[i] = langId & 0xff; + langIdBuf[i] = (char) (langId & 0xff); langId = langId >> 8; } @@ -273,173 +82,31 @@ bool splitPath(const TCHAR* path, TCHAR** dir, TCHAR** file) { } -static LogWriter dfbLog("DynamicFn"); - -DynamicFnBase::DynamicFnBase(const TCHAR* dllName, const char* fnName) : dllHandle(0), fnPtr(0) { - dllHandle = LoadLibrary(dllName); - if (!dllHandle) { - dfbLog.info("DLL %s not found (%d)", (const char*)CStr(dllName), GetLastError()); - return; - } - fnPtr = GetProcAddress(dllHandle, fnName); - if (!fnPtr) - dfbLog.info("proc %s not found in %s (%d)", fnName, (const char*)CStr(dllName), GetLastError()); -} - -DynamicFnBase::~DynamicFnBase() { - if (dllHandle) - FreeLibrary(dllHandle); -} - - -static LogWriter miLog("MonitorInfo"); - -MonitorInfo::MonitorInfo(HWND window) { -#if (WINVER >= 0x0500) - typedef HMONITOR (WINAPI *_MonitorFromWindow_proto)(HWND,DWORD); - rfb::win32::DynamicFn<_MonitorFromWindow_proto> _MonitorFromWindow(_T("user32.dll"), "MonitorFromWindow"); - typedef BOOL (WINAPI *_GetMonitorInfo_proto)(HMONITOR,LPMONITORINFO); - rfb::win32::DynamicFn<_GetMonitorInfo_proto> _GetMonitorInfo(_T("user32.dll"), "GetMonitorInfoA"); - - // Can we dynamically link to the monitor functions? - if (_MonitorFromWindow.isValid()) { - if (_GetMonitorInfo.isValid()) { - HMONITOR monitor = (*_MonitorFromWindow)(window, MONITOR_DEFAULTTONEAREST); - miLog.debug("monitor=%lx", monitor); - if (monitor) { - memset(this, 0, sizeof(MONITORINFOEXA)); - cbSize = sizeof(MONITORINFOEXA); - if ((*_GetMonitorInfo)(monitor, this)) { - miLog.debug("monitor is %d,%d-%d,%d", rcMonitor.left, rcMonitor.top, rcMonitor.right, rcMonitor.bottom); - miLog.debug("work area is %d,%d-%d,%d", rcWork.left, rcWork.top, rcWork.right, rcWork.bottom); - miLog.debug("device is \"%s\"", szDevice); - return; - } - miLog.error("failed to get monitor info: %ld", GetLastError()); - } - } else { - miLog.debug("GetMonitorInfo not found"); - } - } else { - miLog.debug("MonitorFromWindow not found"); - } -#else -#pragma message ("not building in GetMonitorInfo") - cbSize = sizeof(MonitorInfo); - szDevice[0] = 0; -#endif - - // Legacy fallbacks - just return the desktop settings - miLog.debug("using legacy fall-backs"); - HWND desktop = GetDesktopWindow(); - GetWindowRect(desktop, &rcMonitor); - SystemParametersInfo(SPI_GETWORKAREA, 0, &rcWork, 0); - dwFlags = 0; -} - - -#if (WINVER >= 0x0500) - -struct moveToMonitorData { - HWND window; - const char* monitorName; -}; - -typedef BOOL (WINAPI *_GetMonitorInfo_proto)(HMONITOR,LPMONITORINFO); -static rfb::win32::DynamicFn<_GetMonitorInfo_proto> _GetMonitorInfo(_T("user32.dll"), "GetMonitorInfoA"); - -static BOOL CALLBACK moveToMonitorEnumProc(HMONITOR monitor, - HDC dc, - LPRECT pos, - LPARAM d) { - moveToMonitorData* data = (moveToMonitorData*)d; - MONITORINFOEXA info; - memset(&info, 0, sizeof(info)); - info.cbSize = sizeof(info); - - if ((*_GetMonitorInfo)(monitor, &info)) { - if (stricmp(data->monitorName, info.szDevice) == 0) { - SetWindowPos(data->window, 0, - info.rcMonitor.left, info.rcMonitor.top, - info.rcMonitor.right, info.rcMonitor.bottom, - SWP_NOZORDER | SWP_NOSIZE | SWP_NOACTIVATE | SWP_NOOWNERZORDER); - return FALSE; - } - } - - return TRUE; -} - -#endif - -void moveToMonitor(HWND handle, const char* device) { - miLog.debug("moveToMonitor %s", device); - -#if (WINVER >= 0x500) - typedef BOOL (WINAPI *_EnumDisplayMonitors_proto)(HDC, LPCRECT, MONITORENUMPROC, LPARAM); - rfb::win32::DynamicFn<_EnumDisplayMonitors_proto> _EnumDisplayMonitors(_T("user32.dll"), "EnumDisplayMonitors"); - if (!_EnumDisplayMonitors.isValid()) { - miLog.debug("EnumDisplayMonitors not found"); - return; - } - - moveToMonitorData data; - data.window = handle; - data.monitorName = device; - - (*_EnumDisplayMonitors)(0, 0, &moveToMonitorEnumProc, (LPARAM)&data); -#endif -} - - -void centerWindow(HWND handle, HWND parent, bool clipToParent) { +void centerWindow(HWND handle, HWND parent) { RECT r; - if (parent && IsWindowVisible(parent)) { - if (!GetWindowRect(parent, &r)) return; - } else { - MonitorInfo mi(handle); + MonitorInfo mi(parent ? parent : handle); + if (!parent || !IsWindowVisible(parent) || !GetWindowRect(parent, &r)) r=mi.rcWork; - } - centerWindow(handle, r, clipToParent); + centerWindow(handle, r); + mi.clipTo(handle); } -void centerWindow(HWND handle, const RECT& r, bool clipToRect) { +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; - if (clipToRect) { - w = min(r.right-r.left, w); - h = min(r.bottom-r.top, h); - } int x = (r.left + r.right - w)/2; int y = (r.top + r.bottom - h)/2; - UINT flags = SWP_NOACTIVATE | SWP_NOOWNERZORDER | SWP_NOZORDER | (clipToRect ? 0 : SWP_NOSIZE); - SetWindowPos(handle, 0, x, y, w, h, flags); + UINT flags = SWP_NOACTIVATE | SWP_NOOWNERZORDER | SWP_NOZORDER | SWP_NOSIZE; + SetWindowPos(handle, 0, x, y, 0, 0, flags); } - -int MsgBox(HWND parent, const TCHAR* msg, UINT flags) { - const TCHAR* msgType = 0; - UINT tflags = flags & 0x70; - if (tflags == MB_ICONHAND) - msgType = _T("Error"); - else if (tflags == MB_ICONQUESTION) - msgType = _T("Question"); - else if (tflags == MB_ICONEXCLAMATION) - msgType = _T("Warning"); - else if (tflags == MB_ICONASTERISK) - msgType = _T("Information"); - flags |= MB_TOPMOST | MB_SETFOREGROUND; - int len = _tcslen(AppName.buf) + 1; - if (msgType) len += _tcslen(msgType) + 3; - TCharArray title = new TCHAR[len]; - _tcscpy(title.buf, AppName.buf); - if (msgType) { - _tcscat(title.buf, _T(" : ")); - _tcscat(title.buf, msgType); - } - return MessageBox(parent, msg, title.buf, flags); +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/rfb_win32/Win32Util.h b/rfb_win32/Win32Util.h index 5f0ab5a0..8cc1a2b9 100644 --- a/rfb_win32/Win32Util.h +++ b/rfb_win32/Win32Util.h @@ -1,5 +1,5 @@ -/* Copyright (C) 2002-2004 RealVNC Ltd. All Rights Reserved. - * +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * * This is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or @@ -25,105 +25,12 @@ #ifndef __RFB_WIN32_GDIUTIL_H__ #define __RFB_WIN32_GDIUTIL_H__ -#include <rfb/ColourMap.h> -#include <rfb/PixelFormat.h> -#include <rfb/Rect.h> #include <rfb_win32/TCharArray.h> namespace rfb { namespace win32 { - class LogicalPalette { - public: - LogicalPalette(); - ~LogicalPalette(); - void setEntries(int start, int count, const Colour* cols); - HPALETTE getHandle() {return palette;} - protected: - HPALETTE palette; - int numEntries; - }; - - class DeviceContext { - public: - DeviceContext() : dc(0) {} - virtual ~DeviceContext() {} - operator HDC() const {return dc;} - PixelFormat getPF() const; - static PixelFormat getPF(HDC dc); - protected: - HDC dc; - }; - - class WindowDC : public DeviceContext { - public: - WindowDC(HWND wnd); - virtual ~WindowDC(); - protected: - HWND hwnd; - }; - - class CompatibleDC : public DeviceContext { - public: - CompatibleDC(HDC existing); - virtual ~CompatibleDC(); - }; - - class BitmapDC : public CompatibleDC { - public: - BitmapDC(HDC hdc, HBITMAP hbitmap); - ~BitmapDC(); - protected: - HBITMAP oldBitmap; - }; - - class CompatibleBitmap { - public: - CompatibleBitmap(HDC hdc, int width, int height); - virtual ~CompatibleBitmap(); - operator HBITMAP() const {return hbmp;} - protected: - HBITMAP hbmp; - }; - - struct BitmapInfo { - BITMAPINFOHEADER bmiHeader; - union { - struct { - DWORD red; - DWORD green; - DWORD blue; - } mask; - RGBQUAD color[256]; - }; - }; - - inline void initMaxAndShift(DWORD mask, int* max, int* shift) { - for ((*shift) = 0; (mask & 1) == 0; (*shift)++) mask >>= 1; - (*max) = (rdr::U16)mask; - } - - class PaletteSelector { - public: - PaletteSelector(HDC dc, HPALETTE pal); - ~PaletteSelector(); - bool isRedrawRequired() {return redrawRequired;} - protected: - HPALETTE oldPal; - HDC device; - bool redrawRequired; - }; - - struct IconInfo : public ICONINFO { - IconInfo(HICON icon); - ~IconInfo(); - }; - - struct ModuleFileName : public TCharArray { - ModuleFileName(HMODULE module=0); - }; - struct FileVersionInfo : public TCharArray { FileVersionInfo(const TCHAR* filename=0); const TCHAR* getVerString(const TCHAR* name, DWORD langId = 0x080904b0); @@ -131,84 +38,15 @@ namespace rfb { bool splitPath(const TCHAR* path, TCHAR** dir, TCHAR** file); - class DynamicFnBase { - public: - DynamicFnBase(const TCHAR* dllName, const char* fnName); - ~DynamicFnBase(); - bool isValid() const {return fnPtr != 0;} - protected: - void* fnPtr; - HMODULE dllHandle; - }; - - template<class T> class DynamicFn : public DynamicFnBase { - public: - DynamicFn(const TCHAR* dllName, const char* fnName) : DynamicFnBase(dllName, fnName) {} - T operator *() const {return (T)fnPtr;}; - }; - - // Structure containing info on the monitor nearest the window. - // Copes with multi-monitor OSes and older ones. -#if (WINVER >= 0x0500) - struct MonitorInfo : MONITORINFOEXA { - MonitorInfo(HWND hwnd); - }; -#else - struct MonitorInfo { - MonitorInfo(HWND hwnd); - DWORD cbSize; - RECT rcMonitor; - RECT rcWork; - DWORD dwFlags; - char szDevice[1]; // Always null... - }; -#endif - void moveToMonitor(HWND handle, const char* device); - - class Handle { - public: - Handle(HANDLE h_=0) : h(h_) {} - ~Handle() { - if (h) CloseHandle(h); - } - operator HANDLE() {return h;} - HANDLE h; - }; - // Center the window to a rectangle, or to a parent window. // Optionally, resize the window to lay within the rect or parent window // If the parent window is NULL then the working area if the window's // current monitor is used instead. - void centerWindow(HWND handle, const RECT& r, bool clipToRect=false); - void centerWindow(HWND handle, HWND parent, bool clipToRect=false); + void centerWindow(HWND handle, const RECT& r); + void centerWindow(HWND handle, HWND parent); - // MsgBox helper function. Define rfb::win32::AppName somewhere in your - // code and MsgBox will use its value in informational messages. - extern TStr AppName; - int MsgBox(HWND parent, const TCHAR* message, UINT flags); - - // Get the computer name - struct ComputerName : TCharArray { - ComputerName() : TCharArray(MAX_COMPUTERNAME_LENGTH+1) { - ULONG namelength = MAX_COMPUTERNAME_LENGTH+1; - if (!GetComputerName(buf, &namelength)) - _tcscpy(buf, _T("")); - } - }; - - // Allocate and/or manage LocalAlloc memory. - struct LocalMem { - LocalMem(int size) : ptr(LocalAlloc(LMEM_FIXED, size)) { - if (!ptr) throw rdr::SystemException("LocalAlloc", GetLastError()); - } - LocalMem(void* p) : ptr(p) {} - ~LocalMem() {LocalFree(ptr);} - operator void*() {return ptr;} - void* takePtr() { - void* t = ptr; ptr = 0; return t; - } - void* ptr; - }; + // resizeWindow resizes a window about its center + void resizeWindow(HWND handle, int width, int height); }; diff --git a/rfb_win32/keymap.h b/rfb_win32/keymap.h index 69ce66f2..a340d09d 100644 --- a/rfb_win32/keymap.h +++ b/rfb_win32/keymap.h @@ -1,5 +1,5 @@ -/* Copyright (C) 2002-2004 RealVNC Ltd. All Rights Reserved. - * +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * * This is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or diff --git a/rfb_win32/rfb_win32.dsp b/rfb_win32/rfb_win32.dsp index e9b746ca..664e3966 100644 --- a/rfb_win32/rfb_win32.dsp +++ b/rfb_win32/rfb_win32.dsp @@ -42,7 +42,7 @@ RSC=rc.exe # PROP Intermediate_Dir "Release"
# PROP Target_Dir ""
# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /YX /FD /c
-# ADD CPP /nologo /MT /W3 /GX /O2 /I ".." /FI"msvcwarning.h" /D "NDEBUG" /D "_LIB" /D "WIN32" /D "_MBCS" /YX /FD /c
+# ADD CPP /nologo /MT /W3 /GX /O2 /I ".." /FI"rdr/msvcwarning.h" /D "NDEBUG" /D "_LIB" /D "WIN32" /D "_MBCS" /YX /FD /c
# ADD BASE RSC /l 0x809 /d "NDEBUG"
# ADD RSC /l 0x809 /d "NDEBUG"
BSC32=bscmake.exe
@@ -65,7 +65,7 @@ LIB32=link.exe -lib # PROP Intermediate_Dir "Debug"
# PROP Target_Dir ""
# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /YX /FD /GZ /c
-# ADD CPP /nologo /MTd /W3 /Gm /GX /Zi /Od /I ".." /FI"msvcwarning.h" /D "_DEBUG" /D "_LIB" /D "WIN32" /D "_MBCS" /FR /YX /FD /GZ /c
+# ADD CPP /nologo /MTd /W3 /Gm /GX /Zi /Od /I ".." /FI"rdr/msvcwarning.h" /D "_DEBUG" /D "_LIB" /D "WIN32" /D "_MBCS" /FR /YX /FD /GZ /c
# ADD BASE RSC /l 0x809 /d "_DEBUG"
# ADD RSC /l 0x809 /d "_DEBUG"
BSC32=bscmake.exe
@@ -88,7 +88,7 @@ LIB32=link.exe -lib # PROP Intermediate_Dir "Debug_Unicode"
# PROP Target_Dir ""
# ADD BASE CPP /nologo /MTd /W3 /Gm /GX /Zi /Od /I ".." /FI"msvcwarning.h" /D "_DEBUG" /D "_LIB" /D "WIN32" /D "_MBCS" /YX /FD /GZ /c
-# ADD CPP /nologo /MTd /W3 /Gm /GX /Zi /Od /I ".." /FI"msvcwarning.h" /D "_LIB" /D "_DEBUG" /D "WIN32" /D "_UNICODE" /D "UNICODE" /YX /FD /GZ /c
+# ADD CPP /nologo /MTd /W3 /Gm /GX /Zi /Od /I ".." /FI"rdr/msvcwarning.h" /D "_LIB" /D "_DEBUG" /D "WIN32" /D "_UNICODE" /D "UNICODE" /YX /FD /GZ /c
# ADD BASE RSC /l 0x809 /d "_DEBUG"
# ADD RSC /l 0x809 /d "_DEBUG"
BSC32=bscmake.exe
@@ -134,6 +134,10 @@ 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
@@ -146,6 +150,14 @@ 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
@@ -158,6 +170,14 @@ 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
@@ -186,6 +206,18 @@ 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
@@ -210,10 +242,18 @@ 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
@@ -250,6 +290,10 @@ 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
@@ -262,6 +306,14 @@ 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
@@ -270,6 +322,10 @@ 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
@@ -282,10 +338,26 @@ 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
@@ -302,6 +374,30 @@ 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
@@ -330,6 +426,18 @@ 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
@@ -358,6 +466,10 @@ 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
@@ -366,6 +478,10 @@ 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
|