feat(host/steam): M6 — Steam-pad conflict gate (hardware-validated)
Don't present a virtual Steam (28DE) pad on a host that already has a physical Steam controller — the host's own Steam Input would then manage two Decks and confuse player assignment. - physical_steam_controller_present(): scans /sys/bus/hid/devices for a 28DE HID device on a real (non-/virtual/) path. - degrade_steam_on_conflict() in resolve_gamepad: a resolved SteamDeck / SteamController with a physical Steam controller attached degrades to DualSense (then the M5 uhid ladder); PUNKTFUNK_STEAM_FORCE=1 overrides (e.g. a remote-only box with no competing Steam Input). Validated on real hardware (a SteamOS Steam Deck @ .253 + a Bazzite host @ .41, both running Steam): - Conflict confirmed: the Deck-as-host already has its physical 28DE:1205 AND Steam's 28DE:11FF XInput output pad live; a 2nd virtual 28DE = two Decks. - Bind robustness: the virtual Deck binds hid-steam on a SECOND kernel (Bazzite 6.17.7, vs the dev box 7.0) and the kernel accepted our serial (the M1 fix). - Criterion-4 (running-Steam recognition) PARTIAL: a userspace consumer (Steam/ SDL) engaged the virtual Deck (opened the hidraw, ran the lizard-disable + settings sequence the kernel's Deck path skips) but emitted NO 28DE:11FF XInput pad on the desktop — so Steam recognizes it enough to manage lizard mode but did not promote it to a managed XInput controller (likely needs a Big-Picture/game context, or a richer device; the 0x83/0xA1 attribute probes never fired, so it wasn't a probe-reject either). - The heuristic itself checks TRUE on the Deck, FALSE on Bazzite. Workspace clippy/fmt/test green. Not pushed. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -1,12 +1,31 @@
|
||||
# Rich Steam Controller & Steam Deck support
|
||||
|
||||
> **Status:** **M0–M5 GREEN — full pipeline + fallback polish built (2026-06-29).** Host: the
|
||||
> virtual `hid-steam` Deck binds, is byte-exact, is a wired backend (`PUNKTFUNK_GAMEPAD=steamdeck`),
|
||||
> the protocol carries the rich inputs, and the **fallback remap** keeps them from silently dropping
|
||||
> on a non-Steam backend. Clients: the Linux + Windows SDL clients capture + send them; the Decky
|
||||
> plugin has the Steam Deck mode + Disable-Steam-Input UX; the C-ABI has the `TouchpadEx` send path;
|
||||
> Apple/Android round-trip the type. Remaining is **validation, not construction** (see below) + the
|
||||
> deferred extras (M6 SteamOS-host conflict check, M7 Windows UMDF Steam driver).
|
||||
> **Status:** **M0–M6 GREEN — full pipeline + fallback + conflict gate built (2026-06-29).** Host:
|
||||
> the virtual `hid-steam` Deck binds, is byte-exact, is a wired backend (`PUNKTFUNK_GAMEPAD=steamdeck`),
|
||||
> the protocol carries the rich inputs, the **fallback remap** keeps them from silently dropping, and
|
||||
> the **conflict gate** keeps a virtual Steam pad off a host that already has a physical one. Clients:
|
||||
> the Linux + Windows SDL clients capture + send them; the Decky plugin has the Steam Deck mode +
|
||||
> Disable-Steam-Input UX; the C-ABI has the `TouchpadEx` send path; Apple/Android round-trip the type.
|
||||
> Remaining: M7 (Windows UMDF Steam driver) + the last hardware validation.
|
||||
>
|
||||
> **M6 (conflict gate) result — validated on real hardware (a SteamOS Deck + a Bazzite host running
|
||||
> Steam, 2026-06-29):** (a) **Empirical conflict confirmed.** A Deck-as-host already has its physical
|
||||
> `28DE:1205` *and* Steam's `28DE:11FF` XInput output pad live — so a second virtual `28DE` makes Steam
|
||||
> juggle two Decks. (b) **Bind robustness:** the virtual Deck binds `hid-steam` on a *second*
|
||||
> independent kernel (Bazzite 6.17.7, vs the dev box 7.0) and the kernel accepted our serial (the M1
|
||||
> report-id-0 fix). (c) **Criterion-4 (running-Steam recognition) — partial:** with Steam running on
|
||||
> Bazzite, a userspace consumer (Steam/SDL) *did* engage the virtual Deck — it opened the hidraw and
|
||||
> ran the lizard-disable + settings sequence (`0xAE`/`0x85`/`0x8E`/`0x81`/`0x87`; the kernel's Deck path
|
||||
> skips lizard, so that's Steam/SDL) — **but emitted no `28DE:11FF` XInput pad** on the desktop, even
|
||||
> though the Deck proves desktop-Steam *does* emit one for a recognized Deck. So Steam recognizes the
|
||||
> device enough to manage its lizard mode but did **not** promote it to a managed XInput controller —
|
||||
> likely needs a Big-Picture/game foreground context or a richer device (the `0x83`/`0xA1` attribute
|
||||
> probes the research feared never fired, so it wasn't a probe-reject either). **Policy (code):**
|
||||
> `physical_steam_controller_present()` (scans `/sys/bus/hid/devices` for a non-virtual `28DE`) +
|
||||
> `degrade_steam_on_conflict()` in `resolve_gamepad` — a resolved `SteamDeck`/`SteamController` on a
|
||||
> host with a physical Steam controller degrades to DualSense (then the uhid ladder), overridable with
|
||||
> `PUNKTFUNK_STEAM_FORCE=1`. Heuristic hardware-checked: **TRUE on the Deck, FALSE on Bazzite.**
|
||||
> Workspace clippy/fmt/test green.
|
||||
>
|
||||
> **M5 (fallback remap + degrade ladder) result:** new pure, unit-tested `inject/proto/steam_remap.rs`:
|
||||
> (1) **motion rescale** `motion_wire_to_deck` — the wire carries DualSense-convention units (what
|
||||
@@ -34,9 +53,13 @@
|
||||
> device; Android has no rich-input plane yet). Rust workspace clippy/fmt/test green; Decky `src/`
|
||||
> typechecks clean; Swift/Kotlin compile on their CI.
|
||||
>
|
||||
> **Pending VALIDATION (whole feature, needs hardware we don't have):** (1) recognition of the
|
||||
> virtual Deck by a **running Steam** on the host; (2) a **live Deck/Steam Controller client** actually
|
||||
> sending paddles/trackpads/gyro; (3) the **Moonlight paddle regression** from the M3 xpad-map change.
|
||||
> **Pending VALIDATION:** (1) **running-Steam recognition — PARTIAL** (M6: Steam engages the virtual
|
||||
> Deck's hidraw but didn't emit an XInput pad on the desktop; re-test with a foreground Big-Picture
|
||||
> game on the Deck/Bazzite to see if it promotes then — and if not, capture the real `0x83`/`0xA1`
|
||||
> attribute blobs from a physical Deck for M7-style fidelity); (2) a **live Deck/Steam Controller
|
||||
> client** actually sending paddles/trackpads/gyro (the desktop capture is built but unexercised by
|
||||
> real hardware — note the Deck's `/dev/uhid` is root-only `0600`, so the Deck-as-host needs a udev
|
||||
> rule for the input group); (3) the **Moonlight paddle regression** from the M3 xpad-map change.
|
||||
>
|
||||
> **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
|
||||
|
||||
Reference in New Issue
Block a user