# 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 Win32 gates (scripts/bench-win32.sh, scripts/verify-win32-*.sh) # and joey-run: run a Win32 program (make win32) under Wine on a private Xvfb # display with a private Wine prefix, so nothing reaches ~/.wine. Source after # joeyEnv.sh. (Win16 programs run on Windows 3.11 in DOSBox instead: # scripts/lib/win16Gate.sh.) # # wineNeed exit 2 unless Wine is installed # wineEnv PREFIX Wine's environment for the prefix PREFIX, quiet; # Wine makes the prefix when it first runs # winePrefix WORK a prefix under WORK, made and settled before # anything runs in it (a program started alongside # Wine's first-run setup got no sound for its first # seconds); sets WINEPREFIX # wineStart WORK a display, then winePrefix WORK; sets DISPLAY, # WINEPREFIX and WINE_XVFB_PID # wineComPort N PATH the prefix's COMn opens the Unix device PATH (a pty # a gate's peer or joey-run's socat made), and its # wineserver is started; before wineRun # wineRun EXE [ARGS...] start EXE in the background; sets WINE_PID # wineWindow TITLE print the X window titled TITLE (waits for it) # wineStop stop the program, the prefix's wineserver and the # display WINE_PID="" WINE_XVFB_PID="" WINE_WINDOW_TRIES=60 wineNeed() { joeyNeed WINE wine } wineEnv() { mkdir -p "$1" export WINEPREFIX="$1" export WINEDEBUG=-all # No Mono or Gecko install prompts in a fresh prefix. export WINEDLLOVERRIDES="mscoree,mshtml=" } winePrefix() { wineEnv "$1/prefix" "$WINE" wineboot --init > "$1/wineboot.log" 2>&1 "$(dirname "$WINE")/wineserver" -w } wineStart() { export DISPLAY=":$((RANDOM % 400 + 1800))" Xvfb "$DISPLAY" -screen 0 1280x1024x24 -nolisten tcp > /dev/null 2>&1 & WINE_XVFB_PID=$! sleep 1 winePrefix "$1" } # Wine's mount manager makes dosdevices/comN from HKLM\Software\Wine\Ports # when Wine starts (a link made by hand is refused, access denied), so the # key is set and Wine restarted. The new wineserver is started here, its # output in a log: it opens the pty itself and, a daemon with no terminal, # makes it its own, so when the far end closes the pty it gets SIGHUP and # prints every object it holds (megabytes), which must not land where the # program's output goes. wineComPort() { printf 'REGEDIT4\r\n\r\n[HKEY_LOCAL_MACHINE\\Software\\Wine\\Ports]\r\n"COM%s"="%s"\r\n' "$1" "$2" > "$WINEPREFIX/../ports.reg" "$WINE" regedit /S "$WINEPREFIX/../ports.reg" "$(dirname "$WINE")/wineserver" -k "$(dirname "$WINE")/wineserver" -w "$(dirname "$WINE")/wineserver" > "$WINEPREFIX/../wineserver.log" 2>&1 } wineRun() { "$WINE" "$@" > "$WINEPREFIX/../wine.log" 2>&1 & WINE_PID=$! } wineWindow() { local tries local win for ((tries = 0; tries < WINE_WINDOW_TRIES; tries++)); do win="$(xdotool search --name "^$1\$" 2>/dev/null | head -1)" if [ -n "$win" ]; then printf '%s\n' "$win" return 0 fi sleep 1 done return 1 } wineStop() { joeyStop "$WINE_PID" WINE_PID="" if [ -n "${WINEPREFIX:-}" ]; then "$(dirname "$WINE")/wineserver" -k 2> /dev/null || true fi if [ -n "$WINE_XVFB_PID" ]; then kill "$WINE_XVFB_PID" 2> /dev/null || true WINE_XVFB_PID="" fi }