Skip to content

Sdl3 - #949

Open
qdot wants to merge 11 commits into
devfrom
sdl3
Open

Sdl3#949
qdot wants to merge 11 commits into
devfrom
sdl3

Conversation

@qdot

@qdot qdot commented Sep 6, 2026

Copy link
Copy Markdown
Member

Implement support for SDL3, removing the xinput and hid managers at the same time (as SDL3 will handle those)

qdot added 11 commits September 5, 2026 18:25
…fier

Adds the SdlGamepadSpecifier enum variant, schema component, protocol YAML
(two 0-65535 vibrate features, low/high motors), and the regenerated device
config (v5.43). Structural inspiration: chiefautism's abandoned PR #860.
Stateful two-motor handler mirroring the XInput protocol: every vibrate
command carries the full motor state as two u16 LE values on Tx, so each
write is a complete command with no batching step.
Cross-platform (Windows/macOS/Linux) gamepad rumble via SDL3, statically
built from source. One process-lifetime thread owns the SDL context and
multiplexes all gamepads addressed by SDL3 instance ID; discovery is
on-demand enumeration and removal is connected-state polling - the thread
never pumps SDL events. Rumble is armed finitely (60s) and refreshed before
expiry so one-shot commands hold indefinitely. Includes the Phase 0
threading spike example (headless init + no-pump enumeration on a spawned
thread). Joins the workspace as both a member and default member so plain
cargo build compiles it on all three OSes. Structural inspiration:
chiefautism's abandoned PR #860.
Registered in the v1-v4 embedded/json lists. v0 is excluded with a comment:
SingleMotorVibrateCmd broadcasts one speed to all motors and cannot express
the per-motor addressing this case verifies. Test harness gains an
sdl-gamepad specifier branch (Tx-only endpoints) alongside the BLE default.
Registers the SDL gamepad communication manager on all platforms (no OS
gate, unlike XInput) and logs a warning on Windows when both gamepad
managers are enabled, since the same physical controller may then appear as
two devices. Flag plumbing, default-false options, and the registration
decision are unit-tested.
Non-default feature (SDL3 source build is too heavy for default library
consumers): enabling it registers the SDL gamepad manager, cross-platform
with no OS gate. Manager registration is refactored into a named-returns
helper asserted by a feature-gated test.
On all three OS matrix entries: run the in-process client tests with
--features sdl-gamepad-manager, assert via cargo tree that sdl3 is absent
from default features and present with the feature, and run the SDL3
headless threading spike example (automated half of the Phase 0 gate; CI
runners have no controllers, so an empty gamepad set passes).
…devices

SDL3 on macOS routes wired gamepads to GCController (MFI) by default: the
hidapi device drivers decline wired pads (DevSrvsID paths) while MFI is
enabled, and GCController discovery only fires via Cocoa runloop
notifications - which a headless, no-video process never spins. Result: zero
gamepads ever discovered, even on the main thread with event pumping.

Verified against SDL 3.4.14 source (SDL_PLATFORM_MACOS && SDL_JOYSTICK_MFI
guard in SDL_hidapi_xboxone.c) and on hardware: with SDL_JOYSTICK_MFI=0 the
wired Xbox One S enumerates via hidapi and set_rumble succeeds. iOS keeps
the MFI default (GCController is the only backend there).

The spike example mirrors the factory's platform policy. Windows discovery
failure remains under investigation (likely analogous WGI behavior; needs an
on-hardware SDL_LOGGING=3 run).
On-hardware diagnosis (wired Xbox One S, USB): with the MFI=0 hidapi
policy, wired pads enumerate but rumble silently fails - Apple exposes
only read-only shortened HID reports for them, and the working path
(GCController/CoreHaptics) requires a main-thread runloop this
architecture deliberately does not host. SDL2 has the same Apple-side
guard, so a version switch would not help.

Per operator decision, v1 is Bluetooth-only on macOS: scan probes each
candidate's SDL connection state and skips Wired pads with a logged
explanation pointing users at Bluetooth pairing. Verified on hardware:
SDL reports the wired pad as connection Wired. Bluetooth pads and all of
Windows/Linux are unaffected.

The spike example now enables verbose SDL logging and prints each pad's
connection state, so a single run is a complete diagnostic - this is the
tool for the still-open Windows zero-device diagnosis (likely the
analogous XGIP/WGI claiming dance).
On-hardware validation (Bluetooth DualSense, macOS) showed rumble stops
after a few seconds despite the 60s arm - some controllers do not honor
long durations. Re-sending the current non-zero command every second
keeps the effect alive; the long finite arm stays as a safety net if a
keepalive is missed. Same refresh_decision machinery, tighter interval.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant