mirror of
https://github.com/Abdess/retroarch_system.git
synced 2026-11-01 00:27:20 -05:00
feat: pick one system in a few presses
A long systems list opens on None. A there unticks every system and moves to the first one, and A on a system with nothing ticked installs that one alone, so Enter still continues. Inside muxterm, which keeps PgUp and PgDn for its scrollback, Left and Right on the D-pad page. One system of 66 at 40x15 took 17 presses with a pad, 57 in muxterm; it now takes 12 on both. The pyyaml check of the device lists skips where pyyaml is not installed.
This commit is contained in:
1 parent
57811270e3
commit
e919d8478e
7 files changed
+185
-33
No files matched your search
+1
-1
@@ -11,7 +11,7 @@
|
||||
|
||||
$ErrorActionPreference = "Stop"
|
||||
$defaultInstallUrl = "https://raw.githubusercontent.com/Abdess/retrobios/main/install.py"
|
||||
$defaultInstallSha256 = "a564b0582012d8851acea1cfc973101785be4a56d79aacb75bcf50691139c5a3"
|
||||
$defaultInstallSha256 = "b13b2ef42634a371e0fa8c55be5182e8fb04c09d7c456816e300150f507d8697"
|
||||
$maximumInstallerBytes = 2MB
|
||||
$installerExit = 0
|
||||
$installer = if ($PSScriptRoot) { Join-Path $PSScriptRoot "install.py" } else { $null }
|
||||
|
||||
+32
-6
@@ -11685,6 +11685,9 @@ def _activate(state: TuiState, row: TuiRow) -> list[tuple]:
|
||||
return _open_from_browser(state)
|
||||
if action in ("misplaced", "unreadable"):
|
||||
return _push(state, action)
|
||||
if action == "systems" and len(state.systems) > TUI_LONG_LIST:
|
||||
# On None: picking one system of 66 began with Up, Up, A.
|
||||
return _push(state, action, 1)
|
||||
# On the first system, so A continues and the D-pad ticks.
|
||||
return _push(state, action, 3 if action == "systems" else 0)
|
||||
if screen == "systems":
|
||||
@@ -11692,7 +11695,14 @@ def _activate(state: TuiState, row: TuiRow) -> list[tuple]:
|
||||
state.ticked = {row["system"] for row in state.systems}
|
||||
elif action == "none":
|
||||
state.ticked = set()
|
||||
# On the first system: the next A takes the one in focus alone.
|
||||
state.focus["systems"] = next(
|
||||
(i for i, item in enumerate(tui_rows(state)) if item.action == "system"),
|
||||
state.focus.get("systems", 0))
|
||||
else:
|
||||
if action == "system" and not state.ticked:
|
||||
# Nothing ticked: A takes the system in focus, and only it.
|
||||
state.ticked = {row.value}
|
||||
return _begin_install(state)
|
||||
return []
|
||||
if screen == "options":
|
||||
@@ -12046,6 +12056,12 @@ def tui_update(state: TuiState, key: TuiKey) -> list[tuple]:
|
||||
return _filter_key(state, key)
|
||||
if state.pad == "miyoo":
|
||||
key = _miyoo_key(state, key)
|
||||
if (state.pad in _NO_PAGE_PADS and key.name in ("left", "right")
|
||||
and screen not in ("regions", "options")):
|
||||
# muxterm keeps PgUp and PgDn for its scrollback: the D-pad's Left
|
||||
# and Right page instead (X or Y still ticks). One system of 66 took
|
||||
# about 60 Down presses.
|
||||
key = TuiKey("pgup" if key.name == "left" else "pgdn")
|
||||
char = key.text if key.name == "char" else ""
|
||||
if key.name == "f1" or char == "?":
|
||||
state.help = True
|
||||
@@ -12129,6 +12145,10 @@ def tui_update(state: TuiState, key: TuiKey) -> list[tuple]:
|
||||
return []
|
||||
|
||||
|
||||
# The routes inside muxterm, where no button sends PgUp or PgDn.
|
||||
_NO_PAGE_PADS = ("muxterm", "gptokeyb_muxterm")
|
||||
|
||||
|
||||
# A Miyoo's L1 and R1 are the keys E and T (codes 18 and 20).
|
||||
_MIYOO_PAGES = {"e": "pgup", "t": "pgdn"}
|
||||
|
||||
@@ -12422,6 +12442,9 @@ def _scrolled(lines: list[TuiLine], first: int, height: int, lead: int = 0) -> l
|
||||
|
||||
# The rows a screen keeps for its list under pinned lines.
|
||||
TUI_PINNED_ROWS = 4
|
||||
# A list longer than this opens its systems on None, and its help names a
|
||||
# page key.
|
||||
TUI_LONG_LIST = 9
|
||||
|
||||
|
||||
def _compose(
|
||||
@@ -13243,9 +13266,9 @@ _HELP_KEYS: dict[str, dict[str, str | None]] = {
|
||||
"back": "B, Select, Esc", "stop": "B, Esc, Ctrl-C", "kill": None,
|
||||
"enter": "A, Enter", "esc": "B, Esc"},
|
||||
# muxterm keeps PgUp and PgDn for its own scrollback, and its L1 and R1
|
||||
# send nothing: no key pages a list there.
|
||||
"muxterm": {"move": "D-pad, Up/Down", "page": None, "ok": "Start, Enter",
|
||||
"tick": "Y, Space, Right/Left", "options": "o, Tab",
|
||||
# send nothing: the D-pad's Left and Right page a list there.
|
||||
"muxterm": {"move": "D-pad, Up/Down", "page": "D-pad Left/Right", "ok": "Start, Enter",
|
||||
"tick": "Y, Space", "options": "o, Tab",
|
||||
"back": "B, Esc, Backspace", "stop": "B, Esc, Ctrl-C", "kill": None,
|
||||
"enter": "Start, Enter", "esc": "Esc"},
|
||||
"miyoo": {"move": "D-pad, Up/Down", "page": "L1/R1, PgUp/PgDn", "ok": "A, Start, Enter",
|
||||
@@ -13254,8 +13277,9 @@ _HELP_KEYS: dict[str, dict[str, str | None]] = {
|
||||
"enter": "Start, Enter", "esc": "Esc"},
|
||||
}
|
||||
# muxterm keeps PgUp and PgDn for its scrollback even when gptokeyb2 owns the
|
||||
# pad, and the launcher sends nothing on L1 and R1 there.
|
||||
_HELP_KEYS["gptokeyb_muxterm"] = {**_HELP_KEYS["gptokeyb"], "page": None}
|
||||
# pad, and the launcher sends nothing on L1 and R1 there: Left and Right page.
|
||||
_HELP_KEYS["gptokeyb_muxterm"] = {**_HELP_KEYS["gptokeyb"], "page": "D-pad Left/Right",
|
||||
"tick": "X, Space"}
|
||||
|
||||
|
||||
def _route_extras(state: TuiState) -> list[tuple[str, str]]:
|
||||
@@ -13282,7 +13306,7 @@ def _help_lines(state: TuiState) -> list[tuple[str, str]]:
|
||||
("Ctrl-U", "Clear"), (names["esc"], "Back"), ("Ctrl-C", "Quit"),
|
||||
*_route_extras(state)]
|
||||
keys = [(names["move"], "Move")]
|
||||
if len(tui_rows(state)) > 9 or screen in _PAGERS:
|
||||
if len(tui_rows(state)) > TUI_LONG_LIST or screen in _PAGERS:
|
||||
keys += [*([(names["page"], "Page")] if names["page"] else []),
|
||||
("Home/End, g/G", "First, last")]
|
||||
primary = _primary(state) or _TUI_VIEWS[screen](state, 80, 24)[2][0]
|
||||
@@ -13315,6 +13339,8 @@ def _primary(state: TuiState) -> str | None:
|
||||
return None
|
||||
row = rows[max(0, min(state.focus.get(state.screen, 0), len(rows) - 1))]
|
||||
verb = _ROW_VERBS.get(row.action)
|
||||
if state.screen == "systems" and row.action == "system" and not state.ticked:
|
||||
verb = "Only this system"
|
||||
return f"A/Enter {verb}" if verb else None
|
||||
|
||||
|
||||
|
||||
+1
-1
@@ -18,7 +18,7 @@ esac
|
||||
TEMP_INSTALLER=""
|
||||
TEMP_DIRECTORY=""
|
||||
DEFAULT_INSTALL_URL="https://raw.githubusercontent.com/Abdess/retrobios/main/install.py"
|
||||
DEFAULT_INSTALL_SHA256="a564b0582012d8851acea1cfc973101785be4a56d79aacb75bcf50691139c5a3"
|
||||
DEFAULT_INSTALL_SHA256="b13b2ef42634a371e0fa8c55be5182e8fb04c09d7c456816e300150f507d8697"
|
||||
MAX_INSTALLER_BYTES=2097152
|
||||
|
||||
cleanup() {
|
||||
|
||||
@@ -27,8 +27,8 @@
|
||||
{
|
||||
"dest": "roms/ports/retrobios/install.py",
|
||||
"repo_path": "install.py",
|
||||
"size": 622687,
|
||||
"sha256": "a564b0582012d8851acea1cfc973101785be4a56d79aacb75bcf50691139c5a3",
|
||||
"size": 624131,
|
||||
"sha256": "b13b2ef42634a371e0fa8c55be5182e8fb04c09d7c456816e300150f507d8697",
|
||||
"executable": false
|
||||
},
|
||||
{
|
||||
@@ -71,22 +71,22 @@
|
||||
"muos": {
|
||||
"archive": {
|
||||
"dest": "ARCHIVE/RetroBIOS.muxapp",
|
||||
"size": 673341,
|
||||
"sha256": "8ffa4bc6e3e16745012c4cebdfebb7070cdcbd63191e0ef0edb11556338698ad"
|
||||
"size": 674834,
|
||||
"sha256": "b7d51126f63aeff0d7ca790d887556619aeb566b7133fe66ef79f08a7f639929"
|
||||
},
|
||||
"members": [
|
||||
{
|
||||
"name": "RetroBIOS/README.md",
|
||||
"repo_path": "ports/muos/README.md",
|
||||
"size": 1127,
|
||||
"sha256": "c0cfafad5fae8e6764b263b14a06195b267f66c4fe042333941ae6bcc6dab265",
|
||||
"size": 1176,
|
||||
"sha256": "26b5da84f22ce3c652f16ed8335be11d99e3bfb8657eec7caa100cf505f68497",
|
||||
"executable": false
|
||||
},
|
||||
{
|
||||
"name": "RetroBIOS/install.py",
|
||||
"repo_path": "install.py",
|
||||
"size": 622687,
|
||||
"sha256": "a564b0582012d8851acea1cfc973101785be4a56d79aacb75bcf50691139c5a3",
|
||||
"size": 624131,
|
||||
"sha256": "b13b2ef42634a371e0fa8c55be5182e8fb04c09d7c456816e300150f507d8697",
|
||||
"executable": false
|
||||
},
|
||||
{
|
||||
@@ -159,8 +159,8 @@
|
||||
{
|
||||
"dest": ".config/modules/retrobios/install.py",
|
||||
"repo_path": "install.py",
|
||||
"size": 622687,
|
||||
"sha256": "a564b0582012d8851acea1cfc973101785be4a56d79aacb75bcf50691139c5a3",
|
||||
"size": 624131,
|
||||
"sha256": "b13b2ef42634a371e0fa8c55be5182e8fb04c09d7c456816e300150f507d8697",
|
||||
"executable": false
|
||||
},
|
||||
{
|
||||
|
||||
@@ -11,7 +11,8 @@ 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 do nothing in
|
||||
muxterm, which keeps Page Up and Page Down for its own scrollback. Hold
|
||||
muxterm, which keeps Page Up and Page Down for its own scrollback: Left and
|
||||
Right on the D-pad page a list instead. Hold
|
||||
Select, then press Start to quit. Without PortMaster, muxterm reads the pad
|
||||
itself (Y then ticks a system) and Menu closes it.
|
||||
|
||||
|
||||
+133
-9
@@ -444,13 +444,12 @@ class Flow(unittest.TestCase):
|
||||
self.assertEqual(install.tui_rows(state)[state.focus["systems"]].action, "system")
|
||||
press(state, "n")
|
||||
self.assertEqual(state.ticked, set())
|
||||
self.assertEqual(press(state, "enter"), [])
|
||||
self.assertIn("No system is ticked", state.status)
|
||||
focus = next(i for i, row in enumerate(install.tui_rows(state))
|
||||
if row.value == "nintendo-gba")
|
||||
state.focus["systems"] = focus
|
||||
press(state, "right")
|
||||
# Nothing ticked: Enter takes the system in focus alone.
|
||||
self.assertEqual(press(state, "enter"), [("run", "confirm")])
|
||||
self.assertEqual(state.ticked, {"nintendo-gba"})
|
||||
self.assertEqual(state.screen, "confirm")
|
||||
self.assertIn("--system=nintendo-gba", install.tui_argv(state, "confirm", "py", "i.py"))
|
||||
self.assertIn("--dry-run", install.tui_argv(state, "confirm", "py", "i.py"))
|
||||
@@ -1127,19 +1126,20 @@ class PadRoutes(unittest.TestCase):
|
||||
self.assertIn(words, muxterm)
|
||||
self.assertNotIn("Start+Select", muxterm)
|
||||
# gptokeyb2 owns the pad inside muxterm, which keeps PgUp and PgDn
|
||||
# for its scrollback: the gptokeyb buttons, and no page key.
|
||||
# for its scrollback: the gptokeyb buttons, Left and Right to page.
|
||||
in_muxterm = help_text("gptokeyb_muxterm", "preview")
|
||||
for words in ("A, Start, Enter", "Select, then Start"):
|
||||
self.assertIn(words, in_muxterm)
|
||||
self.assertNotIn("L1/R1", in_muxterm)
|
||||
self.assertIn("L1/R1", help_text("gptokeyb", "preview"))
|
||||
# muxterm keeps PgUp and PgDn for its scrollback, and L1 and R1
|
||||
# send nothing: no paging is offered there, on a list or a pager.
|
||||
# send nothing: the D-pad's Left and Right page there instead.
|
||||
for screen in ("systems", "result", "plan"):
|
||||
with self.subTest(screen=screen):
|
||||
text = help_text("muxterm", screen)
|
||||
for words in ("L1", "R1", "PgUp", "PgDn", "Page"):
|
||||
for words in ("L1", "R1", "PgUp", "PgDn"):
|
||||
self.assertNotIn(words, text)
|
||||
self.assertIn("D-pad Left/Right Page", help_text("muxterm", "result"))
|
||||
miyoo = help_text("miyoo", "systems")
|
||||
for words in ("Start, Enter", "A, Space, Right/Left", "R2, Esc, Backspace",
|
||||
"L2, Tab, o", "Sends no key here"):
|
||||
@@ -2879,7 +2879,10 @@ class TheSetupFilterKnowsDevices(unittest.TestCase):
|
||||
self.assertIn("muOS", {row.value: row for row in self.rows("")}["retroarch"].right)
|
||||
|
||||
def test_the_handheld_lists_are_the_guides(self):
|
||||
import yaml
|
||||
try:
|
||||
import yaml
|
||||
except ImportError:
|
||||
self.skipTest("pyyaml is not installed")
|
||||
|
||||
guides = yaml.safe_load((REPO_ROOT / "platforms" / "_guides.yml").read_text())
|
||||
for system in guides["handheld_guide"]["systems"]:
|
||||
@@ -2889,6 +2892,126 @@ class TheSetupFilterKnowsDevices(unittest.TestCase):
|
||||
tuple(str(name) for name in system["devices"]))
|
||||
|
||||
|
||||
class OneSystemInFewPresses(unittest.TestCase):
|
||||
"""tui_audit_4 P3-10.11: one system with a controller took 15 presses on
|
||||
the fixture list, about 60 on muxterm, which has no page key."""
|
||||
|
||||
# The buttons each route sends, as the keys the interface reads.
|
||||
PADS = {
|
||||
"gptokeyb": {"A": "enter", "X": " ", "Start": "enter", "R1": "pgdn"},
|
||||
"muxterm": {"A": "enter", "X": " ", "Start": "enter", "R1": "right"},
|
||||
}
|
||||
|
||||
def long_list(self, pad: str) -> install.TuiState:
|
||||
state = checked_state()
|
||||
state.pad = pad
|
||||
state.cols, state.rows = 40, 15
|
||||
state.systems = [{"event": "system", "platform": "retroarch", "system": f"sys-{n:02d}",
|
||||
"name": f"System {n:02d}", "files": 1, "present": 0, "missing": 1,
|
||||
"wrong_hash": 0, "bytes_needed": 10} for n in range(66)]
|
||||
state.ticked = {row["system"] for row in state.systems}
|
||||
state.focus["plan"] = next(i for i, row in enumerate(install.tui_rows(state))
|
||||
if row.action == "install")
|
||||
return state
|
||||
|
||||
def press_button(self, state, pad: str, button: str) -> list:
|
||||
name = self.PADS[pad].get(button, button)
|
||||
return install.tui_update(state, Key("char", name) if len(name) == 1 else Key(name))
|
||||
|
||||
def go_to(self, state, pad: str, target: str, paging: bool) -> int:
|
||||
"""Presses from the focus to the target system, as a person does."""
|
||||
count = 0
|
||||
while True:
|
||||
rows = install.tui_rows(state)
|
||||
focus = state.focus.get("systems", 0)
|
||||
index = next(i for i, row in enumerate(rows) if row.value == target)
|
||||
if index == focus:
|
||||
return count
|
||||
page = max(1, state.rows - 6)
|
||||
button = "R1" if paging and index - focus >= page else "down"
|
||||
self.press_button(state, pad, button)
|
||||
count += 1
|
||||
|
||||
def presses(self, pad: str, fast: bool) -> int:
|
||||
"""From the plan to the run that installs sys-45 alone, with the
|
||||
setup's A before it."""
|
||||
state = self.long_list(pad)
|
||||
count = 1
|
||||
for button in ("down", "A"):
|
||||
self.press_button(state, pad, button)
|
||||
count += 1
|
||||
self.assertEqual(state.screen, "systems")
|
||||
paging = pad != "muxterm" or fast
|
||||
if fast:
|
||||
self.assertEqual(install.tui_rows(state)[state.focus["systems"]].action, "none")
|
||||
self.press_button(state, pad, "A")
|
||||
count += 1
|
||||
count += self.go_to(state, pad, "sys-45", paging)
|
||||
effects = self.press_button(state, pad, "A")
|
||||
count += 1
|
||||
else:
|
||||
state.focus["systems"] = 3
|
||||
for button in ("up", "up", "A"):
|
||||
self.press_button(state, pad, button)
|
||||
count += 1
|
||||
count += self.go_to(state, pad, "sys-45", paging)
|
||||
self.press_button(state, pad, "X")
|
||||
effects = self.press_button(state, pad, "Start")
|
||||
count += 2
|
||||
self.assertEqual(state.ticked, {"sys-45"})
|
||||
self.assertEqual(effects, [("run", "confirm")])
|
||||
# Install on the confirm screen, then Exit on the result.
|
||||
return count + 2
|
||||
|
||||
def test_presses_before_and_after(self):
|
||||
# The audit's path, A Down A Up Up A R1 x5 X Start A A. Before the
|
||||
# fast path it took 17 presses here, 57 on muxterm (None kept the
|
||||
# focus, and muxterm had no page key).
|
||||
self.assertEqual(self.presses("gptokeyb", fast=False), 15)
|
||||
self.assertEqual(self.presses("gptokeyb", fast=True), 12)
|
||||
self.assertEqual(self.presses("muxterm", fast=False), 55)
|
||||
self.assertEqual(self.presses("muxterm", fast=True), 12)
|
||||
|
||||
def test_a_on_a_system_with_none_ticked_takes_it_alone(self):
|
||||
state = self.long_list("keys")
|
||||
state.stack.append("systems")
|
||||
state.ticked = set()
|
||||
state.focus["systems"] = 5
|
||||
text = install.tui_view(state, 80, 24)[-1]
|
||||
self.assertIn("Enter Only this system", text)
|
||||
focused = install.tui_rows(state)[5].value
|
||||
self.assertEqual(press(state, "enter"), [("run", "confirm")])
|
||||
self.assertEqual(state.ticked, {focused})
|
||||
|
||||
def test_enter_still_continues_with_the_ticks_as_they_are(self):
|
||||
state = self.long_list("keys")
|
||||
state.stack.append("systems")
|
||||
state.focus["systems"] = 5
|
||||
self.assertEqual(press(state, "enter"), [("run", "install")])
|
||||
self.assertEqual(len(state.ticked), 66)
|
||||
|
||||
def test_left_and_right_page_inside_muxterm_only(self):
|
||||
for pad in ("muxterm", "gptokeyb_muxterm", "gptokeyb"):
|
||||
with self.subTest(pad=pad):
|
||||
state = self.long_list(pad)
|
||||
state.stack.append("systems")
|
||||
state.focus["systems"] = 3
|
||||
press(state, "right")
|
||||
paged = pad != "gptokeyb"
|
||||
self.assertEqual(state.focus["systems"] > 4, paged)
|
||||
self.assertEqual(len(state.ticked), 66)
|
||||
state.help = True
|
||||
help_text = " ".join(install.tui_view(state, 80, 24))
|
||||
self.assertEqual("D-pad Left/Right" in help_text, paged)
|
||||
|
||||
def test_a_short_list_opens_on_its_first_system(self):
|
||||
state = checked_state()
|
||||
state.focus["plan"] = next(i for i, row in enumerate(install.tui_rows(state))
|
||||
if row.action == "systems")
|
||||
press(state, "enter")
|
||||
self.assertEqual(install.tui_rows(state)[state.focus["systems"]].action, "system")
|
||||
|
||||
|
||||
class DataFilesHaveASize(unittest.TestCase):
|
||||
"""tui_audit_4 P3-10.8: "8179 files to download, 6.6 GB, and 2636 data
|
||||
files", the data files with no size."""
|
||||
@@ -3896,8 +4019,9 @@ class EndToEnd(unittest.TestCase):
|
||||
term.wait("> Install")
|
||||
term.send(DOWN, "\r")
|
||||
term.wait("Choose systems")
|
||||
# D-pad and A only: up to None, A, down to the second system, Right.
|
||||
term.send(UP, UP, "\r", DOWN, DOWN, DOWN, RIGHT)
|
||||
# D-pad and A only: up to None, A (on the first system), down to the
|
||||
# second system, Right.
|
||||
term.send(UP, UP, "\r", DOWN, RIGHT)
|
||||
screen = term.wait("1 of 2 systems")
|
||||
self.assertIn("> [x] sony-playstation", screen)
|
||||
term.send("\r")
|
||||
|
||||
+6
-5
@@ -131,7 +131,8 @@ how many files are missing, how much they weigh, how many are in place and how
|
||||
much space is free, before anything is written.
|
||||
|
||||
The other choices sit one press away. **Choose systems** ticks the consoles you
|
||||
play, with the total to download under the list. **Options** sets the
|
||||
play, with the total to download under the list. A long list opens on **None**:
|
||||
A there unticks every system, and A on a system then installs that one alone. **Options** sets the
|
||||
preferred regions, game data, the installed cores only, the cores, the parallel
|
||||
downloads and the folder. Each row names the option it sets, and **Show the
|
||||
command line** gives the same choice as one command for a script. **Preview the
|
||||
@@ -1072,8 +1073,8 @@ Start pressed first also chooses the line in focus: hold Select first. A
|
||||
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. L1 and R1 do not
|
||||
page there: muxterm keeps PgUp and PgDn for its own scrollback, so a list
|
||||
moves with the D-pad.
|
||||
page there: muxterm keeps PgUp and PgDn for its own scrollback, so Left and
|
||||
Right on the D-pad page a list instead, as they do with gptokeyb in muxterm.
|
||||
|
||||
Each launcher also sets `RETROBIOS_IDLE_EXIT` to 300: with no button pressed
|
||||
for 5 minutes since it opened, and no check or install running, the interface
|
||||
@@ -1087,8 +1088,8 @@ Each launcher tells the interface which buttons reach it, in
|
||||
| Value | Input | Set by |
|
||||
|-------|-------|--------|
|
||||
| `gptokeyb` | gptokeyb or gptokeyb2 with `retrobios.gptk`, the table above | the PortMaster port and the Tools entry |
|
||||
| `gptokeyb_muxterm` | the same, inside muxterm, which keeps L1 and R1 for its scrollback: no button pages | 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 |
|
||||
| `gptokeyb_muxterm` | the same, inside muxterm, which keeps L1 and R1 for its scrollback: Left and Right page | the muOS app with PortMaster |
|
||||
| `muxterm` | muxterm's own keys: D-pad (Left and Right page), 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, Start as Enter, L1 and R1 as E and T, L2 as Tab, R2 as Backspace | no launcher yet |
|
||||
|
||||
|
||||
Reference in new issue
Block a user