From b63616422f427bb778f49f573e6cc197e29c7b4f Mon Sep 17 00:00:00 2001 From: Matthias Nitsch <53911+fuchsblau@users.noreply.github.com> Date: Sat, 22 Aug 2026 13:43:46 +0200 Subject: [PATCH] Leave an auto-scaled internal panel alone in clamshell recovery (#7581) With the default scale = "auto", sync_internal_scale read the config, rejected "auto" as non-numeric, fell back to the hardcoded default 2, and force-applied it whenever the compositor's auto resolution differed. Since the script runs from omarchy-system-wake after every idle cycle, the panel flapped between 2 and auto's own value (1.5666667 on a 198 DPI panel) on every wake/reload pair. A config without a usable number -- "auto", or an expression only Hyprland's Lua can evaluate -- delegates the scale to the compositor: whatever it resolved for the enabled panel is the configured scale, so there is nothing to correct. Recovery of a disabled panel is unchanged and still re-enables it with the remembered scale, falling back to the historical default 2. Fixes #7265. Also the scale-revert half of #7301. Claude-Session: https://claude.ai/code/session_01L4Z6GimYhR1Kpsir24VAPF Co-authored-by: Claude Fable 5 --- bin/omarchy-hyprland-monitor-clamshell | 26 +++++++- test/shell.d/monitor-clamshell-scale-test.sh | 68 ++++++++++++++++++-- 2 files changed, 87 insertions(+), 7 deletions(-) diff --git a/bin/omarchy-hyprland-monitor-clamshell b/bin/omarchy-hyprland-monitor-clamshell index a55e2028..cbd1b33f 100755 --- a/bin/omarchy-hyprland-monitor-clamshell +++ b/bin/omarchy-hyprland-monitor-clamshell @@ -118,9 +118,17 @@ remember_internal_scale() { store_internal_scale "$scale" } +# $1 is the configured scale when the caller has already read it, so one sync +# does not parse the config twice. read_monitor_scale() { local scale - scale=$(configured_monitor_scale) + + if (( $# )); then + scale="$1" + else + scale=$(configured_monitor_scale) + fi + if valid_scale "$scale"; then echo "$scale" return @@ -159,11 +167,25 @@ enable_internal_output() { sync_internal_scale() { [[ -n $INTERNAL ]] || return 0 + local configured_scale local desired_scale local active_scale - desired_scale=$(read_monitor_scale) + configured_scale=$(configured_monitor_scale) active_scale=$(current_internal_scale) + + # A config without a usable number -- the default "auto", or an expression + # only Hyprland's Lua can evaluate -- delegates the scale to the compositor, + # so whatever it resolved for the enabled panel IS the configured scale. + # There is no number to correct it toward: substituting one makes the scale + # flap between that number and the compositor's own value on every idle-wake. + # Only a panel that is off altogether still gets a hand below, from the + # remembered scale. + if ! valid_scale "$configured_scale" && valid_scale "$active_scale"; then + return 0 + fi + + desired_scale=$(read_monitor_scale "$configured_scale") scales_match "$active_scale" "$desired_scale" && return 0 enable_internal_output "$desired_scale" diff --git a/test/shell.d/monitor-clamshell-scale-test.sh b/test/shell.d/monitor-clamshell-scale-test.sh index 07509f36..1ba617cb 100644 --- a/test/shell.d/monitor-clamshell-scale-test.sh +++ b/test/shell.d/monitor-clamshell-scale-test.sh @@ -70,6 +70,18 @@ local omarchy_monitor_scale = "auto" LUA } +# The shipped default's own shape: the catch-all rule hands the panel a +# bare-word reference to the "auto" local. +write_default_auto_config() { + cat >"$monitor_lua" <<'LUA' +local omarchy_gdk_scale = 2 +local omarchy_monitor_scale = "auto" + +hl.env("GDK_SCALE", tostring(omarchy_gdk_scale)) +hl.monitor({ output = "", mode = "preferred", position = "auto", scale = omarchy_monitor_scale }) +LUA +} + write_internal_monitor_config() { cat >"$monitor_lua" <<'LUA' hl.monitor({ output = "eDP-1", mode = "preferred", position = "0x0", scale = 1.25 }) @@ -213,20 +225,39 @@ run_clamshell() { "$ROOT/bin/omarchy-hyprland-monitor-clamshell" } +# Regression (#7265, #7301): with scale = "auto" the compositor's resolution of +# it IS the configured scale, not a transient to correct. Forcing a number here +# made the scale flap: every idle-wake applied the fallback 2, every config +# reload resolved auto back to the panel's own value. write_auto_monitor_config : >"$eval_log" OMARCHY_TEST_INTERNAL_SCALE=3 run_clamshell -grep -F 'scale = 2' "$eval_log" >/dev/null || fail "clamshell recovery ignores transient auto scale" -! grep -F 'scale = "auto"' "$eval_log" >/dev/null || fail "clamshell recovery does not apply auto scale" +! grep -F 'scale = ' "$eval_log" >/dev/null || fail "clamshell recovery leaves an auto-scaled panel alone" [[ ! -f $scale_state ]] || fail "clamshell recovery does not remember transient scale" -pass "clamshell recovery ignores transient auto scale" +pass "clamshell recovery leaves an auto-scaled panel alone" -write_auto_monitor_config +# The same delegation through the shipped default config, where "auto" reaches +# the panel via the catch-all rule's bare-word reference to the local. +write_default_auto_config : >"$eval_log" -OMARCHY_TEST_INTERNAL_SCALE=2 run_clamshell +OMARCHY_TEST_INTERNAL_SCALE=3 run_clamshell +! grep -F 'scale = ' "$eval_log" >/dev/null || fail "clamshell recovery leaves the shipped auto default alone" +pass "clamshell recovery leaves the shipped auto default alone" + +# A numeric config keeps both sync behaviors: a matching active scale is not +# reapplied, and a drifted one is corrected back to the configured value. +write_internal_monitor_config +: >"$eval_log" +OMARCHY_TEST_INTERNAL_SCALE=1.25 run_clamshell ! grep -F 'scale = ' "$eval_log" >/dev/null || fail "clamshell recovery does not reapply matching scale" pass "clamshell recovery avoids redundant scale apply" +write_internal_monitor_config +: >"$eval_log" +OMARCHY_TEST_INTERNAL_SCALE=3 run_clamshell +grep -F 'scale = 1.25' "$eval_log" >/dev/null || fail "clamshell recovery corrects a drifted numeric scale" +pass "clamshell recovery corrects a drifted numeric scale" + write_auto_monitor_config : >"$eval_log" OMARCHY_TEST_INTERNAL_SCALE=1.6 OMARCHY_TEST_EXTERNAL_ACTIVE=true OMARCHY_TEST_CLAMSHELL=true run_clamshell @@ -240,6 +271,15 @@ grep -F 'scale = 1.6' "$eval_log" >/dev/null || fail "clamshell recovery uses re ! grep -F 'scale = "auto"' "$eval_log" >/dev/null || fail "clamshell recovery avoids auto after disabled internal display" pass "clamshell recovery uses remembered internal scale" +# Recovery of a panel that is off, under an auto config with nothing +# remembered, still needs a number: the historical default 2. +write_auto_monitor_config +rm -f "$scale_state" +: >"$eval_log" +OMARCHY_TEST_INTERNAL_DISABLED=true run_clamshell +grep -F 'scale = 2' "$eval_log" >/dev/null || fail "clamshell recovery falls back to the default scale" +pass "clamshell recovery falls back to the default scale" + write_internal_monitor_config : >"$eval_log" OMARCHY_TEST_INTERNAL_DISABLED=true run_clamshell @@ -312,6 +352,15 @@ OMARCHY_TEST_INTERNAL_DISABLED=true run_clamshell grep -F 'scale = 1.75' "$eval_log" >/dev/null || fail "clamshell recovery leaves an expression scale unresolved" pass "clamshell recovery leaves an expression scale unresolved" +# An expression is Hyprland's to evaluate, not this parser's: like "auto", it +# names no number to correct an enabled panel toward. +write_expression_scale_config +remember_scale 1.75 +: >"$eval_log" +OMARCHY_TEST_INTERNAL_SCALE=3 run_clamshell +! grep -F 'scale = ' "$eval_log" >/dev/null || fail "clamshell recovery leaves an expression-scaled panel alone" +pass "clamshell recovery leaves an expression-scaled panel alone" + # A quoted value is a string, not a reference to a local of the same name. write_shadowed_auto_config remember_scale 1.75 @@ -320,6 +369,15 @@ OMARCHY_TEST_INTERNAL_DISABLED=true run_clamshell grep -F 'scale = 1.75' "$eval_log" >/dev/null || fail "clamshell recovery does not resolve a quoted scale against a local" pass "clamshell recovery does not resolve a quoted scale against a local" +# An explicit "auto" on the internal rule delegates just the same: while the +# panel is enabled, it is not corrected toward the remembered scale. +write_shadowed_auto_config +remember_scale 1.75 +: >"$eval_log" +OMARCHY_TEST_INTERNAL_SCALE=3 run_clamshell +! grep -F 'scale = ' "$eval_log" >/dev/null || fail "clamshell recovery leaves an explicitly auto panel alone" +pass "clamshell recovery leaves an explicitly auto panel alone" + # Half of `3 / 2` is no better inside the rule than inside a local. write_expression_rule_config remember_scale 1.75