diff --git a/install.ps1 b/install.ps1 index 74bf72daf..7892411d6 100644 --- a/install.ps1 +++ b/install.ps1 @@ -11,7 +11,7 @@ $ErrorActionPreference = "Stop" $defaultInstallUrl = "https://raw.githubusercontent.com/Abdess/retrobios/main/install.py" - $defaultInstallSha256 = "c030192b6ecfcedfc114ecf0cbcfc509fcca44e398e6d8cefedc7766065df538" + $defaultInstallSha256 = "c138739989d2ea32639c470f3af89d5038604b7fb446f08cc37659e139b76605" $maximumInstallerBytes = 2MB $installerExit = 0 $installer = if ($PSScriptRoot) { Join-Path $PSScriptRoot "install.py" } else { $null } diff --git a/install.py b/install.py index 0852371ef..9d7fc2a7f 100644 --- a/install.py +++ b/install.py @@ -10299,8 +10299,17 @@ def main() -> None: "or --list-targets") # The screens have no choice for --declared-only: the questions ask the # rest, and the run keeps the platform's own list. - if args.tui or not (plain_requested(args) or args.declared_only or tui_blocker(args)): + blocker = "" if args.tui else tui_blocker(args) + if args.tui or not (plain_requested(args) or args.declared_only or blocker): sys.exit(run_tui(args)) + small = re.match(r"the terminal is (\d+x\d+), smaller than", blocker or "") + # A pseudo-terminal nobody sized (script fed from a pipe) is 0x0: no window. + if (small and "0" not in small[1].split("x") and not plain_requested(args) + and not args.declared_only): + # The questions came line by line with no word of why: the page + # says a smaller window says so (tui_audit_11 P3-4). + print(f"The window is {small[1]}: questions come line by line " + f"({TUI_MIN_COLS}x{TUI_MIN_ROWS} for the full screen).") if args.events: # The events own stdout. The text a person reads goes to stderr, so a # program never has to tell one from the other. @@ -12256,8 +12265,9 @@ def _install(args: argparse.Namespace, real_stdout: TextIO) -> None: # names it: "found on this machine" followed "from E:\". sources = list(dict.fromkeys(copy.index.label for copy in copies.values())) named = ", ".join(screen_text(shown_path(label)) for label in sources) - origin = (f"copied from {named}" if all(copy.index is not bios_index for copy in copies.values()) - else "found on this machine") + origin = (f"copied from {named}" if all( + copy.index is not bios_index for copy in copies.values()) + else "found on this machine") print(f"\nCopying {_plural(len(copies), 'file')} {origin}" f"{rest}{where} ({format_size(dl_size)})...") elif LOCAL.pack is not None: @@ -12902,6 +12912,8 @@ class TuiState: self.cli_regions: list = [] self.cores: list = [] self.no_game_data: bool = False + # A quit the user chose, said as one (tui_quit). + self.quit_asked: bool = False self.installed_only: bool = False self.jobs: int = 8 self.passthrough: list = [] @@ -13787,8 +13799,11 @@ def _back(state: TuiState) -> list[tuple]: return effects -def tui_quit(state: TuiState, code: int = 130) -> list[tuple]: - """Leave now: with the install's own code once it ran, else 130.""" +def tui_quit(state: TuiState, code: int = 130, asked: bool = True) -> list[tuple]: + """Leave now: with the install's own code once it ran, else 130. asked + is a quit the user chose (q, Quit), said as one: it read "stopped", the + word of an interrupted run (tui_audit_11 P3-19).""" + state.quit_asked = asked if state.screen == "result": code = state.code if state.code is not None else 1 elif state.screen == "error": @@ -13804,7 +13819,7 @@ def tui_interrupt(state: TuiState) -> list[tuple]: return [("kill",)] state.stopping = True return [("stop",)] - return tui_quit(state) + return tui_quit(state, asked=False) def _start_run(state: TuiState, run: str) -> list[tuple]: @@ -14868,7 +14883,7 @@ _DESKTOP_LEAD: dict[str, tuple[str, ...]] = { # and a Linux one (SteamOS), RetroDECK is a Linux Flatpak, BizHawk publishes # Windows and Linux builds, RetroPie runs on Debian and Ubuntu. _COMPUTER_SYSTEMS: dict[str, tuple[tuple[str, ...], tuple[str, ...]]] = { - "Windows": (("windows",), ("retroarch", "retrobat", "emudeck", "bizhawk")), + "Windows": (("windows", "pc"), ("retroarch", "retrobat", "emudeck", "bizhawk")), "macOS": (("macos", "mac", "osx", "macbook"), ("retroarch",)), "Linux": (("linux", "ubuntu", "debian", "steamos"), ("retroarch", "emudeck", "retrodeck", "bizhawk", "retropie")), @@ -14878,7 +14893,9 @@ _COMPUTER_SYSTEMS: dict[str, tuple[tuple[str, ...], tuple[str, ...]]] = { def _computer_named(plat: str, needle: str) -> list[str]: """The computer systems the filter names that run plat.""" key = _device_key(needle) - if len(key) < 3: + # Two letters are a word on their way: "wi" said "No setup matches" + # between "w" and "win" (tui_audit_11 P3-17). "pc" is one of the words. + if len(key) < 2: return [] return [name for name, (words, plats) in _COMPUTER_SYSTEMS.items() if plat in plats and any(word.startswith(key) for word in words)] @@ -15200,6 +15217,12 @@ def tui_update(state: TuiState, key: TuiKey) -> list[tuple]: if char == "q": return tui_quit(state) rows = tui_rows(state) + if screen == "error" and key.name in ("pgup", "pgdn"): + # The message scrolls, the rows stay (_view_error). + step = max(1, state.rows - len(rows) - 5) + state.focus["error_text"] = max(0, state.focus.get("error_text", 0) + + (step if key.name == "pgdn" else -step)) + return [] if screen in _PAGERS and not rows: return _pager_key(state, key) if not rows: @@ -15500,11 +15523,17 @@ def _route_hint(state: TuiState, parts: tuple) -> tuple: _SHORT_VERBS = ((" Only this system", " Only this"), (" First system", " First")) +# Below this many columns of text the bar always uses the short key names. +TUI_LONG_BAR_WIDTH = 52 + + def _hint(width: int, *parts: str) -> str: """The keys that matter now, short names first given up, then whole entries from the right.""" full = " ".join(parts) - if cell_width(full) <= width: + # One rule for every screen of a width: at 40 columns the plan read + # "A Install" and the next screen "A/Enter Install" (tui_audit_11 P3-5). + if cell_width(full) <= width and width >= TUI_LONG_BAR_WIDTH: return full short = [] for part in parts: @@ -15543,8 +15572,15 @@ _ROW_VERBS = { } +# The widest a row's two columns spread: at 120 columns a system's size sat +# 85 columns from its name (tui_audit_11 P3-20). +TUI_ROW_SPREAD = 78 + + def _row_line(row: TuiRow, focused: bool, width: int) -> TuiLine: left = ("> " if focused else " ") + row.label + if row.right: + width = min(width, max(TUI_ROW_SPREAD, cell_width(left) + 2 + cell_width(row.right))) if row.keep_right and row.right: whole = width - cell_width(left) - 2 forms = (row.right, *row.narrow) @@ -15820,6 +15856,11 @@ def _plan_lines(check: dict, dest: Path | None, state: TuiState) -> list: _styled(headline, "title"), _styled(f"BIOS folder: {_short_path(_plan_folder(state, dest), state.home)}", "path"), ] + found_in = _retroarch_config_at(dest) if dest is not None else None + if found_in: + # Right under the folder it names: after the save lines it read as + # naming them (tui_audit_11 P3-6). + lines.append(_styled(f"Named by {_short_path(Path(found_in[0]), state.home)}", "path")) if state.facts.get("writable") is False: # The remedy itself comes below, after the counts. lines += cannot_write_words(dest, state.platform, tui_hint(state), @@ -15848,9 +15889,6 @@ def _plan_lines(check: dict, dest: Path | None, state: TuiState) -> list: else state.dry_saves if check is state.dry else None) saves_lines = _saves_lines(state, saves) lines += [_styled(line, "path" if index else "") for index, line in enumerate(saves_lines)] - found_in = _retroarch_config_at(dest) if dest is not None else None - if found_in: - lines.append(_styled(f"Named by {_short_path(Path(found_in[0]), state.home)}", "path")) free = state.facts.get("free") if free is not None: lines.append(f"Free space: {format_size(free)}") @@ -16048,7 +16086,9 @@ def _status_after(state: TuiState) -> list[str]: def _view_browse(state: TuiState, width: int, height: int) -> tuple: - where = "Drives" if state.browse_dir is None else middle(str(state.browse_dir), width) + # Under WSL a drive is E:\ on every screen: the title said /mnt/e + # (tui_audit_11 P3-13). + where = "Drives" if state.browse_dir is None else middle(shown_path(state.browse_dir), width) hint: list = [] if state.browse_for == "from": # Two lines at 40 columns: the long form was cut after "copied,". @@ -16159,7 +16199,22 @@ def _view_plan(state: TuiState, width: int, height: int) -> tuple: whole = (f"{check['present']} in place, {check['missing']} missing, " f"{check['wrong_hash']} wrong ") lines = [line for line in lines if not str(line).startswith(whole)] - lines[2:2] = choice + # Under the folder and what named it, in this order: the card's note, + # the choice named on the command line, the choice made here. + at = next((index + 1 for index, line in enumerate(lines) + if str(line).startswith("BIOS folder:")), 1) + if at < len(lines) and str(lines[at]).startswith("Named by "): + at += 1 + named = [*([f"for {len(state.systems)} of {len(tui_systems(state))} systems (--system)"] + if state.cli_systems and state.systems else []), + *(["--core"] if state.cores else []), *(["--region"] if state.regions else [])] + said = ", ".join(named) + # Named on the command line: said in line mode's words, and widened by + # Choose systems. --core and --region were said in line mode alone + # (tui_audit_11 P3-11). + lines[at:at] = [*([state.card_note] if state.card_note + and state.card_note != state.status else []), + *([f"{said[0].upper()}{said[1:]}."] if said else []), *choice] split = _tui_split(state, [(event["dest"], event["size"]) for event in state.files if event["state"] != "present"]) if split or (state.no_game_data and _game_data_row(state) is not None): @@ -16167,16 +16222,16 @@ def _view_plan(state: TuiState, width: int, height: int) -> tuple: # row that leaves it out. lines[1:1] = [f"{split or 'No game data'}. {GAME_DATA_WORDS[0].upper()}" f"{GAME_DATA_WORDS[1:]}."] - if state.cli_systems and state.systems: - # Named on the command line: said, and widened by Choose systems. - lines[2:2] = [f"For {len(state.systems)} of {len(tui_systems(state))} " - "systems (--system)."] - if state.card_note and state.card_note != state.status: - # Under the folder it is about: a stick no file names, taken for - # KNULLI, read as a KNULLI card. - lines.insert(2, state.card_note) if state.status: lines.append(state.status) + if width > 38: + # A folder that does not fit is wrapped, as the first screen wraps + # it: cut in its middle, the plan hid the part being checked before + # Install. Cut only on a 40-column screen (tui_audit_11 P3-7). + lines = [piece for line in lines for piece in ( + [_styled(part) for part in _wrap(str(line), width)] + if getattr(line, "style", "") == "path" and cell_width(str(line)) > width + else [line])] rows = tui_rows(state) if width < 54: rows = [row._replace(label="Copy from your old card or a folder") @@ -16594,12 +16649,17 @@ def tui_outcome(state: TuiState) -> tuple[str, list[str]]: # As the plan counted them, and as line mode ends. games = done.count(GAME_DATA) lines.append(f"Installed: {kinds_words(len(done) - games, games)}.") + sources = _tui_sources(state, installed, len(failed) + left) + if left and installed and sources: + # One count, then the remedy, as line mode says it: the count came + # twice, the remedy between them (tui_audit_11 P3-18). + lines[0] = sources + sources = "" + if sources: + lines.append(sources) if left: lines.append(_offline_remedy([_short_path(Path(folder), state.home) for folder in state.from_folders])) - sources = _tui_sources(state, installed, len(failed) + left) - if sources: - lines.append(sources) ok = {dest for dest, good, _why in state.finished if good} if any(reason and dest in ok for dest, (_folder, reason) in state.local_dests.items()): # A fix copies: the misplaced files are the user's, never moved. @@ -16697,9 +16757,22 @@ def _view_result(state: TuiState, width: int, height: int) -> tuple: def _view_error(state: TuiState, width: int, height: int) -> tuple: + """The message and its rows. A message longer than the room the rows + leave scrolls, with a line saying which lines are in view: at 40x12 the + offline screen dropped its remedy without a word (tui_audit_11 P3-2).""" + rows = tui_rows(state) + text = _text_lines(list(state.error), width) + room = height - len(rows) - (1 if rows else 0) + hint: tuple = ("A/Enter Choose", "B/Esc Back") + if len(text) > room >= 2: + first = max(0, min(state.focus.get("error_text", 0), len(text) - (room - 1))) + state.focus["error_text"] = first + text = _scrolled(text, first, room) + page = (TUI_ROUTES.get(state.pad, TUI_ROUTES["both"]).help["page"] + or "PgUp/PgDn").split(", ")[0] + hint += (f"{page} Scroll",) return (state.error_title or "Something went wrong", - _compose(state, width, height, state.error, tui_rows(state)), - ("A/Enter Choose", "B/Esc Back")) + _compose(state, width, height, text, rows), hint) def _view_quit(state: TuiState, width: int, height: int) -> tuple: @@ -16740,6 +16813,12 @@ def _help_lines(state: TuiState) -> list[tuple[str, str]]: return [(names["stop"], "Stop: what is done stays done"), *([(names["kill"], "Stop")] if names["kill"] else []), *_route_extras(state), *_help_key(state)] + if state.editing: + # Typing a filter: Space, a, n and q type there, and the help listed + # them as ticks, all, none and quit (tui_audit_11 P3-9). + return [("Enter", "Done"), ("Esc", "Clear the filter"), + ("Backspace", "Delete a character"), ("Ctrl-U", "Clear"), ("Ctrl-C", "Quit"), + *_route_extras(state)] if screen == "path": return [(names["enter"], "Use this folder"), ("Backspace", "Delete a character"), ("Ctrl-U", "Clear"), (names["esc"], "Back"), ("Ctrl-C", "Quit"), @@ -17228,7 +17307,11 @@ def _tui_fetch(plat: str, inbox: queue.Queue, log: io.StringIO) -> None: ", then choose Try again") + "."] if code == "network": lines = [f"The list of BIOS files could not be downloaded: {words}.", - "Check the Wi-Fi or the cable, then choose Try again."] + "Check the Wi-Fi or the cable, then choose Try again.", + # A handheld with no Wi-Fi has the pack as its way + # (tui_audit_11 P3-3). + "Or copy a pack to the card and run the installer with --from-pack " + "PACK: no network is needed then."] proxy = next((name for name in ("HTTPS_PROXY", "https_proxy", "ALL_PROXY", "all_proxy") if os.environ.get(name)), "") if proxy: @@ -17481,6 +17564,8 @@ def _closing_lines(state: TuiState) -> list[str]: for line in state.error)] if state.exit_code == 0: return [f"RetroBIOS: everything is in place in {state.dest}."] + if state.quit_asked: + return ["RetroBIOS: quit. Nothing was installed."] return ["RetroBIOS: stopped. Nothing was installed."] diff --git a/install.sh b/install.sh index d228c5a4c..8dc8ad1b3 100755 --- a/install.sh +++ b/install.sh @@ -18,7 +18,7 @@ esac TEMP_INSTALLER="" TEMP_DIRECTORY="" DEFAULT_INSTALL_URL="https://raw.githubusercontent.com/Abdess/retrobios/main/install.py" -DEFAULT_INSTALL_SHA256="c030192b6ecfcedfc114ecf0cbcfc509fcca44e398e6d8cefedc7766065df538" +DEFAULT_INSTALL_SHA256="c138739989d2ea32639c470f3af89d5038604b7fb446f08cc37659e139b76605" MAX_INSTALLER_BYTES=2097152 cleanup() { diff --git a/install/launchers/index.json b/install/launchers/index.json index 100185b7c..a68f0af23 100644 --- a/install/launchers/index.json +++ b/install/launchers/index.json @@ -27,8 +27,8 @@ { "dest": "roms/ports/retrobios/install.py", "repo_path": "install.py", - "size": 813666, - "sha256": "c030192b6ecfcedfc114ecf0cbcfc509fcca44e398e6d8cefedc7766065df538", + "size": 818472, + "sha256": "c138739989d2ea32639c470f3af89d5038604b7fb446f08cc37659e139b76605", "executable": false }, { @@ -71,8 +71,8 @@ "muos": { "archive": { "dest": "ARCHIVE/RetroBIOS.muxapp", - "size": 866215, - "sha256": "cfe8dc0a1cda5a63d810ebcc8f28e1f1797446d5ef72447d0a64b3f68639a258" + "size": 871021, + "sha256": "0b2f463994437ab12013039a8eec8ce94040a03594e9d820a0da58a1058ef9e1" }, "members": [ { @@ -92,8 +92,8 @@ { "name": "RetroBIOS/install.py", "repo_path": "install.py", - "size": 813666, - "sha256": "c030192b6ecfcedfc114ecf0cbcfc509fcca44e398e6d8cefedc7766065df538", + "size": 818472, + "sha256": "c138739989d2ea32639c470f3af89d5038604b7fb446f08cc37659e139b76605", "executable": false }, { @@ -166,8 +166,8 @@ { "dest": ".config/modules/retrobios/install.py", "repo_path": "install.py", - "size": 813666, - "sha256": "c030192b6ecfcedfc114ecf0cbcfc509fcca44e398e6d8cefedc7766065df538", + "size": 818472, + "sha256": "c138739989d2ea32639c470f3af89d5038604b7fb446f08cc37659e139b76605", "executable": false }, { diff --git a/tests/fixtures/tui/checking-40x12.txt b/tests/fixtures/tui/checking-40x12.txt index f49a3e15a..599dd3dc1 100644 --- a/tests/fixtures/tui/checking-40x12.txt +++ b/tests/fixtures/tui/checking-40x12.txt @@ -9,4 +9,4 @@ - Ctrl-C Quit B/Esc Quit + Ctrl-C Quit B Quit diff --git a/tests/fixtures/tui/checking-40x15.txt b/tests/fixtures/tui/checking-40x15.txt index 5c131dfe1..5345d7513 100644 --- a/tests/fixtures/tui/checking-40x15.txt +++ b/tests/fixtures/tui/checking-40x15.txt @@ -12,4 +12,4 @@ - Ctrl-C Quit B/Esc Quit + Ctrl-C Quit B Quit diff --git a/tests/fixtures/tui/command-40x12.txt b/tests/fixtures/tui/command-40x12.txt index f05700025..1b955d059 100644 --- a/tests/fixtures/tui/command-40x12.txt +++ b/tests/fixtures/tui/command-40x12.txt @@ -9,4 +9,4 @@ - A/Enter Back + A Back diff --git a/tests/fixtures/tui/command-40x15.txt b/tests/fixtures/tui/command-40x15.txt index 6fcca70a1..3e8530cf0 100644 --- a/tests/fixtures/tui/command-40x15.txt +++ b/tests/fixtures/tui/command-40x15.txt @@ -12,4 +12,4 @@ - A/Enter Back + A Back diff --git a/tests/fixtures/tui/confirm-40x12.txt b/tests/fixtures/tui/confirm-40x12.txt index a6829f6b0..704d86a7a 100644 --- a/tests/fixtures/tui/confirm-40x12.txt +++ b/tests/fixtures/tui/confirm-40x12.txt @@ -9,4 +9,4 @@ > Install Back - A/Enter Install B/Esc Back ? Help + A Install B Back ? Help diff --git a/tests/fixtures/tui/confirm-40x15.txt b/tests/fixtures/tui/confirm-40x15.txt index 250844ab7..6cb81ee9f 100644 --- a/tests/fixtures/tui/confirm-40x15.txt +++ b/tests/fixtures/tui/confirm-40x15.txt @@ -12,4 +12,4 @@ - A/Enter Install B/Esc Back ? Help + A Install B Back ? Help diff --git a/tests/fixtures/tui/create-40x12.txt b/tests/fixtures/tui/create-40x12.txt index c5ef8083c..e767d4777 100644 --- a/tests/fixtures/tui/create-40x12.txt +++ b/tests/fixtures/tui/create-40x12.txt @@ -9,4 +9,4 @@ - A/Enter Create B/Esc Quit + A Create B Quit diff --git a/tests/fixtures/tui/create-40x15.txt b/tests/fixtures/tui/create-40x15.txt index 600629ca8..b62b0cd1a 100644 --- a/tests/fixtures/tui/create-40x15.txt +++ b/tests/fixtures/tui/create-40x15.txt @@ -12,4 +12,4 @@ - A/Enter Create B/Esc Quit + A Create B Quit diff --git a/tests/fixtures/tui/error-40x12.txt b/tests/fixtures/tui/error-40x12.txt index 224cf89d2..d3c843856 100644 --- a/tests/fixtures/tui/error-40x12.txt +++ b/tests/fixtures/tui/error-40x12.txt @@ -5,8 +5,8 @@ -2] Name or service not known Address: https://raw.githubusercontent.com/ - Abdess/retrobios/main/install/ - retroarch.json + 1-5 of 7 + > Choose another setup or folder Quit - A/Enter Choose B/Esc Back + A Choose B Back PgUp/PgDn Scroll diff --git a/tests/fixtures/tui/error-40x15.txt b/tests/fixtures/tui/error-40x15.txt index df4e6f1c3..a7d362f90 100644 --- a/tests/fixtures/tui/error-40x15.txt +++ b/tests/fixtures/tui/error-40x15.txt @@ -12,4 +12,4 @@ Quit - A/Enter Choose B/Esc Back + A Choose B Back diff --git a/tests/fixtures/tui/folder-card-40x12.txt b/tests/fixtures/tui/folder-card-40x12.txt index 8a348dfe2..ef783de21 100644 --- a/tests/fixtures/tui/folder-card-40x12.txt +++ b/tests/fixtures/tui/folder-card-40x12.txt @@ -9,4 +9,4 @@ > Browse for a folder Type a path Look again - A/Enter Browse B/Esc Back ? Help + A Browse B Back ? Help diff --git a/tests/fixtures/tui/folder-card-40x15.txt b/tests/fixtures/tui/folder-card-40x15.txt index c791e4241..03aba349e 100644 --- a/tests/fixtures/tui/folder-card-40x15.txt +++ b/tests/fixtures/tui/folder-card-40x15.txt @@ -12,4 +12,4 @@ Look again - A/Enter Browse B/Esc Back ? Help + A Browse B Back ? Help diff --git a/tests/fixtures/tui/installing-40x12.txt b/tests/fixtures/tui/installing-40x12.txt index 5750b4633..d56a30bbd 100644 --- a/tests/fixtures/tui/installing-40x12.txt +++ b/tests/fixtures/tui/installing-40x12.txt @@ -9,4 +9,4 @@ FAILED scph5500.bin (HTTP 404) ok gba_bios.bin - B/Esc Stop ? Help + B Stop ? Help diff --git a/tests/fixtures/tui/installing-40x15.txt b/tests/fixtures/tui/installing-40x15.txt index 51aeca7c0..950ad6515 100644 --- a/tests/fixtures/tui/installing-40x15.txt +++ b/tests/fixtures/tui/installing-40x15.txt @@ -12,4 +12,4 @@ - B/Esc Stop ? Help + B Stop ? Help diff --git a/tests/fixtures/tui/options-120x40.txt b/tests/fixtures/tui/options-120x40.txt index 21aae2729..eac6c418f 100644 --- a/tests/fixtures/tui/options-120x40.txt +++ b/tests/fixtures/tui/options-120x40.txt @@ -2,14 +2,14 @@ Each row is an option of the command line, named on the right. - > Preferred regions: North America, Japan --region - Game data: kept --no-game-data - Installed cores only: no (1 found) --installed-cores - Cores: all --core - Parallel downloads: 8 --jobs - BIOS folder: ~/.config/retroarch/system --dest - Copy from: none --from - Preview the file list --dry-run + > Preferred regions: North America, Japan --region + Game data: kept --no-game-data + Installed cores only: no (1 found) --installed-cores + Cores: all --core + Parallel downloads: 8 --jobs + BIOS folder: ~/.config/retroarch/system --dest + Copy from: none --from + Preview the file list --dry-run Show the command line diff --git a/tests/fixtures/tui/platforms-120x40.txt b/tests/fixtures/tui/platforms-120x40.txt index c4f74e812..0389c94a8 100644 --- a/tests/fixtures/tui/platforms-120x40.txt +++ b/tests/fixtures/tui/platforms-120x40.txt @@ -14,26 +14,26 @@ BizHawk MiSTer FPGA RetroPie - Allium, SD card Miyoo Mini, Miyoo Mini Plus, Miyoo Mini Flip - AmberELEC, SD card RG351, RG552 - Anbernic stock firmware, SD card RG35XX Plus, RG35XX H, RG35XX SP, RG28XX + Allium, SD card Miyoo Mini, Miyoo Mini Plus, Miyoo Mini Flip + AmberELEC, SD card RG351, RG552 + Anbernic stock firmware, SD card RG35XX Plus, RG35XX H, RG35XX SP, RG28XX ArchR, SD card R36S, R33S, R36S Soysauce, Odroid Go Advance, Odroid Go Super, RG351V, +6 - ArkOS, SD card RG353M, RG353V, RG503, RGB30, RK2023, RG351MP, RG351V, R35S, RGB10 - CrossMix-OS, SD card TrimUI Smart Pro - dArkOS, SD card Odroid Go Advance, RGB10, RG351MP, G350 - EmuELEC, SD card Odroid Go + ArkOS, SD card RG353M, RG353V, RG503, RGB30, RK2023, RG351MP, RG351V, R35S, RGB10 + CrossMix-OS, SD card TrimUI Smart Pro + dArkOS, SD card Odroid Go Advance, RGB10, RG351MP, G350 + EmuELEC, SD card Odroid Go JELOS, SD card RG351M, RG351V, RG353M, RG353P, RG353V, RG353VS, RG503, RG552, R33S, R36S, +24 KNULLI, SD card RG ARC-S, RG28XX, RG34XX, RG35XX, RG35XX Plus, RG35XX H, RG35XX 2024, +15 - KOLOS, SD card RG353P, RG353M, RG353V, RG353VS, RG503, RK2023, RGB30, RGB10 Max 3 - Koriki, SD card Miyoo Mini + KOLOS, SD card RG353P, RG353M, RG353V, RG353VS, RG503, RK2023, RGB30, RGB10 Max 3 + Koriki, SD card Miyoo Mini MinUI, SD card RG28XX, RG34XX, RG34XX SP, RG35XX, RG35XX Plus, RG35XX H, RG35XX SP, +16 MiyooCFW, SD card BittBoy, Pocket Go, PowKiddy Q90, PowKiddy V90, PowKiddy Q20 Mini, Sup M3, +1 muOS, SD card RG35XX H, RG35XX Plus, RG35XX SP, RG35XX 2024, RG34XX, RG40XX H, RG40XX V, +4 MyMinUI, SD card RG35XX, Miyoo Mini, Miyoo Mini Flip, Miyoo Mini Plus, Miyoo A30, +16 - NextUI, SD card TrimUI Brick, TrimUI Smart Pro - Onion, SD card Miyoo Mini, Miyoo Mini Plus - plumOS, SD card MagicX XU20 V32 - RetroOZ, SD card Odroid Go Super, Odroid Go Advance, RGB10 Max, RGB10 Max 2 + NextUI, SD card TrimUI Brick, TrimUI Smart Pro + Onion, SD card Miyoo Mini, Miyoo Mini Plus + plumOS, SD card MagicX XU20 V32 + RetroOZ, SD card Odroid Go Super, Odroid Go Advance, RGB10 Max, RGB10 Max 2 ROCKNIX, SD card RG351, RG353, RG503, RG552, RG 55G1, RG ARC, RG35XX 2024, RG35XX Plus, +37 spruceOS, SD card Miyoo A30, Miyoo Flip, Miyoo Mini, Miyoo Mini Plus, Miyoo Mini Flip, +5 UnofficialOS, SD card R35S, R36S, RG351M, RG351P, RG351V, RG353M, RG353P, RG353V, RG503, +5 diff --git a/tests/fixtures/tui/preview-40x12.txt b/tests/fixtures/tui/preview-40x12.txt index 507cf8b5d..55499e667 100644 --- a/tests/fixtures/tui/preview-40x12.txt +++ b/tests/fixtures/tui/preview-40x12.txt @@ -9,4 +9,4 @@ missing 16.0 KB gba_bios.bin - A/Enter Back + A Back diff --git a/tests/fixtures/tui/preview-40x15.txt b/tests/fixtures/tui/preview-40x15.txt index 110e70660..873f126d8 100644 --- a/tests/fixtures/tui/preview-40x15.txt +++ b/tests/fixtures/tui/preview-40x15.txt @@ -12,4 +12,4 @@ - A/Enter Back + A Back diff --git a/tests/fixtures/tui/quit-40x12.txt b/tests/fixtures/tui/quit-40x12.txt index 8d91077c0..fce65bb05 100644 --- a/tests/fixtures/tui/quit-40x12.txt +++ b/tests/fixtures/tui/quit-40x12.txt @@ -9,4 +9,4 @@ - A/Enter Quit B/Esc Stay + A Quit B Stay diff --git a/tests/fixtures/tui/quit-40x15.txt b/tests/fixtures/tui/quit-40x15.txt index 675109b69..1f2fd0f1c 100644 --- a/tests/fixtures/tui/quit-40x15.txt +++ b/tests/fixtures/tui/quit-40x15.txt @@ -12,4 +12,4 @@ - A/Enter Quit B/Esc Stay + A Quit B Stay diff --git a/tests/fixtures/tui/result-40x12.txt b/tests/fixtures/tui/result-40x12.txt index ea7a3e5f6..568b9dce0 100644 --- a/tests/fixtures/tui/result-40x12.txt +++ b/tests/fixtures/tui/result-40x12.txt @@ -9,4 +9,4 @@ To put them back: python3 install.py 1-8 of 17 - A/Enter Exit Up/Down Scroll + A Exit ^v Scroll diff --git a/tests/fixtures/tui/result-40x15.txt b/tests/fixtures/tui/result-40x15.txt index 683877220..45e55c93a 100644 --- a/tests/fixtures/tui/result-40x15.txt +++ b/tests/fixtures/tui/result-40x15.txt @@ -12,4 +12,4 @@ /home/user/.config/retroarch/retrobios-backup/2026-10-23_1200 1-11 of 17 - A/Enter Exit Up/Down Scroll + A Exit ^v Scroll diff --git a/tests/fixtures/tui/route-evmapy.txt b/tests/fixtures/tui/route-evmapy.txt index 4feac0d4a..89b845d3a 100644 --- a/tests/fixtures/tui/route-evmapy.txt +++ b/tests/fixtures/tui/route-evmapy.txt @@ -322,35 +322,6 @@ A Use this folder B Back -=== create 40x12 - RetroBIOS: Create the folder? - - /home/user/bios/new does not exist. - The nearest folder that does: - /home/user - - > Create it and continue - Choose another folder - - - - A Create B Quit -=== create 40x15 - RetroBIOS: Create the folder? - - /home/user/bios/new does not exist. - The nearest folder that does: - /home/user - - > Create it and continue - Choose another folder - - - - - - - A Create B Quit === create 80x24 RetroBIOS: Create the folder? @@ -604,7 +575,7 @@ Copy from: none Preview the file list Show the command line - A Open B Back Left/Right Set + A Open B Back <> Set === options 40x15 RetroBIOS: Options @@ -620,7 +591,7 @@ Sets --region - A Open B Back Left/Right Set + A Open B Back <> Set === options 80x24 RetroBIOS: Options @@ -658,7 +629,7 @@ [ ] Japan - A Tick B Done Left/Right Order + A Tick B Done <> Order === regions 40x15 RetroBIOS: Preferred regions @@ -674,7 +645,7 @@ - A Tick B Done Left/Right Order + A Tick B Done <> Order === regions 80x24 RetroBIOS: Preferred regions @@ -754,35 +725,6 @@ A Filter B Back X Tick -=== preview 40x12 - RetroBIOS: Preview - - 4 files to download, 1.0 MB. Nothing - is written. - - missing 512.0 KB scph5500.bin - wrong copy 512.0 KB scph5501.bin - missing 4 B pcsx/extra.bin - missing 16.0 KB gba_bios.bin - - - A Back -=== preview 40x15 - RetroBIOS: Preview - - 4 files to download, 1.0 MB. Nothing - is written. - - missing 512.0 KB scph5500.bin - wrong copy 512.0 KB scph5501.bin - missing 4 B pcsx/extra.bin - missing 16.0 KB gba_bios.bin - - - - - - A Back === preview 80x24 RetroBIOS: Preview @@ -807,35 +749,6 @@ - A Back -=== command 40x12 - RetroBIOS: Command line - - The same choice without questions: - - python3 install.py --platform - retroarch --dest - /home/user/.config/retroarch/system - --system nintendo-gba - - - - A Back -=== command 40x15 - RetroBIOS: Command line - - The same choice without questions: - - python3 install.py --platform - retroarch --dest - /home/user/.config/retroarch/system - --system nintendo-gba - - - - - - A Back === command 80x24 RetroBIOS: Command line @@ -1032,27 +945,11 @@ -2] Name or service not known Address: https://raw.githubusercontent.com/ - Abdess/retrobios/main/install/ - retroarch.json - > Choose another setup or folder - Quit - A Choose B Back -=== error 40x15 - RetroBIOS: Something went wrong - - Could not fetch the file list for - retroarch after 3 attempts: [Errno - -2] Name or service not known - Address: - https://raw.githubusercontent.com/ - Abdess/retrobios/main/install/ - retroarch.json + 1-5 of 7 > Choose another setup or folder Quit - - - A Choose B Back + A Choose B Back L1/R1 Scroll === error 80x24 RetroBIOS: Something went wrong @@ -1078,35 +975,6 @@ A Choose B Back -=== quit 40x12 - RetroBIOS: Quit? - - Quit without installing? Nothing was - written. - - > Quit - Stay - - - - - A Quit B Stay -=== quit 40x15 - RetroBIOS: Quit? - - Quit without installing? Nothing was - written. - - > Quit - Stay - - - - - - - - A Quit B Stay === quit 80x24 RetroBIOS: Quit? diff --git a/tests/fixtures/tui/route-gptokeyb.txt b/tests/fixtures/tui/route-gptokeyb.txt index c36055601..7ae640cbc 100644 --- a/tests/fixtures/tui/route-gptokeyb.txt +++ b/tests/fixtures/tui/route-gptokeyb.txt @@ -322,35 +322,6 @@ A Use this folder B Back -=== create 40x12 - RetroBIOS: Create the folder? - - /home/user/bios/new does not exist. - The nearest folder that does: - /home/user - - > Create it and continue - Choose another folder - - - - A Create B Quit -=== create 40x15 - RetroBIOS: Create the folder? - - /home/user/bios/new does not exist. - The nearest folder that does: - /home/user - - > Create it and continue - Choose another folder - - - - - - - A Create B Quit === create 80x24 RetroBIOS: Create the folder? @@ -604,7 +575,7 @@ Copy from: none Preview the file list Show the command line - A Open B Back Left/Right Set + A Open B Back <> Set === options 40x15 RetroBIOS: Options @@ -620,7 +591,7 @@ Sets --region - A Open B Back Left/Right Set + A Open B Back <> Set === options 80x24 RetroBIOS: Options @@ -658,7 +629,7 @@ [ ] Japan - A Tick B Done Left/Right Order + A Tick B Done <> Order === regions 40x15 RetroBIOS: Preferred regions @@ -674,7 +645,7 @@ - A Tick B Done Left/Right Order + A Tick B Done <> Order === regions 80x24 RetroBIOS: Preferred regions @@ -754,35 +725,6 @@ A Filter B Back X Tick -=== preview 40x12 - RetroBIOS: Preview - - 4 files to download, 1.0 MB. Nothing - is written. - - missing 512.0 KB scph5500.bin - wrong copy 512.0 KB scph5501.bin - missing 4 B pcsx/extra.bin - missing 16.0 KB gba_bios.bin - - - A Back -=== preview 40x15 - RetroBIOS: Preview - - 4 files to download, 1.0 MB. Nothing - is written. - - missing 512.0 KB scph5500.bin - wrong copy 512.0 KB scph5501.bin - missing 4 B pcsx/extra.bin - missing 16.0 KB gba_bios.bin - - - - - - A Back === preview 80x24 RetroBIOS: Preview @@ -807,35 +749,6 @@ - A Back -=== command 40x12 - RetroBIOS: Command line - - The same choice without questions: - - python3 install.py --platform - retroarch --dest - /home/user/.config/retroarch/system - --system nintendo-gba - - - - A Back -=== command 40x15 - RetroBIOS: Command line - - The same choice without questions: - - python3 install.py --platform - retroarch --dest - /home/user/.config/retroarch/system - --system nintendo-gba - - - - - - A Back === command 80x24 RetroBIOS: Command line @@ -1032,27 +945,11 @@ -2] Name or service not known Address: https://raw.githubusercontent.com/ - Abdess/retrobios/main/install/ - retroarch.json - > Choose another setup or folder - Quit - A Choose B Back -=== error 40x15 - RetroBIOS: Something went wrong - - Could not fetch the file list for - retroarch after 3 attempts: [Errno - -2] Name or service not known - Address: - https://raw.githubusercontent.com/ - Abdess/retrobios/main/install/ - retroarch.json + 1-5 of 7 > Choose another setup or folder Quit - - - A Choose B Back + A Choose B Back L1/R1 Scroll === error 80x24 RetroBIOS: Something went wrong @@ -1078,35 +975,6 @@ A Choose B Back -=== quit 40x12 - RetroBIOS: Quit? - - Quit without installing? Nothing was - written. - - > Quit - Stay - - - - - A Quit B Stay -=== quit 40x15 - RetroBIOS: Quit? - - Quit without installing? Nothing was - written. - - > Quit - Stay - - - - - - - - A Quit B Stay === quit 80x24 RetroBIOS: Quit? diff --git a/tests/fixtures/tui/route-gptokeyb_muxterm.txt b/tests/fixtures/tui/route-gptokeyb_muxterm.txt index c36055601..5a8efe5a0 100644 --- a/tests/fixtures/tui/route-gptokeyb_muxterm.txt +++ b/tests/fixtures/tui/route-gptokeyb_muxterm.txt @@ -322,35 +322,6 @@ A Use this folder B Back -=== create 40x12 - RetroBIOS: Create the folder? - - /home/user/bios/new does not exist. - The nearest folder that does: - /home/user - - > Create it and continue - Choose another folder - - - - A Create B Quit -=== create 40x15 - RetroBIOS: Create the folder? - - /home/user/bios/new does not exist. - The nearest folder that does: - /home/user - - > Create it and continue - Choose another folder - - - - - - - A Create B Quit === create 80x24 RetroBIOS: Create the folder? @@ -604,7 +575,7 @@ Copy from: none Preview the file list Show the command line - A Open B Back Left/Right Set + A Open B Back <> Set === options 40x15 RetroBIOS: Options @@ -620,7 +591,7 @@ Sets --region - A Open B Back Left/Right Set + A Open B Back <> Set === options 80x24 RetroBIOS: Options @@ -658,7 +629,7 @@ [ ] Japan - A Tick B Done Left/Right Order + A Tick B Done <> Order === regions 40x15 RetroBIOS: Preferred regions @@ -674,7 +645,7 @@ - A Tick B Done Left/Right Order + A Tick B Done <> Order === regions 80x24 RetroBIOS: Preferred regions @@ -754,35 +725,6 @@ A Filter B Back X Tick -=== preview 40x12 - RetroBIOS: Preview - - 4 files to download, 1.0 MB. Nothing - is written. - - missing 512.0 KB scph5500.bin - wrong copy 512.0 KB scph5501.bin - missing 4 B pcsx/extra.bin - missing 16.0 KB gba_bios.bin - - - A Back -=== preview 40x15 - RetroBIOS: Preview - - 4 files to download, 1.0 MB. Nothing - is written. - - missing 512.0 KB scph5500.bin - wrong copy 512.0 KB scph5501.bin - missing 4 B pcsx/extra.bin - missing 16.0 KB gba_bios.bin - - - - - - A Back === preview 80x24 RetroBIOS: Preview @@ -807,35 +749,6 @@ - A Back -=== command 40x12 - RetroBIOS: Command line - - The same choice without questions: - - python3 install.py --platform - retroarch --dest - /home/user/.config/retroarch/system - --system nintendo-gba - - - - A Back -=== command 40x15 - RetroBIOS: Command line - - The same choice without questions: - - python3 install.py --platform - retroarch --dest - /home/user/.config/retroarch/system - --system nintendo-gba - - - - - - A Back === command 80x24 RetroBIOS: Command line @@ -1032,26 +945,10 @@ -2] Name or service not known Address: https://raw.githubusercontent.com/ - Abdess/retrobios/main/install/ - retroarch.json - > Choose another setup or folder - Quit - A Choose B Back -=== error 40x15 - RetroBIOS: Something went wrong - - Could not fetch the file list for - retroarch after 3 attempts: [Errno - -2] Name or service not known - Address: - https://raw.githubusercontent.com/ - Abdess/retrobios/main/install/ - retroarch.json + 1-5 of 7 > Choose another setup or folder Quit - - A Choose B Back === error 80x24 RetroBIOS: Something went wrong @@ -1078,35 +975,6 @@ A Choose B Back -=== quit 40x12 - RetroBIOS: Quit? - - Quit without installing? Nothing was - written. - - > Quit - Stay - - - - - A Quit B Stay -=== quit 40x15 - RetroBIOS: Quit? - - Quit without installing? Nothing was - written. - - > Quit - Stay - - - - - - - - A Quit B Stay === quit 80x24 RetroBIOS: Quit? diff --git a/tests/fixtures/tui/route-keys.txt b/tests/fixtures/tui/route-keys.txt index ce2c5937e..e26ac816f 100644 --- a/tests/fixtures/tui/route-keys.txt +++ b/tests/fixtures/tui/route-keys.txt @@ -928,7 +928,7 @@ To put them back: python3 install.py 1-8 of 17 - Enter Exit Up/Down Scroll + Enter Exit ^v Scroll === result 40x15 RetroBIOS: Finished with errors @@ -944,7 +944,7 @@ /home/user/.config/retroarch/retrobios-backup/2026-10-23_1200 1-11 of 17 - Enter Exit Up/Down Scroll + Enter Exit ^v Scroll === result 80x24 RetroBIOS: Finished with errors @@ -978,8 +978,8 @@ -2] Name or service not known Address: https://raw.githubusercontent.com/ - Abdess/retrobios/main/install/ - retroarch.json + 1-5 of 7 + > Choose another setup or folder Quit Enter Choose Esc Back diff --git a/tests/fixtures/tui/route-miyoo.txt b/tests/fixtures/tui/route-miyoo.txt index 7a1554714..77041c30d 100644 --- a/tests/fixtures/tui/route-miyoo.txt +++ b/tests/fixtures/tui/route-miyoo.txt @@ -604,7 +604,7 @@ Copy from: none Preview the file list Show the command line - A Open R2 Back Left/Right Set + A Open R2 Back <> Set === options 40x15 RetroBIOS: Options @@ -620,7 +620,7 @@ Sets --region - A Open R2 Back Left/Right Set + A Open R2 Back <> Set === options 80x24 RetroBIOS: Options @@ -658,7 +658,7 @@ [ ] Japan - A Tick R2 Done Left/Right Order + A Tick R2 Done <> Order === regions 40x15 RetroBIOS: Preferred regions @@ -674,7 +674,7 @@ - A Tick R2 Done Left/Right Order + A Tick R2 Done <> Order === regions 80x24 RetroBIOS: Preferred regions @@ -754,35 +754,6 @@ A Filter R2 Back -=== preview 40x12 - RetroBIOS: Preview - - 4 files to download, 1.0 MB. Nothing - is written. - - missing 512.0 KB scph5500.bin - wrong copy 512.0 KB scph5501.bin - missing 4 B pcsx/extra.bin - missing 16.0 KB gba_bios.bin - - - A Back -=== preview 40x15 - RetroBIOS: Preview - - 4 files to download, 1.0 MB. Nothing - is written. - - missing 512.0 KB scph5500.bin - wrong copy 512.0 KB scph5501.bin - missing 4 B pcsx/extra.bin - missing 16.0 KB gba_bios.bin - - - - - - A Back === preview 80x24 RetroBIOS: Preview @@ -807,35 +778,6 @@ - A Back -=== command 40x12 - RetroBIOS: Command line - - The same choice without questions: - - python3 install.py --platform - retroarch --dest - /home/user/.config/retroarch/system - --system nintendo-gba - - - - A Back -=== command 40x15 - RetroBIOS: Command line - - The same choice without questions: - - python3 install.py --platform - retroarch --dest - /home/user/.config/retroarch/system - --system nintendo-gba - - - - - - A Back === command 80x24 RetroBIOS: Command line @@ -1032,11 +974,11 @@ -2] Name or service not known Address: https://raw.githubusercontent.com/ - Abdess/retrobios/main/install/ - retroarch.json + 1-5 of 7 + > Choose another setup or folder Quit - A Choose R2 Back + A Choose R2 Back L1/R1 Scroll === error 40x15 RetroBIOS: Something went wrong diff --git a/tests/fixtures/tui/route-muxterm.txt b/tests/fixtures/tui/route-muxterm.txt index e754f340f..1e8af124d 100644 --- a/tests/fixtures/tui/route-muxterm.txt +++ b/tests/fixtures/tui/route-muxterm.txt @@ -604,7 +604,7 @@ Copy from: none Preview the file list Show the command line - Start Open B Back Left/Right Set + Start Open B Back <> Set === options 40x15 RetroBIOS: Options @@ -620,7 +620,7 @@ Sets --region - Start Open B Back Left/Right Set + Start Open B Back <> Set === options 80x24 RetroBIOS: Options @@ -658,7 +658,7 @@ [ ] Japan - Start Tick B Done Left/Right Order + Start Tick B Done <> Order === regions 40x15 RetroBIOS: Preferred regions @@ -674,7 +674,7 @@ - Start Tick B Done Left/Right Order + Start Tick B Done <> Order === regions 80x24 RetroBIOS: Preferred regions @@ -1032,8 +1032,8 @@ -2] Name or service not known Address: https://raw.githubusercontent.com/ - Abdess/retrobios/main/install/ - retroarch.json + 1-5 of 7 + > Choose another setup or folder Quit Start Choose B Back diff --git a/tests/fixtures/tui/setup-40x12.txt b/tests/fixtures/tui/setup-40x12.txt index 04e73733b..2d87a3413 100644 --- a/tests/fixtures/tui/setup-40x12.txt +++ b/tests/fixtures/tui/setup-40x12.txt @@ -9,4 +9,4 @@ BIOS folder: /home/user/.config/retroarch/system ES-DE found: its BIOS files go to each - A/Enter Continue B/Esc Quit ? Help + A Continue B Quit ? Help diff --git a/tests/fixtures/tui/setup-40x15.txt b/tests/fixtures/tui/setup-40x15.txt index d2d99ab29..faa6cbd38 100644 --- a/tests/fixtures/tui/setup-40x15.txt +++ b/tests/fixtures/tui/setup-40x15.txt @@ -12,4 +12,4 @@ emulator, not to ES-DE itself. - A/Enter Continue B/Esc Quit ? Help + A Continue B Quit ? Help diff --git a/tests/fixtures/tui/systems-120x40.txt b/tests/fixtures/tui/systems-120x40.txt index 2b859a315..0580bbaa7 100644 --- a/tests/fixtures/tui/systems-120x40.txt +++ b/tests/fixtures/tui/systems-120x40.txt @@ -3,9 +3,9 @@ All None Filter (/ to type) - > [x] nintendo-gba (gba) 1 file, 16.0 KB - [x] sony-playstation 3 files, 1.0 MB - [ ] nintendo-famicom-disk (ファミコン) in place + > [x] nintendo-gba (gba) 1 file, 16.0 KB + [x] sony-playstation 3 files, 1.0 MB + [ ] nintendo-famicom-disk (ファミコン) in place diff --git a/tests/test_install_tui.py b/tests/test_install_tui.py index 5abba0828..b2a023ddc 100644 --- a/tests/test_install_tui.py +++ b/tests/test_install_tui.py @@ -1013,7 +1013,7 @@ class Views(unittest.TestCase): with self.subTest(screen=name): hint = install.tui_view(state, 40, 15)[-1].strip() self.assertTrue(hint.startswith(("A/Enter", "A ", "Enter", "Ctrl-C", "Any key", - "B/Esc Stop")), hint) + "B/Esc Stop", "B Stop")), hint) def test_focus_is_marked_without_colour(self): lines = install.tui_view(fresh_state(), 80, 24) @@ -1030,12 +1030,12 @@ def _bar_keys(line: str) -> set: # The keys each route has, as the bar may name them. "Any" opens "Any key # closes this help", <> and ^v are the narrow forms of Left/Right, Up/Down. ROUTE_KEYS = { - "keys": {"Enter", "Esc", "Space", "o", "?", "/", "Ctrl-C", "Ctrl-U"}, - "gptokeyb": {"A", "B", "X", "Y", "Select+Start"}, - "gptokeyb_muxterm": {"A", "B", "X", "Y", "Select+Start"}, - "evmapy": {"A", "B", "X", "Y"}, - "muxterm": {"Start", "B", "Y", "Menu", "Select"}, - "miyoo": {"A", "Start", "R2", "L2"}, + "keys": {"Enter", "Esc", "Space", "o", "?", "/", "Ctrl-C", "Ctrl-U", "PgUp/PgDn"}, + "gptokeyb": {"A", "B", "X", "Y", "Select+Start", "L1/R1"}, + "gptokeyb_muxterm": {"A", "B", "X", "Y", "Select+Start", "D-pad"}, + "evmapy": {"A", "B", "X", "Y", "L1/R1"}, + "muxterm": {"Start", "B", "Y", "Menu", "Select", "D-pad"}, + "miyoo": {"A", "Start", "R2", "L2", "L1/R1"}, } EVERY_ROUTE_HAS = {"Left/Right", "Up/Down", "<>", "^v", "Any"} @@ -1628,7 +1628,11 @@ class AuditRoundTwo(unittest.TestCase): kind, plat, title, lines, code = inbox.get_nowait() self.assertEqual((kind, title, code), ("manifest_failed", "No connection", 1)) self.assertIn("could not be downloaded: the server refused the connection", lines[0]) - self.assertNotIn("--", " ".join(lines)) + # The pack is the one option named: the way of a handheld with no + # Wi-Fi (tui_audit_11 P3-3). + self.assertIn("Or copy a pack to the card and run the installer with --from-pack", + " ".join(lines)) + self.assertNotIn("--", " ".join(lines).replace("--from-pack", "")) state = fresh_state() with mock.patch.object(install.Path, "exists", return_value=True): press(state, "enter") @@ -5803,7 +5807,8 @@ class AuditSix(unittest.TestCase): self.assertNotIn("before Install", text) self.assertNotIn("backup folder", text) self.assertIn("Copy them from your old card", text) - self.assertEqual(view[-1].strip(), "A/Enter Back ? Help") + # The short names at 40 columns, as every screen of that width. + self.assertEqual(view[-1].strip(), "A Back ? Help") def test_a_refusal_shows_at_40x12_on_type_a_path(self): """P2-2: Enter seemed to do nothing, the refusal drawn below the @@ -9072,6 +9077,131 @@ class AuditEighteenAndEleven(unittest.TestCase): rows = install.tui_rows(state) self.assertEqual(rows[state.focus.get("plan", 0)].action, "fromcard") + # tui_audit_11 P3-2 to P3-20: the screens' polish. + + def test_a_long_message_scrolls_with_its_position(self): + """P3-2: at 40x12 the offline screen dropped its remedy unsaid.""" + state = fresh_state() + state.stack = ["error"] + state.error_title = install.TUI_OFFLINE_TITLE + state.error = ["This run makes no network request (--offline), and the list of BIOS " + "files comes from the network.", + "Run the installer again with the list saved as a file (--manifest " + "FILE, a copy of install/.json) or with a pack (--from-pack " + "PACK), or without --offline."] + state.cols, state.rows = 40, 12 + view = [str(line) for line in install.tui_view(state, 40, 12)] + self.assertTrue(any(re.fullmatch(r" 1-\d+ of \d+", line) for line in view), view) + self.assertIn("Scroll", view[-1]) + press(state, "pgdn") + end = " ".join(" ".join(str(line) for line in install.tui_view(state, 40, 12)).split()) + self.assertIn("--offline.", end) + + def test_a_small_window_says_why_the_questions_come_line_by_line(self): + """P3-4.""" + with mock.patch.object(install, "tui_blocker", + return_value="the terminal is 39x12, smaller than 40x12"), \ + mock.patch.object(install, "_run", side_effect=SystemExit(0)), \ + mock.patch.object(install, "detect_os", return_value="linux"), \ + mock.patch.dict(install._WSL), \ + mock.patch.object(sys, "argv", ["install.py"]), \ + contextlib.redirect_stdout(io.StringIO()) as out, \ + self.assertRaises(SystemExit): + install.main() + self.assertIn("The window is 39x12: questions come line by line (40x12 for the full " + "screen).", out.getvalue()) + + def test_one_bar_rule_per_width(self): + """P3-5: at 40 columns the plan read "A Install", Confirm "A/Enter".""" + states = golden_states() + for name in ("preview", "confirm", "result", "quit"): + with self.subTest(screen=name): + bar = str(install.tui_view(states[name], 40, 12)[-1]) + self.assertNotIn("A/Enter", bar) + self.assertNotIn("B/Esc", bar) + + def test_the_folder_its_config_named_is_said_under_it(self): + """P3-6, P3-7.""" + state = checked_state() + long_dest = Path("/home/user/" + "a-long-folder-name/" * 5 + "system") + state.dest = long_dest + with mock.patch.object(install, "_retroarch_config_at", + return_value=(HOME / ".config/retroarch/retroarch.cfg", HOME)): + lines = [str(line).strip() for line in install.tui_view(state, 80, 30)] + at = next(index for index, line in enumerate(lines) if line.startswith("BIOS folder:")) + tail = next(index for index, line in enumerate(lines) if line.startswith("Named by")) + self.assertNotIn("...", " ".join(lines[at:tail])) + self.assertIn(str(long_dest).replace("/home/user", "~")[-20:], " ".join(lines[at:tail])) + self.assertLessEqual(tail - at, 3) + + def test_the_help_while_typing_lists_what_does_not_type(self): + """P3-9.""" + state = checked_state() + state.stack.append("systems") + state.editing = "systems" + state.help = True + help_text = " ".join(" ".join(install.tui_view(state, 80, 24)).split()) + self.assertIn("Esc Clear the filter", help_text) + self.assertIn("Ctrl-U Clear", help_text) + self.assertNotIn("Tick, untick", help_text) + + def test_the_plan_names_core_and_region_as_line_mode(self): + """P3-11.""" + state = checked_state() + state.cli_systems, state.cores, state.regions = ["sony-playstation"], ["x"], ["us"] + text = " ".join(" ".join(install.tui_view(state, 120, 40)).split()) + self.assertIn("(--system), --core, --region.", text) + + def test_pc_and_short_prefixes_find_the_computer_setups(self): + """P3-17.""" + for typed in ("pc", "wi", "win"): + with self.subTest(typed=typed): + found = {row.value for row in install.setup_list([], set(), typed, "linux") + if row.action == "platform"} + self.assertIn("retrobat", found) + + def test_a_partial_result_counts_once_then_says_the_remedy(self): + """P3-18.""" + state = _installed_state(Path("/x/system")) + state.summary.update(installed=6, pending=16, failed=[]) + state.from_folders = [Path("/media/me/OLD")] + with mock.patch.object(install, "_offline_only", return_value=True), \ + mock.patch.object(install, "_tui_sources", + return_value="6 files copied from OLD, 16 still missing."): + _title, lines = install.tui_outcome(state) + self.assertEqual(lines[0], "6 files copied from OLD, 16 still missing.") + self.assertIn("They are not in", lines[1]) + + def test_q_is_said_as_a_quit(self): + """P3-19: q reported "stopped", the word of an interrupted run.""" + state = checked_state() + press(state, "q") + self.assertEqual(state.exit_code, 130) + self.assertEqual(install._closing_lines(state), + ["RetroBIOS: quit. Nothing was installed."]) + other = checked_state() + install.tui_interrupt(other) + self.assertEqual(install._closing_lines(other), + ["RetroBIOS: stopped. Nothing was installed."]) + + def test_wide_rows_keep_the_size_near_the_name(self): + """P3-20.""" + row = install.TuiRow("system", "[x] 3DO 3do", "2 files, 4.9 KB") + line = str(install._row_line(row, False, 118)) + self.assertLessEqual(len(line.rstrip()), install.TUI_ROW_SPREAD) + + def test_the_wsl_browser_titles_a_drive_by_its_letter(self): + """P3-13.""" + state = fresh_state() + state.stack = ["browse"] + state.browse_dir = Path("/mnt/e") + with mock.patch.dict(install._WSL, {"active": True, "root": "/mnt"}), \ + mock.patch.object(install, "_browse_rows", return_value=([], "")): + state.browse_rows = [] + title = " ".join(str(line) for line in install.tui_view(state, 80, 24)[:4]) + self.assertIn("E:\\", title) + self.assertNotIn("/mnt/e", title) + def test_the_card_marker_table_is_written_from_the_rules(self): """code_audit_18 P3-6, tui_audit_11 P2-2: the table left out the SHARE drives. It is generated: every card rule and SHARE drive file diff --git a/wiki/installer.md b/wiki/installer.md index abbdfdd32..f18fe30b4 100644 --- a/wiki/installer.md +++ b/wiki/installer.md @@ -1485,7 +1485,7 @@ fields, the installer refuses to narrow rather than install everything. | `0` | everything asked for is installed or already up to date | | `1` | a failure: the one-liner could not download or verify `install.py`, or found no Python 3.8 (nothing was run), an unusable base URL, an unknown platform or target, `--installed-cores` with no core found, a manifest that fails validation, a fetch that never succeeded, no platform to install for in a non-interactive run, some files failed or were left out by `--offline` while others installed, `--check` found files to fetch, a `--from-pack` pack that cannot be read, lacks a part or is for another platform, a `--from` folder that does not exist or cannot be read, a `--dest` that is not the card of the system named (another system's card, a folder inside one, a drive with none of its files where its card always has them, or a folder that does not exist), `--restore` could not put a file back, or `--add-app` could not put every file of the app on the card | | `2` | nothing was installed: every file the run tried failed (a BIOS folder that cannot be written, for one), or `--offline` left every file out, the drive has too little free space for the download (checked before any folder is created), or the question about a folder that does not exist, named with `--dest` or typed at the folder question, was answered no. Also an option the installer refused before any download: an unknown option, `--jobs` outside 1 to 32, `--target` with `--every-board` or `--from-pack`, `--target` or `--list-targets` with `--offline` (or `RETROBIOS_INSTALL_OFFLINE` set), which read a core list from the network, `--add-app` with `--check`, `--dry-run`, a listing, `--from-pack` or `--offline`. The message then starts with `install.py: error:`. Also `--add-app` for a folder on no card that takes the app, said in an `Error:` line and an `app` event before anything is written. With `--restore`, a folder that is not a dated folder inside `retrobios-backup` | -| `130` | stopped with Ctrl+C, or with `q`, Ctrl+C or Ctrl+D at a question, or the full screen closed with no button pressed (`RETROBIOS_IDLE_EXIT`) before an install. Run the same command again to carry on | +| `130` | stopped with Ctrl+C, or with `q`, Ctrl+C or Ctrl+D at a question, or the full screen closed with no button pressed (`RETROBIOS_IDLE_EXIT`) before an install. A quit chosen (`q`, **Quit**) is code `130` too, and the terminal keeps `RetroBIOS: quit. Nothing was installed.` where a stop keeps `stopped`. Run the same command again to carry on | | `141` | the output was piped to a reader that closed early (`--list-platforms \| head`) |