From 59d61e258c59dc695783e0b9adc419de2b174e75 Mon Sep 17 00:00:00 2001 From: Simon Rettberg Date: Thu, 11 Oct 2018 14:57:46 +0200 Subject: Initial commit --- .gitignore | 4 + CMakeLists.txt | 89 +++++++++++++++++++++ src/main.cpp | 72 +++++++++++++++++ src/saverwidget.cpp | 178 ++++++++++++++++++++++++++++++++++++++++++ src/saverwidget.h | 45 +++++++++++ src/screensaver.cpp | 217 ++++++++++++++++++++++++++++++++++++++++++++++++++++ src/screensaver.h | 33 ++++++++ src/ui/saver.ui | 96 +++++++++++++++++++++++ src/vroot.h | 156 +++++++++++++++++++++++++++++++++++++ 9 files changed, 890 insertions(+) create mode 100644 .gitignore create mode 100644 CMakeLists.txt create mode 100644 src/main.cpp create mode 100644 src/saverwidget.cpp create mode 100644 src/saverwidget.h create mode 100644 src/screensaver.cpp create mode 100644 src/screensaver.h create mode 100644 src/ui/saver.ui create mode 100755 src/vroot.h diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..bf7f0cd --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +/.project +/.cproject +/build +/CMakeLists.txt.user diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..3079871 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,89 @@ +cmake_minimum_required(VERSION 2.8.12 FATAL_ERROR) + +# cmake -G"Eclipse CDT4 - Unix Makefiles" -D CMAKE_BUILD_TYPE=Debug .. + +# project name +project(bwlp-screensaver) + +set(CMAKE_BUILD_TYPE Debug) +set(CMAKE_CXX_FLAGS_DEBUG "-O0 -g -Wall -Wextra -pedantic -Werror -Wno-multichar") +set(CMAKE_CXX_FLAGS_RELEASE "-O2 -Wno-multichar") + +set(CMAKE_CXX_STANDARD 11) +# Some cmake versions can't understand the CMAKE_CXX_STANDARD option above? +SET( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11" ) + +set(CMAKE_INCLUDE_CURRENT_DIR ON) +set(CMAKE_AUTOMOC ON) + +FIND_PACKAGE(X11 REQUIRED) + +file(GLOB_RECURSE BWLPSAVER_SOURCES src/*.cpp) +file(GLOB_RECURSE BWLPSAVER_UIS src/ui/*.ui) +file(GLOB_RECURSE BWLPSAVER_RESOURCES src/*.qrc) +file(GLOB_RECURSE BWLPSAVER_TSS src/i18n/*.ts) + +include_directories( + ${CMAKE_CURRENT_SOURCE_DIR}/src + ${CMAKE_CURRENT_BINARY_DIR} +) + +# +# Qt5 +# +find_package(Qt5 COMPONENTS Widgets Network X11Extras REQUIRED) + +IF(X11_Xscreensaver_FOUND) + ADD_DEFINITIONS(-DX11_Xscreensaver_FOUND) +ENDIF() +IF(X11_dpms_FOUND) + ADD_DEFINITIONS(-DX11_dpms_FOUND) +ENDIF() + + +QT5_WRAP_UI(BWLPSAVER_UI_HEADERS ${BWLPSAVER_UIS}) + +option(UPDATE_TRANSLATIONS "Update .ts files (WARNING: make clean will delete the .ts files!)") +if(BWLPSAVER_TSS) + if (UPDATE_TRANSLATIONS) + set (FILES_TO_TRANSLATE ${FILES_TO_TRANSLATE} ${BWLPSAVER_SOURCES} ${BWLPSAVER_UIS}) + QT5_CREATE_TRANSLATION(BWLPSAVER_QMS ${FILES_TO_TRANSLATE} ${BWLPSAVER_TSS} OPTIONS -noobsolete) + message(STATUS ".tr files have been updated") + else (UPDATE_TRANSLATIONS) + QT5_ADD_TRANSLATION(BWLPSAVER_QMS ${BWLPSAVER_TSS}) + endif (UPDATE_TRANSLATIONS) + + # write a resource file for qm files + set(resource_file_content "\n \n") + foreach(file ${BWLPSAVER_QMS}) + get_filename_component(filename ${file} NAME) + set(resource_file_content "${resource_file_content} ${filename}\n") + endforeach(file) + set(resource_file_content "${resource_file_content} \n\n") + file(WRITE "${CMAKE_BINARY_DIR}/translation.qrc" "${resource_file_content}") + set(BWLPSAVER_RESOURCES ${BWLPSAVER_RESOURCES} "${CMAKE_BINARY_DIR}/translation.qrc") + + #add_custom_target(translations_target DEPENDS ${BWLPSAVER_QMS}) +endif(BWLPSAVER_TSS) + +QT5_ADD_RESOURCES(BWLPSAVER_RC_SOURCES ${BWLPSAVER_RESOURCES}) + +# +# build bwlp-screensaver +# +add_executable(bwlp-screensaver + ${BWLPSAVER_SOURCES} + ${BWLPSAVER_UI_HEADERS} + ${BWLPSAVER_RC_SOURCES} + ${BWLPSAVER_QMS} +) + +target_link_libraries(bwlp-screensaver + Qt5::Widgets + Qt5::Network + Qt5::X11Extras + ${X11_LIBRARIES} + ${X11_Xscreensaver_LIB} +) + +install(TARGETS bwlp-screensaver RUNTIME DESTINATION bin) 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 +#include +#include +#include +#include +#include + +#include +#include +#include + +//#include +#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 +#include +#include +#include +#include + +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("
" + "Zum oben angegebenen Zeitpunkt wird die aktuell laufende Sitzung beendet, " + "wenn sie zuvor nicht wieder entsperrt wird.
" + "Alle noch laufenden Programme (z.B. Word, Photoshop, Matlab, etc.)
" + "werden ohne Nachfrage geschlossen. Stellen Sie daher sicher, bis zum angegebenen Zeitpunkt
" + "sämtliche sich in Bearbeitung befindlichen Daten abzuspeichern, bzw. die Sitzung wieder zu entsperren.

