#!/usr/bin/env bash set -euo pipefail repo_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)" component="$repo_dir/config/dot/quickshell/modules/settings/DisplayArrangement.qml" page="$repo_dir/config/dot/quickshell/modules/settings/DisplaysPage.qml" harness="$repo_dir/config/dot/quickshell/display-arrangement-harness.qml" identify="$repo_dir/config/dot/quickshell/modules/settings/DisplayIdentify.qml" service="$repo_dir/config/dot/quickshell/services/Displays.qml" shell="$repo_dir/config/dot/quickshell/shell.qml" fail() { printf 'display arrangement contract: %s\n' "$1" >&2 exit 1 } [[ -f "$component" ]] || fail 'DisplayArrangement.qml is missing' for contract in \ 'required property var displayService' \ 'property var draftLayout:' \ 'function setDraftPosition(' \ 'function nudge(' \ 'function applyDraft(' \ 'function makePrimary(' \ 'Accessible.name: "Move "' \ 'DragHandler {' \ 'Keys.onPressed:'; do rg -Fq "$contract" "$component" \ || fail "arrangement interaction is missing: $contract" done rg -Fq 'DisplayArrangement {' "$page" \ || fail 'Displays page does not expose the arrangement canvas' rg -Fq 'visible: Displays.monitors.length > 1' "$page" \ || fail 'arrangement is shown for a single display' [[ -f "$identify" ]] || fail 'DisplayIdentify.qml is missing' for contract in \ 'model: Quickshell.screens' \ 'WlrLayershell.keyboardFocus: WlrKeyboardFocus.None' \ 'mask: Region {}' \ 'visible: Displays.identifying' \ 'text: String(win.number)' \ 'text: win.connector'; do rg -Fq "$contract" "$identify" \ || fail "display identification overlay is incomplete: $contract" done rg -Fq 'DisplayIdentify {}' "$shell" \ || fail 'display identification overlays are not shell-owned' rg -Fq 'function identify()' "$service" \ && rg -Fq 'interval: 3000' "$service" \ || fail 'display identification does not use one three-second service timer' state_home="$(mktemp -d /tmp/panama-display-arrangement-state.XXXXXX)" harness_pid="" cleanup() { [[ "$harness_pid" =~ ^[0-9]+$ ]] && kill "$harness_pid" 2>/dev/null || true rm -rf "$state_home" } trap cleanup EXIT XDG_STATE_HOME="$state_home" qs -p "$harness" --daemonize >/dev/null for _ in $(seq 1 60); do harness_pid="$(qs list --all 2>/dev/null | awk -v expected="$harness" ' /^Instance / {pid=""} /^[[:space:]]*Process ID:/ {pid=$3} /^[[:space:]]*Config path:/ {path=$0; sub(/^[[:space:]]*Config path: /,"",path); if(path==expected) print pid}' | head -1)" [[ "$harness_pid" =~ ^[0-9]+$ ]] \ && XDG_STATE_HOME="$state_home" qs -p "$harness" ipc --pid "$harness_pid" show 2>/dev/null \ | rg -q '^target display-arrangement-test$' && break sleep 0.1 done [[ "$harness_pid" =~ ^[0-9]+$ ]] || fail 'arrangement harness did not start' ipc() { XDG_STATE_HOME="$state_home" qs -p "$harness" ipc --pid "$harness_pid" \ call display-arrangement-test "$@" } wide="$(ipc status 800)" narrow="$(ipc status 500)" for snapshot in "$wide" "$narrow"; do jq -e '(.rects | length) == 2 and .scale > 0 and (.rects[0].width / .rects[0].height - 1.5 | fabs) < 0.0001 and (.rects[1].width / .rects[1].height - (2560 / 1440) | fabs) < 0.0001' \ <<<"$snapshot" >/dev/null \ || fail "canvas lost monitor geometry at a supported width: $snapshot" done drag="$(ipc dragFixture)" jq -e '.[0].x == 0 and .[1].x == 3000' <<<"$drag" >/dev/null \ || fail "drag release did not snap and apply the complete layout: $drag" keyboard="$(ipc keyboardFixture)" jq -e '.afterArrow == 2990 and .afterShiftArrow == 2890' <<<"$keyboard" >/dev/null \ || fail "keyboard movement did not use 10/100 logical-pixel steps: $keyboard" primary="$(ipc primaryFixture)" jq -e '. == [ {"name":"DP-2","x":-3000,"y":0,"primary":false}, {"name":"HDMI-A-1","x":0,"y":0,"primary":true} ]' <<<"$primary" >/dev/null \ || fail "Make primary did not normalize the selected output to 0,0: $primary" ipc identify >/dev/null [[ "$(ipc identifying)" == "true" ]] \ || fail 'identify did not reveal the overlays' sleep 3.2 [[ "$(ipc identifying)" == "false" ]] \ || fail 'identify overlays did not disappear after one three-second timer' printf 'display arrangement contract: PASS\n'