#!/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")