#!/usr/bin/env bash # verify-dvx-input.sh - Typed-character and present gate for DVX. Runs KEYS # (examples/keys) as the desktop of a scratch DVX in DOSBox Staging under # Xvfb and types the RetroNet definition-of-done string with xdotool (real X # key events: ':' needs Shift, which DVX's keyboard layout applies before the # window's onKey hands the character to jlInputGetChar). Three checks: # 1. DVX's reserved chord: Ctrl+Esc (the Task Manager) must not reach KEYS # as Esc, which would end it (its typed line appears in its log); the # Task Manager's Switch To gives KEYS the focus back; # 2. the present: a raw screenshot of the desktop must show KEYS's # verification scanline (row 198: sentinel, count, every received # character as two pixel nibbles) somewhere -- the window's content, # expanded from the stage through the port's palette tables into the # desktop's pixel format at the window's scale -- decoding to exactly # the typed bytes, every stage pixel a whole scale x scale block # (scripts/lib/keysRow.py); # 3. the typed history: after Escape, KEYS logs every received character # as hex to its joeylog.txt (in its settings folder), which must equal # the typed ASCII. # Each desktop given (default 640x480 at 16 bits, where the window is 1x) is # a run of its own; DVX.LOG must show KEYS's window on that desktop at the # scale given. # # Usage: scripts/verify-dvx-input.sh [WxHxBPP:SCALE]... # e.g. scripts/verify-dvx-input.sh 800x600x8:2 1024x768x32:3 # Requires: DOSBox Staging, Xvfb, xdotool, python3 + PIL, the dvx component; # the keys program (make dvx). set -uo pipefail repo=$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd) source "$repo/scripts/lib/joeyEnv.sh" source "$repo/scripts/lib/dvxGate.sh" joeyNeed DOSBOX dosbox joeyNeed DVX_ROOT dvx program=keys target="192.168.1.10:6510" stageWidth=320 stageHeight=200 wall=180 # hard cap on DOSBox, whatever happens to this script readySeconds=60 # DVX boot until its shell reports ready settleSeconds=3 # then KEYS opening its window and taking the focus stepSeconds=2 # a key, then DVX and KEYS acting on it holdSeconds=0.3 # a key held that long: a quick tap can fall between two polls logSeconds=30 # Escape until KEYS's typed line is in its log specs=("$@") [ ${#specs[@]} -gt 0 ] || specs=(640x480x16:1) dvxNeedBuild "$program" work=$(mktemp -d -t joeylib-dvx-input.XXXXXX) guard="" dosbox="" xvfb="" win="" fails="" # 2>/dev/null: bash reports the SIGKILLed timeout wrapper as "Killed". cleanup() { joeyStop "$dosbox" "$guard" "$xvfb" 2> /dev/null rm -rf "$work" } trap cleanup EXIT # fail SPEC WHAT -- note a failed check; the gate goes on to report the rest. fail() { echo "verify-dvx-input: FAIL - $1: $2" fails="${fails:+$fails; }$1: $2" } # hold KEY... -- press the keys in order, each held holdSeconds before the # next (a chord's modifier goes down first), release them in reverse, then # give DVX and KEYS time to act. hold() { local k local i for k in "$@"; do DISPLAY="$display" xdotool keydown --window "$win" "$k" sleep "$holdSeconds" done for ((i = $#; i >= 1; i--)); do DISPLAY="$display" xdotool keyup --window "$win" "${!i}" done sleep "$stepSeconds" } # run SPEC -- one desktop: boot, type, Ctrl+Esc, screenshot, Escape, judge. run() { local spec="$1" local dir="$work/$1" local w local h local bpp local scale local ready="" local waited local log local png if ! [[ "$spec" =~ ^([0-9]+)x([0-9]+)x([0-9]+):([0-9]+)$ ]]; then echo "verify-dvx-input: bad desktop \"$spec\" (WxHxBPP:SCALE)" >&2 exit 2 fi w=${BASH_REMATCH[1]} h=${BASH_REMATCH[2]} bpp=${BASH_REMATCH[3]} scale=${BASH_REMATCH[4]} DVX_VIDEO_W=$w DVX_VIDEO_H=$h DVX_VIDEO_BPP=$bpp dvxStage "$dir/c" "$(dvxAppDir "$program")" dvxDosboxConf "$dir/dosbox.conf" "$dir/c" printf '\n[capture]\ncapture_dir = %s\ndefault_image_capture_formats = raw\n' "$dir/shots" >> "$dir/dosbox.conf" mkdir -p "$dir/xdg" "$dir/shots" XDG_CONFIG_HOME="$dir/xdg" DISPLAY="$display" SDL_AUDIODRIVER=dummy \ timeout -s KILL "$wall" "$DOSBOX" "${JOEY_DOSBOX_ISOLATE[@]}" \ --conf "$dir/dosbox.conf" > "$dir/dosbox.log" 2>&1 & guard=$! dosbox=$(joeyChild "$guard") for ((waited = 0; waited < readySeconds; waited++)); do if dvxDvxLog "$dir/c" 200 | grep -q "DVX Shell ready"; then ready=1 break fi joeyAlive "$guard" || break sleep 1 done if [ -z "$ready" ]; then fail "$spec" "DVX did not come up within ${readySeconds}s (DVX.LOG follows)" dvxDvxLog "$dir/c" >&2 joeyStop "$dosbox" "$guard" 2> /dev/null return fi sleep "$settleSeconds" win=$(joeyFindWindow "$display" "$dosbox") if [ -z "$win" ]; then fail "$spec" "DOSBox window not found" joeyStop "$dosbox" "$guard" 2> /dev/null return fi DISPLAY="$display" xdotool windowactivate --sync "$win" 2> /dev/null || true DISPLAY="$display" xdotool type --window "$win" --delay 150 -- "$target" sleep "$stepSeconds" # DVX's Task Manager chord, then its Switch To back to KEYS. hold ctrl Escape log=$(dvxProgramLog "$dir/c" "$program") if [ -n "$log" ] && tr -d '\r' < "$log" | grep -q '^typed='; then fail "$spec" "Ctrl+Esc reached KEYS as Esc (it ended)" fi hold alt s # Ctrl+F5 = DOSBox screenshot hotkey. DISPLAY="$display" xdotool key --window "$win" ctrl+F5 sleep "$stepSeconds" DISPLAY="$display" xdotool key --window "$win" Escape log=$(dvxAwaitLog "$dir/c" "$program" '^typed=' "$logSeconds" "$guard") cp "$log" "$dir/joeylog.txt" 2> /dev/null joeyStop "$dosbox" "$guard" 2> /dev/null dosbox="" guard="" if ! dvxDvxLog "$dir/c" 400 | grep -qF "JoeyLib: Keys: $((stageWidth * scale))x$((stageHeight * scale)) at ${scale}x on a ${w}x${h} ${bpp}-bit desktop"; then fail "$spec" "DVX.LOG does not show KEYS at ${scale}x on a ${w}x${h} ${bpp}-bit desktop: $(dvxDvxLog "$dir/c" 400 | grep -F 'JoeyLib: Keys:')" fi png=$(ls "$dir"/shots/*.png 2> /dev/null | head -1) if [ -z "$png" ]; then fail "$spec" "no screenshot captured" else python3 "$repo/scripts/lib/keysRow.py" "$png" "$target" --scale "$scale" || fail "$spec" "the present" fi if [ -z "$log" ]; then fail "$spec" "KEYS logged no typed line within ${logSeconds}s of Escape" else dvxKeysTyped "$dir/joeylog.txt" "$target" || fail "$spec" "the log" fi } display=":$((RANDOM % 400 + 2500))" Xvfb "$display" -screen 0 1024x768x24 -nolisten tcp > /dev/null 2>&1 & xvfb=$! sleep 1 for spec in "${specs[@]}"; do echo "verify-dvx-input: $spec" run "$spec" done if [ -n "$fails" ]; then echo "verify-dvx-input: FAIL - $fails" exit 1 fi echo "verify-dvx-input: PASS (${specs[*]}: typed \"$target\", all ${#target} chars reached jlInputGetChar, Ctrl+Esc did not end KEYS, and the present shows them)"