fix(idle): #127 — input wakes the screen compositor-side; revert the clamshell skip
All checks were successful
Check / eval (push) Successful in 3m33s
All checks were successful
Check / eval (push) Successful in 3m33s
The idle brick was never DPMS. Hyprland ships misc:key_press_enables_dpms
and misc:mouse_move_enables_dpms OFF, so hypridle's on-resume was the ONLY
caller of `dpms on` in the session — an idle daemon with the display held
hostage behind it. hypridle then deadlocks on an unfixed upstream bug
(hyprwm/hypridle#171, "Disconnected from pollfd id 1", open since 2025-09,
absent from 0.1.7 and main) and hangs WITHOUT exiting, so Restart=always
never fires and systemd still reports it active (running). Blank + deadlock
= a black seat no keypress can escape. That was the brick: two mundane bugs,
not one exotic DRM state. 19 disconnects in 11 days on the dev box, incl.
the incident day; the compositor is healthy through all of them.
Both options on, so the wake lives in the compositor where no daemon can
lose it — a dead hypridle now costs auto-lock, not the machine. The #127
clamshell DPMS-off skip (060bf52) is therefore reverted: idle blanks again
in every dock/lid state, and the 27" panels stop being held lit all night
under a static lock screen. #135 (an option to opt back in) is retired
unbuilt — there is nothing left to opt into.
TEST B's first run was invalid and nearly bought the wrong answer: it ran
`hyprctl dispatch dpms off` by hand, which no wake path watches, so "input
did not wake it" was guaranteed on any hardware. The undocked control
returning the SAME result — where the theory demanded a difference — is what
exposed it. Driven through hypridle instead, input wakes it in all three
configurations, including clamshell-docked on the sole live output.
Filed from the diagnosis: #146 (hypridle dies silently, systemd calls it
healthy — the surviving bug) and #147 (a re-login leaves session units dead;
graphical-session.target never re-enters, which is why Waybar already runs
from exec-once).
V3 on the incident hardware (AMD dev box, clamshell): hypridle deliberately
stopped, `dpms off` -> keypress woke it in 6s; the identical test before the
options stayed black the full 45s. V1: nix flake check, checks.option-docs,
checks.clamshell-logind, checks.docking-ux, checks.display-profiles; read the
emitted hyprland.conf/hypridle.conf rather than the source.
V3 pending: the fix is proven via runtime `hyprctl keyword`, not yet from our
own Nix — HARDWARE-QUEUE re-checks it after nomarchy-home + relogin.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -1097,6 +1097,20 @@ in
|
||||
# instead of compositor black — on light themes the black flash
|
||||
# reads as a glitch (item 28c, seen in the capture harness).
|
||||
background_color = rgb c.base;
|
||||
|
||||
# #127, and the whole answer to it. Hyprland ships both of these
|
||||
# OFF, so nothing in the compositor turned a blanked screen back
|
||||
# on: hypridle's on-resume was the ONLY caller of `dpms on` in the
|
||||
# session — an idle daemon as a single point of failure with the
|
||||
# display behind it. hypridle then deadlocks on an unfixed upstream
|
||||
# bug (hyprwm/hypridle#171: "Disconnected from pollfd id 1", open,
|
||||
# not in 0.1.7 or main) and hangs WITHOUT exiting, so Restart=always
|
||||
# never fires and systemd still reports it active. Blank + deadlock
|
||||
# = a black seat no keypress could escape. That was the brick.
|
||||
# These put the wake in the compositor, where no daemon can lose it;
|
||||
# a dead hypridle now costs auto-lock, not the machine.
|
||||
key_press_enables_dpms = lib.mkDefault true;
|
||||
mouse_move_enables_dpms = lib.mkDefault true;
|
||||
};
|
||||
|
||||
# First boot of every installed image was showing "Hyprland updated to
|
||||
|
||||
@@ -51,34 +51,6 @@ let
|
||||
inherit pkgs;
|
||||
displayTransition = miniTransition;
|
||||
}).displayWakeTool;
|
||||
|
||||
# #127 mitigation (2026-07-15): blanking the *only* live output with the
|
||||
# lid shut (eDP disabled) left a black seat that sometimes would not
|
||||
# recover (and even VT switch looked dead). Skip DPMS-off when any laptop
|
||||
# internal is present but not enabled — i.e. CLAMSHELL, which is narrower
|
||||
# than "docked" and is deliberately what the incident was (#136, Bernardo:
|
||||
# laptop closed). Docked with the lid OPEN keeps every output enabled and
|
||||
# still blanks; that case is untested, not exempted. Lock still runs at 5 min.
|
||||
# Revisit: intentional DPMS-on-dock once wake is trustworthy — see
|
||||
# BACKLOG #127 and LATER "DPMS when docked".
|
||||
dpmsOff = pkgs.writeShellScript "nomarchy-dpms-off" ''
|
||||
set -euo pipefail
|
||||
jq=${pkgs.jq}/bin/jq
|
||||
internals=$(hyprctl monitors all -j 2>/dev/null \
|
||||
| $jq -r '.[] | select(.name | test("^(eDP|LVDS|DSI)")) | .name' \
|
||||
2>/dev/null || true)
|
||||
if [ -n "$internals" ]; then
|
||||
for m in $internals; do
|
||||
if ! hyprctl monitors -j 2>/dev/null \
|
||||
| $jq -e --arg m "$m" 'any(.[]; .name == $m)' >/dev/null 2>&1; then
|
||||
${pkgs.util-linux}/bin/logger -t nomarchy-idle -- \
|
||||
"dpms-off skipped: clamshell (internal $m present but off)"
|
||||
exit 0
|
||||
fi
|
||||
done
|
||||
fi
|
||||
hyprctl dispatch dpms off
|
||||
'';
|
||||
in
|
||||
{
|
||||
config = lib.mkIf cfg.idle.enable {
|
||||
@@ -182,8 +154,14 @@ in
|
||||
{ timeout = 300; on-timeout = "loginctl lock-session"; }
|
||||
{
|
||||
timeout = 600;
|
||||
# Docked: skip blanking sole output (#127). Undocked: DPMS off.
|
||||
on-timeout = "${dpmsOff}";
|
||||
# Blanks in every dock/lid state — the #127 clamshell skip is
|
||||
# gone: it was a cure for a cause that does not exist (proven on
|
||||
# hardware 2026-07-16, see ROADMAP). The wake no longer depends
|
||||
# on this daemon surviving — misc:{key_press,mouse_move}_enables_dpms
|
||||
# in hyprland.nix means input wakes the screen compositor-side.
|
||||
on-timeout = "hyprctl dispatch dpms off";
|
||||
# Same rescue path as after_sleep (#127): re-enables a disabled
|
||||
# internal, which plain `dpms on` cannot do.
|
||||
on-resume = "${lib.getExe displayWake}";
|
||||
}
|
||||
# Suspend only on battery, and sooner than the old fixed 30 min
|
||||
|
||||
Reference in New Issue
Block a user