diff --git a/bin/omarchy-brightness-display b/bin/omarchy-brightness-display index b3c3b74d..241ee091 100755 --- a/bin/omarchy-brightness-display +++ b/bin/omarchy-brightness-display @@ -32,6 +32,10 @@ if [[ $step == "off" ]]; then hyprctl dispatch 'hl.dsp.dpms({ action = "disable" })' >/dev/null 2>&1 exit 0 elif [[ $step == "on" ]]; then + # Skip the dispatch when every active display is already lit: a redundant + # DPMS enable right after system resume forces another modeset, which blanks + # the panel for a beat (visible flash at the unlock screen). + hyprctl monitors -j 2>/dev/null | jq -e '[.[] | select(.disabled == false)] | length > 0 and all(.dpmsStatus)' >/dev/null 2>&1 && exit 0 hyprctl dispatch 'hl.dsp.dpms({ action = "enable" })' >/dev/null 2>&1 exit 0 fi diff --git a/bin/omarchy-hyprland-monitor-internal b/bin/omarchy-hyprland-monitor-internal index 37c50e87..9916f95c 100755 --- a/bin/omarchy-hyprland-monitor-internal +++ b/bin/omarchy-hyprland-monitor-internal @@ -42,13 +42,14 @@ off() { } recover() { - if ! omarchy-hyprland-monitor-external-active; then - if omarchy-hyprland-toggle-enabled $TOGGLE; then - omarchy-hyprland-toggle $TOGGLE off - fi + # Runs from the clamshell watcher every few seconds, so it must be a no-op + # unless it actually re-enables a display: an unconditional wake here undoes + # lock-screen blanking and races the resume modeset into a visible flash. + omarchy-hyprland-monitor-external-active && return 0 + omarchy-hyprland-toggle-enabled $TOGGLE || return 0 - wake - fi + omarchy-hyprland-toggle $TOGGLE off + wake } toggle() { diff --git a/shell/plugins/lock/Service.qml b/shell/plugins/lock/Service.qml index b414f287..ca6bf6bd 100644 --- a/shell/plugins/lock/Service.qml +++ b/shell/plugins/lock/Service.qml @@ -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 { diff --git a/test/shell.d/monitor-recovery-test.sh b/test/shell.d/monitor-recovery-test.sh index 063ef677..c0d002b0 100755 --- a/test/shell.d/monitor-recovery-test.sh +++ b/test/shell.d/monitor-recovery-test.sh @@ -58,7 +58,9 @@ grep -F "hyprctl dispatch 'hl.dsp.dpms({ action = \"enable\" })' >/dev/null 2>&1 grep -F 'hyprctl monitors all -j' "$monitor_internal" >/dev/null grep -F 'omarchy-hyprland-monitor-external-active' "$monitor_internal" >/dev/null grep -F 'wake' "$monitor_internal" >/dev/null +grep -F 'omarchy-hyprland-toggle-enabled $TOGGLE || return 0' "$monitor_internal" >/dev/null pass "internal monitor helper can re-enable disabled laptop displays" +pass "internal monitor recovery only wakes displays when it re-enables one" grep -F 'omarchy-hyprland-monitor-external-active' "$monitor_mirror" >/dev/null pass "internal mirror helper recovers when no active external display remains"