Skip to content

Commit a7aa932

Browse files
committed
wayland/idle-notify: add idle notify
1 parent 2c29834 commit a7aa932

File tree

8 files changed

+311
-0
lines changed

8 files changed

+311
-0
lines changed

src/wayland/CMakeLists.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,9 @@ endif()
117117
add_subdirectory(idle_inhibit)
118118
list(APPEND WAYLAND_MODULES Quickshell.Wayland._IdleInhibitor)
119119

120+
add_subdirectory(idle_notify)
121+
list(APPEND WAYLAND_MODULES Quickshell.Wayland._IdleNotify)
122+
120123
# widgets for qmenu
121124
target_link_libraries(quickshell-wayland PRIVATE
122125
Qt::Quick Qt::Widgets Qt::WaylandClient Qt::WaylandClientPrivate
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
qt_add_library(quickshell-wayland-idle-notify STATIC
2+
proto.cpp
3+
monitor.cpp
4+
)
5+
6+
qt_add_qml_module(quickshell-wayland-idle-notify
7+
URI Quickshell.Wayland._IdleNotify
8+
VERSION 0.1
9+
DEPENDENCIES QtQuick
10+
)
11+
12+
install_qml_module(quickshell-wayland-idle-notify)
13+
14+
qs_add_module_deps_light(quickshell-wayland-idle-notify Quickshell)
15+
16+
wl_proto(wlp-idle-notify ext-idle-notify-v1 "${WAYLAND_PROTOCOLS}/staging/ext-idle-notify")
17+
18+
target_link_libraries(quickshell-wayland-idle-notify PRIVATE
19+
Qt::Quick Qt::WaylandClient Qt::WaylandClientPrivate wayland-client
20+
wlp-idle-notify
21+
)
22+
23+
qs_module_pch(quickshell-wayland-idle-notify SET large)
24+
25+
target_link_libraries(quickshell PRIVATE quickshell-wayland-idle-notifyplugin)

src/wayland/idle_notify/monitor.cpp

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
#include "monitor.hpp"
2+
#include <algorithm>
3+
4+
#include <qlogging.h>
5+
#include <qscopeguard.h>
6+
#include <qtypes.h>
7+
8+
#include "proto.hpp"
9+
10+
namespace qs::wayland::idle_notify {
11+
12+
IdleMonitor::IdleMonitor() {
13+
this->bParams.setBinding([this] {
14+
return Params {
15+
.enabled = this->bEnabled.value(),
16+
.timeout = this->bTimeout.value(),
17+
.respectInhibitors = this->bRespectInhibitors.value()
18+
};
19+
});
20+
21+
this->bIsIdle.setBinding([this] {
22+
auto* notification = this->bNotification.value();
23+
return notification ? notification->bIsIdle.value() : false;
24+
});
25+
}
26+
27+
IdleMonitor::~IdleMonitor() { delete this->bNotification.value(); }
28+
29+
void IdleMonitor::updateNotification() {
30+
auto* notification = this->bNotification.value();
31+
delete notification;
32+
notification = nullptr;
33+
34+
auto guard = qScopeGuard([&] { this->bNotification = notification; });
35+
36+
auto params = this->bParams.value();
37+
38+
if (params.enabled) {
39+
auto* manager = impl::IdleNotificationManager::instance();
40+
41+
if (!manager) {
42+
qWarning() << "Cannot create idle monitor as ext-idle-notify-v1 is not supported by the "
43+
"current compositor.";
44+
return;
45+
}
46+
47+
auto timeout = static_cast<quint32>(std::max(0, static_cast<int>(params.timeout * 1000)));
48+
notification = manager->createIdleNotification(timeout, params.respectInhibitors);
49+
}
50+
}
51+
52+
} // namespace qs::wayland::idle_notify

src/wayland/idle_notify/monitor.hpp

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
#pragma once
2+
3+
#include <qobject.h>
4+
#include <qproperty.h>
5+
#include <qqmlintegration.h>
6+
#include <qtclasshelpermacros.h>
7+
#include <qtmetamacros.h>
8+
#include <qtypes.h>
9+
10+
#include "proto.hpp"
11+
12+
namespace qs::wayland::idle_notify {
13+
14+
class IdleMonitor: public QObject {
15+
Q_OBJECT;
16+
QML_ELEMENT;
17+
// clang-format off
18+
Q_PROPERTY(bool enabled READ isEnabled WRITE setEnabled NOTIFY enabledChanged);
19+
Q_PROPERTY(qreal timeout READ default WRITE default NOTIFY timeoutChanged BINDABLE bindableTimeout);
20+
Q_PROPERTY(bool respectInhibitors READ default WRITE default NOTIFY respectInhibitorsChanged BINDABLE bindableRespectInhibitors);
21+
Q_PROPERTY(bool isIdle READ default NOTIFY isIdleChanged BINDABLE bindableIsIdle);
22+
// clang-format on
23+
24+
public:
25+
IdleMonitor();
26+
~IdleMonitor() override;
27+
Q_DISABLE_COPY_MOVE(IdleMonitor);
28+
29+
[[nodiscard]] bool isEnabled() const { return this->bNotification.value(); }
30+
void setEnabled(bool enabled) { this->bEnabled = enabled; }
31+
32+
[[nodiscard]] QBindable<qreal> bindableTimeout() { return &this->bTimeout; }
33+
[[nodiscard]] QBindable<bool> bindableRespectInhibitors() { return &this->bRespectInhibitors; }
34+
[[nodiscard]] QBindable<bool> bindableIsIdle() const { return &this->bIsIdle; }
35+
36+
signals:
37+
void enabledChanged();
38+
void timeoutChanged();
39+
void respectInhibitorsChanged();
40+
void isIdleChanged();
41+
42+
private:
43+
void updateNotification();
44+
45+
struct Params {
46+
bool enabled;
47+
qreal timeout;
48+
bool respectInhibitors;
49+
};
50+
51+
// clang-format off
52+
Q_OBJECT_BINDABLE_PROPERTY(IdleMonitor, bool, bEnabled, &IdleMonitor::enabledChanged);
53+
Q_OBJECT_BINDABLE_PROPERTY(IdleMonitor, qreal, bTimeout, &IdleMonitor::timeoutChanged);
54+
Q_OBJECT_BINDABLE_PROPERTY_WITH_ARGS(IdleMonitor, bool, bRespectInhibitors, true, &IdleMonitor::respectInhibitorsChanged);
55+
Q_OBJECT_BINDABLE_PROPERTY(IdleMonitor, Params, bParams, &IdleMonitor::updateNotification);
56+
Q_OBJECT_BINDABLE_PROPERTY(IdleMonitor, impl::IdleNotification*, bNotification);
57+
Q_OBJECT_BINDABLE_PROPERTY(IdleMonitor, bool, bIsIdle, &IdleMonitor::isIdleChanged);
58+
// clang-format on
59+
};
60+
61+
} // namespace qs::wayland::idle_notify

src/wayland/idle_notify/proto.cpp

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
#include "proto.hpp"
2+
3+
#include <private/qwaylanddisplay_p.h>
4+
#include <private/qwaylandinputdevice_p.h>
5+
#include <private/qwaylandintegration_p.h>
6+
#include <qlogging.h>
7+
#include <qloggingcategory.h>
8+
#include <qtypes.h>
9+
#include <qwayland-ext-idle-notify-v1.h>
10+
#include <qwaylandclientextension.h>
11+
#include <wayland-ext-idle-notify-v1-client-protocol.h>
12+
13+
#include "../../core/logcat.hpp"
14+
15+
namespace qs::wayland::idle_notify {
16+
QS_LOGGING_CATEGORY(logIdleNotify, "quickshell.wayland.idle_notify", QtWarningMsg);
17+
}
18+
19+
namespace qs::wayland::idle_notify::impl {
20+
21+
IdleNotificationManager::IdleNotificationManager(): QWaylandClientExtensionTemplate(2) {
22+
this->initialize();
23+
}
24+
25+
IdleNotificationManager* IdleNotificationManager::instance() {
26+
static auto* instance = new IdleNotificationManager(); // NOLINT
27+
return instance->isInitialized() ? instance : nullptr;
28+
}
29+
30+
IdleNotification*
31+
IdleNotificationManager::createIdleNotification(quint32 timeout, bool respectInhibitors) {
32+
if (!respectInhibitors
33+
&& this->QtWayland::ext_idle_notifier_v1::version()
34+
< EXT_IDLE_NOTIFIER_V1_GET_INPUT_IDLE_NOTIFICATION_SINCE_VERSION)
35+
{
36+
qCWarning(logIdleNotify) << "Cannot ignore inhibitors for new idle notifier: Compositor does "
37+
"not support protocol version 2.";
38+
39+
respectInhibitors = true;
40+
}
41+
42+
auto* display = QtWaylandClient::QWaylandIntegration::instance()->display();
43+
auto* inputDevice = display->lastInputDevice();
44+
if (inputDevice == nullptr) inputDevice = display->defaultInputDevice();
45+
if (inputDevice == nullptr) {
46+
qCCritical(logIdleNotify) << "Could not create idle notifier: No seat.";
47+
return nullptr;
48+
}
49+
50+
::ext_idle_notification_v1* notification = nullptr;
51+
if (respectInhibitors) notification = this->get_idle_notification(timeout, inputDevice->object());
52+
else notification = this->get_input_idle_notification(timeout, inputDevice->object());
53+
54+
auto* wrapper = new IdleNotification(notification);
55+
qCDebug(logIdleNotify) << "Created" << wrapper << "with timeout:" << timeout
56+
<< "respects inhibitors:" << respectInhibitors;
57+
return wrapper;
58+
}
59+
60+
IdleNotification::~IdleNotification() {
61+
qCDebug(logIdleNotify) << "Destroyed" << this;
62+
if (this->isInitialized()) this->destroy();
63+
}
64+
65+
void IdleNotification::ext_idle_notification_v1_idled() {
66+
qCDebug(logIdleNotify) << this << "has been marked idle";
67+
this->bIsIdle = true;
68+
}
69+
70+
void IdleNotification::ext_idle_notification_v1_resumed() {
71+
qCDebug(logIdleNotify) << this << "has been marked resumed";
72+
this->bIsIdle = false;
73+
}
74+
75+
} // namespace qs::wayland::idle_notify::impl

src/wayland/idle_notify/proto.hpp

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
#pragma once
2+
3+
#include <qobject.h>
4+
#include <qproperty.h>
5+
#include <qtclasshelpermacros.h>
6+
#include <qtmetamacros.h>
7+
#include <qtypes.h>
8+
#include <qwayland-ext-idle-notify-v1.h>
9+
#include <qwaylandclientextension.h>
10+
#include <wayland-ext-idle-notify-v1-client-protocol.h>
11+
12+
#include "../../core/logcat.hpp"
13+
14+
namespace qs::wayland::idle_notify {
15+
QS_DECLARE_LOGGING_CATEGORY(logIdleNotify);
16+
}
17+
18+
namespace qs::wayland::idle_notify::impl {
19+
20+
class IdleNotification;
21+
22+
class IdleNotificationManager
23+
: public QWaylandClientExtensionTemplate<IdleNotificationManager>
24+
, public QtWayland::ext_idle_notifier_v1 {
25+
public:
26+
explicit IdleNotificationManager();
27+
IdleNotification* createIdleNotification(quint32 timeout, bool respectInhibitors);
28+
29+
static IdleNotificationManager* instance();
30+
};
31+
32+
class IdleNotification
33+
: public QObject
34+
, public QtWayland::ext_idle_notification_v1 {
35+
Q_OBJECT;
36+
37+
public:
38+
explicit IdleNotification(::ext_idle_notification_v1* notification)
39+
: QtWayland::ext_idle_notification_v1(notification) {}
40+
41+
~IdleNotification() override;
42+
Q_DISABLE_COPY_MOVE(IdleNotification);
43+
44+
Q_OBJECT_BINDABLE_PROPERTY(IdleNotification, bool, bIsIdle);
45+
46+
protected:
47+
void ext_idle_notification_v1_idled() override;
48+
void ext_idle_notification_v1_resumed() override;
49+
};
50+
51+
} // namespace qs::wayland::idle_notify::impl
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
import QtQuick
2+
import QtQuick.Controls
3+
import QtQuick.Layouts
4+
import Quickshell
5+
import Quickshell.Wayland
6+
7+
FloatingWindow {
8+
color: contentItem.palette.window
9+
10+
IdleMonitor {
11+
id: monitor
12+
enabled: enabledCb.checked
13+
timeout: timeoutSb.value
14+
respectInhibitors: respectInhibitorsCb.checked
15+
}
16+
17+
ColumnLayout {
18+
Label { text: `Is idle? ${monitor.isIdle}` }
19+
20+
CheckBox {
21+
id: enabledCb
22+
text: "Enabled"
23+
}
24+
25+
CheckBox {
26+
id: respectInhibitorsCb
27+
text: "Respect Inhibitors"
28+
checked: true
29+
}
30+
31+
RowLayout {
32+
Label { text: "Timeout" }
33+
34+
SpinBox {
35+
id: timeoutSb
36+
editable: true
37+
from: 0
38+
to: 1000
39+
value: 5
40+
}
41+
}
42+
}
43+
}

src/wayland/module.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,5 +6,6 @@ headers = [
66
"toplevel_management/qml.hpp",
77
"screencopy/view.hpp",
88
"idle_inhibit/inhibitor.hpp",
9+
"idle_notify/monitor.hpp",
910
]
1011
-----

0 commit comments

Comments
 (0)