1/*
2 SPDX-FileCopyrightText: 2021 David Edmundson <davidedmundson@kde.org>
3
4 SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
5*/
6#include "poller.h"
7
8#include <QDebug>
9#include <QGuiApplication>
10#include <QLoggingCategory>
11#include <QWaylandClientExtensionTemplate>
12#include <QtWaylandClientVersion>
13
14#include <qpa/qplatformnativeinterface.h>
15
16#include "qwayland-ext-idle-notify-v1.h"
17#include "qwayland-idle.h"
18
19Q_DECLARE_LOGGING_CATEGORY(POLLER)
20Q_LOGGING_CATEGORY(POLLER, "kf5idletime_wayland")
21
22/*
23 * Porting notes:
24 * org_kde_kwin_idle refers to an early specific idle timeout protocol
25 * the version ext_idle refers to an upstream stable protocol
26 *
27 * Pragmattically they're both the same, but we have to have two implementations for a while
28 *
29 * When a suitable amount of time passes (Plasma 5.24 being EOL) drop IdleTimeoutKwin and drop IdleManagerKwin as well as merge the abstract IdleTimeout class into the real implementation
30 */
31
32class IdleTimeout : public QObject
33{
34 Q_OBJECT
35public:
36 IdleTimeout() = default;
37Q_SIGNALS:
38 void idle();
39 void resumeFromIdle();
40};
41
42class IdleTimeoutKwin : public IdleTimeout, public QtWayland::org_kde_kwin_idle_timeout
43{
44 Q_OBJECT
45public:
46 IdleTimeoutKwin(struct ::org_kde_kwin_idle_timeout *object)
47 : IdleTimeout()
48 , QtWayland::org_kde_kwin_idle_timeout(object)
49 {}
50
51 ~IdleTimeoutKwin()
52 {
53 if (qGuiApp) {
54 release();
55 }
56 }
57
58protected:
59 void org_kde_kwin_idle_timeout_idle() override {
60 Q_EMIT idle();
61 }
62 void org_kde_kwin_idle_timeout_resumed() override {
63 Q_EMIT resumeFromIdle();
64 }
65};
66
67class IdleTimeoutExt : public IdleTimeout, public QtWayland::ext_idle_notification_v1
68{
69 Q_OBJECT
70public:
71 IdleTimeoutExt(struct ::ext_idle_notification_v1 *object)
72 : IdleTimeout()
73 , QtWayland::ext_idle_notification_v1(object)
74 {
75 }
76
77 ~IdleTimeoutExt()
78 {
79 if (qGuiApp) {
80 destroy();
81 }
82 }
83
84protected:
85 void ext_idle_notification_v1_idled() override
86 {
87 Q_EMIT idle();
88 }
89 void ext_idle_notification_v1_resumed() override
90 {
91 Q_EMIT resumeFromIdle();
92 }
93};
94
95class IdleManagerKwin : public QWaylandClientExtensionTemplate<IdleManagerKwin>, public QtWayland::org_kde_kwin_idle
96{
97public:
98 IdleManagerKwin()
99 : QWaylandClientExtensionTemplate<IdleManagerKwin>(1)
100 {
101 initialize();
102 }
103};
104
105class IdleManagerExt : public QWaylandClientExtensionTemplate<IdleManagerExt>, public QtWayland::ext_idle_notifier_v1
106{
107public:
108 IdleManagerExt()
109 : QWaylandClientExtensionTemplate<IdleManagerExt>(1)
110 {
111 initialize();
112 }
113 ~IdleManagerExt()
114 {
115 if (qGuiApp && isActive()) {
116 destroy();
117 }
118 }
119};
120
121Poller::Poller(QObject *parent)
122 : KAbstractIdleTimePoller(parent)
123 , m_idleManagerKwin(new IdleManagerKwin)
124 , m_idleManagerExt(new IdleManagerExt)
125{
126}
127
128Poller::~Poller() = default;
129
130bool Poller::isAvailable()
131{
132 return m_idleManagerKwin->isActive() || m_idleManagerExt->isActive();
133}
134
135void Poller::addTimeout(int nextTimeout)
136{
137 if (m_timeouts.contains(key: nextTimeout)) {
138 return;
139 }
140
141 auto timeout = createTimeout(timeout: nextTimeout);
142 if (!timeout) {
143 return;
144 }
145
146 connect(sender: timeout, signal: &IdleTimeout::idle, context: this, slot: [this, nextTimeout] {
147 Q_EMIT timeoutReached(msec: nextTimeout);
148 });
149 connect(sender: timeout, signal: &IdleTimeout::resumeFromIdle, context: this, slot: &Poller::resumingFromIdle);
150 m_timeouts.insert(key: nextTimeout, value: QSharedPointer<IdleTimeout>(timeout));
151}
152
153void Poller::removeTimeout(int nextTimeout)
154{
155 m_timeouts.remove(key: nextTimeout);
156}
157
158QList<int> Poller::timeouts() const
159{
160 return QList<int>();
161}
162
163void Poller::catchIdleEvent()
164{
165 if (m_catchResumeTimeout) {
166 // already setup
167 return;
168 }
169 if (!isAvailable()) {
170 return;
171 }
172
173 m_catchResumeTimeout.reset(other: createTimeout(timeout: 0));
174 if (!m_catchResumeTimeout) {
175 return;
176 }
177 connect(sender: m_catchResumeTimeout.get(), signal: &IdleTimeout::resumeFromIdle, context: this, slot: [this] {
178 stopCatchingIdleEvents();
179 Q_EMIT resumingFromIdle();
180 });
181}
182
183void Poller::stopCatchingIdleEvents()
184{
185 m_catchResumeTimeout.reset();
186}
187
188int Poller::forcePollRequest()
189{
190 qCWarning(POLLER) << "This plugin does not support polling idle time";
191 return 0;
192}
193
194void Poller::simulateUserActivity()
195{
196}
197
198IdleTimeout* Poller::createTimeout(int timeout)
199{
200 QPlatformNativeInterface *nativeInterface = qGuiApp->platformNativeInterface();
201 if (!nativeInterface) {
202 return nullptr;
203 }
204 auto seat = static_cast<wl_seat *>(nativeInterface->nativeResourceForIntegration(resource: "wl_seat"));
205 if (!seat) {
206 return nullptr;
207 }
208
209 if (m_idleManagerExt->isActive()) {
210 return new IdleTimeoutExt(m_idleManagerExt->get_idle_notification(timeout, seat));
211 }
212 if (m_idleManagerKwin->isActive()) {
213 return new IdleTimeoutKwin(m_idleManagerKwin->get_idle_timeout(seat, timeout));
214 }
215 return nullptr;
216}
217
218#include "moc_poller.cpp"
219#include "poller.moc"
220

source code of kidletime/src/plugins/wayland/poller.cpp