Add Wayland idle timer

Wayland idle timer use ext_idle_notifier_v1 Wayland protocol to be notified of user idle status. It will only run if the compositor implement the protocol. For ext_idle_notifier_v1 version 2, get_input_idle_notification() only tracks user input, which I think is more appropriate.

Due to the usage of qt_generate_wayland_protocol_client_sources() in CMake to generate Wayland client code, Wayland idle timer can only be compiled using Qt 6. We could take relevant part from KDE's ECM FindQtWaylandScanner, but that seem cumbersome.
This commit is contained in:
Trần Nam Tuấn
2025-03-22 15:13:32 +07:00
parent 54884df07d
commit c50496f79c
8 changed files with 165 additions and 6 deletions
+1 -1
View File
@@ -107,7 +107,7 @@ if (LINUX)
cmake_dependent_option(WITH_MVIZ "Build with music visualizer." ON "WITH_ANIMATIONS;WITH_GUI" OFF)
cmake_dependent_option(USE_XCB "Build with features that require XCB." ON "WITH_GUI" OFF)
cmake_dependent_option(USE_DBUS_MENU "Build with DBus support for the tray." ON "WITH_GUI" OFF)
cmake_dependent_option(USE_WAYLAND "Build with features that require Wayland." ON "WITH_GUI" OFF)
cmake_dependent_option(USE_WAYLAND "Build with features that require Wayland." ON "WITH_GUI;PREFER_QT6" OFF)
option(USE_PORTAUDIO "Use portaudio for music visualizer." OFF)
else ()
cmake_dependent_option(WITH_MVIZ "Build with music visualizer." OFF "WITH_ANIMATIONS;WITH_GUI" OFF)
+35
View File
@@ -414,6 +414,40 @@ elseif (LINUX)
if (USE_WAYLAND)
find_package(Wayland COMPONENTS Client REQUIRED)
find_package(WaylandProtocols REQUIRED)
find_package(Qt${QT_VERSION_MAJOR} REQUIRED COMPONENTS WaylandClient)
if (QT_VERSION VERSION_GREATER_EQUAL "6.0.0")
if (NOT EXISTS ${WaylandProtocols_DATADIR}/staging/ext-idle-notify/ext-idle-notify-v1.xml)
message(FATAL_ERROR "Wayland ext-idle-notify-v1 protocol is not found. Either install them or pass -DUSE_WAYLAND=0 to disable idle detection.")
endif ()
if (Qt6_VERSION VERSION_GREATER_EQUAL "6.8.0")
set(private_code_option "PRIVATE_CODE")
endif()
qt_generate_wayland_protocol_client_sources(
ckb-next
${private_code_option}
FILES
${WaylandProtocols_DATADIR}/staging/ext-idle-notify/ext-idle-notify-v1.xml
)
target_sources(
ckb-next
PRIVATE
wayland/waylandidletimer.cpp
wayland/waylandidletimer.h
)
target_compile_definitions(
ckb-next
PRIVATE
USE_WAYLAND_IDLE_NOTIFY
)
else()
message(FATAL_ERROR "Wayland idle detection required Qt 6 or later. Either build with Qt 6 or pass -DUSE_WAYLAND=0 to disable idle detection.")
endif()
target_compile_definitions(
ckb-next
@@ -425,6 +459,7 @@ elseif (LINUX)
ckb-next
PRIVATE
Wayland::Client
Qt${QT_VERSION_MAJOR}::WaylandClient
)
if (Qt6_VERSION VERSION_LESS "6.5.0")
+10 -2
View File
@@ -11,12 +11,20 @@
#include "xcb/xcbidletimer.h"
#endif
#ifdef USE_WAYLAND_IDLE_NOTIFY
#include "wayland/waylandidletimer.h"
#endif
class IdleTimerHelper {
public:
QScopedPointer<IdleTimerImpl> m_impl;
IdleTimerHelper() {
if (!WaylandUtils::isWayland()) {
if (WaylandUtils::isWayland()) {
#ifdef USE_WAYLAND_IDLE_NOTIFY
m_impl.reset(new WaylandIdleTimer());
#endif
} else {
#ifdef USE_XCB_SCREENSAVER
m_impl.reset(new XcbIdleTimer());
#endif
@@ -37,5 +45,5 @@ int IdleTimer::getIdleTime()
bool IdleTimer::isSupported()
{
return g_helper->m_impl.isNull();
return g_helper->m_impl.isNull() ? false : g_helper->m_impl->isSupported();
}
+4 -1
View File
@@ -3,7 +3,10 @@
class IdleTimerImpl {
public:
virtual int getIdleTime() = 0;
virtual int getIdleTime() const = 0;
virtual inline bool isSupported() const {
return true;
}
};
class IdleTimer
+87
View File
@@ -0,0 +1,87 @@
#include "waylandidletimer.h"
#ifdef USE_WAYLAND_IDLE_NOTIFY
#include "wayland/waylandutils.h"
#include "qwayland-ext-idle-notify-v1.h"
#include <QGuiApplication>
#include <QDateTime>
#include <QWaylandClientExtensionTemplate>
class WaylandIdleNotification : public QtWayland::ext_idle_notification_v1 {
public:
WaylandIdleNotification(struct ::ext_idle_notification_v1 *object)
: QtWayland::ext_idle_notification_v1(object)
{}
~WaylandIdleNotification()
{
if (qGuiApp) {
destroy();
}
}
qint64 m_idleAt = -1;
protected:
void ext_idle_notification_v1_idled() override
{
m_idleAt = QDateTime::currentMSecsSinceEpoch();
}
void ext_idle_notification_v1_resumed() override
{
m_idleAt = -1;
}
};
class WaylandIdleNotifier : public QWaylandClientExtensionTemplate<WaylandIdleNotifier>, public QtWayland::ext_idle_notifier_v1 {
public:
WaylandIdleNotifier()
: QWaylandClientExtensionTemplate<WaylandIdleNotifier>(2)
{
initialize();
}
~WaylandIdleNotifier()
{
if (qGuiApp && isActive()) {
destroy();
}
}
};
WaylandIdleTimer::WaylandIdleTimer()
: m_supported{WaylandUtils::hasInterface("ext_idle_notifier_v1", 1)}
{
if (m_supported) {
auto seat = WaylandUtils::seat();
m_notifier.reset(new WaylandIdleNotifier());
if (WaylandUtils::hasInterface("ext_idle_notifier_v1", 2)) {
m_notification.reset(new WaylandIdleNotification(m_notifier->get_input_idle_notification(0, seat)));
} else {
m_notification.reset(new WaylandIdleNotification(m_notifier->get_idle_notification(0, seat)));
}
}
}
int WaylandIdleTimer::getIdleTime() const
{
if (m_supported) {
const auto idleAt = m_notification->m_idleAt;
if (idleAt >= 0) {
const auto idleTime = QDateTime::currentMSecsSinceEpoch() - idleAt;
if (idleTime > INT_MAX) {
return INT_MAX;
}
return (int)idleTime;
}
}
return 0;
}
bool WaylandIdleTimer::isSupported() const
{
return m_supported;
}
#endif
+26
View File
@@ -0,0 +1,26 @@
#ifndef WAYLANDIDLETIMER_HPP
#define WAYLANDIDLETIMER_HPP
#ifdef USE_WAYLAND_IDLE_NOTIFY
#include "idletimer.h"
#include <QScopedPointer>
class WaylandIdleNotifier;
class WaylandIdleNotification;
class WaylandIdleTimer : public IdleTimerImpl
{
public:
WaylandIdleTimer();
int getIdleTime() const override;
bool isSupported() const override;
private:
bool m_supported;
QScopedPointer<WaylandIdleNotifier> m_notifier;
QScopedPointer<WaylandIdleNotification> m_notification;
};
#endif
#endif // WAYLANDIDLETIMER_HPP
+1 -1
View File
@@ -17,7 +17,7 @@
#include "mainwindow.h"
int XcbIdleTimer::getIdleTime()
int XcbIdleTimer::getIdleTime() const
{
const MainWindow* const mw = MainWindow::mainWindow;
if(!mw || !mw->windowHandle())
+1 -1
View File
@@ -6,7 +6,7 @@
class XcbIdleTimer : public IdleTimerImpl
{
public:
int getIdleTime() override;
int getIdleTime() const override;
};
#endif // XCBIDLETIMER_HPP