diff --git a/autotests/integration/idle_inhibition_test.cpp b/autotests/integration/idle_inhibition_test.cpp index ca14deb8ed..13dc642eb9 100644 --- a/autotests/integration/idle_inhibition_test.cpp +++ b/autotests/integration/idle_inhibition_test.cpp @@ -10,8 +10,6 @@ #include "platform.h" #include "virtualdesktops.h" -#include "wayland/display.h" -#include "wayland/idle_interface.h" #include "wayland_server.h" #include "window.h" #include "workspace.h" @@ -20,7 +18,6 @@ using namespace KWin; using namespace KWayland::Client; -using KWaylandServer::IdleInterface; static const QString s_socketName = QStringLiteral("wayland_test_kwin_idle_inhbition_test-0"); @@ -69,11 +66,8 @@ void TestIdleInhibition::cleanup() void TestIdleInhibition::testInhibit() { - auto idle = waylandServer()->display()->findChild(); - QVERIFY(idle); - QVERIFY(!idle->isInhibited()); - QSignalSpy inhibitedSpy(idle, &IdleInterface::inhibitedChanged); - QVERIFY(inhibitedSpy.isValid()); + // no idle inhibitors at the start + QCOMPARE(input()->idleInhibitors(), QList{}); // now create window QScopedPointer surface(Test::createSurface()); @@ -88,22 +82,23 @@ void TestIdleInhibition::testInhibit() QVERIFY(window); // this should inhibit our server object - QVERIFY(idle->isInhibited()); + QCOMPARE(input()->idleInhibitors(), QList{window}); // deleting the object should uninhibit again inhibitor.reset(); - QVERIFY(inhibitedSpy.wait()); - QVERIFY(!idle->isInhibited()); + Test::flushWaylandConnection(); // don't use QTRY_COMPARE(), it doesn't spin event loop + QGuiApplication::processEvents(); + QCOMPARE(input()->idleInhibitors(), QList{}); // inhibit again and destroy window QScopedPointer inhibitor2(Test::createIdleInhibitorV1(surface.data())); - QVERIFY(inhibitedSpy.wait()); - QVERIFY(idle->isInhibited()); + Test::flushWaylandConnection(); + QGuiApplication::processEvents(); + QCOMPARE(input()->idleInhibitors(), QList{window}); shellSurface.reset(); QVERIFY(Test::waitForWindowDestroyed(window)); - QTRY_VERIFY(!idle->isInhibited()); - QCOMPARE(inhibitedSpy.count(), 4); + QCOMPARE(input()->idleInhibitors(), QList{}); } void TestIdleInhibition::testDontInhibitWhenNotOnCurrentDesktop() @@ -114,13 +109,6 @@ void TestIdleInhibition::testDontInhibitWhenNotOnCurrentDesktop() VirtualDesktopManager::self()->setCount(2); QCOMPARE(VirtualDesktopManager::self()->count(), 2u); - // Get reference to the idle interface. - auto idle = waylandServer()->display()->findChild(); - QVERIFY(idle); - QVERIFY(!idle->isInhibited()); - QSignalSpy inhibitedSpy(idle, &IdleInterface::inhibitedChanged); - QVERIFY(inhibitedSpy.isValid()); - // Create the test window. QScopedPointer surface(Test::createSurface()); QVERIFY(!surface.isNull()); @@ -140,30 +128,26 @@ void TestIdleInhibition::testDontInhibitWhenNotOnCurrentDesktop() QCOMPARE(window->desktops().first(), VirtualDesktopManager::self()->desktops().first()); // This should inhibit our server object. - QVERIFY(idle->isInhibited()); - QCOMPARE(inhibitedSpy.count(), 1); + QCOMPARE(input()->idleInhibitors(), QList{window}); // Switch to the second virtual desktop. VirtualDesktopManager::self()->setCurrent(2); // The surface is no longer visible, so the compositor don't have to honor the // idle inhibitor object. - QVERIFY(!idle->isInhibited()); - QCOMPARE(inhibitedSpy.count(), 2); + QCOMPARE(input()->idleInhibitors(), QList{}); // Switch back to the first virtual desktop. VirtualDesktopManager::self()->setCurrent(1); // The test window became visible again, so the compositor has to honor the idle // inhibitor object back again. - QVERIFY(idle->isInhibited()); - QCOMPARE(inhibitedSpy.count(), 3); + QCOMPARE(input()->idleInhibitors(), QList{window}); // Destroy the test window. shellSurface.reset(); QVERIFY(Test::waitForWindowDestroyed(window)); - QTRY_VERIFY(!idle->isInhibited()); - QCOMPARE(inhibitedSpy.count(), 4); + QCOMPARE(input()->idleInhibitors(), QList{}); } void TestIdleInhibition::testDontInhibitWhenMinimized() @@ -171,13 +155,6 @@ void TestIdleInhibition::testDontInhibitWhenMinimized() // This test verifies that the idle inhibitor object is not honored when the // associated surface is minimized. - // Get reference to the idle interface. - auto idle = waylandServer()->display()->findChild(); - QVERIFY(idle); - QVERIFY(!idle->isInhibited()); - QSignalSpy inhibitedSpy(idle, &IdleInterface::inhibitedChanged); - QVERIFY(inhibitedSpy.isValid()); - // Create the test window. QScopedPointer surface(Test::createSurface()); QVERIFY(!surface.isNull()); @@ -193,24 +170,20 @@ void TestIdleInhibition::testDontInhibitWhenMinimized() QVERIFY(window); // This should inhibit our server object. - QVERIFY(idle->isInhibited()); - QCOMPARE(inhibitedSpy.count(), 1); + QCOMPARE(input()->idleInhibitors(), QList{window}); // Minimize the window, the idle inhibitor object should not be honored. window->minimize(); - QVERIFY(!idle->isInhibited()); - QCOMPARE(inhibitedSpy.count(), 2); + QCOMPARE(input()->idleInhibitors(), QList{}); // Unminimize the window, the idle inhibitor object should be honored back again. window->unminimize(); - QVERIFY(idle->isInhibited()); - QCOMPARE(inhibitedSpy.count(), 3); + QCOMPARE(input()->idleInhibitors(), QList{window}); // Destroy the test window. shellSurface.reset(); QVERIFY(Test::waitForWindowDestroyed(window)); - QTRY_VERIFY(!idle->isInhibited()); - QCOMPARE(inhibitedSpy.count(), 4); + QCOMPARE(input()->idleInhibitors(), QList{}); } void TestIdleInhibition::testDontInhibitWhenUnmapped() @@ -218,13 +191,6 @@ void TestIdleInhibition::testDontInhibitWhenUnmapped() // This test verifies that the idle inhibitor object is not honored by KWin // when the associated window is unmapped. - // Get reference to the idle interface. - auto idle = waylandServer()->display()->findChild(); - QVERIFY(idle); - QVERIFY(!idle->isInhibited()); - QSignalSpy inhibitedSpy(idle, &IdleInterface::inhibitedChanged); - QVERIFY(inhibitedSpy.isValid()); - // Create the test window. QScopedPointer surface(Test::createSurface()); QVERIFY(!surface.isNull()); @@ -253,8 +219,7 @@ void TestIdleInhibition::testDontInhibitWhenUnmapped() QCOMPARE(surfaceConfigureRequestedSpy.count(), 1); // This should inhibit our server object. - QVERIFY(idle->isInhibited()); - QCOMPARE(inhibitedSpy.count(), 1); + QCOMPARE(input()->idleInhibitors(), QList{window}); // Unmap the window. surface->attachBuffer(Buffer::Ptr()); @@ -263,8 +228,7 @@ void TestIdleInhibition::testDontInhibitWhenUnmapped() // The surface is no longer visible, so the compositor doesn't have to honor the // idle inhibitor object. - QVERIFY(!idle->isInhibited()); - QCOMPARE(inhibitedSpy.count(), 2); + QCOMPARE(input()->idleInhibitors(), QList{}); // Tell the compositor that we want to map the surface. surface->commit(KWayland::Client::Surface::CommitFlag::None); @@ -283,14 +247,12 @@ void TestIdleInhibition::testDontInhibitWhenUnmapped() // The test window became visible again, so the compositor has to honor the idle // inhibitor object back again. - QVERIFY(idle->isInhibited()); - QCOMPARE(inhibitedSpy.count(), 3); + QCOMPARE(input()->idleInhibitors(), QList{window}); // Destroy the test window. shellSurface.reset(); QVERIFY(Test::waitForWindowDestroyed(window)); - QTRY_VERIFY(!idle->isInhibited()); - QCOMPARE(inhibitedSpy.count(), 4); + QCOMPARE(input()->idleInhibitors(), QList{}); } void TestIdleInhibition::testDontInhibitWhenLeftCurrentDesktop() @@ -301,13 +263,6 @@ void TestIdleInhibition::testDontInhibitWhenLeftCurrentDesktop() VirtualDesktopManager::self()->setCount(2); QCOMPARE(VirtualDesktopManager::self()->count(), 2u); - // Get reference to the idle interface. - auto idle = waylandServer()->display()->findChild(); - QVERIFY(idle); - QVERIFY(!idle->isInhibited()); - QSignalSpy inhibitedSpy(idle, &IdleInterface::inhibitedChanged); - QVERIFY(inhibitedSpy.isValid()); - // Create the test window. QScopedPointer surface(Test::createSurface()); QVERIFY(!surface.isNull()); @@ -327,30 +282,26 @@ void TestIdleInhibition::testDontInhibitWhenLeftCurrentDesktop() QCOMPARE(window->desktops().first(), VirtualDesktopManager::self()->desktops().first()); // This should inhibit our server object. - QVERIFY(idle->isInhibited()); - QCOMPARE(inhibitedSpy.count(), 1); + QCOMPARE(input()->idleInhibitors(), QList{window}); // Let the window enter the second virtual desktop. window->enterDesktop(VirtualDesktopManager::self()->desktops().at(1)); - QCOMPARE(inhibitedSpy.count(), 1); + QCOMPARE(input()->idleInhibitors(), QList{window}); // If the window leaves the first virtual desktop, then the associated idle // inhibitor object should not be honored. window->leaveDesktop(VirtualDesktopManager::self()->desktops().at(0)); - QVERIFY(!idle->isInhibited()); - QCOMPARE(inhibitedSpy.count(), 2); + QCOMPARE(input()->idleInhibitors(), QList{}); // If the window enters the first desktop, then the associated idle inhibitor // object should be honored back again. window->enterDesktop(VirtualDesktopManager::self()->desktops().at(0)); - QVERIFY(idle->isInhibited()); - QCOMPARE(inhibitedSpy.count(), 3); + QCOMPARE(input()->idleInhibitors(), QList{window}); // Destroy the test window. shellSurface.reset(); QVERIFY(Test::waitForWindowDestroyed(window)); - QTRY_VERIFY(!idle->isInhibited()); - QCOMPARE(inhibitedSpy.count(), 4); + QCOMPARE(input()->idleInhibitors(), QList{}); } WAYLANDTEST_MAIN(TestIdleInhibition) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 290f399a7c..9fef87e009 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -60,6 +60,7 @@ target_sources(kwin PRIVATE globalshortcuts.cpp group.cpp idle_inhibition.cpp + idledetector.cpp input.cpp input_event.cpp input_event_spy.cpp diff --git a/src/idle_inhibition.cpp b/src/idle_inhibition.cpp index 29917afe8f..4474e3db50 100644 --- a/src/idle_inhibition.cpp +++ b/src/idle_inhibition.cpp @@ -9,7 +9,7 @@ */ #include "idle_inhibition.h" #include "deleted.h" -#include "wayland/idle_interface.h" +#include "input.h" #include "wayland/surface_interface.h" #include "window.h" #include "workspace.h" @@ -22,9 +22,8 @@ using KWaylandServer::SurfaceInterface; namespace KWin { -IdleInhibition::IdleInhibition(IdleInterface *idle) - : QObject(idle) - , m_idle(idle) +IdleInhibition::IdleInhibition(QObject *parent) + : QObject(parent) { // Workspace is created after the wayland server is initialized. connect(kwinApp(), &Application::workspaceCreated, this, &IdleInhibition::slotWorkspaceCreated); @@ -58,24 +57,13 @@ void IdleInhibition::registerClient(Window *client) void IdleInhibition::inhibit(Window *client) { - if (isInhibited(client)) { - // already inhibited - return; - } - m_idleInhibitors << client; - m_idle->inhibit(); + input()->addIdleInhibitor(client); // TODO: notify powerdevil? } void IdleInhibition::uninhibit(Window *client) { - auto it = std::find(m_idleInhibitors.begin(), m_idleInhibitors.end(), client); - if (it == m_idleInhibitors.end()) { - // not inhibited - return; - } - m_idleInhibitors.erase(it); - m_idle->uninhibit(); + input()->removeIdleInhibitor(client); } void IdleInhibition::update(Window *client) diff --git a/src/idle_inhibition.h b/src/idle_inhibition.h index 9d03e33d33..31d254887b 100644 --- a/src/idle_inhibition.h +++ b/src/idle_inhibition.h @@ -13,13 +13,6 @@ #include #include -namespace KWaylandServer -{ -class IdleInterface; -} - -using KWaylandServer::IdleInterface; - namespace KWin { class Window; @@ -28,20 +21,11 @@ class IdleInhibition : public QObject { Q_OBJECT public: - explicit IdleInhibition(IdleInterface *idle); + explicit IdleInhibition(QObject *parent = nullptr); ~IdleInhibition() override; void registerClient(Window *client); - bool isInhibited() const - { - return !m_idleInhibitors.isEmpty(); - } - bool isInhibited(Window *client) const - { - return m_idleInhibitors.contains(client); - } - private Q_SLOTS: void slotWorkspaceCreated(); void slotDesktopChanged(); @@ -51,8 +35,6 @@ private: void uninhibit(Window *client); void update(Window *client); - IdleInterface *m_idle; - QVector m_idleInhibitors; QMap m_connections; }; } diff --git a/src/idledetector.cpp b/src/idledetector.cpp new file mode 100644 index 0000000000..cf46465dc1 --- /dev/null +++ b/src/idledetector.cpp @@ -0,0 +1,64 @@ +/* + SPDX-FileCopyrightText: 2021 Vlad Zahorodnii + + SPDX-License-Identifier: GPL-2.0-or-later +*/ + +#include "idledetector.h" +#include "input.h" + +namespace KWin +{ + +IdleDetector::IdleDetector(std::chrono::milliseconds timeout, QObject *parent) + : QObject(parent) + , m_timer(new QTimer(this)) +{ + m_timer->setSingleShot(true); + m_timer->setInterval(timeout); + connect(m_timer, &QTimer::timeout, this, &IdleDetector::idle); + m_timer->start(); + + input()->addIdleDetector(this); +} + +IdleDetector::~IdleDetector() +{ + if (input()) { + input()->removeIdleDetector(this); + } +} + +bool IdleDetector::isInhibited() const +{ + return m_isInhibited; +} + +void IdleDetector::setInhibited(bool inhibited) +{ + if (m_isInhibited == inhibited) { + return; + } + m_isInhibited = inhibited; + + if (inhibited) { + if (!m_timer->isActive()) { + Q_EMIT resumed(); + } + m_timer->stop(); + } else { + m_timer->start(); + } +} + +void IdleDetector::activity() +{ + if (!m_isInhibited) { + if (!m_timer->isActive()) { + Q_EMIT resumed(); + } + m_timer->start(); + } +} + +} // namespace KWin diff --git a/src/idledetector.h b/src/idledetector.h new file mode 100644 index 0000000000..09dc4253ab --- /dev/null +++ b/src/idledetector.h @@ -0,0 +1,38 @@ +/* + SPDX-FileCopyrightText: 2021 Vlad Zahorodnii + + SPDX-License-Identifier: GPL-2.0-or-later +*/ + +#pragma once + +#include + +#include + +namespace KWin +{ + +class KWIN_EXPORT IdleDetector : public QObject +{ + Q_OBJECT + +public: + explicit IdleDetector(std::chrono::milliseconds timeout, QObject *parent = nullptr); + ~IdleDetector() override; + + void activity(); + + bool isInhibited() const; + void setInhibited(bool inhibited); + +Q_SIGNALS: + void idle(); + void resumed(); + +private: + QTimer *m_timer; + bool m_isInhibited = false; +}; + +} // namespace KWin diff --git a/src/input.cpp b/src/input.cpp index 8d65a44ac2..0c82faf93a 100644 --- a/src/input.cpp +++ b/src/input.cpp @@ -17,6 +17,7 @@ #include "gestures.h" #include "globalshortcuts.h" #include "hide_cursor_spy.h" +#include "idledetector.h" #include "input_event.h" #include "input_event_spy.h" #include "inputbackend.h" @@ -2846,7 +2847,7 @@ public: private: void notifyActivity() { - waylandServer()->simulateUserActivity(); + input()->simulateUserActivity(); } }; @@ -3146,6 +3147,49 @@ Qt::MouseButtons InputRedirection::qtButtonStates() const return m_pointer->buttons(); } +void InputRedirection::simulateUserActivity() +{ + for (IdleDetector *idleDetector : std::as_const(m_idleDetectors)) { + idleDetector->activity(); + } +} + +void InputRedirection::addIdleDetector(IdleDetector *detector) +{ + Q_ASSERT(!m_idleDetectors.contains(detector)); + detector->setInhibited(!m_idleInhibitors.isEmpty()); + m_idleDetectors.append(detector); +} + +void InputRedirection::removeIdleDetector(IdleDetector *detector) +{ + m_idleDetectors.removeOne(detector); +} + +QList InputRedirection::idleInhibitors() const +{ + return m_idleInhibitors; +} + +void InputRedirection::addIdleInhibitor(Window *inhibitor) +{ + if (!m_idleInhibitors.contains(inhibitor)) { + m_idleInhibitors.append(inhibitor); + for (IdleDetector *idleDetector : std::as_const(m_idleDetectors)) { + idleDetector->setInhibited(true); + } + } +} + +void InputRedirection::removeIdleInhibitor(Window *inhibitor) +{ + if (m_idleInhibitors.removeOne(inhibitor) && m_idleInhibitors.isEmpty()) { + for (IdleDetector *idleDetector : std::as_const(m_idleDetectors)) { + idleDetector->setInhibited(false); + } + } +} + Window *InputRedirection::findToplevel(const QPoint &pos) { if (!Workspace::self()) { diff --git a/src/input.h b/src/input.h index 980160d212..a5bf0fdd09 100644 --- a/src/input.h +++ b/src/input.h @@ -32,6 +32,7 @@ class QWheelEvent; namespace KWin { +class IdleDetector; class Window; class GlobalShortcutsManager; class InputEventFilter; @@ -166,6 +167,15 @@ public: */ void uninstallInputEventSpy(InputEventSpy *spy); + void simulateUserActivity(); + + void addIdleDetector(IdleDetector *detector); + void removeIdleDetector(IdleDetector *detector); + + QList idleInhibitors() const; + void addIdleInhibitor(Window *inhibitor); + void removeIdleInhibitor(Window *inhibitor); + Window *findToplevel(const QPoint &pos); Window *findManagedToplevel(const QPoint &pos); GlobalShortcutsManager *shortcuts() const @@ -324,6 +334,8 @@ private: QList m_inputBackends; QList m_inputDevices; + QList m_idleDetectors; + QList m_idleInhibitors; WindowSelectorFilter *m_windowSelector = nullptr; QVector m_filters; diff --git a/src/plugins/idletime/poller.cpp b/src/plugins/idletime/poller.cpp index c06051e5ed..d29b2980d7 100644 --- a/src/plugins/idletime/poller.cpp +++ b/src/plugins/idletime/poller.cpp @@ -7,12 +7,8 @@ SPDX-License-Identifier: GPL-2.0-or-later */ #include "poller.h" - -#include - -#include "wayland/idle_interface.h" -#include "wayland/seat_interface.h" -#include "wayland_server.h" +#include "idledetector.h" +#include "input.h" namespace KWin { @@ -22,8 +18,6 @@ KWinIdleTimePoller::KWinIdleTimePoller(QObject *parent) { } -KWinIdleTimePoller::~KWinIdleTimePoller() = default; - bool KWinIdleTimePoller::isAvailable() { return true; @@ -31,100 +25,25 @@ bool KWinIdleTimePoller::isAvailable() bool KWinIdleTimePoller::setUpPoller() { - connect(waylandServer()->idle(), &KWaylandServer::IdleInterface::inhibitedChanged, this, &KWinIdleTimePoller::onInhibitedChanged); - connect(waylandServer()->seat(), &KWaylandServer::SeatInterface::timestampChanged, this, &KWinIdleTimePoller::onTimestampChanged); - return true; } void KWinIdleTimePoller::unloadPoller() { - if (waylandServer() && waylandServer()->idle()) { - disconnect(waylandServer()->idle(), &KWaylandServer::IdleInterface::inhibitedChanged, this, &KWinIdleTimePoller::onInhibitedChanged); - disconnect(waylandServer()->seat(), &KWaylandServer::SeatInterface::timestampChanged, this, &KWinIdleTimePoller::onTimestampChanged); - } - - qDeleteAll(m_timeouts); - m_timeouts.clear(); - - m_idling = false; } -void KWinIdleTimePoller::addTimeout(int newTimeout) +void KWinIdleTimePoller::addTimeout(int nextTimeout) { - if (m_timeouts.contains(newTimeout)) { + if (m_timeouts.contains(nextTimeout)) { return; } - auto timer = new QTimer(); - timer->setInterval(newTimeout); - timer->setSingleShot(true); - timer->callOnTimeout(this, [newTimeout, this]() { - m_idling = true; - Q_EMIT timeoutReached(newTimeout); + auto detector = new IdleDetector(std::chrono::milliseconds(nextTimeout), this); + m_timeouts.insert(nextTimeout, detector); + connect(detector, &IdleDetector::idle, this, [this, nextTimeout] { + Q_EMIT timeoutReached(nextTimeout); }); - - m_timeouts.insert(newTimeout, timer); - - if (!waylandServer()->idle()->isInhibited()) { - timer->start(); - } -} - -void KWinIdleTimePoller::processActivity() -{ - if (m_idling) { - Q_EMIT resumingFromIdle(); - m_idling = false; - } - - for (QTimer *timer : qAsConst(m_timeouts)) { - timer->start(); - } -} - -void KWinIdleTimePoller::onInhibitedChanged() -{ - if (waylandServer()->idle()->isInhibited()) { - // must stop the timers - stopCatchingIdleEvents(); - } else { - // resume the timers - catchIdleEvent(); - - // register some activity - Q_EMIT resumingFromIdle(); - } -} - -void KWinIdleTimePoller::onTimestampChanged() -{ - if (!waylandServer()->idle()->isInhibited()) { - processActivity(); - } -} - -void KWinIdleTimePoller::catchIdleEvent() -{ - for (QTimer *timer : qAsConst(m_timeouts)) { - timer->start(); - } -} - -void KWinIdleTimePoller::stopCatchingIdleEvents() -{ - for (QTimer *timer : qAsConst(m_timeouts)) { - timer->stop(); - } -} - -void KWinIdleTimePoller::simulateUserActivity() -{ - if (waylandServer()->idle()->isInhibited()) { - return; - } - processActivity(); - waylandServer()->simulateUserActivity(); + connect(detector, &IdleDetector::resumed, this, &KWinIdleTimePoller::resumingFromIdle); } void KWinIdleTimePoller::removeTimeout(int nextTimeout) @@ -132,16 +51,39 @@ void KWinIdleTimePoller::removeTimeout(int nextTimeout) delete m_timeouts.take(nextTimeout); } -QList KWinIdleTimePoller::timeouts() const +QList< int > KWinIdleTimePoller::timeouts() const { return m_timeouts.keys(); } +void KWinIdleTimePoller::catchIdleEvent() +{ + if (m_catchResumeTimeout) { + // already setup + return; + } + m_catchResumeTimeout = new IdleDetector(std::chrono::milliseconds::zero(), this); + connect(m_catchResumeTimeout, &IdleDetector::resumed, this, [this]() { + m_catchResumeTimeout->deleteLater(); + m_catchResumeTimeout = nullptr; + Q_EMIT resumingFromIdle(); + }); +} + +void KWinIdleTimePoller::stopCatchingIdleEvents() +{ + delete m_catchResumeTimeout; + m_catchResumeTimeout = nullptr; +} + int KWinIdleTimePoller::forcePollRequest() { return 0; } +void KWinIdleTimePoller::simulateUserActivity() +{ + input()->simulateUserActivity(); } -#include "poller.moc" +} // namespace KWin diff --git a/src/plugins/idletime/poller.h b/src/plugins/idletime/poller.h index 0f28aeacd4..fca765538e 100644 --- a/src/plugins/idletime/poller.h +++ b/src/plugins/idletime/poller.h @@ -16,6 +16,8 @@ namespace KWin { +class IdleDetector; + class KWinIdleTimePoller : public AbstractSystemPoller { Q_OBJECT @@ -24,7 +26,6 @@ class KWinIdleTimePoller : public AbstractSystemPoller public: KWinIdleTimePoller(QObject *parent = nullptr); - ~KWinIdleTimePoller() override; bool isAvailable() override; bool setUpPoller() override; @@ -39,14 +40,9 @@ public Q_SLOTS: void stopCatchingIdleEvents() override; void simulateUserActivity() override; -private Q_SLOTS: - void onInhibitedChanged(); - void onTimestampChanged(); - private: - void processActivity(); - QHash m_timeouts; - bool m_idling = false; + IdleDetector *m_catchResumeTimeout = nullptr; + QHash m_timeouts; }; } diff --git a/src/wayland/autotests/client/CMakeLists.txt b/src/wayland/autotests/client/CMakeLists.txt index 37ca71c291..79cff410e6 100644 --- a/src/wayland/autotests/client/CMakeLists.txt +++ b/src/wayland/autotests/client/CMakeLists.txt @@ -162,17 +162,6 @@ target_link_libraries( testPlasmaShell Qt::Test Qt::Gui KF5::WaylandClient kwin add_test(NAME kwayland-testPlasmaShell COMMAND testPlasmaShell) ecm_mark_as_test(testPlasmaShell) -######################################################## -# Test Idle -######################################################## -set( testIdle_SRCS - test_idle.cpp - ) -add_executable(testIdle ${testIdle_SRCS}) -target_link_libraries( testIdle Qt::Test Qt::Gui KF5::WaylandClient kwin) -add_test(NAME kwayland-testIdle COMMAND testIdle) -ecm_mark_as_test(testIdle) - ######################################################## # Test Shadow ######################################################## diff --git a/src/wayland/autotests/client/test_idle.cpp b/src/wayland/autotests/client/test_idle.cpp deleted file mode 100644 index d434a921db..0000000000 --- a/src/wayland/autotests/client/test_idle.cpp +++ /dev/null @@ -1,271 +0,0 @@ -/* - SPDX-FileCopyrightText: 2016 Martin Gräßlin - - SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL -*/ -// Qt -#include -// client -#include "KWayland/Client/connection_thread.h" -#include "KWayland/Client/event_queue.h" -#include "KWayland/Client/idle.h" -#include "KWayland/Client/registry.h" -#include "KWayland/Client/seat.h" -// server -#include "wayland/display.h" -#include "wayland/idle_interface.h" -#include "wayland/seat_interface.h" - -using namespace KWayland::Client; -using namespace KWaylandServer; - -class IdleTest : public QObject -{ - Q_OBJECT -private Q_SLOTS: - void init(); - void cleanup(); - - void testTimeout(); - void testSimulateUserActivity(); - void testServerSimulateUserActivity(); - void testIdleInhibit(); - void testIdleInhibitBlocksTimeout(); - -private: - KWaylandServer::Display *m_display = nullptr; - SeatInterface *m_seatInterface = nullptr; - IdleInterface *m_idleInterface = nullptr; - ConnectionThread *m_connection = nullptr; - QThread *m_thread = nullptr; - EventQueue *m_queue = nullptr; - Seat *m_seat = nullptr; - Idle *m_idle = nullptr; -}; - -static const QString s_socketName = QStringLiteral("kwayland-test-idle-0"); - -void IdleTest::init() -{ - delete m_display; - m_display = new KWaylandServer::Display(this); - m_display->addSocketName(s_socketName); - m_display->start(); - QVERIFY(m_display->isRunning()); - m_display->createShm(); - m_seatInterface = new SeatInterface(m_display); - m_seatInterface->setName(QStringLiteral("seat0")); - m_idleInterface = new IdleInterface(m_display); - - // setup connection - m_connection = new KWayland::Client::ConnectionThread; - QSignalSpy connectedSpy(m_connection, &ConnectionThread::connected); - QVERIFY(connectedSpy.isValid()); - m_connection->setSocketName(s_socketName); - - m_thread = new QThread(this); - m_connection->moveToThread(m_thread); - m_thread->start(); - - m_connection->initConnection(); - QVERIFY(connectedSpy.wait()); - - m_queue = new EventQueue(this); - m_queue->setup(m_connection); - - Registry registry; - QSignalSpy interfacesAnnouncedSpy(®istry, &Registry::interfacesAnnounced); - QVERIFY(interfacesAnnouncedSpy.isValid()); - registry.setEventQueue(m_queue); - registry.create(m_connection); - QVERIFY(registry.isValid()); - registry.setup(); - QVERIFY(interfacesAnnouncedSpy.wait()); - - m_seat = registry.createSeat(registry.interface(Registry::Interface::Seat).name, registry.interface(Registry::Interface::Seat).version, this); - QVERIFY(m_seat->isValid()); - m_idle = registry.createIdle(registry.interface(Registry::Interface::Idle).name, registry.interface(Registry::Interface::Idle).version, this); - QVERIFY(m_idle->isValid()); -} - -void IdleTest::cleanup() -{ -#define CLEANUP(variable) \ - if (variable) { \ - delete variable; \ - variable = nullptr; \ - } - CLEANUP(m_idle) - CLEANUP(m_seat) - CLEANUP(m_queue) - if (m_connection) { - m_connection->deleteLater(); - m_connection = nullptr; - } - if (m_thread) { - m_thread->quit(); - m_thread->wait(); - delete m_thread; - m_thread = nullptr; - } - - CLEANUP(m_display) -#undef CLEANUP - - // these are the children of the display - m_idleInterface = nullptr; - m_seatInterface = nullptr; -} - -void IdleTest::testTimeout() -{ - // this test verifies the basic functionality of a timeout, that it gets fired - // and that it resumes from idle, etc. - QScopedPointer timeout(m_idle->getTimeout(1, m_seat)); - QVERIFY(timeout->isValid()); - QSignalSpy idleSpy(timeout.data(), &IdleTimeout::idle); - QVERIFY(idleSpy.isValid()); - QSignalSpy resumedFormIdleSpy(timeout.data(), &IdleTimeout::resumeFromIdle); - QVERIFY(resumedFormIdleSpy.isValid()); - - // we requested a timeout of 1 msec, but the minimum the server sets is 5 sec - QVERIFY(!idleSpy.wait(500)); - // the default of 5 sec will now pass - QVERIFY(idleSpy.wait()); - - // simulate some activity - QVERIFY(resumedFormIdleSpy.isEmpty()); - m_idleInterface->simulateUserActivity(); - QVERIFY(resumedFormIdleSpy.wait()); - - timeout.reset(); - m_connection->flush(); - m_display->dispatchEvents(); -} - -void IdleTest::testSimulateUserActivity() -{ - // this test verifies that simulate user activity doesn't fire the timer - QScopedPointer timeout(m_idle->getTimeout(6000, m_seat)); - QVERIFY(timeout->isValid()); - QSignalSpy idleSpy(timeout.data(), &IdleTimeout::idle); - QVERIFY(idleSpy.isValid()); - QSignalSpy resumedFormIdleSpy(timeout.data(), &IdleTimeout::resumeFromIdle); - QVERIFY(resumedFormIdleSpy.isValid()); - m_connection->flush(); - - QTest::qWait(4000); - timeout->simulateUserActivity(); - // waiting default five sec should fail - QVERIFY(!idleSpy.wait()); - // another 2 sec should fire - QVERIFY(idleSpy.wait(2000)); - - // now simulating user activity should emit a resumedFromIdle - QVERIFY(resumedFormIdleSpy.isEmpty()); - timeout->simulateUserActivity(); - QVERIFY(resumedFormIdleSpy.wait()); - - timeout.reset(); - m_connection->flush(); - m_display->dispatchEvents(); -} - -void IdleTest::testServerSimulateUserActivity() -{ - // this test verifies that simulate user activity doesn't fire the timer - QScopedPointer timeout(m_idle->getTimeout(6000, m_seat)); - QVERIFY(timeout->isValid()); - QSignalSpy idleSpy(timeout.data(), &IdleTimeout::idle); - QVERIFY(idleSpy.isValid()); - QSignalSpy resumedFormIdleSpy(timeout.data(), &IdleTimeout::resumeFromIdle); - QVERIFY(resumedFormIdleSpy.isValid()); - m_connection->flush(); - - QTest::qWait(4000); - m_idleInterface->simulateUserActivity(); - // waiting default five sec should fail - QVERIFY(!idleSpy.wait()); - // another 2 sec should fire - QVERIFY(idleSpy.wait(2000)); - - // now simulating user activity should emit a resumedFromIdle - QVERIFY(resumedFormIdleSpy.isEmpty()); - m_idleInterface->simulateUserActivity(); - QVERIFY(resumedFormIdleSpy.wait()); - - timeout.reset(); - m_connection->flush(); - m_display->dispatchEvents(); -} - -void IdleTest::testIdleInhibit() -{ - QCOMPARE(m_idleInterface->isInhibited(), false); - QSignalSpy idleInhibitedSpy(m_idleInterface, &IdleInterface::inhibitedChanged); - QVERIFY(idleInhibitedSpy.isValid()); - m_idleInterface->inhibit(); - QCOMPARE(m_idleInterface->isInhibited(), true); - QCOMPARE(idleInhibitedSpy.count(), 1); - m_idleInterface->inhibit(); - QCOMPARE(m_idleInterface->isInhibited(), true); - QCOMPARE(idleInhibitedSpy.count(), 1); - m_idleInterface->uninhibit(); - QCOMPARE(m_idleInterface->isInhibited(), true); - QCOMPARE(idleInhibitedSpy.count(), 1); - m_idleInterface->uninhibit(); - QCOMPARE(m_idleInterface->isInhibited(), false); - QCOMPARE(idleInhibitedSpy.count(), 2); -} - -void IdleTest::testIdleInhibitBlocksTimeout() -{ - // this test verifies that a timeout does not fire when the system is inhibited - - // so first inhibit - QCOMPARE(m_idleInterface->isInhibited(), false); - m_idleInterface->inhibit(); - - QScopedPointer timeout(m_idle->getTimeout(1, m_seat)); - QVERIFY(timeout->isValid()); - QSignalSpy idleSpy(timeout.data(), &IdleTimeout::idle); - QVERIFY(idleSpy.isValid()); - QSignalSpy resumedFormIdleSpy(timeout.data(), &IdleTimeout::resumeFromIdle); - QVERIFY(resumedFormIdleSpy.isValid()); - - // we requested a timeout of 1 msec, but the minimum the server sets is 5 sec - QVERIFY(!idleSpy.wait(500)); - // the default of 5 sec won't pass - QVERIFY(!idleSpy.wait()); - - // simulate some activity - QVERIFY(resumedFormIdleSpy.isEmpty()); - m_seatInterface->setTimestamp(1); - // resume from idle should not fire - QVERIFY(!resumedFormIdleSpy.wait()); - - // let's uninhibit - m_idleInterface->uninhibit(); - QCOMPARE(m_idleInterface->isInhibited(), false); - // we requested a timeout of 1 msec, but the minimum the server sets is 5 sec - QVERIFY(!idleSpy.wait(500)); - // the default of 5 sec will now pass - QVERIFY(idleSpy.wait()); - - // if we inhibit now it will trigger a resume from idle - QVERIFY(resumedFormIdleSpy.isEmpty()); - m_idleInterface->inhibit(); - QVERIFY(resumedFormIdleSpy.wait()); - - // let's wait again just to verify that also inhibit for already existing IdleTimeout works - QVERIFY(!idleSpy.wait(500)); - QVERIFY(!idleSpy.wait()); - QCOMPARE(idleSpy.count(), 1); - - timeout.reset(); - m_connection->flush(); - m_display->dispatchEvents(); -} - -QTEST_GUILESS_MAIN(IdleTest) -#include "test_idle.moc" diff --git a/src/wayland/idle_interface.cpp b/src/wayland/idle_interface.cpp index 0ce4614961..e894dc0f5f 100644 --- a/src/wayland/idle_interface.cpp +++ b/src/wayland/idle_interface.cpp @@ -7,13 +7,18 @@ #include "idle_interface_p.h" #include "seat_interface.h" +#include "idledetector.h" +#include "input.h" + +using namespace KWin; + namespace KWaylandServer { + static const quint32 s_version = 1; -IdleInterfacePrivate::IdleInterfacePrivate(IdleInterface *_q, Display *display) +IdleInterfacePrivate::IdleInterfacePrivate(Display *display) : QtWaylandServer::org_kde_kwin_idle(*display, s_version) - , q(_q) { } @@ -28,75 +33,29 @@ void IdleInterfacePrivate::org_kde_kwin_idle_get_idle_timeout(Resource *resource return; } - IdleTimeoutInterface *idleTimeout = new IdleTimeoutInterface(s, q, idleTimoutResource); - idleTimeouts << idleTimeout; - - QObject::connect(idleTimeout, &IdleTimeoutInterface::destroyed, q, [this, idleTimeout]() { - idleTimeouts.removeOne(idleTimeout); - }); - idleTimeout->setup(timeout); + new IdleTimeoutInterface(std::chrono::milliseconds(timeout), idleTimoutResource); } IdleInterface::IdleInterface(Display *display, QObject *parent) : QObject(parent) - , d(new IdleInterfacePrivate(this, display)) + , d(new IdleInterfacePrivate(display)) { } IdleInterface::~IdleInterface() = default; -void IdleInterface::inhibit() +IdleTimeoutInterface::IdleTimeoutInterface(std::chrono::milliseconds timeout, wl_resource *resource) + : QtWaylandServer::org_kde_kwin_idle_timeout(resource) { - d->inhibitCount++; - if (d->inhibitCount == 1) { - Q_EMIT inhibitedChanged(); - } -} - -void IdleInterface::uninhibit() -{ - d->inhibitCount--; - if (d->inhibitCount == 0) { - Q_EMIT inhibitedChanged(); - } -} - -bool IdleInterface::isInhibited() const -{ - return d->inhibitCount > 0; -} - -void IdleInterface::simulateUserActivity() -{ - for (auto i : qAsConst(d->idleTimeouts)) { - i->simulateUserActivity(); - } -} - -IdleTimeoutInterface::IdleTimeoutInterface(SeatInterface *seat, IdleInterface *manager, wl_resource *resource) - : QObject() - , QtWaylandServer::org_kde_kwin_idle_timeout(resource) - , seat(seat) - , manager(manager) -{ - connect(manager, &IdleInterface::inhibitedChanged, this, [this, manager] { - if (!timer) { - // not yet configured - return; - } - if (manager->isInhibited()) { - if (!timer->isActive()) { - send_resumed(); - } - timer->stop(); - } else { - timer->start(); - } + auto detector = new IdleDetector(timeout, this); + connect(detector, &IdleDetector::idle, this, [this]() { + send_idle(); + }); + connect(detector, &IdleDetector::resumed, this, [this]() { + send_resumed(); }); } -IdleTimeoutInterface::~IdleTimeoutInterface() = default; - void IdleTimeoutInterface::org_kde_kwin_idle_timeout_release(Resource *resource) { wl_resource_destroy(resource->handle); @@ -111,40 +70,7 @@ void IdleTimeoutInterface::org_kde_kwin_idle_timeout_destroy_resource(Resource * void IdleTimeoutInterface::org_kde_kwin_idle_timeout_simulate_user_activity(Resource *resource) { Q_UNUSED(resource) - simulateUserActivity(); -} -void IdleTimeoutInterface::simulateUserActivity() -{ - if (!timer) { - // not yet configured - return; - } - if (manager->isInhibited()) { - // ignored while inhibited - return; - } - if (!timer->isActive()) { - send_resumed(); - } - timer->start(); + input()->simulateUserActivity(); } -void IdleTimeoutInterface::setup(quint32 timeout) -{ - if (timer) { - return; - } - timer = new QTimer(this); - timer->setSingleShot(true); - // less than 500 msec is not idle by definition - timer->setInterval(qMax(timeout, 500u)); - QObject::connect(timer, &QTimer::timeout, this, [this] { - send_idle(); - }); - if (manager->isInhibited()) { - // don't start if inhibited - return; - } - timer->start(); -} } diff --git a/src/wayland/idle_interface.h b/src/wayland/idle_interface.h index 9527c6a245..632c710eda 100644 --- a/src/wayland/idle_interface.h +++ b/src/wayland/idle_interface.h @@ -42,56 +42,6 @@ public: explicit IdleInterface(Display *display, QObject *parent = nullptr); ~IdleInterface() override; - /** - * Inhibits the IdleInterface. While inhibited no IdleTimeoutInterface interface gets - * notified about an idle timeout. - * - * This can be used to inhibit power management, screen locking, etc. directly from - * Compositor side. - * - * To resume idle timeouts invoke @link{uninhibit}. It is possible to invoke inhibit several - * times, in that case uninhibit needs to called the same amount as inhibit has been called. - * @see uninhibit - * @see isInhibited - * @see inhibitedChanged - */ - void inhibit(); - - /** - * Inhibits the IdleInterface. The idle timeouts are only restarted if uninhibit has been - * called the same amount as inhibit. - * - * @see inhibit - * @see isInhibited - * @see inhibitedChanged - */ - void uninhibit(); - - /** - * @returns Whether idle timeouts are currently inhibited - * @see inhibit - * @see uninhibit - * @see inhibitedChanged - */ - bool isInhibited() const; - - /** - * Calling this method allows the Compositor to simulate user activity. - * This means the same action is performed as if the user interacted with - * an input device on the SeatInterface. - * Idle timeouts are resumed and the idle time gets restarted. - */ - void simulateUserActivity(); - -Q_SIGNALS: - /** - * Emitted when the system gets inhibited or uninhibited. - * @see inhibit - * @see uninhibit - * @see isInhibited - */ - void inhibitedChanged(); - private: QScopedPointer d; }; diff --git a/src/wayland/idle_interface_p.h b/src/wayland/idle_interface_p.h index 45103e912f..3a57adb375 100644 --- a/src/wayland/idle_interface_p.h +++ b/src/wayland/idle_interface_p.h @@ -21,29 +21,18 @@ class IdleTimeoutInterface; class IdleInterfacePrivate : public QtWaylandServer::org_kde_kwin_idle { public: - IdleInterfacePrivate(IdleInterface *_q, Display *display); - - int inhibitCount = 0; - QVector idleTimeouts; - IdleInterface *q; + IdleInterfacePrivate(Display *display); protected: void org_kde_kwin_idle_get_idle_timeout(Resource *resource, uint32_t id, wl_resource *seat, uint32_t timeout) override; }; -class IdleTimeoutInterface : public QObject, QtWaylandServer::org_kde_kwin_idle_timeout +class IdleTimeoutInterface : public QObject, public QtWaylandServer::org_kde_kwin_idle_timeout { Q_OBJECT -public: - explicit IdleTimeoutInterface(SeatInterface *seat, IdleInterface *parent, wl_resource *resource); - ~IdleTimeoutInterface() override; - void setup(quint32 timeout); - void simulateUserActivity(); -private: - SeatInterface *seat; - IdleInterface *manager; - QTimer *timer = nullptr; +public: + explicit IdleTimeoutInterface(std::chrono::milliseconds timeout, wl_resource *resource); protected: void org_kde_kwin_idle_timeout_destroy_resource(Resource *resource) override; diff --git a/src/wayland_server.cpp b/src/wayland_server.cpp index ea741ae886..1efe70a0b5 100644 --- a/src/wayland_server.cpp +++ b/src/wayland_server.cpp @@ -756,13 +756,6 @@ bool WaylandServer::hasGlobalShortcutSupport() const return !m_initFlags.testFlag(InitializationFlag::NoGlobalShortcuts); } -void WaylandServer::simulateUserActivity() -{ - if (m_idle) { - m_idle->simulateUserActivity(); - } -} - bool WaylandServer::isKeyboardShortcutsInhibited() const { auto surface = seat()->focusedKeyboardSurface(); diff --git a/src/wayland_server.h b/src/wayland_server.h index 14633ac156..eb87ae5249 100644 --- a/src/wayland_server.h +++ b/src/wayland_server.h @@ -209,8 +209,6 @@ public: */ SocketPairConnection createConnection(); - void simulateUserActivity(); - QSet linuxDmabufBuffers() const { return m_linuxDmabufBuffers;