WIP: feat(input): support the Switch 2 Pro Controller over USB, with HD rumble #2252
Draft
hckrman101 wants to merge 2 commits from
hckrman101/feat/switch2-pro-controller into main
pull from: hckrman101/feat/switch2-pro-controller
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
1 participant
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!2252
Loading…
Reference in a new issue
No description provided.
Delete branch "hckrman101/feat/switch2-pro-controller"
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?
The Switch 2 Pro Controller doesn't work in Hookshot today: the SDL3 the Windows game ships has no driver for it (SDL's Switch 2 driver needs libusb), so the controller sends nothing. This PR adds a small driver of Hookshot's own for it over USB, with buttons, sticks, motion controls and the game's HD rumble, and nothing for players to install.
I don't have a Switch 2 Pro Controller, so none of this has run on the controller itself. Everything below is checked against the code it comes from, without hardware. If you have one, please try it (details at the end).
What it supports
057E:2069), on Windows and Linux. Bluetooth isn't supported (the controller uses a different protocol over Bluetooth LE, and SDL doesn't support it either).HOS_SW2_GL/HOS_SW2_GRcan assign them: set one to an input name (south east west north back start lstick rstick lshoulder rshoulder up down left right zl zr) and the button presses that input, so the Controls page can bind it like any other.HOS_HD_RUMBLE=0plays the plain two-motor rumble at SDL's fixed frequencies instead.HOS_NO_SW2=1turns all of this off and leaves the controller to SDL, as before.How it works
host/sw2_pro.his the protocol, with no I/O:host/pad_sw2.cruns one thread per controller. It sets the controller up, reads it and sends its rumble:winusb.dll; input and rumble go through the normal HID interface (hid.dll,setupapi.dll). All three are system DLLs, loaded at run time, so nothing changes in the build or link. If the setup interface has no WinUSB driver, a controller another program already started is still read./dev/bus/usb), as SDL's libusb path does. Setup goes over the vendor interface, then the HID interface is taken fromhid-generic(and given back when the controller goes away) and read directly. The device node has to belong to the logged-in user. Steam's udev rules (steam-devices) already do that for this controller, and SDL's own driver needs the same access. Without it, the log gives the one-line rule to add. An interface claimed by another program (Steam Input) is never taken.host/hos_sdl.chands these pads tohost/pad_sdl.cas one more pad, so the dead zone, stick-rest guard, motion filter, rumble strength and bindings all apply unchanged. SDL's own view of the controller is left out of the list (by057E:2069only), and SDL's HIDAPI is told to ignore that one id (SDL_HIDAPI_IGNORE_DEVICES, added to anything the player set), so the controller never shows up twice. The Joy-Con 2 and the GameCube controller are untouched and stay SDL's.057E:2069is ever opened. On Linux the device is picked from sysfs ids before anything is opened; on Windows, by its interface path. Every other controller, the Switch Pro Controller (057E:2009) included, goes through SDL exactly as before.Why not a patched SDL with libusb, like Fritchii? We'd have to build and ship our own SDL3 with libusb for Windows, bundle SDL3 in the AppImage instead of using the system's, and re-apply an SDL patch every release, because SDL's Switch 2 driver can only play its two fixed rumble frequencies (a raw HD rumble effect was proposed to SDL and not merged). Its Linux path also needs the same usbfs access this does. The protocol is small enough to carry here instead, and players don't have to install anything.
Where it comes from (credit)
src/joystick/hidapi/SDL_hidapi_switch2.c, by Vicki Pfau and Sam Lantinga for Valve (zlib license). That covers the init sequence, the flash addresses and calibration layout, the input report layout, the sensor scales and clock check, and the rumble sample encoding. The commits carryCo-authored-bylines for them.Co-authored-byline for them.hid-generic, read the interrupt endpoint.Tests (no controller needed)
tests/test_switch2_pro.pycompiles the protocol and runs it next to copies of the functions it was ported from, on the same bytes:057E:2069matches. The Switch Pro Controller, Joy-Con, Joy-Con 2, GameCube controller and other vendors don't.HandleSwitchProState: 20,000 random reports, with and without stick calibration. Every button, the d-pad, ZL/ZR and all four stick axes match exactly. The sensor values and SDL's sensor-clock check (34.8 vs 40 rad/s full scale) also match, and a flat controller reads 1 G.EncodeHDRumble, checked on 20,000 random samples and on every bit.Switch2FrequencyCode.HOS_HD_RUMBLE=0motor rumble matches SDL's own.linux/usbdevice_fs.h.hos_sdl_pad_*call hands Switch 2 pads to the new driver, SDL's own copy of the controller is skipped by its IDs alone, andpad_sdl.csends it the game's vibration values.tests/run_all.pyon Linux: 357 passed, 4 failed, 13 skipped. The 4 failures are local environment problems in files this PR doesn't touch: two needzstandard, which the system Python here doesn't have (both pass with the bundled Python), one needs Tk, and one is a clang 23 warning in an existing test harness.The Linux build is clean. The Windows files were compiled and linked with llvm-mingw: they import only kernel32, advapi32 and ole32 at link time;
setupapi,winusbandhidare loaded at run time.On Linux I also ran the device scan alone, with a Switch Pro Controller (
057E:2009) plugged in over USB. It found no Switch 2 controller, opened nothing and started no thread.Not tested
If you have a Switch 2 Pro Controller
Please plug it in with a USB cable and run the game. Then send
hookshot_log.txt(thehid:lines) and tell us:HOS_MOTION_DEBUG=1logs the sensor readings twice a second.On Linux, if the log says there is no access to the USB device, install Steam's
steam-devicesudev rules or add the rule the log gives.Switch2Connect already allows wireless Switch 2 controllers to be used on Windows through USB/IP, emulating a genuine USB-connected Switch 2 Pro Controller.
Is it possible that the game recognizes it?
Are you able to send me a testbuild zip for Windows?
I can test it for you right now. :)