feat(menu): text fields in the Insert menu, and a Custom output size typed in it #2594

Open
mooki000 wants to merge 2 commits from mooki000/menu-text-fields into main
mooki000 commented 2026-11-12 00:42:52 +00:00 (Migrated from github.com)

What this is for: a player with a monitor the menu has no size for (3440x1440, 2560x1080, 2560x1600) can type that
size in the Insert menu instead of editing hookshot_options.ini by hand, and every later setting that is text (a
co-op name, a relay address, a room secret: #2269, #2274) has a field to live in. What is measured: on Linux (Arch,
RTX 4090, NVIDIA 615.78.08), base main fce53453, head cefdafbb: a game-free test of the field, the host's queue and the
wiring (3 parts pass with g++ and clang++; the same test fails on main's source); the whole of tests/run_all.py
(450 passed, 1 failed, 20 skipped; the failure is also present on main alone); one headless run, tf6, showing typing
3440x1440, caret movement, committing, and that size in both the row and options file. Six ran without a core
lines were counted, not investigated. Not measured: refusal, Esc cancelling or a completed edit after erasing
in the game on this head; Windows execution, real keyboard/clipboard/controller input, an input method, SDL2 or Deck.

Updated 2026-11-12: rebased and the Windows check repaired. Head 85e3df35 on main 6f7ab2a7 (it merges with 55b976c0), two commits:

  • the feature, rebased over the merged Bug report row (#2347): the two places in gpu/vk/vk_menu.cpp where they met keep both, main's Save prompt and dispatch and the field's Edit, Paste, Done, Cancel and its dispatch;
  • a test-only commit for the failure the first CI run showed on Windows: tests/test_menu_text_field.py's generated sources (and the screensaver test's) call getenv and sscanf, which Microsoft's headers mark deprecated, and with warnings as errors the fixture did not build there. They now begin with _CRT_SECURE_NO_WARNINGS, as main's other fixtures do. No runtime code, check or warning flag changed.

What ran on the new head: the menu's checks with g++ and clang++ (the nine named below plus #2347's test_bug_report.py and test_menu_tune.py: pass with both), the three new or updated tests on main 6f7ab2a7's tree (fail), and a Windows-target syntax compile of the generated sources with a declaration marked deprecated the way Microsoft's is (fails before the guard, passes after). What is from before the rebase, on cefdafbb: the whole suite's totals and the game run tf6 below. Nothing was run on Windows; the project's Windows job is the check.

Refs #2269. This is the text-fields commit of the co-op draft #2274 (b4ec89b7 there), alone on main so that it can be reviewed first; since the rebase below it also carries main's Bug report row beside the field.

Problem

  • The Insert menu's rows are numbers with a text for each value (gpu/vk/vk_menu.cpp Row: value, lo, hi,
    step, text). Nothing in the menu takes typed text: the window's thread forwards key presses (host/display.c
    WM_KEYDOWN, host/posix/display_sdl.c key events), never characters, and the renderer's interface
    (gpu/vk/vk_api.h MkwVkHost) has no call for them.
  • So a setting that is text can only be changed in hookshot_options.ini. Today that is the Output resolution of the
    player's own: the readme sends players to the file for output_res=<width>x<height>, with rules to get right (each
    side a multiple of 8, at least 640x360, at most 5120x2880's pixels, between 5:4 and 48:9), and a size the file gets
    wrong becomes the game's own 2048x1152 with only a line in the log (host/nn_applet.c: "is not a size the game can
    use").

What changed

