diff --git a/crates/punktfunk-core/src/abi.rs b/crates/punktfunk-core/src/abi.rs index 7b24bb85..243d76a7 100644 --- a/crates/punktfunk-core/src/abi.rs +++ b/crates/punktfunk-core/src/abi.rs @@ -889,6 +889,12 @@ pub const PUNKTFUNK_GAMEPAD_STEAMCONTROLLER: u32 = 5; /// four back grips, a right trackpad, and the IMU; re-grabbed by Steam Input with native glyphs when /// Steam runs on the host. Honored only where available (Linux hosts); else folds to X-Box 360. pub const PUNKTFUNK_GAMEPAD_STEAMDECK: u32 = 6; +/// DualSense Edge (Sony `054C:0DF2`): the DualSense plus two back buttons + two Fn buttons, so a +/// client's back paddles land on native slots. Folds to `DUALSENSE` until its backend lands. +pub const PUNKTFUNK_GAMEPAD_DUALSENSEEDGE: u32 = 7; +/// Nintendo Switch Pro Controller (Nintendo `057E:2009`, kernel `hid-nintendo`): Nintendo glyphs + +/// positional layout, gyro/accel, HD rumble. Folds to `XBOX360` until its backend lands. +pub const PUNKTFUNK_GAMEPAD_SWITCHPRO: u32 = 8; /// Extended `InputEvent` gamepad button bits for embedders building raw events: the four back grips /// (Steam L4/L5/R4/R5 ≙ Xbox-Elite P1–P4) + the misc/capture button, in Moonlight's @@ -945,6 +951,8 @@ const _: () = { assert!(PUNKTFUNK_GAMEPAD_DUALSHOCK4 == GamepadPref::DualShock4.to_u8() as u32); assert!(PUNKTFUNK_GAMEPAD_STEAMCONTROLLER == GamepadPref::SteamController.to_u8() as u32); assert!(PUNKTFUNK_GAMEPAD_STEAMDECK == GamepadPref::SteamDeck.to_u8() as u32); + assert!(PUNKTFUNK_GAMEPAD_DUALSENSEEDGE == GamepadPref::DualSenseEdge.to_u8() as u32); + assert!(PUNKTFUNK_GAMEPAD_SWITCHPRO == GamepadPref::SwitchPro.to_u8() as u32); // Extended button bits mirror the wire `input::gamepad` constants. assert!(PUNKTFUNK_GAMEPAD_BTN_PADDLE1 == g::BTN_PADDLE1); assert!(PUNKTFUNK_GAMEPAD_BTN_PADDLE2 == g::BTN_PADDLE2); diff --git a/crates/punktfunk-core/src/config.rs b/crates/punktfunk-core/src/config.rs index b09814a4..9d1680d0 100644 --- a/crates/punktfunk-core/src/config.rs +++ b/crates/punktfunk-core/src/config.rs @@ -138,8 +138,8 @@ impl CompositorPref { /// 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`, -/// `5 = SteamController`, `6 = SteamDeck`), appended to `Hello`/`Welcome` — older peers simply -/// omit/ignore it (an unknown byte degrades to `Auto`). +/// `5 = SteamController`, `6 = SteamDeck`, `7 = DualSenseEdge`, `8 = SwitchPro`), 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). @@ -164,11 +164,18 @@ pub enum GamepadPref { /// 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, + /// DualSense Edge (Sony `054C:0DF2`, kernel `hid-playstation` ≥ 6.3 / Windows UMDF) — the + /// DualSense plus two back buttons + two Fn buttons, so a client's back paddles (Deck grips, + /// Elite P1–P4) land on a native slot instead of the fold/drop policy. + DualSenseEdge, + /// Nintendo Switch Pro Controller (Nintendo `057E:2009`, kernel `hid-nintendo` ≥ 5.16) — + /// correct Nintendo glyphs + positional layout, gyro/accel, HD rumble back. Needs Linux UHID. + SwitchPro, } impl GamepadPref { /// Wire byte. `0 = Auto`, `1 = Xbox360`, `2 = DualSense`, `3 = XboxOne`, `4 = DualShock4`, - /// `5 = SteamController`, `6 = SteamDeck`. + /// `5 = SteamController`, `6 = SteamDeck`, `7 = DualSenseEdge`, `8 = SwitchPro`. pub const fn to_u8(self) -> u8 { match self { GamepadPref::Auto => 0, @@ -178,6 +185,8 @@ impl GamepadPref { GamepadPref::DualShock4 => 4, GamepadPref::SteamController => 5, GamepadPref::SteamDeck => 6, + GamepadPref::DualSenseEdge => 7, + GamepadPref::SwitchPro => 8, } } @@ -191,6 +200,8 @@ impl GamepadPref { 4 => GamepadPref::DualShock4, 5 => GamepadPref::SteamController, 6 => GamepadPref::SteamDeck, + 7 => GamepadPref::DualSenseEdge, + 8 => GamepadPref::SwitchPro, _ => GamepadPref::Auto, } } @@ -208,12 +219,16 @@ impl GamepadPref { "dualshock4" | "dualshock" | "ds4" | "ps4" => GamepadPref::DualShock4, "steamdeck" | "steam-deck" | "deck" => GamepadPref::SteamDeck, "steamcontroller" | "steam-controller" | "steamcon" => GamepadPref::SteamController, + "dualsenseedge" | "dualsense-edge" | "edge" | "dsedge" => GamepadPref::DualSenseEdge, + "switchpro" | "switch-pro" | "switch" | "procontroller" | "pro-controller" => { + GamepadPref::SwitchPro + } _ => return None, }) } /// Canonical lowercase identifier (`"auto"`, `"xbox360"`, `"dualsense"`, `"xboxone"`, - /// `"dualshock4"`, `"steamcontroller"`, `"steamdeck"`). + /// `"dualshock4"`, `"steamcontroller"`, `"steamdeck"`, `"dualsenseedge"`, `"switchpro"`). pub fn as_str(self) -> &'static str { match self { GamepadPref::Auto => "auto", @@ -223,6 +238,8 @@ impl GamepadPref { GamepadPref::DualShock4 => "dualshock4", GamepadPref::SteamController => "steamcontroller", GamepadPref::SteamDeck => "steamdeck", + GamepadPref::DualSenseEdge => "dualsenseedge", + GamepadPref::SwitchPro => "switchpro", } } } diff --git a/crates/punktfunk-core/src/quic/tests.rs b/crates/punktfunk-core/src/quic/tests.rs index 9e09a804..2900fd6f 100644 --- a/crates/punktfunk-core/src/quic/tests.rs +++ b/crates/punktfunk-core/src/quic/tests.rs @@ -275,18 +275,45 @@ fn gamepad_pref_wire_and_names() { GamepadPref::DualSense, GamepadPref::XboxOne, GamepadPref::DualShock4, + GamepadPref::SteamController, + GamepadPref::SteamDeck, + GamepadPref::DualSenseEdge, + GamepadPref::SwitchPro, ] { assert_eq!(GamepadPref::from_u8(p.to_u8()), p); assert_eq!(GamepadPref::from_name(p.as_str()), Some(p)); } - // Distinct wire bytes (forward-compat with peers that only know 0..=2). - assert_eq!(GamepadPref::XboxOne.to_u8(), 3); - assert_eq!(GamepadPref::DualShock4.to_u8(), 4); + // Every wire byte 0..=8 is assigned, distinct, and pinned (forward-compat with peers + // that only know a prefix of the range). + for (v, p) in [ + (0, GamepadPref::Auto), + (1, GamepadPref::Xbox360), + (2, GamepadPref::DualSense), + (3, GamepadPref::XboxOne), + (4, GamepadPref::DualShock4), + (5, GamepadPref::SteamController), + (6, GamepadPref::SteamDeck), + (7, GamepadPref::DualSenseEdge), + (8, GamepadPref::SwitchPro), + ] { + assert_eq!(p.to_u8(), v); + assert_eq!(GamepadPref::from_u8(v), p); + } + // The next unassigned byte degrades to Auto today; assigning it later must update this. + assert_eq!(GamepadPref::from_u8(9), GamepadPref::Auto); // Aliases + unknowns. assert_eq!(GamepadPref::from_name("PS5"), Some(GamepadPref::DualSense)); assert_eq!(GamepadPref::from_name("x360"), Some(GamepadPref::Xbox360)); assert_eq!(GamepadPref::from_name("ps4"), Some(GamepadPref::DualShock4)); assert_eq!(GamepadPref::from_name("DS4"), Some(GamepadPref::DualShock4)); + assert_eq!( + GamepadPref::from_name("edge"), + Some(GamepadPref::DualSenseEdge) + ); + assert_eq!( + GamepadPref::from_name("Switch-Pro"), + Some(GamepadPref::SwitchPro) + ); assert_eq!( GamepadPref::from_name("xbox-one"), Some(GamepadPref::XboxOne) diff --git a/crates/punktfunk-host/src/punktfunk1.rs b/crates/punktfunk-host/src/punktfunk1.rs index 38147ed2..9ed5b460 100644 --- a/crates/punktfunk-host/src/punktfunk1.rs +++ b/crates/punktfunk-host/src/punktfunk1.rs @@ -2700,6 +2700,12 @@ fn pick_gamepad(pref: GamepadPref, env: Option<&str>, linux: bool, windows: bool // DualSense touchpad left/right per DsState::apply_rich). Folding to Xbox360 dropped // all of that silently. GamepadPref::SteamDeck if windows => GamepadPref::DualSense, + // DualSense Edge: until its backend lands (N1), fold to the plain DualSense wherever + // that exists — it keeps the rich planes (touchpad/motion/lightbar) alive; only the + // back-button bits go through the paddle fold. NOT Xbox360 (that would drop the rich + // planes entirely, the SteamDeck-on-Windows lesson above). + GamepadPref::DualSenseEdge if linux || windows => GamepadPref::DualSense, + // Switch Pro: no backend yet (N2) — falls through to Xbox360 below. _ => GamepadPref::Xbox360, } } @@ -5279,6 +5285,17 @@ mod tests { assert_eq!(pick_gamepad(SteamDeck, None, false, true), DualSense); assert_eq!(pick_gamepad(Auto, Some("deck"), false, true), DualSense); assert_eq!(pick_gamepad(SteamDeck, None, false, false), Xbox360); + + // DualSense Edge: folds to the plain DualSense (keeps the rich planes) until the Edge + // backend lands (gamepad-new-types N1); Xbox360 where no DualSense backend exists. + assert_eq!(pick_gamepad(DualSenseEdge, None, true, false), DualSense); + assert_eq!(pick_gamepad(DualSenseEdge, None, false, true), DualSense); + assert_eq!(pick_gamepad(Auto, Some("edge"), true, false), DualSense); + assert_eq!(pick_gamepad(DualSenseEdge, None, false, false), Xbox360); + // Switch Pro: no backend yet (gamepad-new-types N2) — folds to Xbox360 everywhere. + assert_eq!(pick_gamepad(SwitchPro, None, true, false), Xbox360); + assert_eq!(pick_gamepad(Auto, Some("switchpro"), true, false), Xbox360); + assert_eq!(pick_gamepad(SwitchPro, None, false, true), Xbox360); } #[test] diff --git a/include/punktfunk_core.h b/include/punktfunk_core.h index f7bc8d36..838aa24d 100644 --- a/include/punktfunk_core.h +++ b/include/punktfunk_core.h @@ -121,6 +121,14 @@ // Steam runs on the host. Honored only where available (Linux hosts); else folds to X-Box 360. #define PUNKTFUNK_GAMEPAD_STEAMDECK 6 +// DualSense Edge (Sony `054C:0DF2`): the DualSense plus two back buttons + two Fn buttons, so a +// client's back paddles land on native slots. Folds to `DUALSENSE` until its backend lands. +#define PUNKTFUNK_GAMEPAD_DUALSENSEEDGE 7 + +// Nintendo Switch Pro Controller (Nintendo `057E:2009`, kernel `hid-nintendo`): Nintendo glyphs + +// positional layout, gyro/accel, HD rumble. Folds to `XBOX360` until its backend lands. +#define PUNKTFUNK_GAMEPAD_SWITCHPRO 8 + // Extended `InputEvent` gamepad button bits for embedders building raw events: the four back grips // (Steam L4/L5/R4/R5 ≙ Xbox-Elite P1–P4) + the misc/capture button, in Moonlight's // `buttonFlags2 << 16` namespace. Mirror `input::gamepad::BTN_PADDLE1..4` / `BTN_MISC1`.