fix(display): docked idle wake + dump without SSH (#127)
All checks were successful
Check / eval (push) Successful in 3m44s

Recover from DPMS/lock blackout without a second machine: undock/enable
always issue dpms on; hypridle on-resume and after_sleep run
nomarchy-display-wake (zero-output undock rescue + auto-dump).
nomarchy-display-dump writes ~/nomarchy-display-dump-*.txt (SUPER+SHIFT+D
or Ctrl+Alt+F3 TTY). HARDWARE-QUEUE #127 no longer requires SSH.

V1: nix flake check --no-build. V3: docked idle on AMD box still open.
This commit is contained in:
2026-07-15 12:37:00 +01:00
parent 52d1581131
commit e9ab0d8d64
7 changed files with 224 additions and 26 deletions

View File

@@ -19,6 +19,52 @@ let
done
exit 1
'';
# Same transition binary hyprland.nix puts on PATH — re-derived here so
# hypridle can pin absolute store paths (thin PATH in the user unit).
# Must stay in lockstep with modules/home/hyprland.nix's transition.
# We only need undock/enable + dpms; call via PATH-installed name is
# fragile, so wake embeds the transition from a minimal import chain:
# dump+wake live in display-tools.nix; transition is the public name
# on PATH after HM activation — wake script uses absolute path from
# a local reimplementation of just the enable/dpms rescue when the
# full transition package isn't injectable without circular imports.
#
# Practical approach: invoke `nomarchy-display-wake` from PATH after
# activation; for the unit Exec lines use a wrapper that prefers the
# store path of a wake script built with transition = null fallback
# (hyprctl keyword + reload + dpms) matching undock's public contract.
displayWake =
let
# Minimal undock-equivalent for idle's store path (no circular dep on
# hyprland.nix). Prefer the real transition when on PATH at runtime.
miniTransition = pkgs.writeShellScriptBin "nomarchy-display-transition" ''
set -u
case "''${1:-}" in
undock|enable)
internal="''${2:-}"
[ -n "$internal" ] || exit 64
hyprctl keyword monitor "$internal,preferred,auto,1" >/dev/null 2>&1 || true
if ! hyprctl monitors -j 2>/dev/null \
| ${pkgs.jq}/bin/jq -e --arg m "$internal" 'any(.[]; .name == $m)' \
>/dev/null 2>&1; then
hyprctl reload >/dev/null 2>&1 || true
sleep 0.5
fi
hyprctl keyword monitor "$internal,preferred,auto,1" >/dev/null 2>&1 || true
hyprctl dispatch dpms on >/dev/null 2>&1 || true
hyprctl monitors -j 2>/dev/null \
| ${pkgs.jq}/bin/jq -e --arg m "$internal" 'any(.[]; .name == $m)' \
>/dev/null 2>&1
;;
*) exit 64 ;;
esac
'';
in
(import ./display-tools.nix {
inherit pkgs;
displayTransition = miniTransition;
}).displayWakeTool;
in
{
config = lib.mkIf cfg.idle.enable {
@@ -111,7 +157,10 @@ in
# can't be safely dropped after the fact — killing the locker trips
# its "go to a tty" crash failsafe). See nomarchy-lock-before-sleep
# in modules/nixos/default.nix.
after_sleep_cmd = "hyprctl dispatch dpms on";
# #127: not only dpms on — if dock mode left zero enabled
# outputs (eDP disabled + external gone/black), re-enable the
# internal. Absolute store path: hypridle's PATH is thin.
after_sleep_cmd = "${lib.getExe displayWake}";
};
listener = [
# Lock and screen-off are the same on either power source —
@@ -120,7 +169,8 @@ in
{
timeout = 600;
on-timeout = "hyprctl dispatch dpms off";
on-resume = "hyprctl dispatch dpms on";
# Same rescue path as after_sleep (#127).
on-resume = "${lib.getExe displayWake}";
}
# Suspend only on battery, and sooner than the old fixed 30 min
# (it now only fires unplugged). Plugged in, the machine stays