Files
agent-pet-companion/script/validate_overlay_display_width_persistence.sh

194 lines
5.5 KiB
Bash
Executable File

#!/usr/bin/env bash
set -euo pipefail
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
. "$ROOT_DIR/script/validation_helpers.sh"
apc_require_host_ui_opt_in "overlay display-width persistence validation"
APP_BUNDLE="$ROOT_DIR/dist/AgentPetCompanion.app"
APP_BINARY="$APP_BUNDLE/Contents/MacOS/AgentPetCompanion"
PETCORE_BINARY="$APP_BUNDLE/Contents/Resources/bin/petcore"
PETCORE_CLI="$APP_BUNDLE/Contents/Resources/bin/petcore-cli"
TARGET_DISPLAY_WIDTH_PT="${APC_OVERLAY_PERSISTENCE_DISPLAY_WIDTH_PT:-300}"
# Keep APC_HOME short enough for macOS' sockaddr_un path limit. The runtime
# socket lives below `$TMP_DIR/home/run`, so a descriptive mktemp prefix here
# can otherwise turn an isolated-host check into a false startup deferral.
TMP_DIR="$(mktemp -d "${TMPDIR:-/tmp}/apc-owp.XXXXXX")"
apc_use_isolated_home "$TMP_DIR"
OWNED_PROTOCOL="$APC_HOME/run/validation-owned-runtime.json"
APP_LOG="$TMP_DIR/app.log"
ORIGINAL_PLACEMENT=""
restore_original_placement() {
if [[ -z "$ORIGINAL_PLACEMENT" || ! -x "$PETCORE_CLI" ]]; then
return 0
fi
PLACEMENT="$ORIGINAL_PLACEMENT" python3 - <<'PY' | while read -r x y display_width_pt display_id; do
import json
import os
placement = json.loads(os.environ["PLACEMENT"])
print(
placement["x"],
placement["y"],
placement["display_width_pt"],
placement.get("display_id", "main"),
)
PY
"$PETCORE_CLI" overlay placement set \
--x "$x" \
--y "$y" \
--display-width-pt "$display_width_pt" \
--display-id "$display_id" >/dev/null 2>&1 || true
done
}
cleanup() {
restore_original_placement
apc_stop_owned_runtime "$PETCORE_CLI" "$PETCORE_BINARY" "$OWNED_PROTOCOL"
rm -rf "$TMP_DIR"
}
trap cleanup EXIT
if [[ ! -x "$APP_BINARY" || ! -x "$PETCORE_BINARY" || ! -x "$PETCORE_CLI" ]]; then
"$ROOT_DIR/script/build_app_bundle.sh" >/dev/null
fi
apc_start_owned_runtime \
"$APP_BINARY" \
"$PETCORE_CLI" \
"$PETCORE_BINARY" \
"$APP_LOG" \
"$OWNED_PROTOCOL"
wait_snapshot() {
local snapshot=""
for _ in {1..80}; do
snapshot="$("$PETCORE_CLI" snapshot 2>/dev/null || true)"
if SNAPSHOT="$snapshot" python3 - <<'PY'
import json
import os
import sys
try:
data = json.loads(os.environ.get("SNAPSHOT", "{}"))
except json.JSONDecodeError:
sys.exit(1)
sys.exit(0 if isinstance(data.get("overlay_placement"), dict) else 1)
PY
then
printf '%s\n' "$snapshot"
return 0
fi
sleep 0.25
done
return 1
}
ORIGINAL_SNAPSHOT="$(wait_snapshot)"
ORIGINAL_PLACEMENT="$(SNAPSHOT="$ORIGINAL_SNAPSHOT" python3 - <<'PY'
import json
import os
placement = json.loads(os.environ["SNAPSHOT"])["overlay_placement"]
print(json.dumps(placement))
PY
)"
TARGET_PLACEMENT="$(swift - <<'SWIFT'
import AppKit
guard let screen = NSScreen.screens.last ?? NSScreen.main ?? NSScreen.screens.first else {
fputs("overlay display-width persistence validation failed: no NSScreen available\n", stderr)
exit(1)
}
let number = screen.deviceDescription[NSDeviceDescriptionKey("NSScreenNumber")] as? NSNumber
let displayID = number?.stringValue ?? "main"
let visible = screen.visibleFrame
print([
displayID,
String(format: "%.4f", visible.maxX + 400),
String(format: "%.4f", visible.minY - 400),
String(format: "%.4f", visible.minX),
String(format: "%.4f", visible.maxX),
String(format: "%.4f", visible.minY),
String(format: "%.4f", visible.maxY),
].joined(separator: " "))
SWIFT
)"
read -r TARGET_DISPLAY_ID TARGET_X TARGET_Y TARGET_MIN_X TARGET_MAX_X TARGET_MIN_Y TARGET_MAX_Y <<<"$TARGET_PLACEMENT"
"$PETCORE_CLI" overlay placement set \
--x "$TARGET_X" \
--y "$TARGET_Y" \
--display-width-pt "$TARGET_DISPLAY_WIDTH_PT" \
--display-id "$TARGET_DISPLAY_ID" >/dev/null
apc_stop_owned_runtime "$PETCORE_CLI" "$PETCORE_BINARY" "$OWNED_PROTOCOL"
apc_start_owned_runtime \
"$APP_BINARY" \
"$PETCORE_CLI" \
"$PETCORE_BINARY" \
"$APP_LOG" \
"$OWNED_PROTOCOL"
for _ in {1..40}; do
SNAPSHOT="$(wait_snapshot || true)"
if SNAPSHOT="$SNAPSHOT" \
TARGET_DISPLAY_WIDTH_PT="$TARGET_DISPLAY_WIDTH_PT" \
TARGET_DISPLAY_ID="$TARGET_DISPLAY_ID" \
TARGET_MIN_X="$TARGET_MIN_X" \
TARGET_MAX_X="$TARGET_MAX_X" \
TARGET_MIN_Y="$TARGET_MIN_Y" \
TARGET_MAX_Y="$TARGET_MAX_Y" \
python3 - <<'PY'
import json
import os
import sys
try:
data = json.loads(os.environ.get("SNAPSHOT", "{}"))
except json.JSONDecodeError:
sys.exit(1)
placement = data.get("overlay_placement", {})
actual = float(placement.get("display_width_pt", -1))
expected = float(os.environ["TARGET_DISPLAY_WIDTH_PT"])
if abs(actual - expected) >= 0.0001:
sys.exit(1)
if placement.get("display_id") != os.environ["TARGET_DISPLAY_ID"]:
sys.exit(1)
x = float(placement.get("x", 0))
y = float(placement.get("y", 0))
min_x = float(os.environ["TARGET_MIN_X"])
max_x = float(os.environ["TARGET_MAX_X"])
min_y = float(os.environ["TARGET_MIN_Y"])
max_y = float(os.environ["TARGET_MAX_Y"])
behavior = data.get("behavior", {})
visibility = data.get("overlay_visibility", {})
has_presentable_pet = (
isinstance(data.get("pets"), list)
and len(data["pets"]) > 0
and behavior.get("enabled", True)
and visibility.get("pet_visible", True)
)
if has_presentable_pet and not (min_x <= x <= max_x and min_y <= y <= max_y):
sys.exit(1)
sys.exit(0)
PY
then
echo "Overlay display-width/display persistence validation ok"
exit 0
fi
sleep 0.25
done
echo "overlay display-width persistence validation failed: width/display persistence or presented placement was invalid" >&2
"$PETCORE_CLI" snapshot >&2 || true
exit 1