bool
CXWindowsScreenSaver::handleXEvent(const 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;
}
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);
}
CXWindowsScreenSaver::CXWindowsScreenSaver(
				Display* display, Window window, void* eventTarget, IEventQueue* events) :
	m_display(display),
	m_xscreensaverSink(window),
	m_eventTarget(eventTarget),
	m_xscreensaver(None),
	m_xscreensaverActive(false),
	m_dpms(false),
	m_disabled(false),
	m_suppressDisable(false),
	m_disableTimer(NULL),
	m_disablePos(0),
	m_events(events)
{
	// 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);

	// check for DPMS extension.  this is an alternative screen saver
	// that powers down the display.
#if HAVE_X11_EXTENSIONS_DPMS_H
	int eventBase, errorBase;
	if (DPMSQueryExtension(m_display, &eventBase, &errorBase)) {
		if (DPMSCapable(m_display)) {
			// we have DPMS
			m_dpms  = true;
		}
	}
#endif

	// 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 built-in settings
	XGetScreenSaver(m_display, &m_timeout, &m_interval,
								&m_preferBlanking, &m_allowExposures);

	// get the DPMS settings
	m_dpmsEnabled = isDPMSEnabled();

	// get the xscreensaver window, if any
	if (!findXScreenSaver()) {
		setXScreenSaver(None);
	}

	// install disable timer event handler
	m_events->adoptHandler(CEvent::kTimer, this,
							new TMethodEventJob<CXWindowsScreenSaver>(this,
								&CXWindowsScreenSaver::handleDisableTimer));
}
CXWindowsScreenSaver::CXWindowsScreenSaver(
				CXWindowsScreen* screen, Display* display) :
	m_screen(screen),
	m_display(display),
	m_notify(None),
	m_xscreensaver(None),
	m_xscreensaverActive(false),
	m_disabled(false),
	m_suppressDisable(false),
	m_disableJobInstalled(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);
	m_atomSynergyScreenSaver    = XInternAtom(m_display,
										"SYNERGY_SCREENSAVER", 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);
}