120 lines
3.6 KiB
Bash
120 lines
3.6 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 joystick gates of the ports that read the host's gamepads
|
|
# (SDL3, Win32 under Wine): two virtual Xbox 360 pads
|
|
# (scripts/lib/virtualPad.py) as sticks A and B. The pads are the whole
|
|
# machine's while they exist, so a real pad plugged in here would be seen
|
|
# too: PAD_ONLY_ENV is the environment that hides every other pad from SDL
|
|
# (and from Wine's SDL-backed bus driver). Source after joyGate.sh.
|
|
#
|
|
# padStart WORK make the pads (exit 2 when /dev/uinput cannot be
|
|
# written)
|
|
# padOrder the host numbers two identical pads in its own
|
|
# order (Wine does B first): move pad A once with JOY
|
|
# up, and if JOY shows it as stick B, swap the pads'
|
|
# roles from then on
|
|
# padStop remove them
|
|
# joyPress / joyRelease joyGate.sh's: hold or let go of the controls
|
|
|
|
PAD_PID=""
|
|
PAD_FD=""
|
|
PAD_OUT=""
|
|
PAD_SENT=0
|
|
PAD_SWAP=0
|
|
PAD_READY_SECONDS=10
|
|
PAD_REPLY_SECONDS=5
|
|
# The virtual pads' USB vendor/product (virtualPad.py's).
|
|
PAD_ONLY_ENV=(SDL_GAMECONTROLLER_IGNORE_DEVICES_EXCEPT=0x045e/0x028e)
|
|
|
|
|
|
# padWait COUNT SECONDS -- wait until the pad helper has printed COUNT
|
|
# lines of "ok" (or, COUNT 0, "ready").
|
|
padWait() {
|
|
local deadline=$((SECONDS + $2))
|
|
while [ "$SECONDS" -lt "$deadline" ]; do
|
|
if [ "$1" = 0 ]; then
|
|
grep -qx ready "$PAD_OUT" 2> /dev/null && return 0
|
|
elif [ "$(grep -cx ok "$PAD_OUT" 2> /dev/null)" -ge "$1" ]; then
|
|
return 0
|
|
fi
|
|
joeyAlive "$PAD_PID" || return 1
|
|
sleep 0.1
|
|
done
|
|
return 1
|
|
}
|
|
|
|
|
|
padOrder() {
|
|
local got
|
|
padSend press A-left
|
|
sleep "$JOY_PRESS_SECONDS"
|
|
joyShot "$JOY_WORK/order.png"
|
|
got=$(python3 "$JOEY_REPO/scripts/lib/joyScreen.py" "$JOY_WORK/order.png" "${JOY_SCREEN_ARGS[@]}")
|
|
padSend release A-left
|
|
sleep "$JOY_PRESS_SECONDS"
|
|
case "$got" in
|
|
*"A=1-000"*) PAD_SWAP=0 ;;
|
|
*"B=1-000"*) PAD_SWAP=1 ;;
|
|
*) joyFail "pad A moved neither stick: $got" ;;
|
|
esac
|
|
}
|
|
|
|
|
|
# padSend WORD CONTROL... -- with the pads' roles swapped (PAD_SWAP), stick
|
|
# A's controls go to pad B and B's to pad A.
|
|
padSend() {
|
|
local words=("$1")
|
|
local control
|
|
shift
|
|
for control in "$@"; do
|
|
if [ "$PAD_SWAP" = 1 ]; then
|
|
case "$control" in
|
|
A-*) control="B-${control#A-}" ;;
|
|
B-*) control="A-${control#B-}" ;;
|
|
esac
|
|
fi
|
|
words+=("$control")
|
|
done
|
|
printf '%s\n' "${words[*]}" >&"$PAD_FD"
|
|
PAD_SENT=$((PAD_SENT + 1))
|
|
padWait "$PAD_SENT" "$PAD_REPLY_SECONDS" || joyFail "the virtual pads did not take: $*"
|
|
}
|
|
|
|
|
|
padStart() {
|
|
local fifo="$1/pad.in"
|
|
if [ ! -w /dev/uinput ]; then
|
|
echo "$JOY_LABEL: cannot write /dev/uinput, which the virtual gamepads need (a desktop session's user usually can)" >&2
|
|
exit 2
|
|
fi
|
|
PAD_OUT="$1/pad.out"
|
|
mkfifo "$fifo"
|
|
python3 "$JOEY_REPO/scripts/lib/virtualPad.py" "JoeyLib gate" < "$fifo" > "$PAD_OUT" 2>&1 &
|
|
PAD_PID=$!
|
|
exec {PAD_FD}> "$fifo"
|
|
if ! padWait 0 "$PAD_READY_SECONDS"; then
|
|
echo "$JOY_LABEL: FAIL - the virtual gamepads did not come up: $(cat "$PAD_OUT")" >&2
|
|
exit 1
|
|
fi
|
|
}
|
|
|
|
|
|
padStop() {
|
|
[ -n "$PAD_PID" ] || return 0
|
|
printf 'quit\n' >&"$PAD_FD" 2> /dev/null
|
|
exec {PAD_FD}>&-
|
|
wait "$PAD_PID" 2> /dev/null
|
|
PAD_PID=""
|
|
}
|
|
|
|
|
|
joyPress() {
|
|
padSend press "$@"
|
|
}
|
|
|
|
|
|
joyRelease() {
|
|
padSend release "$@"
|
|
}
|