From 43e52437c0f5ccc7aeae44bebbbbf7c9426e6026 Mon Sep 17 00:00:00 2001 From: enricobuehler Date: Mon, 13 Jul 2026 12:04:59 +0200 Subject: [PATCH] fix(gamepad/host): map BTN_MISC1 to the DualSense mute button (G6) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit DsState::from_gamepad mapped GUIDE→PS and TOUCHPAD→TOUCHPAD into buttons[2] but never handled BTN_MISC1, so the mic-mute / capture button clients send was inert on every PlayStation-family virtual pad (DualSense/DualShock4), and btn2::MUTE was dead code. Map BTN_MISC1 → btn2::MUTE (rebuilt from the wire bit each frame like PS/TOUCHPAD, so no persistence gap) and drop the #[allow(dead_code)]. Test extended (from_gamepad_maps_touchpad_click); green on Linux (.21). Co-Authored-By: Claude Opus 4.8 (1M context) --- .../src/inject/proto/dualsense_proto.rs | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/crates/punktfunk-host/src/inject/proto/dualsense_proto.rs b/crates/punktfunk-host/src/inject/proto/dualsense_proto.rs index 62889ae6..1487ad7c 100644 --- a/crates/punktfunk-host/src/inject/proto/dualsense_proto.rs +++ b/crates/punktfunk-host/src/inject/proto/dualsense_proto.rs @@ -96,7 +96,7 @@ pub mod btn1 { pub mod btn2 { pub const PS: u8 = 0x01; pub const TOUCHPAD: u8 = 0x02; - #[allow(dead_code)] + /// Mic-mute / capture button — set from the wire `BTN_MISC1` in `DsState::from_gamepad`. pub const MUTE: u8 = 0x04; } @@ -223,6 +223,12 @@ impl DsState { if on(gs::BTN_TOUCHPAD) { s.buttons[2] |= btn2::TOUCHPAD; } + // The mic-mute / capture button (Deck '…' QAM on the Steam path). Clients send it as + // BTN_MISC1; without this the DualSense mute button was inert on every PlayStation-family + // virtual pad. Rebuilt from the wire bit each frame like PS/TOUCHPAD, so no persistence gap. + if on(gs::BTN_MISC1) { + s.buttons[2] |= btn2::MUTE; + } s } @@ -669,12 +675,16 @@ mod tests { assert_eq!(r[53], 0x0A); } - /// The wire touchpad-click bit (Moonlight's extended position) lands in `buttons[2]`. + /// The wire touchpad-click / guide / mute bits (Moonlight's extended positions) land in + /// `buttons[2]`. #[test] fn from_gamepad_maps_touchpad_click() { use punktfunk_core::input::gamepad as gs; let s = DsState::from_gamepad(gs::BTN_TOUCHPAD | gs::BTN_GUIDE, 0, 0, 0, 0, 0, 0); assert_eq!(s.buttons[2], btn2::PS | btn2::TOUCHPAD); + // BTN_MISC1 → the mic-mute / capture button (G6: was previously dropped entirely). + let s = DsState::from_gamepad(gs::BTN_MISC1, 0, 0, 0, 0, 0, 0); + assert_eq!(s.buttons[2], btn2::MUTE); let s = DsState::from_gamepad(gs::BTN_A, 0, 0, 0, 0, 0, 0); assert_eq!(s.buttons[2], 0); }