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

Type: fix · Risk: low · Tested on: Linux x86-64 (game-free checks only) · Needs: none

Why

With a button set to Toggle on the Controls page (#137), the pause menu's mouse clicks on that button latch.

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.

How to reproduce

Seen on: found reading the code; not seen in the game. Game-free checks on Linux x86-64.
Settings and switches: R set to Toggle on the Controls page (#137).
Steps: Click the R page arrow in the game's pause menu twice.
Expected: The pages turn once per click; each click gives R and then nothing.
Actual: 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.

What changed

The host's menu clicks stay separate from the player's presses, so Toggle no longer latches those clicks. Physical presses, scripts and control bridge presses keep their Toggle behavior.

  • 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.

Measurements

Verdict: the new hold-taps case fails on main and passes here; the suite has no new failures, including after the rebase.

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: 2026-11-14; branch fix/toggle-menu-taps. 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

Before: main @ 5ad0f503 After: 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).

After rebasing onto main @ 543846eb (2026-11-14): #66 (the Walk key) changed read_input and the test's case lists next to this fix. The rebase keeps both: read_input still calls hos_oot_autoload_input and now hands back taps, and hold-taps sits beside walk-toggle. One run each: tests/run_all.py gives 485 passed, 2 failed, 84 skipped on main @ 543846eb and on this PR @ 9953a030; the two failing on both are test_bake_portability.py (zstandard) and test_walk_key.py. test_input_background.py passes every case, walk-toggle and hold-taps included. host/nn_hid.c with -fsyntax-only (llvm-mingw clang, gcc): the same messages as main's. The Skip title screen row presses A through hos_hid_tap too, so with A on Toggle its presses no longer latch either.

Flags and settings

None.

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.

Must be tested: None.

Risks

What could break: 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.

Turning it off: Reverting the commit puts the shared word back.

Compatibility: Menu clicks give plain presses with Toggle enabled; the player's presses still latch.

Checklist

Everyone

  • One topic.
  • The title is a Conventional Commit (type(scope): description) of the status line's type.
  • Why gives the reason plainly and links the issues that fit (the tracker searched).
  • Measurements has a verdict, or one line saying why there is nothing to measure.
  • Testing names what was tested, what wasn't, and what must still be tested, where and how.
  • Rebased on current main.
  • python -I -B tests/run_all.py passes (any failure that also fails on main is named).
  • 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.

When it applies

  • Numbers from the same PC, scene and settings, at least 2 interleaved runs each, the median reported.
  • 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.
  • Host code builds on both Windows (llvm-mingw) and Linux, with UTF-8 paths.
**Type:** fix · **Risk:** low · **Tested on:** Linux x86-64 (game-free checks only) · **Needs:** none ## Why With a button set to Toggle on the Controls page (#137), the pause menu's mouse clicks on that button latch. `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. ## How to reproduce **Seen on:** found reading the code; not seen in the game. Game-free checks on Linux x86-64. **Settings and switches:** R set to Toggle on the Controls page (#137). **Steps:** Click the R page arrow in the game's pause menu twice. **Expected:** The pages turn once per click; each click gives R and then nothing. **Actual:** 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. ## What changed The host's menu clicks stay separate from the player's presses, so Toggle no longer latches those clicks. Physical presses, scripts and control bridge presses keep their Toggle behavior. - 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. ## Measurements **Verdict:** the new `hold-taps` case fails on main and passes here; the suite has no new failures, including after the rebase. 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: 2026-11-14; branch `fix/toggle-menu-taps`. `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 | | Before: main @ 5ad0f503 | After: 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). After rebasing onto main @ 543846eb (2026-11-14): #66 (the Walk key) changed `read_input` and the test's case lists next to this fix. The rebase keeps both: `read_input` still calls `hos_oot_autoload_input` and now hands back `taps`, and `hold-taps` sits beside `walk-toggle`. One run each: `tests/run_all.py` gives 485 passed, 2 failed, 84 skipped on main @ 543846eb and on this PR @ 9953a030; the two failing on both are `test_bake_portability.py` (zstandard) and `test_walk_key.py`. `test_input_background.py` passes every case, `walk-toggle` and `hold-taps` included. `host/nn_hid.c` with `-fsyntax-only` (llvm-mingw clang, gcc): the same messages as main's. The Skip title screen row presses A through `hos_hid_tap` too, so with A on Toggle its presses no longer latch either. ### Flags and settings None. ## 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. **Must be tested:** None. ## Risks **What could break:** 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. **Turning it off:** Reverting the commit puts the shared word back. **Compatibility:** Menu clicks give plain presses with Toggle enabled; the player's presses still latch. ## Checklist ### Everyone - [x] One topic. - [x] The title is a Conventional Commit (`type(scope): description`) of the status line's type. - [x] Why gives the reason plainly and links the issues that fit (the tracker searched). - [x] Measurements has a verdict, or one line saying why there is nothing to measure. - [x] Testing names what was tested, what wasn't, and what must still be tested, where and how. - [x] Rebased on current `main`. - [x] `python -I -B tests/run_all.py` passes (any failure that also fails on `main` is named). - [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. ### When it applies - [x] Numbers from the same PC, scene and settings, at least 2 interleaved runs each, the median reported. - [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. - [x] Host code builds on both Windows (llvm-mingw) and Linux, with UTF-8 paths.
fix(input): a Toggle button no longer latches the game menus' mouse clicks
Some checks failed
PR description / Description follows the template (pull_request_target) Successful in 10s
Website / Website on Cloudflare (pull_request_target) Has been skipped
CI / Conventional Commit title (pull_request) Has been cancelled
CI / Python source checks (ubuntu-24.04, part 1/2) (pull_request) Has been cancelled
CI / Python source checks (ubuntu-24.04, part 2/2) (pull_request) Has been cancelled
CI / SPIR-V fixtures (global narrow load/store, direct surface atomics, F2I immediate) (pull_request) Has been cancelled
CI / Workflow lint (actionlint) (pull_request) Has been cancelled
Third-party parts / As their notes say (pull_request) Has been cancelled
Third-party parts / The same as upstream (pull_request) Has been cancelled
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
jarynclouatre force-pushed fix/toggle-menu-taps from 511f624862
Some checks failed
PR description / Description follows the template (pull_request_target) Successful in 10s
Website / Website on Cloudflare (pull_request_target) Has been skipped
CI / Conventional Commit title (pull_request) Has been cancelled
CI / Python source checks (ubuntu-24.04, part 1/2) (pull_request) Has been cancelled
CI / Python source checks (ubuntu-24.04, part 2/2) (pull_request) Has been cancelled
CI / SPIR-V fixtures (global narrow load/store, direct surface atomics, F2I immediate) (pull_request) Has been cancelled
CI / Workflow lint (actionlint) (pull_request) Has been cancelled
Third-party parts / As their notes say (pull_request) Has been cancelled
Third-party parts / The same as upstream (pull_request) Has been cancelled
to 9953a030fe
Some checks are pending
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 / Conventional Commit title (pull_request) Blocked by required conditions
CI / Workflow lint (actionlint) (pull_request) Blocked by required conditions
Third-party parts / The same as upstream (pull_request) Blocked by required conditions
Third-party parts / As their notes say (pull_request) Blocked by required conditions
Website / Website on Cloudflare (pull_request) Blocked by required conditions
Website / Website on Cloudflare (pull_request_target) Has been skipped
PR description / Description follows the template (pull_request_target) Successful in 14s
beanstalk/approval Test farm not requested
2026-11-14 17:41:58 +00:00
Compare
Some checks are pending
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 / Conventional Commit title (pull_request) Blocked by required conditions
CI / Workflow lint (actionlint) (pull_request) Blocked by required conditions
Third-party parts / The same as upstream (pull_request) Blocked by required conditions
Third-party parts / As their notes say (pull_request) Blocked by required conditions
Website / Website on Cloudflare (pull_request) Blocked by required conditions
Website / Website on Cloudflare (pull_request_target) Has been skipped
PR description / Description follows the template (pull_request_target) Successful in 14s
beanstalk/approval Test farm not requested
This pull request doesn't have enough approvals yet. 0 of 1 approvals granted.
Some workflows are waiting to be reviewed.
This branch is out-of-date with the base branch
You are not authorized to merge this pull request.
View command line instructions

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.