diff --git a/ZAPAROO_FORK.md b/ZAPAROO_FORK.md new file mode 100644 index 000000000..3a2ae6c59 --- /dev/null +++ b/ZAPAROO_FORK.md @@ -0,0 +1,179 @@ +# Zaparoo Fork — Change Map and Cleanup Backlog + +This document maps the Zaparoo-specific changes layered on top of MiSTer-devel +`Main_MiSTer`. It is meant to evolve as the fork iterates, so future +contributors don't have to re-derive the architecture from `git log`. + +**Scope:** commits authored by `wizzomafizzo` (Callan Barrett) and `asturur` +(Andrea Bogazzi) between commit `a2eb35e` and the tip of +`feat/zaparoo-fb-toggle`. Upstream merges, upstream-author changes, and +fully-reverted experiments (F2 toggle, picker/notice commands, perf tweak, +non-blocking spawn) are intentionally omitted. + +> **Living doc:** when you add a Zaparoo-specific behavior, append a row to +> the table below or revise the inconsistencies section. Stale entries are +> worse than no entries — please prune as you cleanup. + +--- + +## 1. Change map + +| # | Cluster | Purpose | Where it lives | +|---|---------|---------|----------------| +| 1 | **External launcher process management** | Spawn `zaparoo/launcher` via `agetty` on tty2; SIGTERM/SIGKILL on shutdown; bounded `waitpid`; respawn timer; 3-strike crash give-up | `support/zaparoo/alt_launcher.cpp` (`spawn`, `kill_launcher`, `alt_launcher_poll`, `return_to_normal_mode`) | +| 2 | **Process discovery & gating** | `alt_launcher_configured()` = file exists at `zaparoo/launcher` (cached, with sticky escape bit); `alt_launcher_active()` = process running | `support/zaparoo/alt_launcher.cpp:34-43,329-332` | +| 3 | **Custom menu RBF discovery** | `menu_rbf_name()` / `is_menu_rbf()` so a Zaparoo build can ship its own renamed menu RBF and the file_io / fpga_io / user_io paths still recognize it | `support/zaparoo/menu_rbf.cpp/.h`; consumers in `file_io.cpp`, `fpga_io.cpp`, `user_io.cpp` | +| 4 | **Forced cfg overrides** | `alt_launcher_cfg_apply()` forces `cfg.fb_terminal = 1; cfg.recents = 1` after INI parse. Original `ALT_LAUNCHER` / `MENU_RBF` INI knobs were dropped in favor of file-existence detection | `cfg.cpp:614`, `support/zaparoo/alt_launcher.cpp:24-30` | +| 5 | **Polling integration** | `alt_launcher_poll()` driven by main scheduler tick | `scheduler.cpp:36`, `support/zaparoo/alt_launcher.cpp:365` | +| 6 | **TTY / framebuffer hygiene** | Clear/reset tty2 around launcher lifecycle; toggle `video_fb_enable` and `video_chvt` only on respawn paths; don't touch them on plain shutdown | `support/zaparoo/alt_launcher.cpp` (`clear_launcher_tty`, `reset_launcher_tty`) | +| 7 | **Joypad routing into launcher** | `alt_launcher_fb_terminal_key()` translates `JOY_L2/R2/OSD` to `KEY_F1/BACKSPACE/MENU`; `joy_digital()` short-circuits to `uinp_send_key` when launcher active | `input.cpp:2475-2484`, `support/zaparoo/alt_launcher.cpp:45-62` | +| 8 | **Native CRT rendering path** | Launcher running in CRT mode: kernel framebuffer at 320×240 RGBA8888, FPGA scans separate region at `0x3A000000`, `status[9]=1` gates it; pre-spawn blank wipes the prior frame | `support/zaparoo/alt_launcher.cpp` (`enable_native_crt_path`, `disable_native_crt_path`, `blank_native_crt_fb`); paired with `Menu_MiSTer/rtl/native_video_*.sv` | +| 9 | **CRT mode persistence** | 1-byte `zaparoo_launcher_crt.bin` via `FileSaveConfig` / `FileLoadConfig`; loaded at menu init, applied on spawn | `support/zaparoo/alt_launcher.cpp:76-89,344,499` | +| 10 | **Native-core auto-init** | `zaparoo_is_native_core()` matches core name `"Zaparoo Launcher"`; `zaparoo_alt_launcher_init_for_core()` auto-spawns when the FPGA loads that core | `support/zaparoo/alt_launcher.cpp:480-495`, `user_io.cpp:1543` | +| 11 | **In-core "Launcher" OSD entry** | Adds row 31 (`ALT_LAUNCHER_MENUSUB`) to MENU_COMMON1 marked with `reboot_req` when activated | `menu.cpp:2831,2845-2849,3088-3091` | +| 12 | **OSD/F12 overlay over running launcher** | F12 / `KEY_MENU` reaches the OSD even with launcher running; on menu core opens System Settings directly (skip file picker); F1/F9 disabled when launcher active; `vga_nag` suppressed; auto-open suppressed in CRT mode | `menu.cpp:843-852,1289,1304-1311,1334,1583,1604-1611,6727,6739,6816,6901`, `user_io.cpp:4162-4171` | +| 13 | **Trimmed System Settings render** | `alt_launcher_render_system_menu()` overrides MENU_SYSTEM1 body for the alt-launcher path; `alt_launcher_translate_system_select()` maps trimmed menusub indices to upstream dispatch slots | `support/zaparoo/alt_launcher_menu.cpp`, `menu.cpp:6739-6745,6816-6821` | +| 14 | **Right-side Display Centering page** | New menu state hosting H/V offset adjustment + relocated CRT toggle; persisted via 2-byte `zaparoo_video_offsets.bin`; pushed via `user_io_status_set("[13:10]" / "[17:14]")` | `support/zaparoo/display_menu.cpp/.h` *(local rename in progress: `launcher_pages.cpp/.h`)*, `support/zaparoo/alt_launcher.cpp` (offset state + setters), `menu.cpp` `MENU_ZAPAROO_DISPLAY*` cases | +| 15 | **Escape-to-stock semantics** | Sticky `s_escaped` flag makes `alt_launcher_configured()` return `false` after a clean exit, so the rest of the session reverts to stock OSD; reboot resets it | `support/zaparoo/alt_launcher.cpp:32,38-43,229-241` | +| 16 | **CI / build infrastructure** | Docker container build; binary named `MiSTer_Zaparoo`; "Z"-suffixed version; release / unstable CI; sync-upstream workflow; deploy script | `docker-build.sh`, `stable-build.sh`, `unstable-build.sh`, `deploy-zaparoo.sh`, `.github/build_*.sh`, `.github/workflows/*.yml` | +| 17 | **Build-time defaults flipped** | `cfg.recents` and `LOG_FILE_ENTRY` default to enabled in Zaparoo builds | `cfg.cpp` (defaults) | + +--- + +## 2. Inconsistencies and cleanup backlog + +These are intentional starting points for follow-up work, ordered roughly from +"30-minute cleanup" to "needs a design pass." + +### 2.1 Mixed namespace prefix in one module +`support/zaparoo/alt_launcher.h` uses two prefixes for the same conceptual thing: + +```c +alt_launcher_init / _shutdown / _toggle_crt / _native_crt / _active / _configured ... +zaparoo_is_native_core +zaparoo_alt_launcher_init_for_core / _for_menu +``` + +The `zaparoo_*` wrappers are the only ones called from `user_io.cpp`. Pick one +prefix (`zap_` is shortest) or split into two headers along that boundary. + +### 2.2 Three overlapping predicates +Gating across the codebase uses `_configured()` / `_active()` / `_native_crt()` +interchangeably without a documented rule. Current de-facto convention: + +| Predicate | Meaning | Used to gate | +|-----------|---------|--------------| +| `_configured` | binary file exists, sticky off after escape | **render paths** (System Settings body, MENU_SYSTEM1 entry, vga_nag, file-picker entry, "Launcher" row visibility, right-arrow gate) | +| `_active` | PID alive | **input handling** (F1/F9 disable, joypad-to-launcher routing, OSD overlay, menu auto-open suppression) | +| `_native_crt` | PID alive AND CRT mode on | **internal video state machine** (`disable_native_crt_path`, status timer) | + +The split is mostly principled but leaks at `MENU_SYSTEM1`, which uses +`_configured` for both the gate AND the body delegation while `MENU_NONE2` +auto-open uses `_active`. A user with the launcher binary present but not yet +running sees different OSD behavior than a user without the binary at all — +worth either a comment or a unified helper. + +### 2.3 Two persistence files, no shared format +Current state: + +``` +zaparoo_launcher_crt.bin 1 byte (CRT mode flag) +zaparoo_video_offsets.bin 2 bytes (h_offset, v_offset) +``` + +Same dir, same `FileSaveConfig` API, but no unified struct. If a third setting +arrives this becomes a per-feature file pattern. A single `zaparoo_state.bin` +with a versioned struct would scale better: + +```c +struct zaparoo_state_v1 { + uint8_t magic; // 'Z' + uint8_t version; // 1 + uint8_t crt; // 0 / 1 + int8_t h_offset; // -8..+7 + int8_t v_offset; // -8..+7 + uint8_t reserved[3]; +}; +``` + +Cost: a one-shot migration on existing installs (read legacy `crt.bin` if +new file is missing; never write the legacy file again). + +### 2.4 Hardcoded paths scattered across modules +- Launcher path: `zaparoo/launcher` in `alt_launcher.cpp:22` +- Menu RBF name(s): hardcoded in `support/zaparoo/menu_rbf.cpp` +- Persistence files: hardcoded in `alt_launcher.cpp` + +INI knobs were intentionally dropped (commit `72037bc`) in favor of +file-existence detection, but the resulting literals are now in three places. +A small `support/zaparoo/paths.h` (or a `paths.cpp` that resolves them at +startup) would centralize them without bringing INI knobs back. + +### 2.5 Status-bit map exists only in code +`status[9]` (CRT gate), `status[13:10]` (h_offset), `status[17:14]` (v_offset) +are agreed upon between this fork and `Menu_MiSTer/feat/dual-mode-native-fb`, +but the agreement is enforced only by lining up `user_io_status_set("[13:10]", …)` +against the SystemVerilog `status[13:10]`. Neither side has a comment +referencing the other. + +A short `STATUS.md` (or a header in `support/zaparoo/`) documenting the shared +register layout would prevent accidental conflicts when a future feature +allocates new bits. + +### 2.6 F-key handling comment is archaeology +F2 toggle was added (`0cea191`), moved to `user_io_kbd` (`6d2690b`), and both +were reverted (`390c141`, `4220a82`). What remains is a multi-line comment +block in `user_io.cpp:4162-4171` that reads like commit-history narration. + +It can shrink to one line: + +```c +// F12/KEY_MENU bypasses alt_launcher_active() so the user can open +// the OSD on top of a running launcher. +``` + +### 2.7 Trimmed-menu dispatcher has a dead branch +`alt_launcher_translate_system_select()` returns `-1` to signal "consumed +inline." That existed for the CRT row, which has now moved to the right page. + +The `if (dispatch < 0) { menustate = MENU_SYSTEM1; break; }` branch in +`MENU_SYSTEM2`'s switch is currently unreachable. Either drop it or note that +it's reserved for future inline-handled rows. + +### 2.8 `alt_launcher.cpp` is doing too many jobs +~600 lines covering: process lifecycle + video state machine + tty handling ++ status-bit pushers + offset persistence + cfg overrides. Splitting into +`alt_launcher_proc.cpp`, `alt_launcher_video.cpp`, `alt_launcher_state.cpp` +would make each concern testable and easier to audit. Not urgent — the file +is still readable — but the next feature will tip it over. + +### 2.9 Hard-override of user INI config is silent +`alt_launcher_cfg_apply()` quietly forces `fb_terminal=1` and `recents=1` +after `cfg_parse`. A user who set `fb_terminal=0` in `MiSTer.ini` gets no +warning that we're overriding them. + +At minimum, log it once at startup: + +```c +printf("alt_launcher: forcing fb_terminal=1, recents=1 (Zaparoo build)\n"); +``` + +### 2.10 Naming drift in the new menu page +The right-side page was committed as `MENU_ZAPAROO_DISPLAY1/2` with files +`display_menu.{cpp,h}`. Local in-progress refactor renames the menu states +to `MENU_ZAPAROO_VIDEO*` / `MENU_ZAPAROO_LAUNCHER*` and the file to +`launcher_pages.{cpp,h}`. After the rename settles, double-check that the +file name and the symbols inside agree on what the page is called. + +--- + +## 3. Boundary commit + +The "fork divergence" reference point used for this analysis is: + +``` +a2eb35eacdd7789abe3411e4d03381e7bf55309f +``` + +Use that as the base in `git log a2eb35e..HEAD` if you want to refresh this +document programmatically. diff --git a/cfg.cpp b/cfg.cpp index 112a7f1bd..8a5f13cce 100644 --- a/cfg.cpp +++ b/cfg.cpp @@ -139,7 +139,6 @@ static const ini_var_t ini_vars[] = { "SCREENSHOT_IMAGE_FORMAT", (void *)(&(cfg.screenshot_image_format)), STRING, 0, sizeof(cfg.screenshot_image_format) - 1 }, { "XBE2_SHIFT", (void*)(&(cfg.xbe2_shift)), UINT16, 0, 0x22F }, { "SPD_QUIRK", (void*)(&(cfg.spd_quirk)), UINT8, 0, 3 }, - { "ALT_LAUNCHER", (void*)(&(cfg.alt_launcher)), STRING, 0, sizeof(cfg.alt_launcher) - 1 }, }; static const int nvars = (int)(sizeof(ini_vars) / sizeof(ini_var_t)); @@ -580,7 +579,6 @@ const char* cfg_get_label(uint8_t alt) void cfg_parse() { memset(&cfg, 0, sizeof(cfg)); - alt_launcher_cfg_defaults(); cfg.csync = 1; cfg.bootscreen = 1; cfg.fb_terminal = 1; @@ -613,6 +611,8 @@ void cfg_parse() ini_parse(altcfg(), video_get_core_mode_name(0)); } + alt_launcher_cfg_apply(); + if (strlen(cfg.vga_mode)) { if (!strcasecmp(cfg.vga_mode, "rgb")) cfg.vga_mode_int = 0; diff --git a/cfg.h b/cfg.h index 6b5213d6c..9a14fe3bc 100644 --- a/cfg.h +++ b/cfg.h @@ -106,7 +106,6 @@ typedef struct { char screenshot_image_format[16]; uint16_t xbe2_shift; uint8_t spd_quirk; - char alt_launcher[1024]; } cfg_t; extern cfg_t cfg; diff --git a/file_io.cpp b/file_io.cpp index 58e9aea82..055e2b827 100644 --- a/file_io.cpp +++ b/file_io.cpp @@ -33,6 +33,7 @@ #include "scheduler.h" #include "video.h" #include "support.h" +#include "support/zaparoo/menu_rbf.h" #define MIN(a,b) (((a)<(b)) ? (a) : (b)) @@ -1127,7 +1128,7 @@ void setStorage(int dev) { device = 0; FileSave(CONFIG_DIR"/device.bin", &dev, sizeof(int)); - fpga_load_rbf("menu.rbf"); + fpga_load_rbf(menu_rbf_name()); } static int orig_device = 0; @@ -1693,7 +1694,7 @@ int ScanDirectory(char* path, int mode, const char *extension, int options, cons // skip hidden files if (!strncasecmp(de->d_name, ".", 1)) continue; //skip non-selectable files - if (!strcasecmp(de->d_name, "menu.rbf")) continue; + if (is_menu_rbf(de->d_name)) continue; if (!strncasecmp(de->d_name, "menu_20", 7)) continue; if (!strncasecmp(de->d_name, "boot", 4)) { diff --git a/fpga_io.cpp b/fpga_io.cpp index 1583883a6..83ff8736a 100644 --- a/fpga_io.cpp +++ b/fpga_io.cpp @@ -17,6 +17,7 @@ #include "shmem.h" #include "offload.h" #include "support/zaparoo/alt_launcher.h" +#include "support/zaparoo/menu_rbf.h" #include "fpga_base_addr_ac5.h" #include "fpga_manager.h" @@ -441,7 +442,7 @@ int fpga_load_rbf(const char *name, const char *cfg, const char *xml) printf("Loading RBF: %s\n", name); if(name[0] == '/') strcpy(path, name); - else sprintf(path, "%s/%s", !strcasecmp(name, "menu.rbf") ? getStorageDir(0) : getRootDir(), name); + else sprintf(path, "%s/%s", is_menu_rbf(name) ? getStorageDir(0) : getRootDir(), name); int rbf = open(path, O_RDONLY); if (rbf < 0) @@ -504,7 +505,7 @@ int fpga_load_rbf(const char *name, const char *cfg, const char *xml) } close(rbf); - app_restart(!strcasecmp(name, "menu.rbf") ? "menu.rbf" : path, xml); + app_restart(is_menu_rbf(name) ? menu_rbf_name() : path, xml); return ret; } diff --git a/input.cpp b/input.cpp index 28f70a050..d8866fa22 100644 --- a/input.cpp +++ b/input.cpp @@ -3564,7 +3564,7 @@ static void input_cb(struct input_event *ev, struct input_absinfo *absinfo, int if (osd_event == 2) joy_digital(input[dev].num, 0, 0, 0, BTN_OSD); } - if (user_io_osd_is_visible() || video_fb_state()) + if (user_io_osd_is_visible() || video_fb_state() || alt_launcher_active()) { if (ev->value <= 1) { diff --git a/menu.cpp b/menu.cpp index d5197652e..6ce4952ce 100644 --- a/menu.cpp +++ b/menu.cpp @@ -50,6 +50,9 @@ along with this program. If not, see . #include "hardware.h" #include "menu.h" #include "support/zaparoo/alt_launcher.h" +#include "support/zaparoo/alt_launcher_menu.h" +#include "support/zaparoo/launcher_pages.h" +#include "support/zaparoo/menu_rbf.h" #include "user_io.h" #include "debug.h" #include "fpga_io.h" @@ -83,6 +86,15 @@ enum MENU MENU_MISC1, MENU_MISC2, + // Right-side companion to System Settings on the alt-launcher menu core. + // Top "Zaparoo Launcher" page lists sub-pages (Video) and an exit row; + // the Video sub-page hosts CRT mode + H/V centering offsets and binds + // left/right arrows to value adjustment. + MENU_ZAPAROO_LAUNCHER1, + MENU_ZAPAROO_LAUNCHER2, + MENU_ZAPAROO_VIDEO1, + MENU_ZAPAROO_VIDEO2, + MENU_SELECT_INI1, MENU_SELECT_INI2, @@ -411,8 +423,8 @@ void SelectFile(const char* path, const char* pFileExt, int Options, unsigned ch if (Options & SCANO_CORES) { - strcpy(selPath, get_rbf_dir()); - if (strlen(get_rbf_name())) + strcpy(selPath, is_menu() ? "" : get_rbf_dir()); + if (!is_menu() && strlen(get_rbf_name())) { if(strlen(selPath)) strcat(selPath, "/"); strcat(selPath, get_rbf_name()); @@ -572,7 +584,7 @@ static uint32_t menu_key_get(void) else if (CheckTimer(repeat)) { repeat = GetTimer(REPEATRATE); - if (GetASCIIKey(c1) || menustate == MENU_FILE_SELECT2 || ((menustate == MENU_COMMON2) && (menusub == 17)) || ((menustate == MENU_SYSTEM2) && (menusub == 5))) + if (GetASCIIKey(c1) || menustate == MENU_FILE_SELECT2 || ((menustate == MENU_COMMON2) && (menusub == 17)) || ((menustate == MENU_SYSTEM2) && (menusub == 5)) || ((menustate == MENU_SYSTEM2) && alt_launcher_system_holding_reboot(menusub))) { c = c1; hold_cnt++; @@ -829,7 +841,11 @@ const char* get_rbf_name_bootcore(char *str) static void vga_nag() { - if (video_fb_state()) + // With an alt launcher configured the user has fb_terminal on by design + // and the CRT is fed directly by the menu core (snow pattern when status[9]=0, + // or the launcher's framebuffer when status[9]=1). The "fix MiSTer.ini" + // nag is not appropriate — just leave the OSD off without the warning. + if (video_fb_state() && !alt_launcher_configured()) { EnableOsd_on(OSD_VGA); OsdSetSize(16); @@ -1270,7 +1286,7 @@ void HandleUI(void) } //prevent OSD control while script is executing on framebuffer - if ((!video_fb_state() || video_chvt(0) != 2) && !select_ini) + if (((!video_fb_state() || video_chvt(0) != 2) || alt_launcher_active()) && !select_ini) { switch (c) { @@ -1285,11 +1301,14 @@ void HandleUI(void) if (!ignore_osd_release) menu = true; ignore_osd_release = false; - if(video_fb_state()) video_menu_bg(user_io_status_get("[3:1]")); - video_fb_enable(0); + if (!alt_launcher_active()) + { + if(video_fb_state()) video_menu_bg(user_io_status_get("[3:1]")); + video_fb_enable(0); + } break; case KEY_F1: - if (is_menu() && cfg.fb_terminal) + if (!alt_launcher_active() && is_menu() && cfg.fb_terminal) { user_io_status_set("[3:1]", user_io_status_get("[3:1]") + 1); user_io_status_save(user_io_create_config_name()); @@ -1312,7 +1331,7 @@ void HandleUI(void) break; case KEY_F9: - if ((is_menu() || ((get_key_mod() & (LALT | RALT)) && (get_key_mod() & (LCTRL | RCTRL))) || has_fb_terminal) && cfg.fb_terminal) + if (!alt_launcher_active() && (is_menu() || ((get_key_mod() & (LALT | RALT)) && (get_key_mod() & (LCTRL | RCTRL))) || has_fb_terminal) && cfg.fb_terminal) { video_chvt(1); video_fb_enable(!video_fb_state()); @@ -1376,7 +1395,6 @@ void HandleUI(void) break; } } - if (select_ini) { DISKLED_ON; @@ -1556,7 +1574,13 @@ void HandleUI(void) menustate = MENU_UNLOCK1; osd_code_entry[0] = 0; } - else if (menu || (is_menu() && !video_fb_state()) || (menustate == MENU_NONE2 && !mgl->done && mgl->state == 1)) + // On the menu core without an alt launcher, the menu *is* the screen — + // keep auto-opening the OSD whenever fb_terminal is off. With an alt + // launcher running, that rule would re-open System Settings the moment + // the user closes it (in CRT mode video_fb_state is false), so the OSD + // could never actually close. Suppress the auto-open in that case; + // explicit F12/MENU presses still open and close it normally. + else if (menu || (is_menu() && !video_fb_state() && !alt_launcher_active()) || (menustate == MENU_NONE2 && !mgl->done && mgl->state == 1)) { OsdSetSize(16); menusub = 0; @@ -1577,8 +1601,19 @@ void HandleUI(void) } else if (is_menu()) { - menusub = 6; - SelectFile("", 0, SCANO_CORES, MENU_CORE_FILE_SELECTED1, MENU_SYSTEM1); + if (alt_launcher_configured()) + { + // With an alt launcher, the file picker is not the user's + // natural entry point — they want the OSD overlay (System + // Settings) directly so they can flip CRT mode etc. + menustate = MENU_SYSTEM1; + menusub = 0; + } + else + { + menusub = 6; + SelectFile("", 0, SCANO_CORES, MENU_CORE_FILE_SELECTED1, MENU_SYSTEM1); + } } else if (is_minimig()) { @@ -3107,7 +3142,7 @@ void HandleUI(void) } } - if(!hold_cnt && reboot_req) fpga_load_rbf("menu.rbf"); + if(!hold_cnt && reboot_req) fpga_load_rbf(menu_rbf_name()); break; case MENU_VIDEOPROC1: @@ -6685,7 +6720,11 @@ void HandleUI(void) /* system menu */ /******************************************************************/ case MENU_SYSTEM1: - if (video_fb_state()) + // Without an alt launcher, the wallpaper / fb_terminal flow can't coexist + // with this menu — bail out so vga_nag can show the MiSTer.ini warning. + // With an alt launcher the OSD overlay is exactly what we want on top of + // the running launcher, so let it render through. + if (video_fb_state() && !alt_launcher_configured()) { menustate = MENU_NONE1; break; @@ -6695,6 +6734,15 @@ void HandleUI(void) helptext_idx = 0; parentstate = menustate; + // alt-launcher mode delegates the entire System menu render to a + // support helper so this upstream block stays untouched. + if (alt_launcher_configured()) + { + cr = alt_launcher_render_system_menu(menusub, &menumask, &reboot_req, &sysinfo_timer); + menustate = MENU_SYSTEM2; + break; + } + m = 0; OsdSetTitle("System Settings", OSD_ARROW_LEFT); menumask = 0x7F; @@ -6765,12 +6813,21 @@ void HandleUI(void) case MENU_SYSTEM2: if (menu) { + if (alt_launcher_configured()) + { + // F12 toggles: the OSD opened directly into System Settings, + // pressing F12 again closes it instead of opening the core picker. + menustate = MENU_NONE1; + break; + } SelectFile("", 0, SCANO_CORES, MENU_CORE_FILE_SELECTED1, MENU_SYSTEM1); break; } else if (select) { - switch (menusub) + int dispatch = alt_launcher_translate_system_select(menusub); + if (dispatch < 0) { menustate = MENU_SYSTEM1; break; } + switch (dispatch) { case 0: if (getStorage(1) || isUSBMounted()) setStorage(!getStorage(1)); @@ -6841,8 +6898,98 @@ void HandleUI(void) { menustate = MENU_MISC1; } + else if (right && alt_launcher_configured()) + { + menustate = MENU_ZAPAROO_LAUNCHER1; + menusub = 0; + } + + if (!hold_cnt && reboot_req) fpga_load_rbf(menu_rbf_name()); + break; - if (!hold_cnt && reboot_req) fpga_load_rbf("menu.rbf"); + /******************************************************************/ + /* zaparoo launcher pages (right-side sibling of System) */ + /******************************************************************/ + case MENU_ZAPAROO_LAUNCHER1: + if (!alt_launcher_configured()) + { + menustate = MENU_NONE1; + break; + } + helptext_idx = 0; + parentstate = menustate; + launcher_page_render(menusub, &menumask); + menustate = MENU_ZAPAROO_LAUNCHER2; + break; + + case MENU_ZAPAROO_LAUNCHER2: + if (menu) + { + menustate = MENU_NONE1; + break; + } + if (left) + { + menustate = MENU_SYSTEM1; + menusub = 0; + break; + } + if (right && menusub == 0) + { + menustate = MENU_ZAPAROO_VIDEO1; + menusub = 0; + break; + } + if (select) + { + int act = launcher_page_handle_select(menusub); + if (act == 1) + { + menustate = MENU_ZAPAROO_VIDEO1; + menusub = 0; + } + else if (act == 0) + { + menustate = MENU_NONE1; + } + } + break; + + case MENU_ZAPAROO_VIDEO1: + if (!alt_launcher_configured()) + { + menustate = MENU_NONE1; + break; + } + helptext_idx = 0; + parentstate = menustate; + video_page_render(menusub, &menumask); + menustate = MENU_ZAPAROO_VIDEO2; + break; + + case MENU_ZAPAROO_VIDEO2: + if (menu) + { + menustate = MENU_ZAPAROO_LAUNCHER1; + menusub = 0; + break; + } + if (left || right || plus || minus) + { + video_page_adjust(menusub, (right || plus) ? +1 : -1); + menustate = MENU_ZAPAROO_VIDEO1; + break; + } + if (select) + { + if (!video_page_handle_select(menusub)) + { + menustate = MENU_ZAPAROO_LAUNCHER1; + menusub = 0; + break; + } + menustate = MENU_ZAPAROO_VIDEO1; + } break; case MENU_JOYSYSMAP: diff --git a/stable-build.sh b/stable-build.sh index 4e15e0f9d..2cd076cc4 100755 --- a/stable-build.sh +++ b/stable-build.sh @@ -54,7 +54,6 @@ fi STABLE_NAME=$(basename "${STABLE_FILE}") STABLE_DATE=${STABLE_NAME#MiSTer_} -FORK_COMMITS=$(git rev-list --reverse --no-merges "${UPSTREAM_REF}..${FORK_HEAD}") if [ -n "${METADATA_FILE}" ]; then cat >"${METADATA_FILE}" < Building from upstream ${STABLE_NAME} with Zaparoo ${FORK_SHORT_SHA}" git worktree add --detach "${TMP_WORKTREE}" "${STABLE_COMMIT}" >/dev/null -if [ -n "${FORK_COMMITS}" ]; then - git -C "${TMP_WORKTREE}" cherry-pick --no-commit ${FORK_COMMITS} +# Apply the cumulative fork-only diff (relative to upstream master) so revert +# pairs and other intra-fork conflicts cancel out — replaying commit-by-commit +# would re-expose them. -3 falls back to a 3-way merge when stable's content +# for a shared file has drifted from upstream master. MiSTer.ini is excluded: +# the fork's only change is an uncomment of a default-valued line, and stable's +# example ini drifts often enough to cause spurious conflicts. +FORK_DIFF=$(git diff --binary "${UPSTREAM_REF}..${FORK_HEAD}" -- . ':(exclude)MiSTer.ini') +if [ -n "${FORK_DIFF}" ]; then + printf '%s\n' "${FORK_DIFF}" | git -C "${TMP_WORKTREE}" apply -3 --index fi "${TMP_WORKTREE}/docker-build.sh" "$@" diff --git a/support/zaparoo/alt_launcher.cpp b/support/zaparoo/alt_launcher.cpp index 0c5554541..e356dc1ae 100644 --- a/support/zaparoo/alt_launcher.cpp +++ b/support/zaparoo/alt_launcher.cpp @@ -9,27 +9,43 @@ #include #include #include +#include #include #include "cfg.h" #include "file_io.h" #include "hardware.h" #include "input.h" +#include "menu.h" +#include "shmem.h" #include "user_io.h" #include "video.h" -void alt_launcher_cfg_defaults(void) +static const char s_launcher_path[] = "zaparoo/launcher"; + +void alt_launcher_cfg_apply(void) { + // Override any user ini values: this fork is single-purpose and the + // launcher needs both flags on to render. + cfg.fb_terminal = 1; cfg.recents = 1; } +static bool s_escaped = false; + bool alt_launcher_configured(void) { - return cfg.alt_launcher[0] && cfg.fb_terminal; + // After a clean exit / give-up, masquerade as not-configured so the rest + // of the OSD reverts to stock menu behavior for the rest of this session. + // Reboot re-execs MiSTer and resets this back to the file-existence cache. + if (s_escaped) return false; + static int cached = -1; + if (cached < 0) cached = FileExists(s_launcher_path, 0) ? 1 : 0; + return cached != 0; } uint16_t alt_launcher_fb_terminal_key(uint32_t mask, bool osd_button) { - if (!cfg.alt_launcher[0]) + if (!alt_launcher_configured()) return 0; if (osd_button) @@ -58,6 +74,45 @@ static const int s_vt = 2; static const char s_tty[] = "tty2"; static const char s_tty_path[] = "/dev/tty2"; static const char s_fb_mode_path[] = "/sys/module/MiSTer_fb/parameters/mode"; +static const char s_crt_state_file[] = "zaparoo_launcher_crt.bin"; +static const char s_offsets_file[] = "zaparoo_video_offsets.bin"; + +static int8_t s_h_offset = 0; +static int8_t s_v_offset = 0; + +static int8_t clamp_offset(int8_t v) +{ + if (v < -8) return -8; + if (v > 7) return 7; + return v; +} + +static bool load_persisted_native_crt(void) +{ + uint8_t v = 0; + FileLoadConfig(s_crt_state_file, &v, sizeof(v)); + return v != 0; +} + +static void save_persisted_native_crt(bool crt) +{ + uint8_t v = crt ? 1 : 0; + FileSaveConfig(s_crt_state_file, &v, sizeof(v)); +} + +static void load_persisted_offsets(void) +{ + int8_t buf[2] = { 0, 0 }; + FileLoadConfig(s_offsets_file, buf, sizeof(buf)); + s_h_offset = clamp_offset(buf[0]); + s_v_offset = clamp_offset(buf[1]); +} + +static void save_persisted_offsets(void) +{ + int8_t buf[2] = { s_h_offset, s_v_offset }; + FileSaveConfig(s_offsets_file, buf, sizeof(buf)); +} static void set_launcher_fb_mode(int fmt, int rb, int width, int height, int stride, bool log = true) { @@ -79,6 +134,27 @@ static void set_native_crt_fb_mode(bool log = true) set_launcher_fb_mode(8888, 1, 320, 240, 1280, log); } +static void blank_native_crt_fb(void) +{ + // CRT path doesn't read /dev/fb0 (kernel FB at 0x22000000). The launcher + // runs a worker that copies the top-left 320x240 from /dev/fb0 into a + // separate FPGA-mapped region at 0x3A000000 (control word + two 320x240 + // RGBA buffers). The FPGA scans out from that region. Nothing zeros it + // across launcher restarts or software reboots, so the previous session's + // last frame ghosts in until the launcher's writer thread starts. + const uint32_t native_addr = 0x3A000000u; + const uint32_t native_size = 0x000A0000u; + void *p = shmem_map(native_addr, native_size); + if (!p) + { + printf("alt_launcher: blank native shmem_map(0x%x, %u) failed\n", native_addr, native_size); + return; + } + memset(p, 0, native_size); + shmem_unmap(p, native_size); + printf("alt_launcher: blanked %u bytes of CRT native video DDR at 0x%x\n", native_size, native_addr); +} + static void clear_launcher_tty(void) { int tty_fd = open(s_tty_path, O_WRONLY | O_CLOEXEC); @@ -148,7 +224,17 @@ static void enable_native_crt_path(void) { set_vga_fb(0); video_fb_enable(0); + + // Double-write with a settle window so the kernel module's 320x240 layout + // is live before status[9] flips. Without this, the launcher renders for + // up to a second under stale dims (the post-fork retry timer used to be + // what eventually fixed the picture). + set_native_crt_fb_mode(false); + usleep(100000); set_native_crt_fb_mode(); + + blank_native_crt_fb(); + user_io_status_set("[9]", 1); s_native_status_timer = GetTimer(500); if (!s_native_status_timer) s_native_status_timer = 1; @@ -177,6 +263,7 @@ static void return_to_normal_mode(void) s_respawn_timer = 0; s_crash_count = 0; s_gave_up = true; + s_escaped = true; } static void reset_launcher_state(void) @@ -186,6 +273,7 @@ static void reset_launcher_state(void) s_crash_count = 0; s_gave_up = false; s_init_pending = false; + s_escaped = false; } static void kill_launcher(pid_t pid, int sig) @@ -197,7 +285,7 @@ static void kill_launcher(pid_t pid, int sig) static void spawn(void) { char path[2100]; - strncpy(path, getFullPath(cfg.alt_launcher), sizeof(path) - 1); + strncpy(path, getFullPath(s_launcher_path), sizeof(path) - 1); path[sizeof(path) - 1] = '\0'; static const char cmd[] = @@ -262,6 +350,11 @@ static void spawn(void) input_switch(0); user_io_status_set("[9]", 1); } + + // The launcher grabs input as soon as it starts. If the OSD is still + // up (e.g. user toggled CRT mode or hit Reboot from System Settings), + // it would trap input with no way to dismiss it — drop it now. + if (menu_present()) MenuHide(); } bool alt_launcher_active(void) @@ -269,9 +362,55 @@ bool alt_launcher_active(void) return s_pid != 0; } +bool alt_launcher_native_crt(void) +{ + return s_native_crt && s_pid != 0; +} + +int8_t alt_launcher_h_offset(void) +{ + return s_h_offset; +} + +int8_t alt_launcher_v_offset(void) +{ + return s_v_offset; +} + +void alt_launcher_set_h_offset(int8_t v) +{ + s_h_offset = clamp_offset(v); + save_persisted_offsets(); + // 4-bit two's-complement bit pattern; FPGA reinterprets as signed -8..+7. + user_io_status_set("[13:10]", (uint32_t)((uint8_t)s_h_offset & 0xF)); +} + +void alt_launcher_set_v_offset(int8_t v) +{ + s_v_offset = clamp_offset(v); + save_persisted_offsets(); + user_io_status_set("[17:14]", (uint32_t)((uint8_t)s_v_offset & 0xF)); +} + +void alt_launcher_toggle_crt(void) +{ + bool current_crt = alt_launcher_native_crt(); + bool target_crt = !current_crt; + + save_persisted_native_crt(target_crt); + + printf("alt_launcher: toggle CRT path %d -> %d\n", current_crt, target_crt); + + // Shutdown drops status[9], releases the FB mode and restores HPS framebuffer + // state regardless of whether the launcher was running. After it returns we + // always have a clean slate to spawn the next launcher invocation. + alt_launcher_shutdown(); + alt_launcher_init(target_crt); +} + void alt_launcher_init(bool native_crt) { - if (!cfg.alt_launcher[0] || !cfg.fb_terminal || s_pid || s_gave_up) + if (!alt_launcher_configured() || s_pid || s_gave_up) return; s_crash_count = 0; s_respawn_timer = 0; @@ -306,6 +445,18 @@ void alt_launcher_poll(void) int sig = WIFSIGNALED(status) ? WTERMSIG(status) : 0; bool escaped = (exited && exit_status == 0) || sig == SIGTERM || sig == SIGINT; bool crashed = !escaped && (sig != 0 || (exited && exit_status != 0)); + // Any exit while in CRT mode drops back to HDMI / no launcher + // for the rest of this session — respawning into CRT after the + // user already left it is a UX trap. The persisted CRT + // preference is intentionally untouched (return_to_normal_mode + // only clears the in-memory s_native_crt), so the next reboot + // honors whatever the user last set in System Settings. + if (s_native_crt) + { + printf("alt_launcher: exited while in CRT mode, dropping to HDMI normal mode\n"); + return_to_normal_mode(); + return; + } if (escaped) { printf("alt_launcher: exited, returning to normal mode until restart\n"); @@ -326,7 +477,7 @@ void alt_launcher_poll(void) return; } - if (!cfg.alt_launcher[0] || !cfg.fb_terminal) + if (!alt_launcher_configured()) return; if (s_init_pending) @@ -403,10 +554,22 @@ bool zaparoo_is_native_core(void) void zaparoo_alt_launcher_init_for_core(void) { - if (cfg.alt_launcher[0] && cfg.fb_terminal && zaparoo_is_native_core()) + if (alt_launcher_configured() && zaparoo_is_native_core()) { printf("alt_launcher: initializing CRT mode for core '%s' '%s'\n", user_io_get_core_name(1), user_io_get_core_name(0)); alt_launcher_init(true); } } + +void zaparoo_alt_launcher_init_for_menu(void) +{ + bool crt = load_persisted_native_crt(); + load_persisted_offsets(); + printf("alt_launcher: initializing menu launcher (persisted crt=%d, h=%d, v=%d)\n", + crt, s_h_offset, s_v_offset); + // Push the persisted offsets to the FPGA now that the menu RBF is loaded. + user_io_status_set("[13:10]", (uint32_t)((uint8_t)s_h_offset & 0xF)); + user_io_status_set("[17:14]", (uint32_t)((uint8_t)s_v_offset & 0xF)); + alt_launcher_init(crt); +} diff --git a/support/zaparoo/alt_launcher.h b/support/zaparoo/alt_launcher.h index 3ce4ba959..0861fb281 100644 --- a/support/zaparoo/alt_launcher.h +++ b/support/zaparoo/alt_launcher.h @@ -2,16 +2,27 @@ #include -#define ALT_LAUNCHER_MENUSUB 31 +#define ALT_LAUNCHER_MENUSUB 31 -void alt_launcher_init(bool native_crt = false); +void alt_launcher_init(bool native_crt); void alt_launcher_poll(void); void alt_launcher_shutdown(void); +void alt_launcher_toggle_crt(void); +bool alt_launcher_native_crt(void); bool alt_launcher_active(void); bool alt_launcher_configured(void); -void alt_launcher_cfg_defaults(void); +// Display centering: signed offsets clamped to -8..+7. Setters update the +// in-memory cache, persist to the config dir, and push to the FPGA via +// user_io_status_set so the change takes effect immediately. +int8_t alt_launcher_h_offset(void); +int8_t alt_launcher_v_offset(void); +void alt_launcher_set_h_offset(int8_t v); +void alt_launcher_set_v_offset(int8_t v); + +void alt_launcher_cfg_apply(void); uint16_t alt_launcher_fb_terminal_key(uint32_t mask, bool osd_button); bool zaparoo_is_native_core(void); void zaparoo_alt_launcher_init_for_core(void); +void zaparoo_alt_launcher_init_for_menu(void); diff --git a/support/zaparoo/alt_launcher_menu.cpp b/support/zaparoo/alt_launcher_menu.cpp new file mode 100644 index 000000000..e0bb50dc2 --- /dev/null +++ b/support/zaparoo/alt_launcher_menu.cpp @@ -0,0 +1,94 @@ +#include "alt_launcher_menu.h" +#include "alt_launcher.h" +#include "file_io.h" +#include "osd.h" + +#include +#include +#include + +extern const char *version; + +// The OSD column used for the Exit row label, sized to match +// menu.cpp's STD_EXIT define (a local #define there, kept in sync +// here rather than re-exported to avoid a header touch). +#define ALT_STD_EXIT " exit" + +int alt_launcher_render_system_menu(int menusub, uint64_t *menumask, + int *reboot_req, + long *sysinfo_timer) +{ + if (!alt_launcher_configured()) return 0; + + char s[256]; + int m = 0; + + // Right arrow indicates a sibling page (Zaparoo Launcher) accessible + // via the right-arrow key — see MENU_ZAPAROO_LAUNCHER1 in menu.cpp. + OsdSetTitle("System Settings", OSD_ARROW_LEFT | OSD_ARROW_RIGHT); + *menumask = 0x1F; + + OsdWrite(m++); + sprintf(s, " MiSTer v%s", version + 5); + { + char str[8] = {}; + FILE *f = fopen("/MiSTer.version", "r"); + if (f) + { + if (fread(str, 6, 1, f)) sprintf(s, " MiSTer v%s, OS v%s", version + 5, str); + fclose(f); + } + } + OsdWrite(m++, s); + + { + uint64_t avail = 0; + struct statvfs buf; + memset(&buf, 0, sizeof(buf)); + if (!statvfs(getRootDir(), &buf)) avail = buf.f_bsize * buf.f_bavail; + if (avail < (10ull * 1024 * 1024 * 1024)) + sprintf(s, " Available space: %llumb", (unsigned long long)(avail / (1024 * 1024))); + else + sprintf(s, " Available space: %llugb", (unsigned long long)(avail / (1024 * 1024 * 1024))); + OsdWrite(m + 2, s, 0, 0); + } + + OsdWrite(m++, ""); + OsdWrite(m++, ""); + m++; + OsdWrite(m++, ""); + OsdWrite(m++, ""); + + OsdWrite(m++, " Remap keyboard \x16", menusub == 0); + OsdWrite(m++, " Define joystick buttons \x16", menusub == 1); + OsdWrite(m++, " Scripts \x16", menusub == 2); + + OsdWrite(m++, ""); + int cr = m; + OsdWrite(m++, " Reboot (hold \x16 cold reboot)", menusub == 3); + *sysinfo_timer = 0; + *reboot_req = 0; + + while (m < OsdGetSize() - 1) OsdWrite(m++, ""); + OsdWrite(15, ALT_STD_EXIT, menusub == 4); + + return cr; +} + +int alt_launcher_translate_system_select(int menusub) +{ + if (!alt_launcher_configured()) return menusub; + + // Maps trimmed-menu menusub to upstream MENU_SYSTEM2 dispatch index: + // 0 Remap -> 1, 1 Define joy -> 2, 2 Scripts -> 3, 3 Reboot -> 5, + // 4 Exit -> 6. CRT mode lives on the Zaparoo Launcher's Video + // sub-page now, not here. + static const int map[] = { 1, 2, 3, 5, 6 }; + if (menusub < 0 || menusub >= (int)(sizeof(map) / sizeof(map[0]))) return -1; + return map[menusub]; +} + +bool alt_launcher_system_holding_reboot(int menusub) +{ + return alt_launcher_configured() && menusub == 3; +} diff --git a/support/zaparoo/alt_launcher_menu.h b/support/zaparoo/alt_launcher_menu.h new file mode 100644 index 000000000..797a9e194 --- /dev/null +++ b/support/zaparoo/alt_launcher_menu.h @@ -0,0 +1,30 @@ +#pragma once + +#include +#include + +// Trimmed System Settings menu for alt-launcher mode. The launcher is +// the only "core" running on top of menu.rbf, so storage toggle and +// the bundled help PDF are irrelevant — this layout hides them and +// exposes Remap(0), Define joy(1), Scripts(2), Reboot(3), Exit(4). +// CRT mode lives on the Zaparoo Launcher's Video sub-page. +// +// All three helpers are no-ops / return safe defaults when +// alt_launcher_configured() is false, so menu.cpp's hook sites can +// be unconditional. Caller must have already set OsdSetSize(16); +// returns the row of the Reboot line so the cold-reboot animation +// in MENU_SYSTEM2 can target the right OSD slot. +int alt_launcher_render_system_menu(int menusub, uint64_t *menumask, + int *reboot_req, + long *sysinfo_timer); + +// Translate a select press on the trimmed menu to its upstream +// MENU_SYSTEM2 switch case index. Returns -1 when the press is +// consumed inline (CRT toggle); the caller should re-render +// MENU_SYSTEM1 and skip the dispatch. +int alt_launcher_translate_system_select(int menusub); + +// menu.cpp's hold-to-cold-reboot key-repeat gate hard-codes +// menusub==5 (the upstream Reboot row). The trimmed menu places +// Reboot at menusub 3, so menu.cpp ORs in this helper. +bool alt_launcher_system_holding_reboot(int menusub); diff --git a/support/zaparoo/launcher_pages.cpp b/support/zaparoo/launcher_pages.cpp new file mode 100644 index 000000000..9bf2a31e5 --- /dev/null +++ b/support/zaparoo/launcher_pages.cpp @@ -0,0 +1,101 @@ +#include "launcher_pages.h" +#include "alt_launcher.h" +#include "osd.h" + +#include + +// Mirrors menu.cpp's STD_EXIT (a local #define there, kept in sync +// here rather than re-exporting it via a header touch). +#define LAUNCHER_STD_EXIT " exit" + +void launcher_page_render(int menusub, uint64_t *menumask) +{ + OsdSetSize(16); + OsdSetTitle("Zaparoo Launcher", OSD_ARROW_LEFT); + *menumask = 0x3; // Video, Exit + + int m = 0; + OsdWrite(m++); + OsdWrite(m++, ""); + OsdWrite(m++, ""); + OsdWrite(m++, ""); + OsdWrite(m++, ""); + + OsdWrite(m++, " Video \x16", menusub == 0); + + while (m < OsdGetSize() - 1) OsdWrite(m++, ""); + OsdWrite(15, LAUNCHER_STD_EXIT, menusub == 1); +} + +int launcher_page_handle_select(int menusub) +{ + switch (menusub) + { + case 0: return 1; + case 1: return 0; + default: return -1; + } +} + +void video_page_render(int menusub, uint64_t *menumask) +{ + OsdSetSize(16); + // No arrow flags: left/right are bound to value adjustment on this + // page, not sibling navigation. + OsdSetTitle("Video", 0); + *menumask = 0xF; // CRT, H Offset, V Offset, Exit + + char s[64]; + int m = 0; + + OsdWrite(m++); + OsdWrite(m++, ""); + OsdWrite(m++, ""); + + sprintf(s, " CRT mode: %-15s", alt_launcher_native_crt() ? "On" : "Off"); + OsdWrite(m++, s, menusub == 0); + + OsdWrite(m++, ""); + sprintf(s, " H Offset: %+3d", alt_launcher_h_offset()); + OsdWrite(m++, s, menusub == 1); + + sprintf(s, " V Offset: %+3d", alt_launcher_v_offset()); + OsdWrite(m++, s, menusub == 2); + + while (m < OsdGetSize() - 1) OsdWrite(m++, ""); + OsdWrite(15, LAUNCHER_STD_EXIT, menusub == 3); +} + +bool video_page_handle_select(int menusub) +{ + switch (menusub) + { + case 0: + alt_launcher_toggle_crt(); + return true; + case 3: + return false; + default: + return true; + } +} + +void video_page_adjust(int menusub, int dir) +{ + if (dir == 0) return; + int8_t step = (int8_t)(dir > 0 ? 1 : -1); + switch (menusub) + { + case 0: + alt_launcher_toggle_crt(); + break; + case 1: + alt_launcher_set_h_offset((int8_t)(alt_launcher_h_offset() + step)); + break; + case 2: + alt_launcher_set_v_offset((int8_t)(alt_launcher_v_offset() + step)); + break; + default: + break; + } +} diff --git a/support/zaparoo/launcher_pages.h b/support/zaparoo/launcher_pages.h new file mode 100644 index 000000000..0f7b1afdd --- /dev/null +++ b/support/zaparoo/launcher_pages.h @@ -0,0 +1,40 @@ +#pragma once + +#include +#include + +// Right-side companion to the trimmed System Settings menu, reachable +// via the right arrow when alt_launcher_configured() is true. This file +// hosts both pages of that companion: the top-level "Zaparoo Launcher" +// page and the nested "Video" sub-page that owns the CRT mode toggle +// plus the H/V centering offsets. +// +// The Video page binds left/right arrows to value adjustment instead of +// sibling navigation, which is why it lives behind a sub-page rather +// than directly under System Settings. + +// Top "Zaparoo Launcher" page. menusub layout: 0 = Video, 1 = Exit. +void launcher_page_render(int menusub, uint64_t *menumask); + +// Translates a select press on the Launcher page. +// 1 -> entered Video sub-page +// 0 -> Exit pressed (close OSD) +// -1 -> no-op +int launcher_page_handle_select(int menusub); + +// Video sub-page. menusub layout: 0 = CRT mode, 1 = H Offset, +// 2 = V Offset, 3 = Exit. +// +// The OSD is closed automatically by the launcher spawn path (see +// MenuHide() in spawn() in alt_launcher.cpp) when CRT toggling +// triggers a respawn — these helpers don't need to signal that. +void video_page_render(int menusub, uint64_t *menumask); + +// Returns true if the press was consumed (re-render only); false when +// Exit was selected (caller pops back to the Launcher page). +bool video_page_handle_select(int menusub); + +// Adjust the highlighted row by `dir` (-1 / +1). Toggles CRT mode on +// the CRT row regardless of sign; ±1 on the H/V offset rows; no-op +// elsewhere. +void video_page_adjust(int menusub, int dir); diff --git a/support/zaparoo/menu_rbf.cpp b/support/zaparoo/menu_rbf.cpp new file mode 100644 index 000000000..80db5af2a --- /dev/null +++ b/support/zaparoo/menu_rbf.cpp @@ -0,0 +1,21 @@ +#include "menu_rbf.h" +#include +#include + +static const char s_menu_rbf_path[] = "zaparoo/menu_zaparoo.rbf"; + +const char *menu_rbf_name(void) +{ + return s_menu_rbf_path; +} + +bool is_menu_rbf(const char *name) +{ + if (!name || !name[0]) return false; + if (!strcasecmp(name, "menu.rbf")) return true; + if (!strcasecmp(name, s_menu_rbf_path)) return true; + const char *base = strrchr(s_menu_rbf_path, '/'); + base = base ? base + 1 : s_menu_rbf_path; + if (base[0] && !strcasecmp(name, base)) return true; + return false; +} diff --git a/support/zaparoo/menu_rbf.h b/support/zaparoo/menu_rbf.h new file mode 100644 index 000000000..72da01dba --- /dev/null +++ b/support/zaparoo/menu_rbf.h @@ -0,0 +1,4 @@ +#pragma once + +const char *menu_rbf_name(void); +bool is_menu_rbf(const char *name); diff --git a/user_io.cpp b/user_io.cpp index 3c136bf5f..76169251b 100644 --- a/user_io.cpp +++ b/user_io.cpp @@ -41,6 +41,7 @@ #include "scaler.h" #include "support.h" #include "support/zaparoo/alt_launcher.h" +#include "support/zaparoo/menu_rbf.h" static char core_path[1024] = {}; static char rbf_path[1024] = {}; @@ -1454,6 +1455,10 @@ void user_io_init(const char *path, const char *xml) app_restart(path, xml, main); } + // Zaparoo: u-boot/stock binary may have loaded the system menu.rbf before we got here. + // Force a reload of our hardcoded menu RBF if we booted without an explicit RBF path. + if (is_menu() && !rbf_path[0]) fpga_load_rbf(menu_rbf_name()); + uint8_t hotswap[4] = {}; ide_reset(hotswap); @@ -1528,7 +1533,7 @@ void user_io_init(const char *path, const char *xml) else if (is_menu()) { user_io_status_set("[4]", (cfg.menu_pal) ? 1 : 0); - if (cfg.alt_launcher[0] && cfg.fb_terminal) alt_launcher_init(); + if (alt_launcher_configured()) zaparoo_alt_launcher_init_for_menu(); else if (cfg.fb_terminal) video_menu_bg(user_io_status_get("[3:1]")); else user_io_status_set("[3:1]", 0); @@ -3032,7 +3037,7 @@ void user_io_set_ini(int ini_num) if (!name[0]) { - name = "menu.rbf"; + name = menu_rbf_name(); xml = NULL; } @@ -3709,7 +3714,7 @@ void user_io_poll() if (!coldreset_req && prev_coldreset_req) { - fpga_load_rbf("menu.rbf"); + fpga_load_rbf(menu_rbf_name()); } prev_coldreset_req = coldreset_req; @@ -4157,8 +4162,12 @@ void user_io_kbd(uint16_t key, int press) if (key) { uint32_t code = get_ps2_code(key); - if (alt_launcher_active() && (key == KEY_MENU || key == KEY_F12)) - return; + // Both F12 and KEY_MENU now reach the normal menu/OSD flow even + // while the alt launcher is running, so the user can open the OSD + // overlay (System Settings) on top of their launcher app from + // either keyboard or joypad MENU button. Input grabbing flips + // automatically when the OSD opens (user_io_osd_key_enable -> + // input_switch -> EVIOCGRAB). bool is_menu_event = ((has_menu() || osd_is_visible || (get_key_mod() & (LALT | RALT | RGUI | LGUI))) && (((key == KEY_F12) && (!is_f12_mod_needed() || (get_key_mod() & (RGUI | LGUI)))) || key == KEY_MENU)); if (!press) {