diff --git a/crates/punktfunk-host/src/gamestream/gamepad.rs b/crates/punktfunk-host/src/gamestream/gamepad.rs index 67446303..fdd7a990 100644 --- a/crates/punktfunk-host/src/gamestream/gamepad.rs +++ b/crates/punktfunk-host/src/gamestream/gamepad.rs @@ -21,38 +21,11 @@ const MAGIC_CONTROLLER_ARRIVAL: u32 = 0x5500_0004; // the platform-neutral injectors, so they live in `core::input` (below both) rather than here. use punktfunk_core::input::{GamepadEvent, GamepadFrame}; -// GameStream's `buttonFlags | buttonFlags2 << 16` layout (Limelight.h) is bit-identical to -// punktfunk's native gamepad wire, so source these from the single point of truth in `punktfunk_core` -// instead of re-declaring the values (the two drifted while separately hand-typed: the click bits -// were named `BTN_LS_CLK`/`BTN_RS_CLK` here vs the core `…_CLICK`). `decode` merges the two 16-bit -// halves into `buttons` raw; these names exist for the uinput injector's button map + hat math. The -// extended touchpad-click / Share bits (`BTN_TOUCHPAD` / `BTN_MISC1`) ride `buttons` too but are -// consumed straight from `punktfunk_core` by the DualSense/DS4 protos, so they aren't re-named here. -// -// These are `pub const` aliases rather than a `pub use` re-export on purpose: on Windows the sole -// consumer (the Linux uinput map) is cfg'd out, and an unused re-export lints as an error there, -// whereas an unused `pub const` does not. The values still come only from core, so they can't drift; -// the exact wire values are pinned by `native.rs::gamepad_wire_bits_are_pinned`. -use punktfunk_core::input::gamepad as wire; -pub const BTN_DPAD_UP: u32 = wire::BTN_DPAD_UP; -pub const BTN_DPAD_DOWN: u32 = wire::BTN_DPAD_DOWN; -pub const BTN_DPAD_LEFT: u32 = wire::BTN_DPAD_LEFT; -pub const BTN_DPAD_RIGHT: u32 = wire::BTN_DPAD_RIGHT; -pub const BTN_START: u32 = wire::BTN_START; -pub const BTN_BACK: u32 = wire::BTN_BACK; -pub const BTN_LS_CLICK: u32 = wire::BTN_LS_CLICK; -pub const BTN_RS_CLICK: u32 = wire::BTN_RS_CLICK; -pub const BTN_LB: u32 = wire::BTN_LB; -pub const BTN_RB: u32 = wire::BTN_RB; -pub const BTN_GUIDE: u32 = wire::BTN_GUIDE; -pub const BTN_A: u32 = wire::BTN_A; -pub const BTN_B: u32 = wire::BTN_B; -pub const BTN_X: u32 = wire::BTN_X; -pub const BTN_Y: u32 = wire::BTN_Y; -pub const BTN_PADDLE1: u32 = wire::BTN_PADDLE1; -pub const BTN_PADDLE2: u32 = wire::BTN_PADDLE2; -pub const BTN_PADDLE3: u32 = wire::BTN_PADDLE3; -pub const BTN_PADDLE4: u32 = wire::BTN_PADDLE4; +// The `BTN_*` button bitmask and axis ids live in `punktfunk_core::input::gamepad` — GameStream's +// `buttonFlags | buttonFlags2 << 16` layout (Limelight.h) is bit-identical to punktfunk's native +// gamepad wire, so the injector button map + hat math source them straight from core (the single +// point of truth). `decode` below merges the two 16-bit halves into `buttons` raw; the exact wire +// values are pinned by `native.rs::gamepad_wire_bits_are_pinned`. /// Decode one decrypted control plaintext into a controller event, if it is one. Mouse, /// keyboard, keepalives etc. yield `None` (they're handled by [`super::input::decode`]). @@ -114,6 +87,7 @@ pub fn rumble_plaintext(index: u16, low: u16, high: u16) -> Vec { #[cfg(test)] mod tests { use super::*; + use punktfunk_core::input::gamepad::{BTN_A, BTN_RB}; fn wrap(magic: u32, body: &[u8]) -> Vec { let mut inp = Vec::new(); diff --git a/crates/punktfunk-host/src/inject/linux/gamepad.rs b/crates/punktfunk-host/src/inject/linux/gamepad.rs index d714adc7..a357251c 100644 --- a/crates/punktfunk-host/src/inject/linux/gamepad.rs +++ b/crates/punktfunk-host/src/inject/linux/gamepad.rs @@ -18,10 +18,9 @@ // Every `unsafe` block in this file carries a `// SAFETY:` proof; enforce it (unsafe-proof program). #![deny(clippy::undocumented_unsafe_blocks)] -use crate::gamestream::gamepad; use crate::inject::pad_slots::PadSlots; use anyhow::{bail, Result}; -use punktfunk_core::input::{GamepadFrame, MAX_PADS}; +use punktfunk_core::input::{gamepad, GamepadFrame, MAX_PADS}; use std::collections::HashMap; use std::os::fd::{AsRawFd, OwnedFd}; use std::time::Instant; diff --git a/crates/punktfunk-host/src/native.rs b/crates/punktfunk-host/src/native.rs index 7078c1fc..537c5423 100644 --- a/crates/punktfunk-host/src/native.rs +++ b/crates/punktfunk-host/src/native.rs @@ -1449,38 +1449,38 @@ mod tests { ); } - /// Freeze the gamepad wire contract: every button bit + axis id pinned to its exact value, read - /// through the GameStream namespace (`crate::gamestream::gamepad`, which re-exports - /// `punktfunk_core::input::gamepad` — the punktfunk/1 native wire and the GameStream/Limelight - /// wire are one and the same). Renumbering a bit in core, or dropping one from that re-export, + /// Freeze the gamepad wire contract: every button bit + axis id pinned to its exact value in + /// `punktfunk_core::input::gamepad` — the single source both the punktfunk/1 native wire and the + /// GameStream/Limelight wire read from (they are one and the same). Renumbering a bit in core /// silently breaks every already-shipped client, so it must fail here first. This is the host /// counterpart to the client-side C-ABI cross-checks in the Apple/Android gamepad tests. #[test] fn gamepad_wire_bits_are_pinned() { - use crate::gamestream::gamepad as gm; use punktfunk_core::input::gamepad as pf; - // buttonFlags — low 16 bits, named via the GameStream re-export the injectors use. - assert_eq!(gm::BTN_DPAD_UP, 0x0000_0001); - assert_eq!(gm::BTN_DPAD_DOWN, 0x0000_0002); - assert_eq!(gm::BTN_DPAD_LEFT, 0x0000_0004); - assert_eq!(gm::BTN_DPAD_RIGHT, 0x0000_0008); - assert_eq!(gm::BTN_START, 0x0000_0010); - assert_eq!(gm::BTN_BACK, 0x0000_0020); - assert_eq!(gm::BTN_LS_CLICK, 0x0000_0040); - assert_eq!(gm::BTN_RS_CLICK, 0x0000_0080); - assert_eq!(gm::BTN_LB, 0x0000_0100); - assert_eq!(gm::BTN_RB, 0x0000_0200); - assert_eq!(gm::BTN_GUIDE, 0x0000_0400); - assert_eq!(gm::BTN_A, 0x0000_1000); - assert_eq!(gm::BTN_B, 0x0000_2000); - assert_eq!(gm::BTN_X, 0x0000_4000); - assert_eq!(gm::BTN_Y, 0x0000_8000); - // buttonFlags2 — high 16 bits: back-grip paddles (re-exported), plus the touchpad-click / - // Share bits the DualSense/DS4 protos consume straight from core. - assert_eq!(gm::BTN_PADDLE1, 0x0001_0000); - assert_eq!(gm::BTN_PADDLE2, 0x0002_0000); - assert_eq!(gm::BTN_PADDLE3, 0x0004_0000); - assert_eq!(gm::BTN_PADDLE4, 0x0008_0000); + // buttonFlags — low 16 bits. The injectors now name these straight from core::input::gamepad + // (the GameStream junk-drawer aliases were removed in the pf-inject un-coupling), so this pins + // core directly. + assert_eq!(pf::BTN_DPAD_UP, 0x0000_0001); + assert_eq!(pf::BTN_DPAD_DOWN, 0x0000_0002); + assert_eq!(pf::BTN_DPAD_LEFT, 0x0000_0004); + assert_eq!(pf::BTN_DPAD_RIGHT, 0x0000_0008); + assert_eq!(pf::BTN_START, 0x0000_0010); + assert_eq!(pf::BTN_BACK, 0x0000_0020); + assert_eq!(pf::BTN_LS_CLICK, 0x0000_0040); + assert_eq!(pf::BTN_RS_CLICK, 0x0000_0080); + assert_eq!(pf::BTN_LB, 0x0000_0100); + assert_eq!(pf::BTN_RB, 0x0000_0200); + assert_eq!(pf::BTN_GUIDE, 0x0000_0400); + assert_eq!(pf::BTN_A, 0x0000_1000); + assert_eq!(pf::BTN_B, 0x0000_2000); + assert_eq!(pf::BTN_X, 0x0000_4000); + assert_eq!(pf::BTN_Y, 0x0000_8000); + // buttonFlags2 — high 16 bits: back-grip paddles, plus the touchpad-click / Share bits the + // DualSense/DS4 protos consume. + assert_eq!(pf::BTN_PADDLE1, 0x0001_0000); + assert_eq!(pf::BTN_PADDLE2, 0x0002_0000); + assert_eq!(pf::BTN_PADDLE3, 0x0004_0000); + assert_eq!(pf::BTN_PADDLE4, 0x0008_0000); assert_eq!(pf::BTN_TOUCHPAD, 0x0010_0000); assert_eq!(pf::BTN_MISC1, 0x0020_0000); // Axis ids — dense, 0-based.