File indexing completed on 2024-04-28 15:25:35

0001 /*
0002     SPDX-FileCopyrightText: 2021 David Edmundson <davidedmundson@kde.org>
0003 
0004     SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
0005 */
0006 #include "poller.h"
0007 
0008 #include <QDebug>
0009 #include <QGuiApplication>
0010 #include <QLoggingCategory>
0011 #include <QWaylandClientExtensionTemplate>
0012 #include <QtWaylandClient/QtWaylandClientVersion>
0013 
0014 #include <qpa/qplatformnativeinterface.h>
0015 
0016 #include "qwayland-ext-idle-notify-v1.h"
0017 #include "qwayland-idle.h"
0018 
0019 Q_DECLARE_LOGGING_CATEGORY(POLLER)
0020 Q_LOGGING_CATEGORY(POLLER, "kf5idletime_wayland")
0021 
0022 /*
0023  * Porting notes:
0024  * org_kde_kwin_idle refers to an early specific idle timeout protocol
0025  * the version ext_idle refers to an upstream stable protocol
0026  *
0027  * Pragmattically they're both the same, but we have to have two implementations for a while
0028  *
0029  * 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
0030  */
0031 
0032 class IdleTimeout : public QObject
0033 {
0034     Q_OBJECT
0035 public:
0036     IdleTimeout() = default;
0037 Q_SIGNALS:
0038     void idle();
0039     void resumeFromIdle();
0040 };
0041 
0042 class IdleTimeoutKwin : public IdleTimeout, public QtWayland::org_kde_kwin_idle_timeout
0043 {
0044     Q_OBJECT
0045 public:
0046     IdleTimeoutKwin(struct ::org_kde_kwin_idle_timeout *object)
0047         : IdleTimeout()
0048         , QtWayland::org_kde_kwin_idle_timeout(object)
0049     {}
0050 
0051     ~IdleTimeoutKwin()
0052     {
0053         if (qGuiApp) {
0054             release();
0055         }
0056     }
0057 
0058 protected:
0059     void org_kde_kwin_idle_timeout_idle() override {
0060         Q_EMIT idle();
0061     }
0062     void org_kde_kwin_idle_timeout_resumed() override {
0063         Q_EMIT resumeFromIdle();
0064     }
0065 };
0066 
0067 class IdleTimeoutExt : public IdleTimeout, public QtWayland::ext_idle_notification_v1
0068 {
0069     Q_OBJECT
0070 public:
0071     IdleTimeoutExt(struct ::ext_idle_notification_v1 *object)
0072         : IdleTimeout()
0073         , QtWayland::ext_idle_notification_v1(object)
0074     {
0075     }
0076 
0077     ~IdleTimeoutExt()
0078     {
0079         if (qGuiApp) {
0080             destroy();
0081         }
0082     }
0083 
0084 protected:
0085     void ext_idle_notification_v1_idled() override
0086     {
0087         Q_EMIT idle();
0088     }
0089     void ext_idle_notification_v1_resumed() override
0090     {
0091         Q_EMIT resumeFromIdle();
0092     }
0093 };
0094 
0095 class IdleManagerKwin : public QWaylandClientExtensionTemplate<IdleManagerKwin>, public QtWayland::org_kde_kwin_idle
0096 {
0097 public:
0098     IdleManagerKwin()
0099         : QWaylandClientExtensionTemplate<IdleManagerKwin>(1)
0100     {
0101 #if QTWAYLANDCLIENT_VERSION >= QT_VERSION_CHECK(6, 2, 0)
0102         initialize();
0103 #else
0104         // QWaylandClientExtensionTemplate invokes this with a QueuedConnection but we want shortcuts
0105         // to be inhibited immediately.
0106         QMetaObject::invokeMethod(this, "addRegistryListener");
0107 #endif
0108     }
0109 };
0110 
0111 class IdleManagerExt : public QWaylandClientExtensionTemplate<IdleManagerExt>, public QtWayland::ext_idle_notifier_v1
0112 {
0113 public:
0114     IdleManagerExt()
0115         : QWaylandClientExtensionTemplate<IdleManagerExt>(1)
0116     {
0117 #if QTWAYLANDCLIENT_VERSION >= QT_VERSION_CHECK(6, 2, 0)
0118         initialize();
0119 #else
0120         // QWaylandClientExtensionTemplate invokes this with a QueuedConnection but we want shortcuts
0121         // to be inhibited immediately.
0122         QMetaObject::invokeMethod(this, "addRegistryListener");
0123 #endif
0124     }
0125     ~IdleManagerExt()
0126     {
0127         if (qGuiApp && isActive()) {
0128             destroy();
0129         }
0130     }
0131 };
0132 
0133 Poller::Poller(QObject *parent)
0134     : AbstractSystemPoller(parent)
0135     , m_idleManagerKwin(new IdleManagerKwin)
0136     , m_idleManagerExt(new IdleManagerExt)
0137 {
0138 }
0139 
0140 Poller::~Poller() = default;
0141 
0142 bool Poller::isAvailable()
0143 {
0144     return m_idleManagerKwin->isActive() || m_idleManagerExt->isActive();
0145 }
0146 
0147 void Poller::addTimeout(int nextTimeout)
0148 {
0149     if (m_timeouts.contains(nextTimeout)) {
0150         return;
0151     }
0152 
0153     auto timeout = createTimeout(nextTimeout);
0154     if (!timeout) {
0155         return;
0156     }
0157 
0158     connect(timeout, &IdleTimeout::idle, this, [this, nextTimeout] {
0159         Q_EMIT timeoutReached(nextTimeout);
0160     });
0161     connect(timeout, &IdleTimeout::resumeFromIdle, this, &Poller::resumingFromIdle);
0162     m_timeouts.insert(nextTimeout, QSharedPointer<IdleTimeout>(timeout));
0163 }
0164 
0165 void Poller::removeTimeout(int nextTimeout)
0166 {
0167     m_timeouts.remove(nextTimeout);
0168 }
0169 
0170 QList<int> Poller::timeouts() const
0171 {
0172     return QList<int>();
0173 }
0174 
0175 void Poller::catchIdleEvent()
0176 {
0177     if (m_catchResumeTimeout) {
0178         // already setup
0179         return;
0180     }
0181     if (!isAvailable()) {
0182         return;
0183     }
0184 
0185     m_catchResumeTimeout.reset(createTimeout(0));
0186     if (!m_catchResumeTimeout) {
0187         return;
0188     }
0189     connect(m_catchResumeTimeout.get(), &IdleTimeout::resumeFromIdle, this, [this] {
0190         stopCatchingIdleEvents();
0191         Q_EMIT resumingFromIdle();
0192     });
0193 }
0194 
0195 void Poller::stopCatchingIdleEvents()
0196 {
0197     m_catchResumeTimeout.reset();
0198 }
0199 
0200 int Poller::forcePollRequest()
0201 {
0202     qCWarning(POLLER) << "This plugin does not support polling idle time";
0203     return 0;
0204 }
0205 
0206 void Poller::simulateUserActivity()
0207 {
0208 }
0209 
0210 IdleTimeout* Poller::createTimeout(int timeout)
0211 {
0212     QPlatformNativeInterface *nativeInterface = qGuiApp->platformNativeInterface();
0213     if (!nativeInterface) {
0214         return nullptr;
0215     }
0216     auto seat = static_cast<wl_seat *>(nativeInterface->nativeResourceForIntegration("wl_seat"));
0217     if (!seat) {
0218         return nullptr;
0219     }
0220 
0221     if (m_idleManagerExt->isActive()) {
0222         return new IdleTimeoutExt(m_idleManagerExt->get_idle_notification(timeout, seat));
0223     }
0224     if (m_idleManagerKwin->isActive()) {
0225         return new IdleTimeoutKwin(m_idleManagerKwin->get_idle_timeout(seat, timeout));
0226     }
0227     return nullptr;
0228 }
0229 
0230 #include "moc_poller.cpp"
0231 #include "poller.moc"