Holding all four buttons simultaneously toggles the ABXY layout for that controller with a 200ms rumble confirmation pulse.
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-opencode)
Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
- Migrate all SDL2 GameController API calls to SDL3 Gamepad equivalents
- Fix PermissionError on controller reconnect by closing UART handle before
releasing port back to pool (Windows holds COM ports exclusively)
- Add DisplayIndexAllocator so controllers get stable display indices (0, 0, 0)
instead of incrementing SDL instance IDs (1, 2, 3) on each reconnect
- Button mapping now uses SDL3 positional constants (SOUTH/EAST/WEST/NORTH)
which works correctly for all controller types without USE_BUTTON_LABELS hint
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-opencode)
Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
SDL3 includes a dedicated 8BitDo HIDAPI driver with native gyro/accel sensor support, which SDL2 lacked.
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-opencode)
Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
SDL's hidapi_switch.c SendSensorUpdate() remaps Nintendo's raw sensor axes
to match PlayStation convention before emitting SDL sensor events:
SDL_X = -Nintendo_Y, SDL_Y = +Nintendo_Z, SDL_Z = -Nintendo_X
This commit reverses that transform so the values sent to the Pico are in
Nintendo's native coordinate frame:
Nintendo_X_raw = -SDL_Z
Nintendo_Y_raw = -SDL_X
Nintendo_Z_raw = +SDL_Y
Same mapping for gyro (bias subtraction happens before axis reversal).
Confirmed from SDL3 hidapi_switch.c source (SDL2 uses same SDL3 backend).
- Add SwitchImuSample struct and IMU fields to SwitchInputState
- Add fill_imu_report_data() to pack samples into imuData[36]
- Rewrite switch_pro_apply_uart_packet() for v2 with checksum validation
- Enlarge poll_uart_frames() buffer to 64 bytes for variable-length frames
- Clear imu_sample_count after each USB report send (prevent stale IMU)