#!/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-dos-joy.sh - Joystick gate for DOS: 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. # # The port (src/dos/input.c) reads the game port through the BIOS, INT 15h # function 84h: the switches, then the four positions, both sticks connected # while the BIOS answers. DOSBox's joysticktype 4axis gives it two sticks on # one port, moved by host keys through a mapper file # (scripts/lib/dosboxJoy.sh); the raw screenshots go through # scripts/lib/joyScreen.py (scripts/lib/joyGate.sh). With joysticktype none # the BIOS says there is no game port and JOY must show both sticks # disconnected. Each run ends with Escape, which must end JOY (DOSBox exits # after it). # # Usage: scripts/verify-dos-joy.sh # Env: JOY_SHOTS also keep the screenshots in this folder # Requires: DOSBox Staging, Xvfb, xdotool, python3 + PIL; # build/dos/bin/JOY.EXE (make dos). 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" joeyNeed DOSBOX dosbox JOY_LABEL=verify-dos-joy program="$repo/build/dos/bin/JOY.EXE" wall=240 # hard cap on each DOSBox run, whatever happens to this script bootSeconds=12 # DOS booting and JOY drawing on the emulated 386SX-16 exitSeconds=10 # Escape until DOSBox, which exits after JOY, has gone [ -f "$program" ] || { echo "$JOY_LABEL: build JOY.EXE first (make dos)" >&2; exit 2; } JOY_WORK=$(mktemp -d -t joeylib-dos-joy.XXXXXX) guard="" dosbox="" xvfb="" cleanup() { joeyStop "$dosbox" "$guard" "$xvfb" 2> /dev/null rm -rf "$JOY_WORK" } trap cleanup EXIT # boot RUN JOYTYPE -- JOY in DOSBox with joysticktype JOYTYPE; sets guard, # dosbox and DOSBOX_JOY_WINDOW. boot() { local dir="$JOY_WORK/$1" mkdir -p "$dir/xdg" "$dir/shots" dosboxJoyMapper "$dir/mapper.txt" DOSBOX_JOY_SHOTS="$dir/shots" XDG_CONFIG_HOME="$dir/xdg" DISPLAY="$DOSBOX_JOY_DISPLAY" SDL_AUDIODRIVER=dummy \ timeout -s KILL "$wall" "$DOSBOX" "${JOEY_DOSBOX_ISOLATE[@]}" \ --conf "$repo/scripts/dosbox-386sx16.conf" --set "joysticktype=$2" \ --set "mapperfile=$dir/mapper.txt" --set "capture_dir=$dir/shots" \ -c "MOUNT C \"$(dirname "$program")\"" -c "C:" -c "$(basename "$program")" -c "EXIT" \ > "$dir/dosbox.log" 2>&1 & guard=$! dosbox=$(joeyChild "$guard") sleep "$bootSeconds" 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 } # quit -- Escape, which must end JOY and with it DOSBox. quit() { local waited dosboxJoyTap escape for ((waited = 0; waited < exitSeconds; waited++)); do joeyAlive "$guard" || break sleep 1 done joeyAlive "$guard" && joyFail "Escape did not end JOY" joeyStop "$dosbox" "$guard" 2> /dev/null dosbox="" guard="" } DOSBOX_JOY_DISPLAY=":$((RANDOM % 400 + 4700))" Xvfb "$DOSBOX_JOY_DISPLAY" -screen 0 1024x768x24 -nolisten tcp > /dev/null 2>&1 & xvfb=$! sleep 1 # 1. Both sticks on the game port. boot sticks 4axis || { echo "$JOY_LABEL: FAIL - DOSBox with JOY did not come up" >&2; exit 1; } joyCheck "rest" "A=$JOY_REST B=$JOY_REST" joyRunSteps "${JOY_STEPS_TWO_STICKS[@]}" quit # 2. No game port. boot none none || { echo "$JOY_LABEL: FAIL - DOSBox with JOY and no joystick did not come up" >&2; exit 1; } joyCheck "no joystick" "A=$JOY_ABSENT B=$JOY_ABSENT" quit joyResult "$JOY_LABEL" "both sticks, every direction, both buttons and every release; no joystick without a game port; Escape ends JOY"