diff --git a/platform/CXWindowsScreen.cpp b/platform/CXWindowsScreen.cpp index f1eb3992..4e88b5ed 100644 --- a/platform/CXWindowsScreen.cpp +++ b/platform/CXWindowsScreen.cpp @@ -7,10 +7,61 @@ #include "CLock.h" #include "CThread.h" #include "CLog.h" +#include "IJob.h" #include "CString.h" #include #include +// +// CXWindowsScreen::CTimer +// + +CXWindowsScreen::CTimer::CTimer(IJob* job, double timeout) : + m_job(job), + m_timeout(timeout) +{ + assert(m_job != NULL); + assert(m_timeout > 0.0); + + reset(); +} + +CXWindowsScreen::CTimer::~CTimer() +{ + // do nothing +} + +void +CXWindowsScreen::CTimer::run() +{ + m_job->run(); +} + +void +CXWindowsScreen::CTimer::reset() +{ + m_time = m_timeout; +} + +CXWindowsScreen::CTimer::CTimer& +CXWindowsScreen::CTimer::operator-=(double dt) +{ + m_time -= dt; + return *this; +} + +CXWindowsScreen::CTimer::operator double() const +{ + return m_time; +} + +bool +CXWindowsScreen::CTimer::operator<(const CTimer& t) const +{ + return m_time < t.m_time; +} + + // // CXWindowsScreen // @@ -37,6 +88,38 @@ CXWindowsScreen::~CXWindowsScreen() s_screen = NULL; } +void +CXWindowsScreen::addTimer(IJob* job, double timeout) +{ + CLock lock(&m_timersMutex); + removeTimerNoLock(job); + m_timers.push(CTimer(job, timeout)); +} + +void +CXWindowsScreen::removeTimer(IJob* job) +{ + CLock lock(&m_timersMutex); + removeTimerNoLock(job); +} + +void +CXWindowsScreen::removeTimerNoLock(IJob* job) +{ + // do it the hard way. first collect all jobs that are not + // the removed job. + CTimerPriorityQueue::container_type tmp; + for (CTimerPriorityQueue::iterator index = m_timers.begin(); + index != m_timers.end(); ++index) { + if (index->getJob() != job) { + tmp.push_back(*index); + } + } + + // now swap in the new list + m_timers.swap(tmp); +} + void CXWindowsScreen::openDisplay() { @@ -81,7 +164,7 @@ CXWindowsScreen::openDisplay() } // initialize the screen saver - m_screenSaver = new CXWindowsScreenSaver(m_display); + m_screenSaver = new CXWindowsScreenSaver(this, m_display); } void @@ -181,6 +264,12 @@ CXWindowsScreen::getEvent(XEvent* xevent) const m_mutex.lock(); for (;;) { while (!m_stop && XPending(m_display) == 0) { + // check timers + if (const_cast(this)->processTimers()) { + continue; + } + + // wait m_mutex.unlock(); CThread::sleep(0.01); m_mutex.lock(); @@ -298,13 +387,56 @@ CXWindowsScreen::processEvent(XEvent* xevent) } // let screen saver have a go - if (m_screenSaver->processEvent(xevent)) { - return true; - } + m_screenSaver->processEvent(xevent); return false; } +bool +CXWindowsScreen::processTimers() +{ + std::vector jobs; + { + CLock lock(&m_timersMutex); + + // get current time + const double time = m_time.getTime(); + + // done if no timers have expired + if (m_timers.empty() || m_timers.top() > time) { + return false; + } + + // subtract current time from all timers. note that this won't + // change the order of elements in the priority queue (except + // for floating point round off which we'll ignore). + for (CTimerPriorityQueue::iterator index = m_timers.begin(); + index != m_timers.end(); ++index) { + (*index) -= time; + } + + // process all timers at or below zero, saving the jobs + while (m_timers.top() <= 0.0) { + CTimer timer = m_timers.top(); + jobs.push_back(timer.getJob()); + timer.reset(); + m_timers.pop(); + m_timers.push(timer); + } + + // reset the clock + m_time.reset(); + } + + // now run the jobs. note that if one of these jobs removes + // a timer later in the jobs list and deletes that job pointer + // then this will crash when it tries to run that job. + for (std::vector::iterator index = jobs.begin(); + index != jobs.end(); ++index) { + (*index)->run(); + } +} + CXWindowsScreenSaver* CXWindowsScreen::getScreenSaver() const { diff --git a/platform/CXWindowsScreen.h b/platform/CXWindowsScreen.h index b7a13fdd..e4ed2cb3 100644 --- a/platform/CXWindowsScreen.h +++ b/platform/CXWindowsScreen.h @@ -3,13 +3,18 @@ #include "ClipboardTypes.h" #include "CMutex.h" +#include "CStopwatch.h" +#include "stdvector.h" #if defined(X_DISPLAY_MISSING) # error X11 is required to build synergy #else # include #endif +#include +#include class IClipboard; +class IJob; class IScreenSaver; class CXWindowsClipboard; class CXWindowsScreenSaver; @@ -19,6 +24,15 @@ public: CXWindowsScreen(); virtual ~CXWindowsScreen(); + // manipulators + + // add/remove a job to invoke every timeout seconds. the job is + // called with the display locked. if a job timeout expires twice + // or more before the job can be called then the job is called + // just once. the caller retains ownership of the job. + void addTimer(IJob*, double timeout); + void removeTimer(IJob*); + protected: class CDisplayLock { public: @@ -94,9 +108,15 @@ protected: virtual void onLostClipboard(ClipboardID) = 0; private: + // remove a timer without locking + void removeTimerNoLock(IJob*); + // internal event processing bool processEvent(XEvent*); + // process timers + bool processTimers(); + // determine the clipboard from the X selection. returns // kClipboardEnd if no such clipboard. ClipboardID getClipboardID(Atom selection) const; @@ -112,6 +132,112 @@ private: static int ioErrorHandler(Display*); private: + // a priority queue will direct access to the elements + template , + class Compare = std::greater > + class CPriorityQueue { + public: + typedef typename Container::value_type value_type; + typedef typename Container::size_type size_type; + typedef typename Container::iterator iterator; + typedef Container container_type; + + CPriorityQueue() { } + CPriorityQueue(Container& swappedIn); + ~CPriorityQueue() { } + + // manipulators + + void push(const value_type& v) + { + c.push_back(v); + std::push_heap(c.begin(), c.end(), comp); + } + + void pop() + { + std::pop_heap(c.begin(), c.end(), comp); + c.pop_back(); + } + + iterator begin() + { + return c.begin(); + } + + iterator end() + { + return c.end(); + } + + void swap(CPriorityQueue& q) + { + c.swap(q.c); + } + + void swap(Container& c2) + { + c.swap(c2); + std::make_heap(c.begin(), c.end(), comp); + } + + // accessors + + bool empty() const + { + return c.empty(); + } + + size_type size() const + { + return c.size(); + } + + const value_type& + top() const + { + return c.front(); + } + + private: + Container c; + Compare comp; + }; + + // a timer priority queue element + class CTimer { + public: + CTimer(IJob* job, double timeout); + ~CTimer(); + + // manipulators + + void run(); + + void reset(); + + CTimer& operator-=(double); + + // accessors + + IJob* getJob() const + { + return m_job; + } + + operator double() const; + + bool operator<(const CTimer&) const; + + private: + IJob* m_job; + double m_timeout; + double m_time; + }; + +private: + typedef CPriorityQueue CTimerPriorityQueue; + Display* m_display; int m_screen; Window m_root; @@ -125,6 +251,11 @@ private: // screen saver CXWindowsScreenSaver* m_screenSaver; + // timers, the stopwatch used to time, and a mutex for the timers + CTimerPriorityQueue m_timers; + CStopwatch m_time; + CMutex m_timersMutex; + // X is not thread safe CMutex m_mutex; diff --git a/platform/CXWindowsScreenSaver.cpp b/platform/CXWindowsScreenSaver.cpp index ab5ec8e6..c6dbddc6 100644 --- a/platform/CXWindowsScreenSaver.cpp +++ b/platform/CXWindowsScreenSaver.cpp @@ -1,17 +1,31 @@ #include "CXWindowsScreenSaver.h" +#include "CXWindowsScreen.h" #include "CXWindowsUtil.h" #include "CLog.h" +#include "TMethodJob.h" #include +#if defined(HAVE_X11_EXTENSIONS_XTEST_H) +# include +#else +# error The XTest extension is required to build synergy +#endif // // CXWindowsScreenSaver // -CXWindowsScreenSaver::CXWindowsScreenSaver(Display* display) : +CXWindowsScreenSaver::CXWindowsScreenSaver( + CXWindowsScreen* screen, Display* display) : + m_screen(screen), m_display(display), m_notify(None), - m_xscreensaver(None) + m_xscreensaver(None), + m_xscreensaverActive(false) { + // screen saver disable callback + m_disableJob = new TMethodJob(this, + &CXWindowsScreenSaver::disableCallback); + // get atoms m_atomScreenSaver = XInternAtom(m_display, "SCREENSAVER", False); @@ -22,8 +36,9 @@ CXWindowsScreenSaver::CXWindowsScreenSaver(Display* display) : m_atomScreenSaverDeactivate = XInternAtom(m_display, "DEACTIVATE", False); - // create dummy window to receive xscreensaver responses. earlier - // versions of xscreensaver will die if we pass None as the window. + // create dummy window to receive xscreensaver responses. this + // shouldn't be necessary (we should be able to send responses + // to None) but it doesn't hurt. XSetWindowAttributes attr; attr.event_mask = 0;//PropertyChangeMask; attr.do_not_propagate_mask = 0; @@ -53,7 +68,9 @@ CXWindowsScreenSaver::CXWindowsScreenSaver(Display* display) : } // get the xscreensaver window, if any - updateXScreenSaver(); + if (!findXScreenSaver()) { + setXScreenSaver(None); + } // get the built-in settings XGetScreenSaver(m_display, &m_timeout, &m_interval, @@ -62,30 +79,67 @@ CXWindowsScreenSaver::CXWindowsScreenSaver(Display* display) : CXWindowsScreenSaver::~CXWindowsScreenSaver() { + // clear watch list + clearWatchForXScreenSaver(); + // stop watching root for events CXWindowsUtil::CErrorLock lock(m_display); Window root = DefaultRootWindow(m_display); XSelectInput(m_display, root, m_rootEventMask); + + // destroy dummy sink window + XDestroyWindow(m_display, m_xscreensaverSink); + + // done with disable job + m_screen->removeTimer(m_disableJob); + delete m_disableJob; } bool CXWindowsScreenSaver::processEvent(XEvent* xevent) { switch (xevent->type) { + case CreateNotify: + if (m_xscreensaver == None) { + if (isXScreenSaver(xevent->xcreatewindow.window)) { + // found the xscreensaver + setXScreenSaver(xevent->xcreatewindow.window); + } + else { + // another window to watch. to detect the xscreensaver + // window we look for a property but that property may + // not yet exist by the time we get this event so we + // have to watch the window for property changes. + // this would be so much easier if xscreensaver did the + // smart thing and stored its window in a property on + // the root window. + addWatchXScreenSaver(xevent->xcreatewindow.window); + } + } + break; + case DestroyNotify: if (xevent->xdestroywindow.window == m_xscreensaver) { // xscreensaver is gone - setXScreenSaver(false); log((CLOG_DEBUG "xscreensaver died")); - m_xscreensaver = None; + setXScreenSaver(None); return true; } break; + case PropertyNotify: + if (xevent->xproperty.state == PropertyNewValue) { + if (isXScreenSaver(xevent->xproperty.window)) { + // found the xscreensaver + setXScreenSaver(xevent->xcreatewindow.window); + } + } + break; + case MapNotify: if (xevent->xmap.window == m_xscreensaver) { // xscreensaver has activated - setXScreenSaver(true); + setXScreenSaverActive(true); return true; } break; @@ -93,7 +147,7 @@ CXWindowsScreenSaver::processEvent(XEvent* xevent) case UnmapNotify: if (xevent->xunmap.window == m_xscreensaver) { // xscreensaver has deactivated - setXScreenSaver(false); + setXScreenSaverActive(false); return true; } break; @@ -111,14 +165,11 @@ CXWindowsScreenSaver::setNotify(Window notify) void CXWindowsScreenSaver::enable() { - // try xscreensaver - updateXScreenSaver(); - if (m_xscreensaver) { - // FIXME - return; - } +log((CLOG_INFO "enable screensaver")); + // for xscreensaver + m_screen->removeTimer(m_disableJob); - // use built-in X screen saver + // for built-in X screen saver XSetScreenSaver(m_display, m_timeout, m_interval, m_preferBlanking, m_allowExposures); } @@ -126,12 +177,10 @@ CXWindowsScreenSaver::enable() void CXWindowsScreenSaver::disable() { - // try xscreensaver - updateXScreenSaver(); - if (m_xscreensaver) { - // FIXME - return; - } +log((CLOG_INFO "disable screensaver")); + // for xscreensaver. 5 seconds should be plenty often to + // suppress the screen saver. + m_screen->addTimer(m_disableJob, 5.0); // use built-in X screen saver XGetScreenSaver(m_display, &m_timeout, &m_interval, @@ -145,8 +194,8 @@ void CXWindowsScreenSaver::activate() { // try xscreensaver - updateXScreenSaver(); - if (m_xscreensaver) { + findXScreenSaver(); + if (m_xscreensaver != None) { sendXScreenSaverCommand(m_atomScreenSaverActivate); return; } @@ -159,8 +208,8 @@ void CXWindowsScreenSaver::deactivate() { // try xscreensaver - updateXScreenSaver(); - if (m_xscreensaver) { + findXScreenSaver(); + if (m_xscreensaver != None) { sendXScreenSaverCommand(m_atomScreenSaverDeactivate); return; } @@ -202,8 +251,70 @@ CXWindowsScreenSaver::sendNotify(bool activated) } } +bool +CXWindowsScreenSaver::findXScreenSaver() +{ + // do nothing if we've already got the xscreensaver window + if (m_xscreensaver == None) { + // find top-level window xscreensaver window + Window root = DefaultRootWindow(m_display); + Window rw, pw, *cw; + unsigned int nc; + if (XQueryTree(m_display, root, &rw, &pw, &cw, &nc)) { + for (unsigned int i = 0; i < nc; ++i) { + if (isXScreenSaver(cw[i])) { + setXScreenSaver(cw[i]); + break; + } + } + XFree(cw); + } + } + + return (m_xscreensaver != None); +} + void -CXWindowsScreenSaver::setXScreenSaver(bool activated) +CXWindowsScreenSaver::setXScreenSaver(Window window) +{ + log((CLOG_DEBUG "xscreensaver window: 0x%08x", window)); + + // save window + m_xscreensaver = window; + + if (m_xscreensaver != None) { + // clear old watch list + clearWatchForXScreenSaver(); + + // see if xscreensaver is active + bool error = false; + CXWindowsUtil::CErrorLock lock(m_display, &error); + XWindowAttributes attr; + XGetWindowAttributes(m_display, m_xscreensaver, &attr); + setXScreenSaverActive(!error && attr.map_state != IsUnmapped); + } + else { + // screen saver can't be active if it doesn't exist + setXScreenSaverActive(false); + + // start watching for xscreensaver + watchForXScreenSaver(); + } +} + +bool +CXWindowsScreenSaver::isXScreenSaver(Window w) const +{ + // check for m_atomScreenSaverVersion string property + Atom type; + return (CXWindowsUtil::getWindowProperty(m_display, w, + m_atomScreenSaverVersion, + NULL, &type, NULL, False) && + type == XA_STRING); +} + +void +CXWindowsScreenSaver::setXScreenSaverActive(bool activated) { if (m_xscreensaverActive != activated) { log((CLOG_DEBUG "xscreensaver %s", activated ? "activated" : "deactivated")); @@ -212,48 +323,6 @@ CXWindowsScreenSaver::setXScreenSaver(bool activated) } } -void -CXWindowsScreenSaver::updateXScreenSaver() -{ - // do nothing if we've already got the xscreensaver window - if (m_xscreensaver != None) { - return; - } - - // find top-level window with m_atomScreenSaverVersion string property - CXWindowsUtil::CErrorLock lock(m_display); - Window root = DefaultRootWindow(m_display); - Window rw, pw, *cw; - unsigned int nc; - if (XQueryTree(m_display, root, &rw, &pw, &cw, &nc)) { - CString data; - Atom type; - for (unsigned int i = 0; i < nc; ++i) { - if (CXWindowsUtil::getWindowProperty(m_display, cw[i], - m_atomScreenSaverVersion, - &data, &type, NULL, False) && - type == XA_STRING) { - m_xscreensaver = cw[i]; - log((CLOG_DEBUG "found xscreensaver: 0x%08x", m_xscreensaver)); - break; - } - } - XFree(cw); - } - - // see if xscreensaver is active - if (m_xscreensaver != None) { - bool error = false; - CXWindowsUtil::CErrorLock lock(m_display, &error); - XWindowAttributes attr; - XGetWindowAttributes(m_display, m_xscreensaver, &attr); - setXScreenSaver(!error && attr.map_state != IsUnmapped); - } - else { - setXScreenSaver(false); - } -} - void CXWindowsScreenSaver::sendXScreenSaverCommand(Atom cmd, long arg1, long arg2) { @@ -274,6 +343,93 @@ CXWindowsScreenSaver::sendXScreenSaverCommand(Atom cmd, long arg1, long arg2) CXWindowsUtil::CErrorLock lock(m_display, &error); XSendEvent(m_display, m_xscreensaver, False, 0, &event); if (error) { - updateXScreenSaver(); + findXScreenSaver(); } } + +void +CXWindowsScreenSaver::watchForXScreenSaver() +{ + // clear old watch list + clearWatchForXScreenSaver(); + + // add every child of the root to the list of windows to watch + Window root = DefaultRootWindow(m_display); + Window rw, pw, *cw; + unsigned int nc; + if (XQueryTree(m_display, root, &rw, &pw, &cw, &nc)) { + for (unsigned int i = 0; i < nc; ++i) { + addWatchXScreenSaver(cw[i]); + } + XFree(cw); + } + + // now check for xscreensaver window in case it set the property + // before we could request property change events. + if (findXScreenSaver()) { + // found it so clear out our watch list + clearWatchForXScreenSaver(); + } +} + +void +CXWindowsScreenSaver::clearWatchForXScreenSaver() +{ + // stop watching all windows + CXWindowsUtil::CErrorLock lock(m_display); + for (CWatchList::iterator index = m_watchWindows.begin(); + index != m_watchWindows.end(); ++index) { + XSelectInput(m_display, index->first, index->second); + } + m_watchWindows.clear(); +} + +void +CXWindowsScreenSaver::addWatchXScreenSaver(Window window) +{ + bool error = false; + CXWindowsUtil::CErrorLock lock(m_display, &error); + + // get window attributes + XWindowAttributes attr; + XGetWindowAttributes(m_display, window, &attr); + + // if successful and window uses override_redirect (like xscreensaver + // does) then watch it for property changes. + if (!error && attr.override_redirect == True) { + XSelectInput(m_display, window, + attr.your_event_mask | PropertyChangeMask); + if (!error) { + // if successful then add the window to our list + m_watchWindows.insert(std::make_pair(window, attr.your_event_mask)); + } + } +} + +void +CXWindowsScreenSaver::disableCallback(void*) +{ + // send fake mouse motion directly to xscreensaver + if (m_xscreensaver != None) { + XEvent event; + event.xmotion.type = MotionNotify; + event.xmotion.display = m_display; + event.xmotion.window = m_xscreensaver; + event.xmotion.root = DefaultRootWindow(m_display); + event.xmotion.subwindow = None; + event.xmotion.time = CurrentTime; + event.xmotion.x = 0; + event.xmotion.y = 0; + event.xmotion.x_root = 0; + event.xmotion.y_root = 0; + event.xmotion.state = 0; + event.xmotion.is_hint = NotifyNormal; + event.xmotion.same_screen = True; + + CXWindowsUtil::CErrorLock lock(m_display); + XSendEvent(m_display, m_xscreensaver, False, 0, &event); + } + + // force screen saver off and reset the timer + XForceScreenSaver(m_display, ScreenSaverReset); +} diff --git a/platform/CXWindowsScreenSaver.h b/platform/CXWindowsScreenSaver.h index 67ac2347..cf5c97e0 100644 --- a/platform/CXWindowsScreenSaver.h +++ b/platform/CXWindowsScreenSaver.h @@ -2,18 +2,22 @@ #define CXWINDOWSSCREENSAVER_H #include "IScreenSaver.h" +#include "stdmap.h" #if defined(X_DISPLAY_MISSING) # error X11 is required to build synergy #else # include #endif +class IJob; +class CXWindowsScreen; + class CXWindowsScreenSaver : public IScreenSaver { public: // note -- the caller must ensure that Display* passed to c'tor isn't // being used in another call to Xlib when calling any method on this // object (including during the c'tor and d'tor) except processEvent(). - CXWindowsScreenSaver(Display*); + CXWindowsScreenSaver(CXWindowsScreen*, Display*); virtual ~CXWindowsScreenSaver(); // process X event. returns true if the event was handled. @@ -38,17 +42,42 @@ private: // send a notification void sendNotify(bool activated); + // find and set the running xscreensaver's window. returns true iff + // found. + bool findXScreenSaver(); + + // set the xscreensaver's window, updating the activation state flag + void setXScreenSaver(Window); + + // returns true if the window appears to be the xscreensaver window + bool isXScreenSaver(Window) const; + // set xscreensaver's activation state flag. sends notification // if the state has changed. - void setXScreenSaver(bool activated); - - // find the running xscreensaver's window - void updateXScreenSaver(); + void setXScreenSaverActive(bool activated); // send a command to xscreensaver void sendXScreenSaverCommand(Atom, long = 0, long = 0); + // watch all windows that could potentially be the xscreensaver for + // the events that will confirm it. + void watchForXScreenSaver(); + + // stop watching all watched windows + void clearWatchForXScreenSaver(); + + // add window to the watch list + void addWatchXScreenSaver(Window window); + + // called periodically to prevent the screen saver from starting + void disableCallback(void*); + private: + typedef std::map CWatchList; + + // the event loop object + CXWindowsScreen* m_screen; + // the X display Display* m_display; @@ -67,6 +96,9 @@ private: // dummy window to receive xscreensaver repsonses Window m_xscreensaverSink; + // potential xscreensaver windows being watched + CWatchList m_watchWindows; + // atoms used to communicate with xscreensaver's window Atom m_atomScreenSaver; Atom m_atomScreenSaverVersion; @@ -78,6 +110,9 @@ private: int m_interval; int m_preferBlanking; int m_allowExposures; + + // the job used to invoke disableCallback + IJob* m_disableJob; }; #endif diff --git a/platform/CXWindowsUtil.cpp b/platform/CXWindowsUtil.cpp index 8279d084..253a3362 100644 --- a/platform/CXWindowsUtil.cpp +++ b/platform/CXWindowsUtil.cpp @@ -13,7 +13,6 @@ CXWindowsUtil::getWindowProperty(Display* display, Window window, int* format, bool deleteProperty) { assert(display != NULL); - assert(data != NULL); Atom actualType; int actualDatumSize; @@ -59,7 +58,13 @@ CXWindowsUtil::getWindowProperty(Display* display, Window window, } // append data - data->append((char*)rawData, numBytes); + if (data != NULL) { + data->append((char*)rawData, numBytes); + } + else { + // data is not required so don't try to get any more + bytesLeft = 0; + } // done with returned data XFree(rawData); @@ -78,7 +83,7 @@ CXWindowsUtil::getWindowProperty(Display* display, Window window, *format = static_cast(actualDatumSize); } - log((CLOG_DEBUG1 "read property %d on window 0x%08x: bytes=%d", property, window, data->size())); + log((CLOG_DEBUG1 "read property %d on window 0x%08x: bytes=%d", property, window, (data == NULL) ? 0 : data->size())); return true; } diff --git a/server/CServer.cpp b/server/CServer.cpp index fdbb808f..d869d8e9 100644 --- a/server/CServer.cpp +++ b/server/CServer.cpp @@ -1284,6 +1284,12 @@ CServer::handshakeClient(void* vsocket) // now connected; client no longer subject to timeout. } + // activate screen saver on new client if active on the + // primary screen + if (m_primary->isScreenSaverActive()) { + protocol->sendScreenSaver(true); + } + // handle messages from client. returns when the client // disconnects. log((CLOG_NOTE "client \"%s\" has connected", name.c_str())); diff --git a/server/CXWindowsPrimaryScreen.cpp b/server/CXWindowsPrimaryScreen.cpp index 1537af18..24d97652 100644 --- a/server/CXWindowsPrimaryScreen.cpp +++ b/server/CXWindowsPrimaryScreen.cpp @@ -545,6 +545,13 @@ CXWindowsPrimaryScreen::isLockedToScreen() const return false; } +bool +CXWindowsPrimaryScreen::isScreenSaverActive() const +{ + CDisplayLock display(this); + return getScreenSaver()->isActive(); +} + void CXWindowsPrimaryScreen::onOpenDisplay(Display* display) { diff --git a/server/CXWindowsPrimaryScreen.h b/server/CXWindowsPrimaryScreen.h index b3f0829f..408c0a2d 100644 --- a/server/CXWindowsPrimaryScreen.h +++ b/server/CXWindowsPrimaryScreen.h @@ -26,6 +26,7 @@ public: virtual void getClipboard(ClipboardID, IClipboard*) const; virtual KeyModifierMask getToggleMask() const; virtual bool isLockedToScreen() const; + virtual bool isScreenSaverActive() const; protected: // CXWindowsScreen overrides diff --git a/server/Makefile.am b/server/Makefile.am index 0d145475..7c7af55f 100644 --- a/server/Makefile.am +++ b/server/Makefile.am @@ -29,6 +29,8 @@ synergyd_LDADD = \ $(DEPTH)/base/libbase.a \ $(X_LIBS) \ $(X_PRE_LIBS) \ + -lXtst \ + -lXext \ -lX11 \ $(X_EXTRA_LIBS) \ $(NULL) diff --git a/synergy/IPrimaryScreen.h b/synergy/IPrimaryScreen.h index 036e4885..94db6bfa 100644 --- a/synergy/IPrimaryScreen.h +++ b/synergy/IPrimaryScreen.h @@ -88,6 +88,9 @@ public: // any other reason that the user should not be allowed to switch // screens. virtual bool isLockedToScreen() const = 0; + + // return true if the screen saver is activated + virtual bool isScreenSaverActive() const = 0; }; #endif