fix(input): a Toggle button no longer latches the game menus' mouse clicks #339

Open
jarynclouatre wants to merge 1 commit from jarynclouatre/Longshot:fix/toggle-menu-taps into main
Contributor

Why

With a button set to Toggle on the Controls page (#137), the pause menu's mouse clicks on that button latch. Set R to Toggle, then click the R page arrow in the game's pause menu twice: the first click holds R for good, the second lets it go, and the pages do not turn once per click. The same happens with A, L, Y or B and the clicks that tap them.

host/oot_menu.c sends its clicks as short presses through hos_hid_tap ("0:R/75"). host_taps in host/nn_hid.c parses them into the same presses[] as a script's, scripted_input ORs them into the buttons word, and npad_states runs hold_toggle_apply over that whole word. A tap's start is a rising edge, so it latches; its end is ignored; the next tap lets go.

What changed

  • input (host/nn_hid.c): a Press remembers whether it is the host's (host_taps parses with live 2). scripted_input and read_input hand the host's active presses back in a word of their own (taps); npad_states latches the player's word and ORs taps in after hold_toggle_apply. The two words stay apart, so a physical press of the same button held across a click makes no second edge when the click ends. read_input's own checks of held ZR, L and R for the mouse (hos_mouse_native_free, mouse_stick) still see the clicks. Script (HOS_INPUT_SCRIPT, HOS_INPUT_LIVE) and control bridge presses latch as before; the pause edge (Plus, Minus) is unchanged.
  • tests (tests/test_input_background.py, tests/input_background_test.c): the fixture now uses production's hos_hid_tap and host_taps instead of an empty spy. New case hold-taps: with R, then A, set to Toggle, two clicks give the button and then nothing each time (main: R, R, 0, 0 instead of R, 0, R, 0); a physical press still latches, a click while it is held or latched changes nothing, and the next physical press lets go. The existing hold-toggle case (bridge presses latch) is unchanged and passes.

Flags and settings

None.

Measurements

main: 5ad0f503 (2026-11-14)
this PR: fix/toggle-menu-taps @ 511f6248
Hardware: none used (game-free checks on a Linux x86-64 PC, gcc 15.2, Python 3.14.4; no clang++ there)
Scene: none, the game was not run
Method: python3 -I -B tests/run_all.py, two runs each, interleaved (main, PR, main, PR, with the other two branches' runs between); both runs of a side gave the same counts. The PR's new or changed checks also run against main's source.
How to reproduce: the commands above in a checkout of each commit

main @ 5ad0f503 this PR @ 511f6248
tests/run_all.py (run 1, run 2) 465 passed, 3 failed, 80 skipped (both) 465 passed, 3 failed, 80 skipped (both)
failing checks test_bake_portability.py, test_pad_layout.py, test_menu_keys.py the same three
test_input_background.py --case hold-taps fails: after the first click ends the game still reads R passes

test_bake_portability.py fails here for a missing Python module (zstandard), on main as well. test_pad_layout.py fails on main since #137 (its fixture lacks host/hold_toggle.h; #331 and #338 fix it). test_menu_keys.py doesn't build with g++ on main since #137 (#338 fixes it).

Testing

Tested: the game-free suite on a Linux PC, as in Measurements. host/nn_hid.c compiles (-fsyntax-only) with llvm-mingw 20261104's x86_64-w64-mingw32-clang and with Linux gcc 15.2, with the same warnings as main's; no file paths are touched.

Not tested: the pause menu in the game with a Toggle button. The fixture runs production's tap parser, input merge and Npad writer, which is where the latch happened.

Needs other testers: None.

Screenshots

Nothing on screen.

Risk and rollback

A Toggle button no longer latches from the host's own clicks; anything that wanted a menu click to latch would now get a plain press. Reverting the commit puts the shared word back.

Reviewer notes

Issues

None.

Checklist

For everyone

  • One topic.
  • The title is a Conventional Commit (type(scope): description).
  • Rebased on current main.
  • python -I -B tests/run_all.py run and passing (any failure that also fails on main is named above).
  • Tested in the game, or what wasn't is named under Testing.
  • Testing filled in: what was tested, what wasn't, and what needs other testers (with test requests).
  • Measured: main and this PR at named commits, on the same PC, scene and settings, at least 2 interleaved runs each, the median reported.
  • No game files, dumps, build output or generated files.
  • third_party/ untouched (a new folder with a PROVENANCE.md instead).
  • The license is OK: GPL-3.0-or-later, nothing incompatible and nothing decompiled.

If applicable

  • Host code builds on both Windows (llvm-mingw) and Linux, with UTF-8 paths.
  • A game-free test added for the fix, failing on main and passing here.
  • A check under tests/ that reads changed source updated in this PR, not weakened.
## Why With a button set to Toggle on the Controls page (#137), the pause menu's mouse clicks on that button latch. Set R to Toggle, then click the R page arrow in the game's pause menu twice: the first click holds R for good, the second lets it go, and the pages do not turn once per click. The same happens with A, L, Y or B and the clicks that tap them. `host/oot_menu.c` sends its clicks as short presses through `hos_hid_tap` ("0:R/75"). `host_taps` in `host/nn_hid.c` parses them into the same `presses[]` as a script's, `scripted_input` ORs them into the buttons word, and `npad_states` runs `hold_toggle_apply` over that whole word. A tap's start is a rising edge, so it latches; its end is ignored; the next tap lets go. ## What changed - input (`host/nn_hid.c`): a `Press` remembers whether it is the host's (`host_taps` parses with `live` 2). `scripted_input` and `read_input` hand the host's active presses back in a word of their own (`taps`); `npad_states` latches the player's word and ORs `taps` in after `hold_toggle_apply`. The two words stay apart, so a physical press of the same button held across a click makes no second edge when the click ends. `read_input`'s own checks of held ZR, L and R for the mouse (`hos_mouse_native_free`, `mouse_stick`) still see the clicks. Script (`HOS_INPUT_SCRIPT`, `HOS_INPUT_LIVE`) and control bridge presses latch as before; the pause edge (Plus, Minus) is unchanged. - tests (`tests/test_input_background.py`, `tests/input_background_test.c`): the fixture now uses production's `hos_hid_tap` and `host_taps` instead of an empty spy. New case `hold-taps`: with R, then A, set to Toggle, two clicks give the button and then nothing each time (main: R, R, 0, 0 instead of R, 0, R, 0); a physical press still latches, a click while it is held or latched changes nothing, and the next physical press lets go. The existing `hold-toggle` case (bridge presses latch) is unchanged and passes. ## Flags and settings None. ## Measurements main: 5ad0f503 (2026-11-14) this PR: fix/toggle-menu-taps @ 511f6248 Hardware: none used (game-free checks on a Linux x86-64 PC, gcc 15.2, Python 3.14.4; no clang++ there) Scene: none, the game was not run Method: `python3 -I -B tests/run_all.py`, two runs each, interleaved (main, PR, main, PR, with the other two branches' runs between); both runs of a side gave the same counts. The PR's new or changed checks also run against main's source. How to reproduce: the commands above in a checkout of each commit | | main @ 5ad0f503 | this PR @ 511f6248 | |---|---|---| | `tests/run_all.py` (run 1, run 2) | 465 passed, 3 failed, 80 skipped (both) | 465 passed, 3 failed, 80 skipped (both) | | failing checks | `test_bake_portability.py`, `test_pad_layout.py`, `test_menu_keys.py` | the same three | | `test_input_background.py --case hold-taps` | fails: after the first click ends the game still reads R | passes | `test_bake_portability.py` fails here for a missing Python module (zstandard), on main as well. `test_pad_layout.py` fails on main since #137 (its fixture lacks `host/hold_toggle.h`; #331 and #338 fix it). `test_menu_keys.py` doesn't build with g++ on main since #137 (#338 fixes it). ## Testing **Tested:** the game-free suite on a Linux PC, as in Measurements. `host/nn_hid.c` compiles (`-fsyntax-only`) with llvm-mingw 20261104's x86_64-w64-mingw32-clang and with Linux gcc 15.2, with the same warnings as main's; no file paths are touched. **Not tested:** the pause menu in the game with a Toggle button. The fixture runs production's tap parser, input merge and Npad writer, which is where the latch happened. **Needs other testers:** None. ## Screenshots Nothing on screen. ## Risk and rollback A Toggle button no longer latches from the host's own clicks; anything that wanted a menu click to latch would now get a plain press. Reverting the commit puts the shared word back. ## Reviewer notes ## Issues None. ## Checklist ### For everyone - [x] One topic. - [x] The title is a Conventional Commit (`type(scope): description`). - [x] Rebased on current `main`. - [x] `python -I -B tests/run_all.py` run and passing (any failure that also fails on `main` is named above). - [x] Tested in the game, or what wasn't is named under Testing. - [x] Testing filled in: what was tested, what wasn't, and what needs other testers (with test requests). - [x] Measured: main and this PR at named commits, on the same PC, scene and settings, at least 2 interleaved runs each, the median reported. - [x] No game files, dumps, build output or generated files. - [x] `third_party/` untouched (a new folder with a `PROVENANCE.md` instead). - [x] The license is OK: GPL-3.0-or-later, nothing incompatible and nothing decompiled. ### If applicable - [x] Host code builds on both Windows (llvm-mingw) and Linux, with UTF-8 paths. - [x] A game-free test added for the fix, failing on `main` and passing here. - [x] A check under `tests/` that reads changed source updated in this PR, not weakened.
fix(input): a Toggle button no longer latches the game menus' mouse clicks
Some checks are pending
CI / Conventional Commit title (pull_request) Blocked by required conditions
CI / Python source checks (ubuntu-24.04, part 1/2) (pull_request) Blocked by required conditions
CI / Python source checks (ubuntu-24.04, part 2/2) (pull_request) Blocked by required conditions
CI / SPIR-V fixtures (global narrow load/store, direct surface atomics, F2I immediate) (pull_request) Blocked by required conditions
CI / Workflow lint (actionlint) (pull_request) Blocked by required conditions
Third-party parts / As their notes say (pull_request) Blocked by required conditions
Third-party parts / The same as upstream (pull_request) Blocked by required conditions
PR description / Description follows the template (pull_request_target) Successful in 10s
Website / Website on Cloudflare (pull_request_target) Has been skipped
511f624862
The pause menu's mouse clicks reach the game as short presses from the host
(hos_hid_tap, host/oot_menu.c). They went into the same button word as the
player's, and hold-to-toggle latched that word, so with R set to Toggle the
first click on the R page arrow held R for good and the second let it go.

The host's presses are now kept apart from the player's (Press.host, a taps
word from read_input) and added to the game's word after the latch. Script
and control bridge presses still latch, as before. read_input's own checks of
held ZR, L and R for the mouse see the clicks as they did.
jarynclouatre changed title from WIP: fix(input): a Toggle button no longer latches the game menus' mouse clicks to fix(input): a Toggle button no longer latches the game menus' mouse clicks 2026-11-14 02:15:15 +00:00
Some checks are pending
CI / Conventional Commit title (pull_request) Blocked by required conditions
CI / Python source checks (ubuntu-24.04, part 1/2) (pull_request) Blocked by required conditions
CI / Python source checks (ubuntu-24.04, part 2/2) (pull_request) Blocked by required conditions
CI / SPIR-V fixtures (global narrow load/store, direct surface atomics, F2I immediate) (pull_request) Blocked by required conditions
CI / Workflow lint (actionlint) (pull_request) Blocked by required conditions
Third-party parts / As their notes say (pull_request) Blocked by required conditions
Third-party parts / The same as upstream (pull_request) Blocked by required conditions
PR description / Description follows the template (pull_request_target) Successful in 10s
Website / Website on Cloudflare (pull_request_target) Has been skipped
This pull request has changes conflicting with the target branch.
  • host/nn_hid.c
  • tests/test_input_background.py
Some workflows are waiting to be reviewed.
View command line instructions

Manual merge helper

Use this merge commit message when completing the merge manually.

Checkout

From your project repository, check out a new branch and test the changes.
git fetch -u 'fix/toggle-menu-taps:jarynclouatre-fix/toggle-menu-taps'
git switch 'jarynclouatre-fix/toggle-menu-taps'
Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
MagicBeanDev/Longshot!339
No description provided.