]> Some of my projects - aniplayer.git/commitdiff
Add WakeLock
authorAPTX <marek321@gmail.com>
Fri, 4 Mar 2022 14:05:27 +0000 (23:05 +0900)
committerAPTX <marek321@gmail.com>
Fri, 4 Mar 2022 14:05:27 +0000 (23:05 +0900)
A cross-platform (through platform backends) class for obtaining
a wake lock (force OS to stay awake and not turn off the screen).

12 files changed:
CMakeLists.txt
core/CMakeLists.txt
core/player.cpp
core/player.h
core/wake_lock/wakelock.cpp [new file with mode: 0644]
core/wake_lock/wakelock.h [new file with mode: 0644]
core/wake_lock/wakelock_null.cpp [new file with mode: 0644]
core/wake_lock/wakelock_null.h [new file with mode: 0644]
core/wake_lock/wakelock_win.cpp [new file with mode: 0644]
core/wake_lock/wakelock_win.h [new file with mode: 0644]
core/wake_lock/wakelock_x11.cpp [new file with mode: 0644]
core/wake_lock/wakelock_x11.h [new file with mode: 0644]

index 09e5b9cb21c250ceddc35a003bd15796fa2a4892..c2e0f48b1dc947b975b216d9b90c4afbebdf6b32 100644 (file)
@@ -19,6 +19,11 @@ add_feature_info(FeatureLocalMyList WITH_FEATURE_LOCALMYLIST "automatically mark
 option(WITH_FEATURE_ANNOTATIONS "Build annotations feature plugin" ON)
 add_feature_info(FeatureAnnotations WITH_FEATURE_ANNOTATIONS "annotate certain features in the video")
 
+if (UNIX)
+    option(WITH_FEATURE_DBUS "Build Build with DBus" ON)
+    add_feature_info(FeatureDBus WITH_FEATURE_DBUS "Linux requires DBus for wake locks")
+endif()
+
 cmake_dependent_option(USE_SHARED_DLIB "Use shared dlib" OFF "WITH_FEATURE_ANNOTATIONS" OFF)
 
 add_subdirectory(pluginapi)
index cbbaafd42d9da75ee0ce78f618a48cd214ef7ddd..6109dc478a10aeb72e3c45fadefd6af36793fd99 100644 (file)
@@ -43,6 +43,8 @@ set(core_SOURCES
     annotationmodel.cpp
     playlist.cpp
     directoryplaylist.cpp
+    wake_lock/wakelock.cpp
+    wake_lock/wakelock_null.cpp
 )
 
 set(core_HEADERS
@@ -56,8 +58,33 @@ set(core_HEADERS
     annotationmodel.h
     playlist.h
     directoryplaylist.h
+    wake_lock/wakelock.h
+    wake_lock/wakelock_null.h
 )
 
+if (WIN32)
+    set(core_SOURCES ${core_SOURCES}
+        wake_lock/wakelock_win.cpp
+    )
+    set(core_HEADERS ${core_HEADERS}
+        wake_lock/wakelock_win.h
+    )
+elseif(UNIX AND WITH_FEATURE_DBUS)
+    add_definitions(-DWITH_FEATURE_DBUS)
+    find_package(Qt5 ${QT_MIN_VERSION} CONFIG REQUIRED COMPONENTS
+        DBus
+    )
+    set(core_LIBS ${core_LIBS}
+        Qt::DBus
+    )
+    set(core_SOURCES ${core_SOURCES}
+        wake_lock/wakelock_x11.cpp
+    )
+    set(core_HEADERS ${core_HEADERS}
+        wake_lock/wakelock_x11.h
+    )
+endif()
+
 set(core_PUBLIC_HEADERS
     include/aniplayer/backendpluginbase.h
     include/aniplayer/playerplugininterface.h
index 566b161290888f4d483437de7ff838eb89213fd0..738194f93b1b2809eb3284de503885f76c090534 100644 (file)
@@ -24,6 +24,8 @@ Player::Player(BackendPluginBase *backendPlugin, QObject *parent)
   m_backend = backendPlugin->createInstance(this);
   Q_CHECK_PTR(m_backend);
 
+  m_wakeLock = WakeLock::makeWakeLock();
+
   m_videoTrackModel = new TrackModel{this};
   m_videoTrackModel->type = "video";
   Q_CHECK_PTR(m_videoTrackModel);
@@ -57,9 +59,7 @@ Player::Player(BackendPluginBase *backendPlugin, QObject *parent)
 
 Player::~Player() {
   qCDebug(playerCategory) << "Destroying player" << this;
-#ifdef Q_OS_WIN
-  SetThreadExecutionState(ES_CONTINUOUS);
-#endif
+  m_wakeLock->waitForUnlock();
 }
 
 BackendInstance *Player::backend() const { return m_backend; }
@@ -271,12 +271,11 @@ void Player::playStateChanged(PlayerPluginInterface::PlayState state) {
   m_state = s;
   qCDebug(playerCategory) << "Play state changed to" << s;
 
-#ifdef Q_OS_WIN
-  if (m_state == PlayState::Playing)
-    SetThreadExecutionState(ES_CONTINUOUS | ES_DISPLAY_REQUIRED);
-  else
-    SetThreadExecutionState(ES_CONTINUOUS);
-#endif
+  if (m_state == PlayState::Playing) {
+    m_wakeLock->lock();
+  } else {
+    m_wakeLock->unlock();
+  }
 
   emit stateChanged(s);
 }
index 6e588a125bc5b69440edba71f5ecfd7c5baf218e..6a4a5a2f3de422320a905912dd3ce15a131d250c 100644 (file)
@@ -9,10 +9,11 @@
 
 #include "aniplayer/backendpluginbase.h"
 #include "aniplayer/playerfeatureplugininterface.h"
-#include "chaptermodel.h"
-#include "trackmodel.h"
 #include "annotationmodel.h"
+#include "chaptermodel.h"
 #include "playlist.h"
+#include "trackmodel.h"
+#include "wake_lock/wakelock.h"
 
 class Player : public QObject,
                public PlayerPluginInterface,
@@ -216,6 +217,7 @@ private:
   void updatePlaylistState(const QUrl &resource);
 
   BackendInstance *m_backend = nullptr;
+  std::unique_ptr<WakeLock> m_wakeLock;
   QUrl m_currentSource;
   QUrl m_nextSource;
   PlayState m_state = PlayState::Stopped;
diff --git a/core/wake_lock/wakelock.cpp b/core/wake_lock/wakelock.cpp
new file mode 100644 (file)
index 0000000..1df6a10
--- /dev/null
@@ -0,0 +1,59 @@
+#include "wakelock.h"
+
+#include <QtGlobal>
+
+#if defined(Q_OS_WIN32)
+#include "wake_lock/wakelock_win.h"
+#elif defined(Q_OS_LINUX) and defined(WITH_FEATURE_DBUS)
+#include "wake_lock/wakelock_x11.h"
+#endif
+#include "wake_lock/wakelock_null.h"
+
+WakeLock::WakeLock() = default;
+
+WakeLock::~WakeLock() {
+  Q_ASSERT_X(m_state == Unlocked, "WakeLock", "Destroyed without unlocking");
+}
+
+void WakeLock::lock() {
+  if (isLocked()) {
+    return;
+  }
+  if (doLockAsync()) {
+    m_state = Locked;
+  }
+}
+
+void WakeLock::unlock() {
+  if (!isLocked()) {
+    return;
+  }
+  doUnlockAsync();
+  m_state = Unlocked;
+}
+
+void WakeLock::waitForUnlock() {
+  if (!isLocked()) {
+    return;
+  }
+  doUnlock();
+  m_state = Unlocked;
+}
+
+bool WakeLock::isLocked() const { return m_state == Locked; }
+
+std::unique_ptr<WakeLock> WakeLock::makeWakeLock() {
+#if defined(Q_OS_WIN32)
+  return std::make_unique<WakeLockWin>();
+#elif defined(Q_OS_LINUX) and defined(WITH_FEATURE_DBUS)
+  return std::make_unique<WakeLockX11>();
+#else
+  return std::make_unique<WakeLockNull>();
+#endif
+}
+
+bool WakeLock::doLockAsync() { return doLock(); }
+
+void WakeLock::doUnlockAsync() {}
+
+void WakeLock::updateLockState(State state) { m_state = state; }
diff --git a/core/wake_lock/wakelock.h b/core/wake_lock/wakelock.h
new file mode 100644 (file)
index 0000000..f046787
--- /dev/null
@@ -0,0 +1,34 @@
+#ifndef WAKELOCK_H
+#define WAKELOCK_H
+
+#include <memory>
+
+class WakeLock {
+public:
+  enum State {
+    Unlocked,
+    Locked,
+  };
+
+  virtual ~WakeLock();
+  void lock();
+  void unlock();
+  void waitForUnlock();
+
+  bool isLocked() const;
+
+  static std::unique_ptr<WakeLock> makeWakeLock();
+
+protected:
+  WakeLock();
+  virtual bool doLockAsync();
+  virtual bool doLock() = 0;
+  virtual void doUnlockAsync();
+  virtual void doUnlock() = 0;
+
+  void updateLockState(State);
+
+  State m_state = Unlocked;
+};
+
+#endif // WAKELOCK_H
diff --git a/core/wake_lock/wakelock_null.cpp b/core/wake_lock/wakelock_null.cpp
new file mode 100644 (file)
index 0000000..b7cf390
--- /dev/null
@@ -0,0 +1,4 @@
+#include "wakelock_null.h"
+
+bool WakeLockNull::doLock() { return false; }
+void WakeLockNull::doUnlock() {}
diff --git a/core/wake_lock/wakelock_null.h b/core/wake_lock/wakelock_null.h
new file mode 100644 (file)
index 0000000..cc13f51
--- /dev/null
@@ -0,0 +1,13 @@
+#ifndef WAKELOCKNULL_H
+#define WAKELOCKNULL_H
+
+#include "wake_lock/wakelock.h"
+
+class WakeLockNull : public WakeLock {
+  // WakeLock interface
+protected:
+  bool doLock() override;
+  void doUnlock() override;
+};
+
+#endif // WAKELOCKNULL_H
diff --git a/core/wake_lock/wakelock_win.cpp b/core/wake_lock/wakelock_win.cpp
new file mode 100644 (file)
index 0000000..1579d25
--- /dev/null
@@ -0,0 +1,12 @@
+#include "wakelock_win.h"
+
+#include <Windows.h>
+
+WakeLockWin::WakeLockWin() = default;
+
+bool WakeLockWin::doLock() {
+  SetThreadExecutionState(ES_CONTINUOUS | ES_DISPLAY_REQUIRED);
+  return true;
+}
+
+void WakeLockWin::doUnlock() { SetThreadExecutionState(ES_CONTINUOUS); }
diff --git a/core/wake_lock/wakelock_win.h b/core/wake_lock/wakelock_win.h
new file mode 100644 (file)
index 0000000..277f0ac
--- /dev/null
@@ -0,0 +1,16 @@
+#ifndef WAKELOCKWIN_H
+#define WAKELOCKWIN_H
+
+#include "wake_lock/wakelock.h"
+
+class WakeLockWin : public WakeLock {
+public:
+  WakeLockWin();
+
+  // WakeLock interface
+protected:
+  bool doLock() override;
+  void doUnlock() override;
+};
+
+#endif // WAKELOCKWIN_H
diff --git a/core/wake_lock/wakelock_x11.cpp b/core/wake_lock/wakelock_x11.cpp
new file mode 100644 (file)
index 0000000..65588bd
--- /dev/null
@@ -0,0 +1,101 @@
+#include "wakelock_x11.h"
+
+#include <QCoreApplication>
+#include <QDBusReply>
+
+#include <QDebug>
+#include <QLoggingCategory>
+
+Q_LOGGING_CATEGORY(wakeLockX11, "wakelock.x11");
+
+namespace {
+const char SERVICE_NAME[] = "org.freedesktop.ScreenSaver";
+const char SERVICE_PATH[] = "/org/freedesktop/ScreenSaver";
+const char INTERFACE_NAME[] = "org.freedesktop.ScreenSaver";
+const char INHIBIT[] = "Inhibit";
+const char UNINHIBIT[] = "UnInhibit";
+
+const char REASON[] = "Playing Video";
+} // namespace
+
+WakeLockX11::WakeLockX11()
+    : m_screenSaver{SERVICE_NAME, SERVICE_PATH, INTERFACE_NAME,
+                    QDBusConnection::sessionBus()} {}
+
+bool WakeLockX11::doLock() {
+  if (!QDBusConnection::sessionBus().isConnected()) {
+    qCWarning(wakeLockX11) << "Could not connect to the DBus session bus";
+    return false;
+  }
+  if (!m_screenSaver.isValid()) {
+    qCWarning(wakeLockX11) << "Could not find interface: " << SERVICE_NAME;
+    return false;
+  }
+  if (m_shouldUnlock) {
+    m_shouldUnlock = false;
+    return true;
+  }
+  const bool sent = m_screenSaver.callWithCallback(
+      INHIBIT, {QVariant{qApp->applicationName()}, QVariant{REASON}}, this,
+      SLOT(onLockObtained(quint32)), SLOT(onLockError(QDBusError)));
+  return sent;
+}
+
+void WakeLockX11::doUnlockAsync() {
+  if (!QDBusConnection::sessionBus().isConnected()) {
+    qCWarning(wakeLockX11) << "Could not connect to the DBus session bus";
+    return;
+  }
+  if (!m_screenSaver.isValid()) {
+    qCWarning(wakeLockX11) << "Could not find interface: " << SERVICE_NAME;
+    return;
+  }
+  if (!m_cookie) {
+    m_shouldUnlock = true;
+    return;
+  }
+  m_screenSaver.callWithCallback(UNINHIBIT, {QVariant{m_cookie}}, this,
+                                 SLOT(onUnlocked(QDBusMessage)),
+                                 SLOT(onUnlockError(QDBusError)));
+}
+
+void WakeLockX11::doUnlock() {
+  if (!QDBusConnection::sessionBus().isConnected()) {
+    qCWarning(wakeLockX11) << "Could not connect to the DBus session bus";
+    return;
+  }
+  if (!m_screenSaver.isValid()) {
+    qCWarning(wakeLockX11) << "Could not find interface: " << SERVICE_NAME;
+    return;
+  }
+  if (!m_cookie) {
+    m_shouldUnlock = true;
+    return;
+  }
+  QDBusMessage message = m_screenSaver.call(UNINHIBIT, QVariant{m_cookie});
+  qCDebug(wakeLockX11) << "Unlock call result: " << message;
+}
+
+void WakeLockX11::onLockObtained(quint32 cookie) {
+  m_cookie = cookie;
+  qCDebug(wakeLockX11) << "Lock obtained. Got cookie: " << m_cookie;
+  if (m_shouldUnlock) {
+    doUnlock();
+  }
+}
+
+void WakeLockX11::onLockError(QDBusError error) {
+  qCWarning(wakeLockX11) << "Failed to get lock: " << error;
+  m_shouldUnlock = false;
+  updateLockState(Unlocked);
+}
+
+void WakeLockX11::onUnlocked(QDBusMessage) {
+  m_shouldUnlock = false;
+  qCDebug(wakeLockX11) << "Lock released";
+}
+
+void WakeLockX11::onUnlockError(QDBusError error) {
+  qCWarning(wakeLockX11) << "Failed to unlock: " << error;
+  m_shouldUnlock = false;
+}
diff --git a/core/wake_lock/wakelock_x11.h b/core/wake_lock/wakelock_x11.h
new file mode 100644 (file)
index 0000000..9a569d5
--- /dev/null
@@ -0,0 +1,33 @@
+#ifndef WAKELOCKX11_H
+#define WAKELOCKX11_H
+
+#include "wake_lock/wakelock.h"
+#include <QObject>
+
+#include <QDBusError>
+#include <QDBusInterface>
+
+class WakeLockX11 : public QObject, public WakeLock {
+  Q_OBJECT
+public:
+  WakeLockX11();
+
+  // WakeLock interface
+protected:
+  bool doLock() override;
+  void doUnlockAsync() override;
+  void doUnlock() override;
+
+private slots:
+  void onLockObtained(quint32 cookie);
+  void onLockError(QDBusError);
+  void onUnlocked(QDBusMessage);
+  void onUnlockError(QDBusError);
+
+private:
+  QDBusInterface m_screenSaver;
+  quint32 m_cookie = 0;
+  bool m_shouldUnlock = false;
+};
+
+#endif // WAKELOCKX11_H