#!/usr/bin/env bash # Part of JoeyLib, Copyright (c) 2026 Scott Duensing, under the MIT License. # See LICENSE.md for license information. # SPDX-License-Identifier: MIT # # verify-amiga-input.sh - Typed-character acceptance gate for the Amiga # port. Boots the Keys example off a directory hard drive in FS-UAE # under Xvfb, types the RetroNet definition-of-done string with xdotool # (real X key events, so ':' goes through the emulated shift and # keymap.library's MapRawKey), then presses Escape. On exit Keys logs # its full received-character history as hex to joeylog.txt on the boot # volume -- which is the staged host directory -- and the harness # compares that against the typed ASCII. # # Before the typing, the named keys the Amiga gives a second key for: the # keypad's 8, 2, 4, and 6 as the arrows, its Enter as Return, and the right # Alt as Alt, each held alone, and Up and the keypad's 8 held together with # either let go first, which must leave Up down (scripts/lib/keysGate.sh) -- # read off KEYS's cells in FS-UAE's screenshots (tests/gate/keysCells.c), # the stage found by its still empty verification row # (tests/gate/keysRow.c). # # Usage: scripts/verify-amiga-input.sh # Requires: fs-uae, Xvfb, xdotool; build/amiga/bin/Keys (make amiga); # the Kickstart (install.sh install emulator:amiga). set -euo pipefail repo=$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd) source "$repo/scripts/lib/joeyEnv.sh" joeyNeed FSUAE fsuae source "$repo/scripts/lib/amigaGate.sh" source "$repo/scripts/lib/keysGate.sh" amigaMachine bin_dir=$repo/build/amiga/bin target="192.168.1.10:6510" shotKey=F11 [ -f "$bin_dir/Keys" ] || { echo "verify-amiga-input: build Keys first (make amiga)" >&2; exit 2; } work=$(mktemp -d -t joeylib-amiga-input.XXXXXX) display_num=$((RANDOM % 500 + 300)) cleanup() { joeyStop "${fsuae_pid:-}" [ -n "${xvfb_pid:-}" ] && kill "$xvfb_pid" 2>/dev/null rm -rf "$work" } trap cleanup EXIT mkdir -p "$work/hd/s" "$work/shots" cp "$bin_dir/Keys" "$work/hd/" echo ":Keys" > "$work/hd/s/startup-sequence" Xvfb ":$display_num" -screen 0 1024x768x24 >/dev/null 2>&1 & xvfb_pid=$! sleep 1 # No keyboard joystick in port 1 and no modifier key: by default FS-UAE # takes the cursor keys and the right Ctrl and Alt for a joystick, and the # left Alt for its own shortcuts, and the Amiga never sees them. # --fullscreen=1 is REQUIRED under a WM-less Xvfb: SDL only accepts # keyboard input once its window has input focus, no window manager # ever grants it to a plain window, and xdotool's windowfocus/ # XSendEvent routes are ignored by SDL. A fullscreen SDL window takes # focus itself, after which plain XTEST typing (xdotool without # --window) lands in the emulated Amiga. DISPLAY=":$display_num" "$FSUAE" \ "${AMIGA_MACHINE_ARGS[@]}" \ --fast_memory=2048 \ --hard_drive_0="$work/hd" \ --hard_drive_0_label=JOEYLIB \ --fullscreen=1 \ --initial_input_grab=1 \ --floppy_drive_speed=800 \ --joystick_port_1=none \ --modifier_key=none \ --keyboard_key_${shotKey,,}=action_screenshot \ --screenshots_output_dir="$work/shots" \ >/dev/null 2>&1 & fsuae_pid=$! # Boot AmigaDOS off the directory HD and let startup-sequence launch # Keys. No sentinel channel here, so give it a generous fixed wait. sleep 28 # XTEST keyboard events land in whatever window HAS focus, and under # a WM-less Xvfb the fullscreen SDL window latches it on its own # schedule -- so wait until getwindowfocus actually reports the FS-UAE # window before typing, nudging with windowfocus while waiting. win=$(DISPLAY=":$display_num" xdotool search --name -- "FS-UAE" | head -1) for i in $(seq 1 30); do focus=$(DISPLAY=":$display_num" xdotool getwindowfocus 2>/dev/null || true) [ -n "$win" ] && [ "$focus" = "$win" ] && break [ -n "$win" ] && DISPLAY=":$display_num" xdotool windowfocus --sync "$win" 2>/dev/null || true sleep 1 [ -z "$win" ] && win=$(DISPLAY=":$display_num" xdotool search --name -- "FS-UAE" | head -1) done if ! amigaShot ":$display_num" "$shotKey" "$work/shots" "$work/stage.png"; then echo "verify-amiga-input: FAIL - FS-UAE took no screenshot" >&2 exit 1 fi found=$("$(joeyTool keysRow)" "$work/stage.png" "") || true if [[ ! "$found" =~ row\ at\ \(([0-9]+),([0-9]+)\)\ at\ ([0-9]+)x ]]; then echo "verify-amiga-input: FAIL - KEYS's stage is not on the screen: $found" >&2 exit 1 fi scale=${BASH_REMATCH[3]} stageAt="${BASH_REMATCH[1]},$((BASH_REMATCH[2] - 198 * scale))" # X's names for the keys (scripts/lib/keysGate.sh's). declare -A xKey=([up]=Up [kp8]=KP_Up [kp2]=KP_Down [kp4]=KP_Left [kp6]=KP_Right [kpenter]=KP_Enter [ralt]=Alt_R) KEYS_LABEL=verify-amiga-input keysPress() { DISPLAY=":$display_num" xdotool keydown "${xKey[$1]}" } keysRelease() { DISPLAY=":$display_num" xdotool keyup "${xKey[$1]}" } keysRead() { if amigaShot ":$display_num" "$shotKey" "$work/shots" "$work/cells.png"; then "$(joeyTool keysCells)" "$work/cells.png" --at "$stageAt" --scale "$scale" else echo "no screenshot" fi } keysRunSteps kp8 kp2 kp4 kp6 kpenter ralt if [ -n "$KEYS_FAILS" ]; then echo "verify-amiga-input: FAIL - $KEYS_FAILS" >&2 exit 1 fi # Up to three attempts: type, press Escape, then look for the typed= # line Keys logs on exit. The verdict compares the history's SUFFIX # against the target, so a partially-delivered early attempt can't # poison a complete later one -- the proof is that the final 17 # characters arrived in order through the queue. log="$work/hd/joeylog.txt" delivered="" for attempt in 1 2 3; do DISPLAY=":$display_num" xdotool type --delay 200 -- "$target" sleep 2 # Hold Escape across several emulated frames: an instantaneous # down+up can land inside ONE drainMessages pass, where set-then- # clear in the same poll is invisible to the edge predicate. DISPLAY=":$display_num" xdotool keydown Escape sleep 0.5 DISPLAY=":$display_num" xdotool keyup Escape for i in $(seq 1 10); do if [ -f "$log" ] && grep -q "typed=" "$log"; then delivered=1 break 2 fi sleep 1 done done if [ -z "$delivered" ]; then echo "verify-amiga-input: FAIL - no typed= line in joeylog.txt (keystrokes never landed?)" >&2 ls -la "$work/hd" >&2 exit 1 fi typedHex=$(grep -o "typed=[0-9A-Fa-f]*" "$log" | tail -1 | cut -d= -f2) expect=$(printf '%s' "$target" | od -A n -t x1 | tr -d ' \n' | tr 'a-f' 'A-F') echo "VERIFY-AMIGA-INPUT typed=$typedHex" case "$typedHex" in *"$expect") echo "verify-amiga-input: PASS (typed \"$target\", all ${#target} chars arrived via jlInputGetChar; $KEYS_STEPS named-key steps: the keypad's arrows and Enter, right Alt, Up held with its second key)" ;; *) echo "verify-amiga-input: FAIL" >&2 echo " expected suffix: $expect" >&2 echo " got: $typedHex" >&2 exit 1 ;; esac