Repository navigation
feat(prompts): a Keyboard face: the keys and mouse buttons bound, in texts, hints and menus - #2435
Conversation
Joey-Magnus
left a comment
There was a problem hiding this comment.
This is a well-built addition. The keyboard face sits on top of the live prompt system from #2366 without disturbing the controller faces. The code-point ranges are checked against each other in tests/test_prompt_font_bind.py: KEY_BASE past every face, slots and sticks clear of BIND_BASE. Auto only falls back to Keyboard when the build actually made prompts/keyboard/, and the textures and the Talk ring stay on the Switch's, which is the safe choice. At the PR's head I ran prompt, bound_keys and menu_hit locally (7 checks), and all pass. On GitHub only the title check has run so far; the other workflows need a maintainer to approve them.
should fix, nx2/prompt_keys.py:61,69-70,120-121,144,191-194: CONTRIBUTING (Code and data conventions) accepts the prompt packages (fontTools, Pillow, astc-encoder-py, texture2ddecoder) "for nx2/prompt_assets.py and nx2/prompt_draw.py alone", and asks reviewers to flag any other use. This new module imports fontTools.pens.* and fontTools.ttLib. The imports are inside functions, so the tests still import it on the standard library alone, which is good. Either move the drawing into nx2/prompt_draw.py, which is where drawing lives, or have the maintainer agree to add prompt_keys.py to that list and update the sentence in CONTRIBUTING in this PR.
should fix (testing): the in-game test installed the new icons into a v2.1.0 build as the Switch face's font. That shows the artwork, but not this PR's host code: auto_face (host/prompts.c:119), the keyboard branch of cmap4_bind, key_slot and the slots kept across live_font (host/prompts.c:967 on), and the stick rewrites in text_code. Those are the parts that write into the game's font in memory while it runs. Please run a build of this branch once: with Keyboard picked, with Auto and no controller, then plug a pad in and out, and rebind a key while a hint is on screen. Note the result in the description. Screenshots from that build would be better than the transplanted ones.
question, third_party/kenney_input/: CONTRIBUTING says not to modify third_party/, and to add a new folder with its own provenance note rather than edit one. This adds an unmodified font from the same Kenney release to the existing folder and adds a line to its PROVENANCE.md. That seems within the spirit of the rule, but it's the maintainer's call. If not, third_party/kenney_input_keyboard/ with its own PROVENANCE.md and License.txt is the literal reading.
minor, host/prompts.c:53,967: the 320 slots are given out once per (button, key) pair for the whole run. Someone who rebinds a lot can use them up, and then key_slot silently falls back to the Switch's icon. A single hos_log the first time it runs out would make that easy to diagnose.
One heads-up: #1990, also open, raises HOS_PAD_N, which is the stride of the BIND_BASE codes this PR builds on. Whichever of the two lands second needs to re-check text_code and the slot/BIND_BASE overlap test.
|
Thanks for the careful review, @Joey-Magnus. Addressed in 81a5445: Prompt packages outside their files:
Slots running out: #1990: noted. Whichever lands second re-checks Testing: agreed, the in-game run so far covered the artwork, not this branch's host code. I'll run a build of this branch through the four cases you list (Keyboard picked, Auto with no controller, a pad plugged in and out, a key rebound with a hint on screen), then update the description with the results and screenshots from that build.
|
…rebound-pause.png
|
@Joey-Magnus I ran a build of this branch (81a5445) in game; the results and screenshots from that build are now in the description. Auto with no controller, Keyboard with a rebound key, and Xbox all show what they should at the start. The live cases didn't run, for a reason outside this PR. @gab9281, this one may interest you (#2366): on native Windows 11 (RTX 4060, the v131072 game) the font part of the live prompts is never found. The textures' pool and the layouts are found: But To rule this PR out, I served a font built without the keyboard face ( |
|
I don't have any windows PC anymore, sadly 😅 |
Joey-Magnus
left a comment
There was a problem hiding this comment.
This will turn main red on merge. #2448 landed today with tests/test_prompt_auto_face.py:51, which requires choose_face to contain face_of_name(name). This PR changes that line in host/prompts.c to auto_face(name). Fix the test to look for auto_face(name) in choose_face and face_of_name(name) in auto_face.
Everything else looks right: all five bind[HOS_SW_N] arrays are widened to BINDS, the enum-only vk_api.h change needs no version bump, and the earlier points are addressed. Still pending: the promised in-game run (Auto with no pad, plugging a pad in and out, rebinding with a hint on screen) and a CI run, which hasn't happened on 81a5445.
No worries! I can't dig further on my side right now either. The log lines above are what a native Windows run shows. If someone with a Windows build wants to look, a log line in |
81a5445 to
b3bb874
Compare
|
@Joey-Magnus thanks, good catch. Rebased onto main (with #2448), and in b3bb874 On the in-game run: it's in the description, from a build of 81a5445, which is the same code before this rebase. Auto with no controller, Keyboard with a rebound key, and Xbox all show what they should at the start. The live cases (rebinding with a hint on screen, plugging a pad in and out) didn't run: on native Windows the font part of #2366's live path is never found, with or without the keyboard face (see the comment above), and I have no controller here. CI still needs a maintainer's approval to run. |
Joey-Magnus
left a comment
There was a problem hiding this comment.
b3bb874 fixes what would have turned main red. The branch is rebased on #2448. tests/test_prompt_auto_face.py now looks for auto_face(name) in choose_face and live_face and for face_of_name(name) in auto_face, and it tests auto_face's keyboard cases. At the new head, test_prompt_auto_face, test_prompt_font_bind, test_prompts_live, test_appimage_prompt_fonts and test_menu_hooks all pass for me, and the branch merges cleanly with main.
The earlier points are done: the drawing is in prompt_draw.py, the font is in its own third_party/kenney_input_keyboard/ with provenance and license, and key_slot logs once when its codes run out. The in-game run covers Auto with no pad, a rebound key and a pad face. Plugging a pad in and out, and rebinding with a hint on screen, still weren't tested. Your comparison points to #2366's font lookup on native Windows rather than this PR; that deserves its own issue. What's left is a full CI run on b3bb874. The #1990 HOS_PAD_N heads-up still applies.
|
This now conflicts with main in |
|
Tested this branch (b3bb874) on native Windows 11 (RTX 5060 Ti, v131072), keyboard + mouse, through the control bridge:
The live part not finding the font on native Windows: found it. Your guess about the size check was close: the face's stream has the served file's size (55088 = With that fix on top of this branch, Keyboard → Xbox → Keyboard while the game runs applies at once ( |
…texts, hints and menus The button prompts showed a controller's buttons even to players on the keyboard and mouse. A Keyboard face (the Button prompts row's seventh value, and Auto's choice with no controller connected) shows the key or mouse button each action is bound to, follows the Controls page while the game runs as the pad faces do, and shows WASD and the mouse for the sticks. - nx2/prompt_keys.py: the keys from Kenney's Input Prompts Keyboard & Mouse font (CC0); the keys whose names are words in the game's own bold font, and the mouse's buttons, drawn; the code points (past every face's, and a range for texts no font of the game uses). - nx2/prompt_assets.py: the keyboard face in the font (the Switch's pad icons, its keys and sticks), its ocarina hint by key. - host/prompts.c: the keys bound followed with the pad inputs; the keyboard face's icons at the start and after a change (a text's code per button and key given out as they come); the Switch's textures and Talk ring places; Auto's keyboard face only with no controller through SDL or XInput (host/nn_hid.c hos_xinput_pads). - gpu/vk/vk_menu.cpp: the Keyboard value and its hint. Part of NotRexed#1007 Assisted-by: Claude Code Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…er of its own, a log when the slots run out - nx2/prompt_keys.py is data alone (the standard library imports it); the drawing (the word keys, the mice, the keyboard face's glyphs) is in nx2/prompt_draw.py, where CONTRIBUTING allows the prompt packages. The font built is the same. - The Keyboard & Mouse font in third_party/kenney_input_keyboard with its own PROVENANCE.md and the release's License.txt; third_party/kenney_input as it was. The AppImage copies it (tests/test_appimage_prompt_fonts.py checks both). - host/prompts.c key_slot: a log line the first time the codes for buttons on keys run out. Assisted-by: Claude Code Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…keyboard face tests/test_prompt_auto_face.py looks for auto_face(name) in choose_face and live_face and face_of_name(name) in auto_face, and checks auto_face: with no controller the keyboard's face when the build made it and XInput has no controller either, else the Switch's; a controller's name its face. Assisted-by: Claude Code Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
b3bb874 to
0a76662
Compare
|
@hckrman101 rebased onto main (55b976c); the branch is at 0a76662 and merges cleanly. The conflicts were all with #1990's second gamepad binding:
@polackangry thanks for testing it and for finding the font offset! I've added your live result with #2583 to the description. |
… stream's start (#2583) On native Windows the live button prompts (#2366) never find the font, so a change of face or of controls waits for a restart (seen in #2435's testing). The game keeps the font there from its stream's start with the stream's size the file's, and `hos_prompts_glyph` looked 8 bytes in, for a header that isn't there. ## What it does `hos_prompts_glyph` (host/prompts.c) takes a stream of the served font's size and looks for the font's head where it looked before (behind the 8-byte header when the size is the file's), then at the stream's start. Whichever holds the head is `font.at`. A face whose bytes aren't readable yet is looked at again, as before; one with neither head is another font, as before. ## Why A diagnostic build on native Windows 11 (RTX 5060 Ti, the v131072 game) logged each face FreeType draws from, its stream's base and size: ``` prompts-diag: face 0x107e504c00 stream 0x107e4f9370 base 0x7fd37800e8 size 55088 (want 55088 or 55080) prompts-diag: face 0x107e504c00 has the size but not the head (00030030 vs 4f54544f) ``` The size is the served file's (55088), so the code read from `base + 8`. The bytes there, `00 03 00 30`, are the OpenType header's own `entrySelector` (3) and `rangeShift` (48): the font starts at `base`, with no header in front. #2435's description and comment guessed a size mismatch; the size matches and the start is the difference. ## Testing In game, native Windows 11, RTX 5060 Ti, keyboard and mouse, driven with the control bridge (headless), a save in the Fire Temple: | Case | Before | After | |---|---|---| | `prompts: the font at ...` | never logged | `prompts: the font at 0x7fd37800e8: the icons in texts follow at once` | | Keyboard → Xbox while the game runs (#2435's branch) | held for a restart | `now the xbox icons in texts`; the pause menu reopened shows LB/RB, A, B | | Xbox → Keyboard while the game runs | held for a restart | `now the keyboard icons in texts`; Q/R, E, Shift again | Tests: `test_prompts_live`, `test_prompt_font_bind`, `test_menu_hooks` pass. The fix was tested on top of #2435's branch, the same function as on main. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…s behind them Upstream's main fails 15 of its own source checks at c356bef (PRs merged without a rebase). These are the eleven outside the upscalers: - vk_memory.cpp: RX_VK_CAPTURE_KEEP_OLD and RX_VK_CAP_BLOCKS are on/off switches and were read as set-means-on, so =0 turned them on (NotRexed#2437). env_on, as the rest. - mouse_native.c: the mouse's kept angles are let go when the camera leaves states 3 and 4 (first-person aim, Z-targeting): they were written back on return, and the view snapped to where it was before (NotRexed#2617; test_mouse_native's yaw). - menu_i18n.h: three English texts changed in vk_menu.cpp (NotRexed#1930, NotRexed#2362, NotRexed#2435), so their 13 translations are keyed and translated again (machine-made, as the file's others). - Fixtures that cut source by its text and missed what the source gained: window_title (NotRexed#2116), dead_import_bytes (NotRexed#1906), env_on in reverb_frame, hos_xinput_pads (NotRexed#2435), wait_recorded's forward declaration (NotRexed#2458); and the Dockerfile check's parser, which took --no-update for a PPA. Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
**What this is for:** restore ten game-free checks whose extracted fixtures or expectations no longer match intentional changes on main; [main's CI log](https://github.com/NotRexed/Hookshot/actions/runs/43151254256) shows these failures. **What is measured:** those ten checks on Linux with GCC 16.2.1 and Clang 23.1.3, plus the strict full suite and deliberate regression mutations, as detailed below. **Not measured:** a game run, Windows execution, audio output or GPU behavior; no production file changes in this diff. ## Cause and changes Each row is a separate commit, naming the change that made the fixture stale; the quoted diagnostics come from [main's failed jobs](https://github.com/NotRexed/Hookshot/actions/runs/43149508038). | Introduced by | Checks / CI diagnostic | Repair | |---|---|---| | #1544, after #2042's switch helper | `audio_aux`, `audio_biquad`: `implicit declaration of function ‘env_on’` | Include the real helper before the extracted DSP. | | #2116 | `display_startup_failure`, `wayland_libdecor_hint`: `use of undeclared identifier 'window_title'` | Extract the real title helper along with its callers. | | #1906 | `texel_buffer_ownership`: `use of undeclared identifier 'dead_import_bytes'` | Model the counter and check that it stays charged until completion. | | #2458 | `readback_part`: `extraneous closing brace ('}')` | Select the complete `wait_recorded` definition, not its new forward declaration. | | #2574 | `dlss_checks`: `FAIL under a third wide at line 40`; `framegen_preset` / `rumble_menu`: undeclared `temporal_reset_failure` | Check both sides of the intended two-pixel tolerance; supply a counted reset hook and exercise the real scaling rows. | | #2435 | `mods_size_table`: `undefined reference to hos_xinput_pads` | Supply the fixture's no-controller answer; retain real merge and routing checks. | The diff retains the existing assertions: no check is deleted or skipped, and it does not revert the production behavior named above. ## How it was tested Recorded on 2026-11-11, Linux kernel `7.2.15-1-omarchy`; base `de866017`, head `eeb09c09`, GCC 16.2.1 and Clang 23.1.3 (compiler version output and test records). - Untouched main `a016b7a6`: `455 passed, 15 failed, 20 skipped`; each of these ten failures also reproduced alone with both compilers, matching the linked CI diagnostics. - Branch: all ten repaired checks pass independently with each compiler (`python -I -B tests/run_all.py --strict --jobs 1 <test>` records). - Negative controls: 14 deliberate source regressions were each rejected with both compilers, without a compile failure—28 behavioral rejections (mutation records). These remove Aux sizing, biquad filtering, startup failure handling, the libdecor hint, view ownership release, part-specific waits, preset preservation, temporal reset or mod-table routing; the DLSS controls independently make each axis's tolerance too narrow and too broad. - One full `python -I -B tests/run_all.py --strict` run: **`464 passed, 6 failed, 20 skipped in 141 s`**, 32 workers, load 7.24 → 28.38 (complete suite output). ### Remaining failures and the path to green The complete branch run names these six failures; the first five are outside this fixture-only diff: | Remaining check | Separate fix | |---|---| | `test_env_on.py` | `fix/capture-switch-values`: the three presence-only capture switch reads from #2437. | | `test_linux_docker_packages.py` | `test/docker-ppa-options`: distinguish `--no-update` from its PPA operand. | | `test_mouse_native.py` | `fix/mouse-camera-handover`: discard retained angles while another camera owns the view. | | `test_menu_i18n.py` | `fix/menu-hint-translations`: update three complete hint keys and their translations. | | `test_hdr_encode.py` | Already repaired in #2614; deliberately not duplicated here. | | `test_release_scripts.py` | An intermittent, unchanged release-script concurrency failure: `SHA256SUMS still differs from the files after 5 uploads`. It passed untouched main and current main CI; isolated reruns passed once and failed once. Neither run invokes a C/C++ compiler. | ## Not tested / risks No Windows, game, display, controller, listening or GPU run was performed; the evidence above is game-free only. The fixture extraction still follows source structure, so future helper/signature changes must update its inputs rather than remove its behavioral guards. ## Overlaps The inspected merge hunks overlap #1834's frame-generation fixture helpers and #2622's older readback extraction; neither contains these repairs. #2614 owns the separate HDR initializer check. Other same-file changes merge cleanly in the recorded overlap comparison. Assisted-by: GPT-6 Astra Co-Authored-By: GPT-6 Astra <noreply@openai.com> --------- Co-authored-by: GPT-6 Astra <noreply@openai.com>
**What this is for:** make the current controls, automatic mesh-shader and automatic button-prompt hints find their translations ([current main CI](https://github.com/NotRexed/Hookshot/actions/runs/43151254256)). **What is measured:** the affected game-free check before and after, with GCC 16.2.1 and Clang 23.1.3 selected, plus one strict full-suite run, as recorded below. This check is Python-only; the two environment runs do not claim compiler coverage. **Not measured:** a game run or Windows execution. ## Cause #1864 imported all thirteen catalogs after #1930, #2362 and #2435 had changed those three complete English hints; runtime lookup is exact, so their old keys no longer match (`gpu/vk/menu_i18n.h`, `vk_menu.cpp`, `vk_menu_i18n.cpp`). The relevant failure in [main's CI log](https://github.com/NotRexed/Hookshot/actions/runs/43151254256) is: ```text 'Gamepad: the D-pad moves, A binds, B goes back.' is no text of vk_menu.cpp (renamed? then translate it again) ``` ## What changed Update all 39 keys and their full translations; leave English help and runtime lookup unchanged. Retain exact membership, order, count, formatting and whitespace checks, and require all three current hints in every language so deleting an entry cannot hide the mismatch. ## How it was tested Recorded on 2026-11-11, Linux kernel `7.2.15-1-omarchy`, base `de866017`, head `f9a7e70f` (version and test records). - Untouched main `a016b7a6`: the affected check fails independently in both compiler environments with the diagnostic above; the branch passes independently in both (`python -I -B tests/run_all.py --strict --jobs 1 tests/test_menu_i18n.py` records). - Each old key was restored independently, and each replacement was separately removed with its table count reduced: all six mutations fail in both environments (12 records), including the new completeness check. - One full `python -I -B tests/run_all.py --strict`: **`456 passed, 14 failed, 20 skipped in 162 s (linux, 32 at once)`**, load 81.38 → 42.70 (complete suite output). - That run's remaining failures are: `test_wayland_libdecor_hint.py`, `test_display_startup_failure.py`, `test_texel_buffer_ownership.py`, `test_audio_aux.py`, `test_audio_biquad.py`, `test_dlss_checks.py`, `test_env_on.py`, `test_framegen_preset.py`, `test_hdr_encode.py`, `test_linux_docker_packages.py`, `test_mods_size_table.py`, `test_mouse_native.py`, `test_readback_part.py`, `test_rumble_menu.py` (complete suite output). The companion fixture, capture-switch, PPA-parser and camera-handover branches address their separate causes; #2614 owns the HDR check and is not duplicated here. ## About the translations The added clauses in the 39 entries were written with a language model, in the wording of each catalogue's existing entry (the part that was already translated is kept word for word). Nobody who speaks these languages natively has read them; corrections are welcome and will not break the check, which compares only the English keys. ## Not tested / risks No game or layout screenshot was taken, and no native-speaker review was performed; the checks establish key coverage and structural consistency, not linguistic or visual polish. ## Overlaps #2417 is an alternative localization implementation, not a repair of these catalogs; it and #2566 merge cleanly with this change in the recorded overlap comparison. Assisted-by: GPT-6 Astra Co-Authored-By: GPT-6 Astra <noreply@openai.com> Co-authored-by: GPT-6 Astra <noreply@openai.com>
- main's 19 is NotRexed#1930's ocarina note bindings (a changed meaning of the pad binding calls, no new field); MkwVkHost.data_dir, still the last field and last in the host's initializer, is 20. - host/prompts.c: NotRexed#2435's keyboard face; choose_face takes the game folder again for prompts/keyboard (a build input), the options stay the data folder's. NotRexed#2435 adds no generated file of its own: all still in cache/prompts. - host/nn_hid.c, docs/development.md: main's text (the ocarina's note controls, the RX_VK_FALLBACK_PHI_LIMIT row) with the data folder's places. - tests/test_ocarina_note_bindings.py, tests/test_ocarina_notes.py (NotRexed#1930): stub hos_data_path as tests/test_bound_keys.py does. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Players on the keyboard and mouse see a controller's buttons in every prompt (the Switch's when no controller is connected), so they have to work out which key each one means. This adds a Keyboard face to the Button prompts system: the game's texts, hints and menus show the key or mouse button each action is bound to.
Part of #1007 (keyboard button prompts, from #2028).
What it does
How
nx2/prompt_keys.py(new, data only, standard library): which icon each key and stick shows, and the code points.nx2/prompt_draw.py: the keyboard face's glyphs. They are Kenney's Input Prompts Keyboard & Mouse font (CC0, the same 1.5 release as the controller faces), in its own folderthird_party/kenney_input_keyboardwith aPROVENANCE.mdand the release'sLicense.txt; the AppImage copies it. The word keys and the mice are drawn as non-crossing contours, so they fill the same under either winding rule.nx2/prompt_assets.py:faces_fontbuilds the keyboard face (the Switch's pad icons, plus each key's icon atKEY_BASE + key code, past every face's code points), and writes itshint.binper key code.host/prompts.c:BINDS), so key changes go through the same live path.cmap4_bindpoints a button at its key's icon on the keyboard face.text_codegives a text a code of its own for each (button, key) pair as it first appears (key_slot), and points it at the key's icon where the game keeps the font.host/nn_hid.c:hos_xinput_pads(), so Auto doesn't pick Keyboard for an XInput-only controller.gpu/vk/vk_menu.cpp: the Keyboard value and its hint;pad_namenames pad inputs by place on the keyboard face.Testing
In game, a build of this branch (81a5445, the same code before the rebases onto #2448 and #1990) on Windows 11 (native, RTX 4060), keyboard and mouse, no controller. Driven with the control bridge (
HOS_CONTROL, headless,--useron a copy of the saves), into Hyrule Field from a save, then the pause menu:prompts: auto: controller "(none)": keyboard,keyboard icons, 16 buttons on these keys. The pause menu shows Q/R on the tabs, the mouse for Rotate, E for Select, Shift for Back; the HUD's item buttons show R and X.now the xbox icons in texts/now the keyboard icons in texts), the pause menu reopened showing each face's icons. Without #2583 the row says "(restart)" on native Windows, as for every face.Also:
tests/test_prompt_font_bind.pyis extended. On the keyboard face it checks: keys bound in the first or second slot, a key with no icon, the sticks, and text codes for each (button, key) pair. The other faces are unchanged. It passes, as doprompts_live,appimage_prompt_fontsandmenu_hooks.tests/run_all.py: the same results with and without the PR on this machine (its other failures are missing .NET and MSVC-target C++ headers).Screenshots
From the build of this branch, 1280×720 (the bridge's shots: the game's own picture).
Auto with no controller, the pause menu: Q/R tabs, the mouse for Rotate, E for Select, Shift for Back.
Keyboard with A rebound to F: Select reads F.
Xbox, for comparison (a pad face, as before this PR).
The field: the HUD's item buttons on R and X.
🤖 Generated with Claude Code