refactor(display): extract the shared display-transition + keyboard tools (#150)
All checks were successful
Check / eval (push) Successful in 4m29s

The full nomarchy-display-transition (dock/undock/enable — logging, the
lid-inhibitor gate, restore_keyboards, the failure dump) and the
nomarchy-keyboard-layout helper it calls move out of hyprland.nix into
leaf files (modules/home/display-transition.nix, keyboard-tool.nix),
imported by both hyprland.nix and idle.nix.

This kills the lossy ~22-line mini idle.nix carried under the same binary
name to dodge a circular import. The hypridle wake path (after_sleep /
DPMS on-resume) now runs the real undock — restore_keyboards after a
reload and the evidence dump on enable-failure — instead of a bare
keyword+reload, and the two copies can no longer drift.

Behaviour on the hyprland side is unchanged: the built
nomarchy-display-transition and nomarchy-keyboard-layout have
byte-identical store paths on HEAD vs this change (verbatim extraction).

Verify: all four files parse; home activationPackage + system toplevel
build; checks.docking-ux green (bash -n + safety-feature greps on the
extracted scripts). The tools/monitor-fallback.nix dock harness is red on
main already (the #148 reload-mid-dock assertion — identical failure on
clean HEAD; filed BACKLOG #154), so it could not gate this. The idle
wake-path behaviour change (full undock, incl. dock-intent clearing) is
V3 pending: on-hardware suspend/resume + zero-output rescue queued in
HARDWARE-QUEUE.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-07-20 19:17:56 +01:00
parent dbe07ea429
commit baf2cd2e13
7 changed files with 343 additions and 293 deletions

View File

@@ -0,0 +1,81 @@
# The per-device keyboard-layout helper (nomarchy-keyboard-layout).
# Extracted from hyprland.nix (#150) so it is built ONCE and shared: the
# Hyprland module puts it on PATH and drives it from the hotplug watcher,
# while the display-transition tool calls it (restore_keyboards) after a
# config reload. Pass the module's { pkgs, lib, config }.
{ pkgs, lib, config }:
let
sync = lib.getExe config.nomarchy.package;
# Candidate layouts offered by the new-keyboard picker: the session
# layout(s) (nomarchy.keyboard.layout, comma-split for a multi-layout
# session) plus the extra nomarchy.keyboard.layouts pool. The pool is
# deliberately NOT merged into input.kb_layout — those candidates are for
# *external* keyboards and get applied per-device by the watcher's apply().
# Loading them onto the session keyboard is what let a global switch flip
# the built-in board to the wrong layout.
pickerLayouts = lib.unique (
(lib.splitString "," config.nomarchy.keyboard.layout)
++ config.nomarchy.keyboard.layouts
);
in
# Per-device keyboard helper shared by the hotplug watcher and the manual
# System Keyboard menu. The configured candidates are listed first, then
# every XKB layout known to the system, so the feature works on a default
# install instead of silently disappearing until home.nix is edited.
pkgs.writeShellScriptBin "nomarchy-keyboard-layout" ''
set -u
sync=${sync}
layouts() {
printf '%s\n' ${lib.concatMapStringsSep " " lib.escapeShellArg pickerLayouts}
${pkgs.gawk}/bin/awk '
/^! layout/ { in_layouts=1; next }
/^!/ && in_layouts { exit }
in_layouts && NF { print $1 }
' ${pkgs.xkeyboard_config}/share/X11/xkb/rules/base.lst
}
saved_map() { "$sync" get settings.keyboard.devices 2>/dev/null || echo '{}'; }
saved_for() { saved_map | jq -r --arg k "$1" '.[$k] // empty'; }
apply_runtime() { hyprctl keyword "device[$1]:kb_layout" "$2" >/dev/null 2>&1; }
case "''${1:-}" in
layouts)
layouts | ${pkgs.gawk}/bin/awk 'NF && !seen[$0]++' ;;
devices)
hyprctl devices -j 2>/dev/null \
| jq -r '.keyboards[] | "\(.name)\t\(.active_keymap // \"unknown\")"' ;;
saved)
[ -n "''${2:-}" ] || exit 64
saved_for "$2" ;;
restore)
[ -n "''${2:-}" ] || exit 64
layout=$(saved_for "$2")
[ -n "$layout" ] && apply_runtime "$2" "$layout" ;;
apply)
[ -n "''${2:-}" ] && [ -n "''${3:-}" ] || exit 64
layouts | ${pkgs.gnugrep}/bin/grep -Fxq "$3" \
|| { notify-send "Keyboard" "Unknown XKB layout: $3"; exit 64; }
if apply_runtime "$2" "$3"; then
map=$(saved_map | jq -c --arg k "$2" --arg v "$3" '. + {($k): $v}') || exit 1
if "$sync" --quiet set settings.keyboard.devices "$map" --no-switch; then
notify-send "Keyboard" "$2 $3"
else
notify-send "Keyboard" "$2 $3 for this session, but the choice could not be saved."
exit 1
fi
else
notify-send "Keyboard" "Could not apply $3 to $2."
exit 1
fi ;;
forget)
[ -n "''${2:-}" ] || exit 64
map=$(saved_map | jq -c --arg k "$2" 'del(.[$k])') || exit 1
"$sync" --quiet set settings.keyboard.devices "$map" --no-switch || exit 1
apply_runtime "$2" ${lib.escapeShellArg config.nomarchy.keyboard.layout} || true
notify-send "Keyboard" "$2 now follows the session layout." ;;
*)
echo "usage: nomarchy-keyboard-layout [layouts|devices|saved <device>|restore <device>|apply <device> <layout>|forget <device>]" >&2
exit 64 ;;
esac
''