- 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)
63 lines
2.1 KiB
Bash
Executable File
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
|