261 lines
10 KiB
Bash
Executable file
261 lines
10 KiB
Bash
Executable file
#!/usr/bin/env bash
|
|
# verify-dvx-joy.sh - Joystick gate for DVX: both sticks, every direction,
|
|
# both buttons and every release, driven through DOSBox Staging's emulated
|
|
# game port and read back from what examples/joy draws; and no joystick at
|
|
# all when the game port is empty, though DVX then offers its keyboard
|
|
# joystick.
|
|
#
|
|
# The port (src/dvx/input.c) reads DVX's joystick library: its first two
|
|
# devices that are real joysticks, each axis -32768..32767 >> 8, clamped to
|
|
# -127..127. DOSBox Staging's joysticktype 4axis puts four axes and four
|
|
# buttons on the game port with no host joystick, which DVX's game port
|
|
# driver (two-stick mode, its default) reads as two sticks, A and B, exactly
|
|
# as a Y cable would give it. A mapper file binds host keys to DOSBox's
|
|
# joystick events (as DVX's own tests do, src/test/dos/joyDrive.py), and
|
|
# xdotool holds them; the raw screenshot is bound to a key of its own, so no
|
|
# Ctrl reaches the program. With no host joystick nothing else moves the
|
|
# stick: it rests in the middle, buttons up.
|
|
#
|
|
# JOY (examples/joy) draws, for each stick, a dot (in the middle at rest,
|
|
# grey; green when moved), two button squares and a patch that says whether
|
|
# the port reports a stick; scripts/lib/joyScreen.py reads the WHOLE state of
|
|
# BOTH sticks off each screenshot: each direction of each stick alone, two
|
|
# diagonals each, each button alone and both together, with a release after
|
|
# every press so a latched line shows up, and a step holding different
|
|
# lines on both sticks at once so crosstalk shows up too. Axes are judged
|
|
# as DVX's own tests judge them, beyond half way: an uncalibrated DVX axis
|
|
# ranges itself as it moves, so a full push reads a little short of the
|
|
# end. Escape then ends JOY, which must leave DVX cleanly ("reaped").
|
|
#
|
|
# Second run, joysticktype disabled: DVX finds no game port stick and falls
|
|
# back to its keyboard joystick (the arrows, LCtrl, Space...), which the
|
|
# port leaves out, as every port reports no joystick then: JOY must show
|
|
# both sticks disconnected at rest while the keyboard joystick's Left and
|
|
# Space are held.
|
|
#
|
|
# Usage: scripts/verify-dvx-joy.sh
|
|
# Env: DVX_JOY_SHOTS also copy the screenshots into this folder
|
|
# Requires: DOSBox Staging, Xvfb, xdotool, python3 + PIL, the dvx component;
|
|
# the joy 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=joy
|
|
wall=240 # hard cap on each DOSBox run, whatever happens to this script
|
|
readySeconds=60 # DVX boot until its shell reports ready
|
|
settleSeconds=3 # then JOY opening its window and taking the focus
|
|
pressSeconds=1 # keys held (or let go), then DVX's poll, JOY and the compositor catching up
|
|
shotSeconds=5 # the screenshot key until DOSBox has written the file
|
|
exitSeconds=10 # Escape until DVX reaps JOY
|
|
rest="10000" # a stick connected, in the middle, buttons up
|
|
absent="00000" # no stick
|
|
# DVX.LOG when the game port gives two sticks, and when it has none and the
|
|
# keyboard joystick stands in.
|
|
twoSticksLog="Joystick: 2 device(s) found"
|
|
keyboardLog="Joystick: keyboard0"
|
|
reapedLog="Shell: reaped app 'Joy'"
|
|
|
|
# Host key (xdotool name), its SDL scancode, and the DOSBox mapper event it
|
|
# drives. Stick A = the game port's first two axes and buttons, stick B =
|
|
# its last two (DOSBox 4axis: the first joystick's axes 2-3, buttons 2-3).
|
|
mapKeys=(
|
|
"j 13 jaxis_0_0-" "l 15 jaxis_0_0+" "i 12 jaxis_0_1-" "k 14 jaxis_0_1+"
|
|
"a 4 jbutton_0_0" "s 22 jbutton_0_1"
|
|
"f 9 jaxis_0_2-" "h 11 jaxis_0_2+" "t 23 jaxis_0_3-" "g 10 jaxis_0_3+"
|
|
"q 20 jbutton_0_2" "w 26 jbutton_0_3"
|
|
"F11 68 hand_rawshot"
|
|
"Escape 41 key_esc"
|
|
)
|
|
shotKey=F11
|
|
|
|
# name, keys held, stick A's state, stick B's state (joyScreen.py's
|
|
# characters: connected, X, Y, button 0, button 1).
|
|
steps=(
|
|
"A left|j|1-000|$rest" "A right|l|1+000|$rest"
|
|
"A up|i|10-00|$rest" "A down|k|10+00|$rest"
|
|
"A up-left|j i|1--00|$rest" "A down-right|l k|1++00|$rest"
|
|
"A button 0|a|10010|$rest" "A button 1|s|10001|$rest"
|
|
"A both buttons|a s|10011|$rest"
|
|
"B left|f|$rest|1-000" "B right|h|$rest|1+000"
|
|
"B up|t|$rest|10-00" "B down|g|$rest|10+00"
|
|
"B up-right|h t|$rest|1+-00" "B down-left|f g|$rest|1-+00"
|
|
"B button 0|q|$rest|10010" "B button 1|w|$rest|10001"
|
|
"B both buttons|q w|$rest|10011"
|
|
"A left+button 1, B right+button 0|j s h q|1-001|1+010"
|
|
)
|
|
|
|
dvxNeedBuild "$program"
|
|
|
|
work=$(mktemp -d -t joeylib-dvx-joy.XXXXXX)
|
|
guard=""
|
|
dosbox=""
|
|
xvfb=""
|
|
win=""
|
|
fails=""
|
|
checked=0
|
|
# 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
|
|
|
|
|
|
# boot RUN JOYTYPE [MAPPER] -- DVX staged in $work/RUN with JOY its desktop,
|
|
# DOSBox's joysticktype JOYTYPE and, if given, its mapper file; sets guard,
|
|
# dosbox and win.
|
|
boot() {
|
|
local dir="$work/$1"
|
|
local waited
|
|
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"
|
|
printf '\n[joystick]\njoysticktype = %s\n' "$2" >> "$dir/dosbox.conf"
|
|
if [ -n "${3:-}" ]; then
|
|
printf '\n[sdl]\nmapperfile = %s\n' "$3" >> "$dir/dosbox.conf"
|
|
fi
|
|
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
|
|
sleep "$settleSeconds"
|
|
win=$(joeyFindWindow "$display" "$dosbox")
|
|
[ -n "$win" ] || return 1
|
|
DISPLAY="$display" xdotool windowactivate --sync "$win" 2> /dev/null || true
|
|
return 0
|
|
fi
|
|
joeyAlive "$guard" || return 1
|
|
sleep 1
|
|
done
|
|
return 1
|
|
}
|
|
|
|
|
|
# check RUN NAME HOTKEY WANT -- a screenshot (HOTKEY, e.g. F11 or ctrl+F5)
|
|
# as $work/RUN/NAME.png, whose two sticks must read WANT ("A=... B=...").
|
|
check() {
|
|
local dir="$work/$1"
|
|
local before
|
|
local png=""
|
|
local got
|
|
local waited
|
|
before=$(ls "$dir/shots" | wc -l)
|
|
DISPLAY="$display" xdotool key --window "$win" "$3"
|
|
for ((waited = 0; waited < shotSeconds * 10; waited++)); do
|
|
if [ "$(ls "$dir/shots" | wc -l)" -gt "$before" ]; then
|
|
sleep 0.5
|
|
png=$(ls -t "$dir"/shots/*.png | head -1)
|
|
break
|
|
fi
|
|
sleep 0.1
|
|
done
|
|
checked=$((checked + 1))
|
|
if [ -z "$png" ]; then
|
|
fail "$2: no screenshot"
|
|
return
|
|
fi
|
|
mv "$png" "$dir/$(printf '%02d' "$checked") ${2}.png"
|
|
got=$(python3 "$repo/scripts/lib/joyScreen.py" "$dir/$(printf '%02d' "$checked") ${2}.png" --scale 1)
|
|
echo "verify-dvx-joy: $2: $got"
|
|
[ "${got% scale=*}" = "$4" ] || fail "$2: read $got, want $4"
|
|
}
|
|
|
|
|
|
# fail WHAT -- note a failed check; the gate goes on to report the rest.
|
|
fail() {
|
|
echo "verify-dvx-joy: FAIL - $1"
|
|
fails="${fails:+$fails; }$1"
|
|
}
|
|
|
|
|
|
# keys DOWN|UP KEY... -- press or let go of the keys.
|
|
keys() {
|
|
local action="key$1"
|
|
local k
|
|
shift
|
|
for k in "$@"; do
|
|
DISPLAY="$display" xdotool "$action" --window "$win" "$k"
|
|
done
|
|
}
|
|
|
|
|
|
# stop -- end the DOSBox run.
|
|
stop() {
|
|
joeyStop "$dosbox" "$guard" 2> /dev/null
|
|
dosbox=""
|
|
guard=""
|
|
}
|
|
|
|
|
|
display=":$((RANDOM % 400 + 4300))"
|
|
Xvfb "$display" -screen 0 1024x768x24 -nolisten tcp > /dev/null 2>&1 &
|
|
xvfb=$!
|
|
sleep 1
|
|
|
|
# 1. Both sticks on the game port, moved through the mapper.
|
|
mapper="$work/mapper.txt"
|
|
for entry in "${mapKeys[@]}"; do
|
|
read -r name code event <<< "$entry"
|
|
printf '%s "key %s"\n' "$event" "$code"
|
|
done > "$mapper"
|
|
if ! boot sticks 4axis "$mapper"; then
|
|
echo "verify-dvx-joy: FAIL - DVX with JOY did not come up (DVX.LOG follows)" >&2
|
|
dvxDvxLog "$work/sticks/c" >&2
|
|
exit 1
|
|
fi
|
|
dvxDvxLog "$work/sticks/c" 400 | grep -q "$twoSticksLog" || fail "DVX.LOG has no \"$twoSticksLog\": $(dvxDvxLog "$work/sticks/c" 400 | grep 'Joystick:' | tr '\n' ' ')"
|
|
check sticks "rest" "$shotKey" "A=$rest B=$rest"
|
|
for step in "${steps[@]}"; do
|
|
IFS='|' read -r name held wantA wantB <<< "$step"
|
|
read -r -a heldKeys <<< "$held"
|
|
keys down "${heldKeys[@]}"
|
|
sleep "$pressSeconds"
|
|
check sticks "$name" "$shotKey" "A=$wantA B=$wantB"
|
|
keys up "${heldKeys[@]}"
|
|
sleep "$pressSeconds"
|
|
check sticks "$name released" "$shotKey" "A=$rest B=$rest"
|
|
done
|
|
keys down Escape
|
|
sleep 0.3
|
|
keys up Escape
|
|
for ((waited = 0; waited < exitSeconds; waited++)); do
|
|
dvxDvxLog "$work/sticks/c" 400 | grep -qF "$reapedLog" && break
|
|
sleep 1
|
|
done
|
|
dvxDvxLog "$work/sticks/c" 400 | grep -qF "$reapedLog" || fail "Escape did not end JOY (no \"$reapedLog\" in DVX.LOG)"
|
|
stop
|
|
|
|
# 2. No stick on the game port: DVX's keyboard joystick stands in, and the
|
|
# port must leave it out. Plain DOSBox keys (no mapper), the default
|
|
# screenshot hotkey.
|
|
if ! boot none disabled; then
|
|
echo "verify-dvx-joy: FAIL - DVX with JOY and no joystick did not come up (DVX.LOG follows)" >&2
|
|
dvxDvxLog "$work/none/c" >&2
|
|
exit 1
|
|
fi
|
|
dvxDvxLog "$work/none/c" 400 | grep -q "$keyboardLog" || fail "DVX.LOG has no \"$keyboardLog\" (the keyboard joystick): $(dvxDvxLog "$work/none/c" 400 | grep 'Joystick:' | tr '\n' ' ')"
|
|
check none "no joystick" ctrl+F5 "A=$absent B=$absent"
|
|
keys down Left space
|
|
sleep "$pressSeconds"
|
|
check none "no joystick, keyboard joystick held" ctrl+F5 "A=$absent B=$absent"
|
|
keys up Left space
|
|
stop
|
|
|
|
if [ -n "${DVX_JOY_SHOTS:-}" ]; then
|
|
mkdir -p "$DVX_JOY_SHOTS"
|
|
cp "$work"/sticks/*.png "$work"/none/*.png "$DVX_JOY_SHOTS/"
|
|
fi
|
|
if [ -n "$fails" ]; then
|
|
echo "verify-dvx-joy: FAIL - $fails"
|
|
exit 1
|
|
fi
|
|
echo "verify-dvx-joy: PASS ($checked screenshots: both sticks, every direction, both buttons and every release; no joystick without one, the keyboard joystick left out)"
|