From 92908f0bc74edf1d35cd409b1707f38c13fedbaf Mon Sep 17 00:00:00 2001 From: "Derek J. Clark" Date: Mon, 17 Aug 2026 19:17:19 -0700 Subject: [PATCH] fix: Avoid clearing macro 'up' events, leaving buttons stuck on PR #623 introduced a bug where macros that were implemented on the target device would have their 'up' events cleared by mistake if the event arrived quickly. This would happen because the queed 'down' event would fire after the up events were unqueued if the up event arrived sooner than the down event fired. That would trigger the debounce and no later up event would fire, leaving the button stuck 'down'. Avoid the race by triggering the second event to land 1 frame after the first event. While at it, since the delayed ordering is no longer necessary, treat up and down events the same for enqueueing. --- src/input/composite_device/mod.rs | 18 ++++++++++-------- src/input/target/horipad_steam.rs | 22 ++++++++-------------- src/input/target/steam_deck.rs | 19 ++++++------------- src/input/target/steam_deck_uhid.rs | 19 ++++++------------- src/input/target/ulitmate_2.rs | 19 ++++++------------- 5 files changed, 36 insertions(+), 61 deletions(-) diff --git a/src/input/composite_device/mod.rs b/src/input/composite_device/mod.rs index fd2fdd78..4542ac42 100644 --- a/src/input/composite_device/mod.rs +++ b/src/input/composite_device/mod.rs @@ -728,14 +728,16 @@ impl CompositeDevice { // Check if the event needs to be translated based on the // capability map. Translated events will be re-enqueued, so this will // return early. - log::trace!( - "Translatable capabilities: {:?}", - self.translatable_capabilities - ); - if self.capability_map.is_some() && self.translatable_capabilities.contains(&cap) { - log::trace!("Capability mapping found for event"); - self.translate_capability(&event).await?; - return Ok(()); + if !self.translatable_capabilities.is_empty() { + log::trace!( + "Translatable capabilities: {:?}", + self.translatable_capabilities + ); + if self.capability_map.is_some() && self.translatable_capabilities.contains(&cap) { + log::trace!("Capability mapping found for event"); + self.translate_capability(&event).await?; + return Ok(()); + } } self.handle_event(event).await?; diff --git a/src/input/target/horipad_steam.rs b/src/input/target/horipad_steam.rs index c204e277..185edf48 100644 --- a/src/input/target/horipad_steam.rs +++ b/src/input/target/horipad_steam.rs @@ -122,27 +122,21 @@ impl HoripadSteamDevice { event.get_value(), ); // triggers at 128 exactly - let trigv = if pressed { 0.5 } else { 0.0 }; - let trigr = NativeEvent::new( + let trigger_value = if pressed { 0.5 } else { 0.0 }; + let trigger = NativeEvent::new( Capability::Gamepad(Gamepad::Trigger(GamepadTrigger::RightTrigger)), - InputValue::Float(trigv), + InputValue::Float(trigger_value), ); - let (guide, trigr) = if pressed { + let (guide, trigger) = { let guide = ScheduledNativeEvent::new(guide, Duration::from_millis(0)); - let trigr = - ScheduledNativeEvent::new(trigr, Duration::from_millis(160)); - (guide, trigr) - } else { - let guide = - ScheduledNativeEvent::new(guide, Duration::from_millis(240)); - let trigr = - ScheduledNativeEvent::new(trigr, Duration::from_millis(160)); - (guide, trigr) + let trigger = + ScheduledNativeEvent::new(trigger, Duration::from_millis(8)); + (guide, trigger) }; self.queued_events.push(guide); - self.queued_events.push(trigr); + self.queued_events.push(trigger); } _ => (), }, diff --git a/src/input/target/steam_deck.rs b/src/input/target/steam_deck.rs index bd37e64d..fbc3a016 100644 --- a/src/input/target/steam_deck.rs +++ b/src/input/target/steam_deck.rs @@ -548,31 +548,24 @@ impl SteamDeckDevice { GamepadButton::RightStickTouch => self.state.r_stick_touch = event.pressed(), // TODO: Remove this once we add target device profiles GamepadButton::Screenshot => { - let pressed = event.pressed(); let guide = NativeEvent::new( Capability::Gamepad(Gamepad::Button(GamepadButton::Guide)), event.get_value(), ); - let bumpr = NativeEvent::new( + let bumper = NativeEvent::new( Capability::Gamepad(Gamepad::Button(GamepadButton::RightBumper)), event.get_value(), ); - let (guide, bumpr) = if pressed { + let (guide, bumper) = { let guide = ScheduledNativeEvent::new(guide, Duration::from_millis(0)); - let bumpr = - ScheduledNativeEvent::new(bumpr, Duration::from_millis(160)); - (guide, bumpr) - } else { - let guide = - ScheduledNativeEvent::new(guide, Duration::from_millis(240)); - let bumpr = - ScheduledNativeEvent::new(bumpr, Duration::from_millis(160)); - (guide, bumpr) + let bumper = + ScheduledNativeEvent::new(bumper, Duration::from_millis(1)); + (guide, bumper) }; self.queued_events.push(guide); - self.queued_events.push(bumpr); + self.queued_events.push(bumper); } _ => (), }, diff --git a/src/input/target/steam_deck_uhid.rs b/src/input/target/steam_deck_uhid.rs index 0f084423..ec9d1876 100644 --- a/src/input/target/steam_deck_uhid.rs +++ b/src/input/target/steam_deck_uhid.rs @@ -152,31 +152,24 @@ impl SteamDeckUhidDevice { GamepadButton::RightStickTouch => self.state.r_stick_touch = event.pressed(), // TODO: Remove this once we add target device profiles GamepadButton::Screenshot => { - let pressed = event.pressed(); let guide = NativeEvent::new( Capability::Gamepad(Gamepad::Button(GamepadButton::Guide)), event.get_value(), ); - let bumpr = NativeEvent::new( + let bumper = NativeEvent::new( Capability::Gamepad(Gamepad::Button(GamepadButton::RightBumper)), event.get_value(), ); - let (guide, bumpr) = if pressed { + let (guide, bumper) = { let guide = ScheduledNativeEvent::new(guide, Duration::from_millis(0)); - let bumpr = - ScheduledNativeEvent::new(bumpr, Duration::from_millis(160)); - (guide, bumpr) - } else { - let guide = - ScheduledNativeEvent::new(guide, Duration::from_millis(240)); - let bumpr = - ScheduledNativeEvent::new(bumpr, Duration::from_millis(160)); - (guide, bumpr) + let bumper = + ScheduledNativeEvent::new(bumper, Duration::from_millis(8)); + (guide, bumper) }; self.queued_events.push(guide); - self.queued_events.push(bumpr); + self.queued_events.push(bumper); } _ => (), }, diff --git a/src/input/target/ulitmate_2.rs b/src/input/target/ulitmate_2.rs index 86a150d1..7f30ecd4 100644 --- a/src/input/target/ulitmate_2.rs +++ b/src/input/target/ulitmate_2.rs @@ -303,8 +303,6 @@ impl TargetInputDevice for Ultimate2WirelessDevice { return Ok(()); } - // Screenshot maps to Guide+RightTrigger combo (press: Guide@0ms RightTrigger@160ms, - // release: RightTrigger@160ms Guide@240ms), same timing as horipad target. if event.as_capability() == Capability::Gamepad(Gamepad::Button(GamepadButton::Screenshot)) { let pressed = event.pressed(); @@ -312,24 +310,19 @@ impl TargetInputDevice for Ultimate2WirelessDevice { Capability::Gamepad(Gamepad::Button(GamepadButton::Guide)), event.get_value(), ); - let trigv = if pressed { 0.5 } else { 0.0 }; - let trigr = NativeEvent::new( + let trigger_value = if pressed { 0.5 } else { 0.0 }; + let trigger = NativeEvent::new( Capability::Gamepad(Gamepad::Trigger(GamepadTrigger::RightTrigger)), - InputValue::Float(trigv), + InputValue::Float(trigger_value), ); - let (guide, trigr) = if pressed { + let (guide, trigger) = { ( ScheduledNativeEvent::new(guide, Duration::from_millis(0)), - ScheduledNativeEvent::new(trigr, Duration::from_millis(160)), - ) - } else { - ( - ScheduledNativeEvent::new(guide, Duration::from_millis(240)), - ScheduledNativeEvent::new(trigr, Duration::from_millis(160)), + ScheduledNativeEvent::new(trigger, Duration::from_millis(1)), ) }; self.queued_events.push(guide); - self.queued_events.push(trigr); + self.queued_events.push(trigger); return Ok(()); }