Files
Frank abb8a2a9d0 fix(autoswitch): WorkspacesChanged output-set diff; unit paths; deploy symlinks
- niri 26.04 has no hotplug event: OutputConfigChanged is only an ACK
  (niri-ipc). Watch WorkspacesChanged, diff sorted output set, apply on
  change. Proven live: eDP-1 off/on -> 3 output-set transitions, each
  matched + applied + reloaded.
- unit: %h/.local/bin/desk-autoswitch.sh, quoted Environment, drop
  User/Group (user unit); add desk-autoswitch.sh bin symlink
- desk.sh: silence validate/load-config/notify stdout
- deploy.sh: deploy repo symlinks (find -type l), so .local/bin links land
- docs: event semantics corrected (displays diagram, log)
2026-10-09 14:46:17 -07:00

63 lines
2.1 KiB
Bash
Executable File

#!/usr/bin/env bash
# scripts/desk-autoswitch.sh — hotplug daemon: apply the matching desk profile
# when the SET OF OUTPUTS changes.
#
# niri has no dedicated hotplug event (verified on 26.04):
# - WorkspacesChanged carries every workspace's `output`, so the sorted
# unique set of outputs changes exactly on connect/disconnect.
# - OutputConfigChanged is only an ACK that an IPC/config output change was
# Applied (niri-ipc), NOT a hotplug signal.
#
# Usage: desk-autoswitch.sh watch
#
# Environment overrides:
# DESK_AUTO_HOSTNAME only run on this hostname (default: blankpad)
# DESK_SCRIPT path to desk.sh (default ~/.local/bin/desk.sh)
# DESK_NIRI niri binary (default niri)
# DESK_DEBOUNCE_S min seconds between applies (default 1.5)
set -euo pipefail
HOST="${DESK_AUTO_HOSTNAME:-blankpad}"
DESK="${DESK_SCRIPT:-$HOME/.local/bin/desk.sh}"
NIRI="${DESK_NIRI:-niri}"
DEBOUNCE="${DESK_DEBOUNCE_S:-1.5}"
usage() { sed -n '2,18p' "$0" | sed 's/^# \{0,1\}//'; }
outputs_from_event() { # $1 = one JSON event line
jq -r '[.WorkspacesChanged.workspaces[]?.output // empty] | unique | join(" ")' <<<"$1" 2>/dev/null || true
}
watch_mode() {
[ "$(hostname)" = "$HOST" ] || { echo "desk-autoswitch: not on $HOST, exiting"; exit 0; }
echo "desk-autoswitch: initial match"
"$DESK" match --apply || true
local seen="" last_seen="" last=0 now
# One JSON event per line; reconnect forever (niri restart drops the stream).
while "$NIRI" msg --json event-stream 2>/dev/null | while IFS= read -r line; do
case "$line" in
*WorkspacesChanged*)
seen="$(outputs_from_event "$line")"
[ -n "$seen" ] || continue
[ "$seen" = "$last_seen" ] && continue
last_seen="$seen"
now="$(date +%s.%N)"
# swallow bursts inside the debounce window
if awk -v l="$last" -v n="$now" -v d="$DEBOUNCE" 'BEGIN { exit !(n - l >= d) }'; then
last="$now"
echo "desk-autoswitch: outputs changed [$seen]"
"$DESK" match --apply || true
fi
;;
esac
done; do :; done
}
case "${1:-}" in
watch) watch_mode ;;
-h|--help) usage ;;
*) usage >&2; exit 1 ;;
esac