diff --git a/bin/omarchy-hw-clamshell b/bin/omarchy-hw-clamshell new file mode 100755 index 00000000..0792e6ec --- /dev/null +++ b/bin/omarchy-hw-clamshell @@ -0,0 +1,16 @@ +#!/bin/bash + +# omarchy:summary=Returns true when clamshell mode is active +# omarchy:hidden=true + +lid_closed=false + +for state in /proc/acpi/button/lid/*/state; do + [[ -r $state ]] || continue + if [[ $(< "$state") == *"closed"* ]]; then + lid_closed=true + break + fi +done + +[[ $lid_closed == "true" ]] && omarchy-hw-external-monitors diff --git a/bin/omarchy-hyprland-monitor-clamshell b/bin/omarchy-hyprland-monitor-clamshell new file mode 100755 index 00000000..8f5675d2 --- /dev/null +++ b/bin/omarchy-hyprland-monitor-clamshell @@ -0,0 +1,11 @@ +#!/bin/bash + +# omarchy:summary=Apply clamshell display state to Hyprland monitors +# omarchy:hidden=true + +omarchy-hyprland-monitor-internal recover >/dev/null 2>&1 || true +omarchy-hyprland-monitor-internal-mirror recover >/dev/null 2>&1 || true + +if omarchy-hw-clamshell; then + omarchy-hyprland-monitor-internal off >/dev/null 2>&1 || true +fi diff --git a/bin/omarchy-hyprland-monitor-watch b/bin/omarchy-hyprland-monitor-watch index 7379fc68..e03b8f46 100755 --- a/bin/omarchy-hyprland-monitor-watch +++ b/bin/omarchy-hyprland-monitor-watch @@ -4,22 +4,33 @@ SOCKET="$XDG_RUNTIME_DIR/hypr/$HYPRLAND_INSTANCE_SIGNATURE/.socket2.sock" -recover_internal_monitor_toggles() { - omarchy-hyprland-monitor-internal recover - omarchy-hyprland-monitor-internal-mirror recover +sync_clamshell() { + omarchy-hyprland-monitor-clamshell } recover_after_monitor_removal() { - recover_internal_monitor_toggles + sync_clamshell ( sleep 1 - recover_internal_monitor_toggles + sync_clamshell + ) & +} + +sync_clamshell_after_monitor_change() { + sync_clamshell + + ( + sleep 1 + sync_clamshell ) & } socat -U - "UNIX-CONNECT:$SOCKET" | while read -r event; do case "$event" in + monitoradded\>\>*|monitoraddedv2\>\>*) + sync_clamshell_after_monitor_change + ;; monitorremoved\>\>*|monitorremovedv2\>\>*) recover_after_monitor_removal ;; diff --git a/bin/omarchy-system-sleep-lock b/bin/omarchy-system-sleep-lock index ec4d5748..3d9e5df5 100755 --- a/bin/omarchy-system-sleep-lock +++ b/bin/omarchy-system-sleep-lock @@ -10,16 +10,15 @@ if [[ ! $wait_attempts =~ ^[0-9]+$ ]] || (( wait_attempts < 1 )); then wait_attempts=100 fi -recover_internal_monitor_toggles() { - omarchy-hyprland-monitor-internal recover >/dev/null 2>&1 || true - omarchy-hyprland-monitor-internal-mirror recover >/dev/null 2>&1 || true +sync_clamshell() { + omarchy-hyprland-monitor-clamshell >/dev/null 2>&1 || true } -recover_internal_monitor_toggles +sync_clamshell omarchy-shell lock lock >/dev/null 2>&1 || exit 1 for (( attempt = 0; attempt < wait_attempts; attempt++ )); do - (( attempt % 5 == 0 )) && recover_internal_monitor_toggles + (( attempt % 5 == 0 )) && sync_clamshell status=$(omarchy-shell lock status 2>/dev/null || true) diff --git a/bin/omarchy-system-wake b/bin/omarchy-system-wake index e5b90389..f558007f 100755 --- a/bin/omarchy-system-wake +++ b/bin/omarchy-system-wake @@ -7,3 +7,4 @@ omarchy-brightness-display on omarchy-brightness-keyboard restore +omarchy-hyprland-monitor-clamshell >/dev/null 2>&1 || true diff --git a/test/shell.d/monitor-recovery-test.sh b/test/shell.d/monitor-recovery-test.sh index 65fd41b8..9c9a9083 100755 --- a/test/shell.d/monitor-recovery-test.sh +++ b/test/shell.d/monitor-recovery-test.sh @@ -6,18 +6,36 @@ source "$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)/base-test.sh" monitor_watch="$ROOT/bin/omarchy-hyprland-monitor-watch" sleep_lock="$ROOT/bin/omarchy-system-sleep-lock" +system_wake="$ROOT/bin/omarchy-system-wake" +clamshell="$ROOT/bin/omarchy-hyprland-monitor-clamshell" lock_service="$ROOT/shell/plugins/lock/Service.qml" +hw_clamshell="$ROOT/bin/omarchy-hw-clamshell" grep -F 'recover_after_monitor_removal' "$monitor_watch" >/dev/null grep -F 'sleep 1' "$monitor_watch" >/dev/null -grep -F 'omarchy-hyprland-monitor-internal recover' "$monitor_watch" >/dev/null -grep -F 'omarchy-hyprland-monitor-internal-mirror recover' "$monitor_watch" >/dev/null +grep -F 'omarchy-hyprland-monitor-clamshell' "$monitor_watch" >/dev/null pass "monitor watcher retries internal monitor recovery after removal" -grep -F 'omarchy-hyprland-monitor-internal recover >/dev/null 2>&1 || true' "$sleep_lock" >/dev/null -grep -F 'omarchy-hyprland-monitor-internal-mirror recover >/dev/null 2>&1 || true' "$sleep_lock" >/dev/null -grep -F '(( attempt % 5 == 0 )) && recover_internal_monitor_toggles' "$sleep_lock" >/dev/null -pass "sleep lock recovers internal monitor toggles while waiting for secure lock" +grep -F 'monitoradded\>\>*|monitoraddedv2\>\>*)' "$monitor_watch" >/dev/null +grep -F 'omarchy-hyprland-monitor-clamshell' "$monitor_watch" >/dev/null +pass "monitor watcher disables the internal monitor after closed-lid external hotplug" + +grep -F '/proc/acpi/button/lid/*/state' "$hw_clamshell" >/dev/null +grep -F 'omarchy-hw-external-monitors' "$hw_clamshell" >/dev/null +pass "clamshell helper detects closed-lid external monitor state" + +grep -F 'omarchy-hyprland-monitor-internal recover >/dev/null 2>&1 || true' "$clamshell" >/dev/null +grep -F 'omarchy-hyprland-monitor-internal-mirror recover >/dev/null 2>&1 || true' "$clamshell" >/dev/null +grep -F 'omarchy-hw-clamshell' "$clamshell" >/dev/null +grep -F 'omarchy-hyprland-monitor-internal off >/dev/null 2>&1 || true' "$clamshell" >/dev/null +pass "clamshell monitor sync recovers stale toggles and disables internal display" + +grep -F 'omarchy-hyprland-monitor-clamshell >/dev/null 2>&1 || true' "$sleep_lock" >/dev/null +grep -F '(( attempt % 5 == 0 )) && sync_clamshell' "$sleep_lock" >/dev/null +pass "sleep lock syncs clamshell display state while waiting for secure lock" + +grep -F 'omarchy-hyprland-monitor-clamshell >/dev/null 2>&1 || true' "$system_wake" >/dev/null +pass "system wake resyncs clamshell display state" grep -F 'lock-pending: no-real-screen' "$lock_service" >/dev/null grep -F 'lock-pending: screen-stabilizing' "$lock_service" >/dev/null