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)
annotationmodel.cpp
playlist.cpp
directoryplaylist.cpp
+ wake_lock/wakelock.cpp
+ wake_lock/wakelock_null.cpp
)
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
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);
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; }
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);
}
#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,
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;
--- /dev/null
+#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; }
--- /dev/null
+#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
--- /dev/null
+#include "wakelock_null.h"
+
+bool WakeLockNull::doLock() { return false; }
+void WakeLockNull::doUnlock() {}
--- /dev/null
+#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
--- /dev/null
+#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); }
--- /dev/null
+#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
--- /dev/null
+#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;
+}
--- /dev/null
+#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