diff options
author | Simon Rettberg | 2018-10-11 14:57:46 +0200 |
---|---|---|
committer | Simon Rettberg | 2018-10-11 14:57:46 +0200 |
commit | 59d61e258c59dc695783e0b9adc419de2b174e75 (patch) | |
tree | e2acde9f594b69b0c04fa5b8b7568d52ec3a1705 /src | |
download | bwlp-screensaver-59d61e258c59dc695783e0b9adc419de2b174e75.tar.gz bwlp-screensaver-59d61e258c59dc695783e0b9adc419de2b174e75.tar.xz bwlp-screensaver-59d61e258c59dc695783e0b9adc419de2b174e75.zip |
Initial commit
Diffstat (limited to 'src')
-rw-r--r-- | src/main.cpp | 72 | ||||
-rw-r--r-- | src/saverwidget.cpp | 178 | ||||
-rw-r--r-- | src/saverwidget.h | 45 | ||||
-rw-r--r-- | src/screensaver.cpp | 217 | ||||
-rw-r--r-- | src/screensaver.h | 33 | ||||
-rw-r--r-- | src/ui/saver.ui | 96 | ||||
-rwxr-xr-x | src/vroot.h | 156 |
7 files changed, 797 insertions, 0 deletions
diff --git a/src/main.cpp b/src/main.cpp new file mode 100644 index 0000000..2f59a14 --- /dev/null +++ b/src/main.cpp @@ -0,0 +1,72 @@ +#include "saverwidget.h" + +#include <QApplication> +#include <QMessageBox> +#include <QWindow> +#include <QLabel> +#include <QDebug> +#include <QX11Info> + +#include <sys/types.h> +#include <unistd.h> +#include <sys/wait.h> + +//#include <X11/Xlib.h> +#include "vroot.h" + +static pid_t childId; + +static void __attribute((noreturn)) sh(int) +{ + // Clean up if xscreensaver kills us (relay to forked child) + kill(childId, SIGTERM); + exit(0); +} + +int main(int argc, char **argv) +{ + // Fork once and wait for child so SIGSTOP won't affect us and the counter keeps going + // while the auth dialog is up + childId = fork(); + if (childId != 0) { + signal(SIGTERM, sh); + signal(SIGINT, sh); + int status; + for (;;) { + pid_t ret = waitpid(childId, &status, 0); + if (ret == childId) + return 0; + if (errno == EINTR) + continue; + fprintf(stderr, "errno=%d, bye\n", errno); + return 1; + } + } + for (int i = 0; i < argc; ++i) { + qDebug() << "Argument" << i << "=" << argv[i]; + } + QApplication app(argc, argv); + Display *dpy = QX11Info::display(); + if (dpy == nullptr) { + fprintf(stderr, "No display"); + return 1; + } + Window win = DefaultRootWindow(dpy); + QWindow *xwin = QWindow::fromWinId(win); + qDebug() << "Foreign window id is" << win << "pointer is" << xwin; + //QWindow *qwin = new QWindow(xwin); + //QWidget *w = QWidget::createWindowContainer(qwin); + SaverWidget *w = new SaverWidget(win); + w->winId(); + qDebug() << "New Qt window is" << w << "window handle is" << w->windowHandle(); + w->windowHandle()->setParent(xwin); + XWindowAttributes attr; + // Because QWindow is broken and doesn't report any geometry for foreign windows + if (XGetWindowAttributes(dpy, win, &attr)) { + qDebug() << "Query:" << attr.width << attr.height; + w->setGeometry(0, 0, attr.width, attr.height); + } + w->show(); + app.exec(); + return 0; +} diff --git a/src/saverwidget.cpp b/src/saverwidget.cpp new file mode 100644 index 0000000..5a138dc --- /dev/null +++ b/src/saverwidget.cpp @@ -0,0 +1,178 @@ +#include "saverwidget.h" +#include "ui_saver.h" +#include "screensaver.h" + +#include <QTimer> +#include <QX11Info> +#include <QDateTime> +#include <QSettings> +#include <X11/Xlib.h> + +static const QChar C_ZERO('0'); + +static QString formatTime(qlonglong span) +{ + QString ret; + qlonglong days = span / 86400; + if (days > 0) { + ret = QString::number(days) + "d "; + span += 86400 * days; + } + return ret + QString("%1:%2:%3").arg(span / 3600, 2, 10, C_ZERO).arg((span % 3600) / 60, 2, 10, C_ZERO).arg(span % 60, 2, 10, C_ZERO); +} + +SaverWidget::SaverWidget(WId parentWinId, QWidget *parent) : + QWidget(parent), + ui(new Ui::Saver), + _parentWinId(parentWinId), + _lastMouseFake(0), + _logoutDeadline(0), + _shutdownDeadline(0), + _counter(0), + _deadlineType(DeadlineType::Unspecified), + _standbyDisabled(false), + _isLocked(false) +{ + ui->setupUi(this); + QTimer *t = new QTimer(this); + t->setInterval(1000); + connect(t, &QTimer::timeout, [=]() { + if (_counter % 60 == 0) { + this->reloadValues(); + } + if (_counter % 10 == 0) { + _isLocked = ScreenSaver::isLocked(); + } + _counter++; + qlonglong now = QDateTime::currentMSecsSinceEpoch() / 1000; + // Determine what's timing out next (session/schedule) + DeadlineType type; + qlonglong remaining = 0; + if (_shutdownDeadline > 0 && _shutdownDeadline < _logoutDeadline) { + type = DeadlineType::Shutdown; + remaining = _shutdownDeadline - now; + } else if (_logoutDeadline > 0) { + type = (_isLocked ? DeadlineType::IdleKillLocked : DeadlineType::IdleKill); + remaining = _logoutDeadline - now; + } else { + type = DeadlineType::Unspecified; + } + if (type == DeadlineType::Shutdown) { + // Shutdown is about to happen (sooner than idle logout) + if (remaining < 0) { + ui->lblHeader->setText(QString("Dieser Rechner ist seit %1 ausgeschaltet (theoretisch zumindest)").arg(formatTime(-remaining))); + } else { + ui->lblHeader->setText(QString("Achtung: Rechner wird in %1 heruntergefahren!").arg(formatTime(remaining))); + } + } else if (type == DeadlineType::IdleKillLocked) { + // Idle logout is about to happen + if (remaining < 0) { + ui->lblHeader->setText("Diese Sitzung kann durch einen Klick auf 'New Login' beendet werden..."); + } else { + ui->lblHeader->setText(QString("Diese Sitzung wird in %1 beendet, wenn sie nicht entsperrt wird.").arg(formatTime(remaining))); + } + } else if (type == DeadlineType::IdleKill) { + // Idle logout is about to happen, but password is not required to reactivate session + if (remaining < 0) { + ui->lblHeader->setText("Hier hat wohl jemand vergessen sich auszuloggen..."); + } else { + ui->lblHeader->setText(QString("Diese Sitzung wird in %1 beendet, wenn sie nicht weiter verwendet wird.").arg(formatTime(remaining))); + } + } else { + if (_isLocked) { + ui->lblHeader->setText("Rechner gesperrt"); + } else { + ui->lblHeader->setText("Schon schon schon..."); + } + } + ui->lblClock->setText(QDateTime::currentDateTime().toString(QLocale().dateFormat() + " HH:mm ")); + displayText(type); + bool shouldDisable = remaining < 300; + if (_standbyDisabled != shouldDisable) { + _standbyDisabled = shouldDisable; + if (shouldDisable) { + ScreenSaver::allowSaverAndStandby(false); + ScreenSaver::forceScreenOn(); + } else { + ScreenSaver::allowSaverAndStandby(true); + } + } + }); + t->start(); + this->setMouseTracking(true); + reloadValues(); +} + +SaverWidget::~SaverWidget() +{ + delete ui; +} + +void SaverWidget::reloadValues() +{ + QString display = QString::fromLocal8Bit(qgetenv("DISPLAY")); + int i = display.indexOf('.'); + if (i != -1) { + display = display.left(i); + } + QSettings s(QString("/run/idleaction-") + display, QSettings::IniFormat); + _logoutDeadline = s.value("lockDeadline", 0).toLongLong(); + _shutdownDeadline = s.value("shutdownDeadline", 0).toLongLong(); + // TODO: Load text strings for everything + _deadlineType = DeadlineType::Unspecified; +} + +void SaverWidget::displayText(DeadlineType dlt) +{ + if (_deadlineType == dlt) + return; + _deadlineType = dlt; + if (dlt == DeadlineType::IdleKillLocked) { + ui->lblText->setText("<html><body><br>" + "Zum oben angegebenen Zeitpunkt wird die aktuell laufende Sitzung beendet, " + "wenn sie zuvor nicht wieder entsperrt wird.<br>" + "Alle noch laufenden Programme (z.B. Word, Photoshop, Matlab, etc.)<br>" + "werden ohne Nachfrage geschlossen. Stellen Sie daher sicher, bis zum angegebenen Zeitpunkt<br>" + "sämtliche sich in Bearbeitung befindlichen Daten abzuspeichern, bzw. die Sitzung wieder zu entsperren.<br><br>" + "Dies dient dazu zu vermeiden, dass ein Rechner stundenlang gesperrt wird und somit<br>" + "anderen Nutzern nicht zur Verfügung steht.</body></html>"); + } else if (dlt == DeadlineType::IdleKill) { + ui->lblText->setText("<html><body>Keine Nutzeraktivität festgestellt.<br>" + "Zum oben angegebenen Zeitpunkt wird die aktuell laufende Sitzung beendet, " + "wenn der Rechner nicht mehr verwendet wird.<br>" + "Alle noch laufenden Programme (z.B. Word, Photoshop, Matlab, etc.)<br>" + "werden ohne Nachfrage geschlossen. Stellen Sie daher sicher, bis zum angegebenen Zeitpunkt<br>" + "sämtliche sich in Bearbeitung befindlichen Daten abzuspeichern.<br><br>" + "Dies dient dazu zu vermeiden, dass ein Rechner stundenlang gesperrt wird und somit<br>" + "anderen Nutzern nicht zur Verfügung steht.</body></html>"); + } else if (dlt == DeadlineType::Shutdown) { + ui->lblText->setText("<html><body>Achtung: Zum oben angegebenen Zeitpunkt wird der Computer heruntergefahren bzw. neugestartet.<br>" + "Alle noch laufenden Programme (z.B. Word, Photoshop, Matlab, etc.)<br>" + "werden ohne Nachfrage beendet. Stellen Sie daher sicher, bis zum angegebenen Zeitpunkt<br>" + "sämtliche Daten abzuspeichern und die Sitzung zu verlassen.</body></html>"); + } else { + ui->lblText->setText(""); + } +} + +void SaverWidget::mouseMoveEvent(QMouseEvent *) +{ + /* + // Doesn't work, method is never called :-( + qint64 now = QDateTime::currentMSecsSinceEpoch(); + if (now - _lastMouseFake < 1000) + return; + ui->lblText->setText(QString::number(int(now / 1000))); + _lastMouseFake = now; + XEvent ev; + memset(&ev, 0, sizeof(ev)); + ev.type = ButtonPress; + ev.xbutton.button = 1; + ev.xbutton.same_screen = True; + ev.xbutton.root = ev.xbutton.window = ev.xbutton.subwindow = _parentWinId; + XSendEvent(QX11Info::display(), _parentWinId, True, 0xfff, &ev); + ev.type = ButtonRelease; + ev.xbutton.state = 0x100; + XSendEvent(QX11Info::display(), _parentWinId, True, 0xfff, &ev); + */ +} diff --git a/src/saverwidget.h b/src/saverwidget.h new file mode 100644 index 0000000..86eee0c --- /dev/null +++ b/src/saverwidget.h @@ -0,0 +1,45 @@ +#ifndef SAVERWIDGET_H +#define SAVERWIDGET_H + +#include <QWidget> + +namespace Ui { +class Saver; +} + +enum class DeadlineType { + Unspecified, + Shutdown, + IdleKill, + IdleKillLocked, +}; + +class SaverWidget : public QWidget +{ + Q_OBJECT + +public: + explicit SaverWidget(WId parentWinId, QWidget *parent = nullptr); + ~SaverWidget(); + +protected: + void mouseMoveEvent(QMouseEvent *e) override; + +protected slots: + void reloadValues(); + void displayText(DeadlineType dlt); + +private: + Ui::Saver *ui; + WId _parentWinId; + qint64 _lastMouseFake; + // + qlonglong _logoutDeadline; + qlonglong _shutdownDeadline; // Actually used for reboot, standby too... Same result for user (session = dead) + int _counter; + DeadlineType _deadlineType; + bool _standbyDisabled; + bool _isLocked; +}; + +#endif // SAVERWIDGET_H diff --git a/src/screensaver.cpp b/src/screensaver.cpp new file mode 100644 index 0000000..75e6ba5 --- /dev/null +++ b/src/screensaver.cpp @@ -0,0 +1,217 @@ +#include "screensaver.h" + +#include <QX11Info> +#include <QStringList> +#include <QProcess> +#include <X11/Xlib.h> +#include <X11/Xatom.h> + +#ifdef X11_Xscreensaver_FOUND +#include <X11/extensions/scrnsaver.h> +#include <X11/Xutil.h> +#endif +#ifdef X11_dpms_FOUND +#include <X11/extensions/dpms.h> +#endif + +namespace { + +Display *display = nullptr; +bool extensionSupported = false; +bool dpmsSupported = false; +Atom XA_SCREENSAVER_VERSION, XA_SCREENSAVER_STATUS, XA_LOCK; +XErrorHandler old_handler = nullptr; +Bool got_badwindow = False; + +bool init() +{ + if (display != nullptr) { + return true; + } + display = QX11Info::display(); + if (display == nullptr) { + return false; + } +#ifdef X11_Xscreensaver_FOUND + int dummy; + extensionSupported = (XScreenSaverQueryExtension(display, &dummy, &dummy) == True); + if (extensionSupported) { + XA_SCREENSAVER_STATUS = XInternAtom( display, "_SCREENSAVER_STATUS", False ); + XA_SCREENSAVER_VERSION = XInternAtom( display, "_SCREENSAVER_VERSION",False ); + XA_LOCK = XInternAtom( display, "LOCK", False ); + } +#endif +#ifdef X11_dpms_FOUND + dpmsSupported = (DPMSCapable(display) == True); +#endif + return true; +} + +static int BadWindow_ehandler( Display *dpy, XErrorEvent *error ) +{ + if( error->error_code == BadWindow ) { + got_badwindow = True; + return 0; + } else { + if( !old_handler ) { + abort(); + } + return ( *old_handler )( dpy, error ); + } +} + +#ifdef X11_Xscreensaver_FOUND +static Window find_screensaver_window( Display *dpy, char **version ) +{ + unsigned int i; + Window root = RootWindowOfScreen( DefaultScreenOfDisplay( dpy ) ); + Window root2, parent, *kids; + unsigned int nkids; + + if( version ) { + *version = 0; + } + + if( ! XQueryTree( dpy, root, &root2, &parent, &kids, &nkids ) ) { + abort(); + } + if( root != root2 ) { + abort(); + } + if( parent ) { + abort(); + } + if( !( kids && nkids ) ) { + return 0; + } + for( i = 0; i < nkids; i++ ) { + Atom type; + int format; + unsigned long nitems, bytesafter; + unsigned char *v; + int status; + + /* We're walking the list of root-level windows and trying to find + the one that has a particular property on it. We need to trap + BadWindows errors while doing this, because it's possible that + some random window might get deleted in the meantime. (That + window won't have been the one we're looking for.) + */ + XSync( dpy, False ); + if( old_handler ) { + abort(); + } + got_badwindow = False; + old_handler = XSetErrorHandler( BadWindow_ehandler ); + status = XGetWindowProperty( dpy, kids[i], + XA_SCREENSAVER_VERSION, + 0, 200, False, XA_STRING, + &type, &format, &nitems, &bytesafter, + &v ); + XSync( dpy, False ); + XSetErrorHandler( old_handler ); + old_handler = nullptr; + + if( got_badwindow ) { + status = BadWindow; + got_badwindow = False; + } + + if( status == Success && type != None ) { + Window ret = kids[i]; + if( version ) { + *version = ( char* )v; + } + XFree( kids ); + return ret; + } + } + + if( kids ) { + XFree( kids ); + } + return 0; +} +#endif + +} + +/* + * Main attraction + */ + +namespace ScreenSaver { + +void allowSaverAndStandby(bool allow) +{ + if (!init()) + return; +#ifdef X11_Xscreensaver_FOUND + if (extensionSupported) { + XScreenSaverSuspend(display, allow ? False : True); + return; + } +#endif + // TODO: Maybe try some fallback, call xset, or trigger some event periodically +} + +void forceScreenOn() +{ + if (!init()) + return; +#ifdef X11_dpms_FOUND + CARD16 power_level; + BOOL state; + if (DPMSInfo(display, &power_level, &state) && state) { + DPMSForceLevel(display, DPMSModeOn); + return; + } +#endif +} + +bool isLocked() +{ +#ifdef X11_Xscreensaver_FOUND + if (!init()) + return false; + char *v = nullptr; + Window window = find_screensaver_window( display, &v ); + if( !window ) + return false; + if( !v || !*v ) + return false; + XClassHint hint; + memset( &hint, 0, sizeof( hint ) ); + XGetClassHint( display, window, &hint ); + if( !hint.res_class ) + return false; + + Atom type; + int format; + unsigned long nitems, bytesafter; + Atom *data = nullptr; + if( XGetWindowProperty( display, + RootWindow( display, 0 ), + XA_SCREENSAVER_STATUS, + 0, 999, False, XA_INTEGER, + &type, &format, &nitems, &bytesafter, + reinterpret_cast<unsigned char**>(&data) ) + != Success + || !type + || data == nullptr ) + return false; + if( type != XA_INTEGER || nitems < 3 ) { + if( data ) { + free( data ); + } + return false; + } + + Atom blanked = ( Atom ) data[0]; + if( blanked == XA_LOCK ) + return true; // Yay +#endif + return false; +} + +} diff --git a/src/screensaver.h b/src/screensaver.h new file mode 100644 index 0000000..88cc992 --- /dev/null +++ b/src/screensaver.h @@ -0,0 +1,33 @@ +#ifndef SCREENSAVER_H_ +#define SCREENSAVER_H_ + +namespace ScreenSaver { + + /** + * Whether we want to allow the screen to enter + * standby. + * Note: If the screen is already in standby, + * this will not power it on again, or disable + * the screen saver, so to be safe, you might + * want to call forceOn() too. + * @param allow true to allow ss/standby + */ + void allowSaverAndStandby(bool allow); + + /** + * Disable the screen saver (only if password + * locking is disabled!), power screen on if + * it's in standby. + */ + void forceScreenOn(); + + /** + * Whether the screen is locked (requires password). + * false if screen saver is not active, or doesn't + * require password for unlocking. + */ + bool isLocked(); + +} + +#endif /* SCREENSAVER_H_ */ diff --git a/src/ui/saver.ui b/src/ui/saver.ui new file mode 100644 index 0000000..7927e2f --- /dev/null +++ b/src/ui/saver.ui @@ -0,0 +1,96 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ui version="4.0"> + <class>Saver</class> + <widget class="QWidget" name="Saver"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>499</width> + <height>431</height> + </rect> + </property> + <property name="font"> + <font> + <pointsize>10</pointsize> + </font> + </property> + <property name="windowTitle"> + <string>Form</string> + </property> + <property name="styleSheet"> + <string notr="true">#Saver { + background: qlineargradient(spread:pad, x1:0, y1:0, x2:0, y2:1, stop:0 #443, stop:1 #000) +} + +QLabel { +color: #f64; +}</string> + </property> + <layout class="QVBoxLayout" name="verticalLayout_2"> + <item> + <widget class="QLabel" name="lblHeader"> + <property name="font"> + <font> + <pointsize>20</pointsize> + </font> + </property> + <property name="text"> + <string notr="true">¯\_(ツ)_/¯</string> + </property> + <property name="alignment"> + <set>Qt::AlignCenter</set> + </property> + </widget> + </item> + <item> + <widget class="QLabel" name="lblText"> + <property name="font"> + <font> + <pointsize>10</pointsize> + </font> + </property> + <property name="text"> + <string notr="true">.</string> + </property> + <property name="textFormat"> + <enum>Qt::AutoText</enum> + </property> + </widget> + </item> + <item> + <spacer name="verticalSpacer"> + <property name="orientation"> + <enum>Qt::Vertical</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>20</width> + <height>40</height> + </size> + </property> + </spacer> + </item> + <item> + <widget class="QLabel" name="lblClock"> + <property name="font"> + <font> + <pointsize>20</pointsize> + </font> + </property> + <property name="styleSheet"> + <string notr="true">color: #999;</string> + </property> + <property name="text"> + <string notr="true"/> + </property> + <property name="alignment"> + <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set> + </property> + </widget> + </item> + </layout> + </widget> + <resources/> + <connections/> +</ui> diff --git a/src/vroot.h b/src/vroot.h new file mode 100755 index 0000000..ae6326b --- /dev/null +++ b/src/vroot.h @@ -0,0 +1,156 @@ +/* -*- Mode: C; tab-width: 2 -*- */ +/*****************************************************************************/ +/** Copyright 1991 by Andreas Stolcke **/ +/** Copyright 1990 by Solbourne Computer Inc. **/ +/** Longmont, Colorado **/ +/** **/ +/** All Rights Reserved **/ +/** **/ +/** Permission to use, copy, modify, and distribute this software and **/ +/** its documentation for any purpose and without fee is hereby **/ +/** granted, provided that the above copyright notice appear in all **/ +/** copies and that both that copyright notice and this permis- **/ +/** sion notice appear in supporting documentation, and that the **/ +/** name of Solbourne not be used in advertising **/ +/** in publicity pertaining to distribution of the software without **/ +/** specific, written prior permission. **/ +/** **/ +/** ANDREAS STOLCKE AND SOLBOURNE COMPUTER INC. DISCLAIMS ALL WARRANTIES **/ +/** WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF **/ +/** MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL ANDREAS STOLCKE **/ +/** OR SOLBOURNE BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL **/ +/** DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA **/ +/** OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER **/ +/** TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE **/ +/** OR PERFORMANCE OF THIS SOFTWARE. **/ +/*****************************************************************************/ +/* + * vroot.h -- Virtual Root Window handling header file + * + * This header file redefines the X11 macros RootWindow and DefaultRootWindow, + * making them look for a virtual root window as provided by certain `virtual' + * window managers like swm and tvtwm. If none is found, the ordinary root + * window is returned, thus retaining backward compatibility with standard + * window managers. + * The function implementing the virtual root lookup remembers the result of + * its last invocation to avoid overhead in the case of repeated calls + * on the same display and screen arguments. + * The lookup code itself is taken from Tom LaStrange's ssetroot program. + * + * Most simple root window changing X programs can be converted to using + * virtual roots by just including + * + * #include <X11/vroot.h> + * + * after all the X11 header files. It has been tested on such popular + * X clients as xphoon, xfroot, xloadimage, and xaqua. + * It also works with the core clients xprop, xwininfo, xwd, and editres + * (and is necessary to get those clients working under tvtwm). + * It does NOT work with xsetroot; get the xsetroot replacement included in + * the tvtwm distribution instead. + * + * Andreas Stolcke <stolcke@ICSI.Berkeley.EDU>, 9/7/90 + * - replaced all NULL's with properly cast 0's, 5/6/91 + * - free children list (suggested by Mark Martin <mmm@cetia.fr>), 5/16/91 + * - include X11/Xlib.h and support RootWindowOfScreen, too 9/17/91 + * + * Jamie Zawinski <jwz@jwz.org>, 28-Apr-1997 + * - use ANSI C + * + * Jamie Zawinski <jwz@jwz.org>, 3-Sep-2003 + * - if the environment variable "XSCREENSAVER_WINDOW" is set, use that + * as the root window instead of searching for __SWM_VROOT. + * + * Jamie Zawinski <jwz@jwz.org>, 14-Aug-2004 + * - changes to get gcc to stop whining about "type punning". + * + * Jamie Zawinski <jwz@jwz.org>, 16-Dec-2004 + * - fixed that last fix. + */ + +#ifndef _VROOT_H_ +#define _VROOT_H_ +#define _XSCREENSAVER_VROOT_H_ + +#if !defined(lint) && !defined(SABER) +static const char vroot_rcsid[] = + "#Id: vroot.h,v 1.8 2004/12/16 05:33:54 jwz Exp #" "\n" + "#Id: vroot.h,v 1.4 1991/09/30 19:23:16 stolcke Exp stolcke #"; +#endif + +#include <X11/X.h> +#include <X11/Xatom.h> +#include <X11/Xlib.h> + +static Window +#ifdef __STDC__ /* ANSIfication added by jwz, to avoid superfluous warnings. */ +VirtualRootWindowOfScreen(Screen *screen) +#else /* !__STDC__ */ +VirtualRootWindowOfScreen(screen) Screen *screen; +#endif /* !__STDC__ */ +{ + static Screen *save_screen = (Screen *)0; + static Window root = (Window)0; + + if (screen != save_screen) { + Display *dpy = DisplayOfScreen(screen); + Atom __SWM_VROOT = None; + unsigned int i; + Window rootReturn, parentReturn, *children; + unsigned int numChildren; + + /* first check for a hex or decimal window ID in the environment */ + const char *xss_id = getenv("XSCREENSAVER_WINDOW"); + if (xss_id && *xss_id) { + unsigned long id = 0; + char c; + if (1 == sscanf (xss_id, " 0x%lx %c", &id, &c) || + 1 == sscanf (xss_id, " %lu %c", &id, &c)) { + root = (Window) id; + save_screen = screen; + return root; + } + } + + root = RootWindowOfScreen(screen); + + /* go look for a virtual root */ + __SWM_VROOT = XInternAtom(dpy, "__SWM_VROOT", False); + if (XQueryTree(dpy, root, &rootReturn, &parentReturn, + &children, &numChildren)) { + for (i = 0; i < numChildren; i++) { + Atom actual_type; + int actual_format; + unsigned long nitems, bytesafter; + unsigned char *newRoot = 0; + + if (XGetWindowProperty(dpy, children[i], + __SWM_VROOT, 0, 1, False, XA_WINDOW, + &actual_type, &actual_format, + &nitems, &bytesafter, + &newRoot) == Success + && newRoot) { + root = *((Window *) newRoot); + break; + } + } + if (children) + XFree((char *)children); + } + + save_screen = screen; + } + + return root; +} + +#undef RootWindowOfScreen +#define RootWindowOfScreen(s) VirtualRootWindowOfScreen(s) + +#undef RootWindow +#define RootWindow(dpy,screen) VirtualRootWindowOfScreen(ScreenOfDisplay(dpy,screen)) + +#undef DefaultRootWindow +#define DefaultRootWindow(dpy) VirtualRootWindowOfScreen(DefaultScreenOfDisplay(dpy)) + +#endif /* _VROOT_H_ */ |