deskflow/src/lib/platform/XWindowsScreenSaver.cpp
Nick Bolton 37bb0f989e
Format all source with Clang and introduce lint workflow (#7364)
* Lint script with CLang format

* Use `-i` for `clang-format` and add more logging for lint scripts

* Apply LLVM CLang formatting

* Fixed comment

* Update ChangeLog

* Fixed bad return code logic

* Improve output for linters

* Fixed re-used var in clang lint

* Fixed include order for tests and use `#if 0` instead of commenting out code

* Use full Clang format dump

* Dump Ubuntu Clang format

* Dump Clang format from Debian

* Dumped from: clang-format version 18.1.8

* Revert "Dumped from: clang-format version 18.1.8"

This reverts commit b9bbda2995bb0b4b65baf14afebb0361ec64e644.

* Remove BasedOnStyle

* Restore base Clang format file

* Use clang-format from pip

* Apply Clang format 18 style

* Add clang_format deps

* Fixed include order issues and *some* accidental includes
2024-07-02 20:07:06 +01:00

546 lines
15 KiB
C++

/*
* synergy -- mouse and keyboard sharing utility
* Copyright (C) 2012-2016 Symless Ltd.
* Copyright (C) 2002 Chris Schoeneman
*
* This package is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* found in the file LICENSE that should have accompanied this file.
*
* This package is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "platform/XWindowsScreenSaver.h"
#include "base/Event.h"
#include "base/IEventQueue.h"
#include "base/Log.h"
#include "base/TMethodEventJob.h"
#include "platform/XWindowsUtil.h"
#include "synergy/IPlatformScreen.h"
#include <X11/Xatom.h>
#if HAVE_X11_EXTENSIONS_XTEST_H
#include <X11/extensions/XTest.h>
#else
#error The XTest extension is required to build synergy
#endif
#if HAVE_X11_EXTENSIONS_DPMS_H
extern "C" {
#include <X11/Xmd.h>
#include <X11/extensions/dpms.h>
#if !HAVE_DPMS_PROTOTYPES
#undef DPMSModeOn
#undef DPMSModeStandby
#undef DPMSModeSuspend
#undef DPMSModeOff
#define DPMSModeOn 0
#define DPMSModeStandby 1
#define DPMSModeSuspend 2
#define DPMSModeOff 3
extern Bool DPMSQueryExtension(Display *, int *, int *);
extern Bool DPMSCapable(Display *);
extern Status DPMSEnable(Display *);
extern Status DPMSDisable(Display *);
extern Status DPMSForceLevel(Display *, CARD16);
extern Status DPMSInfo(Display *, CARD16 *, BOOL *);
#endif
}
#endif
//
// XWindowsScreenSaver
//
XWindowsScreenSaver::XWindowsScreenSaver(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;
{
XWindowsUtil::ErrorLock 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(Event::kTimer, this,
new TMethodEventJob<XWindowsScreenSaver>(
this, &XWindowsScreenSaver::handleDisableTimer));
}
XWindowsScreenSaver::~XWindowsScreenSaver() {
// done with disable job
if (m_disableTimer != NULL) {
m_events->deleteTimer(m_disableTimer);
}
m_events->removeHandler(Event::kTimer, this);
if (m_display != NULL) {
enableDPMS(m_dpmsEnabled);
XSetScreenSaver(m_display, m_timeout, m_interval, m_preferBlanking,
m_allowExposures);
clearWatchForXScreenSaver();
XWindowsUtil::ErrorLock lock(m_display);
XSelectInput(m_display, DefaultRootWindow(m_display), m_rootEventMask);
}
}
void XWindowsScreenSaver::destroy() {
m_display = NULL;
delete this;
}
bool XWindowsScreenSaver::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;
}
void XWindowsScreenSaver::enable() {
// for xscreensaver
m_disabled = false;
updateDisableTimer();
// for built-in X screen saver
XSetScreenSaver(m_display, m_timeout, m_interval, m_preferBlanking,
m_allowExposures);
// for DPMS
enableDPMS(m_dpmsEnabled);
}
void XWindowsScreenSaver::disable() {
// for xscreensaver
m_disabled = true;
updateDisableTimer();
// 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);
// for DPMS
m_dpmsEnabled = isDPMSEnabled();
enableDPMS(false);
// FIXME -- now deactivate?
}
void XWindowsScreenSaver::activate() {
// remove disable job timer
m_suppressDisable = true;
updateDisableTimer();
// enable DPMS if it was enabled
enableDPMS(m_dpmsEnabled);
// try xscreensaver
findXScreenSaver();
if (m_xscreensaver != None) {
sendXScreenSaverCommand(m_atomScreenSaverActivate);
return;
}
// try built-in X screen saver
if (m_timeout != 0) {
XForceScreenSaver(m_display, ScreenSaverActive);
}
// try DPMS
activateDPMS(true);
}
void XWindowsScreenSaver::deactivate() {
// reinstall disable job timer
m_suppressDisable = false;
updateDisableTimer();
// try DPMS
activateDPMS(false);
// disable DPMS if screen saver is disabled
if (m_disabled) {
enableDPMS(false);
}
// try xscreensaver
findXScreenSaver();
if (m_xscreensaver != None) {
sendXScreenSaverCommand(m_atomScreenSaverDeactivate);
return;
}
// use built-in X screen saver
XForceScreenSaver(m_display, ScreenSaverReset);
}
bool XWindowsScreenSaver::isActive() const {
// check xscreensaver
if (m_xscreensaver != None) {
return m_xscreensaverActive;
}
// check DPMS
if (isDPMSActivated()) {
return true;
}
// can't check built-in X screen saver activity
return false;
}
bool XWindowsScreenSaver::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 XWindowsScreenSaver::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;
XWindowAttributes attr;
{
XWindowsUtil::ErrorLock lock(m_display, &error);
XGetWindowAttributes(m_display, m_xscreensaver, &attr);
}
setXScreenSaverActive(!error && attr.map_state != IsUnmapped);
// save current DPMS state; xscreensaver may have changed it.
m_dpmsEnabled = isDPMSEnabled();
} else {
// screen saver can't be active if it doesn't exist
setXScreenSaverActive(false);
// start watching for xscreensaver
watchForXScreenSaver();
}
}
bool XWindowsScreenSaver::isXScreenSaver(Window w) const {
// check for m_atomScreenSaverVersion string property
Atom type;
return (XWindowsUtil::getWindowProperty(m_display, w,
m_atomScreenSaverVersion, NULL, &type,
NULL, False) &&
type == XA_STRING);
}
void XWindowsScreenSaver::setXScreenSaverActive(bool activated) {
if (m_xscreensaverActive != activated) {
LOG((CLOG_DEBUG "xscreensaver %s on window 0x%08x",
activated ? "activated" : "deactivated", m_xscreensaver));
m_xscreensaverActive = activated;
// if screen saver was activated forcefully (i.e. against
// our will) then just accept it. don't try to keep it
// from activating since that'll just pop up the password
// dialog if locking is enabled.
m_suppressDisable = activated;
updateDisableTimer();
if (activated) {
m_events->addEvent(Event(
m_events->forIPrimaryScreen().screensaverActivated(), m_eventTarget));
} else {
m_events->addEvent(
Event(m_events->forIPrimaryScreen().screensaverDeactivated(),
m_eventTarget));
}
}
}
void XWindowsScreenSaver::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;
{
XWindowsUtil::ErrorLock lock(m_display, &error);
XSendEvent(m_display, m_xscreensaver, False, 0, &event);
}
if (error) {
findXScreenSaver();
}
}
void XWindowsScreenSaver::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 XWindowsScreenSaver::clearWatchForXScreenSaver() {
// stop watching all windows
XWindowsUtil::ErrorLock lock(m_display);
for (WatchList::iterator index = m_watchWindows.begin();
index != m_watchWindows.end(); ++index) {
XSelectInput(m_display, index->first, index->second);
}
m_watchWindows.clear();
}
void XWindowsScreenSaver::addWatchXScreenSaver(Window window) {
// get window attributes
bool error = false;
XWindowAttributes attr;
{
XWindowsUtil::ErrorLock lock(m_display, &error);
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) {
error = false;
{
XWindowsUtil::ErrorLock lock(m_display, &error);
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 XWindowsScreenSaver::updateDisableTimer() {
if (m_disabled && !m_suppressDisable && m_disableTimer == NULL) {
// 5 seconds should be plenty often to suppress the screen saver
m_disableTimer = m_events->newTimer(5.0, this);
} else if ((!m_disabled || m_suppressDisable) && m_disableTimer != NULL) {
m_events->deleteTimer(m_disableTimer);
m_disableTimer = NULL;
}
}
void XWindowsScreenSaver::handleDisableTimer(const Event &, 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 = m_disablePos;
event.xmotion.y = 0;
event.xmotion.x_root = m_disablePos;
event.xmotion.y_root = 0;
event.xmotion.state = 0;
event.xmotion.is_hint = NotifyNormal;
event.xmotion.same_screen = True;
XWindowsUtil::ErrorLock lock(m_display);
XSendEvent(m_display, m_xscreensaver, False, 0, &event);
m_disablePos = 20 - m_disablePos;
}
}
void XWindowsScreenSaver::activateDPMS(bool activate) {
#if HAVE_X11_EXTENSIONS_DPMS_H
if (m_dpms) {
// DPMSForceLevel will generate a BadMatch if DPMS is disabled
XWindowsUtil::ErrorLock lock(m_display);
DPMSForceLevel(m_display, activate ? DPMSModeStandby : DPMSModeOn);
}
#endif
}
void XWindowsScreenSaver::enableDPMS(bool enable) {
#if HAVE_X11_EXTENSIONS_DPMS_H
if (m_dpms) {
if (enable) {
DPMSEnable(m_display);
} else {
DPMSDisable(m_display);
}
}
#endif
}
bool XWindowsScreenSaver::isDPMSEnabled() const {
#if HAVE_X11_EXTENSIONS_DPMS_H
if (m_dpms) {
CARD16 level;
BOOL state;
DPMSInfo(m_display, &level, &state);
return (state != False);
} else {
return false;
}
#else
return false;
#endif
}
bool XWindowsScreenSaver::isDPMSActivated() const {
#if HAVE_X11_EXTENSIONS_DPMS_H
if (m_dpms) {
CARD16 level;
BOOL state;
DPMSInfo(m_display, &level, &state);
return (level != DPMSModeOn);
} else {
return false;
}
#else
return false;
#endif
}