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