#!/usr/bin/env bash # Reusable UI-proof capture — runs ON the RUN host (apricot). # # Drives flatpak Godot under a weston headless Wayland compositor pointed at a # proof scene `.tscn`. The proof scene writes one or more PNGs to # `user://screenshots/` (Godot flatpak userdata) and prints `SCREENSHOT_PATH:` # per shot. This script wipes the matching prior PNGs, runs the scene, then lists # the produced files so the caller can rsync them back. # # A real display is required because `--headless` returns a null viewport image # (the proof scenes call get_viewport().get_texture().get_image()). weston # --backend=headless provides a virtual Wayland host with no live session. # # Usage: # SCRATCH_DIR= \ # PROOF_SCENE=res://engine/scenes/tests/.tscn \ # SHOT_GLOB='_*.png' \ # bash scripts/ui-proof-capture.sh # # Env: # SCRATCH_DIR repo root on apricot containing src/game (required) # PROOF_SCENE res:// path to the proof tscn (required) # SHOT_GLOB glob (within the screenshots dir) of PNGs this run emits (required) # LOG_FILE Godot log destination (default /tmp/ui-proof.log) # TIMEOUT_SEC Godot timeout in seconds (default 120) # WIDTH/HEIGHT weston virtual output size (default 1280/720) set -uo pipefail SCRATCH_DIR="${SCRATCH_DIR:?SCRATCH_DIR is required}" PROOF_SCENE="${PROOF_SCENE:?PROOF_SCENE is required}" SHOT_GLOB="${SHOT_GLOB:?SHOT_GLOB is required}" LOG_FILE="${LOG_FILE:-/tmp/ui-proof.log}" TIMEOUT_SEC="${TIMEOUT_SEC:-120}" WIDTH="${WIDTH:-1280}" HEIGHT="${HEIGHT:-720}" GODOT_USERDATA="$HOME/.var/app/org.godotengine.Godot/data/godot/app_userdata/Magic Civilization" SHOT_DIR="$GODOT_USERDATA/screenshots" echo "=== UI proof capture ===" echo "SCRATCH_DIR: $SCRATCH_DIR" echo "PROOF_SCENE: $PROOF_SCENE" echo "SHOT_GLOB: $SHOT_GLOB" echo "Sandbox shots dir: $SHOT_DIR" mkdir -p "$SHOT_DIR" rm -f "$SHOT_DIR"/$SHOT_GLOB 2>/dev/null || true if ! command -v weston >/dev/null 2>&1; then echo "ERROR: weston not installed" >&2 exit 2 fi WESTON_SOCKET="ui-proof-$$" WESTON_LOG="/tmp/ui-proof-weston.log" echo "Starting weston (headless, socket=$WESTON_SOCKET, ${WIDTH}x${HEIGHT})..." weston --backend=headless --no-config --socket="$WESTON_SOCKET" \ --width="$WIDTH" --height="$HEIGHT" \ >"$WESTON_LOG" 2>&1 & WESTON_PID=$! sleep 1.5 if ! kill -0 "$WESTON_PID" 2>/dev/null; then echo "ERROR: weston failed to launch — see $WESTON_LOG" >&2 tail -20 "$WESTON_LOG" >&2 exit 3 fi trap 'kill $WESTON_PID 2>/dev/null; wait $WESTON_PID 2>/dev/null' EXIT XDG_RUNTIME_DIR="${XDG_RUNTIME_DIR:-/run/user/$(id -u)}" \ timeout "$TIMEOUT_SEC" flatpak run --user \ --filesystem=home \ --socket=wayland \ --unset-env=DISPLAY \ --env=WAYLAND_DISPLAY="$WESTON_SOCKET" \ --filesystem=xdg-run/${WESTON_SOCKET} \ org.godotengine.Godot \ --path "$SCRATCH_DIR/src/game" \ --display-driver wayland \ --rendering-driver opengl3 \ --rendering-method gl_compatibility \ "$PROOF_SCENE" \ >"$LOG_FILE" 2>&1 RC=$? echo "--- Godot exit rc=$RC ---" echo "--- Tail of log ---" tail -40 "$LOG_FILE" echo "-------------------" echo "--- SCREENSHOT_PATH lines ---" grep "SCREENSHOT_PATH:" "$LOG_FILE" || echo "(none)" echo "-----------------------------" shopt -s nullglob SHOTS=( "$SHOT_DIR"/$SHOT_GLOB ) shopt -u nullglob if [ "${#SHOTS[@]}" -eq 0 ]; then echo "ERROR: no $SHOT_GLOB produced under $SHOT_DIR" >&2 exit 1 fi echo "Captured ${#SHOTS[@]} shots:" ls -la "$SHOT_DIR"/$SHOT_GLOB echo "=== Done ===" exit 0