feat(display): blackout rescue — re-enable a disabled panel on undock
Some checks failed
Check / eval (push) Failing after 1m40s
Some checks failed
Check / eval (push) Failing after 1m40s
Bernardo's question: with "Laptop screen off" active, does the panel come back when the dock is yanked? VM answer (new maintainer harness tools/monitor-fallback.nix, softGL desktop): no — Hyprland 0.55.4 leaves the session with zero active outputs and never re-enables a soft-disabled monitor. rescue_blackout in the display hotplug watcher now re-enables every disabled output (immediate, retried, idempotent keyword + toast) whenever a monitorremoved leaves nothing active; independent of the profile auto-switch and its settings gate. Sharp edge documented rather than hidden: the zero-output state itself crashes Hyprland in the VM ~4/5 runs (aquamarine CBackend::dispatchIdle ABRT within ~0.5s — faster than any userland rescue; a control run removing the external with the panel ACTIVE survives 5/5, so the state is the trigger, not headless removal; crash reproduced with the rescue provably inert, so not caused by it). Rescue ships as defense-in-depth: it recovers every surviving case, and the worst case on real DRM is session-to-greeter on a re-lit panel instead of a black brick. Rider fix: `grep -c .` exits 1 at count zero — the ||-guard on that pipeline silently disabled the rescue exactly when it mattered. Verified: hazard V2 (VM reproduces the dead state); control discriminator 5/5; flake check green. V3 pending: real-dock undock-while-off in HARDWARE-QUEUE (run with nothing important open; crash on real DRM = rework the row to mirror or wait for a Hyprland bump). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -215,7 +215,43 @@ ${lib.concatStringsSep "\n" (lib.mapAttrsToList
|
||||
case "$v" in true|True) return 0 ;; *) return 1 ;; esac
|
||||
}
|
||||
outputs() { hyprctl monitors all -j 2>/dev/null | jq -r '.[].name' | sort; }
|
||||
|
||||
|
||||
# Blackout rescue — the session must never end up with ZERO active
|
||||
# outputs. Hyprland does NOT re-enable a soft-disabled monitor when
|
||||
# the last active one disconnects (VM-verified, tools/
|
||||
# monitor-fallback.nix): menu "Laptop screen off" + sudden undock
|
||||
# left a dead session. On every monitorremoved, if nothing is
|
||||
# active, re-enable whatever is disabled (preferred/auto — the
|
||||
# declared rule returns at the next reload/relogin). Independent of
|
||||
# the profile auto-switch and its settings gate: this is a safety
|
||||
# invariant, not a layout preference.
|
||||
active_count() {
|
||||
# NB: `grep -c .` exits 1 when the count is 0 — the case this whole
|
||||
# rescue exists for — so never `||`-guard on the pipeline status.
|
||||
hyprctl monitors -j 2>/dev/null | jq -r '.[].name' | grep -c . || true
|
||||
}
|
||||
rescue_blackout() {
|
||||
# Act IMMEDIATELY and retry: the zero-output state is not just a
|
||||
# black screen, it can kill the compositor outright — in the softGL
|
||||
# VM harness aquamarine ABRT'd (CBackend::dispatchIdle) within
|
||||
# ~0.5s of the last output going while it persisted, so shrinking
|
||||
# the window is the priority; the keyword is idempotent and the
|
||||
# retries cover a first attempt racing the removal teardown.
|
||||
tries=0
|
||||
while [ "$(active_count)" = 0 ] && [ "$tries" -lt 4 ]; do
|
||||
hyprctl monitors all -j 2>/dev/null | jq -r '.[] | select(.disabled) | .name' \
|
||||
| while read -r m; do
|
||||
[ -n "$m" ] || continue
|
||||
hyprctl keyword monitor "$m,preferred,auto,auto" >/dev/null 2>&1
|
||||
done
|
||||
tries=$((tries+1))
|
||||
sleep 1
|
||||
done
|
||||
if [ "$tries" -gt 0 ] && [ "$(active_count)" != 0 ]; then
|
||||
notify-send "Display" "Screen re-enabled — the active output disappeared." 2>/dev/null || true
|
||||
fi
|
||||
}
|
||||
|
||||
check_monitors() {
|
||||
cur=$(outputs)
|
||||
[ "$cur" = "$1" ] && return
|
||||
@@ -239,7 +275,12 @@ ${lib.concatStringsSep "\n" (lib.mapAttrsToList
|
||||
# Listen to Hyprland's IPC socket for instant hotplug reaction
|
||||
${pkgs.socat}/bin/socat -U - UNIX-CONNECT:$XDG_RUNTIME_DIR/hypr/$HYPRLAND_INSTANCE_SIGNATURE/.socket2.sock | while read -r line; do
|
||||
case "$line" in
|
||||
monitoradded*|monitorremoved*)
|
||||
monitorremoved*)
|
||||
rescue_blackout
|
||||
res=$(check_monitors "$prev")
|
||||
[ -n "$res" ] && prev="$res"
|
||||
;;
|
||||
monitoradded*)
|
||||
res=$(check_monitors "$prev")
|
||||
[ -n "$res" ] && prev="$res"
|
||||
;;
|
||||
|
||||
Reference in New Issue
Block a user