Don't lock the screen when it's already locked.
authorLuís Pereira <luis.artur.pereira@gmail.com>
Fri, 1 Jul 2016 10:58:45 +0000 (11:58 +0100)
committerLuís Pereira <luis.artur.pereira@gmail.com>
Fri, 1 Jul 2016 11:54:26 +0000 (12:54 +0100)
The status of the screensaver wasn't taken into account when locking it.
Now we only lock it if it's not already locked. This way we don't get the
annoying error message stating that there was an error starting the
screensaver.

Part of the code is taken from the Chromium project. Adapted to fit our
purposes.

CMakeLists.txt
lxqtscreensaver.cpp
lxqtscreensaver.h

index cf25164..b875e89 100644 (file)
@@ -131,6 +131,7 @@ set(CMAKE_POSITION_INDEPENDENT_CODE ON)
 set(CMAKE_AUTOMOC ON)
 set(CMAKE_AUTOUIC ON)
 
+find_package(X11 REQUIRED QUIET)
 find_package(Qt5Widgets REQUIRED QUIET)
 find_package(Qt5DBus REQUIRED QUIET)
 find_package(Qt5X11Extras REQUIRED QUIET)
@@ -296,6 +297,9 @@ target_link_libraries(${LXQT_LIBRARY_NAME}
         Qt5::DBus
         Qt5::X11Extras
         Qt5Xdg
+    PRIVATE
+        ${X11_Xscreensaver_LIB}
+        ${X11_X11_LIB}
 )
 
 set_target_properties(${LXQT_LIBRARY_NAME} PROPERTIES
index 17e7192..c38e46b 100644 (file)
@@ -7,6 +7,8 @@
  * Copyright: 2010-2011 Razor team
  * Authors:
  *   Petr Vanek <petr@scribus.info>
+ * Copyright (c) 2016 Luís Pereira <luis.artur.pereira@gmail.com>
+ * Copyright (c) 2012 The Chromium Authors. All rights reserved.
  *
  * This program or library is free software; you can redistribute it
  * and/or modify it under the terms of the GNU Lesser General Public
 #include "lxqtscreensaver.h"
 #include "lxqttranslator.h"
 
+#include <memory>
+
 #include <XdgIcon>
 #include <QMessageBox>
 #include <QAction>
 #include <QPointer>
 #include <QProcess>
 #include <QCoreApplication> // for Q_DECLARE_TR_FUNCTIONS
+#include <QX11Info>
+
+#include <X11/extensions/scrnsaver.h>
+
+// Avoid polluting everything with X11/Xlib.h:
+typedef struct _XDisplay Display;
+
+typedef unsigned long XAtom;
+typedef unsigned long XID;
+
+extern "C" {
+int XFree(void*);
+}
+
+template <class T, class R, R (*F)(T*)>
+struct XObjectDeleter {
+  inline void operator()(void* ptr) const { F(static_cast<T*>(ptr)); }
+};
+
+template <class T, class D = XObjectDeleter<void, int, XFree>>
+using XScopedPtr = std::unique_ptr<T, D>;
+
 
 namespace LXQt {
 
+static bool GetProperty(XID window, const std::string& property_name, long max_length,
+                 Atom* type, int* format, unsigned long* num_items,
+                 unsigned char** property);
+
+
+static bool GetIntArrayProperty(XID window,
+                         const std::string& property_name,
+                         std::vector<int>* value);
+
+
+static bool GetProperty(XID window, const std::string& property_name, long max_length,
+                 Atom* type, int* format, unsigned long* num_items,
+                 unsigned char** property)
+{
+    Atom property_atom =  XInternAtom(QX11Info::display(), property_name.c_str(), false);
+    unsigned long remaining_bytes = 0;
+    return XGetWindowProperty(QX11Info::display(),
+                              window,
+                              property_atom,
+                              0,          // offset into property data to read
+                              max_length, // max length to get
+                              False,      // deleted
+                              AnyPropertyType,
+                              type,
+                              format,
+                              num_items,
+                              &remaining_bytes,
+                              property);
+}
+
+static bool GetIntArrayProperty(XID window,
+                         const std::string& property_name,
+                         std::vector<int>* value)
+{
+    Atom type = None;
+    int format = 0;  // size in bits of each item in 'property'
+    unsigned long num_items = 0;
+    unsigned char* properties = NULL;
+
+    int result = GetProperty(window, property_name,
+                           (~0L), // (all of them)
+                           &type, &format, &num_items, &properties);
+    XScopedPtr<unsigned char> scoped_properties(properties);
+    if (result != Success)
+        return false;
+
+    if (format != 32)
+        return false;
+
+    long* int_properties = reinterpret_cast<long*>(properties);
+    value->clear();
+    for (unsigned long i = 0; i < num_items; ++i)
+    {
+        value->push_back(static_cast<int>(int_properties[i]));
+    }
+    return true;
+}
+
 class ScreenSaverPrivate
 {
     Q_DECLARE_TR_FUNCTIONS(LXQt::ScreenSaver);
@@ -48,6 +132,8 @@ public:
     ScreenSaverPrivate(ScreenSaver *q) : q_ptr(q) {};
 
     void _l_xdgProcess_finished(int, QProcess::ExitStatus);
+    bool isScreenSaverLocked();
+
     QPointer<QProcess> m_xdgProcess;
 };
 
@@ -97,6 +183,34 @@ void ScreenSaverPrivate::_l_xdgProcess_finished(int err, QProcess::ExitStatus st
     emit q->done();
 }
 
+bool ScreenSaverPrivate::isScreenSaverLocked()
+{
+    XScreenSaverInfo *info = 0;
+    Display *display = QX11Info::display();
+    XID window = DefaultRootWindow(display);
+    info = XScreenSaverAllocInfo();
+
+    XScreenSaverQueryInfo(QX11Info::display(), window, info);
+    const int state = info->state;
+    XFree(info);
+    if (state == ScreenSaverOn)
+        return true;
+
+    // Ironically, xscreensaver does not conform to the XScreenSaver protocol, so
+    // info.state == ScreenSaverOff or info.state == ScreenSaverDisabled does not
+    // necessarily mean that a screensaver is not active, so add a special check
+    // for xscreensaver.
+    XAtom lock_atom = XInternAtom(display, "LOCK", false);
+    std::vector<int> atom_properties;
+    if (GetIntArrayProperty(window, "_SCREENSAVER_STATUS", &atom_properties) &&
+        atom_properties.size() > 0)
+    {
+        if (atom_properties[0] == static_cast<int>(lock_atom))
+            return true;
+    }
+
+    return false;
+}
 
 ScreenSaver::ScreenSaver(QObject * parent)
     : QObject(parent),
@@ -128,7 +242,8 @@ QList<QAction*> ScreenSaver::availableActions()
 void ScreenSaver::lockScreen()
 {
     Q_D(ScreenSaver);
-    d->m_xdgProcess->start("xdg-screensaver", QStringList() << "lock");
+    if (!d->isScreenSaverLocked())
+        d->m_xdgProcess->start("xdg-screensaver", QStringList() << "lock");
 }
 
 } // namespace LXQt
index 7292092..723dab9 100644 (file)
@@ -8,6 +8,8 @@
  * Authors:
  *   Petr Vanek <petr@scribus.info>
  *
+ * Copyright (c) 2016 Luís Pereira <luis.artur.pereira@gmail.com>
+ *
  * This program or library is free software; you can redistribute it
  * and/or modify it under the terms of the GNU Lesser General Public
  * License as published by the Free Software Foundation; either