fix(input): keep camera inversion out of stick rest detection #2286

Merged
Arkex-ai merged 1 commit from fix/stick-rest-inversion into main 2026-11-11 17:06:51 +00:00
Arkex-ai commented 2026-11-11 13:32:07 +00:00 (Migrated from github.com)

Summary

Changing camera X/Y inversion can make the camera spin again after the held-stick protection blocked a phantom axis. The setting flips the sign used to recognize a physical crossing, permanently admitting the unchanged held input; reversing the setting then reverses the spin instead of stopping it.

Fixes #2273 by keeping rest detection independent of configurable output inversion.

Technical changes

  • Admit each SDL axis in a stable orientation, then apply camera X/Y inversion to its output.
  • Preserve genuine neutral/crossing recovery, the selected dead zone, independently tracked devices/axes and immediate inversion of already-admitted input.
  • Add a game-free test that compiles the actual SDL conversion/rest/output block with production axis conversion helpers and authored samples. No binding, camera-speed, XInput or renderer change.

Validation

Tested 8f572915 on main 49f75dc8:

  • 108 scenarios pass on Windows and Linux: repeated inversion toggles, both right axes/signs, endpoints and just-outside-dead-zone input, all six dead zones, two independent devices, genuine crossings, neutral recovery and unchanged left-axis mapping.
  • The original source fails. Five mutations are rejected on both OSes: inversion before filtering, missing gating, missing output inversion, missing physical-crossing recovery and shared device state.
  • Existing stick-rest and exhaustive dead-zone checks pass on both OSes. The entire changed pad_sdl.c translation unit also compiles on both, using runtime model headers generated solely from tracked recompiler source, without game instructions.
  • Full Linux: 352 passed / 0 failed / 23 skipped, versus exact base 351/0/23; jq supplied for the release-script checks.
  • Full Windows: 342 passed / 3 failed / 30 skipped, versus exact base 341/3/30. The same three checks fail: NUP file-link privilege fixtures, missing windows.h for the compute-guard fixture, and the recorder destructor negative control. One new source check passes; no new failures.

These are authored device samples and real compiled host code. A physical controller and game runtime have not been tested; no gameplay or performance result is claimed. Hosted exact-head checks are pending.

Claimed before implementation in dev-work and on the issue. Severity S2; reach1.0 is an upper bound, since the affected SDL/phantom-device subset is unknown; confidence1.0 refers to the authored source regression.

Current-head workflow Result
Linux AppImage Needs attention
Game-free source regressions Passed
PR title Passed
Windows builder Passed
## Summary Changing camera X/Y inversion can make the camera spin again after the held-stick protection blocked a phantom axis. The setting flips the sign used to recognize a physical crossing, permanently admitting the unchanged held input; reversing the setting then reverses the spin instead of stopping it. Fixes #2273 by keeping rest detection independent of configurable output inversion. ## Technical changes - Admit each SDL axis in a stable orientation, then apply camera X/Y inversion to its output. - Preserve genuine neutral/crossing recovery, the selected dead zone, independently tracked devices/axes and immediate inversion of already-admitted input. - Add a game-free test that compiles the actual SDL conversion/rest/output block with production axis conversion helpers and authored samples. No binding, camera-speed, XInput or renderer change. ## Validation Tested `8f572915` on main `49f75dc8`: - **108 scenarios pass on Windows and Linux**: repeated inversion toggles, both right axes/signs, endpoints and just-outside-dead-zone input, all six dead zones, two independent devices, genuine crossings, neutral recovery and unchanged left-axis mapping. - The original source fails. Five mutations are rejected on both OSes: inversion before filtering, missing gating, missing output inversion, missing physical-crossing recovery and shared device state. - Existing stick-rest and exhaustive dead-zone checks pass on both OSes. The entire changed `pad_sdl.c` translation unit also compiles on both, using runtime model headers generated solely from tracked recompiler source, without game instructions. - Full Linux: **352 passed / 0 failed / 23 skipped**, versus exact base **351/0/23**; `jq` supplied for the release-script checks. - Full Windows: **342 passed / 3 failed / 30 skipped**, versus exact base **341/3/30**. The same three checks fail: NUP file-link privilege fixtures, missing `windows.h` for the compute-guard fixture, and the recorder destructor negative control. One new source check passes; no new failures. These are authored device samples and real compiled host code. A physical controller and game runtime have not been tested; no gameplay or performance result is claimed. Hosted exact-head checks are pending. Claimed before implementation in [dev-work](https://discord.com/channels/1568810484818378804/1569019786766188665/1570065599344943105) and on the issue. Severity S2; reach1.0 is an upper bound, since the affected SDL/phantom-device subset is unknown; confidence1.0 refers to the authored source regression. <!-- pr-ci-status --> | Current-head workflow | Result | |---|---| | [Linux AppImage](https://github.com/NotRexed/Hookshot/actions/runs/43029999591) | Needs attention | | [Game-free source regressions](https://github.com/NotRexed/Hookshot/actions/runs/43029999010) | Passed | | [PR title](https://github.com/NotRexed/Hookshot/actions/runs/43029998508) | Passed | | [Windows builder](https://github.com/NotRexed/Hookshot/actions/runs/43029999201) | Passed | <!-- /pr-ci-status -->
hckrman101 (Migrated from github.com) approved these changes 2026-11-11 16:49:31 +00:00
hckrman101 (Migrated from github.com) left a comment

Looks good. The fix is small and right: the rest detector now always sees the stick in one fixed orientation (right X as 0, right Y as 1), and camera inversion is applied to the admitted output afterwards. That is the same output as before for every admitted input (the old inv_y ? 0 : 1 and the new -v on top of the fixed flip cancel exactly), and it stops the setting from flipping the sign the rest/crossing check sees. Neutral and worn-stick crossing recovery are untouched.

CI: the source regressions, Windows builder and Vulkan jobs pass. The AppImage job timed out at 1h, which is unrelated to a 6-line change in pad_sdl.c.

Overlap: #1600 (stick direction outside the dead zone) edits the same block in pads_poll and drops stick_value, so whichever lands second needs a rebase. They don't disagree, though: #1600 also does the inversion after filtering (if (inv_x) v[2] = -v[2]), just with the Y flip folded in differently, and it would need to keep this PR's placement after the rest gating. #1945 (camera speed) doesn't touch pad_sdl.c, so no conflict.

Nit: a physical controller has not been tried (the PR says so), so a quick in-game check of a toggle with a real pad would be nice, but not blocking.

Looks good. The fix is small and right: the rest detector now always sees the stick in one fixed orientation (right X as 0, right Y as 1), and camera inversion is applied to the admitted output afterwards. That is the same output as before for every admitted input (the old `inv_y ? 0 : 1` and the new `-v` on top of the fixed flip cancel exactly), and it stops the setting from flipping the sign the rest/crossing check sees. Neutral and worn-stick crossing recovery are untouched. CI: the source regressions, Windows builder and Vulkan jobs pass. The AppImage job timed out at 1h, which is unrelated to a 6-line change in `pad_sdl.c`. Overlap: #1600 (stick direction outside the dead zone) edits the same block in `pads_poll` and drops `stick_value`, so whichever lands second needs a rebase. They don't disagree, though: #1600 also does the inversion after filtering (`if (inv_x) v[2] = -v[2]`), just with the Y flip folded in differently, and it would need to keep this PR's placement after the rest gating. #1945 (camera speed) doesn't touch `pad_sdl.c`, so no conflict. Nit: a physical controller has not been tried (the PR says so), so a quick in-game check of a toggle with a real pad would be nice, but not blocking.
Commenting is not possible because the repository is archived.
No description provided.