fix(input): preserve stick direction outside the dead zone #1600
Closed
bronze-ff wants to merge 2 commits from
bronze-ff/fix/radial-stick-dead-zone into main
pull from: bronze-ff/fix/radial-stick-dead-zone
merge into: MagicBeanDev:main
MagicBeanDev:main
MagicBeanDev:github/pr/2893
MagicBeanDev:github/pr/2890
MagicBeanDev:github/pr/2887
MagicBeanDev:github/pr/2878
MagicBeanDev:github/pr/2875
MagicBeanDev:github/pr/2874
MagicBeanDev:github/pr/2873
MagicBeanDev:github/pr/2871
MagicBeanDev:github/pr/2756
MagicBeanDev:github/pr/2897
MagicBeanDev:github/pr/2896
MagicBeanDev:github/pr/2718
MagicBeanDev:github/pr/2842
MagicBeanDev:github/pr/2691
MagicBeanDev:github/pr/2895
MagicBeanDev:github/pr/2743
MagicBeanDev:github/pr/2765
MagicBeanDev:github/pr/2708
MagicBeanDev:github/pr/2732
MagicBeanDev:github/pr/2755
MagicBeanDev:github/pr/2817
MagicBeanDev:github/pr/2876
MagicBeanDev:release-please--branches--main
MagicBeanDev:release-please--branches--main--release-notes
MagicBeanDev:github/pr/2778
MagicBeanDev:github/pr/2888
MagicBeanDev:github/pr/2866
MagicBeanDev:github/pr/2877
MagicBeanDev:github/pr/2800
MagicBeanDev:github/pr/2829
MagicBeanDev:github/pr/2850
MagicBeanDev:github/pr/2889
MagicBeanDev:github/pr/2882
MagicBeanDev:github/pr/2860
MagicBeanDev:github/pr/2849
MagicBeanDev:github/pr/2886
MagicBeanDev:github/pr/2794
MagicBeanDev:github/pr/2840
MagicBeanDev:github/pr/2830
MagicBeanDev:github/pr/2834
MagicBeanDev:github/pr/2719
MagicBeanDev:github/pr/2742
MagicBeanDev:github/pr/2741
MagicBeanDev:github/pr/2820
MagicBeanDev:github/pr/2771
MagicBeanDev:github/pr/2859
MagicBeanDev:github/pr/2858
MagicBeanDev:github/pr/2823
MagicBeanDev:github/pr/2757
MagicBeanDev:github/pr/2769
MagicBeanDev:github/pr/2844
MagicBeanDev:github/pr/2843
MagicBeanDev:github/pr/2673
MagicBeanDev:github/pr/2855
MagicBeanDev:github/pr/2739
MagicBeanDev:github/pr/2807
MagicBeanDev:github/pr/2851
MagicBeanDev:github/pr/2789
MagicBeanDev:github/pr/2814
MagicBeanDev:github/pr/2818
MagicBeanDev:github/pr/2774
MagicBeanDev:github/pr/2772
MagicBeanDev:github/pr/2750
MagicBeanDev:github/pr/2786
MagicBeanDev:github/pr/2785
MagicBeanDev:github/pr/2782
MagicBeanDev:github/pr/2779
MagicBeanDev:github/pr/2775
MagicBeanDev:github/pr/2706
MagicBeanDev:github/pr/2766
MagicBeanDev:github/pr/2747
MagicBeanDev:github/pr/2759
MagicBeanDev:github/pr/2745
MagicBeanDev:github/pr/2740
MagicBeanDev:github/pr/2733
MagicBeanDev:github/pr/2725
MagicBeanDev:github/pr/2727
MagicBeanDev:github/pr/2728
MagicBeanDev:github/pr/2729
MagicBeanDev:github/pr/2721
MagicBeanDev:github/pr/2722
MagicBeanDev:github/pr/2715
MagicBeanDev:github/pr/2714
MagicBeanDev:github/pr/2707
MagicBeanDev:github/pr/2703
MagicBeanDev:github/pr/2705
MagicBeanDev:github/pr/2697
MagicBeanDev:github/pr/2679
MagicBeanDev:github/pr/2676
MagicBeanDev:github/pr/2669
MagicBeanDev:docs/what-hookshot-connects-to
MagicBeanDev:fix/hdr-encode-test
MagicBeanDev:fix/main-tests
MagicBeanDev:fix/play-cache-files
MagicBeanDev:perf/game-barrier-order
MagicBeanDev:feat/heap-lazy
MagicBeanDev:feat/texture-detail
MagicBeanDev:perf/astc-opaque-bc1
MagicBeanDev:fix/fs-dir-lock
MagicBeanDev:fix/framegen-native-kept
MagicBeanDev:fix/update-no-downgrade
MagicBeanDev:port/k10-gcn-f16-mirrors
MagicBeanDev:fix/framegen-mult-cap
MagicBeanDev:chore/release-as-2.1.1
MagicBeanDev:perf/pcache-load-switch
MagicBeanDev:fix/prepare-progress-log
MagicBeanDev:fix/update-closes-stale-game
MagicBeanDev:fix/hdr-scaled-sampled
MagicBeanDev:perf/first-start-preset
MagicBeanDev:perf/zero-upload
MagicBeanDev:fix/vcore-escape-2s
MagicBeanDev:test/fake-gh-atomic
MagicBeanDev:perf/batch-idle-flush
MagicBeanDev:test/finish-stub-a
MagicBeanDev:chore/record-2.1.0
MagicBeanDev:fix/main-red-tests
MagicBeanDev:fix/test-display-startup-driver-cache
MagicBeanDev:feat/mesh-shader-mode
MagicBeanDev:fix/menu-probes-at-startup
MagicBeanDev:fix/fps-counter-classic
MagicBeanDev:fix/test-log-batch-flake
MagicBeanDev:fix/narrow-screen-hud
MagicBeanDev:ci/prereleases
MagicBeanDev:release/v2.0.15
MagicBeanDev:hotfix/v2.0.15
MagicBeanDev:fix/present-ahead-replay
MagicBeanDev:release/v2.0.13
MagicBeanDev:fix/bracket-paths-2013
MagicBeanDev:fix/hdr-overlays-2013
MagicBeanDev:fix/fps-counter-scale-2013
MagicBeanDev:fix/stick-seen-at-rest
MagicBeanDev:hotfix/menu-fits-screen
MagicBeanDev:fix/f1-always-opens-menu
MagicBeanDev:fix/ini-keep-lines
MagicBeanDev:claude/cranky-pike-7cf247
MagicBeanDev:perf/texture-answer-flat
MagicBeanDev:fix/stamp-test-modules
MagicBeanDev:fix/appimage-fonts
MagicBeanDev:fix/display-awake
MagicBeanDev:release/v2.0.12
MagicBeanDev:fix/discord-windows
MagicBeanDev:rc/v2.0.12-tests
MagicBeanDev:fix/dlss-keep-feature
MagicBeanDev:rc/v2.0.12
MagicBeanDev:button-icons
No reviewers
Labels
Clear labels
accessibility
Barrier affecting people with disabilities
autorelease: pending
autorelease: tagged
bug
Something isn't working
dependencies
Pull requests that update a dependency file
documentation
Improvements or additions to documentation
duplicate
This issue or pull request already exists
enhancement
New feature or request
github_actions
Pull requests that update GitHub Actions code
good first issue
Good for newcomers
help wanted
Extra attention is needed
invalid
This doesn't seem right
question
Further information is requested
testing wanted
wontfix
This will not be worked on
No labels
accessibility
autorelease: pending
autorelease: tagged
bug
dependencies
documentation
duplicate
enhancement
github_actions
good first issue
help wanted
invalid
question
testing wanted
wontfix
Milestone
Clear milestone
No items
No milestone
Assignees
Clear assignees
No assignees
2 participants
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/Hookshot!1600
Loading…
Reference in a new issue
No description provided.
Delete branch "bronze-ff/fix/radial-stick-dead-zone"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Summary
Addresses #1480. The host currently applies the stick dead zone to X and Y independently. For example, with the default cutoff SDL input
(12000, -4000)reaches the game as(12000, 0), snapping a diagonal toward horizontal.Use the same circular cutoff for both sticks in SDL and XInput. Inside the circle both components are zero. Outside it both components pass through with their original magnitude and the existing axis conventions. The six thresholds and the saved option stay the same.
Validation
mainwith synthetic input. The regression now exercises all 65,536 values on each axis at each threshold, diagonals in every quadrant, the exact cutoff, and extreme inputs.Looks good. The circular cutoff is applied per stick in both SDL and XInput, and outside the circle both components pass through unchanged, so diagonals keep their direction. I checked the details: the squared sum can't overflow int32 once both components are clamped to -32767 (2*32767^2 is about 2.147e9, just under INT32_MAX), the SDL Y flip and the invert X/Y handling match the old per-axis behaviour, and on-axis input gives the same result as before. hos_stick_axis has no other callers. The test, menu text and readme are updated to match, and CI is green.
Nit: the Default zone (0) is now a circle of radius 7849 rather than a per-axis cut, so a stick that drifts just past 24% on one axis only will behave slightly differently. That's the intended fix and the readme says it.
Heads-up: this conflicts with main now (a lot landed today). Could you merge main into the branch (or rebase) and resolve the conflicts? Thanks!
Re-checked at the new head. The push is only a merge of main: the circular filter is unchanged, and the SDL path keeps main's rest-before-steer check (#1673/#1661) right after the filter, as the merge message says. The resolved hunks, the test, menu text and readme still read right, and CI is green (the two skipped jobs are the release ones). It merges cleanly now.
For the related PRs: #2286 (camera inversion out of rest detection) edits the same SDL block, where the invert flips now sit just above the rest loop, so whichever lands second needs a small rebase. #1945 (camera speed row) should not overlap beyond the right-stick values in pads_poll.
The circular cutoff in
hos_filter_stick(nn_hid.c) still isn't on main. Main still applieshos_stick_axisper axis, so the fix is still wanted. The conflict is the one predicted in the last review: #2286 moved the invert-X/Y flips inpads_poll(host/pad_sdl.c) below the rest-detection loop, so a flipped axis doesn't count as rested. When you rebase, keep main's order: run the filter and the SDL Y flip, then therestedloop, and only thenif (inv_x) v[2] = -v[2]; if (inv_y) v[3] = -v[3];. Don't apply the PR'sif (!inv_y)before the loop. The readme conflict is the dead-zone line.One thing to think about, not blocking. With the circle, a phantom axis that reads as held (the #1661/#1673 devices) keeps its partner axis "active", so the partner's drift now passes without its own dead zone once the rest loop admits it. The game's own dead zone still follows, so it's probably fine. Please re-run
tests/test_stick_dead_zone.pyafter the rebase.I merged main into the branch (047c5f9) and added a fix on top (cbf59bb).
Merge: the SDL block uses main's #2286 order, as @Joey-Magnus asked: the circular filter and both Y flips first, then the
restedloop, and the invert X/Y flips only after it. I dropped theif (!inv_y)from before the loop. The readme keeps main's new Camera speed entry next to your dead zone text. Main had also added a new caller ofstick_value(the motion-only pad's activity check) andtest_stick_inversion_rest.py, which readshos_stick_axis/stick_value, so both now usehos_filter_stick.Joey's phantom-axis point: I fixed it rather than leave it. With the circle, an axis held from connection (the #1661/#1673 devices) kept its stick outside the circle. That did two things: the partner axis never read as rested at its own center, and once it was admitted its drift got through without a dead zone. Now the rest loop judges each axis on its own against the dead zone, as before the circle, and each stick goes through the circle again after the loop, so an axis that's held back doesn't count toward it. For a normal stick nothing changes, because the second pass keeps values that are already outside the circle.
test_stick_inversion_rest.pynow covers that case (it fails without the fix) and checks that diagonals keep their full magnitude in every quadrant and dead zone.test_stick_rest.pyis updated for the reshaped loop.Also fixed: the menu translations were still keyed to the old per-axis help text, so
test_menu_i18n.pyfailed after the merge. All 13 languages are retranslated for the new wording.The stick, menu, input, rumble and option-id tests and the full suite pass locally (the only failures were two checks that need Python modules this PC doesn't have), and the renderer builds. Thanks for the fix, @bronze-ff, and for the careful tests!