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
diff --git a/rfb_win32/SDisplay.cxx b/rfb_win32/SDisplay.cxx
index 4916c48..0af5064 100644
--- a/rfb_win32/SDisplay.cxx
+++ b/rfb_win32/SDisplay.cxx
@@ -1,5 +1,5 @@
-/* Copyright (C) 2002-2004 RealVNC Ltd. All Rights Reserved.
- *
+/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
+ *
* This is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
@@ -20,21 +20,19 @@
//
// The SDisplay class encapsulates a particular system display.
-#include <assert.h>
-
#include <rfb_win32/SDisplay.h>
#include <rfb_win32/Service.h>
-#include <rfb_win32/WMShatter.h>
-#include <rfb_win32/osVersion.h>
-#include <rfb_win32/Win32Util.h>
-#include <rfb_win32/IntervalTimer.h>
+#include <rfb_win32/TsSessions.h>
#include <rfb_win32/CleanDesktop.h>
-
-#include <rfb/util.h>
-#include <rfb/LogWriter.h>
+#include <rfb_win32/CurrentUser.h>
+#include <rfb_win32/DynamicFn.h>
+#include <rfb_win32/MonitorInfo.h>
+#include <rfb_win32/SDisplayCorePolling.h>
+#include <rfb_win32/SDisplayCoreWMHooks.h>
+#include <rfb_win32/SDisplayCoreDriver.h>
#include <rfb/Exception.h>
+#include <rfb/LogWriter.h>
-#include <rfb/Configuration.h>
using namespace rdr;
using namespace rfb;
@@ -44,209 +42,37 @@
// - 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,57 +97,14 @@
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;
-
- // Switch to the current input desktop
- // ***
- if (rfb::win32::desktopChangeRequired()) {
- if (!rfb::win32::changeDesktop())
- throw rdr::Exception("unable to switch into input desktop");
- }
-
- // Clear the change tracker
- change_tracker.clear();
-
- // Create the framebuffer object
- recreatePixelBuffer();
-
- // Create the SDisplayCore
- core = new SDisplayCore(this);
- assert(core);
-
- // Start display monitor and clipboard handler
- core->wm_monitor.setNotifier(this);
- core->clipboard.setNotifier(this);
-
- // Apply desktop optimisations
- if (removePattern)
- core->cleanDesktop.disablePattern();
- if (removeWallpaper)
- core->cleanDesktop.disableWallpaper();
- if (disableEffects)
- core->cleanDesktop.disableEffects();
-
- // Start hooks
- core->wm_hooks.setClipRect(screenRect);
- if (core->use_hooks) {
- // core->wm_hooks.setDiagnosticRange(0, 0x400-1);
- core->using_hooks = core->wm_hooks.setUpdateTracker(this);
- if (!core->using_hooks)
- vlog.debug("hook subsystem failed to initialise");
- }
-
- // Set up timers
- core->pollTimer.start(core->using_hooks ? BG_POLLING_INTERVAL : FG_POLLING_INTERVAL);
- core->cursorTimer.start(10);
-
- // Register an interest in faked copyrect events
- core->wm_copyrect.setUpdateTracker(&change_tracker);
- core->wm_copyrect.setClipRect(screenRect);
-
- // Polling of particular windows on the desktop
- core->wm_poller.setUpdateTracker(&change_tracker);
- core->wm_poller.setClipRect(screenRect);
+ startCore();
vlog.debug("started");
@@ -331,108 +114,229 @@
void SDisplay::stop()
{
vlog.debug("stopping");
+
+ // If we successfully start()ed then perform the DisconnectAction
if (core) {
- // If SDisplay was actually active then perform the disconnect action
+ CurrentUserToken cut;
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)();
+ 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);
+ }
}
}
- delete core;
- core = 0;
- delete pb;
- pb = 0;
- if (device) {
- if (releaseDevice)
- ReleaseDC(0, device);
- else
- DeleteDC(device);
- }
- device = 0;
+
+ // Stop the SDisplayCore
if (server)
server->setPixelBuffer(0);
-
+ stopCore();
server = 0;
+
vlog.debug("stopped");
if (statusLocation) *statusLocation = false;
}
-void SDisplay::restart() {
- vlog.debug("restarting");
- // Close down the hooks
- delete core;
- core = 0;
+
+void SDisplay::startCore() {
+
+ // Currently, we just check whether we're in the console session, and
+ // fail if not
+ if (!inConsoleSession())
+ throw rdr::Exception("Console is not session zero - oreconnect to restore Console sessin");
+
+ // Switch to the current input desktop
+ if (rfb::win32::desktopChangeRequired()) {
+ if (!rfb::win32::changeDesktop())
+ throw rdr::Exception("unable to switch into input desktop");
+ }
+
+ // Initialise the change tracker and clipper
+ updates.clear();
+ clipper.setUpdateTracker(server);
+
+ // Create the framebuffer object
+ recreatePixelBuffer(true);
+
+ // Create the SDisplayCore
+ updateMethod_ = updateMethod;
+ int tryMethod = updateMethod_;
+ while (!core) {
+ try {
+ if (tryMethod == 2)
+ core = new SDisplayCoreDriver(this, &updates);
+ else if (tryMethod == 1)
+ core = new SDisplayCoreWMHooks(this, &updates);
+ else
+ core = new SDisplayCorePolling(this, &updates);
+ core->setScreenRect(screenRect);
+ } catch (rdr::Exception& e) {
+ delete core; core = 0;
+ if (tryMethod == 0)
+ throw rdr::Exception("unable to access desktop");
+ tryMethod--;
+ vlog.error(e.str());
+ }
+ }
+ vlog.info("Started %s", core->methodName());
+
+ // Start display monitor, clipboard handler and input handlers
+ monitor = new WMMonitor;
+ monitor->setNotifier(this);
+ clipboard = new Clipboard;
+ clipboard->setNotifier(this);
+ ptr = new SPointer;
+ kbd = new SKeyboard;
+ inputs = new WMBlockInput;
+ cursor = new WMCursor;
+
+ // Apply desktop optimisations
+ cleanDesktop = new CleanDesktop;
+ if (removePattern)
+ cleanDesktop->disablePattern();
+ if (removeWallpaper)
+ cleanDesktop->disableWallpaper();
+ if (disableEffects)
+ cleanDesktop->disableEffects();
+ isWallpaperRemoved = removeWallpaper;
+ isPatternRemoved = removePattern;
+ areEffectsDisabled = disableEffects;
+}
+
+void SDisplay::stopCore() {
+ if (core)
+ vlog.info("Stopping %s", core->methodName());
+ delete core; core = 0;
+ delete pb; pb = 0;
+ delete device; device = 0;
+ delete monitor; monitor = 0;
+ delete clipboard; clipboard = 0;
+ delete inputs; inputs = 0;
+ delete ptr; ptr = 0;
+ delete kbd; kbd = 0;
+ delete cleanDesktop; cleanDesktop = 0;
+ delete cursor; cursor = 0;
+ ResetEvent(updateEvent);
+}
+
+
+bool SDisplay::areHooksAvailable() {
+ return WMHooks::areAvailable();
+}
+
+bool SDisplay::isDriverAvailable() {
+ return SDisplayCoreDriver::isAvailable();
+}
+
+
+bool SDisplay::isRestartRequired() {
+ // - We must restart the SDesktop if:
+ // 1. We are no longer in the input desktop.
+ // 2. The any setting has changed.
+
+ // - Check that our session is the Console
+ if (!inConsoleSession())
+ return true;
+
+ // - Check that we are in the input desktop
+ if (rfb::win32::desktopChangeRequired())
+ return true;
+
+ // - Check that the update method setting hasn't changed
+ // NB: updateMethod reflects the *selected* update method, not
+ // necessarily the one in use, since we fall back to simpler
+ // methods if more advanced ones fail!
+ if (updateMethod_ != updateMethod)
+ return true;
+
+ // - Check that the desktop optimisation settings haven't changed
+ // This isn't very efficient, but it shouldn't change very often!
+ if ((isWallpaperRemoved != removeWallpaper) ||
+ (isPatternRemoved != removePattern) ||
+ (areEffectsDisabled != disableEffects))
+ return true;
+
+ return false;
+}
+
+
+void SDisplay::restartCore() {
+ vlog.info("restarting");
+
+ // Stop the existing Core related resources
+ stopCore();
try {
- // Re-start the hooks if possible
- start(server);
- vlog.debug("restarted");
+ // Start a new Core if possible
+ startCore();
+ vlog.info("restarted");
} catch (rdr::Exception& e) {
- // If start() fails then we MUST disconnect all clients,
- // to cause the server to stop using the desktop.
+ // If startCore() fails then we MUST disconnect all clients,
+ // to cause the server to stop() the desktop.
// Otherwise, the SDesktop is in an inconsistent state
- // and the server will crash
+ // and the server will crash.
server->closeClients(e.str());
}
}
-void SDisplay::pointerEvent(const Point& pos, rdr::U8 buttonmask) {
+void SDisplay::pointerEvent(const Point& pos, int buttonmask) {
if (pb->getRect().contains(pos)) {
Point screenPos = pos.translate(screenRect.tl);
- core->ptr.pointerEvent(screenPos, buttonmask);
+ // - Check that the SDesktop doesn't need restarting
+ if (isRestartRequired())
+ restartCore();
+ if (ptr)
+ ptr->pointerEvent(screenPos, buttonmask);
}
}
void SDisplay::keyEvent(rdr::U32 key, bool down) {
- core->kbd.keyEvent(key, down);
+ // - Check that the SDesktop doesn't need restarting
+ if (isRestartRequired())
+ restartCore();
+ if (kbd)
+ kbd->keyEvent(key, down);
}
void SDisplay::clientCutText(const char* text, int len) {
CharArray clip_sz(len+1);
memcpy(clip_sz.buf, text, len);
clip_sz.buf[len] = 0;
- core->clipboard.setClipText(clip_sz.buf);
+ clipboard->setClipText(clip_sz.buf);
}
void SDisplay::framebufferUpdateRequest()
{
- triggerUpdate();
+ SetEvent(updateEvent);
}
Point SDisplay::getFbSize() {
bool startAndStop = !core;
+
// If not started, do minimal initialisation to get desktop size.
- if (startAndStop) recreatePixelBuffer();
+ if (startAndStop)
+ recreatePixelBuffer();
Point result = Point(pb->width(), pb->height());
+
// Destroy the initialised structures.
- if (startAndStop) stop();
+ if (startAndStop)
+ stopCore();
return result;
}
void
-SDisplay::add_changed(const Region& rgn) {
- change_tracker.add_changed(rgn);
- triggerUpdate();
-}
-
-void
-SDisplay::add_copied(const Region& dest, const Point& delta) {
- change_tracker.add_copied(dest, delta);
- triggerUpdate();
-}
-
-
-void
SDisplay::notifyClipboardChanged(const char* text, int len) {
vlog.debug("clipboard text changed");
if (server)
@@ -462,36 +366,42 @@
}
}
-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 @@
// 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 @@
// 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)
- throw SystemException("cannot open the display", GetLastError());
+ if (!new_device) {
+ vlog.info("Attaching to virtual desktop");
+ new_device = new WindowDC(0);
+ }
- // 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);
- }
-
- // Create a DeviceFrameBuffer attached to it
- DeviceFrameBuffer* new_buffer = new DeviceFrameBuffer(new_device);
-
- // Has anything actually changed about the screen or the buffer?
- if (!pb ||
- (!newScreenRect.equals(screenRect)) ||
- (!new_buffer->getPF().equal(pb->getPF())))
- {
- // Yes. Update the buffer state.
- screenRect = newScreenRect;
- vlog.debug("creating pixel buffer for device");
-
- // Flush any existing changes to the server
- flushChangeTracker();
-
- // Replace the old PixelBuffer
- if (pb) delete pb;
- if (device) DeleteDC(device);
- pb = new_buffer;
- device = new_device;
-
- // Initialise the pixels
- pb->grabRegion(pb->getRect());
-
- // Prevent future grabRect operations from throwing exceptions
- pb->setIgnoreGrabErrors(true);
-
- // Update the SDisplayCore if required
- if (core)
- core->setPixelBuffer(pb);
-
- // Inform the server of the changes
- if (server)
- server->setPixelBuffer(pb);
-
+ if (deviceName.buf[0]) {
+ MonitorInfo info(CStr(deviceName.buf));
+ newScreenRect = Rect(info.rcMonitor.left, info.rcMonitor.top,
+ info.rcMonitor.right, info.rcMonitor.bottom);
} else {
- delete new_buffer;
- DeleteDC(new_device);
+ newScreenRect = new_device->getClipBox();
}
+
+ // If nothing has changed & a recreate has not been forced, delete
+ // the new device context and return
+ if (pb && !force &&
+ newScreenRect.equals(screenRect) &&
+ new_device->getPF().equal(pb->getPF())) {
+ delete new_device;
+ return;
+ }
+
+ // Flush any existing changes to the server
+ flushChangeTracker();
+
+ // Delete the old pixelbuffer and device context
+ vlog.debug("deleting old pixel buffer & device");
+ if (pb)
+ delete pb;
+ if (device)
+ delete device;
+
+ // Create a DeviceFrameBuffer attached to the new device
+ vlog.debug("creating pixel buffer");
+ DeviceFrameBuffer* new_buffer = new DeviceFrameBuffer(*new_device);
+
+ // Replace the old PixelBuffer
+ screenRect = newScreenRect;
+ pb = new_buffer;
+ device = new_device;
+
+ // Initialise the pixels
+ pb->grabRegion(pb->getRect());
+
+ // Prevent future grabRect operations from throwing exceptions
+ pb->setIgnoreGrabErrors(true);
+
+ // Update the clipping update tracker
+ clipper.setClipRect(pb->getRect());
+
+ // Inform the core of the changes
+ if (core)
+ core->setScreenRect(screenRect);
+
+ // Inform the server of the changes
+ if (server)
+ server->setPixelBuffer(pb);
}
bool SDisplay::flushChangeTracker() {
- if (change_tracker.is_empty())
+ if (updates.is_empty())
return false;
- // Translate the update coordinates from Screen coords to Desktop
- change_tracker.translate(screenRect.tl.negate());
- // Flush the updates through
- change_tracker.get_update(*server);
- change_tracker.clear();
- return true;
-}
-void SDisplay::triggerUpdate() {
- if (core)
- SetEvent(updateEvent);
+ vlog.write(120, "flushChangeTracker");
+
+ // Translate the update coordinates from Screen coords to Desktop
+ updates.translate(screenRect.tl.negate());
+
+ // Clip the updates & flush them to the server
+ updates.copyTo(&clipper);
+ updates.clear();
+ return true;
}