145 lines
6.2 KiB
Bash
145 lines
6.2 KiB
Bash
# shellcheck shell=bash
|
|
# Part of JoeyLib, Copyright (c) 2026 Scott Duensing, under the MIT License.
|
|
# See LICENSE.md for license information.
|
|
# SPDX-License-Identifier: MIT
|
|
#
|
|
# Shared by the joystick gates that read examples/joy's screen
|
|
# (scripts/verify-*-joy.sh): the steps, the check of each screenshot
|
|
# (scripts/lib/joyScreen.py) and the tally. Source after joeyEnv.sh. The gate
|
|
# defines three functions first:
|
|
#
|
|
# joyShot PNG save a screenshot of JOY as PNG; non-zero if none
|
|
# joyPress CONTROL... hold the controls (below)
|
|
# joyRelease CONTROL... let them go
|
|
#
|
|
# A control is a stick (A or B) and a line: A-left A-right A-up A-down
|
|
# A-button0 A-button1, and the same for B. A screenshot reads as
|
|
# "A=<state> B=<state>", each state five characters: connected (1/0), X and
|
|
# Y (- 0 +), button 0 and button 1 (1/0).
|
|
#
|
|
# joyCheck NAME WANT a screenshot, which must read WANT ("A=... B=...");
|
|
# an unreadable one ('?': JOY caught between
|
|
# erasing a dot and drawing it again) is taken
|
|
# once more before it counts
|
|
# joyRunSteps STEPS... for each "name|controls|A's state|B's state":
|
|
# hold the controls, check, let go, check at rest;
|
|
# REST in A's state stands for JOY_REST, in B's
|
|
# for JOY_REST_B (JOY_REST, or JOY_ABSENT where the
|
|
# machine has one stick)
|
|
# joyFail WHAT note a failure (after JOY_PASS, if the gate set
|
|
# it); the gate goes on to report the rest
|
|
# joyResult LABEL WHAT print LABEL's PASS (with WHAT) or FAIL line;
|
|
# exit 1 on a failure
|
|
#
|
|
# The gate sets JOY_LABEL (its name, for the lines it prints) and JOY_WORK
|
|
# (its scratch folder). JOY_SCALE (joyScreen.py --scale) is the stage's
|
|
# scale in the screenshot, empty to let joyScreen.py find it;
|
|
# JOY_SCREEN_ARGS are more joyScreen.py options (--st); JOY_SHOTS, if
|
|
# set, keeps every screenshot there; JOY_PRESS_SECONDS is how long a change
|
|
# takes to show; JOY_SHOT_TRIES is 1 for a gate whose screenshots were all
|
|
# taken before the checks (they cannot be taken again).
|
|
|
|
JOY_REST="10000" # a stick connected, in the middle, buttons up
|
|
JOY_ABSENT="00000" # no stick
|
|
JOY_REST_B="$JOY_REST" # stick B when nothing holds it
|
|
JOY_SCALE="${JOY_SCALE:-}"
|
|
JOY_SCREEN_ARGS=()
|
|
JOY_PRESS_SECONDS="${JOY_PRESS_SECONDS:-1}"
|
|
JOY_SHOT_TRIES="${JOY_SHOT_TRIES:-2}"
|
|
JOY_RETRY_SECONDS=0.5
|
|
JOY_FAILS=""
|
|
JOY_CHECKED=0
|
|
JOY_PASS="" # a gate that runs its steps more than once names each run
|
|
|
|
# Both sticks, every direction, two diagonals each, each button alone and
|
|
# together, and a step holding different lines on both sticks at once, so
|
|
# crosstalk between them shows up.
|
|
JOY_STEPS_TWO_STICKS=(
|
|
"A left|A-left|1-000|REST" "A right|A-right|1+000|REST"
|
|
"A up|A-up|10-00|REST" "A down|A-down|10+00|REST"
|
|
"A up-left|A-left A-up|1--00|REST" "A down-right|A-right A-down|1++00|REST"
|
|
"A button 0|A-button0|10010|REST" "A button 1|A-button1|10001|REST"
|
|
"A both buttons|A-button0 A-button1|10011|REST"
|
|
"B left|B-left|REST|1-000" "B right|B-right|REST|1+000"
|
|
"B up|B-up|REST|10-00" "B down|B-down|REST|10+00"
|
|
"B up-right|B-right B-up|REST|1+-00" "B down-left|B-left B-down|REST|1-+00"
|
|
"B button 0|B-button0|REST|10010" "B button 1|B-button1|REST|10001"
|
|
"B both buttons|B-button0 B-button1|REST|10011"
|
|
"A left+button 1, B right+button 0|A-left A-button1 B-right B-button0|1-001|1+010"
|
|
)
|
|
|
|
# The same for sticks with one button (the Atari's fire button). Held for the
|
|
# screenshot, the button is button 1: JOY_PRESS_SECONDS is past the half
|
|
# second that makes a press a hold. A tap is button 0 for one poll only,
|
|
# which no screenshot catches; tests/host/inputHost.c checks it.
|
|
JOY_STEPS_ONE_BUTTON=(
|
|
"A left|A-left|1-000|REST" "A right|A-right|1+000|REST"
|
|
"A up|A-up|10-00|REST" "A down|A-down|10+00|REST"
|
|
"A up-left|A-left A-up|1--00|REST" "A down-right|A-right A-down|1++00|REST"
|
|
"A button held: button 1|A-button0|10001|REST"
|
|
"B left|B-left|REST|1-000" "B right|B-right|REST|1+000"
|
|
"B up|B-up|REST|10-00" "B down|B-down|REST|10+00"
|
|
"B up-right|B-right B-up|REST|1+-00" "B down-left|B-left B-down|REST|1-+00"
|
|
"B button held: button 1|B-button0|REST|10001"
|
|
"A left+button held, B right+button held|A-left A-button0 B-right B-button0|1-001|1+001"
|
|
)
|
|
|
|
|
|
joyCheck() {
|
|
local png="$JOY_WORK/shot.png"
|
|
local got
|
|
local try
|
|
local args=("${JOY_SCREEN_ARGS[@]}")
|
|
JOY_CHECKED=$((JOY_CHECKED + 1))
|
|
[ -z "$JOY_SCALE" ] || args+=(--scale "$JOY_SCALE")
|
|
for ((try = 1; try <= JOY_SHOT_TRIES; try++)); do
|
|
[ "$try" = 1 ] || sleep "$JOY_RETRY_SECONDS"
|
|
rm -f "$png"
|
|
if ! joyShot "$png" || [ ! -s "$png" ]; then
|
|
joyFail "$1: no screenshot"
|
|
return
|
|
fi
|
|
got=$(python3 "$JOEY_REPO/scripts/lib/joyScreen.py" "$png" "${args[@]}")
|
|
[[ "${got% scale=*}" == *"?"* ]] || break
|
|
done
|
|
if [ -n "${JOY_SHOTS:-}" ]; then
|
|
mkdir -p "$JOY_SHOTS"
|
|
cp "$png" "$JOY_SHOTS/$(printf '%02d' "$JOY_CHECKED") $1.png"
|
|
fi
|
|
echo "$JOY_LABEL: $1: $got"
|
|
[ "${got% scale=*}" = "$2" ] || joyFail "$1: read ${got% scale=*}, want $2"
|
|
}
|
|
|
|
|
|
joyFail() {
|
|
JOY_FAILS="${JOY_FAILS:+$JOY_FAILS; }${JOY_PASS:+$JOY_PASS: }$1"
|
|
}
|
|
|
|
|
|
joyResult() {
|
|
if [ -n "$JOY_FAILS" ]; then
|
|
echo "$1: FAIL - $JOY_FAILS"
|
|
exit 1
|
|
fi
|
|
echo "$1: PASS ($JOY_CHECKED screenshots: $2)"
|
|
}
|
|
|
|
|
|
joyRunSteps() {
|
|
local step
|
|
local name
|
|
local held
|
|
local wantA
|
|
local wantB
|
|
local controls
|
|
for step in "$@"; do
|
|
IFS='|' read -r name held wantA wantB <<< "$step"
|
|
read -r -a controls <<< "$held"
|
|
joyPress "${controls[@]}"
|
|
sleep "$JOY_PRESS_SECONDS"
|
|
joyCheck "$name" "A=${wantA/REST/$JOY_REST} B=${wantB/REST/$JOY_REST_B}"
|
|
joyRelease "${controls[@]}"
|
|
sleep "$JOY_PRESS_SECONDS"
|
|
joyCheck "$name released" "A=$JOY_REST B=$JOY_REST_B"
|
|
done
|
|
}
|