fix(inject/host/windows): fold Steam back grips on the Windows DS/DS4 backends (G7)

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) <noreply@anthropic.com>
This commit is contained in:
2026-07-13 13:36:13 +02:00
parent 0c427cb3f1
commit 6263108e15
3 changed files with 23 additions and 3 deletions
+3 -1
View File
@@ -527,7 +527,9 @@ pub mod steam_gadget;
#[path = "inject/proto/steam_proto.rs"] #[path = "inject/proto/steam_proto.rs"]
pub mod steam_proto; pub mod steam_proto;
/// Pure fallback-remap policy (Steam-only inputs onto a non-Steam backend) + the Deck motion rescale. /// 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"] #[path = "inject/proto/steam_remap.rs"]
pub mod steam_remap; pub mod steam_remap;
/// Linux: virtual Steam Deck over **USB/IP** (`vhci_hcd`) — the shippable, Secure-Boot-clean, /// Linux: virtual Steam Deck over **USB/IP** (`vhci_hcd`) — the shippable, Secure-Boot-clean,
@@ -389,6 +389,9 @@ pub struct DualSenseWindowsManager {
/// Create-retry gate: a transient UMDF-channel failure backs off and retries instead of /// Create-retry gate: a transient UMDF-channel failure backs off and retries instead of
/// permanently disabling every pad for the session. /// permanently disabling every pad for the session.
gate: PadGate, 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 { impl Default for DualSenseWindowsManager {
@@ -405,6 +408,7 @@ impl DualSenseWindowsManager {
last_rumble: vec![(0, 0); MAX_PADS], last_rumble: vec![(0, 0); MAX_PADS],
last_write: vec![Instant::now(); MAX_PADS], last_write: vec![Instant::now(); MAX_PADS],
gate: PadGate::new(), gate: PadGate::new(),
remap: crate::inject::steam_remap::RemapConfig::from_env(),
} }
} }
@@ -433,8 +437,13 @@ impl DualSenseWindowsManager {
} }
self.ensure(idx); self.ensure(idx);
let prev = self.state[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( let mut s = DsState::from_gamepad(
f.buttons, buttons,
f.ls_x, f.ls_x,
f.ls_y, f.ls_y,
f.rs_x, f.rs_x,
@@ -153,6 +153,9 @@ pub struct DualShock4WindowsManager {
/// Create-retry gate: a transient UMDF-channel failure backs off and retries instead of /// Create-retry gate: a transient UMDF-channel failure backs off and retries instead of
/// permanently disabling every pad for the session. /// permanently disabling every pad for the session.
gate: PadGate, 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 { impl Default for DualShock4WindowsManager {
@@ -170,6 +173,7 @@ impl DualShock4WindowsManager {
last_led: vec![None; MAX_PADS], last_led: vec![None; MAX_PADS],
last_write: vec![Instant::now(); MAX_PADS], last_write: vec![Instant::now(); MAX_PADS],
gate: PadGate::new(), gate: PadGate::new(),
remap: crate::inject::steam_remap::RemapConfig::from_env(),
} }
} }
@@ -199,8 +203,13 @@ impl DualShock4WindowsManager {
} }
self.ensure(idx); self.ensure(idx);
let prev = self.state[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( let mut s = DsState::from_gamepad(
f.buttons, buttons,
f.ls_x, f.ls_x,
f.ls_y, f.ls_y,
f.rs_x, f.rs_x,