mirror of
https://git.outfoxxed.me/quickshell/quickshell.git
synced 2025-11-04 19:04:56 +11:00
wayland/idle-notify: add idle notify
This commit is contained in:
parent
b8fa424f85
commit
6eb12551ba
8 changed files with 329 additions and 0 deletions
|
|
@ -117,6 +117,9 @@ endif()
|
||||||
add_subdirectory(idle_inhibit)
|
add_subdirectory(idle_inhibit)
|
||||||
list(APPEND WAYLAND_MODULES Quickshell.Wayland._IdleInhibitor)
|
list(APPEND WAYLAND_MODULES Quickshell.Wayland._IdleInhibitor)
|
||||||
|
|
||||||
|
add_subdirectory(idle_notify)
|
||||||
|
list(APPEND WAYLAND_MODULES Quickshell.Wayland._IdleNotify)
|
||||||
|
|
||||||
# widgets for qmenu
|
# widgets for qmenu
|
||||||
target_link_libraries(quickshell-wayland PRIVATE
|
target_link_libraries(quickshell-wayland PRIVATE
|
||||||
Qt::Quick Qt::Widgets Qt::WaylandClient Qt::WaylandClientPrivate
|
Qt::Quick Qt::Widgets Qt::WaylandClient Qt::WaylandClientPrivate
|
||||||
|
|
|
||||||
25
src/wayland/idle_notify/CMakeLists.txt
Normal file
25
src/wayland/idle_notify/CMakeLists.txt
Normal file
|
|
@ -0,0 +1,25 @@
|
||||||
|
qt_add_library(quickshell-wayland-idle-notify STATIC
|
||||||
|
proto.cpp
|
||||||
|
monitor.cpp
|
||||||
|
)
|
||||||
|
|
||||||
|
qt_add_qml_module(quickshell-wayland-idle-notify
|
||||||
|
URI Quickshell.Wayland._IdleNotify
|
||||||
|
VERSION 0.1
|
||||||
|
DEPENDENCIES QtQuick
|
||||||
|
)
|
||||||
|
|
||||||
|
install_qml_module(quickshell-wayland-idle-notify)
|
||||||
|
|
||||||
|
qs_add_module_deps_light(quickshell-wayland-idle-notify Quickshell)
|
||||||
|
|
||||||
|
wl_proto(wlp-idle-notify ext-idle-notify-v1 "${WAYLAND_PROTOCOLS}/staging/ext-idle-notify")
|
||||||
|
|
||||||
|
target_link_libraries(quickshell-wayland-idle-notify PRIVATE
|
||||||
|
Qt::Quick Qt::WaylandClient Qt::WaylandClientPrivate wayland-client
|
||||||
|
wlp-idle-notify
|
||||||
|
)
|
||||||
|
|
||||||
|
qs_module_pch(quickshell-wayland-idle-notify SET large)
|
||||||
|
|
||||||
|
target_link_libraries(quickshell PRIVATE quickshell-wayland-idle-notifyplugin)
|
||||||
52
src/wayland/idle_notify/monitor.cpp
Normal file
52
src/wayland/idle_notify/monitor.cpp
Normal file
|
|
@ -0,0 +1,52 @@
|
||||||
|
#include "monitor.hpp"
|
||||||
|
#include <algorithm>
|
||||||
|
|
||||||
|
#include <qlogging.h>
|
||||||
|
#include <qscopeguard.h>
|
||||||
|
#include <qtypes.h>
|
||||||
|
|
||||||
|
#include "proto.hpp"
|
||||||
|
|
||||||
|
namespace qs::wayland::idle_notify {
|
||||||
|
|
||||||
|
IdleMonitor::~IdleMonitor() { delete this->bNotification.value(); }
|
||||||
|
|
||||||
|
void IdleMonitor::onPostReload() {
|
||||||
|
this->bParams.setBinding([this] {
|
||||||
|
return Params {
|
||||||
|
.enabled = this->bEnabled.value(),
|
||||||
|
.timeout = this->bTimeout.value(),
|
||||||
|
.respectInhibitors = this->bRespectInhibitors.value()
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
this->bIsIdle.setBinding([this] {
|
||||||
|
auto* notification = this->bNotification.value();
|
||||||
|
return notification ? notification->bIsIdle.value() : false;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void IdleMonitor::updateNotification() {
|
||||||
|
auto* notification = this->bNotification.value();
|
||||||
|
delete notification;
|
||||||
|
notification = nullptr;
|
||||||
|
|
||||||
|
auto guard = qScopeGuard([&] { this->bNotification = notification; });
|
||||||
|
|
||||||
|
auto params = this->bParams.value();
|
||||||
|
|
||||||
|
if (params.enabled) {
|
||||||
|
auto* manager = impl::IdleNotificationManager::instance();
|
||||||
|
|
||||||
|
if (!manager) {
|
||||||
|
qWarning() << "Cannot create idle monitor as ext-idle-notify-v1 is not supported by the "
|
||||||
|
"current compositor.";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto timeout = static_cast<quint32>(std::max(0, static_cast<int>(params.timeout * 1000)));
|
||||||
|
notification = manager->createIdleNotification(timeout, params.respectInhibitors);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace qs::wayland::idle_notify
|
||||||
78
src/wayland/idle_notify/monitor.hpp
Normal file
78
src/wayland/idle_notify/monitor.hpp
Normal file
|
|
@ -0,0 +1,78 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <qobject.h>
|
||||||
|
#include <qproperty.h>
|
||||||
|
#include <qqmlintegration.h>
|
||||||
|
#include <qtclasshelpermacros.h>
|
||||||
|
#include <qtmetamacros.h>
|
||||||
|
#include <qtypes.h>
|
||||||
|
|
||||||
|
#include "../../core/reload.hpp"
|
||||||
|
#include "proto.hpp"
|
||||||
|
|
||||||
|
namespace qs::wayland::idle_notify {
|
||||||
|
|
||||||
|
///! Provides a notification when a wayland session is makred idle
|
||||||
|
/// An idle monitor detects when the user stops providing input for a period of time.
|
||||||
|
///
|
||||||
|
/// > [!NOTE] Using an idle monitor requires the compositor support the [ext-idle-notify-v1] protocol.
|
||||||
|
///
|
||||||
|
/// [ext-idle-notify-v1]: https://wayland.app/protocols/ext-idle-notify-v1
|
||||||
|
class IdleMonitor: public PostReloadHook {
|
||||||
|
Q_OBJECT;
|
||||||
|
QML_ELEMENT;
|
||||||
|
// clang-format off
|
||||||
|
/// If the idle monitor should be enabled. Defaults to true.
|
||||||
|
Q_PROPERTY(bool enabled READ isEnabled WRITE setEnabled NOTIFY enabledChanged);
|
||||||
|
/// The amount of time in seconds the idle monitor should wait before reporting an idle state.
|
||||||
|
///
|
||||||
|
/// Defaults to zero, which reports idle status immediately.
|
||||||
|
Q_PROPERTY(qreal timeout READ default WRITE default NOTIFY timeoutChanged BINDABLE bindableTimeout);
|
||||||
|
/// When set to true, @@isIdle will depend on both user interaction and active idle inhibitors.
|
||||||
|
/// When false, the value will depend solely on user interaction. Defaults to true.
|
||||||
|
Q_PROPERTY(bool respectInhibitors READ default WRITE default NOTIFY respectInhibitorsChanged BINDABLE bindableRespectInhibitors);
|
||||||
|
/// This property is true if the user has been idle for at least @@timeout.
|
||||||
|
/// What is considered to be idle is influenced by @@respectInhibitors.
|
||||||
|
Q_PROPERTY(bool isIdle READ default NOTIFY isIdleChanged BINDABLE bindableIsIdle);
|
||||||
|
// clang-format on
|
||||||
|
|
||||||
|
public:
|
||||||
|
IdleMonitor() = default;
|
||||||
|
~IdleMonitor() override;
|
||||||
|
Q_DISABLE_COPY_MOVE(IdleMonitor);
|
||||||
|
|
||||||
|
void onPostReload() override;
|
||||||
|
|
||||||
|
[[nodiscard]] bool isEnabled() const { return this->bNotification.value(); }
|
||||||
|
void setEnabled(bool enabled) { this->bEnabled = enabled; }
|
||||||
|
|
||||||
|
[[nodiscard]] QBindable<qreal> bindableTimeout() { return &this->bTimeout; }
|
||||||
|
[[nodiscard]] QBindable<bool> bindableRespectInhibitors() { return &this->bRespectInhibitors; }
|
||||||
|
[[nodiscard]] QBindable<bool> bindableIsIdle() const { return &this->bIsIdle; }
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void enabledChanged();
|
||||||
|
void timeoutChanged();
|
||||||
|
void respectInhibitorsChanged();
|
||||||
|
void isIdleChanged();
|
||||||
|
|
||||||
|
private:
|
||||||
|
void updateNotification();
|
||||||
|
|
||||||
|
struct Params {
|
||||||
|
bool enabled;
|
||||||
|
qreal timeout;
|
||||||
|
bool respectInhibitors;
|
||||||
|
};
|
||||||
|
|
||||||
|
// clang-format off
|
||||||
|
Q_OBJECT_BINDABLE_PROPERTY_WITH_ARGS(IdleMonitor, bool, bEnabled, true, &IdleMonitor::enabledChanged);
|
||||||
|
Q_OBJECT_BINDABLE_PROPERTY(IdleMonitor, qreal, bTimeout, &IdleMonitor::timeoutChanged);
|
||||||
|
Q_OBJECT_BINDABLE_PROPERTY_WITH_ARGS(IdleMonitor, bool, bRespectInhibitors, true, &IdleMonitor::respectInhibitorsChanged);
|
||||||
|
Q_OBJECT_BINDABLE_PROPERTY(IdleMonitor, Params, bParams, &IdleMonitor::updateNotification);
|
||||||
|
Q_OBJECT_BINDABLE_PROPERTY(IdleMonitor, impl::IdleNotification*, bNotification);
|
||||||
|
Q_OBJECT_BINDABLE_PROPERTY(IdleMonitor, bool, bIsIdle, &IdleMonitor::isIdleChanged);
|
||||||
|
// clang-format on
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace qs::wayland::idle_notify
|
||||||
75
src/wayland/idle_notify/proto.cpp
Normal file
75
src/wayland/idle_notify/proto.cpp
Normal file
|
|
@ -0,0 +1,75 @@
|
||||||
|
#include "proto.hpp"
|
||||||
|
|
||||||
|
#include <private/qwaylanddisplay_p.h>
|
||||||
|
#include <private/qwaylandinputdevice_p.h>
|
||||||
|
#include <private/qwaylandintegration_p.h>
|
||||||
|
#include <qlogging.h>
|
||||||
|
#include <qloggingcategory.h>
|
||||||
|
#include <qtypes.h>
|
||||||
|
#include <qwayland-ext-idle-notify-v1.h>
|
||||||
|
#include <qwaylandclientextension.h>
|
||||||
|
#include <wayland-ext-idle-notify-v1-client-protocol.h>
|
||||||
|
|
||||||
|
#include "../../core/logcat.hpp"
|
||||||
|
|
||||||
|
namespace qs::wayland::idle_notify {
|
||||||
|
QS_LOGGING_CATEGORY(logIdleNotify, "quickshell.wayland.idle_notify", QtWarningMsg);
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace qs::wayland::idle_notify::impl {
|
||||||
|
|
||||||
|
IdleNotificationManager::IdleNotificationManager(): QWaylandClientExtensionTemplate(2) {
|
||||||
|
this->initialize();
|
||||||
|
}
|
||||||
|
|
||||||
|
IdleNotificationManager* IdleNotificationManager::instance() {
|
||||||
|
static auto* instance = new IdleNotificationManager(); // NOLINT
|
||||||
|
return instance->isInitialized() ? instance : nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
IdleNotification*
|
||||||
|
IdleNotificationManager::createIdleNotification(quint32 timeout, bool respectInhibitors) {
|
||||||
|
if (!respectInhibitors
|
||||||
|
&& this->QtWayland::ext_idle_notifier_v1::version()
|
||||||
|
< EXT_IDLE_NOTIFIER_V1_GET_INPUT_IDLE_NOTIFICATION_SINCE_VERSION)
|
||||||
|
{
|
||||||
|
qCWarning(logIdleNotify) << "Cannot ignore inhibitors for new idle notifier: Compositor does "
|
||||||
|
"not support protocol version 2.";
|
||||||
|
|
||||||
|
respectInhibitors = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto* display = QtWaylandClient::QWaylandIntegration::instance()->display();
|
||||||
|
auto* inputDevice = display->lastInputDevice();
|
||||||
|
if (inputDevice == nullptr) inputDevice = display->defaultInputDevice();
|
||||||
|
if (inputDevice == nullptr) {
|
||||||
|
qCCritical(logIdleNotify) << "Could not create idle notifier: No seat.";
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
::ext_idle_notification_v1* notification = nullptr;
|
||||||
|
if (respectInhibitors) notification = this->get_idle_notification(timeout, inputDevice->object());
|
||||||
|
else notification = this->get_input_idle_notification(timeout, inputDevice->object());
|
||||||
|
|
||||||
|
auto* wrapper = new IdleNotification(notification);
|
||||||
|
qCDebug(logIdleNotify) << "Created" << wrapper << "with timeout:" << timeout
|
||||||
|
<< "respects inhibitors:" << respectInhibitors;
|
||||||
|
return wrapper;
|
||||||
|
}
|
||||||
|
|
||||||
|
IdleNotification::~IdleNotification() {
|
||||||
|
qCDebug(logIdleNotify) << "Destroyed" << this;
|
||||||
|
if (this->isInitialized()) this->destroy();
|
||||||
|
}
|
||||||
|
|
||||||
|
void IdleNotification::ext_idle_notification_v1_idled() {
|
||||||
|
qCDebug(logIdleNotify) << this << "has been marked idle";
|
||||||
|
this->bIsIdle = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void IdleNotification::ext_idle_notification_v1_resumed() {
|
||||||
|
qCDebug(logIdleNotify) << this << "has been marked resumed";
|
||||||
|
this->bIsIdle = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace qs::wayland::idle_notify::impl
|
||||||
51
src/wayland/idle_notify/proto.hpp
Normal file
51
src/wayland/idle_notify/proto.hpp
Normal file
|
|
@ -0,0 +1,51 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <qobject.h>
|
||||||
|
#include <qproperty.h>
|
||||||
|
#include <qtclasshelpermacros.h>
|
||||||
|
#include <qtmetamacros.h>
|
||||||
|
#include <qtypes.h>
|
||||||
|
#include <qwayland-ext-idle-notify-v1.h>
|
||||||
|
#include <qwaylandclientextension.h>
|
||||||
|
#include <wayland-ext-idle-notify-v1-client-protocol.h>
|
||||||
|
|
||||||
|
#include "../../core/logcat.hpp"
|
||||||
|
|
||||||
|
namespace qs::wayland::idle_notify {
|
||||||
|
QS_DECLARE_LOGGING_CATEGORY(logIdleNotify);
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace qs::wayland::idle_notify::impl {
|
||||||
|
|
||||||
|
class IdleNotification;
|
||||||
|
|
||||||
|
class IdleNotificationManager
|
||||||
|
: public QWaylandClientExtensionTemplate<IdleNotificationManager>
|
||||||
|
, public QtWayland::ext_idle_notifier_v1 {
|
||||||
|
public:
|
||||||
|
explicit IdleNotificationManager();
|
||||||
|
IdleNotification* createIdleNotification(quint32 timeout, bool respectInhibitors);
|
||||||
|
|
||||||
|
static IdleNotificationManager* instance();
|
||||||
|
};
|
||||||
|
|
||||||
|
class IdleNotification
|
||||||
|
: public QObject
|
||||||
|
, public QtWayland::ext_idle_notification_v1 {
|
||||||
|
Q_OBJECT;
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit IdleNotification(::ext_idle_notification_v1* notification)
|
||||||
|
: QtWayland::ext_idle_notification_v1(notification) {}
|
||||||
|
|
||||||
|
~IdleNotification() override;
|
||||||
|
Q_DISABLE_COPY_MOVE(IdleNotification);
|
||||||
|
|
||||||
|
Q_OBJECT_BINDABLE_PROPERTY(IdleNotification, bool, bIsIdle);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void ext_idle_notification_v1_idled() override;
|
||||||
|
void ext_idle_notification_v1_resumed() override;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace qs::wayland::idle_notify::impl
|
||||||
44
src/wayland/idle_notify/test/manual/idle_notify.qml
Normal file
44
src/wayland/idle_notify/test/manual/idle_notify.qml
Normal file
|
|
@ -0,0 +1,44 @@
|
||||||
|
import QtQuick
|
||||||
|
import QtQuick.Controls
|
||||||
|
import QtQuick.Layouts
|
||||||
|
import Quickshell
|
||||||
|
import Quickshell.Wayland
|
||||||
|
|
||||||
|
FloatingWindow {
|
||||||
|
color: contentItem.palette.window
|
||||||
|
|
||||||
|
IdleMonitor {
|
||||||
|
id: monitor
|
||||||
|
enabled: enabledCb.checked
|
||||||
|
timeout: timeoutSb.value
|
||||||
|
respectInhibitors: respectInhibitorsCb.checked
|
||||||
|
}
|
||||||
|
|
||||||
|
ColumnLayout {
|
||||||
|
Label { text: `Is idle? ${monitor.isIdle}` }
|
||||||
|
|
||||||
|
CheckBox {
|
||||||
|
id: enabledCb
|
||||||
|
text: "Enabled"
|
||||||
|
checked: true
|
||||||
|
}
|
||||||
|
|
||||||
|
CheckBox {
|
||||||
|
id: respectInhibitorsCb
|
||||||
|
text: "Respect Inhibitors"
|
||||||
|
checked: true
|
||||||
|
}
|
||||||
|
|
||||||
|
RowLayout {
|
||||||
|
Label { text: "Timeout" }
|
||||||
|
|
||||||
|
SpinBox {
|
||||||
|
id: timeoutSb
|
||||||
|
editable: true
|
||||||
|
from: 0
|
||||||
|
to: 1000
|
||||||
|
value: 5
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -6,5 +6,6 @@ headers = [
|
||||||
"toplevel_management/qml.hpp",
|
"toplevel_management/qml.hpp",
|
||||||
"screencopy/view.hpp",
|
"screencopy/view.hpp",
|
||||||
"idle_inhibit/inhibitor.hpp",
|
"idle_inhibit/inhibitor.hpp",
|
||||||
|
"idle_notify/monitor.hpp",
|
||||||
]
|
]
|
||||||
-----
|
-----
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue