git-svn-id: svn://svn.code.sf.net/p/tigervnc/code/branches/merge-with-vnc-4.1.1@523 3789f03b-4d11-0410-bbf8-ca57d06f2519tags/v0.0.90
@@ -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"))); | |||
} |
@@ -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 |
@@ -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 |
@@ -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; | |||
} |
@@ -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; | |||
}; |
@@ -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); | |||
} | |||
} |
@@ -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; |
@@ -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 | |||
} | |||
@@ -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 |
@@ -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 |
@@ -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 |
@@ -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 |
@@ -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 |
@@ -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)); | |||
} |
@@ -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(); | |||
}; | |||
} | |||
} |
@@ -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(); | |||
} | |||
} | |||
} | |||
@@ -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> |
@@ -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); | |||
} |
@@ -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 |
@@ -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 |
@@ -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); | |||
}; | |||
}; |
@@ -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,14 +296,22 @@ 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) | |||
@@ -291,10 +331,8 @@ 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); |
@@ -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> |
@@ -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); | |||
} | |||
@@ -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 |
@@ -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; | |||
} | |||
} | |||
} |
@@ -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 |
@@ -0,0 +1,43 @@ | |||
/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. | |||
* | |||
* This is free software; you can redistribute it and/or modify | |||
* it under the terms of the GNU General Public License as published by | |||
* the Free Software Foundation; either version 2 of the License, or | |||
* (at your option) any later version. | |||
* | |||
* This software is distributed in the hope that it will be useful, | |||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||
* GNU General Public License for more details. | |||
* | |||
* You should have received a copy of the GNU General Public License | |||
* along with this software; if not, write to the Free Software | |||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, | |||
* USA. | |||
*/ | |||
// Wrapper for Win32 HANDLEs that can/must be CloseHandle()d. | |||
#ifndef __RFB_WIN32_HANDLE_H__ | |||
#define __RFB_WIN32_HANDLE_H__ | |||
#include <windows.h> | |||
namespace rfb { | |||
namespace win32 { | |||
class Handle { | |||
public: | |||
Handle(HANDLE h_=0) : h(h_) {} | |||
~Handle() { | |||
if (h) CloseHandle(h); | |||
} | |||
operator HANDLE() {return h;} | |||
HANDLE h; | |||
}; | |||
}; | |||
}; | |||
#endif |
@@ -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 |
@@ -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 |
@@ -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; | |||
} |
@@ -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; |
@@ -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 |
@@ -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 |
@@ -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; | |||
} | |||
} |
@@ -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__ |
@@ -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 |
@@ -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); | |||
} | |||
@@ -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 |
@@ -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 |
@@ -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); | |||
} | |||
} |
@@ -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 { |
@@ -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 |
@@ -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 { |
@@ -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(); | |||
} |
@@ -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; | |||
}; | |||
}; |
@@ -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; | |||
} |
@@ -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: |
@@ -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,167 +97,245 @@ 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"); | |||
@@ -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; | |||
} |
@@ -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; |
@@ -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 |
@@ -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); | |||
} | |||
} | |||
} |
@@ -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 |
@@ -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(); | |||
} |
@@ -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 |
@@ -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"); |
@@ -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; |
@@ -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(); | |||
} |
@@ -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); | |||
} | |||
@@ -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; | |||
} | |||
} |
@@ -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(); |
@@ -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--; | |||
} | |||
@@ -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; | |||
}; | |||
} |
@@ -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 |
@@ -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 | |||
#endif |
@@ -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; | |||
} |
@@ -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 |
@@ -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> |
@@ -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 | |||
} | |||
}; | |||
}; |
@@ -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 |
@@ -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); |
@@ -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__ |
@@ -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; | |||
} | |||
@@ -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(); |
@@ -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() { |
@@ -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 |
@@ -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; | |||
} | |||
@@ -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; | |||
}; | |||
}; |
@@ -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 |
@@ -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__ |
@@ -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; | |||
} |
@@ -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; | |||
}; |
@@ -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); | |||
} | |||
@@ -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); | |||
}; | |||
@@ -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 |
@@ -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,10 @@ SOURCE=.\ListViewControl.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 +202,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 | |||
@@ -202,10 +230,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 | |||
@@ -242,6 +278,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 | |||
@@ -254,6 +294,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 | |||
@@ -262,6 +310,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 | |||
@@ -274,10 +326,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 | |||
@@ -294,6 +362,26 @@ 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=.\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 | |||
@@ -322,6 +410,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 | |||
@@ -342,6 +442,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 | |||
@@ -350,6 +454,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 |