fix: notice input on the lock screen despite idle inhibitors

This commit is contained in:
Felitendo committed 2026-09-22 21:03:41 +02:00
1 parent 7c87adb733
commit 77ae883b98
12 files changed
+280 -38

No files matched your search

+87
View File
@@ -0,0 +1,87 @@
// SPDX-License-Identifier: GPL-3.0-or-later
#include "inputwatcher.h"
#include <QGuiApplication>
#include <QWaylandClientExtensionTemplate>
#include <QtGui/qguiapplication_platform.h>
#include <functional>
#include "qwayland-ext-idle-notify-v1.h"
class IdleNotifier : public QWaylandClientExtensionTemplate<IdleNotifier>, public QtWayland::ext_idle_notifier_v1
{
public:
IdleNotifier()
: QWaylandClientExtensionTemplate<IdleNotifier>(2)
{
initialize();
}
~IdleNotifier() override
{
if (isActive()) {
destroy();
}
}
};
class IdleNotification : public QtWayland::ext_idle_notification_v1
{
public:
IdleNotification(::ext_idle_notification_v1 *object, std::function<void()> resumed)
: QtWayland::ext_idle_notification_v1(object)
, m_resumed(std::move(resumed))
{
}
~IdleNotification() override
{
destroy();
}
protected:
// Only ever sent after idled: calm first, then this.
void ext_idle_notification_v1_resumed() override
{
if (!m_fired) {
m_fired = true;
m_resumed();
}
}
private:
std::function<void()> m_resumed;
bool m_fired = false;
};
InputWatcher::InputWatcher(QObject *parent)
: QObject(parent)
, m_notifier(std::make_unique<IdleNotifier>())
{
}
InputWatcher::~InputWatcher() = default;
void InputWatcher::watch(int calmMs)
{
m_notification.reset();
auto *wayland = qGuiApp->nativeInterface<QNativeInterface::QWaylandApplication>();
if (!m_notifier->isActive() || !wayland || !wayland->seat()) {
qWarning("no ext_idle_notifier_v1, so no telling when somebody comes back");
return;
}
// Version 1 has only the notification that inhibitors hold back.
::ext_idle_notification_v1 *object = m_notifier->QWaylandClientExtension::version() >= 2
? m_notifier->get_input_idle_notification(uint32_t(calmMs), wayland->seat())
: m_notifier->get_idle_notification(uint32_t(calmMs), wayland->seat());
// Queued: not from inside the Wayland event, which the receiver might
// answer by watching anew and so destroying the notification.
m_notification = std::make_unique<IdleNotification>(object, [this] {
QMetaObject::invokeMethod(this, &InputWatcher::input, Qt::QueuedConnection);
});
}
void InputWatcher::stop()
{
m_notification.reset();
}
+37
View File
@@ -0,0 +1,37 @@
// SPDX-License-Identifier: GPL-3.0-or-later
//
// Somebody touching the machine: a key, a click, the mouse, the touchpad.
//
// From ext_idle_notifier_v1, like KIdleTime, but with the input notification
// of version 2. KIdleTime's own honours idle inhibitors: with a video playing
// or an app keeping the screen on, no key reached it and the lock screen never
// scanned.
#pragma once
#include <QObject>
#include <memory>
class IdleNotifier;
class IdleNotification;
class InputWatcher : public QObject
{
Q_OBJECT
public:
explicit InputWatcher(QObject *parent = nullptr);
~InputWatcher() override;
// Emit input() once, for the first input after nothing was touched for
// calmMs. 0: the next input. Replaces what was watched before.
void watch(int calmMs);
void stop();
Q_SIGNALS:
void input();
private:
std::unique_ptr<IdleNotifier> m_notifier;
std::unique_ptr<IdleNotification> m_notification;
};
+8 -26
View File
@@ -6,8 +6,6 @@
#include "daemonclient.h"
#include "userconfig.h"
#include <KIdleTime>
#include <QDBusConnection>
#include <QDBusMessage>
#include <QDBusPendingCallWatcher>
@@ -38,7 +36,10 @@ LockController::LockController(BubbleController *bubble, UserConfig *config, QOb
, m_config(config)
{
m_armTimer.setSingleShot(true);
connect(&m_armTimer, &QTimer::timeout, this, &LockController::arm);
connect(&m_armTimer, &QTimer::timeout, this, [this] {
arm(0);
});
connect(&m_input, &InputWatcher::input, this, &LockController::onResume);
QDBusConnection session = QDBusConnection::sessionBus();
session.connect(QStringLiteral("org.freedesktop.ScreenSaver"),
@@ -55,17 +56,6 @@ LockController::LockController(BubbleController *bubble, UserConfig *config, QOb
this,
SLOT(onPrepareForSleep(bool)));
KIdleTime *idle = KIdleTime::instance();
connect(idle, &KIdleTime::resumingFromIdle, this, &LockController::onResume);
connect(idle, qOverload<int, int>(&KIdleTime::timeoutReached), this, [this, idle](int id, int) {
if (id != m_idleId) {
return;
}
idle->removeIdleTimeout(id);
m_idleId = -1;
arm();
});
// Started while the screen is already locked (the agent restarted).
const QDBusMessage get = QDBusMessage::createMethodCall(QStringLiteral("org.freedesktop.ScreenSaver"),
QStringLiteral("/ScreenSaver"),
@@ -86,16 +76,10 @@ void LockController::onActiveChanged(bool active)
if (active == m_locked) {
return;
}
KIdleTime *idle = KIdleTime::instance();
if (!active) {
m_locked = false;
m_armTimer.stop();
if (m_idleId >= 0) {
idle->removeIdleTimeout(m_idleId);
m_idleId = -1;
}
idle->stopCatchingResumeEvent();
m_input.stop();
if (m_scan) {
m_scan->abort();
m_scan->deleteLater();
@@ -148,12 +132,12 @@ void LockController::onPrepareForSleep(bool sleeping)
}
}
void LockController::arm()
void LockController::arm(int calmMs)
{
if (!m_locked || m_stopped || !m_config->scanOnWake()) {
return;
}
KIdleTime::instance()->catchNextResumeEvent();
m_input.watch(calmMs);
}
void LockController::onResume()
@@ -221,9 +205,7 @@ void LockController::onScanFinished(const QJsonObject &result)
m_armTimer.start(GraceAfterLockMs);
return;
}
if (m_idleId < 0) {
m_idleId = KIdleTime::instance()->addIdleTimeout(CalmBeforeRetryMs);
}
arm(CalmBeforeRetryMs);
}
void LockController::unlock()
+5 -2
View File
@@ -19,6 +19,8 @@
#pragma once
#include "inputwatcher.h"
#include <QElapsedTimer>
#include <QObject>
#include <QPointer>
@@ -44,7 +46,8 @@ private Q_SLOTS:
void onPrepareForSleep(bool sleeping);
private:
void arm();
// Scan at the next input after calmMs without any.
void arm(int calmMs);
void onResume();
void startScan(const QString &why);
void onScanFinished(const QJsonObject &result);
@@ -58,5 +61,5 @@ private:
QElapsedTimer m_lockedFor;
QPointer<DaemonRequest> m_scan;
QTimer m_armTimer;
int m_idleId = -1;
InputWatcher m_input;
};