From 6263108e1515245dbdcc68c62a46aeda7c7602af Mon Sep 17 00:00:00 2001 From: enricobuehler Date: Mon, 13 Jul 2026 13:36:13 +0200 Subject: [PATCH] fix(inject/host/windows): fold Steam back grips on the Windows DS/DS4 backends (G7) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The Windows DualSense and DualShock 4 managers passed the raw wire buttons straight into `DsState::from_gamepad`, so a client's Steam back grips (BTN_PADDLE1..4) were silently dropped and `PUNKTFUNK_STEAM_REMAP` was ignored — the Linux DS/DS4 backends already fold them via `steam_remap::fold_paddles`. Bring the Windows backends to parity: add a `remap: steam_remap::RemapConfig` field (`::from_env()` in `new()`) to both managers and fold the paddles before `from_gamepad`, exactly as `linux/dualsense.rs` / `linux/dualshock4.rs`. Default policy stays Drop (don't fire buttons the user didn't ask for); set the env to map the grips onto stick-clicks or shoulders. `steam_remap` was gated `target_os = "linux"`; widened to `any(linux, windows)`. It's pure (only punktfunk_core + std::env); its Linux-only Deck motion rescale is `pub` so it compiles clean on Windows with no dead-code warning. Verified: Linux .21 (clippy -D warnings clean, inject tests 32 pass / 0 fail — the gate widening is a no-op there); Windows .173 (clean-recheck of punktfunk-host, cargo clippy --all-targets -D warnings EXITCODE 0, steam_remap + both managers compiling on Windows for the first time). On-glass with a real DualSense/DS4 + PUNKTFUNK_STEAM_REMAP still owed. Co-Authored-By: Claude Opus 4.8 (1M context) --- crates/punktfunk-host/src/inject.rs | 4 +++- .../src/inject/windows/dualsense_windows.rs | 11 ++++++++++- .../src/inject/windows/dualshock4_windows.rs | 11 ++++++++++- 3 files changed, 23 insertions(+), 3 deletions(-) diff --git a/crates/punktfunk-host/src/inject.rs b/crates/punktfunk-host/src/inject.rs index 9fc1696a..0bf07aa6 100644 --- a/crates/punktfunk-host/src/inject.rs +++ b/crates/punktfunk-host/src/inject.rs @@ -527,7 +527,9 @@ pub mod steam_gadget; #[path = "inject/proto/steam_proto.rs"] pub mod steam_proto; /// Pure fallback-remap policy (Steam-only inputs onto a non-Steam backend) + the Deck motion rescale. -#[cfg(target_os = "linux")] +/// Shared by the Linux and Windows DualSense/DS4 backends (the slot-less pads that must fold the +/// Steam back grips); the Deck motion rescale is Linux-only but harmless to compile on Windows. +#[cfg(any(target_os = "linux", target_os = "windows"))] #[path = "inject/proto/steam_remap.rs"] pub mod steam_remap; /// Linux: virtual Steam Deck over **USB/IP** (`vhci_hcd`) — the shippable, Secure-Boot-clean, diff --git a/crates/punktfunk-host/src/inject/windows/dualsense_windows.rs b/crates/punktfunk-host/src/inject/windows/dualsense_windows.rs index cc6ce9d9..9f1b3659 100644 --- a/crates/punktfunk-host/src/inject/windows/dualsense_windows.rs +++ b/crates/punktfunk-host/src/inject/windows/dualsense_windows.rs @@ -389,6 +389,9 @@ pub struct DualSenseWindowsManager { /// Create-retry gate: a transient UMDF-channel failure backs off and retries instead of /// permanently disabling every pad for the session. gate: PadGate, + /// Fallback policy for the Steam back grips a client may send (the DualSense has no back-button + /// HID slot). `PUNKTFUNK_STEAM_REMAP=paddles=…`; default drop. Parity with `linux/dualsense.rs`. + remap: crate::inject::steam_remap::RemapConfig, } impl Default for DualSenseWindowsManager { @@ -405,6 +408,7 @@ impl DualSenseWindowsManager { last_rumble: vec![(0, 0); MAX_PADS], last_write: vec![Instant::now(); MAX_PADS], gate: PadGate::new(), + remap: crate::inject::steam_remap::RemapConfig::from_env(), } } @@ -433,8 +437,13 @@ impl DualSenseWindowsManager { } self.ensure(idx); let prev = self.state[idx]; + // Steam back grips have no DualSense slot — fold them onto standard buttons per the + // configured policy (default drop) so they aren't silently lost, exactly as + // `linux/dualsense.rs` does. + let buttons = + crate::inject::steam_remap::fold_paddles(f.buttons, self.remap.paddles); let mut s = DsState::from_gamepad( - f.buttons, + buttons, f.ls_x, f.ls_y, f.rs_x, diff --git a/crates/punktfunk-host/src/inject/windows/dualshock4_windows.rs b/crates/punktfunk-host/src/inject/windows/dualshock4_windows.rs index fd91f308..29ba5d1a 100644 --- a/crates/punktfunk-host/src/inject/windows/dualshock4_windows.rs +++ b/crates/punktfunk-host/src/inject/windows/dualshock4_windows.rs @@ -153,6 +153,9 @@ pub struct DualShock4WindowsManager { /// Create-retry gate: a transient UMDF-channel failure backs off and retries instead of /// permanently disabling every pad for the session. gate: PadGate, + /// Fallback policy for the Steam back grips a client may send (the DS4 has no back-button HID + /// slot). `PUNKTFUNK_STEAM_REMAP=paddles=…`; default drop. Parity with `linux/dualshock4.rs`. + remap: crate::inject::steam_remap::RemapConfig, } impl Default for DualShock4WindowsManager { @@ -170,6 +173,7 @@ impl DualShock4WindowsManager { last_led: vec![None; MAX_PADS], last_write: vec![Instant::now(); MAX_PADS], gate: PadGate::new(), + remap: crate::inject::steam_remap::RemapConfig::from_env(), } } @@ -199,8 +203,13 @@ impl DualShock4WindowsManager { } self.ensure(idx); let prev = self.state[idx]; + // Steam back grips have no DS4 slot — fold them onto standard buttons per the + // configured policy (default drop) so they aren't silently lost, exactly as + // `linux/dualshock4.rs` does. + let buttons = + crate::inject::steam_remap::fold_paddles(f.buttons, self.remap.paddles); let mut s = DsState::from_gamepad( - f.buttons, + buttons, f.ls_x, f.ls_y, f.rs_x,