feat: tell the interface which pad route runs

Every launcher exports RETROBIOS_PAD: gptokeyb, muxterm, evmapy or
miyoo, so the interface can name the buttons that reach it. The wiki
lists the values, and the tests check each launcher's.
This commit is contained in:
Abdessamad Derraz committed 2026-10-23 18:58:37 +02:00
1 parent 41efb3e99f
commit c8c2dcd1a3
5 files changed
+54 -2

No files matched your search

+1
View File
@@ -16,6 +16,7 @@ RB_ROUTES="app vt"
RB_VT_SWITCH="${RB_VT_SWITCH:-4}"
RB_PACK_PATH="$RB_DIR:$HERE:/userdata"
RB_LOG="$RB_DIR/log.txt"
RB_PAD="evmapy"
# The X11 builds start EmulationStation on display :0, which a script run
# from it does not always inherit, with the cookie S31emulationstation
+1
View File
@@ -55,6 +55,7 @@ if [ -x "$MUXTERM" ]; then
else
# No gptokeyb2: muxterm's own buttons are all there is.
RB_TERMAPP=("$MUXTERM" --gl --)
RB_PAD="muxterm"
fi
else
RB_ROUTES="vt"
+12
View File
@@ -30,6 +30,10 @@
# RB_BIOS_DEST the one of them the firmware reads, never offered as the
# other card
# RB_FALLBACK 1 when the launcher has screens of its own to fall back to
# RB_PAD the input route, exported to the interface as
# RETROBIOS_PAD: gptokeyb (retrobios.gptk, the default when
# RB_MAPPER is set), muxterm (muxterm's own keys), evmapy
# (RetroBIOS.sh.keys), miyoo (buttons already keys)
# ESUDO sudo where the launcher needs it, as PortMaster sets it
RB_LIB="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/$(basename "${BASH_SOURCE[0]}")"
@@ -55,6 +59,7 @@ 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_PAD="${RB_PAD:-}"
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"
@@ -893,6 +898,13 @@ rb_launch() {
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
# The interface names the buttons of this route in its hints.
if [ -z "$RB_PAD" ] && [ -n "${RB_MAPPER:-}" ]; then
RB_PAD="gptokeyb"
fi
if [ -n "$RB_PAD" ]; then
export RETROBIOS_PAD="$RB_PAD"
fi
trap 'rb_cleanup; exit 143' TERM HUP
for route in $RB_ROUTES; do
rb_route_ready "$route" || continue
+28 -2
View File
@@ -308,7 +308,8 @@ with open(calls, "a") as fh:
fh.write(json.dumps({"argv0": sys.argv[0], "args": sys.argv[1:], "stdin": tty(0),
"stdout": tty(1), "term": os.environ.get("TERM", ""),
"pythonhome": os.environ.get("PYTHONHOME", ""),
"base_url": os.environ.get("RETROBIOS_BASE_URL", "")}) + "\n")
"base_url": os.environ.get("RETROBIOS_BASE_URL", ""),
"pad": os.environ.get("RETROBIOS_PAD")}) + "\n")
if "--check" in sys.argv:
if os.environ.get("FAKE_DETECT_HANG"):
time.sleep(60)
@@ -725,6 +726,7 @@ class ThePortMasterPort(unittest.TestCase):
self.assertEqual(call["term"], "linux")
installer = str(self.gamedir / "install.py")
self.assertEqual(call["argv0"], installer)
self.assertEqual(call["pad"], "gptokeyb")
# Kill mode on the Start + Select sentinel, never on the installer's
# path, which the engine's command line carries too.
args = device.mapper_args()
@@ -955,6 +957,7 @@ class ThePortMasterPort(unittest.TestCase):
self.assertEqual(device.tools(), [f"muxterm --gl -- bash {self.gamedir}/tui.txt here"])
self.assertEqual(device.lines("termapp.sdl"), ["0x0001/0x0001"])
self.assertEqual(device.mapper_args()[2:], ["-c", str(self.gamedir / "retrobios.gptk")])
self.assertEqual(call["pad"], "gptokeyb")
self.assertEqual(self.dialog(device), ["FINISH"])
def test_on_muos_without_muxterm_the_log_says_why_the_screens_open(self):
@@ -1121,6 +1124,7 @@ class TheBatoceraPort(unittest.TestCase):
[f"xterm -fullscreen -e bash {ports}/retrobios/tui.txt here"])
# evmapy reads RetroBIOS.sh.keys: no gptokeyb is started.
self.assertEqual(device.mapper_args(), [])
self.assertEqual(call["pad"], "evmapy")
def test_the_x_cookie_goes_with_the_display(self):
"""S31emulationstation starts X with XAUTHORITY=/var/lib/.Xauthority:
@@ -1220,13 +1224,15 @@ class TheMuosApp(unittest.TestCase):
self.assertRegex(args[1], r"^retrobios-stop-\d+x$")
self.assertEqual(args[2:], ["-c", str(app / "retrobios.gptk")])
self.assertEqual(read_gptk(app / "retrobios.gptk")["y"], ["tab"])
self.assertEqual(call["pad"], "gptokeyb")
device.assert_mapper_stopped()
def test_muxterm_alone_without_gptokeyb2(self):
device, app = self.device(muxterm=True, gptokeyb2=False)
proc = device.run([str(app / "mux_launch.sh"), str(app)])
self.assertEqual(proc.returncode, 0, proc.stderr.decode())
self.assertEqual(len(device.tui_calls()), 1)
[call] = device.tui_calls()
self.assertEqual(call["pad"], "muxterm")
# muxterm then reads the pad itself.
self.assertEqual(device.lines("termapp.sdl"), [""])
self.assertEqual(device.mapper_args(), [])
@@ -1369,6 +1375,7 @@ class TheToolsEntry(unittest.TestCase):
self.assertEqual(proc.returncode, 0, proc.stderr.decode())
[call] = device.tui_calls()
self.assertEqual((call["stdin"], call["term"]), (device.console, "linux"))
self.assertEqual(call["pad"], "gptokeyb")
args = device.mapper_args()
self.assertEqual(args[0], "-1")
self.assertRegex(args[1], r"^retrobios-stop-\d+x$")
@@ -1376,6 +1383,15 @@ class TheToolsEntry(unittest.TestCase):
device.assert_mapper_stopped()
device.assert_console_restored()
def test_the_previous_log_is_kept(self):
device, tools = self.device()
self.arkos(device)
log = tools / "retrobios" / "log.txt"
log.write_text("the failure\n")
device.run(["bash", str(tools / "RetroBIOS.sh")], RB_CONSOLE=device.console)
self.assertEqual((tools / "retrobios" / "log.prev.txt").read_text(), "the failure\n")
self.assertNotIn("the failure", log.read_text())
def test_arkos_sudo_keeps_the_controller_list(self):
"""sudo's env_reset would drop SDL_GAMECONTROLLERCONFIG_FILE, and a
pad SDL does not know sends nothing."""
@@ -1447,6 +1463,7 @@ class TheToolsEntry(unittest.TestCase):
[call] = device.tui_calls()
self.assertEqual((call["stdin"], call["stdout"]), (device.console, device.console))
self.assertEqual(call["term"], "xterm")
self.assertEqual(call["pad"], "gptokeyb")
# The terminal app owns its window: no console reset, no font.
self.assertNotIn("\x1bc", device.screen)
self.assertEqual([t for t in device.tools() if t.startswith("setfont")], [])
@@ -1526,6 +1543,15 @@ class TheDocs(unittest.TestCase):
text = (REPO_ROOT / "wiki" / "installer.md").read_text(encoding="utf-8")
self.assertIn("The releases carry no pack for ROCKNIX,\nKnulli or AmberELEC", text)
def test_every_pad_value_is_documented(self):
"""RETROBIOS_PAD is a contract the interface reads: each value tui.txt
names has its row in the handheld section."""
text = (REPO_ROOT / "wiki" / "installer.md").read_text(encoding="utf-8")
tui = (PORTS / "shared" / "tui.txt").read_text()
for value in ("gptokeyb", "muxterm", "evmapy", "miyoo"):
self.assertIn(f"| `{value}` |", text)
self.assertIn(value, tui)
def test_muos_paths_are_the_card_muos_mounts(self):
"""muOS mounts the first card at /mnt/mmc (MustardOS internal
mux_launch.sh, GET_VAR storage/rom/mount)."""
+12
View File
@@ -1013,6 +1013,18 @@ running install is then stopped the way B stops it, and the last screen says
what it wrote. On muOS without PortMaster, muxterm reads the pad itself: Y
ticks a system, Select shows its keyboard, and Menu quits.
Each launcher tells the interface which buttons reach it, in
`RETROBIOS_PAD`, so the hints can name them:
| Value | Input | Set by |
|-------|-------|--------|
| `gptokeyb` | gptokeyb or gptokeyb2 with `retrobios.gptk`, the table above | the PortMaster port, the Tools entry, the muOS app with PortMaster |
| `muxterm` | muxterm's own keys: D-pad, Start as Enter, B as Backspace, Y as Space | the muOS app without PortMaster |
| `evmapy` | `RetroBIOS.sh.keys`, the same buttons as the table | the Batocera and Knulli port |
| `miyoo` | buttons that are already keys, A as Space and B as Left Ctrl | no launcher yet |
A launcher with no mapper at all (a keyboard) leaves it unset.
**Without Wi-Fi.** On a computer, download the BIOS pack that serves your
system (every part, for a pack in parts): RetroArch's on muOS, ArkOS,
dArkOS and JELOS, Batocera's on Batocera. Copy it next to RetroBIOS: into