feat(clients/steam): M4 — desktop SDL clients capture the rich Steam inputs

The Linux + Windows native clients (clients/{linux,windows}/src/gamepad.rs) now
capture and send the Steam Controller / Steam Deck rich inputs, so a real Deck
(off Steam Input) or a Steam Controller on a desktop client drives the host's
virtual hid-steam pad end-to-end:

- Set SDL's HIDAPI Steam hints (SDL_JOYSTICK_HIDAPI_STEAMDECK / _STEAM) before
  init so SDL opens Valve devices directly (paddles + both trackpads + gyro as
  first-class SDL gamepad inputs).
- Detect the Deck/SC by VID/PID (0x28DE + 0x1205 / 0x1102 / 0x1142) ->
  GamepadPref::SteamDeck (there is no SDL gamepad type for it), so the host
  builds the virtual Deck with the right identity.
- Map the SDL paddle + Misc1 buttons -> BTN_PADDLE1..4 / BTN_MISC1 (a free win
  for Xbox Elite paddles too).
- Route a SECOND touchpad -> RichInput::TouchpadEx (SDL touchpad 0 = left ->
  surface 1, 1 = right -> surface 2, signed coords); a single touchpad keeps the
  legacy Touchpad. New forward_touch() helper centralizes the choice.
- Track held touchpad contacts per (surface, finger) and lift them on pad
  switch/detach so a contact held at that moment can't stick.
- Sensor (gyro/accel) capture was already generic across pad types.

Linux client builds + clippy clean; the Windows client is a near-verbatim
mirror (windows CI compiles it). On a Deck in Game Mode, Steam Input still holds
the device — the user disables Steam Input for the client (the Decky UX, next);
on a desktop client (or a Deck with Steam Input off) the hints just work.

Remaining M4: Decky Disable-Steam-Input UX, Apple/Android parity, and the C-ABI
PunktfunkRichInputEx + send_rich_input2 (Apple/embedder send path). Not pushed.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-06-29 12:20:09 +00:00
parent 01c55aed38
commit ae71e4628d
3 changed files with 239 additions and 60 deletions
+17 -6
View File
@@ -1,11 +1,22 @@
# Rich Steam Controller & Steam Deck support
> **Status:** **M0M3 GREEN — virtual Deck binds + byte-exact + wired backend + the rich wire,
> on-box (2026-06-29).** The greenfield virtual `hid-steam` device works end-to-end as a selectable
> host gamepad backend (`PUNKTFUNK_GAMEPAD=steamdeck`), and the protocol now carries the rich Steam
> inputs (back buttons + second trackpad). Next: M4 (client capture — SDL Steam hints, paddles, 2nd
> touchpad, the Decky Disable-Steam-Input UX, + the C-ABI `PunktfunkRichInputEx`/`send_rich_input2`
> for the Apple/embedder send path). The Steam analogue of the shipped virtual DualSense.
> **Status:** **M0M3 GREEN + M4 desktop-capture done (2026-06-29).** The host side is complete:
> the virtual `hid-steam` Deck binds, is byte-exact, is a wired backend (`PUNKTFUNK_GAMEPAD=
> steamdeck`), and the protocol carries the rich Steam inputs. The **Linux + Windows SDL clients now
> capture + send** them. Remaining M4: the Decky Disable-Steam-Input UX, Apple/Android parity, and
> the C-ABI `PunktfunkRichInputEx`/`send_rich_input2` (Apple/embedder send path).
>
> **M4 (desktop client capture) result:** `clients/{linux,windows}/src/gamepad.rs` (the SDL services)
> now: set the SDL HIDAPI Steam hints (`SDL_JOYSTICK_HIDAPI_STEAMDECK`/`_STEAM`) so SDL opens Valve
> devices directly; detect the Deck/SC by VID/PID (`0x28DE` + `0x1205`/`0x1102`/`0x1142`) →
> `GamepadPref::SteamDeck`; map the SDL paddle + Misc1 buttons → the `BTN_PADDLE1..4`/`BTN_MISC1`
> wire bits; and route a **second** touchpad → `RichInput::TouchpadEx` (SDL touchpad 0 = left →
> surface 1, 1 = right → surface 2, signed coords) while a single touchpad keeps the legacy
> `Touchpad`. Held touchpad contacts are now tracked per `(surface,finger)` and lifted on pad
> switch/detach. Sensor (gyro/accel) capture was already generic. Linux client builds + clippy clean;
> Windows is a near-verbatim mirror (windows CI compiles it). **Caveat:** on a Deck in Game Mode,
> Steam Input still holds the device — the user must disable Steam Input for the client (the Decky UX,
> next); on a desktop client (or a Deck with Steam Input off) the hints just work.
>
> **M3 result (protocol / ABI wire, on-box):** strictly additive + forward-compatible (§5).
> Core: back-button bits `BTN_PADDLE1..4` + `BTN_MISC1` (in Moonlight's `buttonFlags2<<16`