diff --git a/ports/batocera/RetroBIOS.sh b/ports/batocera/RetroBIOS.sh index b296b640..5776c288 100755 --- a/ports/batocera/RetroBIOS.sh +++ b/ports/batocera/RetroBIOS.sh @@ -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 diff --git a/ports/muos/mux_launch.sh b/ports/muos/mux_launch.sh index f0706c14..1cdcb98a 100755 --- a/ports/muos/mux_launch.sh +++ b/ports/muos/mux_launch.sh @@ -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" diff --git a/ports/shared/tui.txt b/ports/shared/tui.txt index ecb73063..bccb30c6 100644 --- a/ports/shared/tui.txt +++ b/ports/shared/tui.txt @@ -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 diff --git a/tests/test_handheld_launchers.py b/tests/test_handheld_launchers.py index 9475bad8..e20c8700 100644 --- a/tests/test_handheld_launchers.py +++ b/tests/test_handheld_launchers.py @@ -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).""" diff --git a/wiki/installer.md b/wiki/installer.md index e9ae8128..417b9316 100644 --- a/wiki/installer.md +++ b/wiki/installer.md @@ -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