feat: open gtklock from outside once it can

This commit is contained in:
Felitendo committed 2026-09-26 19:36:17 +02:00
1 parent c228291520
commit 5699158bb1
9 files changed
+61 -19

No files matched your search

+10
View File
@@ -42,6 +42,14 @@ LockController::LockController(BubbleController *bubble, UserConfig *config, Ses
});
connect(&m_input, &InputWatcher::input, this, &LockController::onResume);
connect(&m_lock, &LockWatcher::lockedChanged, this, &LockController::onLockedChanged);
// A lock screen that is only starting cannot be opened yet: the scan
// that was due when it locked follows once it can.
connect(&m_lock, &LockWatcher::unlockable, this, [this] {
if (m_lockScanDue) {
m_lockScanDue = false;
startScan(QStringLiteral("lock"));
}
});
QDBusConnection::systemBus().connect(QStringLiteral("org.freedesktop.login1"),
QStringLiteral("/org/freedesktop/login1"),
@@ -58,6 +66,7 @@ void LockController::onLockedChanged(bool locked)
}
if (!locked) {
m_locked = false;
m_lockScanDue = false;
m_armTimer.stop();
m_input.stop();
if (m_scan) {
@@ -90,6 +99,7 @@ void LockController::onLockedChanged(bool locked)
if (m_config->scanOnLock()) {
QTimer::singleShot(400, this, [this] {
m_lockScanDue = m_locked && !m_lock.canUnlock();
startScan(QStringLiteral("lock"));
});
} else {
+2
View File
@@ -59,6 +59,8 @@ private:
LockScreenController *m_screen;
bool m_locked = false;
bool m_stopped = false;
// Scan right after locking, once the lock screen can be opened.
bool m_lockScanDue = false;
QElapsedTimer m_lockedFor;
QPointer<DaemonRequest> m_scan;
QTimer m_armTimer;
+13 -4
View File
@@ -5,6 +5,7 @@
#include <QFile>
#include <QString>
#include <algorithm>
#include <csignal>
#include <dirent.h>
#include <sys/stat.h>
@@ -14,7 +15,7 @@ namespace
{
bool isLocker(const QByteArray &name)
{
return name == "hyprlock" || name == "swaylock";
return name == "hyprlock" || name == "swaylock" || name == "gtklock";
}
QByteArray readFile(const QString &path)
@@ -33,9 +34,9 @@ pid_t parentOf(pid_t pid)
// The lock screens of this user on this display. Another session of the same
// user has a display of its own, and its lock screen is left alone. A child
// of a lock screen (swaylock checks the password in one) is left out: killed
// by the signal before its parent unlocks, it would take the parent down
// with it, and the screen would stay locked.
// of a lock screen (swaylock and gtklock check the password in one) is left
// out: killed by the signal before its parent unlocks, it would take the
// parent down with it, and the screen would stay locked.
QList<pid_t> findAll()
{
QList<pid_t> found;
@@ -104,6 +105,14 @@ QList<pid_t> Lockers::find(const QByteArray &name)
return found;
}
bool Lockers::ready(int sig)
{
const QList<pid_t> lockers = find();
return std::any_of(lockers.cbegin(), lockers.cend(), [sig](pid_t pid) {
return catches(pid, sig);
});
}
int Lockers::signal(int sig, const QByteArray &name)
{
int waiting = 0;
+4 -1
View File
@@ -1,7 +1,7 @@
// SPDX-License-Identifier: GPL-3.0-or-later
//
// The lock screens that are programs of their own and take signals from
// outside: hyprlock and swaylock open on SIGUSR1.
// outside: hyprlock, swaylock and gtklock open on SIGUSR1.
#pragma once
@@ -19,4 +19,7 @@ QList<pid_t> find(const QByteArray &name = {});
// that, the signal kills it, and the compositor keeps the screen locked with
// nobody to open it.
int signal(int sig, const QByteArray &name = {});
// Whether one of them handles sig: gtklock only opens on SIGUSR1 since 2025,
// and none does before it has locked.
bool ready(int sig);
}
+7 -1
View File
@@ -160,7 +160,13 @@ void LockWatcher::onSessionProperties(const QString &interface, const QVariantMa
void LockWatcher::pollLockers()
{
m_lockerRunning = !Lockers::find().isEmpty();
const bool ready = Lockers::ready(SIGUSR1);
const bool became = ready && !m_lockerReady;
m_lockerReady = ready;
update();
if (became) {
Q_EMIT unlockable();
}
}
void LockWatcher::setOwnLock(SessionLock *lock)
@@ -171,7 +177,7 @@ void LockWatcher::setOwnLock(SessionLock *lock)
bool LockWatcher::canUnlock() const
{
return (m_own && m_own->isLocked()) || !m_ownLockers || m_lockerRunning;
return (m_own && m_own->isLocked()) || !m_ownLockers || m_lockerReady;
}
void LockWatcher::update()
+10 -5
View File
@@ -7,11 +7,12 @@
// GNOME logind's LockedHint. Unlocked through logind.
// Niri logind's LockedHint, which niri sets for any lock screen.
// Hyprland sets no LockedHint, so the lock screen is looked for among this
// user's processes (hyprlock, swaylock) once a second. Only on
// compositors where the lock screen is a program of its own
// (ext-session-lock).
// user's processes (hyprlock, swaylock, gtklock) once a second.
// Only on compositors where the lock screen is a program of its
// own (ext-session-lock).
//
// hyprlock and swaylock do not listen to logind. They unlock on SIGUSR1.
// hyprlock, swaylock and gtklock do not listen to logind. They unlock on
// SIGUSR1.
// face-unlock's own lock screen (SessionLock) is simply told to. Any other
// lock screen of that kind only opens itself: it gets the face through the
// PAM module in its own stack (see src/lib/pam.sh), when Enter is pressed.
@@ -41,13 +42,16 @@ public:
return m_locked;
}
// Whether unlock() can open the lock screen that is up: always where
// logind unlocks (Plasma, GNOME), only hyprlock and swaylock elsewhere.
// logind unlocks (Plasma, GNOME), elsewhere only hyprlock, swaylock and
// gtklock, once they take SIGUSR1.
bool canUnlock() const;
void unlock();
void setOwnLock(SessionLock *lock);
Q_SIGNALS:
void lockedChanged(bool locked);
// canUnlock() came true: the lock screen that is up now takes SIGUSR1.
void unlockable();
private Q_SLOTS:
void onScreenSaver(bool active);
@@ -63,6 +67,7 @@ private:
bool m_screenSaver = false;
bool m_lockedHint = false;
bool m_lockerRunning = false;
bool m_lockerReady = false;
bool m_locked = false;
// The compositor's lock screen is a program of its own.
bool m_ownLockers = false;