Browse Source

The "rfb_win32" library merged with VNC 4.1.1 code.

git-svn-id: svn://svn.code.sf.net/p/tigervnc/code/branches/merge-with-vnc-4.1.1@523 3789f03b-4d11-0410-bbf8-ca57d06f2519
tags/v0.0.90
Constantin Kaplinsky 18 years ago
parent
commit
0a1eca1728
86 changed files with 4094 additions and 1975 deletions
  1. 4
    4
      rfb_win32/AboutDialog.cxx
  2. 2
    2
      rfb_win32/AboutDialog.h
  3. 48
    0
      rfb_win32/BitmapInfo.h
  4. 14
    15
      rfb_win32/CKeyboard.cxx
  5. 7
    9
      rfb_win32/CKeyboard.h
  6. 20
    22
      rfb_win32/CPointer.cxx
  7. 7
    9
      rfb_win32/CPointer.h
  8. 95
    29
      rfb_win32/CleanDesktop.cxx
  9. 2
    2
      rfb_win32/CleanDesktop.h
  10. 8
    7
      rfb_win32/Clipboard.cxx
  11. 2
    2
      rfb_win32/Clipboard.h
  12. 46
    0
      rfb_win32/CompatibleBitmap.h
  13. 40
    0
      rfb_win32/ComputerName.h
  14. 91
    32
      rfb_win32/CurrentUser.cxx
  15. 37
    15
      rfb_win32/CurrentUser.h
  16. 10
    9
      rfb_win32/DIBSectionBuffer.cxx
  17. 2
    4
      rfb_win32/DIBSectionBuffer.h
  18. 188
    0
      rfb_win32/DeviceContext.cxx
  19. 86
    0
      rfb_win32/DeviceContext.h
  20. 20
    29
      rfb_win32/DeviceFrameBuffer.cxx
  21. 5
    20
      rfb_win32/DeviceFrameBuffer.h
  22. 47
    9
      rfb_win32/Dialog.cxx
  23. 2
    3
      rfb_win32/Dialog.h
  24. 45
    0
      rfb_win32/DynamicFn.cxx
  25. 51
    0
      rfb_win32/DynamicFn.h
  26. 103
    0
      rfb_win32/EventManager.cxx
  27. 77
    0
      rfb_win32/EventManager.h
  28. 43
    0
      rfb_win32/Handle.h
  29. 44
    0
      rfb_win32/IconInfo.h
  30. 2
    2
      rfb_win32/IntervalTimer.h
  31. 25
    14
      rfb_win32/LaunchProcess.cxx
  32. 21
    9
      rfb_win32/LaunchProcess.h
  33. 45
    0
      rfb_win32/LocalMem.h
  34. 90
    0
      rfb_win32/LogicalPalette.h
  35. 96
    0
      rfb_win32/LowLevelKeyEvents.cxx
  36. 49
    0
      rfb_win32/LowLevelKeyEvents.h
  37. 40
    0
      rfb_win32/ModuleFileName.h
  38. 205
    0
      rfb_win32/MonitorInfo.cxx
  39. 72
    0
      rfb_win32/MonitorInfo.h
  40. 63
    0
      rfb_win32/MsgBox.h
  41. 3
    3
      rfb_win32/MsgWindow.cxx
  42. 2
    4
      rfb_win32/MsgWindow.h
  43. 2
    2
      rfb_win32/OSVersion.cxx
  44. 2
    3
      rfb_win32/OSVersion.h
  45. 52
    89
      rfb_win32/RegConfig.cxx
  46. 41
    13
      rfb_win32/RegConfig.h
  47. 57
    13
      rfb_win32/Registry.cxx
  48. 10
    9
      rfb_win32/Registry.h
  49. 288
    376
      rfb_win32/SDisplay.cxx
  50. 53
    39
      rfb_win32/SDisplay.h
  51. 52
    0
      rfb_win32/SDisplayCoreDriver.h
  52. 81
    0
      rfb_win32/SDisplayCorePolling.cxx
  53. 75
    0
      rfb_win32/SDisplayCorePolling.h
  54. 74
    0
      rfb_win32/SDisplayCoreWMHooks.cxx
  55. 68
    0
      rfb_win32/SDisplayCoreWMHooks.h
  56. 18
    11
      rfb_win32/SInput.cxx
  57. 3
    5
      rfb_win32/SInput.h
  58. 192
    0
      rfb_win32/Security.cxx
  59. 42
    117
      rfb_win32/Security.h
  60. 37
    30
      rfb_win32/Service.cxx
  61. 12
    7
      rfb_win32/Service.h
  62. 147
    184
      rfb_win32/SocketManager.cxx
  63. 32
    49
      rfb_win32/SocketManager.h
  64. 2
    2
      rfb_win32/TCharArray.cxx
  65. 21
    5
      rfb_win32/TCharArray.h
  66. 151
    0
      rfb_win32/Threading.cxx
  67. 154
    0
      rfb_win32/Threading.h
  68. 2
    3
      rfb_win32/TrayIcon.h
  69. 93
    0
      rfb_win32/TsSessions.cxx
  70. 61
    0
      rfb_win32/TsSessions.h
  71. 21
    15
      rfb_win32/WMCursor.cxx
  72. 2
    6
      rfb_win32/WMCursor.h
  73. 176
    106
      rfb_win32/WMHooks.cxx
  74. 24
    17
      rfb_win32/WMHooks.h
  75. 3
    3
      rfb_win32/WMNotifier.cxx
  76. 2
    2
      rfb_win32/WMNotifier.h
  77. 6
    22
      rfb_win32/WMPoller.cxx
  78. 4
    9
      rfb_win32/WMPoller.h
  79. 2
    2
      rfb_win32/WMShatter.cxx
  80. 2
    5
      rfb_win32/WMShatter.h
  81. 22
    38
      rfb_win32/WMWindowCopyRect.cxx
  82. 3
    6
      rfb_win32/WMWindowCopyRect.h
  83. 30
    363
      rfb_win32/Win32Util.cxx
  84. 6
    168
      rfb_win32/Win32Util.h
  85. 2
    2
      rfb_win32/keymap.h
  86. 108
    0
      rfb_win32/rfb_win32.dsp