" + "Dies dient dazu zu vermeiden, dass ein Rechner stundenlang gesperrt wird und somit
" + "anderen Nutzern nicht zur Verfügung steht."); + } else if (dlt == DeadlineType::IdleKill) { + ui->lblText->setText("Keine Nutzeraktivität festgestellt.
" + "Zum oben angegebenen Zeitpunkt wird die aktuell laufende Sitzung beendet, " + "wenn der Rechner nicht mehr verwendet wird.
" + "Alle noch laufenden Programme (z.B. Word, Photoshop, Matlab, etc.)
" + "werden ohne Nachfrage geschlossen. Stellen Sie daher sicher, bis zum angegebenen Zeitpunkt
" + "sämtliche sich in Bearbeitung befindlichen Daten abzuspeichern.

" + "Dies dient dazu zu vermeiden, dass ein Rechner stundenlang gesperrt wird und somit
" + "anderen Nutzern nicht zur Verfügung steht."); + } else if (dlt == DeadlineType::Shutdown) { + ui->lblText->setText("Achtung: Zum oben angegebenen Zeitpunkt wird der Computer heruntergefahren bzw. neugestartet.
" + "Alle noch laufenden Programme (z.B. Word, Photoshop, Matlab, etc.)
" + "werden ohne Nachfrage beendet. Stellen Sie daher sicher, bis zum angegebenen Zeitpunkt
" + "sämtliche Daten abzuspeichern und die Sitzung zu verlassen."); + } 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 + +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 +#include +#include +#include +#include + +#ifdef X11_Xscreensaver_FOUND +#include +#include +#endif +#ifdef X11_dpms_FOUND +#include +#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(&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 @@ + + + Saver + + + + 0 + 0 + 499 + 431 + + + + + 10 + + + + Form + + + #Saver { + background: qlineargradient(spread:pad, x1:0, y1:0, x2:0, y2:1, stop:0 #443, stop:1 #000) +} + +QLabel { +color: #f64; +} + + + + + + + 20 + + + + ¯\_(ツ)_/¯ + + + Qt::AlignCenter + + + + + + + + 10 + + + + . + + + Qt::AutoText + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + 20 + + + + color: #999; + + + + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + 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 + * + * 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 , 9/7/90 + * - replaced all NULL's with properly cast 0's, 5/6/91 + * - free children list (suggested by Mark Martin ), 5/16/91 + * - include X11/Xlib.h and support RootWindowOfScreen, too 9/17/91 + * + * Jamie Zawinski , 28-Apr-1997 + * - use ANSI C + * + * Jamie Zawinski , 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 , 14-Aug-2004 + * - changes to get gcc to stop whining about "type punning". + * + * Jamie Zawinski , 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 +#include +#include + +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_ */ -- cgit v1.2.3-55-g7522