Harden display apply and recovery

This commit is contained in:
Gabriel Brown
2026-08-18 02:55:43 -04:00
parent 5671324eb2
commit 2d4b126f14
7 changed files with 422 additions and 64 deletions
+139 -13
View File
@@ -23,33 +23,144 @@ set -euo pipefail
repo_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
harness="$repo_dir/config/dot/quickshell/displays-harness.qml"
config_home="$(mktemp -d /tmp/panama-displays-config.XXXXXX)"
service="$repo_dir/config/dot/quickshell/services/Displays.qml"
page="$repo_dir/config/dot/quickshell/modules/settings/DisplaysPage.qml"
settings_page="$repo_dir/config/dot/quickshell/modules/settings/SettingsPage.qml"
monitors_lua="$repo_dir/config/dot/hypr/monitors.lua"
fail() {
printf 'displays contract: %s\n' "$1" >&2
exit 1
}
# Keep is unavailable until compositor readback exactly matches the request.
for contract in \
'property var pendingRequested:' \
'readonly property bool canConfirm:' \
'function matchesRequest(' \
'function scalesForMode(' \
'function isScaleClean('; do
rg -Fq "$contract" "$service" || fail "display service contract is missing: $contract"
done
rg -Fq 'enabled: Displays.canConfirm' "$page" \
|| fail 'Keep is enabled before the display change is verified'
rg -Fq 'options: Displays.scalesForMode(' "$page" \
|| fail 'scale choices are not filtered for the active resolution'
# Stored JSON is untyped at field level, so the Lua startup consumer is the
# final validation boundary and must support every named output it accepts.
for contract in 'valid_mode' 'valid_scale' 'valid_transform' 'pairs(displays)'; do
rg -Fq "$contract" "$monitors_lua" || fail "monitor startup validation is missing: $contract"
done
# SettingsPage headers are genuinely pinned outside its scrolling surface.
python3 - "$settings_page" <<'PY' || fail 'SettingsPage header is not pinned outside the Flickable'
import sys
text = open(sys.argv[1], encoding="utf-8").read()
loader = text.find("id: pinnedHeader")
flickable = text.find("id: pageScroll")
if loader < 0 or flickable < 0 or loader > flickable:
raise SystemExit(1)
PY
MONITORS_LUA="$monitors_lua" lua - <<'LUA' || fail 'monitor startup accepted invalid persisted geometry or ignored a named output'
package.preload["prefs"] = function()
return {
get = function()
return {
["DP-2"] = { mode = "not-a-mode", scale = -1, transform = 99 },
["HDMI-A-1"] = { mode = "1920x1080@60", scale = 1.5, transform = 1 },
["BAD OUTPUT"] = { mode = "1920x1080@60", scale = 1, transform = 0 },
}
end,
}
end
local calls = {}
hl = { monitor = function(value) table.insert(calls, value) end }
assert(loadfile(os.getenv("MONITORS_LUA")))()
local by_output = {}
for _, value in ipairs(calls) do by_output[value.output] = value end
assert(by_output["DP-2"].mode == "4500x3000@60")
assert(by_output["DP-2"].scale == 1.5)
assert(by_output["DP-2"].transform == 0)
assert(by_output["HDMI-A-1"].mode == "1920x1080@60")
assert(by_output["HDMI-A-1"].scale == 1.5)
assert(by_output["HDMI-A-1"].transform == 1)
assert(by_output["BAD OUTPUT"] == nil)
assert(by_output[""] ~= nil)
LUA
if [[ "${PANAMA_DISPLAYS_STATIC_ONLY:-0}" == "1" ]]; then
printf 'displays contract: PASS (static)\n'
exit 0
fi
config_home="$(mktemp -d /tmp/panama-displays-config.XXXXXX)"
run() { XDG_CONFIG_HOME="$config_home" qs -p "$harness" "$@"; }
status() { run ipc call displays-test status; }
original_mode=""
original_scale=""
original_transform=""
original_width=""
original_height=""
original_refresh=""
monitor_name=""
restore() {
# Through hyprctl rather than the harness: if the harness apply path is what
# is broken, the daily-driver display must still come back.
if [[ -n "$original_mode" ]]; then
hyprctl eval "hl.monitor({ output = \"$monitor_name\", mode = \"$original_mode\", scale = $original_scale, transform = $original_transform })" >/dev/null 2>&1 || true
fi
monitor_state() {
hyprctl -j monitors | jq -c --arg output "$monitor_name" '.[] | select(.name == $output)'
}
display_is_restored() {
local current
current="$(monitor_state)"
[[ -n "$current" ]] || return 1
jq -e \
--argjson width "$original_width" \
--argjson height "$original_height" \
--argjson refresh "$original_refresh" \
--argjson scale "$original_scale" \
--argjson transform "$original_transform" \
'.width == $width and .height == $height
and ((.refreshRate - $refresh) | fabs) < 0.6
and ((.scale - $scale) | fabs) < 0.001
and .transform == $transform' <<<"$current" >/dev/null
}
restore_display() {
[[ -n "$original_mode" ]] || return 0
hyprctl eval "hl.monitor({ output = \"$monitor_name\", mode = \"$original_mode\", scale = $original_scale, transform = $original_transform })" >/dev/null \
|| return 1
for _ in $(seq 1 50); do
display_is_restored && return 0
sleep 0.2
done
return 1
}
stop_harness() {
# Kill by PID, never `pkill -f displays-harness`: that pattern also matches
# any shell whose command line contains this script's text, which includes
# the invoking shell itself.
[[ -n "${harness_pid:-}" ]] && kill "$harness_pid" >/dev/null 2>&1 || true
rm -rf "$config_home"
}
trap restore EXIT
cleanup() {
local status=$?
trap - EXIT
if ! restore_display; then
printf 'displays contract: FAILED to restore %s to %s scale %s transform %s\n' \
"$monitor_name" "$original_mode" "$original_scale" "$original_transform" >&2
status=1
fi
stop_harness
exit "$status"
}
trap cleanup EXIT
XDG_CONFIG_HOME="$config_home" qs -p "$harness" --daemonize >/dev/null
harness_pid=""
@@ -69,6 +180,9 @@ state="$(status)"
monitor_name="$(jq -r .name <<<"$state")"
[[ -n "$monitor_name" ]] || fail "no display was detected: $state"
original_mode="$(jq -r '"\(.width)x\(.height)@\(.refresh)"' <<<"$state")"
original_width="$(jq -r .width <<<"$state")"
original_height="$(jq -r .height <<<"$state")"
original_refresh="$(jq -r .refresh <<<"$state")"
original_scale="$(jq -r .scale <<<"$state")"
original_transform="$(jq -r .transform <<<"$state")"
@@ -85,6 +199,7 @@ mode
scale
transform
output
dirtyScale
KINDS
# The display must not have moved for any of those.
@@ -98,16 +213,17 @@ target_scale=$(awk -v s="$original_scale" 'BEGIN { print (s == 1.25) ? 1.5 : 1.2
applied=false
for _ in $(seq 1 30); do
[[ "$(hyprctl -j monitors | jq -r '.[0].scale')" == "$target_scale" ]] && { applied=true; break; }
[[ "$(monitor_state | jq -r '.scale')" == "$target_scale" ]] && { applied=true; break; }
sleep 0.2
done
[[ "$applied" == true ]] || fail 'the scale change never reached the compositor'
[[ "$(status | jq -r .awaiting)" == "true" ]] || fail 'an applied change is not awaiting confirmation'
[[ "$(status | jq -r .canConfirm)" == "true" ]] || fail 'an applied change was never verified by compositor readback'
# Wait out the countdown. This is the whole point of the contract.
reverted=false
for _ in $(seq 1 120); do
if [[ "$(hyprctl -j monitors | jq -r '.[0].scale')" == "$original_scale" ]]; then
if [[ "$(monitor_state | jq -r '.scale')" == "$original_scale" ]]; then
reverted=true
break
fi
@@ -119,8 +235,16 @@ done
# ── A confirmed change is what writes ────────────────────────────────────────
run ipc call displays-test applyScale "$target_scale" >/dev/null
sleep 1
run ipc call displays-test confirmChange >/dev/null
[[ "$(run ipc call displays-test confirmChange)" == "false" ]] \
|| fail 'Keep accepted a display change before compositor readback'
verified=false
for _ in $(seq 1 30); do
[[ "$(status | jq -r .canConfirm)" == "true" ]] && { verified=true; break; }
sleep 0.2
done
[[ "$verified" == true ]] || fail 'the confirmed change never became safe to keep'
[[ "$(run ipc call displays-test confirmChange)" == "true" ]] \
|| fail 'Keep refused a verified display change'
sleep 0.6
[[ "$(status | jq -r .awaiting)" == "false" ]] || fail 'confirming did not clear the pending state'
[[ "$(status | jq -r .overridden)" == "true" ]] || fail 'confirming did not store the change'
@@ -134,6 +258,8 @@ run ipc call displays-test forget >/dev/null
sleep 0.6
[[ "$(status | jq -r .overridden)" == "false" ]] || fail 'forget did not clear the stored display setting'
restore_display || fail 'the final cleanup could not restore and verify the original display'
original_mode=""
stop_harness
trap - EXIT
restore
printf 'displays contract: PASS\n'