# 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 |restore |apply |forget ]" >&2 exit 64 ;; esac ''