+ 4
- 4
rfb_win32/AboutDialog.cxx View File

@@ -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")));
}

+ 2
- 2
rfb_win32/AboutDialog.h View File

@@ -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

+ 48
- 0
rfb_win32/BitmapInfo.h View File

@@ -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

+ 14
- 15
rfb_win32/CKeyboard.cxx View File

@@ -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;
}

+ 7
- 9
rfb_win32/CKeyboard.h View File

@@ -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;
};

+ 20
- 22
rfb_win32/CPointer.cxx View File

@@ -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);
}
}

+ 7
- 9
rfb_win32/CPointer.h View File

@@ -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;

+ 95
- 29
rfb_win32/CleanDesktop.cxx View File

@@ -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
}


+ 2
- 2
rfb_win32/CleanDesktop.h View File

@@ -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

+ 8
- 7
rfb_win32/Clipboard.cxx View File

@@ -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

+ 2
- 2
rfb_win32/Clipboard.h View File

@@ -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

+ 46
- 0
rfb_win32/CompatibleBitmap.h View File

@@ -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

+ 40
- 0
rfb_win32/ComputerName.h View File

@@ -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

+ 91
- 32
rfb_win32/CurrentUser.cxx View File

@@ -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));
}

+ 37
- 15
rfb_win32/CurrentUser.h View File

@@ -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();
};

}

}

+ 10
- 9
rfb_win32/DIBSectionBuffer.cxx View File

@@ -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();
}

}
}


+ 2
- 4
rfb_win32/DIBSectionBuffer.h View File

@@ -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>

+ 188
- 0
rfb_win32/DeviceContext.cxx View File

@@ -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);
}

+ 86
- 0
rfb_win32/DeviceContext.h View File

@@ -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

+ 20
- 29
rfb_win32/DeviceFrameBuffer.cxx View File

@@ -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

+ 5
- 20
rfb_win32/DeviceFrameBuffer.h View File

@@ -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);

};

};

+ 47
- 9
rfb_win32/Dialog.cxx View File

@@ -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);

+ 2
- 3
rfb_win32/Dialog.h View File

@@ -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>

+ 45
- 0
rfb_win32/DynamicFn.cxx View File

@@ -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);
}



+ 51
- 0
rfb_win32/DynamicFn.h View File

@@ -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

+ 103
- 0
rfb_win32/EventManager.cxx View File

@@ -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;
}
}
}

+ 77
- 0
rfb_win32/EventManager.h View File

@@ -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

+ 43
- 0
rfb_win32/Handle.h View File

@@ -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

+ 44
- 0
rfb_win32/IconInfo.h View File

@@ -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

+ 2
- 2
rfb_win32/IntervalTimer.h View File

@@ -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
rfb_win32/LaunchProcess.cxx View File

@@ -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;
}

+ 21
- 9
rfb_win32/LaunchProcess.h View File

@@ -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;

+ 45
- 0
rfb_win32/LocalMem.h View File

@@ -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

+ 90
- 0
rfb_win32/LogicalPalette.h View File

@@ -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

+ 96
- 0
rfb_win32/LowLevelKeyEvents.cxx View File

