summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.gitignore4
-rw-r--r--CMakeLists.txt89
-rw-r--r--src/main.cpp72
-rw-r--r--src/saverwidget.cpp178
-rw-r--r--src/saverwidget.h45
-rw-r--r--src/screensaver.cpp217
-rw-r--r--src/screensaver.h33
-rw-r--r--src/ui/saver.ui96
-rwxr-xr-xsrc/vroot.h156
9 files changed, 890 insertions, 0 deletions
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 "<RCC>\n <qresource prefix=\"/\">\n")
+ foreach(file ${BWLPSAVER_QMS})
+ get_filename_component(filename ${file} NAME)
+ set(resource_file_content "${resource_file_content} <file>${filename}</file>\n")
+ endforeach(file)
+ set(resource_file_content "${resource_file_content} </qresource>\n</RCC>\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 <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_ */