The field, host text queue, platform input paths, documentation and their game-free checks change together.

  • A text field (gpu/vk/vk_menu.cpp TextField, text_edit, field_rows, drawn by main's value cell as
    text_cell): a row whose value is typed. Left, Right, Home, End, Backspace and Delete edit; Ctrl+V and Shift+Insert
    paste the clipboard's first line; Enter sets the text (the field's own check can refuse it and says why in the help
    box), Esc puts it back. A controller: confirm and back are Enter and Esc, the D-pad moves the caret, a free face button
    (the top one with either layout) pastes (gpu/vk/vk_menu_keys.h; gpu/vk/vk_ui.h: its prompt, the glyph kPaste
    after main's kCompact). A click elsewhere sets, a right click puts back; F1, Home or closing the menu put back. While a
    field has the keyboard no key does anything else, and the game gets none (as for the whole menu: host/nn_hid.c
    key()). The prompts bar shows Paste, Done, Cancel while a field is edited, and Edit on a field's row, inside main's
    compact-bar layout.
  • ASCII only, on purpose: the menu's glyphs are baked for printable ASCII (vk_imgui.cpp bake_some), so a field
    keeps printable ASCII; any other character typed or pasted (é, 中, an emoji) is left out rather than drawn as a
    missing glyph.
  • The text's way in: WM_CHAR (Windows, host/display.c; the window is ANSI, a double-byte character's pair is
    left out) and SDL's text input (Linux, host/posix/display_sdl.c; SDL3's and SDL2's SDL_TEXTINPUT read at their
    offsets in host/hos_sdl.c), turned on only while a field has the keyboard, so no input method's window opens over
    the game. A small queue in host/rx_backend.c carries characters and editing keys to the menu. The window's thread
    reads the clipboard (WM_APP + 12 on Windows, the SDL loop on Linux).
  • The renderer's interface changes: MKWVK_API_VERSION goes from 18 to 20. One new MkwVkHost member, take_text,
    appended last (after main's prompts_live, version 17), filled by rx_backend.c's vk_host in the same place; nothing
    else in the interface moves. Why 20 and not 19: the open #2317 (data folder) already takes 19 for its own MkwVkHost
    member, and this keeps the co-op draft #2274's current text-fields interface number (its Co-op tab is 21). This does not
    make two independent interface additions interchangeable: whichever lands later must append after the merged call and
    take a fresh version. The executable and hookshot_vk.so must come from the same build.
  • The first field: Custom output size (Graphics, in the "Resolution and upscaling" section under Output
    resolution): typed as <width>x<height>, checked by the rules output_res= already has (host/hos_wide_size.h); a
    size the row offers is chosen there; the Output resolution row then shows it, and the restart question counts a new
    size of your own as a change. Like the Output resolution row itself it is preset_exempt (the Quality preset leaves
    it alone, #2042's rule), and a size typed calls env_release, so it is the player's own from then on also in a run
    whose HOS_OUTPUT_RES held the row.
  • RX_VK_MENU_SCRIPT gains TYPE:<text> and, while a field has the keyboard, HOME, END, BACK, DEL, so a
    headless run can type (docs/development.md). The readme's Graphics table gets the row, and the paragraph on sizes of
    your own says to type it there.
  • tests/test_menu_keys.py keeps all its existing keyboard checks. Its source extraction now ends the Esc block at
    the text-editing branch, rather than looking for the old first page's if, which is now an else if.
  • Unchanged: every existing row, every default, the options file's keys. Without a field being edited no text input is
    switched on and the queue takes nothing.

How to try it

Insert (or F1), Graphics, down to Custom output size, Enter, type 3440x1440, Enter; the Output resolution row
then shows it and the menu asks for a restart on closing. Headless: RX_VK_MENU_SCRIPT='25:INS,26:DOWN,26.5:DOWN, 32:ENTER,33:TYPE:3440x1440,37.5:ENTER' with RX_VK_SHOTS and RX_VK_SHOT_MENU=1.

How it was tested

Environment: Arch Linux, kernel 7.2.15, i9-13900K, RTX 4090 with NVIDIA 615.78.08, clang 23.1.3 and g++ 16.2.1; base
main fce53453, head cefdafbb; 2026-11-11; the game run headless (no window), pictures 1280x720.

  • The new test, both ways. tests/test_menu_text_field.py on the branch, with g++ and again with clang++
    (-Wall -Wextra -Werror):

    PASS: typing, the caret's keys, the limits, ASCII alone, Enter and Esc; Custom output size sets the Output resolution
    PASS: the text queue: only while a field has the keyboard, in order, UTF-8 to characters, a paste's first line
    PASS: the text field's wiring: the interface's take_text, WM_CHAR, SDL's text input, the menu's focus and TYPE
    

    The same file on main fce53453's source: FAIL: vk_api.h: no HOS_TEXT_KEY and HOS_TEXT_OFF/ON/PASTE (both
    compilers). It cuts the field's edit function out of vk_menu.cpp and drives it with key and text events (typing, the
    caret, limits, ASCII only, Enter and Esc), Custom output size's Enter for good, rounded, offered, refused and emptied
    sizes; the host's queue cut out of rx_backend.c and compiled as C; and the wiring, including that vk_host gives
    host_take_text in take_text's place.

  • tests/screensaver_test.c (the real window procedure under mocks) and tests/test_menu_restart_slots.py updated for
    the new lines; on main's source they fail as they should (the new mocks unused under -Werror; FAIL: restart_changed does not skip a row with no slot). #2042's menu tests (test_menu_env_overrides.py and test_menu_env_on.py with
    both compilers, test_preset_exempt.py) and #2177's test_menu_sections.py and test_upscaler_rows.py pass on the
    branch.

  • The whole suite, python -I -B tests/run_all.py (32 at once, the test gate's idle class):

    450 passed, 1 failed, 20 skipped in 246 s (linux, 32 at once)
    

    Final head cefdafbb, in the idle scheduling class with the pinned Python, 2026-11-11 17:31:30–17:35:36 CST;
    load average 92.91 / 115.60 / 148.65 → 94.12 / 106.14 / 137.48. The sole failure is tests/test_env_on.py:
    HOS_FPS_LOG (host/oot_high_fps.c:15) still uses set-means-on semantics. The same check fails at that exact site on
    main fce53453 alone; this patch changes neither the switch nor its check. No core dumps were found.

    The earlier suite found a real extraction failure in test_menu_keys.py; it was repaired without dropping any
    checks. The final run above includes that repair and passes the test. The earlier run also timed out
    test_recorder_sync.py and test_fpx_sticky.py under heavy load; both pass in this final suite. No performance
    conclusion is drawn from test duration or machine load.

  • In the game on cefdafbb: tf6, started 2026-11-11 at 18:24:39 CST, headless (HOS_HEADLESS=1), no shader
    preparation (RX_VK_PRECOMPILE=0). The script reached the correct Custom output size row, typed 3440x14,
    appended 40, moved the caret and committed with Enter. Both the Output resolution row and the saved file show
    3440x1440. The game itself started at 2048x1152; the new size requires a restart, which this run did not perform.

    Run / build Input and observed result Safety/counts Load average before → after
    tf6 / cefdafbb on fce53453 typed size and caret visible; Enter commits; output_res=3440x1440 saved; second edit left unfinished 62.3 s wall; 78 pictures; exit 124; fatal/crash/device-lost/kernel/NVRM 0; 6 vcore escapes 47.44 / 54.35 / 63.40 → 80.78 / 60.08 / 64.65
    hs-run tf6 exit=124 wall=62.3s span=42.6s from=0s frames=2339(swapchain) mean=54.9 low1=5.1 over16.7=1078 over20=155 over25=87 shots=78 1280x720 215.7MB fatal=0 crash=0 devlost=0 nocore=6 kernel=0 nvrm=0 dev=0:NVIDIA_GeForce_RTX_4090 load 47.44 54.35 63.40 -> 80.78 60.08 64.65
    output_res=3440x1440
    

    The wrapper's frame-source label is quoted unchanged; the recorded environment confirms a headless run. This was
    not a timing measurement: no FPS or frame-time conclusion is drawn. The six ran without a core lines are
    log lines 79, 81, 82, 83, 86, 91: four PreActorModelCompose, one AudioFrameworkThread, one
    SceneLowPriority. They are counted and not looked into here.

    tf6: selected custom size, partial typing, complete text, moved caret, committed size, and the unfinished second edit

    All six frames and the labelled panel were inspected. Panels 1–5 show None → 3440x14 → 3440x1440 → the
    caret moved left → Enter commits, with “Restart needed: Output resolution”. Panel 6 is a later unfinished
    edit: its field is empty while the committed size remains in the row above.

    Not shown in the game on this head: a refused size, Esc cancelling, or a completed edit after erasing.
    The 62 s run ended during that second edit, before those remaining scripted steps. The current game-free field
    test covers refusal and cancellation. Earlier run tf4, build cf925b67 (the same tree as 34a634a3, on main
    49f75dc8) showed 100x100 refused with its reason and Esc keeping 3440x1440; that is evidence on the older head,
    not a repeat of those steps on this head. No retry was run.

  • Windows, syntax only: host/display.c, host/hos_sdl.c and host/rx_backend.c compiled with clang
    --target=x86_64-w64-mingw32 and llvm-mingw's headers, -Wall -Wextra: display.c and hos_sdl.c no warning,
    rx_backend.c the same six as main's. Nothing was linked or run for Windows.

  • Before pushing: the commit identity, trailers and title checks pass. main had moved to ef81aee7 (past the tested base fce53453) and the branch merges with it without a conflict; nothing was rerun on the merge. Rerun on the head by me: the seven menu checks named above pass with g++ and with clang++, and the three new or updated ones (test_menu_text_field, test_screensaver, test_menu_restart_slots) fail on main's tree.

Not tested

  • Windows at all: WM_CHAR, the clipboard message, Ctrl+V and Shift+Insert exist only as compiled text.
  • A real keyboard, the clipboard and the mouse on Linux: the run is headless and its text came from
    RX_VK_MENU_SCRIPT's TYPE, which enters the same queue the keyboard's characters do, after SDL.
  • A controller (the D-pad caret, the paste button), an input method (Windows' is disabled for the game's thread
    already, host/display.c ImmDisableIME; on Linux SDL's text input is on only while a field has the keyboard),
    SDL2 (only SDL3 on this machine), the Steam Deck's on-screen keyboard, AMD and Intel GPUs (the menu is drawn the
    same way on all of them, but none was run).
  • The typed size after the restart: the run ended at the options file; that output_res=3440x1440 is honoured at the
    next start is main's existing path.

Risks

  • The interface version. #1930 and #2317 take 19; #2459, #2478 and #2479 take 18 from an older main.
    Whichever merges later renumbers and appends after; a mixed pair of executable and hookshot_vk.so is refused at
    start, as for every bump.
  • Keys while typing. While a field has the keyboard, no other menu key and no game key acts. If the field's focus
    were ever left on by a path this change missed, the keyboard would seem dead in the menu until Esc; F1, Home, closing
    the menu, a click elsewhere and a right click all end it, and the test drives each.
  • Windows is unproven: a wrong guess in WM_CHAR handling would show as a field that takes no characters, or takes
    them twice; nothing else of the game reads that message.

Overlaps

From pr_overlap.py --branch menu-text-fields (2026-11-11, on fce53453: 73 open pull requests touch one of its 15 files,
10 conflict), each conflict hunk read:

Pull request File What meets
#1872 macOS host/hos_sdl.c, host/posix/display_sdl.c both add SDL function pointers to the same list, and a line where the window starts: keep both
#1882 prompts from the game gpu/vk/vk_ui.h both add a member to the same struct (paste_dot here, prompt_face there): keep both
#1930 ocarina note buttons gpu/vk/vk_api.h version 19 versus 20: the later interface addition needs a new version
#2459 microphone, #2478 Mods tab, #2479 amiibo gpu/vk/vk_api.h, host/rx_backend.c version 18 versus 20, and new host declarations/table entries at the same end: retain both in one consistent interface order
#2261 item wheel, #2317 data folder host/rx_backend.c (and gpu/vk/vk_api.h for #2317, version 19) the host table's last line: host_take_text here, hos_wheel_state / hos_data_dir there: keep both, in merge order
#2417 localized menu gpu/vk/vk_menu.cpp translated cheat cells, hints and prompts meet the text-field cell, error hint and Paste/Done/Cancel branch: keep the field behavior and translate its added strings too
#2566 display-mode resolution choices gpu/vk/vk_menu.cpp, readme.md its new output-resolution hint and documentation replace the same fixed-choice explanation; retain its mode-aware behavior and this field's typed-size path, then rerun both sets of checks

Questions for you

  1. Is appending to MkwVkHost with a version bump the way you want new host calls to arrive, or would you rather
    have text reach the menu another way?
  2. ASCII only is the limit of the menu's baked glyphs today. Is that acceptable for a first version (names and
    addresses are ASCII; a save folder with an é in its path could not be typed)?
  3. Version 20 here (19 left to #2317), or 19?

Assisted-by: Claude Opus 5.5
Co-Authored-By: Claude Opus 5.5 [email protected]
Assisted-by: GPT-6 Astra
Co-Authored-By: GPT-6 Astra [email protected]

**What this is for:** a player with a monitor the menu has no size for (3440x1440, 2560x1080, 2560x1600) can type that size in the Insert menu instead of editing `hookshot_options.ini` by hand, and every later setting that is text (a co-op name, a relay address, a room secret: #2269, #2274) has a field to live in. **What is measured:** on Linux (Arch, RTX 4090, NVIDIA 615.78.08), base `main` fce53453, head cefdafbb: a game-free test of the field, the host's queue and the wiring (3 parts pass with g++ and clang++; the same test fails on main's source); the whole of `tests/run_all.py` (450 passed, 1 failed, 20 skipped; the failure is also present on main alone); one headless run, tf6, showing typing `3440x1440`, caret movement, committing, and that size in both the row and options file. Six `ran without a core` lines were counted, not investigated. **Not measured:** refusal, Esc cancelling or a completed edit after erasing in the game on this head; Windows execution, real keyboard/clipboard/controller input, an input method, SDL2 or Deck. **Updated 2026-11-12: rebased and the Windows check repaired.** Head `85e3df35` on main `6f7ab2a7` (it merges with `55b976c0`), two commits: - the feature, rebased over the merged Bug report row (#2347): the two places in `gpu/vk/vk_menu.cpp` where they met keep both, main's Save prompt and dispatch and the field's Edit, Paste, Done, Cancel and its dispatch; - a test-only commit for the failure the first CI run showed on Windows: `tests/test_menu_text_field.py`'s generated sources (and the screensaver test's) call `getenv` and `sscanf`, which Microsoft's headers mark deprecated, and with warnings as errors the fixture did not build there. They now begin with `_CRT_SECURE_NO_WARNINGS`, as main's other fixtures do. No runtime code, check or warning flag changed. What ran on the new head: the menu's checks with g++ and clang++ (the nine named below plus #2347's `test_bug_report.py` and `test_menu_tune.py`: pass with both), the three new or updated tests on main `6f7ab2a7`'s tree (fail), and a Windows-target syntax compile of the generated sources with a declaration marked deprecated the way Microsoft's is (fails before the guard, passes after). What is from before the rebase, on `cefdafbb`: the whole suite's totals and the game run tf6 below. Nothing was run on Windows; the project's Windows job is the check. Refs #2269. This is the text-fields commit of the co-op draft #2274 (b4ec89b7 there), alone on `main` so that it can be reviewed first; since the rebase below it also carries main's Bug report row beside the field. ## Problem - The Insert menu's rows are numbers with a text for each value (`gpu/vk/vk_menu.cpp` `Row`: `value`, `lo`, `hi`, `step`, `text`). Nothing in the menu takes typed text: the window's thread forwards key presses (`host/display.c` `WM_KEYDOWN`, `host/posix/display_sdl.c` key events), never characters, and the renderer's interface (`gpu/vk/vk_api.h` `MkwVkHost`) has no call for them. - So a setting that is text can only be changed in `hookshot_options.ini`. Today that is the Output resolution of the player's own: the readme sends players to the file for `output_res=<width>x<height>`, with rules to get right (each side a multiple of 8, at least 640x360, at most 5120x2880's pixels, between 5:4 and 48:9), and a size the file gets wrong becomes the game's own 2048x1152 with only a line in the log (`host/nn_applet.c`: "is not a size the game can use"). ## What changed The field, host text queue, platform input paths, documentation and their game-free checks change together. - **A text field** (`gpu/vk/vk_menu.cpp` `TextField`, `text_edit`, `field_rows`, drawn by main's value cell as `text_cell`): a row whose value is typed. Left, Right, Home, End, Backspace and Delete edit; Ctrl+V and Shift+Insert paste the clipboard's first line; Enter sets the text (the field's own check can refuse it and says why in the help box), Esc puts it back. A controller: confirm and back are Enter and Esc, the D-pad moves the caret, a free face button (the top one with either layout) pastes (`gpu/vk/vk_menu_keys.h`; `gpu/vk/vk_ui.h`: its prompt, the glyph `kPaste` after main's `kCompact`). A click elsewhere sets, a right click puts back; F1, Home or closing the menu put back. While a field has the keyboard no key does anything else, and the game gets none (as for the whole menu: `host/nn_hid.c` `key()`). The prompts bar shows Paste, Done, Cancel while a field is edited, and Edit on a field's row, inside main's compact-bar layout. - **ASCII only, on purpose**: the menu's glyphs are baked for printable ASCII (`vk_imgui.cpp` `bake_some`), so a field keeps printable ASCII; any other character typed or pasted (é, 中, an emoji) is left out rather than drawn as a missing glyph. - **The text's way in**: `WM_CHAR` (Windows, `host/display.c`; the window is ANSI, a double-byte character's pair is left out) and SDL's text input (Linux, `host/posix/display_sdl.c`; SDL3's and SDL2's `SDL_TEXTINPUT` read at their offsets in `host/hos_sdl.c`), turned on only while a field has the keyboard, so no input method's window opens over the game. A small queue in `host/rx_backend.c` carries characters and editing keys to the menu. The window's thread reads the clipboard (`WM_APP + 12` on Windows, the SDL loop on Linux). - **The renderer's interface changes: `MKWVK_API_VERSION` goes from 18 to 20.** One new `MkwVkHost` member, `take_text`, appended last (after main's `prompts_live`, version 17), filled by `rx_backend.c`'s `vk_host` in the same place; nothing else in the interface moves. Why 20 and not 19: the open #2317 (data folder) already takes 19 for its own `MkwVkHost` member, and this keeps the co-op draft #2274's current text-fields interface number (its Co-op tab is 21). This does not make two independent interface additions interchangeable: whichever lands later must append after the merged call and take a fresh version. The executable and `hookshot_vk.so` must come from the same build. - **The first field: Custom output size** (Graphics, in the "Resolution and upscaling" section under Output resolution): typed as `<width>x<height>`, checked by the rules `output_res=` already has (`host/hos_wide_size.h`); a size the row offers is chosen there; the Output resolution row then shows it, and the restart question counts a new size of your own as a change. Like the Output resolution row itself it is `preset_exempt` (the Quality preset leaves it alone, #2042's rule), and a size typed calls `env_release`, so it is the player's own from then on also in a run whose `HOS_OUTPUT_RES` held the row. - `RX_VK_MENU_SCRIPT` gains `TYPE:<text>` and, while a field has the keyboard, `HOME`, `END`, `BACK`, `DEL`, so a headless run can type (`docs/development.md`). The readme's Graphics table gets the row, and the paragraph on sizes of your own says to type it there. - `tests/test_menu_keys.py` keeps all its existing keyboard checks. Its source extraction now ends the Esc block at the text-editing branch, rather than looking for the old first page's `if`, which is now an `else if`. - Unchanged: every existing row, every default, the options file's keys. Without a field being edited no text input is switched on and the queue takes nothing. ## How to try it Insert (or F1), Graphics, down to **Custom output size**, Enter, type `3440x1440`, Enter; the Output resolution row then shows it and the menu asks for a restart on closing. Headless: `RX_VK_MENU_SCRIPT='25:INS,26:DOWN,26.5:DOWN, 32:ENTER,33:TYPE:3440x1440,37.5:ENTER'` with `RX_VK_SHOTS` and `RX_VK_SHOT_MENU=1`. ## How it was tested Environment: Arch Linux, kernel 7.2.15, i9-13900K, RTX 4090 with NVIDIA 615.78.08, clang 23.1.3 and g++ 16.2.1; base `main` fce53453, head cefdafbb; 2026-11-11; the game run headless (no window), pictures 1280x720. - **The new test, both ways.** `tests/test_menu_text_field.py` on the branch, with g++ and again with clang++ (`-Wall -Wextra -Werror`): ``` PASS: typing, the caret's keys, the limits, ASCII alone, Enter and Esc; Custom output size sets the Output resolution PASS: the text queue: only while a field has the keyboard, in order, UTF-8 to characters, a paste's first line PASS: the text field's wiring: the interface's take_text, WM_CHAR, SDL's text input, the menu's focus and TYPE ``` The same file on main fce53453's source: `FAIL: vk_api.h: no HOS_TEXT_KEY and HOS_TEXT_OFF/ON/PASTE` (both compilers). It cuts the field's edit function out of `vk_menu.cpp` and drives it with key and text events (typing, the caret, limits, ASCII only, Enter and Esc), Custom output size's Enter for good, rounded, offered, refused and emptied sizes; the host's queue cut out of `rx_backend.c` and compiled as C; and the wiring, including that `vk_host` gives `host_take_text` in `take_text`'s place. - `tests/screensaver_test.c` (the real window procedure under mocks) and `tests/test_menu_restart_slots.py` updated for the new lines; on main's source they fail as they should (the new mocks unused under `-Werror`; `FAIL: restart_changed does not skip a row with no slot`). #2042's menu tests (`test_menu_env_overrides.py` and `test_menu_env_on.py` with both compilers, `test_preset_exempt.py`) and #2177's `test_menu_sections.py` and `test_upscaler_rows.py` pass on the branch. - **The whole suite**, `python -I -B tests/run_all.py` (32 at once, the test gate's idle class): ``` 450 passed, 1 failed, 20 skipped in 246 s (linux, 32 at once) ``` Final head cefdafbb, in the idle scheduling class with the pinned Python, 2026-11-11 17:31:30–17:35:36 CST; load average 92.91 / 115.60 / 148.65 → 94.12 / 106.14 / 137.48. The sole failure is `tests/test_env_on.py`: `HOS_FPS_LOG (host/oot_high_fps.c:15)` still uses set-means-on semantics. The same check fails at that exact site on main fce53453 alone; this patch changes neither the switch nor its check. No core dumps were found. The earlier suite found a real extraction failure in `test_menu_keys.py`; it was repaired without dropping any checks. The final run above includes that repair and passes the test. The earlier run also timed out `test_recorder_sync.py` and `test_fpx_sticky.py` under heavy load; both pass in this final suite. No performance conclusion is drawn from test duration or machine load. - **In the game on cefdafbb:** tf6, started 2026-11-11 at 18:24:39 CST, headless (`HOS_HEADLESS=1`), no shader preparation (`RX_VK_PRECOMPILE=0`). The script reached the correct Custom output size row, typed `3440x14`, appended `40`, moved the caret and committed with Enter. Both the Output resolution row and the saved file show `3440x1440`. The game itself started at 2048x1152; the new size requires a restart, which this run did not perform. | Run / build | Input and observed result | Safety/counts | Load average before → after | |---|---|---|---| | tf6 / cefdafbb on fce53453 | typed size and caret visible; Enter commits; `output_res=3440x1440` saved; second edit left unfinished | 62.3 s wall; 78 pictures; exit 124; fatal/crash/device-lost/kernel/NVRM 0; 6 vcore escapes | 47.44 / 54.35 / 63.40 → 80.78 / 60.08 / 64.65 | ``` hs-run tf6 exit=124 wall=62.3s span=42.6s from=0s frames=2339(swapchain) mean=54.9 low1=5.1 over16.7=1078 over20=155 over25=87 shots=78 1280x720 215.7MB fatal=0 crash=0 devlost=0 nocore=6 kernel=0 nvrm=0 dev=0:NVIDIA_GeForce_RTX_4090 load 47.44 54.35 63.40 -> 80.78 60.08 64.65 output_res=3440x1440 ``` The wrapper's frame-source label is quoted unchanged; the recorded environment confirms a headless run. This was not a timing measurement: no FPS or frame-time conclusion is drawn. The six `ran without a core` lines are log lines **79, 81, 82, 83, 86, 91**: four `PreActorModelCompose`, one `AudioFrameworkThread`, one `SceneLowPriority`. They are counted and **not looked into** here. ![tf6: selected custom size, partial typing, complete text, moved caret, committed size, and the unfinished second edit](https://gist.githubusercontent.com/mooki000/b86bd3f35969803fb032f71f035844f8/raw/a8a51d337b6d9948821b129c6330a83566b504ac/hookshot-text-fields.jpg) All six frames and the labelled panel were inspected. Panels 1–5 show None → `3440x14` → `3440x1440` → the caret moved left → Enter commits, with “Restart needed: Output resolution”. Panel 6 is a later **unfinished** edit: its field is empty while the committed size remains in the row above. **Not shown in the game on this head:** a refused size, Esc cancelling, or a completed edit after erasing. The 62 s run ended during that second edit, before those remaining scripted steps. The current game-free field test covers refusal and cancellation. Earlier run **tf4, build cf925b67** (the same tree as 34a634a3, on main 49f75dc8) showed `100x100` refused with its reason and Esc keeping 3440x1440; that is evidence on the older head, not a repeat of those steps on this head. No retry was run. - **Windows, syntax only**: `host/display.c`, `host/hos_sdl.c` and `host/rx_backend.c` compiled with clang `--target=x86_64-w64-mingw32` and llvm-mingw's headers, `-Wall -Wextra`: `display.c` and `hos_sdl.c` no warning, `rx_backend.c` the same six as main's. Nothing was linked or run for Windows. - **Before pushing:** the commit identity, trailers and title checks pass. `main` had moved to ef81aee7 (past the tested base fce53453) and the branch merges with it without a conflict; nothing was rerun on the merge. Rerun on the head by me: the seven menu checks named above pass with g++ and with clang++, and the three new or updated ones (`test_menu_text_field`, `test_screensaver`, `test_menu_restart_slots`) fail on main's tree. ## Not tested - **Windows at all**: `WM_CHAR`, the clipboard message, Ctrl+V and Shift+Insert exist only as compiled text. - A real keyboard, the clipboard and the mouse on Linux: the run is headless and its text came from `RX_VK_MENU_SCRIPT`'s `TYPE`, which enters the same queue the keyboard's characters do, after SDL. - A controller (the D-pad caret, the paste button), an input method (Windows' is disabled for the game's thread already, `host/display.c` `ImmDisableIME`; on Linux SDL's text input is on only while a field has the keyboard), SDL2 (only SDL3 on this machine), the Steam Deck's on-screen keyboard, AMD and Intel GPUs (the menu is drawn the same way on all of them, but none was run). - The typed size after the restart: the run ended at the options file; that `output_res=3440x1440` is honoured at the next start is main's existing path. ## Risks - **The interface version.** #1930 and #2317 take 19; #2459, #2478 and #2479 take 18 from an older main. Whichever merges later renumbers and appends after; a mixed pair of executable and `hookshot_vk.so` is refused at start, as for every bump. - **Keys while typing.** While a field has the keyboard, no other menu key and no game key acts. If the field's focus were ever left on by a path this change missed, the keyboard would seem dead in the menu until Esc; F1, Home, closing the menu, a click elsewhere and a right click all end it, and the test drives each. - **Windows is unproven**: a wrong guess in `WM_CHAR` handling would show as a field that takes no characters, or takes them twice; nothing else of the game reads that message. ## Overlaps From `pr_overlap.py --branch menu-text-fields` (2026-11-11, on fce53453: 73 open pull requests touch one of its 15 files, 10 conflict), each conflict hunk read: | Pull request | File | What meets | |---|---|---| | #1872 macOS | `host/hos_sdl.c`, `host/posix/display_sdl.c` | both add SDL function pointers to the same list, and a line where the window starts: keep both | | #1882 prompts from the game | `gpu/vk/vk_ui.h` | both add a member to the same struct (`paste_dot` here, `prompt_face` there): keep both | | #1930 ocarina note buttons | `gpu/vk/vk_api.h` | version 19 versus 20: the later interface addition needs a new version | | #2459 microphone, #2478 Mods tab, #2479 amiibo | `gpu/vk/vk_api.h`, `host/rx_backend.c` | version 18 versus 20, and new host declarations/table entries at the same end: retain both in one consistent interface order | | #2261 item wheel, #2317 data folder | `host/rx_backend.c` (and `gpu/vk/vk_api.h` for #2317, version 19) | the host table's last line: `host_take_text` here, `hos_wheel_state` / `hos_data_dir` there: keep both, in merge order | | #2417 localized menu | `gpu/vk/vk_menu.cpp` | translated cheat cells, hints and prompts meet the text-field cell, error hint and Paste/Done/Cancel branch: keep the field behavior and translate its added strings too | | #2566 display-mode resolution choices | `gpu/vk/vk_menu.cpp`, `readme.md` | its new output-resolution hint and documentation replace the same fixed-choice explanation; retain its mode-aware behavior and this field's typed-size path, then rerun both sets of checks | ## Questions for you 1. Is appending to `MkwVkHost` with a version bump the way you want new host calls to arrive, or would you rather have text reach the menu another way? 2. ASCII only is the limit of the menu's baked glyphs today. Is that acceptable for a first version (names and addresses are ASCII; a save folder with an é in its path could not be typed)? 3. Version 20 here (19 left to #2317), or 19? Assisted-by: Claude Opus 5.5 Co-Authored-By: Claude Opus 5.5 <[email protected]> Assisted-by: GPT-6 Astra Co-Authored-By: GPT-6 Astra <[email protected]>
mooki000 commented 2026-11-12 01:49:35 +00:00 (Migrated from github.com)

Rebased onto main (85e3df35 on 6f7ab2a7) and the Windows check repaired; the description's first block says what changed and what ran on the new head.

The Windows failure of the first run was in the test, not in the feature: tests/test_menu_text_field.py builds a fixture from the menu's source, which calls getenv (through host/hos_wide_size.h) and sscanf. Microsoft's headers mark both deprecated, and with warnings as errors the fixture did not compile on the Windows runner. The generated sources now start with _CRT_SECURE_NO_WARNINGS, like main's other fixtures; the screensaver test's template had the same trap and got the same line. I could only check this with a Windows-target syntax compile here, so the Windows job on this head is the proof.

The rebase met the merged Bug report row (#2347) in two places in gpu/vk/vk_menu.cpp; both sides are kept. The game run in the description (typing a size, the caret, committing) was made before the rebase, on cefdafbb, and is labelled so.

Rebased onto `main` (`85e3df35` on `6f7ab2a7`) and the Windows check repaired; the description's first block says what changed and what ran on the new head. **The Windows failure** of the first run was in the test, not in the feature: `tests/test_menu_text_field.py` builds a fixture from the menu's source, which calls `getenv` (through `host/hos_wide_size.h`) and `sscanf`. Microsoft's headers mark both deprecated, and with warnings as errors the fixture did not compile on the Windows runner. The generated sources now start with `_CRT_SECURE_NO_WARNINGS`, like main's other fixtures; the screensaver test's template had the same trap and got the same line. I could only check this with a Windows-target syntax compile here, so the Windows job on this head is the proof. **The rebase** met the merged Bug report row (#2347) in two places in `gpu/vk/vk_menu.cpp`; both sides are kept. The game run in the description (typing a size, the caret, committing) was made before the rebase, on `cefdafbb`, and is labelled so.
Owner

@mooki000 commented on GitHub · 2026-11-12 05:27 UTC · original

Rebased onto current main (edaef2a9) as faf1c9b1; nothing in what it does has changed.

  • gpu/vk/vk_api.h: #1930 took API version 19 for the ocarina note bindings, so this one is 20 (it adds take_text at the end of the host table; all of main's ids are kept).
  • gpu/vk/vk_menu.cpp: merged without conflict beside #2574's reset hook, #2435's keyboard prompts and #2347's Bug report row.
  • gpu/vk/menu_i18n.h: this change rewords one hint of the Output resolution row ("Your own size, from Custom output size below."), and since #1864 every catalogue has that hint. Its entry is replaced in all 13 catalogues, none removed. The reworded translations were written with a language model on top of the existing ones; corrections are welcome.

The text-field, menu-key, restart-slot, translation and switch checks pass on the new head with GCC 16.2.1 and Clang 23.1.3; the Windows-target syntax pass of the changed host files has no error. The whole suite: 470 passed, 1 failed, 20 skipped; the one is test_input_background.py under Clang, which fails the same way on untouched main (a variable set and not used in a fixture this pull request does not touch).

> [@mooki000](https://github.com/mooki000) commented on GitHub · 2026-11-12 05:27 UTC · [original](https://github.com/NotRexed/Hookshot/pull/2594#issuecomment-6815519920) Rebased onto current main (`edaef2a9`) as `faf1c9b1`; nothing in what it does has changed. - `gpu/vk/vk_api.h`: #1930 took API version 19 for the ocarina note bindings, so this one is 20 (it adds `take_text` at the end of the host table; all of main's ids are kept). - `gpu/vk/vk_menu.cpp`: merged without conflict beside #2574's reset hook, #2435's keyboard prompts and #2347's Bug report row. - `gpu/vk/menu_i18n.h`: this change rewords one hint of the Output resolution row ("Your own size, from Custom output size below."), and since #1864 every catalogue has that hint. Its entry is replaced in all 13 catalogues, none removed. The reworded translations were written with a language model on top of the existing ones; corrections are welcome. The text-field, menu-key, restart-slot, translation and switch checks pass on the new head with GCC 16.2.1 and Clang 23.1.3; the Windows-target syntax pass of the changed host files has no error. The whole suite: 470 passed, 1 failed, 20 skipped; the one is `test_input_background.py` under Clang, which fails the same way on untouched main (a variable set and not used in a fixture this pull request does not touch). <!-- github-sync:c:6815519920 -->
Owner

@Joey-Magnus reviewed on GitHub · 2026-11-12 14:33 UTC · original

I merged this into current main. tests/test_menu_text_field.py (all three parts), test_menu_keys.py, test_menu_restart_slots.py, test_menu_i18n.py, test_output_resolution.py, test_menu_setting_walk.py, test_utf8_paths.py and test_env_on.py pass.

I checked the SDL event offsets by hand against SDL2's and SDL3's structs:

  • key down: scancode 24/16, keycode 28/20, mod 32/24
  • text event: a pointer at 24 on SDL3, inline at 12 on SDL2

All match. The SDL3 text pointer is copied right away in event_text. The queue in host/rx_backend.c:216 is locked and drops input while no field is focused. Text input is only on while a field has focus, so no input method window opens over the game.

The one thing in the way is #2566. Both claim MKWVK_API_VERSION 20 for a new MkwVkHost member (take_text here, screen_size there). Merging #2566 on top of this conflicts in vk_api.h, vk_menu.cpp, menu_i18n.h, rx_backend.c and readme.md. You've already offered to rebase if #2566 goes first. In that case take_text goes after screen_size at version 21, and the Custom output size has to fit #2566's per-screen list (custom_output/output_extras).

> [@Joey-Magnus](https://github.com/Joey-Magnus) reviewed on GitHub · 2026-11-12 14:33 UTC · [original](https://github.com/NotRexed/Hookshot/pull/2594#pullrequestreview-5876219864) I merged this into current main. tests/test_menu_text_field.py (all three parts), test_menu_keys.py, test_menu_restart_slots.py, test_menu_i18n.py, test_output_resolution.py, test_menu_setting_walk.py, test_utf8_paths.py and test_env_on.py pass. I checked the SDL event offsets by hand against SDL2's and SDL3's structs: - key down: scancode 24/16, keycode 28/20, mod 32/24 - text event: a pointer at 24 on SDL3, inline at 12 on SDL2 All match. The SDL3 text pointer is copied right away in `event_text`. The queue in host/rx_backend.c:216 is locked and drops input while no field is focused. Text input is only on while a field has focus, so no input method window opens over the game. The one thing in the way is #2566. Both claim MKWVK_API_VERSION 20 for a new MkwVkHost member (`take_text` here, `screen_size` there). Merging #2566 on top of this conflicts in vk_api.h, vk_menu.cpp, menu_i18n.h, rx_backend.c and readme.md. You've already offered to rebase if #2566 goes first. In that case `take_text` goes after `screen_size` at version 21, and the Custom output size has to fit #2566's per-screen list (`custom_output`/`output_extras`). <!-- github-sync:r:5876219864 -->
Owner

@mooki000 commented on GitHub · 2026-11-12 14:34 UTC · original

Thanks for checking the event offsets by hand. Agreed on the order: #2566 first. I am testing #2566 on Linux now (its head ea303801), and I am preparing this branch on top of it so it can follow straight after: take_text after screen_size at API version 21, and the custom output size fitted to #2566's per-screen list (custom_output / output_extras). I will push that rebase when #2566 lands, with the tests you ran repeated on it.

> [@mooki000](https://github.com/mooki000) commented on GitHub · 2026-11-12 14:34 UTC · [original](https://github.com/NotRexed/Hookshot/pull/2594#issuecomment-6826508631) Thanks for checking the event offsets by hand. Agreed on the order: #2566 first. I am testing #2566 on Linux now (its head `ea303801`), and I am preparing this branch on top of it so it can follow straight after: `take_text` after `screen_size` at API version 21, and the custom output size fitted to #2566's per-screen list (`custom_output` / `output_extras`). I will push that rebase when #2566 lands, with the tests you ran repeated on it. <!-- github-sync:c:6826508631 -->
Owner

@mooki000 commented on GitHub · 2026-11-12 18:54 UTC · original

Rebased onto 15d4ae8c6 now that #2566 is merged; final head c102f6f5.
API 22: #2566 took 20 for screen_size, and #2760 took 21 for nvn_program_created; take_text follows screen_size.
A typed listed size reuses its choice. An unlisted size joins its shape's choices; #2566's modes and monitor/native marks stay unchanged.
All 22 final checks pass with GCC 16.2.1 and Clang 23.1.3: the 19 requested checks, both changed #2788 drivers, and the README check. A full run of the project's checks in my fork, Windows included, is green on this exact head.
Earlier full suite: 496 passed, 4 failed, 20 skipped. Two compiler-invocation failures passed after repair; the two main failures were subsequently fixed by #2861 and #2883.
The two game runs were on 4914fdec (main 8cf9a524), Linux/RTX4090, windowless.
The menu run typed 1920x800, moved the caret, committed, selected listed 2560x1080 and 3440x1440, then returned to the custom size; rows and saved sizes agree.
The next start logs picture 1920x800 and camera ratio 2.4000: #2566's rule gives this non-16:9 size its own picture/camera shape; the row shows 21:9.
Both games exited 124 with zero fatal/crash/device-loss/kernel/NVRM/vcore-escape counts. A closing-pipe error in the first observer was recovered from existing receipts, without repeating that game.

The Graphics tab in six steps: 1. typing 1920x8 before committing, 2. the complete text with the caret moved inside, 3. Enter commits 1920x800 and the Aspect ratio becomes 21:9, 4. the custom size in Output resolution, 5. the next listed choice 2560x1080, 6. then 3440x1440

After the restart at 1920x800: Output resolution and Custom output size both read 1920x800, Aspect ratio 21:9

The final head adds a clean rebase over five merges, one touching the menu file, and a docs-only commit. That combination was checked game-free only, not run in the game.
Not tested: Windows gameplay, a real window, HDR, physical keyboard/clipboard/controller input, IME or SDL2. No performance claim.
Assisted-by: GPT-6 Astra
Co-Authored-By: GPT-6 Astra [email protected]

> [@mooki000](https://github.com/mooki000) commented on GitHub · 2026-11-12 18:54 UTC · [original](https://github.com/NotRexed/Hookshot/pull/2594#issuecomment-6832944729) Rebased onto `15d4ae8c6` now that #2566 is merged; final head `c102f6f5`. API **22**: #2566 took 20 for `screen_size`, and #2760 took 21 for `nvn_program_created`; `take_text` follows `screen_size`. A typed listed size reuses its choice. An unlisted size joins its shape's choices; #2566's modes and monitor/native marks stay unchanged. All 22 final checks pass with GCC 16.2.1 and Clang 23.1.3: the 19 requested checks, both changed #2788 drivers, and the README check. A full run of the project's checks in my fork, Windows included, is green on this exact head. Earlier full suite: 496 passed, 4 failed, 20 skipped. Two compiler-invocation failures passed after repair; the two main failures were subsequently fixed by #2861 and #2883. The two game runs were on `4914fdec` (main `8cf9a524`), Linux/RTX4090, windowless. The menu run typed `1920x800`, moved the caret, committed, selected listed `2560x1080` and `3440x1440`, then returned to the custom size; rows and saved sizes agree. The next start logs `picture 1920x800` and `camera ratio 2.4000`: #2566's rule gives this non-16:9 size its own picture/camera shape; the row shows 21:9. Both games exited 124 with zero fatal/crash/device-loss/kernel/NVRM/vcore-escape counts. A closing-pipe error in the first observer was recovered from existing receipts, without repeating that game. ![The Graphics tab in six steps: 1. typing 1920x8 before committing, 2. the complete text with the caret moved inside, 3. Enter commits 1920x800 and the Aspect ratio becomes 21:9, 4. the custom size in Output resolution, 5. the next listed choice 2560x1080, 6. then 3440x1440](https://gist.githubusercontent.com/mooki000/b86bd3f35969803fb032f71f035844f8/raw/df9b0b32e43d66b1ca6febca32b9d610703b701a/131-text-fields-rebased.jpg) ![After the restart at 1920x800: Output resolution and Custom output size both read 1920x800, Aspect ratio 21:9](https://gist.githubusercontent.com/mooki000/b86bd3f35969803fb032f71f035844f8/raw/df9b0b32e43d66b1ca6febca32b9d610703b701a/132-text-fields-saved-start.jpg) The final head adds a clean rebase over five merges, one touching the menu file, and a docs-only commit. That combination was checked game-free only, not run in the game. Not tested: Windows gameplay, a real window, HDR, physical keyboard/clipboard/controller input, IME or SDL2. No performance claim. Assisted-by: GPT-6 Astra Co-Authored-By: GPT-6 Astra <[email protected]> <!-- github-sync:c:6832944729 -->
Commenting is not possible because the repository is archived.
No description provided.