barrier/platform/CXWindowsScreenSaver.cpp
crs 7100e07d2b now disabling disable job timer when forcing screen saver
activation.  previously the timer would deactivate the screen
saver shortly after activation.  job timer is restored when
the screen saver is deactivated.
2002-06-23 21:48:33 +00:00

447 lines
11 KiB
C++

#include "CXWindowsScreenSaver.h"
#include "CXWindowsScreen.h"
#include "CXWindowsUtil.h"
#include "CLog.h"
#include "TMethodJob.h"
#include <X11/Xatom.h>
#if defined(HAVE_X11_EXTENSIONS_XTEST_H)
# include <X11/extensions/XTest.h>
#else
# error The XTest extension is required to build synergy
#endif
//
// CXWindowsScreenSaver
//
CXWindowsScreenSaver::CXWindowsScreenSaver(
CXWindowsScreen* screen, Display* display) :
m_screen(screen),
m_display(display),
m_notify(None),
m_xscreensaver(None),
m_xscreensaverActive(false),
m_disabled(false)
{
// screen saver disable callback
m_disableJob = new TMethodJob<CXWindowsScreenSaver>(this,
&CXWindowsScreenSaver::disableCallback);
// get atoms
m_atomScreenSaver = XInternAtom(m_display,
"SCREENSAVER", False);
m_atomScreenSaverVersion = XInternAtom(m_display,
"_SCREENSAVER_VERSION", False);
m_atomScreenSaverActivate = XInternAtom(m_display,
"ACTIVATE", False);
m_atomScreenSaverDeactivate = XInternAtom(m_display,
"DEACTIVATE", False);
// 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;
attr.override_redirect = True;
m_xscreensaverSink = XCreateWindow(m_display,
DefaultRootWindow(m_display),
0, 0, 1, 1, 0, 0,
InputOnly, CopyFromParent,
CWDontPropagate | CWEventMask |
CWOverrideRedirect,
&attr);
log((CLOG_DEBUG "xscreensaver sink window is 0x%08x", m_xscreensaverSink));
// watch top-level windows for changes
{
bool error = false;
CXWindowsUtil::CErrorLock lock(m_display, &error);
Window root = DefaultRootWindow(m_display);
XWindowAttributes attr;
XGetWindowAttributes(m_display, root, &attr);
m_rootEventMask = attr.your_event_mask;
XSelectInput(m_display, root, m_rootEventMask | SubstructureNotifyMask);
if (error) {
log((CLOG_DEBUG "didn't set root event mask"));
m_rootEventMask = 0;
}
}
// get the xscreensaver window, if any
if (!findXScreenSaver()) {
setXScreenSaver(None);
}
// get the built-in settings
XGetScreenSaver(m_display, &m_timeout, &m_interval,
&m_preferBlanking, &m_allowExposures);
}
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
log((CLOG_DEBUG "xscreensaver died"));
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
setXScreenSaverActive(true);
return true;
}
break;
case UnmapNotify:
if (xevent->xunmap.window == m_xscreensaver) {
// xscreensaver has deactivated
setXScreenSaverActive(false);
return true;
}
break;
}
return false;
}
void
CXWindowsScreenSaver::setNotify(Window notify)
{
m_notify = notify;
}
void
CXWindowsScreenSaver::enable()
{
// for xscreensaver
m_disabled = false;
m_screen->removeTimer(m_disableJob);
// for built-in X screen saver
XSetScreenSaver(m_display, m_timeout, m_interval,
m_preferBlanking, m_allowExposures);
}
void
CXWindowsScreenSaver::disable()
{
// for xscreensaver. 5 seconds should be plenty often to
// suppress the screen saver.
m_disabled = true;
m_screen->addTimer(m_disableJob, 5.0);
// use built-in X screen saver
XGetScreenSaver(m_display, &m_timeout, &m_interval,
&m_preferBlanking, &m_allowExposures);
XSetScreenSaver(m_display, 0, m_interval,
m_preferBlanking, m_allowExposures);
// FIXME -- now deactivate?
}
void
CXWindowsScreenSaver::activate()
{
// remove disable job timer
if (m_disabled) {
m_screen->removeTimer(m_disableJob);
}
// try xscreensaver
findXScreenSaver();
if (m_xscreensaver != None) {
sendXScreenSaverCommand(m_atomScreenSaverActivate);
return;
}
// use built-in X screen saver
XForceScreenSaver(m_display, ScreenSaverActive);
}
void
CXWindowsScreenSaver::deactivate()
{
// reinstall disable job timer
if (m_disabled) {
m_screen->addTimer(m_disableJob, 5.0);
}
// try xscreensaver
findXScreenSaver();
if (m_xscreensaver != None) {
sendXScreenSaverCommand(m_atomScreenSaverDeactivate);
return;
}
// use built-in X screen saver
XForceScreenSaver(m_display, ScreenSaverReset);
}
bool
CXWindowsScreenSaver::isActive() const
{
// check xscreensaver
if (m_xscreensaver != None) {
return m_xscreensaverActive;
}
// can't check built-in X screen saver activity
return false;
}
void
CXWindowsScreenSaver::sendNotify(bool activated)
{
if (m_notify != None) {
XEvent event;
event.xclient.type = ClientMessage;
event.xclient.display = m_display;
event.xclient.window = m_notify;
event.xclient.message_type = m_atomScreenSaver;
event.xclient.format = 32;
event.xclient.data.l[0] = activated ? 1 : 0;
event.xclient.data.l[1] = 0;
event.xclient.data.l[2] = 0;
event.xclient.data.l[3] = 0;
event.xclient.data.l[4] = 0;
CXWindowsUtil::CErrorLock lock(m_display);
XSendEvent(m_display, m_notify, False, 0, &event);
}
}
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(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"));
m_xscreensaverActive = activated;
sendNotify(activated);
}
}
void
CXWindowsScreenSaver::sendXScreenSaverCommand(Atom cmd, long arg1, long arg2)
{
XEvent event;
event.xclient.type = ClientMessage;
event.xclient.display = m_display;
event.xclient.window = m_xscreensaverSink;
event.xclient.message_type = m_atomScreenSaver;
event.xclient.format = 32;
event.xclient.data.l[0] = static_cast<long>(cmd);
event.xclient.data.l[1] = arg1;
event.xclient.data.l[2] = arg2;
event.xclient.data.l[3] = 0;
event.xclient.data.l[4] = 0;
log((CLOG_DEBUG "send xscreensaver command: %d %d %d", (long)cmd, arg1, arg2));
bool error = false;
CXWindowsUtil::CErrorLock lock(m_display, &error);
XSendEvent(m_display, m_xscreensaver, False, 0, &event);
if (error) {
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);
}