feat(input): the Nintendo Switch 2 Pro Controller over USB, read by the game itself #279
No reviewers
Labels
No labels
area/accessibility
area/audio
area/input
area/install
area/menu
area/renderer
area/saves
area/upscaling
good first issue
gpu/amd
gpu/igpu
gpu/intel
gpu/nvidia
help wanted
kind
bug
kind
chore
kind
docs
kind
feature
kind
performance
kind
question
needs measurements
platform/linux
platform/steam-deck
platform/windows
preview
priority
critical
priority
high
priority
low
status
blocked
status
confirmed
status
duplicate
status
invalid
status
needs-info
status
wontfix
testing wanted
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set
Reference
MagicBeanDev/Longshot!279
Loading…
Reference in a new issue
No description provided.
Delete branch "airdamien/Longshot:feat/switch2-pro-usb"
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?
Why
A Nintendo Switch 2 Pro Controller plugged into a Windows PC does nothing in Longshot: Windows has no driver a game can use for it, and the SDL the game ships has none either. Players run a bridge program that shows it as an Xbox pad, without its motion sensor. This lets the game read the controller itself over USB, with its gyroscope and its rumble.
What changed
host/pad_switch2.c(new, Windows; a stub elsewhere).pad_openfinds the controller's two interfaces (SetupAPI), opens the vendor one with WinUSB, reads its factory and user stick calibration and its gyroscope's rest from flash, and sends the start-up commands;switch2_mainreads the HID reports (overlapped, so a silent controller is noticed and started again) andreportgives them to an SDL virtual gamepad (sdl_attach) with an accelerometer and a gyroscope, sohost/pad_sdl.cplays it as any pad: the Controls page, the prompts, the Motion controls.rumble_mainsends the rumble on a thread of its own: output report 0x02, three frames a motor in the layout the controller takes (not the one SDL's driver packs), limited as for a USB port; when the controller stops taking those reports it plays the controller's own buzz and click samples instead and says so in the log.host/pad_sdl.cstarts it with the pads;nx2/build.pylinkssetupapi,winusbandhidinto the Windows exe.gpu/vk/vk_menu.cpp: a Switch 2 Pro Layout preset (apply_pad_layout,detect_pad_layout: every button the pad's own, and the Switch's icons), and a Controller menu button row (opts.pad_menu: Home as before, or GL, GR, or none) for the button that opens and closes the Insert menu.docs/development.md(the two switches).tests/test_pad_layout.py(main's, from #266) also checks the Switch 2 Pro preset: from each preset and with each button cleared, every button gets the pad's own input and the row says Switch 2 Pro; Nintendo chosen after it says Nintendo.Flags and settings
HOS_SWITCH2=0HOS_SWITCH2_RUMBLE_TEST=1controller_menu_button)Measurements
main:
fbd42024(2026-11-13)this PR: feat/switch2-pro-usb @
120b08ce(the measured commit;00d333ebafter it added only the row in docs/tested-configurations.md)Hardware: NVIDIA GeForce RTX 5080 (Vulkan driver 0x93cec000), Windows 11; a Switch 2 Pro Controller (firmware 1.1.5) plugged in over USB
Scene: the save's load point, Link standing still; 3840x2160 output, DLSS, frame generation 3x, the game's frame rate cap 60
Method: 150 s runs, the first 60 s left out, two interleaved runs each (main, this PR, ...). The numbers are
RX_VK_FRAME_LOG's frame times, a line per present (made frames included); the game's own rate islongshot_log.txt'svi:line.How to reproduce: the builds from the same dump with
nx2/make_game.py, the samelongshot_options.iniand save,HOS_INPUT_SCRIPT=12:A,15:A,...,42:A HOS_INPUT_SCRIPT_BACKGROUND=1 RX_VK_FRAME_LOG=<csv>.fbd42024120b08ceRebased since onto main @
514591a3(the branch's head is nowd530cb97) and not measured again: main's ten commits sincefbd42024are the website, docs, the forge script, a Damage taken row, an HDR route for FSR 4 that is opt-in, occlusion query numbering, quick pipelines and two input fixes; none is in this change's path, and this change's code is as measured but forapply_pad_layout, where main's fix (#266: a preset is asked again until it is had whole) now also covers the Switch 2 Pro preset. The rebased branch was built whole (nx2/make_game.py) and its checks run.Rumble, with the controller lying still (
HOS_SWITCH2_RUMBLE_TEST=1):switch2: rumble test: the accelerometer moved 288 in a second at rest, 6022 in 0.8 s of rumble.mainhas no rumble for this controller to compare with.Testing
Tested:
python -I -B tests/run_all.pyon Windows 11 (llvm-mingw): 454 passed, 7 failed, at the rebased head. The same 7 fail onmain@514591a3on this PC (test_bake_portability,test_fpx_sticky,test_spawn_emit,test_nup_paths,test_check_third_party,test_release,test_menu_i18n): local tools and packages, and one text of main's without its translations, not this change.host/pad_switch2.c,host/pad_sdl.ccompile there (-fsyntax-onlywith the build's includes); the driver is a stub on Linux.switch2: ... started (sticks calibrated at 2021 2173 / 2164 2119),hid: controller "Nintendo Switch 2 Pro Controller" connected (SDL), with a gyroscope. Rumble, with the controller lying still (HOS_SWITCH2_RUMBLE_TEST=1):the accelerometer moved 288 in a second at rest, 6022 in 0.8 s of rumble. Played by the author in a build that also has the VR pull request's changes: sticks, buttons, the Home button opening the menu.Not tested:
Needs other testers:
Screenshots
The Controls tab's new row, this PR @
120b08ce(mainhas no such row):Risk and rollback
HOS_SWITCH2=0gives it back).setupapi,winusbandhid(all part of Windows).Reviewer notes
hos_switch2_command.#dev-workbeforehand.Issues
None.
Checklist
For everyone
type(scope): description).main.python -I -B tests/run_all.pyrun and passing (any failure that also fails onmainis named above).third_party/untouched (a new folder with aPROVENANCE.mdinstead).If applicable
Assisted-by:/Co-Authored-By:trailers.docs/tested-configurations.mdfor each measured run.🤖 Generated with Claude Code
WIP: feat(input): the Nintendo Switch 2 Pro Controller over USB, read by the game itselfto feat(input): the Nintendo Switch 2 Pro Controller over USB, read by the game itself00d333eb4dd530cb977bRebased onto main @
514591a3(headd530cb97). The conflict wasapply_pad_layout: main's fix (#266) asks a preset again until it is had whole, and the Switch 2 Pro preset now does the same for all the pad's buttons.tests/test_pad_layout.pychecks that preset too. With #266's row taking its value fromdetect_pad_layout, Nintendo chosen right after Switch 2 Pro would have read Switch 2 Pro again (the inputs are the same): the row now keeps Nintendo. Built whole and the checks run (454 passed; the 7 that fail also fail on main here). Not measured again: the description says why.View command line instructions
Checkout
From your project repository, check out a new branch and test the changes.