joeylib2/scripts/lib/win16Gate.sh
2026-10-13 14:01:46 -05:00

141 lines
5.3 KiB
Bash

# Part of JoeyLib, Copyright (c) 2026 Scott Duensing, under the MIT License.
# See LICENSE.md for license information.
# SPDX-License-Identifier: MIT
#
# Shared by the Win16 gates (scripts/bench-win16.sh, scripts/verify-win16-*.sh)
# and joey-run: run a Win16 program (make win16) on Windows for Workgroups
# 3.11 under DOSBox Staging (scripts/dosbox-win16.conf), headless on a
# private Xvfb display (or, WIN16_HEADLESS=0, in a window on $DISPLAY).
# Windows is a copy of the installer's (component win311, WIN16_C) made for
# the run, so a run never changes it; the program's folder is drive D:, so
# what it writes lands there, and it is Windows' shell, so Windows ends (and
# DOSBox with it) when the program does. The copy runs JoeyLib's own drivers:
# KPJOY.DRV (component kpjoy, KPJOY_DRV), the joystick driver Windows 3.1
# lacks, and KPCOMM.DRV (component kpcomm, KPCOMM_DRV) in place of its
# COMM.DRV. Source after joeyEnv.sh.
#
# win16Need exit 2 unless Windows, its drivers and DOSBox
# are installed
# win16Stage WORK FOLDER EXE [ARGS...]
# WORK/c: a copy of Windows whose shell is EXE
# in FOLDER (drive D:), given ARGS (WIN.COM
# hands its command line to the shell; a shell=
# line with arguments is not loaded at all),
# with JoeyLib's drivers
# win16Start WORK [NAME=VALUE...]
# boot it in the background, the NAME=VALUEs in
# the DOS environment Windows starts from (the
# port's JOEYLIB_SCALE and the like); sets
# WIN16_DISPLAY, WIN16_GUARD (the timeout
# wrapper, which is DOSBox itself when
# WIN16_WALL=0: no cap) and WIN16_PID (DOSBox).
# Sound is off unless WIN16_SOUND=1 (then SDL
# plays it, through SDL_AUDIODRIVER if set);
# WIN16_DOSBOX_ARGS are more DOSBox arguments
# (--set serial1=...)
# win16Log FOLDER print the program's joeylog in FOLDER, if any
# (a DOS program makes it JOEYLOG.TXT)
# win16Window print DOSBox's X window
# win16Stop stop DOSBox and the display
#
# Win16 code only ever runs on DOSBox's emulated CPU: running it natively
# (the 32-bit Wine's winevdm) crashed the host's kernel.
WIN16_DISPLAY=""
WIN16_GUARD=""
WIN16_PID=""
WIN16_XVFB_PID=""
WIN16_FOLDER=""
WIN16_ARGS=""
WIN16_HEADLESS="${WIN16_HEADLESS:-1}"
WIN16_SOUND="${WIN16_SOUND:-0}"
WIN16_DOSBOX_ARGS=()
# A hard cap on DOSBox in seconds, whatever happens to the script that
# started it; 0 for none.
WIN16_WALL="${WIN16_WALL:-900}"
win16Need() {
joeyNeed DOSBOX dosbox
joeyNeed WIN16_C emulator:win16
joeyNeed KPJOY_DRV kpjoy
joeyNeed KPCOMM_DRV kpcomm
}
win16Stage() {
local work="$1"
WIN16_FOLDER="$2"
WIN16_ARGS="${*:4}"
rm -rf "${work:?}/c"
mkdir -p "$work"
cp -r "$WIN16_C" "$work/c"
cp "$KPJOY_DRV" "$work/c/WINDOWS/SYSTEM/KPJOY.DRV"
cp "$KPCOMM_DRV" "$work/c/WINDOWS/SYSTEM/KPCOMM.DRV"
python3 "$JOEY_REPO/tools/winIni.py" "$work/c/WINDOWS/SYSTEM.INI" \
set boot shell "D:\\$3" \
set boot comm.drv kpcomm.drv \
set drivers joystick kpjoy.drv
}
win16Start() {
local work="$1"
local assign
local envArgs=()
local soundArgs=(--set "nosound=true")
local procEnv=(XDG_CONFIG_HOME="$work/xdg" SDL_AUDIODRIVER=dummy)
local wrapper=()
shift
for assign in "$@"; do
envArgs+=(-c "SET $assign")
done
if [ "$WIN16_SOUND" = "1" ]; then
soundArgs=()
procEnv=(XDG_CONFIG_HOME="$work/xdg")
fi
[ "$WIN16_WALL" = "0" ] || wrapper=(timeout -s KILL "$WIN16_WALL")
mkdir -p "$work/xdg"
if [ "$WIN16_HEADLESS" = "1" ]; then
# A second boot in the same WORK (the save gate's) must not read the
# last display's number before the new Xvfb has written its own.
rm -f "$work/display"
Xvfb -displayfd 3 -screen 0 1024x768x24 -nolisten tcp 3> "$work/display" > /dev/null 2>&1 &
WIN16_XVFB_PID=$!
for _ in 1 2 3 4 5 6 7 8 9 10; do
[ -s "$work/display" ] && break
sleep 0.5
done
WIN16_DISPLAY=":$(cat "$work/display")"
else
WIN16_DISPLAY="$DISPLAY"
fi
env "${procEnv[@]}" DISPLAY="$WIN16_DISPLAY" "${wrapper[@]}" "$DOSBOX" "${JOEY_DOSBOX_ISOLATE[@]}" \
--conf "$JOEY_REPO/scripts/dosbox-win16.conf" "${soundArgs[@]}" "${WIN16_DOSBOX_ARGS[@]}" \
-c "MOUNT C \"$work/c\"" -c "MOUNT D \"$WIN16_FOLDER\"" "${envArgs[@]}" \
-c "C:" -c 'CD \WINDOWS' -c "WIN${WIN16_ARGS:+ $WIN16_ARGS}" -c "EXIT" > "$work/dosbox.log" 2>&1 &
WIN16_GUARD=$!
if [ ${#wrapper[@]} -eq 0 ]; then
WIN16_PID=$WIN16_GUARD
else
WIN16_PID="$(joeyChild "$WIN16_GUARD")"
fi
}
win16Log() {
find "$1" -maxdepth 1 -iname joeylog.txt -print -quit 2> /dev/null
}
win16Window() {
joeyFindWindow "$WIN16_DISPLAY" "$WIN16_PID"
}
win16Stop() {
joeyStop "$WIN16_PID" "$WIN16_GUARD" "$WIN16_XVFB_PID" 2> /dev/null
WIN16_PID=""
WIN16_GUARD=""
WIN16_XVFB_PID=""
}