Repository navigation
feat(input): add a second gamepad binding in Controls - #1990
Conversation
28b50c0 to
9c08d4d
Compare
Joey-Magnus
left a comment
There was a problem hiding this comment.
Nice feature, and the core is solid. The binding table moved from Interlocked* to an SRW lock, and that works on Linux through the host/posix/windows.h shim. The ini format stays readable both ways: an older build's atoi("1,17") still reads slot 0. The paddles keep SDL3's order (right/left paddle 1, then 2), so R4/L4/R5/L5 are the right names. The tests are extended rather than weakened (test_bound_keys, test_menu_hit, test_touchpad_menu). The one red CI job (test_release_scripts on Ubuntu) is unrelated to this change.
The problem is main. This branch is 69 commits behind and conflicts in gpu/vk/vk_api.h, host/prompts.c and tests/test_bound_keys.py. One of those conflicts needs more than picking a side:
blocking, gpu/vk/vk_api.h:47 (HOS_PAD_N 17 → 21) against main's host/prompts.c: since #2366, the live button prompts give each Switch button × pad input a code point of its own, at BIND_BASE + (face * HOS_SW_N + b) * HOS_PAD_N + pad (text_code on main). nx2/prompt_assets.py builds the font with a stride of len(PADS) = 17. Once this PR raises HOS_PAD_N, the game points texts at code points the font doesn't have at those places. I merged this branch into main locally, kept main's prompts.c, and set the API version to 18: tests/test_prompt_font_bind.py then fails with face 0: text code U+E041 became 61471, not 61467. When you rebase, keep the prompts' stride at the 17 inputs that have artwork. For example, give it its own constant (HOS_PAD_RTRIGGER + 1) in text_code and anywhere else prompts.c uses HOS_PAD_N for code points. Also make a paddle bound in slot 0 fall back to switch_pad[b], as you did in cmap4_bind (host/prompts.c:124 here). The alternative is to add the paddles to PADS in prompt_assets.py and draw artwork for them, but that's a bigger change.
blocking, gpu/vk/vk_api.h:18: main is at MKWVK_API_VERSION 17 now, so this needs to become 18, not 15.
question, host/nn_hid.c:608-613 (hos_bind): the old swap kept every button bound ("none left without"). With a second slot, binding an input that is already some button's only input into an empty Input 2 moves it there, because old is -1. That button is then left with no gamepad input. For example, put Y's face button into A's Input 2, and Y becomes None. The menu does show it, so this may be what you want, but it's easy to do by accident on the face buttons. Should a slot-1 bind skip inputs that are another button's slot 0, or show a short note in the help box when it takes one?
minor, nx2/build.py:843: making vk_api.h a host dependency on Windows too is right, now that the host reads HOS_PAD_N. It's worth one sentence in the description, since it changes rebuild behaviour beyond this feature.
Give each gamepad action two slots in the Controls menu. Read SDL paddles and accept either binding in play. Load old settings with an empty alternate slot and keep the two bindings together when saving. Update the shared input interface version and include its header in Windows host cache keys so incremental builds rebuild both sides. Exercise the second binding and its mouse hit target in the existing input and menu regression tests. Assisted-by: OpenAI Codex Co-Authored-By: Codex <codex@openai.com> Review (Joey-Magnus): rebased on main; MKWVK_API_VERSION 18 (main is at 17); the prompt font's code points keep their stride of the 17 inputs with artwork (HOS_PAD_ART_N, nx2/prompt_assets.py PADS), a paddle bound to Input 1 shows its Switch button's default icon; a button's only gamepad input is not moved into another's empty Input 2 (taken from a button with an Input 2, that one moves up), with a note in the Controls help. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01KbciDXYLkJnK9V27XzPsus
9c08d4d to
49371a5
Compare
|
Thanks for the review! All points are addressed in 49371a5. To your question about The rest:
|
Joey-Magnus
left a comment
There was a problem hiding this comment.
Everything from my earlier review is fixed in 49371a5. MKWVK_API_VERSION is 18. The prompt code points use the 17-input stride through HOS_PAD_ART_N in text_code and cmap4_bind, with paddles falling back to switch_pad[b]. A slot-1 bind no longer takes another button's only input. I merged it into today's main: no conflict, and run_all gives 376 passed, 0 failed. test_bound_keys, test_prompt_font_bind, test_touchpad_menu and test_menu_hit all pass on their own too.
There's a collision with #2435 (Keyboard prompt face) for whichever PR lands second. #2435 still indexes prompt code points by HOS_PAD_N in text_code (BIND_BASE + (face * HOS_SW_N + b) * HOS_PAD_N + pad). Once this PR makes HOS_PAD_N 21, that has to become HOS_PAD_ART_N or the font lookups shift. #2435 also adds a HOS_PROMPT_KEYBOARD row, while pad_name here is sized [HOS_PROMPT_N + 1][HOS_PAD_RPADDLE1]. The keyboard row would then be all null pointers, so it has to keep #2435's face < HOS_PROMPT_KEYBOARD guard. CI still needs approving for this fork.
…ves in two places, API 19 - NotRexed#2282 (the Linux saves' move leaves a linked folder alone) is carried into host/hos_data.c: on Linux a link in the saves is made again in the data folder to the same folder (never renamed or copied through), a link to an entry that moves too follows it, and a user/ that is a link itself is used as it is. On Windows a link or junction keeps the saves where they are. tests/test_linux_saves_move.py runs its cases on hos_data.c. - Saves in both <game>/user and the data folder (a downgrade, then the upgrade again): this folder's are used, nothing is merged, and saves-in-two-places.txt and the log say where both are and how to switch. - MKWVK_API_VERSION 19: main's 18 is NotRexed#1990's pad inputs. - Conflicts in nn_hid.c, docs/saves.md and docs/development.md: main's text with the data folder's places. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…texts, hints and menus (#2435) 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 - **Button prompts → Keyboard** (a seventh value). **Auto** picks it when no controller is connected, through SDL or XInput; with one connected, Auto picks that controller's face as before. - Each Switch button shows the first key bound to it on the Controls page, or the second when the first slot is clear. Mouse buttons show as a mouse with that button lit, and keys named by words (Ctrl, Alt, Esc, Home, PgUp, Num1...) as wider keys set in the game's own bold font. Kenney's versions of those are too small to read at prompt size. - The sticks' icons (Move, Change View and their direction variants) show **WASD** (or W/A/S/D, AD, WS) and the mouse moving. - It follows the live system from #2366: changing a key, or switching to or from Keyboard, shows at once, like a pad rebind. - Kept as the Switch's: the textures (ocarina notes, the letters on black, the pause menu's controller picture) and the Talk ring's places. The ocarina hint is spaced by the keys' icon widths. ## 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 folder `third_party/kenney_input_keyboard` with a `PROVENANCE.md` and the release's `License.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_font` builds the keyboard face (the Switch's pad icons, plus each key's icon at `KEY_BASE + key code`, past every face's code points), and writes its `hint.bin` per key code. - `host/prompts.c`: - The bind arrays carry each button's first key after its pad input (`BINDS`), so key changes go through the same live path. - `cmap4_bind` points a button at its key's icon on the keyboard face. - `text_code` gives 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. - Code points: the per-pair codes come from 320 codes at U+EEC0–U+EFFF, and the sticks' codes are at U+F8C0–U+F8DF. I checked every font in the romfs: none uses either range. The CJK fonts use most of the private use area, including parts of U+F700–U+F7BF. - `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_name` names 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, `--user` on a copy of the saves), into Hyrule Field from a save, then the pause menu: | Case | Result | |---|---| | Auto, no controller | ✅ `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. | | Keyboard picked, a key rebound (A from E to F) | ✅ Select reads F. | | Xbox picked (a pad face, unchanged by this PR) | ✅ LB/RB, Select on B and Back on A (the pad layout in use); the ocarina hint spread for Xbox's icons. | | A change while the game runs (Keyboard ↔ Xbox) | ✅ with #2583: on native Windows #2366's live path never found the font (it read it 8 bytes past its start), with or without the keyboard face. @polackangry found it and fixed it in #2583, and with that on top of this branch, Keyboard → Xbox → Keyboard while the game runs applies at once (`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. | | A pad plugged in and out, a key rebound with a hint on screen |⚠️ Not tested: no controller here, and both go through the live path. | Also: - `tests/test_prompt_font_bind.py` is 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 do `prompts_live`, `appimage_prompt_fonts` and `menu_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](https://claude.com/claude-code) --------- Co-authored-by: Needicla <226049938+Needicla@users.noreply.github.com> Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
I wanted to roll with either the right face button or R4 on my Steam Controller. Hookshot currently lets you assign one gamepad button per action.
This adds Input 1 and Input 2 under Gamepad in the F1 Controls menu. Either input can trigger the action. Existing bindings load into Input 1, and Input 2 starts empty. The menu can also capture rear paddles such as R4 on supported controllers.
Testing
mainatbebb0c6(review round 1). The binding, prompt font, menu click, and touchpad tests pass.mainwith the same tools.Note
gpu/vk/vk_api.his now a host dependency on Windows too (it was Linux-only), so a change to it also rebuilds the host. The host readsHOS_PAD_Nfrom it.Screenshots
Current Controls layout at 1280 × 720
Steam Controller test on v2.0.14
AI disclosure
OpenAI Codex assisted with implementation and automated testing. Claude (Anthropic) assisted with the review changes.