00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019 #include "CXWindowsScreenSaver.h"
00020 #include "CXWindowsUtil.h"
00021 #include "IPlatformScreen.h"
00022 #include "CLog.h"
00023 #include "CEvent.h"
00024 #include "IEventQueue.h"
00025 #include "TMethodEventJob.h"
00026 #include <X11/Xatom.h>
00027 #if HAVE_X11_EXTENSIONS_XTEST_H
00028 # include <X11/extensions/XTest.h>
00029 #else
00030 # error The XTest extension is required to build synergy
00031 #endif
00032 #if HAVE_X11_EXTENSIONS_DPMS_H
00033 extern "C" {
00034 # include <X11/Xmd.h>
00035 # include <X11/extensions/dpms.h>
00036 # if !HAVE_DPMS_PROTOTYPES
00037 # undef DPMSModeOn
00038 # undef DPMSModeStandby
00039 # undef DPMSModeSuspend
00040 # undef DPMSModeOff
00041 # define DPMSModeOn 0
00042 # define DPMSModeStandby 1
00043 # define DPMSModeSuspend 2
00044 # define DPMSModeOff 3
00045 extern Bool DPMSQueryExtension(Display *, int *, int *);
00046 extern Bool DPMSCapable(Display *);
00047 extern Status DPMSEnable(Display *);
00048 extern Status DPMSDisable(Display *);
00049 extern Status DPMSForceLevel(Display *, CARD16);
00050 extern Status DPMSInfo(Display *, CARD16 *, BOOL *);
00051 # endif
00052 }
00053 #endif
00054
00055
00056
00057
00058
00059 CXWindowsScreenSaver::CXWindowsScreenSaver(
00060 Display* display, Window window, void* eventTarget, IEventQueue& eventQueue) :
00061 m_display(display),
00062 m_xscreensaverSink(window),
00063 m_eventTarget(eventTarget),
00064 m_xscreensaver(None),
00065 m_xscreensaverActive(false),
00066 m_dpms(false),
00067 m_disabled(false),
00068 m_suppressDisable(false),
00069 m_disableTimer(NULL),
00070 m_disablePos(0),
00071 m_eventQueue(eventQueue)
00072 {
00073
00074 m_atomScreenSaver = XInternAtom(m_display,
00075 "SCREENSAVER", False);
00076 m_atomScreenSaverVersion = XInternAtom(m_display,
00077 "_SCREENSAVER_VERSION", False);
00078 m_atomScreenSaverActivate = XInternAtom(m_display,
00079 "ACTIVATE", False);
00080 m_atomScreenSaverDeactivate = XInternAtom(m_display,
00081 "DEACTIVATE", False);
00082
00083
00084
00085 #if HAVE_X11_EXTENSIONS_DPMS_H
00086 int eventBase, errorBase;
00087 if (DPMSQueryExtension(m_display, &eventBase, &errorBase)) {
00088 if (DPMSCapable(m_display)) {
00089
00090 m_dpms = true;
00091 }
00092 }
00093 #endif
00094
00095
00096 bool error = false;
00097 {
00098 CXWindowsUtil::CErrorLock lock(m_display, &error);
00099 Window root = DefaultRootWindow(m_display);
00100 XWindowAttributes attr;
00101 XGetWindowAttributes(m_display, root, &attr);
00102 m_rootEventMask = attr.your_event_mask;
00103 XSelectInput(m_display, root, m_rootEventMask | SubstructureNotifyMask);
00104 }
00105 if (error) {
00106 LOG((CLOG_DEBUG "didn't set root event mask"));
00107 m_rootEventMask = 0;
00108 }
00109
00110
00111 XGetScreenSaver(m_display, &m_timeout, &m_interval,
00112 &m_preferBlanking, &m_allowExposures);
00113
00114
00115 m_dpmsEnabled = isDPMSEnabled();
00116
00117
00118 if (!findXScreenSaver()) {
00119 setXScreenSaver(None);
00120 }
00121
00122
00123 m_eventQueue.adoptHandler(CEvent::kTimer, this,
00124 new TMethodEventJob<CXWindowsScreenSaver>(this,
00125 &CXWindowsScreenSaver::handleDisableTimer));
00126 }
00127
00128 CXWindowsScreenSaver::~CXWindowsScreenSaver()
00129 {
00130
00131 if (m_disableTimer != NULL) {
00132 m_eventQueue.deleteTimer(m_disableTimer);
00133 }
00134 m_eventQueue.removeHandler(CEvent::kTimer, this);
00135
00136 if (m_display != NULL) {
00137 enableDPMS(m_dpmsEnabled);
00138 XSetScreenSaver(m_display, m_timeout, m_interval,
00139 m_preferBlanking, m_allowExposures);
00140 clearWatchForXScreenSaver();
00141 CXWindowsUtil::CErrorLock lock(m_display);
00142 XSelectInput(m_display, DefaultRootWindow(m_display), m_rootEventMask);
00143 }
00144 }
00145
00146 void
00147 CXWindowsScreenSaver::destroy()
00148 {
00149 m_display = NULL;
00150 delete this;
00151 }
00152
00153 bool
00154 CXWindowsScreenSaver::handleXEvent(const XEvent* xevent)
00155 {
00156 switch (xevent->type) {
00157 case CreateNotify:
00158 if (m_xscreensaver == None) {
00159 if (isXScreenSaver(xevent->xcreatewindow.window)) {
00160
00161 setXScreenSaver(xevent->xcreatewindow.window);
00162 }
00163 else {
00164
00165
00166
00167
00168
00169
00170
00171 addWatchXScreenSaver(xevent->xcreatewindow.window);
00172 }
00173 }
00174 break;
00175
00176 case DestroyNotify:
00177 if (xevent->xdestroywindow.window == m_xscreensaver) {
00178
00179 LOG((CLOG_DEBUG "xscreensaver died"));
00180 setXScreenSaver(None);
00181 return true;
00182 }
00183 break;
00184
00185 case PropertyNotify:
00186 if (xevent->xproperty.state == PropertyNewValue) {
00187 if (isXScreenSaver(xevent->xproperty.window)) {
00188
00189 setXScreenSaver(xevent->xcreatewindow.window);
00190 }
00191 }
00192 break;
00193
00194 case MapNotify:
00195 if (xevent->xmap.window == m_xscreensaver) {
00196
00197 setXScreenSaverActive(true);
00198 return true;
00199 }
00200 break;
00201
00202 case UnmapNotify:
00203 if (xevent->xunmap.window == m_xscreensaver) {
00204
00205 setXScreenSaverActive(false);
00206 return true;
00207 }
00208 break;
00209 }
00210
00211 return false;
00212 }
00213
00214 void
00215 CXWindowsScreenSaver::enable()
00216 {
00217
00218 m_disabled = false;
00219 updateDisableTimer();
00220
00221
00222 XSetScreenSaver(m_display, m_timeout, m_interval,
00223 m_preferBlanking, m_allowExposures);
00224
00225
00226 enableDPMS(m_dpmsEnabled);
00227 }
00228
00229 void
00230 CXWindowsScreenSaver::disable()
00231 {
00232
00233 m_disabled = true;
00234 updateDisableTimer();
00235
00236
00237 XGetScreenSaver(m_display, &m_timeout, &m_interval,
00238 &m_preferBlanking, &m_allowExposures);
00239 XSetScreenSaver(m_display, 0, m_interval,
00240 m_preferBlanking, m_allowExposures);
00241
00242
00243 m_dpmsEnabled = isDPMSEnabled();
00244 enableDPMS(false);
00245
00246
00247 }
00248
00249 void
00250 CXWindowsScreenSaver::activate()
00251 {
00252
00253 m_suppressDisable = true;
00254 updateDisableTimer();
00255
00256
00257 enableDPMS(m_dpmsEnabled);
00258
00259
00260 findXScreenSaver();
00261 if (m_xscreensaver != None) {
00262 sendXScreenSaverCommand(m_atomScreenSaverActivate);
00263 return;
00264 }
00265
00266
00267 if (m_timeout != 0) {
00268 XForceScreenSaver(m_display, ScreenSaverActive);
00269 }
00270
00271
00272 activateDPMS(true);
00273 }
00274
00275 void
00276 CXWindowsScreenSaver::deactivate()
00277 {
00278
00279 m_suppressDisable = false;
00280 updateDisableTimer();
00281
00282
00283 activateDPMS(false);
00284
00285
00286 if (m_disabled) {
00287 enableDPMS(false);
00288 }
00289
00290
00291 findXScreenSaver();
00292 if (m_xscreensaver != None) {
00293 sendXScreenSaverCommand(m_atomScreenSaverDeactivate);
00294 return;
00295 }
00296
00297
00298 XForceScreenSaver(m_display, ScreenSaverReset);
00299 }
00300
00301 bool
00302 CXWindowsScreenSaver::isActive() const
00303 {
00304
00305 if (m_xscreensaver != None) {
00306 return m_xscreensaverActive;
00307 }
00308
00309
00310 if (isDPMSActivated()) {
00311 return true;
00312 }
00313
00314
00315 return false;
00316 }
00317
00318 bool
00319 CXWindowsScreenSaver::findXScreenSaver()
00320 {
00321
00322 if (m_xscreensaver == None) {
00323
00324 Window root = DefaultRootWindow(m_display);
00325 Window rw, pw, *cw;
00326 unsigned int nc;
00327 if (XQueryTree(m_display, root, &rw, &pw, &cw, &nc)) {
00328 for (unsigned int i = 0; i < nc; ++i) {
00329 if (isXScreenSaver(cw[i])) {
00330 setXScreenSaver(cw[i]);
00331 break;
00332 }
00333 }
00334 XFree(cw);
00335 }
00336 }
00337
00338 return (m_xscreensaver != None);
00339 }
00340
00341 void
00342 CXWindowsScreenSaver::setXScreenSaver(Window window)
00343 {
00344 LOG((CLOG_DEBUG "xscreensaver window: 0x%08x", window));
00345
00346
00347 m_xscreensaver = window;
00348
00349 if (m_xscreensaver != None) {
00350
00351 clearWatchForXScreenSaver();
00352
00353
00354 bool error = false;
00355 XWindowAttributes attr;
00356 {
00357 CXWindowsUtil::CErrorLock lock(m_display, &error);
00358 XGetWindowAttributes(m_display, m_xscreensaver, &attr);
00359 }
00360 setXScreenSaverActive(!error && attr.map_state != IsUnmapped);
00361
00362
00363 m_dpmsEnabled = isDPMSEnabled();
00364 }
00365 else {
00366
00367 setXScreenSaverActive(false);
00368
00369
00370 watchForXScreenSaver();
00371 }
00372 }
00373
00374 bool
00375 CXWindowsScreenSaver::isXScreenSaver(Window w) const
00376 {
00377
00378 Atom type;
00379 return (CXWindowsUtil::getWindowProperty(m_display, w,
00380 m_atomScreenSaverVersion,
00381 NULL, &type, NULL, False) &&
00382 type == XA_STRING);
00383 }
00384
00385 void
00386 CXWindowsScreenSaver::setXScreenSaverActive(bool activated)
00387 {
00388 if (m_xscreensaverActive != activated) {
00389 LOG((CLOG_DEBUG "xscreensaver %s on window 0x%08x", activated ? "activated" : "deactivated", m_xscreensaver));
00390 m_xscreensaverActive = activated;
00391
00392
00393
00394
00395
00396 m_suppressDisable = activated;
00397 updateDisableTimer();
00398
00399 if (activated) {
00400 m_eventQueue.addEvent(CEvent(
00401 IPlatformScreen::getScreensaverActivatedEvent(),
00402 m_eventTarget));
00403 }
00404 else {
00405 m_eventQueue.addEvent(CEvent(
00406 IPlatformScreen::getScreensaverDeactivatedEvent(),
00407 m_eventTarget));
00408 }
00409 }
00410 }
00411
00412 void
00413 CXWindowsScreenSaver::sendXScreenSaverCommand(Atom cmd, long arg1, long arg2)
00414 {
00415 XEvent event;
00416 event.xclient.type = ClientMessage;
00417 event.xclient.display = m_display;
00418 event.xclient.window = m_xscreensaverSink;
00419 event.xclient.message_type = m_atomScreenSaver;
00420 event.xclient.format = 32;
00421 event.xclient.data.l[0] = static_cast<long>(cmd);
00422 event.xclient.data.l[1] = arg1;
00423 event.xclient.data.l[2] = arg2;
00424 event.xclient.data.l[3] = 0;
00425 event.xclient.data.l[4] = 0;
00426
00427 LOG((CLOG_DEBUG "send xscreensaver command: %d %d %d", (long)cmd, arg1, arg2));
00428 bool error = false;
00429 {
00430 CXWindowsUtil::CErrorLock lock(m_display, &error);
00431 XSendEvent(m_display, m_xscreensaver, False, 0, &event);
00432 }
00433 if (error) {
00434 findXScreenSaver();
00435 }
00436 }
00437
00438 void
00439 CXWindowsScreenSaver::watchForXScreenSaver()
00440 {
00441
00442 clearWatchForXScreenSaver();
00443
00444
00445 Window root = DefaultRootWindow(m_display);
00446 Window rw, pw, *cw;
00447 unsigned int nc;
00448 if (XQueryTree(m_display, root, &rw, &pw, &cw, &nc)) {
00449 for (unsigned int i = 0; i < nc; ++i) {
00450 addWatchXScreenSaver(cw[i]);
00451 }
00452 XFree(cw);
00453 }
00454
00455
00456
00457 if (findXScreenSaver()) {
00458
00459 clearWatchForXScreenSaver();
00460 }
00461 }
00462
00463 void
00464 CXWindowsScreenSaver::clearWatchForXScreenSaver()
00465 {
00466
00467 CXWindowsUtil::CErrorLock lock(m_display);
00468 for (CWatchList::iterator index = m_watchWindows.begin();
00469 index != m_watchWindows.end(); ++index) {
00470 XSelectInput(m_display, index->first, index->second);
00471 }
00472 m_watchWindows.clear();
00473 }
00474
00475 void
00476 CXWindowsScreenSaver::addWatchXScreenSaver(Window window)
00477 {
00478
00479 bool error = false;
00480 XWindowAttributes attr;
00481 {
00482 CXWindowsUtil::CErrorLock lock(m_display, &error);
00483 XGetWindowAttributes(m_display, window, &attr);
00484 }
00485
00486
00487
00488 if (!error && attr.override_redirect == True) {
00489 error = false;
00490 {
00491 CXWindowsUtil::CErrorLock lock(m_display, &error);
00492 XSelectInput(m_display, window,
00493 attr.your_event_mask | PropertyChangeMask);
00494 }
00495 if (!error) {
00496
00497 m_watchWindows.insert(std::make_pair(window, attr.your_event_mask));
00498 }
00499 }
00500 }
00501
00502 void
00503 CXWindowsScreenSaver::updateDisableTimer()
00504 {
00505 if (m_disabled && !m_suppressDisable && m_disableTimer == NULL) {
00506
00507 m_disableTimer = m_eventQueue.newTimer(5.0, this);
00508 }
00509 else if ((!m_disabled || m_suppressDisable) && m_disableTimer != NULL) {
00510 m_eventQueue.deleteTimer(m_disableTimer);
00511 m_disableTimer = NULL;
00512 }
00513 }
00514
00515 void
00516 CXWindowsScreenSaver::handleDisableTimer(const CEvent&, void*)
00517 {
00518
00519 if (m_xscreensaver != None) {
00520 XEvent event;
00521 event.xmotion.type = MotionNotify;
00522 event.xmotion.display = m_display;
00523 event.xmotion.window = m_xscreensaver;
00524 event.xmotion.root = DefaultRootWindow(m_display);
00525 event.xmotion.subwindow = None;
00526 event.xmotion.time = CurrentTime;
00527 event.xmotion.x = m_disablePos;
00528 event.xmotion.y = 0;
00529 event.xmotion.x_root = m_disablePos;
00530 event.xmotion.y_root = 0;
00531 event.xmotion.state = 0;
00532 event.xmotion.is_hint = NotifyNormal;
00533 event.xmotion.same_screen = True;
00534
00535 CXWindowsUtil::CErrorLock lock(m_display);
00536 XSendEvent(m_display, m_xscreensaver, False, 0, &event);
00537
00538 m_disablePos = 20 - m_disablePos;
00539 }
00540 }
00541
00542 void
00543 CXWindowsScreenSaver::activateDPMS(bool activate)
00544 {
00545 #if HAVE_X11_EXTENSIONS_DPMS_H
00546 if (m_dpms) {
00547
00548 CXWindowsUtil::CErrorLock lock(m_display);
00549 DPMSForceLevel(m_display, activate ? DPMSModeStandby : DPMSModeOn);
00550 }
00551 #endif
00552 }
00553
00554 void
00555 CXWindowsScreenSaver::enableDPMS(bool enable)
00556 {
00557 #if HAVE_X11_EXTENSIONS_DPMS_H
00558 if (m_dpms) {
00559 if (enable) {
00560 DPMSEnable(m_display);
00561 }
00562 else {
00563 DPMSDisable(m_display);
00564 }
00565 }
00566 #endif
00567 }
00568
00569 bool
00570 CXWindowsScreenSaver::isDPMSEnabled() const
00571 {
00572 #if HAVE_X11_EXTENSIONS_DPMS_H
00573 if (m_dpms) {
00574 CARD16 level;
00575 BOOL state;
00576 DPMSInfo(m_display, &level, &state);
00577 return (state != False);
00578 }
00579 else {
00580 return false;
00581 }
00582 #else
00583 return false;
00584 #endif
00585 }
00586
00587 bool
00588 CXWindowsScreenSaver::isDPMSActivated() const
00589 {
00590 #if HAVE_X11_EXTENSIONS_DPMS_H
00591 if (m_dpms) {
00592 CARD16 level;
00593 BOOL state;
00594 DPMSInfo(m_display, &level, &state);
00595 return (level != DPMSModeOn);
00596 }
00597 else {
00598 return false;
00599 }
00600 #else
00601 return false;
00602 #endif
00603 }