joeylib2/scripts/verify-dvx-joy.sh
2026-10-13 14:01:46 -05:00

176 lines
7.4 KiB
Bash
Executable file

#!/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-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 (scripts/lib/dosboxJoy.sh, 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 (scripts/lib/joyGate.sh's steps: 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 (the mapper binds them too in that run).
#
# Usage: scripts/verify-dvx-joy.sh
# Env: JOY_SHOTS also keep the screenshots in 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/joyGate.sh"
source "$repo/scripts/lib/dosboxJoy.sh"
source "$repo/scripts/lib/dvxGate.sh"
joeyNeed DOSBOX dosbox
joeyNeed DVX_ROOT dvx
JOY_LABEL=verify-dvx-joy
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
exitSeconds=10 # Escape until DVX reaps JOY
# 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'"
# DVX's keyboard joystick keys the second run holds (xdotool's names), and
# their mapper bindings: Left and Space (SDL scancodes 80 and 44).
keyboardKeys=(Left space)
keyboardBinds='key_left "key 80"
key_space "key 44"'
dvxNeedBuild "$program"
JOY_WORK=$(mktemp -d -t joeylib-dvx-joy.XXXXXX)
guard=""
dosbox=""
xvfb=""
# 2>/dev/null: bash reports the SIGKILLed timeout wrapper as "Killed".
cleanup() {
joeyStop "$dosbox" "$guard" "$xvfb" 2> /dev/null
rm -rf "$JOY_WORK"
}
trap cleanup EXIT
# boot RUN JOYTYPE [BINDS] -- DVX staged in $JOY_WORK/RUN with JOY its
# desktop, DOSBox's joysticktype JOYTYPE and the joystick mapper (with BINDS
# appended); sets guard, dosbox and DOSBOX_JOY_WINDOW.
boot() {
local dir="$JOY_WORK/$1"
local waited
dvxStage "$dir/c" "$(dvxAppDir "$program")"
dvxDosboxConf "$dir/dosbox.conf" "$dir/c"
mkdir -p "$dir/xdg" "$dir/shots"
dosboxJoyMapper "$dir/mapper.txt"
[ -z "${3:-}" ] || printf '%s\n' "$3" >> "$dir/mapper.txt"
DOSBOX_JOY_SHOTS="$dir/shots"
printf '\n[capture]\ncapture_dir = %s\n' "$dir/shots" >> "$dir/dosbox.conf"
printf '\n[joystick]\njoysticktype = %s\n' "$2" >> "$dir/dosbox.conf"
printf '\n[sdl]\nmapperfile = %s\n' "$dir/mapper.txt" >> "$dir/dosbox.conf"
XDG_CONFIG_HOME="$dir/xdg" DISPLAY="$DOSBOX_JOY_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"
DOSBOX_JOY_WINDOW=$(joeyFindWindow "$DOSBOX_JOY_DISPLAY" "$dosbox")
[ -n "$DOSBOX_JOY_WINDOW" ] || return 1
DISPLAY="$DOSBOX_JOY_DISPLAY" xdotool windowactivate --sync "$DOSBOX_JOY_WINDOW" 2> /dev/null || true
return 0
fi
joeyAlive "$guard" || return 1
sleep 1
done
return 1
}
# keyboardJoystick keydown|keyup -- hold or let go of DVX's keyboard
# joystick keys.
keyboardJoystick() {
local k
for k in "${keyboardKeys[@]}"; do
DISPLAY="$DOSBOX_JOY_DISPLAY" xdotool "$1" --window "$DOSBOX_JOY_WINDOW" "$k"
done
}
# stop -- end the DOSBox run.
stop() {
joeyStop "$dosbox" "$guard" 2> /dev/null
dosbox=""
guard=""
}
DOSBOX_JOY_DISPLAY=":$((RANDOM % 400 + 4300))"
Xvfb "$DOSBOX_JOY_DISPLAY" -screen 0 1024x768x24 -nolisten tcp > /dev/null 2>&1 &
xvfb=$!
sleep 1
# 1. Both sticks on the game port, moved through the mapper.
if ! boot sticks 4axis; then
echo "$JOY_LABEL: FAIL - DVX with JOY did not come up (DVX.LOG follows)" >&2
dvxDvxLog "$JOY_WORK/sticks/c" >&2
exit 1
fi
dvxDvxLog "$JOY_WORK/sticks/c" 400 | grep -q "$twoSticksLog" || joyFail "DVX.LOG has no \"$twoSticksLog\": $(dvxDvxLog "$JOY_WORK/sticks/c" 400 | grep 'Joystick:' | tr '\n' ' ')"
joyCheck "rest" "A=$JOY_REST B=$JOY_REST"
joyRunSteps "${JOY_STEPS_TWO_STICKS[@]}"
dosboxJoyTap escape
for ((waited = 0; waited < exitSeconds; waited++)); do
dvxDvxLog "$JOY_WORK/sticks/c" 400 | grep -qF "$reapedLog" && break
sleep 1
done
dvxDvxLog "$JOY_WORK/sticks/c" 400 | grep -qF "$reapedLog" || joyFail "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.
if ! boot none disabled "$keyboardBinds"; then
echo "$JOY_LABEL: FAIL - DVX with JOY and no joystick did not come up (DVX.LOG follows)" >&2
dvxDvxLog "$JOY_WORK/none/c" >&2
exit 1
fi
dvxDvxLog "$JOY_WORK/none/c" 400 | grep -q "$keyboardLog" || joyFail "DVX.LOG has no \"$keyboardLog\" (the keyboard joystick): $(dvxDvxLog "$JOY_WORK/none/c" 400 | grep 'Joystick:' | tr '\n' ' ')"
joyCheck "no joystick" "A=$JOY_ABSENT B=$JOY_ABSENT"
keyboardJoystick keydown
sleep "$JOY_PRESS_SECONDS"
joyCheck "no joystick, keyboard joystick held" "A=$JOY_ABSENT B=$JOY_ABSENT"
keyboardJoystick keyup
stop
joyResult "$JOY_LABEL" "both sticks, every direction, both buttons and every release; no joystick without one, the keyboard joystick left out"