mirror of
https://github.com/Abdess/retroarch_system.git
synced 2026-10-23 20:29:06 -05:00
fix: end handheld runs that lose their input
Start + Select now kills a sentinel the launcher owns, and the launcher asks the interface to stop, so a running install still writes its result. A mapper that dies, a chvt the compositor refuses, a terminal app that cannot open and a read-only card each end the run or hand over to the next route instead of a console waiting for a button. muOS starts gptokeyb2 with -X and keeps muxterm off the pad so Y opens the options.
This commit is contained in:
1 parent
bfd82cedf8
commit
5db889f538
11 files changed
+1228
-205
No files matched your search
@@ -28,8 +28,10 @@ GAMEDIR="/$directory/ports/retrobios"
|
||||
cd "$GAMEDIR" || exit 1
|
||||
|
||||
# Appended to, as the launcher and install.py also write this file: a tee
|
||||
# writing from its own offset would overwrite their lines.
|
||||
: > "$GAMEDIR/log.txt" && exec > >(tee -a "$GAMEDIR/log.txt") 2>&1
|
||||
# writing from its own offset would overwrite their lines. The previous run's
|
||||
# is kept, for a player who starts again before reading it.
|
||||
[ -s "$GAMEDIR/log.txt" ] && mv -f "$GAMEDIR/log.txt" "$GAMEDIR/log.prev.txt" 2>/dev/null
|
||||
{ : > "$GAMEDIR/log.txt"; } 2>/dev/null && exec > >(tee -a "$GAMEDIR/log.txt") 2>&1
|
||||
|
||||
# PortMasterDialog.txt has shipped with PortMaster since before 2024.02.26,
|
||||
# and the 8.5.23 copy JELOS and KOLOS preinstall has no funcs.txt to source
|
||||
@@ -48,17 +50,33 @@ RB_CONSOLE="${CUR_TTY:-/dev/tty0}"
|
||||
RB_MAPPER="$GPTOKEYB"
|
||||
RB_FALLBACK=1
|
||||
RB_PACK_PATH="$GAMEDIR:/$directory/ports:/$directory"
|
||||
# ArkOS and dArkOS, as control.txt tells them apart (its plymouth theme): the
|
||||
# games card is /$directory, roms2 when fstab mounts a second card, and its
|
||||
# bios folder is the one RetroArch reads. The other card's is offered.
|
||||
# ArkOS and dArkOS, as control.txt tells them apart (its plymouth theme):
|
||||
# their two card folders are offered to each other, and the destination is
|
||||
# the one RetroArch reads. control.txt names the card PortMaster runs from
|
||||
# roms2 whenever fstab mounts a second card, even when RetroArch still reads
|
||||
# /roms/bios, so RetroArch's own setting decides, as in the Tools entry.
|
||||
if [ -e "${RB_ARK_MARKER:-/usr/share/plymouth/themes/text.plymouth}" ]; then
|
||||
RB_OTHER_CARDS="${RB_OTHER_CARDS:-/roms/bios:/roms2/bios}"
|
||||
RB_BIOS_DEST="/$directory/bios"
|
||||
RB_BIOS_DEST=$(awk -F'"' '/^system_directory *=/ { print $2; exit }' \
|
||||
"${RB_RETROARCH_CFG:-/home/ark/.config/retroarch/retroarch.cfg}" 2>/dev/null)
|
||||
RB_BIOS_DEST="${RB_BIOS_DEST:-/$directory/bios}"
|
||||
fi
|
||||
# shellcheck source=../shared/tui.txt
|
||||
source "$GAMEDIR/tui.txt"
|
||||
# shellcheck source=retrobios/screens.txt
|
||||
source "$GAMEDIR/screens.txt"
|
||||
rb_open_log keep
|
||||
|
||||
# muOS: PortMaster's mod_muOS.txt points CUR_TTY at /dev/null, so there is no
|
||||
# console to draw on. muxterm, the terminal muOS's Terminal core runs, shows
|
||||
# the interface instead. It is told to leave the pad alone (an SDL hint) so
|
||||
# that gptokeyb sends every button once, with this port's mapping: muxterm
|
||||
# sends Y as Space, where the interface reads Y as Options. The hint allows
|
||||
# one vendor and product id, 0001:0001, which no pad carries.
|
||||
RB_MUXTERM="${RB_MUXTERM:-/opt/muos/frontend/muxterm}"
|
||||
if [ -x "$RB_MUXTERM" ] && [ -n "$RB_MAPPER" ]; then
|
||||
RB_TERMAPP=(env SDL_GAMECONTROLLER_IGNORE_DEVICES_EXCEPT=0x0001/0x0001 "$RB_MUXTERM" --gl --)
|
||||
fi
|
||||
|
||||
export SDL_GAMECONTROLLERCONFIG="$sdl_controllerconfig"
|
||||
|
||||
|
||||
@@ -17,7 +17,8 @@ options, L1 and R1 to page. B on the first screen closes it.
|
||||
EmulationStation covers the text console on Batocera and Knulli, and no
|
||||
terminal for a script is documented there. RetroBIOS opens xterm (the X11
|
||||
builds) or foot when either is installed, else switches to console 4, runs
|
||||
there, and switches back. Which of the two shows up on a given build is not
|
||||
verified. If the screen stays black, `retrobios/log.txt` says which route
|
||||
there, and switches back. When console 4 does not come to the front within
|
||||
3 s, it switches back and stops. Which of the two shows up on a given build
|
||||
is not verified. If nothing shows, `retrobios/log.txt` says which route
|
||||
was taken, and the computer route always works: with the card mounted, run
|
||||
the one-line command from abdess.github.io/retrobios/wiki/installer.
|
||||
@@ -18,14 +18,20 @@ RB_PACK_PATH="$RB_DIR:$HERE:/userdata"
|
||||
RB_LOG="$RB_DIR/log.txt"
|
||||
|
||||
# The X11 builds start EmulationStation on display :0, which a script run
|
||||
# from it does not always inherit.
|
||||
if [ -z "${DISPLAY:-}" ] && [ -z "${WAYLAND_DISPLAY:-}" ] && [ -S /tmp/.X11-unix/X0 ]; then
|
||||
# from it does not always inherit, with the cookie S31emulationstation
|
||||
# exports: xterm is refused the display without it.
|
||||
if [ -z "${DISPLAY:-}" ] && [ -z "${WAYLAND_DISPLAY:-}" ] \
|
||||
&& [ -S "${RB_X_SOCKET:-/tmp/.X11-unix/X0}" ]; then
|
||||
export DISPLAY=:0
|
||||
fi
|
||||
RB_XAUTH="${RB_XAUTH:-/var/lib/.Xauthority}"
|
||||
if [ -n "${DISPLAY:-}" ] && [ -z "${XAUTHORITY:-}" ] && [ -f "$RB_XAUTH" ]; then
|
||||
export XAUTHORITY="$RB_XAUTH"
|
||||
fi
|
||||
|
||||
# shellcheck source=../shared/tui.txt
|
||||
source "$RB_DIR/tui.txt"
|
||||
: > "$RB_LOG"
|
||||
rb_open_log fresh
|
||||
rb_select_python
|
||||
# A launcher's own arguments are not install.py's (muOS passes the app folder).
|
||||
# shellcheck disable=SC2119
|
||||
|
||||
@@ -7,10 +7,12 @@ It checks the BIOS folder (`/run/muos/storage/bios`) against the files its
|
||||
emulators read and installs the missing ones: downloaded with a network
|
||||
connection, or from a pack copied to the root of the card without one.
|
||||
|
||||
It runs in muxterm, muOS's terminal. muxterm already sends the D-pad as
|
||||
arrows, Start as Enter, B as Backspace (back) and Y as Space (tick). With
|
||||
PortMaster installed, gptokeyb2 adds A (choose), X (tick) and L1/R1 (page).
|
||||
Menu closes it.
|
||||
It runs in muxterm, muOS's terminal. With PortMaster installed, gptokeyb2
|
||||
turns every button into a key and muxterm is told to leave the pad alone, so
|
||||
each button is sent once: D-pad to move, A or Start to choose, B or Select to
|
||||
go back, X to tick a system, Y for the options, L1 and R1 to page. Hold
|
||||
Select, then press Start to quit. Without PortMaster, muxterm reads the pad
|
||||
itself (Y then ticks a system) and Menu closes it.
|
||||
|
||||
Not tried on a device yet: the icon, and whether muxterm and gptokeyb2 share
|
||||
the pad as described.
|
||||
Not tried on a device yet: the icon, and whether muxterm honours SDL's
|
||||
controller allow-list as SDL 2.30 does.
|
||||
@@ -5,8 +5,8 @@
|
||||
|
||||
# RetroBIOS for muOS, from Applications: the installer's full-screen
|
||||
# interface in muxterm, the terminal muOS's own Terminal core runs, with
|
||||
# PortMaster's gptokeyb2 adding the keys muxterm leaves unmapped. Without
|
||||
# muxterm it runs on the console with the full mapping.
|
||||
# PortMaster's gptokeyb2 turning the buttons into keys. Without muxterm it
|
||||
# runs on the console.
|
||||
|
||||
MUOS="${RB_MUOS:-/opt/muos}"
|
||||
# shellcheck source=/dev/null
|
||||
@@ -19,12 +19,12 @@ PM_DIR="$ROM_MOUNT/MUOS/PortMaster"
|
||||
MUXTERM="$MUOS/frontend/muxterm"
|
||||
|
||||
RB_DIR="$APP_DIR"
|
||||
RB_PACK_PATH="$APP_DIR:$ROM_MOUNT"
|
||||
RB_LOG="$APP_DIR/log.txt"
|
||||
# muOS binds MUOS/bios from SD2 when that card has one, else from SD1, onto
|
||||
# /run/muos/storage/bios (script/device/bind.sh:10-11,30-52): the other card's
|
||||
# MUOS/bios is the one offered to copy from.
|
||||
SD2_MOUNT="$(GET_VAR "device" "storage/sdcard/mount")"
|
||||
RB_PACK_PATH="$APP_DIR:$ROM_MOUNT:$SD2_MOUNT"
|
||||
RB_OTHER_CARDS="$ROM_MOUNT/MUOS/bios:$SD2_MOUNT/MUOS/bios"
|
||||
RB_BIOS_DEST="${RB_BIOS_DEST:-/run/muos/storage/bios}"
|
||||
# PortMaster keeps its runtimes here: the Python runtime is used when
|
||||
@@ -33,21 +33,32 @@ controlfolder="$PM_DIR"
|
||||
|
||||
# shellcheck source=../shared/tui.txt
|
||||
source "$APP_DIR/tui.txt"
|
||||
: > "$RB_LOG"
|
||||
rb_open_log fresh
|
||||
|
||||
RB_GPTK="$APP_DIR/retrobios.gptk"
|
||||
if [ -x "$PM_DIR/gptokeyb2" ]; then
|
||||
# As muOS's own launchers start it (script/launch/ext-xroar.sh).
|
||||
# As muOS's own launchers start it (script/launch/ext-xroar.sh). gptokeyb2
|
||||
# kills only with SIGKILL: by process name by default, which never names
|
||||
# this run, and quits all the same, or with -X by command line, which is
|
||||
# what reaches the Start + Select sentinel (gptokeyb2 src/util.c).
|
||||
ln -sf "$PM_DIR/libinterpose.aarch64.so" "/usr/lib/libinterpose.aarch64.so" >/dev/null 2>&1
|
||||
RB_MAPPER="$PM_DIR/gptokeyb2"
|
||||
RB_MAPPER="$PM_DIR/gptokeyb2 -X"
|
||||
fi
|
||||
if [ -x "$MUXTERM" ]; then
|
||||
RB_ROUTES="app"
|
||||
RB_TERMAPP=("$MUXTERM" --gl --)
|
||||
RB_GPTK="$APP_DIR/muxterm.gptk"
|
||||
if [ -n "${RB_MAPPER:-}" ]; then
|
||||
# muxterm reads the pad itself and sends Y as Space (MustardOS/terminal
|
||||
# input.c), where the interface reads Y as Options. The SDL hint allows
|
||||
# only vendor and product 0001:0001, which no pad carries, so muxterm
|
||||
# leaves the pad to gptokeyb2 and every button is sent once.
|
||||
RB_TERMAPP=(env SDL_GAMECONTROLLER_IGNORE_DEVICES_EXCEPT=0x0001/0x0001 "$MUXTERM" --gl --)
|
||||
else
|
||||
# No gptokeyb2: muxterm's own buttons are all there is.
|
||||
RB_TERMAPP=("$MUXTERM" --gl --)
|
||||
fi
|
||||
else
|
||||
RB_ROUTES="vt"
|
||||
RB_CONSOLE="${RB_CONSOLE:-/dev/tty0}"
|
||||
RB_GPTK="$APP_DIR/retrobios.gptk"
|
||||
fi
|
||||
|
||||
rb_select_python
|
||||
|
||||
@@ -1,10 +0,0 @@
|
||||
# RetroBIOS inside muxterm (gptokeyb2). muxterm already sends the D-pad as
|
||||
# arrows, Start as Enter, B as Backspace and Y as Space to the program it
|
||||
# runs (MustardOS/terminal input.c and osk.c), so mapping those here too
|
||||
# would press each key twice. Only what muxterm leaves unmapped is added.
|
||||
[controls]
|
||||
overlay = clear
|
||||
a = enter
|
||||
x = space
|
||||
l1 = pageup
|
||||
r1 = pagedown
|
||||
+434
-95
@@ -5,18 +5,23 @@
|
||||
#
|
||||
# Sourced by every launcher (the PortMaster port, the Batocera and Knulli
|
||||
# port, the muOS app, the Tools entry), each of which sets the inputs below
|
||||
# and calls rb_select_python, then rb_launch. Run as `bash tui.txt here`, it
|
||||
# is the part that runs inside a terminal app the launcher opened.
|
||||
# and calls rb_open_log, rb_select_python, then rb_launch. Run as
|
||||
# `bash tui.txt here`, it is the part that runs inside a terminal app the
|
||||
# launcher opened.
|
||||
#
|
||||
# Inputs, all optional:
|
||||
# RB_ROUTES where to look for a terminal, in order: here (this
|
||||
# script's own stdin and stdout), app (foot or xterm in the
|
||||
# graphical session), vt (a Linux console). Default "app vt".
|
||||
# A route that fails to show the interface hands over to the
|
||||
# next one.
|
||||
# RB_CONSOLE the console of the vt route (default /dev/tty0)
|
||||
# RB_VT_SWITCH a console number to switch to when a graphical session
|
||||
# covers the current one (Batocera, Knulli)
|
||||
# RB_MAPPER the command that starts gptokeyb, kill flag included
|
||||
# ("$GPTOKEYB" in a PortMaster port), empty for none
|
||||
# RB_MAPPER_NEEDED 0 when the terminal reads the pad itself, so the
|
||||
# interface still has buttons without the mapper
|
||||
# RB_GPTK its mapping (default retrobios.gptk next to this file)
|
||||
# RB_PACK_PATH folders searched for a pack when there is no network,
|
||||
# separated by ":", the first one named to the player
|
||||
@@ -44,33 +49,140 @@ RB_FB="${RB_FB:-/sys/class/graphics/fb0/virtual_size}"
|
||||
RB_ROUTE_TABLE="${RB_ROUTE_TABLE:-/proc/net/route}"
|
||||
RB_VT_ACTIVE="${RB_VT_ACTIVE:-/sys/class/tty/tty0/active}"
|
||||
RB_PROBE_URL="${RETROBIOS_BASE_URL:-https://raw.githubusercontent.com/Abdess/retrobios/}"
|
||||
# Seconds: the interface's time to write its result once asked to stop, the
|
||||
# wait for a button when the mapper is gone, the offline detection run.
|
||||
RB_STOP_GRACE="${RB_STOP_GRACE:-10}"
|
||||
RB_DEAD_WAIT="${RB_DEAD_WAIT:-10}"
|
||||
RB_DETECT_LIMIT="${RB_DETECT_LIMIT:-20}"
|
||||
RB_MAPPER_NEEDED="${RB_MAPPER_NEEDED:-1}"
|
||||
RB_RUNTIME="python_3.11"
|
||||
RB_RELEASES="github.com/Abdess/retrobios/releases/latest"
|
||||
RB_PC_ROUTE="On a computer, with this SD card mounted, the same installer fills the card: install.py --platform NAME --dest BIOS-FOLDER-ON-THE-CARD. Instructions: abdess.github.io/retrobios/wiki/installer"
|
||||
RB_PYTHON="${RB_PYTHON:-}"
|
||||
RB_PYTHONHOME="${RB_PYTHONHOME:-}"
|
||||
RB_PYTHON_PROBLEM="${RB_PYTHON_PROBLEM:-}"
|
||||
RB_MAPPER_PID="${RB_MAPPER_PID:-}"
|
||||
RB_STOP_PID="${RB_STOP_PID:-}"
|
||||
RB_STOP_TOKEN="${RB_STOP_TOKEN:-}"
|
||||
RB_STATE_PROBLEM=""
|
||||
RB_MOUNTED=""
|
||||
RB_MAPPER_PID=""
|
||||
RB_UI_PID=""
|
||||
RB_APP_PID=""
|
||||
RB_STOPPED=""
|
||||
RB_TTY=""
|
||||
RB_STTY=""
|
||||
RB_FONT_BACK=""
|
||||
RB_VT_BACK=""
|
||||
RB_ROUTE=""
|
||||
RB_ROUTE_FAILED=""
|
||||
RB_NAP_FD=""
|
||||
RB_KEY=""
|
||||
RB_TERMAPP=()
|
||||
RB_PYCMD=()
|
||||
|
||||
rb_arch() {
|
||||
uname -m
|
||||
}
|
||||
|
||||
rb_py() {
|
||||
rb_say() {
|
||||
printf '%s\n' "RetroBIOS: $*" >> "$RB_LOG" 2>/dev/null
|
||||
}
|
||||
|
||||
# The interpreter as one command, so it can be started in the background and
|
||||
# signalled itself: a function run with & is a subshell whose python child a
|
||||
# kill does not reach.
|
||||
rb_pycmd() {
|
||||
if [ -n "$RB_PYTHONHOME" ]; then
|
||||
PYTHONHOME="$RB_PYTHONHOME" "$RB_PYTHON" "$@"
|
||||
RB_PYCMD=(env "PYTHONHOME=$RB_PYTHONHOME" "$RB_PYTHON")
|
||||
else
|
||||
"$RB_PYTHON" "$@"
|
||||
RB_PYCMD=("$RB_PYTHON")
|
||||
fi
|
||||
}
|
||||
|
||||
rb_py() {
|
||||
rb_pycmd
|
||||
"${RB_PYCMD[@]}" "$@"
|
||||
}
|
||||
|
||||
# A pause without a child process, in fractions of a second, which busybox
|
||||
# sleep does not always take: a read that times out on a pipe this shell
|
||||
# holds both ends of.
|
||||
rb_nap() {
|
||||
if [ -z "$RB_NAP_FD" ]; then
|
||||
exec {RB_NAP_FD}<> <(:) || RB_NAP_FD="none"
|
||||
fi
|
||||
if [ "$RB_NAP_FD" = "none" ]; then
|
||||
sleep 1
|
||||
else
|
||||
read -r -t "$1" -u "$RB_NAP_FD" _
|
||||
fi
|
||||
return 0
|
||||
}
|
||||
|
||||
# Whether a process runs. /proc, not kill -0: the mapper may run as root
|
||||
# under sudo, and kill -0 on it fails for a user. A zombie is not running.
|
||||
rb_alive() {
|
||||
local stat
|
||||
[ -n "${1:-}" ] || return 1
|
||||
{ read -r stat < "/proc/$1/stat"; } 2>/dev/null || return 1
|
||||
stat="${stat##*) }"
|
||||
[ "${stat%% *}" != "Z" ]
|
||||
}
|
||||
|
||||
# Runs a command for at most $1 seconds: status 124 when it was stopped.
|
||||
rb_bounded() {
|
||||
local limit="$1" pid waited=0
|
||||
shift
|
||||
"$@" &
|
||||
pid=$!
|
||||
while rb_alive "$pid"; do
|
||||
if [ "$waited" -ge $((limit * 4)) ]; then
|
||||
kill "$pid" 2>/dev/null
|
||||
wait "$pid" 2>/dev/null
|
||||
return 124
|
||||
fi
|
||||
rb_nap 0.25
|
||||
waited=$((waited + 1))
|
||||
done
|
||||
wait "$pid"
|
||||
}
|
||||
|
||||
# --- State and log --------------------------------------------------------
|
||||
|
||||
rb_can_write() {
|
||||
mkdir -p "$1" 2>/dev/null || return 1
|
||||
{ printf 'x' > "$1/.probe"; } 2>/dev/null || return 1
|
||||
rm -f "$1/.probe"
|
||||
}
|
||||
|
||||
# The folder the run keeps its state in, and its log, started afresh with the
|
||||
# previous one kept as log.prev.txt. A read-only or full card moves both to
|
||||
# XDG_RUNTIME_DIR or /tmp: the interface then runs and says itself what it
|
||||
# cannot write. "fresh" starts the log, "keep" leaves it as the launcher
|
||||
# opened it.
|
||||
rb_open_log() {
|
||||
local spare="${XDG_RUNTIME_DIR:-/tmp}/retrobios" moved=""
|
||||
if ! rb_can_write "$RB_STATE" || ! { : >> "$RB_LOG"; } 2>/dev/null; then
|
||||
moved="$RB_DIR"
|
||||
RB_STATE="$spare/run"
|
||||
RB_LOG="$spare/log.txt"
|
||||
if ! rb_can_write "$RB_STATE"; then
|
||||
RB_STATE_PROBLEM="RetroBIOS cannot write to $moved or to $spare: the SD card may be read-only or full."
|
||||
return 1
|
||||
fi
|
||||
fi
|
||||
if [ "${1:-}" != "keep" ] || [ -n "$moved" ]; then
|
||||
if [ -s "$RB_LOG" ]; then
|
||||
mv -f "$RB_LOG" "${RB_LOG%.txt}.prev.txt" 2>/dev/null
|
||||
fi
|
||||
: > "$RB_LOG"
|
||||
fi
|
||||
if [ -n "$moved" ]; then
|
||||
rb_say "$moved cannot be written (read-only or full card): the log and the run's files are in $spare"
|
||||
fi
|
||||
return 0
|
||||
}
|
||||
|
||||
# --- Python ---------------------------------------------------------------
|
||||
|
||||
rb_python_ok() {
|
||||
@@ -144,20 +256,22 @@ rb_python_text() {
|
||||
|
||||
# Whether the file list can be reached. Only a failure to resolve or to
|
||||
# connect counts as offline: a certificate refused by an old curl still means
|
||||
# a network, and install.py names that problem itself.
|
||||
# a network, and install.py names that problem itself. Bounded as a whole:
|
||||
# GNU wget retries a timeout 20 times.
|
||||
rb_online() {
|
||||
local code
|
||||
if command -v curl >/dev/null 2>&1; then
|
||||
curl -sSI --max-time 8 "$RB_PROBE_URL" >/dev/null 2>&1
|
||||
rb_bounded 8 curl -sSI --connect-timeout 5 --max-time 7 "$RB_PROBE_URL" >/dev/null 2>&1
|
||||
code=$?
|
||||
case "$code" in
|
||||
5|6|7|28) return 1 ;;
|
||||
5|6|7|28|124) return 1 ;;
|
||||
*) return 0 ;;
|
||||
esac
|
||||
fi
|
||||
if command -v wget >/dev/null 2>&1; then
|
||||
wget -q --spider -T 8 "$RB_PROBE_URL" >/dev/null 2>&1
|
||||
[ $? -ne 4 ]
|
||||
rb_bounded 8 wget -q --spider -T 7 "$RB_PROBE_URL" >/dev/null 2>&1
|
||||
code=$?
|
||||
[ "$code" -ne 4 ] && [ "$code" -ne 124 ]
|
||||
return
|
||||
fi
|
||||
# Neither tool: a default route is the best guess there is.
|
||||
@@ -166,12 +280,18 @@ rb_online() {
|
||||
|
||||
# The system and BIOS folder install.py finds here, read without a network:
|
||||
# detection is local, and the run stops where the file list would download.
|
||||
# That download is sent to a loopback port nothing listens on, so it fails
|
||||
# at once instead of retrying three times 30 s on a network already found
|
||||
# missing.
|
||||
rb_detect() {
|
||||
RB_PLATFORM=""
|
||||
RB_PLATFORM_NAME=""
|
||||
RB_BIOS_DIR=""
|
||||
mkdir -p "$RB_STATE"
|
||||
rb_py "$RB_INSTALL" --check --events > "$RB_STATE/detect.jsonl" 2>> "$RB_LOG"
|
||||
rb_pycmd
|
||||
RETROBIOS_BASE_URL="http://127.0.0.1:9" rb_bounded "$RB_DETECT_LIMIT" \
|
||||
"${RB_PYCMD[@]}" "$RB_INSTALL" --check --events > "$RB_STATE/detect.jsonl" 2>> "$RB_LOG"
|
||||
[ $? = 124 ] && rb_say "the detection run took over $RB_DETECT_LIMIT s and was stopped"
|
||||
local kind a b c
|
||||
while IFS=$'\t' read -r kind a b c; do
|
||||
if [ "$kind" = "platform" ]; then
|
||||
@@ -269,44 +389,50 @@ rb_terminal_app() {
|
||||
fi
|
||||
}
|
||||
|
||||
# Sets RB_ROUTE to the first of RB_ROUTES that can show text: here, app,
|
||||
# vt, or none.
|
||||
rb_route() {
|
||||
local route
|
||||
RB_ROUTE="none"
|
||||
for route in $RB_ROUTES; do
|
||||
case "$route" in
|
||||
here)
|
||||
if [ -t 0 ] && [ -t 1 ]; then
|
||||
RB_ROUTE="here"
|
||||
return 0
|
||||
fi
|
||||
;;
|
||||
app)
|
||||
if rb_terminal_app; then
|
||||
RB_ROUTE="app"
|
||||
return 0
|
||||
fi
|
||||
;;
|
||||
vt)
|
||||
# A compositor or X server holds the console it runs on: text
|
||||
# written there is never seen, so the console needs a switch.
|
||||
if rb_graphical; then
|
||||
if [ -n "${RB_VT_SWITCH:-}" ] && command -v chvt >/dev/null 2>&1 \
|
||||
&& [ -n "$(rb_active_vt)" ]; then
|
||||
RB_ROUTE="vt"
|
||||
return 0
|
||||
fi
|
||||
elif [ -c "$RB_CONSOLE" ]; then
|
||||
RB_ROUTE="vt"
|
||||
return 0
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
done
|
||||
# A console a terminal can be read from. PortMaster's mod_muOS.txt sets
|
||||
# CUR_TTY to /dev/null, a character device that is no console at all. A
|
||||
# console this user cannot open yet (rb_vt_enter opens it up) is judged by
|
||||
# its name.
|
||||
rb_is_console() {
|
||||
[ -c "$1" ] || return 1
|
||||
if [ -r "$1" ] && [ -w "$1" ]; then
|
||||
{ [ -t 0 ]; } < "$1" 2>/dev/null
|
||||
return
|
||||
fi
|
||||
case "$1" in
|
||||
/dev/tty[0-9]*|/dev/console|/dev/pts/*) return 0 ;;
|
||||
esac
|
||||
return 1
|
||||
}
|
||||
|
||||
# Whether a route can show text here. A compositor or X server holds the
|
||||
# console it runs on: text written there is never seen, so the console needs
|
||||
# a switch.
|
||||
rb_route_ready() {
|
||||
case "$1" in
|
||||
here)
|
||||
[ -t 0 ] && [ -t 1 ]
|
||||
;;
|
||||
app)
|
||||
rb_terminal_app
|
||||
;;
|
||||
vt)
|
||||
if rb_graphical; then
|
||||
[ -n "${RB_VT_SWITCH:-}" ] && command -v chvt >/dev/null 2>&1 \
|
||||
&& [ -n "$(rb_active_vt)" ]
|
||||
elif rb_is_console "$RB_CONSOLE"; then
|
||||
return 0
|
||||
else
|
||||
rb_say "$RB_CONSOLE is not a console: no route on it"
|
||||
return 1
|
||||
fi
|
||||
;;
|
||||
*)
|
||||
return 1
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
# The largest Terminus font that keeps 56 columns and 20 rows, the way
|
||||
# dArkOS's own tools pick one per screen (Lat7-Terminus16 on 480x320,
|
||||
# TerminusBold22x11 on 640x480, 32x16 on 1024x768). Only changed when the
|
||||
@@ -368,12 +494,34 @@ rb_active_vt() {
|
||||
printf '%s\n' "$active"
|
||||
}
|
||||
|
||||
# chvt, and whether the console really came to the front. chvt waits for the
|
||||
# switch (VT_WAITACTIVE): a compositor that keeps the display would hold it
|
||||
# forever, so it gets 3 s.
|
||||
rb_chvt() {
|
||||
local pid waited=0
|
||||
$ESUDO chvt "$1" >> "$RB_LOG" 2>&1 &
|
||||
pid=$!
|
||||
while [ "$waited" -lt 12 ]; do
|
||||
[ "$(rb_active_vt)" = "$1" ] && return 0
|
||||
rb_nap 0.25
|
||||
waited=$((waited + 1))
|
||||
done
|
||||
$ESUDO kill "$pid" >/dev/null 2>&1
|
||||
return 1
|
||||
}
|
||||
|
||||
rb_vt_enter() {
|
||||
RB_TTY="$RB_CONSOLE"
|
||||
if rb_graphical && [ -n "${RB_VT_SWITCH:-}" ]; then
|
||||
RB_VT_BACK=$(rb_active_vt)
|
||||
if ! rb_chvt "$RB_VT_SWITCH"; then
|
||||
rb_say "console $RB_VT_SWITCH did not come to the front (console ${RB_VT_BACK:-?} still is)"
|
||||
[ -n "$RB_VT_BACK" ] && rb_chvt "$RB_VT_BACK"
|
||||
RB_TTY=""
|
||||
RB_VT_BACK=""
|
||||
return 1
|
||||
fi
|
||||
RB_TTY="${RB_VT_PREFIX:-/dev/tty}$RB_VT_SWITCH"
|
||||
$ESUDO chvt "$RB_VT_SWITCH" >> "$RB_LOG" 2>&1
|
||||
fi
|
||||
if [ ! -r "$RB_TTY" ] || [ ! -w "$RB_TTY" ]; then
|
||||
$ESUDO chmod 666 "$RB_TTY" >> "$RB_LOG" 2>&1
|
||||
@@ -391,32 +539,98 @@ rb_vt_leave() {
|
||||
stty "$RB_STTY" < "$RB_TTY" 2>/dev/null
|
||||
fi
|
||||
if [ -n "$RB_VT_BACK" ]; then
|
||||
$ESUDO chvt "$RB_VT_BACK" >> "$RB_LOG" 2>&1
|
||||
rb_chvt "$RB_VT_BACK"
|
||||
fi
|
||||
RB_TTY=""
|
||||
RB_STTY=""
|
||||
RB_VT_BACK=""
|
||||
}
|
||||
|
||||
# gptokeyb on a name only this installer's processes carry: kill mode runs
|
||||
# pkill -f on it, and the path of install.py is in the command line of the
|
||||
# interface and of every run it starts, never in python3's alone.
|
||||
# --- Controller and Start + Select ----------------------------------------
|
||||
|
||||
# Start + Select is the mapper's kill mode: gptokeyb runs pkill -f on its
|
||||
# target, then pkill -9 3 s later, and gptokeyb2 -X runs kill -9 at once.
|
||||
# Either would reach the interface's engine too, whose command line carries
|
||||
# the installer's path, and SIGKILL leaves no time to say what an install
|
||||
# wrote. So the target is a sentinel, a process this launcher owns whose
|
||||
# command line alone carries the token: when it dies, the launcher asks the
|
||||
# interface to stop, and the interface gives a running install its 2 s.
|
||||
rb_stop_start() {
|
||||
RB_STOP_TOKEN="retrobios-stop-${$}x"
|
||||
bash -c 'exec {f}<> <(:); while :; do read -r -t 3600 -u "$f" _; done' \
|
||||
"$RB_STOP_TOKEN" > /dev/null 2>&1 &
|
||||
RB_STOP_PID=$!
|
||||
}
|
||||
|
||||
rb_stop_requested() {
|
||||
[ -n "$RB_STOP_PID" ] && ! rb_alive "$RB_STOP_PID"
|
||||
}
|
||||
|
||||
rb_stop_end() {
|
||||
[ -n "$RB_STOP_PID" ] || return 0
|
||||
kill "$RB_STOP_PID" >/dev/null 2>&1
|
||||
RB_STOP_PID=""
|
||||
}
|
||||
|
||||
# The mapper, checked half a second after it starts: where it is the only
|
||||
# input, a mapper that exits at once leaves an interface nothing can move.
|
||||
rb_mapper_start() {
|
||||
[ -n "${RB_MAPPER:-}" ] || return 0
|
||||
[ -n "$RB_MAPPER_PID" ] && return 0
|
||||
rb_stop_start
|
||||
# shellcheck disable=SC2086 # RB_MAPPER is a command and its flags, as $GPTOKEYB is
|
||||
$RB_MAPPER "$RB_INSTALL" -c "$RB_GPTK" >> "$RB_LOG" 2>&1 &
|
||||
$RB_MAPPER "$RB_STOP_TOKEN" -c "$RB_GPTK" >> "$RB_LOG" 2>&1 &
|
||||
RB_MAPPER_PID=$!
|
||||
rb_nap 0.5
|
||||
if ! rb_alive "$RB_MAPPER_PID"; then
|
||||
rb_say "the controller mapper ($RB_MAPPER) stopped as soon as it started"
|
||||
fi
|
||||
}
|
||||
|
||||
# The mapper was started, is needed, and is no longer running.
|
||||
rb_mapper_gone() {
|
||||
[ -n "${RB_MAPPER:-}" ] && [ -n "$RB_MAPPER_PID" ] && [ "$RB_MAPPER_NEEDED" != "0" ] \
|
||||
&& ! rb_alive "$RB_MAPPER_PID"
|
||||
}
|
||||
|
||||
rb_mapper_stop() {
|
||||
[ -n "$RB_MAPPER_PID" ] || return 0
|
||||
$ESUDO kill "$RB_MAPPER_PID" >/dev/null 2>&1
|
||||
wait "$RB_MAPPER_PID" 2>/dev/null
|
||||
if [ -n "$RB_MAPPER_PID" ]; then
|
||||
$ESUDO kill "$RB_MAPPER_PID" >/dev/null 2>&1
|
||||
wait "$RB_MAPPER_PID" 2>/dev/null
|
||||
fi
|
||||
RB_MAPPER_PID=""
|
||||
rb_stop_end
|
||||
}
|
||||
|
||||
# A screen of text on the current terminal, closed by any button.
|
||||
rb_message() {
|
||||
rb_mapper_text() {
|
||||
printf '%s\n' "The controller cannot be read: gptokeyb, which turns the buttons into keys, stopped as soon as it started. What it said is in $RB_LOG. $RB_PC_ROUTE"
|
||||
}
|
||||
|
||||
# --- Messages -------------------------------------------------------------
|
||||
|
||||
# One key into RB_KEY. False when none came: the wait ran out, Start + Select
|
||||
# was pressed, or the mapper is gone and only a keyboard is left, which gets
|
||||
# RB_DEAD_WAIT seconds.
|
||||
rb_read_key() {
|
||||
local limit="${RB_MESSAGE_WAIT:-300}" waited=0 code
|
||||
RB_KEY=""
|
||||
while [ "$waited" -lt "$limit" ]; do
|
||||
rb_stop_requested && return 1
|
||||
if rb_mapper_gone && [ "$limit" -gt "$RB_DEAD_WAIT" ]; then
|
||||
limit="$RB_DEAD_WAIT"
|
||||
[ "$waited" -lt "$limit" ] || return 1
|
||||
fi
|
||||
IFS= read -r -s -n 1 -t 1 RB_KEY
|
||||
code=$?
|
||||
[ "$code" = 0 ] && return 0
|
||||
# The end of the input, not a timeout.
|
||||
[ "$code" -gt 128 ] || return 1
|
||||
waited=$((waited + 1))
|
||||
done
|
||||
return 1
|
||||
}
|
||||
|
||||
rb_screen() {
|
||||
local cols
|
||||
cols=$(stty size 2>/dev/null | awk '{print $2}')
|
||||
case "$cols" in
|
||||
@@ -426,53 +640,116 @@ rb_message() {
|
||||
printf '\033[2J\033[H'
|
||||
printf 'RetroBIOS\n\n'
|
||||
printf '%s\n' "$1" | fold -s -w $((cols - 1))
|
||||
printf '\n%s\n' "Press A or Enter to close."
|
||||
read -r -s -n 1 -t "${RB_MESSAGE_WAIT:-300}" _
|
||||
printf '\n%s\n' "$2"
|
||||
}
|
||||
|
||||
# A screen of text on the current terminal, closed by any button.
|
||||
rb_message() {
|
||||
rb_screen "$1" "Press A or Enter to close."
|
||||
rb_read_key
|
||||
printf '\033[2J\033[H'
|
||||
}
|
||||
|
||||
# A question on the current terminal: true for A or Enter, false for B, Esc,
|
||||
# any other button or no answer.
|
||||
rb_ask() {
|
||||
local cols key
|
||||
cols=$(stty size 2>/dev/null | awk '{print $2}')
|
||||
case "$cols" in
|
||||
''|*[!0-9]*) cols=40 ;;
|
||||
esac
|
||||
[ "$cols" -ge 20 ] || cols=40
|
||||
printf '\033[2J\033[H'
|
||||
printf 'RetroBIOS\n\n'
|
||||
printf '%s\n' "$1" | fold -s -w $((cols - 1))
|
||||
printf '\n%s\n' "A or Enter: yes. B or Esc: no."
|
||||
if ! IFS= read -r -s -n 1 -t "${RB_MESSAGE_WAIT:-300}" key; then
|
||||
rb_screen "$1" "A or Enter: yes. B or Esc: no."
|
||||
if ! rb_read_key; then
|
||||
printf '\033[2J\033[H'
|
||||
return 1
|
||||
fi
|
||||
# The rest of an escape sequence (an arrow) is not a second answer.
|
||||
read -r -s -t 0.05 -n 8 _ 2>/dev/null
|
||||
printf '\033[2J\033[H'
|
||||
[ -z "$key" ] || [ "$key" = " " ]
|
||||
[ -z "$RB_KEY" ] || [ "$RB_KEY" = " " ]
|
||||
}
|
||||
|
||||
# --- The run --------------------------------------------------------------
|
||||
|
||||
# The interface itself, watched: Start + Select or a mapper gone (nothing
|
||||
# moves it any more) asks it to stop with SIGTERM, then SIGKILL after
|
||||
# RB_STOP_GRACE seconds. Its stdin is named, since a background command in a
|
||||
# script reads /dev/null otherwise.
|
||||
rb_interface() {
|
||||
local status naps=0
|
||||
rb_pycmd
|
||||
# A Ctrl-C typed while the terminal still sends signals reaches the
|
||||
# interface on its own: this shell keeps watching.
|
||||
trap ':' INT
|
||||
"${RB_PYCMD[@]}" "$RB_INSTALL" --tui "$@" <&0 2> "$RB_STATE/tui.err" &
|
||||
RB_UI_PID=$!
|
||||
while rb_alive "$RB_UI_PID"; do
|
||||
if [ -z "$RB_STOPPED" ]; then
|
||||
if rb_stop_requested; then
|
||||
RB_STOPPED="start_select"
|
||||
rb_say "Start + Select: the interface is asked to stop"
|
||||
elif rb_mapper_gone; then
|
||||
RB_STOPPED="mapper"
|
||||
rb_say "the controller mapper stopped: the interface is asked to stop"
|
||||
fi
|
||||
[ -n "$RB_STOPPED" ] && kill -TERM "$RB_UI_PID" 2>/dev/null
|
||||
else
|
||||
naps=$((naps + 1))
|
||||
if [ "$naps" -ge $((RB_STOP_GRACE * 4)) ]; then
|
||||
rb_say "the interface did not stop within $RB_STOP_GRACE s"
|
||||
kill -KILL "$RB_UI_PID" 2>/dev/null
|
||||
rb_kill_engines
|
||||
fi
|
||||
fi
|
||||
rb_nap 0.25
|
||||
done
|
||||
wait "$RB_UI_PID"
|
||||
status=$?
|
||||
trap - INT
|
||||
RB_UI_PID=""
|
||||
return "$status"
|
||||
}
|
||||
|
||||
# An engine the interface started survives it when the interface is killed:
|
||||
# it runs in its own session.
|
||||
rb_kill_engines() {
|
||||
command -v pkill >/dev/null 2>&1 || return 0
|
||||
pkill -KILL -f "$RB_INSTALL" >/dev/null 2>&1
|
||||
}
|
||||
|
||||
rb_ui_stop() {
|
||||
local naps=0
|
||||
rb_alive "$RB_UI_PID" || return 0
|
||||
kill -TERM "$RB_UI_PID" 2>/dev/null
|
||||
while rb_alive "$RB_UI_PID" && [ "$naps" -lt $((RB_STOP_GRACE * 4)) ]; do
|
||||
rb_nap 0.25
|
||||
naps=$((naps + 1))
|
||||
done
|
||||
if rb_alive "$RB_UI_PID"; then
|
||||
kill -KILL "$RB_UI_PID" 2>/dev/null
|
||||
rb_kill_engines
|
||||
fi
|
||||
RB_UI_PID=""
|
||||
}
|
||||
|
||||
# The interface on the current terminal: install.py --tui, from a pack on the
|
||||
# card when there is no network. The status also goes to RB_STATE/status,
|
||||
# since a terminal app does not always pass its child's on.
|
||||
rb_here() {
|
||||
local status pack args=()
|
||||
local status pack args=() other
|
||||
mkdir -p "$RB_STATE"
|
||||
rm -f "$RB_STATE/status"
|
||||
case "${TERM:-}" in
|
||||
''|dumb|unknown) export TERM=xterm ;;
|
||||
esac
|
||||
if rb_mapper_gone; then
|
||||
rb_message "$(rb_mapper_text)"
|
||||
printf '1\n' > "$RB_STATE/status"
|
||||
return 1
|
||||
fi
|
||||
if [ -z "$RB_PYTHON" ]; then
|
||||
rb_message "$(rb_python_text)"
|
||||
printf '1\n' > "$RB_STATE/status"
|
||||
return 1
|
||||
fi
|
||||
local other
|
||||
printf '\033[2J\033[HRetroBIOS\n\nChecking the connection...\n'
|
||||
if ! rb_online; then
|
||||
printf 'No connection. Looking for the BIOS folder and a pack on the card...\n'
|
||||
# Detection also names the BIOS folder the other card must not be.
|
||||
rb_detect
|
||||
other=$(rb_other_card)
|
||||
@@ -480,6 +757,10 @@ rb_here() {
|
||||
args=(--from "$other")
|
||||
fi
|
||||
pack=$(rb_find_pack)
|
||||
if rb_stop_requested; then
|
||||
printf '143\n' > "$RB_STATE/status"
|
||||
return 143
|
||||
fi
|
||||
if [ -z "$pack" ]; then
|
||||
if [ "${#args[@]}" -gt 0 ]; then
|
||||
# --from needs the list of files, which comes from the network or a pack.
|
||||
@@ -495,7 +776,7 @@ rb_here() {
|
||||
args+=(--platform "$RB_PLATFORM")
|
||||
fi
|
||||
fi
|
||||
rb_py "$RB_INSTALL" --tui "${args[@]}" "$@" 2> "$RB_STATE/tui.err"
|
||||
rb_interface "${args[@]}" "$@"
|
||||
status=$?
|
||||
cat "$RB_STATE/tui.err" >> "$RB_LOG" 2>/dev/null
|
||||
if [ "$status" = "2" ] && grep -q -- "--tui cannot open" "$RB_STATE/tui.err"; then
|
||||
@@ -503,59 +784,117 @@ rb_here() {
|
||||
if [ "${RB_FALLBACK:-0}" != "1" ]; then
|
||||
rb_message "$(tail -n 1 "$RB_STATE/tui.err") The messages are in $RB_LOG."
|
||||
fi
|
||||
elif [ -n "$RB_STOPPED" ] || [ "$status" -ge 128 ]; then
|
||||
# Ended by Start + Select or a signal: the player asked to leave, and no
|
||||
# button may be left to close a message.
|
||||
:
|
||||
elif [ "$status" != "0" ] && [ -s "$RB_STATE/tui.err" ]; then
|
||||
rb_message "$(tail -n 1 "$RB_STATE/tui.err") The messages are in $RB_LOG."
|
||||
# The first line of the farewell: the error screen it closes was read.
|
||||
rb_message "$(head -n 1 "$RB_STATE/tui.err") The messages are in $RB_LOG."
|
||||
fi
|
||||
printf '%s\n' "$status" > "$RB_STATE/status"
|
||||
return "$status"
|
||||
}
|
||||
|
||||
rb_cleanup() {
|
||||
rb_ui_stop
|
||||
if rb_alive "$RB_APP_PID"; then
|
||||
kill -TERM "$RB_APP_PID" 2>/dev/null
|
||||
wait "$RB_APP_PID" 2>/dev/null
|
||||
fi
|
||||
RB_APP_PID=""
|
||||
rb_vt_leave
|
||||
rb_mapper_stop
|
||||
}
|
||||
|
||||
# The whole session: the terminal, the mapper, the interface, then the
|
||||
# console, its font and the mapper put back. Returns the interface's status,
|
||||
# 125 when no terminal could show it.
|
||||
rb_launch() {
|
||||
local status
|
||||
mkdir -p "$RB_STATE"
|
||||
if ! rb_route; then
|
||||
printf '%s\n' "RetroBIOS: no terminal to show the interface on (routes: $RB_ROUTES)" >> "$RB_LOG"
|
||||
return 125
|
||||
fi
|
||||
export RB_DIR RB_INSTALL RB_EVENTS RB_STATE RB_LOG RB_PACK_PATH RB_FALLBACK
|
||||
export RB_OTHER_CARDS RB_BIOS_DEST
|
||||
export RB_PYTHON RB_PYTHONHOME RB_PYTHON_PROBLEM RB_MESSAGE_WAIT RB_ROUTE_TABLE
|
||||
trap 'rb_cleanup; exit 143' TERM HUP
|
||||
rb_mapper_start
|
||||
# One route: sets RB_ROUTE_FAILED when it could not show the interface, so
|
||||
# the next route is tried.
|
||||
rb_route_run() {
|
||||
local status code
|
||||
RB_ROUTE_FAILED=""
|
||||
case "$RB_ROUTE" in
|
||||
here)
|
||||
rb_here "$@"
|
||||
status=$?
|
||||
;;
|
||||
app)
|
||||
"${RB_TERMAPP[@]}" bash "$RB_LIB" here "$@" 2>> "$RB_LOG"
|
||||
status=$(cat "$RB_STATE/status" 2>/dev/null)
|
||||
rm -f "$RB_STATE/status"
|
||||
"${RB_TERMAPP[@]}" bash "$RB_LIB" here "$@" 2>> "$RB_LOG" &
|
||||
RB_APP_PID=$!
|
||||
wait "$RB_APP_PID"
|
||||
code=$?
|
||||
RB_APP_PID=""
|
||||
if [ -f "$RB_STATE/status" ]; then
|
||||
status=$(cat "$RB_STATE/status")
|
||||
else
|
||||
# foot or xterm refused to open (no display, no XAUTHORITY): the
|
||||
# interface never ran.
|
||||
rb_say "${RB_TERMAPP[0]} ended with status $code before the interface ran"
|
||||
RB_ROUTE_FAILED=1
|
||||
return 1
|
||||
fi
|
||||
;;
|
||||
vt)
|
||||
rb_vt_enter
|
||||
if ! rb_vt_enter; then
|
||||
RB_ROUTE_FAILED=1
|
||||
return 1
|
||||
fi
|
||||
export TERM=linux
|
||||
# shellcheck disable=SC2094 # a console is read and written at once by design
|
||||
rb_here "$@" < "$RB_TTY" > "$RB_TTY" 2>> "$RB_LOG"
|
||||
status=$?
|
||||
rb_vt_leave
|
||||
;;
|
||||
esac
|
||||
return "${status:-1}"
|
||||
}
|
||||
|
||||
# The whole session: the terminal, the mapper, the interface, then the
|
||||
# console, its font and the mapper put back. Returns the interface's status,
|
||||
# 125 when no terminal could show it.
|
||||
rb_launch() {
|
||||
local status="" route
|
||||
RB_STOPPED=""
|
||||
if [ -n "$RB_STATE_PROBLEM" ]; then
|
||||
printf '%s\n' "$RB_STATE_PROBLEM" >&2
|
||||
if rb_is_console "$RB_CONSOLE"; then
|
||||
printf '\r\n%s\r\n' "$RB_STATE_PROBLEM" > "$RB_CONSOLE" 2>/dev/null
|
||||
fi
|
||||
return 1
|
||||
fi
|
||||
mkdir -p "$RB_STATE"
|
||||
export RB_DIR RB_INSTALL RB_EVENTS RB_STATE RB_LOG RB_PACK_PATH RB_FALLBACK
|
||||
export RB_OTHER_CARDS RB_BIOS_DEST RB_STOP_GRACE RB_DEAD_WAIT RB_DETECT_LIMIT
|
||||
export RB_PYTHON RB_PYTHONHOME RB_PYTHON_PROBLEM RB_MESSAGE_WAIT RB_ROUTE_TABLE
|
||||
trap 'rb_cleanup; exit 143' TERM HUP
|
||||
for route in $RB_ROUTES; do
|
||||
rb_route_ready "$route" || continue
|
||||
rb_mapper_start
|
||||
export RB_MAPPER RB_MAPPER_PID RB_MAPPER_NEEDED RB_STOP_PID
|
||||
RB_ROUTE="$route"
|
||||
rb_say "showing the interface by the $route route"
|
||||
rb_route_run "$@"
|
||||
status=$?
|
||||
[ -z "$RB_ROUTE_FAILED" ] && break
|
||||
status=""
|
||||
done
|
||||
rb_cleanup
|
||||
trap - TERM HUP
|
||||
return "${status:-1}"
|
||||
if [ -z "$status" ]; then
|
||||
RB_ROUTE="none"
|
||||
rb_say "no terminal to show the interface on (routes: $RB_ROUTES)"
|
||||
return 125
|
||||
fi
|
||||
return "$status"
|
||||
}
|
||||
|
||||
if [ "${BASH_SOURCE[0]}" = "$0" ]; then
|
||||
case "${1:-}" in
|
||||
here)
|
||||
shift
|
||||
# The terminal app closed or the launcher signalled: the interface
|
||||
# gets its time to stop.
|
||||
trap 'rb_ui_stop; exit 143' TERM HUP
|
||||
rb_here "$@"
|
||||
exit $?
|
||||
;;
|
||||
|
||||
@@ -13,7 +13,8 @@ the missing ones: downloaded with a network connection, or from a pack copied
|
||||
into the `retrobios` folder without one.
|
||||
|
||||
Controls: D-pad to move, A or Start to choose, B or Select to go back, X to
|
||||
tick a system, Y for the options, L1 and R1 to page, Start + Select to quit.
|
||||
tick a system, Y for the options, L1 and R1 to page. To quit, hold Select, then press Start: Start pressed
|
||||
first would also choose the line in focus.
|
||||
|
||||
ArkOS has Python 3.7, older than the installer needs: RetroBIOS uses the
|
||||
Python runtime the RetroBIOS PortMaster port downloads, so start that port
|
||||
|
||||
@@ -15,6 +15,8 @@ ROOT="${RB_ROOT:-}"
|
||||
RB_DIR="$HERE/retrobios"
|
||||
RB_LOG="$RB_DIR/log.txt"
|
||||
ESUDO=""
|
||||
MAPPER_SUDO=""
|
||||
kill_flag="-1"
|
||||
OS_NAME=""
|
||||
if [ -f "$ROOT/etc/os-release" ]; then
|
||||
OS_NAME=$(awk -F= '$1 == "NAME" { gsub(/"/, "", $2); print $2 }' "$ROOT/etc/os-release")
|
||||
@@ -49,6 +51,10 @@ if [ -d "$ROOT/opt/system" ] && [ -d "$ROOT/opt/inttools" ]; then
|
||||
mapper=$(rb_gptokeyb "$ROOT/opt/inttools/gptokeyb" "$controlfolder/gptokeyb")
|
||||
[ -f "$ROOT/opt/inttools/gamecontrollerdb.txt" ] \
|
||||
&& export SDL_GAMECONTROLLERCONFIG_FILE="$ROOT/opt/inttools/gamecontrollerdb.txt"
|
||||
# sudo's env_reset drops the controller list exported above, and a pad
|
||||
# SDL does not know then sends nothing: PortMaster keeps it the same way
|
||||
# on ArkOS (control.txt, ESUDO).
|
||||
[ -n "$ESUDO" ] && MAPPER_SUDO="sudo --preserve-env=SDL_GAMECONTROLLERCONFIG_FILE"
|
||||
elif [ "$OS_NAME" = "AmberELEC" ]; then
|
||||
RB_ROUTES="vt"
|
||||
RB_CONSOLE="${RB_CONSOLE:-/dev/console}"
|
||||
@@ -65,15 +71,29 @@ else
|
||||
RB_PACK_PATH="$RB_DIR:$HERE:/storage/roms"
|
||||
controlfolder="/storage/roms/ports/PortMaster"
|
||||
mapper=$(rb_gptokeyb "$ROOT/usr/bin/gptokeyb" "$controlfolder/gptokeyb")
|
||||
# ROCKNIX's own tools load the pad's controller list first (fex-emu's
|
||||
# Start FEXConfig.sh: control-gen_init.sh, control.ini, get_controls), and
|
||||
# start the GPTOKEYB it names. control.ini also sets ESUDO and
|
||||
# controlfolder, which this launcher keeps as they were.
|
||||
control="$ROOT/storage/.config/gptokeyb/control.ini"
|
||||
command -v control-gen_init.sh >/dev/null 2>&1 && control-gen_init.sh >/dev/null 2>&1
|
||||
if [ -f "$control" ]; then
|
||||
saved_esudo="$ESUDO" saved_folder="$controlfolder"
|
||||
# shellcheck source=/dev/null
|
||||
source "$control"
|
||||
declare -F get_controls >/dev/null && get_controls
|
||||
# It carries its own kill flag (-sudokill).
|
||||
[ -n "${GPTOKEYB:-}" ] && mapper="$GPTOKEYB" kill_flag=""
|
||||
ESUDO="$saved_esudo" controlfolder="$saved_folder"
|
||||
fi
|
||||
fi
|
||||
# Kill mode on the installer's own path: Start + Select closes the
|
||||
# interface, never another program.
|
||||
# Kill mode: Start + Select closes the interface, never another program.
|
||||
RB_MAPPER=""
|
||||
[ -n "$mapper" ] && RB_MAPPER="$ESUDO $mapper -1"
|
||||
[ -n "$mapper" ] && RB_MAPPER="${MAPPER_SUDO:-$ESUDO} $mapper $kill_flag"
|
||||
|
||||
# shellcheck source=../shared/tui.txt
|
||||
source "$RB_DIR/tui.txt"
|
||||
: > "$RB_LOG"
|
||||
rb_open_log fresh
|
||||
rb_select_python
|
||||
# A launcher's own arguments are not install.py's (muOS passes the app folder).
|
||||
# shellcheck disable=SC2119
|
||||
|
||||
@@ -103,7 +103,6 @@ def archive_members(name: str, root: Path = REPO_ROOT) -> list[tuple[str, Path]]
|
||||
source = ports / "muos"
|
||||
members = [
|
||||
("RetroBIOS/mux_launch.sh", source / "mux_launch.sh"),
|
||||
("RetroBIOS/muxterm.gptk", source / "muxterm.gptk"),
|
||||
("RetroBIOS/README.md", source / "README.md"),
|
||||
*_engine("RetroBIOS", root),
|
||||
]
|
||||
|
||||
File diff suppressed because it is too large.
Load diff
Reference in new issue
Block a user