feat(host/steam): M2 — virtual Steam Deck as a wired PadBackend (Linux)
Make the virtual hid-steam device a selectable per-session host gamepad,
end-to-end on Linux: PUNKTFUNK_GAMEPAD=steamdeck now builds a
SteamControllerManager that creates a /dev/uhid 28DE:1205 Deck, enters
gamepad_mode, and feeds the byte-exact Deck report (M1).
- inject/linux/steam_controller.rs: SteamControllerManager / SteamDeckPad,
mirroring dualsense.rs (open/create2, GET/SET_REPORT pump, heartbeat, RAII
destroy). Two Steam-specific quirks beyond the DualSense path:
* gamepad_mode entry — best-effort `lizard_mode=0` via sysfs, plus a b9.6
creation pulse (MODE_ENTER) so steam_do_deck_input_event stops
early-returning, plus an anti-toggle guard (MENU_HOLD_CAP) so a long
in-game Start-hold can't flip gamepad_mode back off.
* UHID_SET_REPORT answered err=0 (DualSense omits it; the kernel stalls
~5s/cmd otherwise); the 0xEB rumble report parsed onto the 0xCA plane.
- core config.rs: GamepadPref::SteamDeck (wire byte 6) + SteamController
(byte 5, reserved — folds to Xbox360 until its backend lands); from_u8 /
from_name / as_str. Forward-compatible (unknown byte -> Auto); the C-ABI
PUNKTFUNK_GAMEPAD_* constants stay M3, so no generated-header drift.
- punktfunk1.rs: PadBackend::SteamDeck variant + select / handle / apply_rich
/ pump / heartbeat arms; pick_gamepad Linux arm.
On-box: an #[ignore]d backend test (backend_binds_and_input_flows) drives the
real SteamDeckPad — it binds hid-steam (gamepad + IMU evdevs), enters gamepad
mode, BTN_A reaches the evdev, and the device tears down on drop. Workspace
clippy/fmt/test green. Not pushed. Next: M3 (protocol/ABI wire) + M4 (client
capture).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -137,8 +137,9 @@ impl CompositorPref {
|
||||
/// host decide (its `PUNKTFUNK_GAMEPAD` env var, else X-Box 360). A concrete preference is
|
||||
/// honored only if that backend is available on the host (DualSense / DualShock 4 need Linux UHID);
|
||||
/// otherwise the host falls back and reports the real choice in `Welcome`. The wire form is a single
|
||||
/// byte (`0 = Auto`, `1 = Xbox360`, `2 = DualSense`, `3 = XboxOne`, `4 = DualShock4`), appended to
|
||||
/// `Hello`/`Welcome` — older peers simply omit/ignore it (an unknown byte degrades to `Auto`).
|
||||
/// byte (`0 = Auto`, `1 = Xbox360`, `2 = DualSense`, `3 = XboxOne`, `4 = DualShock4`,
|
||||
/// `5 = SteamController`, `6 = SteamDeck`), appended to `Hello`/`Welcome` — older peers simply
|
||||
/// omit/ignore it (an unknown byte degrades to `Auto`).
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, Default)]
|
||||
pub enum GamepadPref {
|
||||
/// Let the host pick (its `PUNKTFUNK_GAMEPAD` env var, else X-Box 360).
|
||||
@@ -155,10 +156,19 @@ pub enum GamepadPref {
|
||||
/// UHID DualShock 4 (kernel `hid-playstation`, ≥ 6.2) — lightbar, touchpad, motion, rumble. Like
|
||||
/// `DualSense` minus adaptive triggers / player LEDs / mute. Needs Linux UHID on the host.
|
||||
DualShock4,
|
||||
/// UHID classic Steam Controller (Valve `28DE:1102`, kernel `hid-steam`) — dual trackpads, gyro,
|
||||
/// two grip paddles, trackpad-only haptics. Needs Linux UHID. *(Reserved; its backend is not yet
|
||||
/// built — currently folds to `Xbox360`; the Deck identity below is the implemented one.)*
|
||||
SteamController,
|
||||
/// UHID Steam Deck controller (Valve `28DE:1205`, kernel `hid-steam`) — full Deck gamepad incl.
|
||||
/// the four back grips (L4/L5/R4/R5), a right trackpad, and the IMU; re-grabbed by Steam Input
|
||||
/// with native glyphs when Steam runs on the host. Needs Linux UHID.
|
||||
SteamDeck,
|
||||
}
|
||||
|
||||
impl GamepadPref {
|
||||
/// Wire byte. `0 = Auto`, `1 = Xbox360`, `2 = DualSense`, `3 = XboxOne`, `4 = DualShock4`.
|
||||
/// Wire byte. `0 = Auto`, `1 = Xbox360`, `2 = DualSense`, `3 = XboxOne`, `4 = DualShock4`,
|
||||
/// `5 = SteamController`, `6 = SteamDeck`.
|
||||
pub const fn to_u8(self) -> u8 {
|
||||
match self {
|
||||
GamepadPref::Auto => 0,
|
||||
@@ -166,6 +176,8 @@ impl GamepadPref {
|
||||
GamepadPref::DualSense => 2,
|
||||
GamepadPref::XboxOne => 3,
|
||||
GamepadPref::DualShock4 => 4,
|
||||
GamepadPref::SteamController => 5,
|
||||
GamepadPref::SteamDeck => 6,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -177,6 +189,8 @@ impl GamepadPref {
|
||||
2 => GamepadPref::DualSense,
|
||||
3 => GamepadPref::XboxOne,
|
||||
4 => GamepadPref::DualShock4,
|
||||
5 => GamepadPref::SteamController,
|
||||
6 => GamepadPref::SteamDeck,
|
||||
_ => GamepadPref::Auto,
|
||||
}
|
||||
}
|
||||
@@ -192,12 +206,14 @@ impl GamepadPref {
|
||||
GamepadPref::XboxOne
|
||||
}
|
||||
"dualshock4" | "dualshock" | "ds4" | "ps4" => GamepadPref::DualShock4,
|
||||
"steamdeck" | "steam-deck" | "deck" => GamepadPref::SteamDeck,
|
||||
"steamcontroller" | "steam-controller" | "steamcon" => GamepadPref::SteamController,
|
||||
_ => return None,
|
||||
})
|
||||
}
|
||||
|
||||
/// Canonical lowercase identifier (`"auto"`, `"xbox360"`, `"dualsense"`, `"xboxone"`,
|
||||
/// `"dualshock4"`).
|
||||
/// `"dualshock4"`, `"steamcontroller"`, `"steamdeck"`).
|
||||
pub fn as_str(self) -> &'static str {
|
||||
match self {
|
||||
GamepadPref::Auto => "auto",
|
||||
@@ -205,6 +221,8 @@ impl GamepadPref {
|
||||
GamepadPref::DualSense => "dualsense",
|
||||
GamepadPref::XboxOne => "xboxone",
|
||||
GamepadPref::DualShock4 => "dualshock4",
|
||||
GamepadPref::SteamController => "steamcontroller",
|
||||
GamepadPref::SteamDeck => "steamdeck",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user