155 lines
5.6 KiB
Bash
Executable file
155 lines
5.6 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-dos-mouse.sh - Headless mouse acceptance gate for the DOS port.
|
|
# The port has the mouse driver call it on every move and button change
|
|
# (INT 33h function 0Ch, a DPMI real-mode callback, src/dos/input.c); a
|
|
# callback that never runs leaves the pointer where it started. Runs
|
|
# KEYS.EXE in DOSBox Staging under Xvfb with the mouse captured from the
|
|
# start, moves it with xdotool (real X pointer motion), and decodes KEYS's
|
|
# mouse verification scanline (row 199: 5A5A, the button flags, X, Y as
|
|
# nibble pixels) from a raw screenshot after each step. Covered: the start
|
|
# at the stage's middle, a move each way on both axes, clamping at all
|
|
# four edges, an odd column, and press and release of the left and right
|
|
# buttons.
|
|
# DOSBox scales host motion into mickeys its own way, so a move is checked
|
|
# by its direction and only the clamps by position, and a one-pixel nudge
|
|
# by landing on an odd column: in mode 13h the driver steps X by 2 in a
|
|
# 640-wide virtual screen, which the port halves, so every column counts.
|
|
#
|
|
# Usage: scripts/verify-dos-mouse.sh
|
|
# Requires: DOSBox Staging (install.sh install emulator:dos), Xvfb, xdotool, python3 + PIL;
|
|
# build/dos/bin/KEYS.EXE (make dos).
|
|
set -uo pipefail
|
|
|
|
repo=$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)
|
|
source "$repo/scripts/lib/joeyEnv.sh"
|
|
source "$repo/scripts/lib/dosboxKeys.sh"
|
|
joeyNeed DOSBOX dosbox
|
|
bin_dir=$repo/build/dos/bin
|
|
conf=$repo/scripts/dosbox-386sx16.conf
|
|
label=verify-dos-mouse
|
|
|
|
[ -f "$bin_dir/KEYS.EXE" ] || { echo "$label: build KEYS.EXE first (make dos)" >&2; exit 2; }
|
|
|
|
work=$(mktemp -d -t joeylib-dos-mouse.XXXXXX)
|
|
display_num=$((RANDOM % 500 + 300))
|
|
cleanup() {
|
|
joeyStop "${dosbox_pid:-}"
|
|
[ -n "${xvfb_pid:-}" ] && kill "$xvfb_pid" 2>/dev/null
|
|
rm -rf "$work"
|
|
}
|
|
trap cleanup EXIT
|
|
|
|
mkdir -p "$work/shots"
|
|
dosboxKeysMapper "$work/mapper.txt"
|
|
|
|
Xvfb ":$display_num" -screen 0 1024x768x24 >/dev/null 2>&1 &
|
|
xvfb_pid=$!
|
|
sleep 1
|
|
|
|
XDG_CONFIG_HOME="$work/xdg" DISPLAY=":$display_num" "$DOSBOX" "${JOEY_DOSBOX_ISOLATE[@]}" \
|
|
--conf "$conf" \
|
|
--set "capture_dir=$work/shots" \
|
|
--set "mapperfile=$work/mapper.txt" \
|
|
--set "mouse_capture=onstart" \
|
|
-c "MOUNT C $bin_dir" -c "C:" -c "KEYS.EXE" \
|
|
>"$work/dosbox.log" 2>&1 &
|
|
dosbox_pid=$!
|
|
|
|
# Let DOS boot and KEYS reach its poll loop on the simulated 386SX-16.
|
|
sleep 12
|
|
|
|
win=$(joeyFindWindow ":$display_num" "$dosbox_pid")
|
|
if [ -z "$win" ]; then
|
|
echo "$label: FAIL - DOSBox window not found" >&2
|
|
exit 1
|
|
fi
|
|
DISPLAY=":$display_num" xdotool windowactivate --sync "$win" 2>/dev/null || true
|
|
|
|
# The mouse row of a fresh screenshot as "flags x y" (hex flags).
|
|
readRow() {
|
|
local png="$work/$1.png"
|
|
dosboxShot ":$display_num" "$win" "$work/shots" "$png" || return 1
|
|
python3 - "$png" <<'PY'
|
|
import sys
|
|
from PIL import Image
|
|
|
|
img = Image.open(sys.argv[1]).convert("RGB")
|
|
w, h = img.size
|
|
sx, sy = w // 320, h // 200
|
|
if sx < 1 or sy < 1 or w % 320 or h % 200:
|
|
sys.exit(1)
|
|
|
|
def dac8(c4):
|
|
v6 = ((c4 << 2) | (c4 >> 2)) & 0x3F
|
|
return (v6 << 2) | (v6 >> 4)
|
|
|
|
# KEYS palette 0 (as verify-dos-input.sh decodes it).
|
|
pal12 = [(i << 8) | (i << 4) | i for i in range(16)]
|
|
pal12[0], pal12[1], pal12[2], pal12[3] = 0x000, 0x333, 0x0F0, 0xFFF
|
|
pal12[15] = 0xF00
|
|
palRgb = [(dac8((v >> 8) & 0xF), dac8((v >> 4) & 0xF), dac8(v & 0xF)) for v in pal12]
|
|
|
|
def pix(px):
|
|
r, g, b = img.getpixel((px * sx, 199 * sy))
|
|
return min(range(16), key=lambda n: (r - palRgb[n][0]) ** 2 + (g - palRgb[n][1]) ** 2 + (b - palRgb[n][2]) ** 2)
|
|
|
|
nib = [pix(i) for i in range(16)]
|
|
words = [(nib[4 * k] << 12) | (nib[4 * k + 1] << 8) | (nib[4 * k + 2] << 4) | nib[4 * k + 3] for k in range(4)]
|
|
if words[0] != 0x5A5A:
|
|
sys.exit(1)
|
|
print("%04X %d %d" % (words[1], words[2], words[3]))
|
|
PY
|
|
}
|
|
|
|
fails=()
|
|
step() {
|
|
local tag="$1"
|
|
local row
|
|
shift
|
|
if [ $# -gt 0 ]; then
|
|
DISPLAY=":$display_num" xdotool "$@"
|
|
sleep 1
|
|
fi
|
|
if ! row=$(readRow "$tag"); then
|
|
fails+=("$tag: no mouse row in the screenshot")
|
|
row="---- -1 -1"
|
|
fi
|
|
read -r flags mx my <<< "$row"
|
|
echo " $tag: flags=$flags x=$mx y=$my"
|
|
}
|
|
|
|
step start
|
|
[ "$flags $mx $my" = "1000 160 100" ] || fails+=("start: expected flags 1000 at 160,100")
|
|
step upLeft mousemove_relative -- -60 -40
|
|
[ "$flags" = "1000" ] && [ "$mx" -lt 160 ] && [ "$my" -lt 100 ] || fails+=("a move up and left did not arrive")
|
|
lastX=$mx
|
|
lastY=$my
|
|
step downRight mousemove_relative -- 120 80
|
|
[ "$flags" = "1000" ] && [ "$mx" -gt "$lastX" ] && [ "$my" -gt "$lastY" ] || fails+=("a move down and right did not arrive")
|
|
step topLeft mousemove_relative -- -2000 -2000
|
|
[ "$flags $mx $my" = "1000 0 0" ] || fails+=("not held at the top left (0,0)")
|
|
step nudge mousemove_relative -- 1 0
|
|
[ "$flags" = "1000" ] && [ $((mx % 2)) -eq 1 ] || fails+=("a one-pixel nudge did not reach an odd column")
|
|
step bottomRight mousemove_relative -- 2000 2000
|
|
[ "$flags $mx $my" = "1000 319 199" ] || fails+=("not held at the bottom right (319,199)")
|
|
step leftDown mousedown 1
|
|
[ "$flags" = "1100" ] || fails+=("the left button down not seen")
|
|
step leftUp mouseup 1
|
|
[ "$flags" = "1000" ] || fails+=("the left button up not seen")
|
|
step rightDown mousedown 3
|
|
[ "$flags" = "1010" ] || fails+=("the right button down not seen")
|
|
step rightUp mouseup 3
|
|
[ "$flags" = "1000" ] || fails+=("the right button up not seen")
|
|
|
|
if [ ${#fails[@]} -eq 0 ]; then
|
|
echo "$label: PASS (moves both ways, all four edges, every column, both buttons down and up)"
|
|
exit 0
|
|
fi
|
|
for f in "${fails[@]}"; do
|
|
echo "$label: FAIL - $f" >&2
|
|
done
|
|
exit 1
|