@@ -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;
}
}

+ 49
- 0
rfb_win32/LowLevelKeyEvents.h View File

@@ -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__

+ 40
- 0
rfb_win32/ModuleFileName.h View File

@@ -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

+ 205
- 0
rfb_win32/MonitorInfo.cxx View File

@@ -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);
}



+ 72
- 0
rfb_win32/MonitorInfo.h View File

@@ -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

+ 63
- 0
rfb_win32/MsgBox.h View File

@@ -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

+ 3
- 3
rfb_win32/MsgWindow.cxx View File

@@ -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);
}
}

+ 2
- 4
rfb_win32/MsgWindow.h View File

@@ -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 {

+ 2
- 2
rfb_win32/OSVersion.cxx View File

@@ -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

+ 2
- 3
rfb_win32/OSVersion.h View File

@@ -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 {

+ 52
- 89
rfb_win32/RegConfig.cxx View File

@@ -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(&notifyMsg, 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();
}

+ 41
- 13
rfb_win32/RegConfig.h View File

@@ -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;
};

};

+ 57
- 13
rfb_win32/Registry.cxx View File

@@ -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;
}

+ 10
- 9
rfb_win32/Registry.h View File

@@ -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:

+ 288
- 376
rfb_win32/SDisplay.cxx View File

@@ -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;
}

+ 53
- 39
rfb_win32/SDisplay.h View File

@@ -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;

+ 52
- 0
rfb_win32/SDisplayCoreDriver.h View File

@@ -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

+ 81
- 0
rfb_win32/SDisplayCorePolling.cxx View File

@@ -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);
}
}
}

+ 75
- 0
rfb_win32/SDisplayCorePolling.h View File

@@ -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

+ 74
- 0
rfb_win32/SDisplayCoreWMHooks.cxx View File

@@ -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();
}

+ 68
- 0
rfb_win32/SDisplayCoreWMHooks.h View File

@@ -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

+ 18
- 11
rfb_win32/SInput.cxx View File

@@ -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");

+ 3
- 5
rfb_win32/SInput.h View File

@@ -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;

+ 192
- 0
rfb_win32/Security.cxx View File

@@ -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();
}

+ 42
- 117
rfb_win32/Security.h View File

@@ -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);

}


+ 37
- 30
rfb_win32/Service.cxx View File

@@ -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;
}
}

+ 12
- 7
rfb_win32/Service.h View File

@@ -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();

+ 147
- 184
rfb_win32/SocketManager.cxx View File

@@ -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--;
}


+ 32
- 49
rfb_win32/SocketManager.h View File

@@ -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;
};

}

+ 2
- 2
rfb_win32/TCharArray.cxx View File

@@ -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
- 5
rfb_win32/TCharArray.h View File

@@ -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

+ 151
- 0
rfb_win32/Threading.cxx View File

@@ -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;
}

+ 154
- 0
rfb_win32/Threading.h View File

@@ -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

+ 2
- 3
rfb_win32/TrayIcon.h View File

@@ -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>

+ 93
- 0
rfb_win32/TsSessions.cxx View File

@@ -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
}

};
};

+ 61
- 0
rfb_win32/TsSessions.h View File

@@ -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

+ 21
- 15
rfb_win32/WMCursor.cxx View File

@@ -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);

+ 2
- 6
rfb_win32/WMCursor.h View File

@@ -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__

+ 176
- 106
rfb_win32/WMHooks.cxx View File

@@ -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;
}



+ 24
- 17
rfb_win32/WMHooks.h View File

@@ -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();

+ 3
- 3
rfb_win32/WMNotifier.cxx View File

@@ -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() {

+ 2
- 2
rfb_win32/WMNotifier.h View File

@@ -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

+ 6
- 22
rfb_win32/WMPoller.cxx View File

@@ -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;
}


+ 4
- 9
rfb_win32/WMPoller.h View File

@@ -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;
};

};

+ 2
- 2
rfb_win32/WMShatter.cxx View File

@@ -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

+ 2
- 5
rfb_win32/WMShatter.h View File

@@ -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__

+ 22
- 38
rfb_win32/WMWindowCopyRect.cxx View File

@@ -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;
}

+ 3
- 6
rfb_win32/WMWindowCopyRect.h View File

@@ -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;
};

+ 30
- 363
rfb_win32/Win32Util.cxx View File

@@ -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);
}



+ 6
- 168
rfb_win32/Win32Util.h View File

@@ -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);

};


+ 2
- 2
rfb_win32/keymap.h View File

@@ -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

+ 108
- 0
rfb_win32/rfb_win32.dsp View File

@@ -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

Loading…
Cancel
Save