From: APTX Date: Fri, 4 Mar 2022 14:05:27 +0000 (+0900) Subject: Add WakeLock X-Git-Url: https://gitweb.tyo.aptx.org/?a=commitdiff_plain;h=ba6eb4b5a410aab84ec98c01402fb5a759a330ee;p=aniplayer.git Add WakeLock A cross-platform (through platform backends) class for obtaining a wake lock (force OS to stay awake and not turn off the screen). --- diff --git a/CMakeLists.txt b/CMakeLists.txt index 09e5b9c..c2e0f48 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -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) diff --git a/core/CMakeLists.txt b/core/CMakeLists.txt index cbbaafd..6109dc4 100644 --- a/core/CMakeLists.txt +++ b/core/CMakeLists.txt @@ -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 diff --git a/core/player.cpp b/core/player.cpp index 566b161..738194f 100644 --- a/core/player.cpp +++ b/core/player.cpp @@ -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); } diff --git a/core/player.h b/core/player.h index 6e588a1..6a4a5a2 100644 --- a/core/player.h +++ b/core/player.h @@ -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 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 index 0000000..1df6a10 --- /dev/null +++ b/core/wake_lock/wakelock.cpp @@ -0,0 +1,59 @@ +#include "wakelock.h" + +#include + +#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::makeWakeLock() { +#if defined(Q_OS_WIN32) + return std::make_unique(); +#elif defined(Q_OS_LINUX) and defined(WITH_FEATURE_DBUS) + return std::make_unique(); +#else + return std::make_unique(); +#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 index 0000000..f046787 --- /dev/null +++ b/core/wake_lock/wakelock.h @@ -0,0 +1,34 @@ +#ifndef WAKELOCK_H +#define WAKELOCK_H + +#include + +class WakeLock { +public: + enum State { + Unlocked, + Locked, + }; + + virtual ~WakeLock(); + void lock(); + void unlock(); + void waitForUnlock(); + + bool isLocked() const; + + static std::unique_ptr 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 index 0000000..b7cf390 --- /dev/null +++ b/core/wake_lock/wakelock_null.cpp @@ -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 index 0000000..cc13f51 --- /dev/null +++ b/core/wake_lock/wakelock_null.h @@ -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 index 0000000..1579d25 --- /dev/null +++ b/core/wake_lock/wakelock_win.cpp @@ -0,0 +1,12 @@ +#include "wakelock_win.h" + +#include + +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 index 0000000..277f0ac --- /dev/null +++ b/core/wake_lock/wakelock_win.h @@ -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 index 0000000..65588bd --- /dev/null +++ b/core/wake_lock/wakelock_x11.cpp @@ -0,0 +1,101 @@ +#include "wakelock_x11.h" + +#include +#include + +#include +#include + +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 index 0000000..9a569d5 --- /dev/null +++ b/core/wake_lock/wakelock_x11.h @@ -0,0 +1,33 @@ +#ifndef WAKELOCKX11_H +#define WAKELOCKX11_H + +#include "wake_lock/wakelock.h" +#include + +#include +#include + +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