Fix screen flash when waking from sleep into the unlock screen

The lock service arms a 5s blank timer whenever the screen locks, and
input at the lock screen re-arms it. Closing the lid sprays pointer
noise over the lock surface, so the timer was routinely armed right
before suspend, froze mid-countdown, and fired moments after resume --
blanking the freshly woken unlock screen under the user.

Guard the timer with a wall-clock check: if far more time elapsed than
the interval, the countdown slept through a suspend, so take a fresh
run-up instead of blanking. This also blanks the lock screen 5s after
an untouched resume.

Two accomplices made the flash worse and hid the real bug:

- The clamshell watcher's 2s poll fired an unconditional global DPMS
  enable whenever no external monitor was active, relighting any blank
  within 2 seconds (lock-screen blanking never stuck on undocked
  laptops) and racing the resume modeset. Recovery now only wakes
  displays when it actually re-enables one.

- Every keystroke at the lock screen dispatched a redundant DPMS
  enable via omarchy-system-wake, forcing extra modesets in the
  fragile just-resumed DRM state. Brightness "on" now skips the
  dispatch when every active display is already lit.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
David Heinemeier Hansson
2026-07-19 19:42:18 -07:00
co-authored by Claude Fable 5
parent 324fd387c0
commit 54d79d9d16
4 changed files with 32 additions and 10 deletions
+19 -4
View File
@@ -108,7 +108,7 @@ Item {
resetAuthenticationState()
lockRequested = true
idleBlankTimer.restart()
armBlankTimer()
logEvent("lock-requested")
queueSessionLock()
@@ -134,9 +134,14 @@ Item {
runWake()
}
function armBlankTimer() {
idleBlankTimer.armedAt = Date.now()
idleBlankTimer.restart()
}
function runWake() {
if (!wakeProcess.running) wakeProcess.running = true
if (lockRequested) idleBlankTimer.restart()
if (lockRequested) armBlankTimer()
}
function runBlank() {
@@ -370,7 +375,17 @@ Item {
id: idleBlankTimer
interval: 5000
repeat: false
onTriggered: if (root.lockRequested && !root.authenticating) root.runBlank()
property double armedAt: 0
onTriggered: {
// A countdown frozen by suspend fires right after resume, which would
// blank the freshly woken unlock screen under the user. Wall-clock time
// exposes the gap: take a fresh run-up instead of blanking.
if (Date.now() - armedAt > interval + 2000) {
root.armBlankTimer()
return
}
if (root.lockRequested && !root.authenticating) root.runBlank()
}
}
Timer {
@@ -395,7 +410,7 @@ Item {
onAuthenticatingChanged: {
if (!lockRequested) return
if (authenticating) idleBlankTimer.stop()
else idleBlankTimer.restart()
else armBlankTimer()
}
FileView {