Files
omarchycn/bin/omarchy-hyprland-monitor-watch
T
David Heinemeier HanssonandClaude Fable 5 d548b730e0 Only run the clamshell poll while docked, not for every laptop
The reconciliation poll (added in "Fix clamshell display recovery") is a
backstop for lid transitions the Hyprland switch binds can miss across
suspend/resume. But the internal panel is only ever disabled while the
laptop is docked — lid shut with an external monitor active — so the poll
has nothing to reconcile when undocked. Running it 24/7 on every laptop
wakes the CPU every 2s for no reason, which is exactly the case where
battery matters most.

Start the poll when an external monitor is present and stop it when the
last one goes away (driven by the socat monitor-add/remove watch), gated
on omarchy-hw-laptop. Undocked laptops and desktops now never poll; a
docked laptop keeps the 2s recovery poll. Switch the event reader from a
pipe to process substitution so it runs in the main shell and can manage
the poll's lifetime.

Desktop verified: no poll runs even with external monitors attached. The
docked-laptop start/stop path needs a check on real laptop hardware.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-20 08:15:09 -07:00

69 lines
1.9 KiB
Bash
Executable File

#!/bin/bash
# omarchy:summary=Watch Hyprland monitor events and recover monitor toggles when a monitor is removed
SOCKET="$XDG_RUNTIME_DIR/hypr/$HYPRLAND_INSTANCE_SIGNATURE/.socket2.sock"
LOCK="${XDG_RUNTIME_DIR:-/tmp}/omarchy-monitor-clamshell.lock"
sync_clamshell() {
(
flock -n 9 || exit 0
omarchy-hyprland-monitor-clamshell
) 9>"$LOCK"
}
sync_clamshell_after_monitor_change() {
sync_clamshell
(
for delay in 1 3 7; do
sleep "$delay"
sync_clamshell
done
) &
}
poll_clamshell_state() {
while true; do
sleep 2
sync_clamshell
done
}
# The internal panel is only ever disabled while a laptop is docked (lid shut
# with an external monitor active), so the reconciliation poll only has anything
# to reconcile in that window. Run it while docked, stop it when undocked, and
# never on a machine without a lid. Lid open/close itself is handled by the
# Hyprland "switch:*:Lid Switch" binds; this poll is the recovery backstop for
# drift those binds can miss (e.g. across suspend/resume).
poll_pid=""
sync_poll_state() {
if omarchy-hw-laptop && omarchy-hyprland-monitor-external-active; then
if [[ -z $poll_pid ]] || ! kill -0 "$poll_pid" 2>/dev/null; then
poll_clamshell_state &
poll_pid=$!
fi
elif [[ -n $poll_pid ]]; then
kill "$poll_pid" 2>/dev/null
poll_pid=""
fi
}
sync_clamshell_after_monitor_change
sync_poll_state
# Process substitution (not a pipe) keeps this loop in the main shell, so
# sync_poll_state can start and stop the background poll as monitors come and go.
while read -r event; do
case "$event" in
monitoradded\>\>*|monitoraddedv2\>\>*)
sync_clamshell_after_monitor_change
sync_poll_state
;;
monitorremoved\>\>*|monitorremovedv2\>\>*)
sync_clamshell_after_monitor_change
sync_poll_state
;;
esac
done < <(socat -U - "UNIX-CONNECT:$SOCKET")