From 7e07d9e018e6b7ca7036a53665df4dee70d2b58a Mon Sep 17 00:00:00 2001 From: James Dansereau <106631954+jkdansereau@users.noreply.github.com> Date: Sun, 27 Sep 2026 12:18:36 -0400 Subject: [PATCH] D347: F10 overlay controller value-adjust + hold-to-repeat D-pad left/right and stick-X now adjust the selected row's value (A/X/B/Y kept); 18/4-frame hold-to-repeat added to value adjust and up/down nav, matching the options screen's D345(e)/(f) cadence. Port-only; user controller test verified. --- docs/dev/findings-index.csv | 1 + docs/dev/findings.md | 30 +++++++++++++++++++++ port/src/optionsoverlay.c | 53 ++++++++++++++++++++++++++++++++----- 3 files changed, 78 insertions(+), 6 deletions(-) diff --git a/docs/dev/findings-index.csv b/docs/dev/findings-index.csv index 76469084..19f4a258 100644 --- a/docs/dev/findings-index.csv +++ b/docs/dev/findings-index.csv @@ -224,3 +224,4 @@ D343,PARTIAL,"File select: PC ""Options"" label opens the settings overlay (v0.4 D344,FIXED,Host PC bugchecks (BSOD 0x119 VIDEO_SCHEDULER_INTERNAL_ERROR) during game test runs; probable cause:,"FIXED (probable cause; orderly quit + GE_QUITFRAME), headless + user-verified; no dump, watch for recurrence." D345,FIXED,"Front-end crosshair snaps away from the pointer on screen/page changes (file select → mode select setCursorPOSforMode(0), front.c:2516; every MENU_PC_OPTIONS p…","Port-only, user-verified (parts a–g):" D346,PARTIAL,PC-settings wording pass:,Port-only display change landed (optionsoverlay.c rows[]/valueText + one colour match in frontoptions.c); both UIs (F10 overlay + options screen) share the table. +D347,PARTIAL,F10 overlay controller:,Port-only fix landed (optionsoverlay.c): diff --git a/docs/dev/findings.md b/docs/dev/findings.md index 5045a3bd..a75c2531 100644 --- a/docs/dev/findings.md +++ b/docs/dev/findings.md @@ -632,6 +632,7 @@ covers D24–D69; the log continues in §H (D32 procedure, D70–D121). | D344 | **Host PC bugchecks (BSOD 0x119 VIDEO_SCHEDULER_INTERNAL_ERROR) during game test runs; probable cause: quit called exit() while the render thread could be inside the GL driver (2026-09-27).** — full `## D344` entry at file tail | FIXED (probable cause; orderly quit + GE_QUITFRAME), headless + user-verified; no dump, watch for recurrence. | | D345 | **Front-end crosshair snaps away from the pointer on screen/page changes (file select → mode select `setCursorPOSforMode(0)`, front.c:2516; every `MENU_PC_OPTIONS` page hop), and the options screen's keyboard navigation was missing/mis-modelled (2026-09-27).** — full `## D345` entry at file tail | Port-only, user-verified (parts a–g): (a) `input.c` re-assert window on menu change; (b) `cursorToItem` skips its snap while the pointer is live; (d) W/S emit stick in menus under NaturalPitch; (e/f) F10-style discrete row stepping + arrow left/right value adjust replace raw drift (first pass shipped broken, fixed); (g) screen-placed cursor parks in the left gutter, off the row text. Game-side one-liner (pad users) parked pending Rule-2 sign-off. | | D346 | **PC-settings wording pass: option labels/values aligned to Nightdive/Turok + PD-port conventions (title-case On/Off, "Frame rate cap", "Anti-aliasing"/"None", units in values not labels, self-explanatory auto-FOV toggles); VIEW section merged into VIDEO (2026-09-27).** — full `## D346` entry at file tail | Port-only display change landed (`optionsoverlay.c` rows[]/valueText + one colour match in `frontoptions.c`); both UIs (F10 overlay + options screen) share the table. Config keys/values untouched. Deadzone % display uses raw-step grid (cosmetic follow-up noted). | +| D347 | **F10 overlay controller: left/right did nothing (only A/Y adjusted values) and no input had hold-to-repeat (2026-09-27).** — full `## D347` entry at file tail | Port-only fix landed (`optionsoverlay.c`): D-pad left/right + stick-X wired into value adjust (A/X/B/Y kept); 18/4-frame hold-to-repeat on adjust and up/down nav, matching the options screen's D345(e)/(f) cadence. Real-device controller retest owed. | Phase 2 replaced the Phase-1 demo loop with the real `mainproc()` on real OS threads, compiled GE's real `src/sched.c`, and brought in PD's fast3d software @@ -13702,3 +13703,32 @@ now 4 entries: Video / Mouse / Aim / Controller / Game. boot into the options screen (`GE_STARTMENU=26 GE_QUITFRAME=300`) orderly self-quit, zero crash lines. All strings plain ASCII (JP-region safe, #87). Port-only: no `src/game` edits, N64 build untouched. + +## D347 — F10 overlay controller value-adjust + hold-to-repeat (2026-09-27) + +User report (controller, Steam Deck class): in the F10 options overlay, +left/right did nothing — only A (increment) / Y (decrement) adjusted values, +"feels bad". Root cause: `optionsOverlayHandleInput()` wired only DPAD_UP/DOWN ++ stick-Y to navigation and A/X/B/Y to adjust; **DPAD_LEFT/RIGHT and stick-X +were never read**, and every action was edge-triggered — no hold-to-repeat +anywhere (holding a key or the stick stepped exactly once). + +Fix (port-only, `optionsoverlay.c`): +- D-pad left/right + left-stick X (±12000, matching the existing Y-axis style) + OR-ed into the lf/rt adjust inputs. A/X/B/Y keep working as before. +- Hold-to-repeat on value adjust: first press fires immediately, then an + 18-frame delay and a 4-frame repeat — the same cadence as the options + screen's D345(e)/(f) blocks, so both settings UIs now feel identical. +- Same repeat added to up/down navigation (previously one row per press even + on keyboard — traversing the list held-down was impossible). `overlayStepSel` + clamps at the ends, so holding at an end is a harmless no-op. +- Repeat state (`navDir/navTimer/adjDir/adjTimer`) resets when the overlay + closes, alongside the existing prev*/dragRow reset. + +Toggles flip on either adjust direction (existing `rowAdjust` behaviour); +sliders step by their registered step; MSAA/enum wrap as before. + +**Verification:** `build-pc.sh ntsc-final` clean; headless boot smoke clean. +Controller feel is owed a real-device retest (headless can't drive the pad): +hold ←/→ on a slider, hold ↑/↓ to traverse, confirm A/X/B/Y still work and +nothing reaches the game while the overlay is open. diff --git a/port/src/optionsoverlay.c b/port/src/optionsoverlay.c index b34d0516..35d73d3d 100644 --- a/port/src/optionsoverlay.c +++ b/port/src/optionsoverlay.c @@ -763,10 +763,14 @@ void optionsOverlayHandleInput(void) { static int prevUp, prevDn, prevLf, prevRt, prevLmb, prevRmb; static int dragRow = -1; + /* D347: hold-to-repeat state (18/4-frame cadence, same as the options + * screen's D345(e)/(f) blocks). */ + static int navDir = 0, navTimer = 0, adjDir = 0, adjTimer = 0; if (!s_open) { prevUp = prevDn = prevLf = prevRt = prevLmb = prevRmb = 0; dragRow = -1; + navDir = adjDir = 0; SDL_AtomicSet(&s_wheelPending, 0); return; } @@ -799,18 +803,26 @@ void optionsOverlayHandleInput(void) * logic as the keyboard, so repeat/clamp/scroll behaviour is identical. * input.c swallows the pad while we are open, so none of this reaches * the game. (Select also closes -- handled in input.c's toggle, which - * runs before this one.) */ + * runs before this one.) + * D347: D-pad left/right and stick-X join A/X/B/Y as value adjust -- + * the standard controller convention; A/X/B/Y keep working. */ int gUp = inputPadButton(0, SDL_CONTROLLER_BUTTON_DPAD_UP) || inputPadAxis(0, SDL_CONTROLLER_AXIS_LEFTY) < -12000; int gDn = inputPadButton(0, SDL_CONTROLLER_BUTTON_DPAD_DOWN) || inputPadAxis(0, SDL_CONTROLLER_AXIS_LEFTY) > 12000; + int gLf = inputPadButton(0, SDL_CONTROLLER_BUTTON_DPAD_LEFT) + || inputPadAxis(0, SDL_CONTROLLER_AXIS_LEFTX) < -12000; + int gRt = inputPadButton(0, SDL_CONTROLLER_BUTTON_DPAD_RIGHT) + || inputPadAxis(0, SDL_CONTROLLER_AXIS_LEFTX) > 12000; int up = ks[SDL_SCANCODE_UP] || ks[SDL_SCANCODE_KP_8] || gUp; int dn = ks[SDL_SCANCODE_DOWN] || ks[SDL_SCANCODE_KP_2] || gDn; int lf = ks[SDL_SCANCODE_LEFT] || ks[SDL_SCANCODE_KP_4] + || gLf || inputPadButton(0, SDL_CONTROLLER_BUTTON_B) || inputPadButton(0, SDL_CONTROLLER_BUTTON_Y); int rt = ks[SDL_SCANCODE_RIGHT] || ks[SDL_SCANCODE_KP_6] || ks[SDL_SCANCODE_RETURN] || ks[SDL_SCANCODE_KP_ENTER] + || gRt || inputPadButton(0, SDL_CONTROLLER_BUTTON_A) || inputPadButton(0, SDL_CONTROLLER_BUTTON_X); @@ -820,12 +832,41 @@ void optionsOverlayHandleInput(void) if (startNow && !prevStart) optionsOverlayToggle(); /* Start closes */ prevStart = startNow; - /* ---- keyboard / D-pad nav (clamped at the ends; scroll follows) ---- */ - if (up && !prevUp) s_sel = overlayStepSel(s_sel, -1); /* skips headers */ - if (dn && !prevDn) s_sel = overlayStepSel(s_sel, +1); + /* ---- keyboard / D-pad nav (clamped at the ends; scroll follows). + * D347: hold-to-repeat so a controller (and held keys) can traverse the + * list without one-press-per-row. */ + { + int vdir = (dn && !prevDn) ? +1 : (up && !prevUp) ? -1 : 0; + if (vdir != 0) { + navDir = vdir; + navTimer = 18; + s_sel = overlayStepSel(s_sel, vdir); /* skips headers */ + } else if ((up || dn) && navDir != 0) { + if (--navTimer <= 0) { + navTimer = 4; + s_sel = overlayStepSel(s_sel, navDir); + } + } else { + navDir = 0; + } + } overlayUpdateScroll(); - if (lf && !prevLf) rowAdjust(&rows[s_visIdx[s_sel]], -1); - if (rt && !prevRt) rowAdjust(&rows[s_visIdx[s_sel]], +1); + /* ---- value adjust (D347: D-pad/stick left-right wired in; hold repeats) ---- */ + { + int dir = (rt && !prevRt) ? +1 : (lf && !prevLf) ? -1 : 0; + if (dir != 0) { + adjDir = dir; + adjTimer = 18; + rowAdjust(&rows[s_visIdx[s_sel]], dir); + } else if ((rt || lf) && adjDir != 0) { + if (--adjTimer <= 0) { + adjTimer = 4; + rowAdjust(&rows[s_visIdx[s_sel]], adjDir); + } + } else { + adjDir = 0; + } + } /* ---- mouse ---- * D316: mx/my are raw window pixels, but the overlay's own 2D content