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

115 lines
5 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-win16-joy.sh - Joystick gate for the Win16 port: both sticks,
# every direction, both buttons and every release, driven through DOSBox
# Staging's emulated game port, read by Windows through KPJOY.DRV (the
# joystick driver JoeyLib's Windows runs with, component kpjoy) and read
# back from what examples/joy draws; and no joystick at all when the game
# port is empty.
#
# The port (src/hosted/win16) reads the first two sticks through MMSYSTEM's
# joyGetPos, scaled by joyGetDevCaps's ranges. DOSBox Staging's joysticktype
# 4axis puts four axes and four buttons on the game port with no host
# joystick, which KPJOY reads as two sticks, A and B, as a Y cable gives
# them; it takes each axis's rest from its first readings and reports
# readings near the rest as the rest, so the dots sit exactly in the middle
# through the jitter interrupts give the counts. KPJOY counts reads of the
# port, so the emulated CPU runs at a fixed speed, as a real PC's does: at
# DOSBox's max cycles (how joey-run runs Windows) the speed follows this
# machine's load, and the counts with it. A mapper file binds host keys to
# DOSBox's joystick events (scripts/lib/dosboxJoy.sh) and xdotool holds
# them; the raw screenshot is on a key of its own, so no Ctrl reaches
# Windows. The mapper also leaves out any joystick plugged into this
# machine, so nothing else moves the sticks: they rest in the middle,
# buttons up.
#
# JOY (examples/joy) is Windows' shell, at one window pixel per stage pixel;
# scripts/lib/joyScreen.py reads both sticks' whole state off each
# screenshot of the 800x600 screen (scripts/lib/joyGate.sh's steps). Escape
# then ends JOY, and with it Windows and DOSBox.
#
# Second run, joysticktype disabled: no game port, so JOY must show both
# sticks disconnected.
#
# Usage: scripts/verify-win16-joy.sh
# Env: JOY_SHOTS also keep the screenshots in this folder
# Requires: DOSBox Staging, Xvfb, xdotool, python3 + PIL, the win311 and
# kpjoy components; JOY.EXE (make win16).
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/win16Gate.sh"
win16Need
JOY_LABEL=verify-win16-joy
JOY_SCALE=1
program=$(joeyExampleProgram joy win16)
[ -f "$program" ] || { echo "$JOY_LABEL: build JOY.EXE first (make win16)" >&2; exit 2; }
upSeconds=60 # Windows booting until JOY's screen is there
exitSeconds=20 # Escape until Windows and DOSBox have ended
fixedCycles=60000 # the emulated CPU's speed (DOSBox cycles per ms)
JOY_WORK=$(mktemp -d -t joeylib-win16-joy.XXXXXX)
cleanup() {
win16Stop
rm -rf "$JOY_WORK"
}
trap cleanup EXIT
# boot RUN JOYTYPE -- Windows staged in $JOY_WORK/RUN with JOY its shell,
# DOSBox's joysticktype JOYTYPE and the joystick mapper; waits for JOY's
# screen.
boot() {
local dir="$JOY_WORK/$1"
local waited
mkdir -p "$dir/program" "$dir/shots"
cp "$program" "$dir/program/JOY.EXE"
win16Stage "$dir" "$dir/program" JOY.EXE
dosboxJoyMapper "$dir/mapper.txt"
DOSBOX_JOY_SHOTS="$dir/shots"
WIN16_DOSBOX_ARGS=(--set "capture_dir=$dir/shots" --set "default_image_capture_formats=raw"
--set "joysticktype=$2" --set "mapperfile=$dir/mapper.txt"
--set "cpu_cycles=$fixedCycles" --set "cpu_cycles_protected=$fixedCycles")
win16Start "$dir" JOEYLIB_SCALE=1 JOEYLIB_ASPECT=square
DOSBOX_JOY_DISPLAY="$WIN16_DISPLAY"
for ((waited = 0; waited < upSeconds; waited++)); do
joeyAlive "$WIN16_PID" || return 1
DOSBOX_JOY_WINDOW=$(win16Window) && break
sleep 1
done
[ -n "$DOSBOX_JOY_WINDOW" ] || return 1
DISPLAY="$DOSBOX_JOY_DISPLAY" xdotool windowactivate --sync "$DOSBOX_JOY_WINDOW" 2> /dev/null || true
for ((; waited < upSeconds; waited++)); do
joyShot "$dir/up.png" && python3 "$repo/scripts/lib/joyScreen.py" "$dir/up.png" --scale "$JOY_SCALE" > /dev/null 2>&1 && return 0
joeyAlive "$WIN16_PID" || return 1
sleep 1
done
return 1
}
# 1. Both sticks on the game port, moved through the mapper.
boot sticks 4axis || { echo "$JOY_LABEL: FAIL - Windows with JOY did not come up within ${upSeconds} s" >&2; exit 1; }
joyCheck "rest" "A=$JOY_REST B=$JOY_REST"
joyRunSteps "${JOY_STEPS_TWO_STICKS[@]}"
dosboxJoyTap escape
for ((waited = 0; waited < exitSeconds; waited++)); do
joeyAlive "$WIN16_PID" || break
sleep 1
done
joeyAlive "$WIN16_PID" && joyFail "Escape did not end JOY, and Windows with it, within ${exitSeconds} s"
win16Stop
# 2. No game port: no stick.
boot none disabled || { echo "$JOY_LABEL: FAIL - Windows with JOY and no game port did not come up within ${upSeconds} s" >&2; exit 1; }
joyCheck "no joystick" "A=$JOY_ABSENT B=$JOY_ABSENT"
win16Stop
joyResult "$JOY_LABEL" "both sticks through KPJOY.DRV, every direction, both buttons and every release; no stick without a game port"