fix lock feedback loop + impatient sessiond boot lock
Session.lock() notified logind unconditionally; hypridle's lock_cmd fires on logind's Lock signal, so every lock echoed back through logind -> hypridle -> lock() forever. Observed: ~450 lock requests/min for 7 minutes, re-locking the screen seconds after each unlock. Notify only on the unlocked->locked edge. sessiond gave up waiting for the compositor lock ack after one dispatch — at boot Hyprland defers the ack while the splash holds DRM, so the initial lock silently failed (journal: 'never acknowledged', 0.7s in) and boot ran on the legacy lock path. Wait up to 60s for locked/finished.
This commit is contained in:
parent
9254333fcc
commit
50530b1159
2 changed files with 26 additions and 11 deletions
|
|
@ -247,20 +247,23 @@ pub fn run(
|
|||
state.lock = Some(lock);
|
||||
queue.roundtrip(&mut state).context("lock roundtrip")?;
|
||||
|
||||
// Wait for the compositor's verdict: exactly one of locked/finished is
|
||||
// guaranteed by the protocol, but NOT promptly. At boot Hyprland defers
|
||||
// the ack until it is the active DRM session — and the splash can hold
|
||||
// DRM for many seconds before its watchdog fails open. One dispatch was
|
||||
// far too impatient (observed: gave up 0.7s into boot, and the whole
|
||||
// lock-before-shell guarantee silently degraded to the legacy path).
|
||||
// Wait up to 60s, dispatching as events arrive.
|
||||
let verdict_deadline = std::time::Instant::now() + std::time::Duration::from_secs(60);
|
||||
while !state.locked && !state.finished {
|
||||
if std::time::Instant::now() >= verdict_deadline {
|
||||
bail!("compositor never acknowledged the session lock (60s)");
|
||||
}
|
||||
queue.blocking_dispatch(&mut state).context("waiting for locked")?;
|
||||
}
|
||||
if state.finished {
|
||||
return Ok(SessionOutcome::Denied);
|
||||
}
|
||||
if !state.locked {
|
||||
// Compositor may answer on a later dispatch; give it one blocking
|
||||
// turn before declaring failure.
|
||||
queue.blocking_dispatch(&mut state).context("waiting for locked")?;
|
||||
if state.finished {
|
||||
return Ok(SessionOutcome::Denied);
|
||||
}
|
||||
if !state.locked {
|
||||
bail!("compositor never acknowledged the session lock");
|
||||
}
|
||||
}
|
||||
info!("session lock acquired");
|
||||
|
||||
let user = auth::session_user().map_err(|e| anyhow::anyhow!(e))?;
|
||||
|
|
|
|||
|
|
@ -314,7 +314,19 @@ Singleton {
|
|||
// We also notify logind when it is available so other consumers see
|
||||
// the standard session event. The safe lock does not depend on that
|
||||
// asynchronous notification returning successfully.
|
||||
//
|
||||
// Notify ONLY on the unlocked->locked edge. hypridle's lock_cmd
|
||||
// fires on logind's Lock signal, so an unconditional
|
||||
// `loginctl lock-session` here echoes back through logind ->
|
||||
// hypridle -> this function forever. Observed on the phone: a
|
||||
// sustained storm of lock requests (several per second, for hours)
|
||||
// that re-locked the screen seconds after every unlock and chewed
|
||||
// battery all night.
|
||||
const alreadyLocked = GlobalStates.screenLocked;
|
||||
GlobalStates.screenLocked = true;
|
||||
if (alreadyLocked) {
|
||||
return { ok: true, status: "already-locked" };
|
||||
}
|
||||
if (root.hasLoginctl) {
|
||||
const notified = root.runVerb("lock", ["loginctl", "lock-session"]);
|
||||
return notified
|
||||
|
|
|
|||
Loading…
Reference in a new issue