diff --git a/rootfs/usr/share/inputplumber/devices/50-legion_go_s.yaml b/rootfs/usr/share/inputplumber/devices/50-legion_go_s.yaml index 23b652de..be6cd848 100644 --- a/rootfs/usr/share/inputplumber/devices/50-legion_go_s.yaml +++ b/rootfs/usr/share/inputplumber/devices/50-legion_go_s.yaml @@ -79,15 +79,13 @@ source_devices: - group: gamepad evdev: vendor_id: "1a86" - product_id: "e310" - name: "{QH Electronics Controller,Legion Go S}" - handler: event* - - group: gamepad - evdev: - vendor_id: "1a86" - product_id: "e311" + product_id: "{e31[01]}" name: "{QH Electronics Controller,Legion Go S}" handler: event* + events: + # Block input events, but use device for output events + exclude: + - "*" # IMU - group: imu @@ -116,4 +114,6 @@ options: # The target input device(s) to emulate by default target_devices: - - deck-uhid + - xbox-elite + - keyboard + - mouse diff --git a/src/input/event/native.rs b/src/input/event/native.rs index 589f6bf2..07aae4f4 100644 --- a/src/input/event/native.rs +++ b/src/input/event/native.rs @@ -198,4 +198,9 @@ impl ScheduledNativeEvent { pub fn is_ready(&self) -> bool { self.scheduled_time.elapsed() > self.wait_time } + + /// Returns the capability that the scheduled event implements + pub fn as_capability(&self) -> Capability { + self.event.capability.clone() + } } diff --git a/src/input/target/dualsense.rs b/src/input/target/dualsense.rs index b9b24493..70e2e831 100644 --- a/src/input/target/dualsense.rs +++ b/src/input/target/dualsense.rs @@ -1009,7 +1009,13 @@ impl TargetInputDevice for DualSenseDevice { if self.queued_events.is_empty() { return None; } - Some(self.queued_events.drain(..).collect()) + let (ready, pending): (Vec<_>, Vec<_>) = + self.queued_events.drain(..).partition(|e| e.is_ready()); + self.queued_events = pending; + if ready.is_empty() { + return None; + } + Some(ready) } fn stop(&mut self) -> Result<(), InputError> { diff --git a/src/input/target/horipad_steam.rs b/src/input/target/horipad_steam.rs index 009d284e..c204e277 100644 --- a/src/input/target/horipad_steam.rs +++ b/src/input/target/horipad_steam.rs @@ -313,9 +313,7 @@ impl HoripadSteamDevice { report_number: u8, _report_type: uhid_virt::ReportType, ) -> Result<(), Box> { - log::debug!( - "Received GetReport request: id: {id}, report_number: {report_number}" - ); + log::debug!("Received GetReport request: id: {id}, report_number: {report_number}"); if let Err(e) = self.device.write_get_report_reply(id, 1, vec![]) { log::warn!("Failed to write get report reply: {:?}", e); return Err(e.to_string().into()); @@ -372,7 +370,13 @@ impl TargetInputDevice for HoripadSteamDevice { if self.queued_events.is_empty() { return None; } - Some(self.queued_events.drain(..).collect()) + let (ready, pending): (Vec<_>, Vec<_>) = + self.queued_events.drain(..).partition(|e| e.is_ready()); + self.queued_events = pending; + if ready.is_empty() { + return None; + } + Some(ready) } fn stop(&mut self) -> Result<(), InputError> { diff --git a/src/input/target/mod.rs b/src/input/target/mod.rs index 0530bc90..42cee2b0 100644 --- a/src/input/target/mod.rs +++ b/src/input/target/mod.rs @@ -537,6 +537,7 @@ impl TargetDriver i += 1; } for event in ready_events.drain(..) { + log::trace!("Emmiting scheduled event: {event:?}"); let mut implementation = self.implementation.lock().unwrap(); if let Err(e) = implementation.write_event(event.into()) { log::error!("Error writing event: {e:?}"); diff --git a/src/input/target/steam_deck.rs b/src/input/target/steam_deck.rs index 071be6a9..78b57632 100644 --- a/src/input/target/steam_deck.rs +++ b/src/input/target/steam_deck.rs @@ -70,7 +70,7 @@ impl Default for SteamDeckConfig { // The minimum amount of time that button up events must wait after // a button down event. -const MIN_FRAME_TIME: Duration = Duration::from_millis(80); +const MIN_FRAME_TIME: Duration = Duration::from_millis(8); pub struct SteamDeckDevice { chip_id: [u8; 15], @@ -927,6 +927,14 @@ impl TargetInputDevice for SteamDeckDevice { log::trace!("Button down: {cap:?}"); // Keep track of button down events self.pressed_events.insert(cap.clone(), Instant::now()); + // Clear any stale up events for this capability + self.queued_events.retain(|scheduled| { + let found = scheduled.as_capability() == cap; + if found { + log::trace!("Found stale queued release for {cap:?}, Clearing."); + } + !found + }); } else { log::trace!("Button up: {cap:?}"); // If the event is a button up event, check to @@ -1004,11 +1012,18 @@ impl TargetInputDevice for SteamDeckDevice { ]) } + /// Returns any events in the queue up to the [TargetDriver] fn scheduled_events(&mut self) -> Option> { if self.queued_events.is_empty() { return None; } - Some(self.queued_events.drain(..).collect()) + let (ready, pending): (Vec<_>, Vec<_>) = + self.queued_events.drain(..).partition(|e| e.is_ready()); + self.queued_events = pending; + if ready.is_empty() { + return None; + } + Some(ready) } /// Stop the virtual USB read/write threads diff --git a/src/input/target/steam_deck_uhid.rs b/src/input/target/steam_deck_uhid.rs index 3bf3f2a6..3996f592 100644 --- a/src/input/target/steam_deck_uhid.rs +++ b/src/input/target/steam_deck_uhid.rs @@ -47,7 +47,7 @@ use super::{ // The minimum amount of time that button up events must wait after // a button down event. -const MIN_FRAME_TIME: Duration = Duration::from_millis(80); +const MIN_FRAME_TIME: Duration = Duration::from_millis(8); pub struct SteamDeckUhidDevice { chip_id: [u8; 15], @@ -735,6 +735,14 @@ impl TargetInputDevice for SteamDeckUhidDevice { log::trace!("Button down: {cap:?}"); // Keep track of button down events self.pressed_events.insert(cap.clone(), Instant::now()); + // Clear any stale up events for this capability + self.queued_events.retain(|scheduled| { + let found = scheduled.as_capability() == cap; + if found { + log::trace!("Found stale queued release for {cap:?}, Clearing."); + } + !found + }); } else { log::trace!("Button up: {cap:?}"); // If the event is a button up event, check to @@ -812,11 +820,18 @@ impl TargetInputDevice for SteamDeckUhidDevice { ]) } + /// Returns any events in the queue up to the [TargetDriver] fn scheduled_events(&mut self) -> Option> { if self.queued_events.is_empty() { return None; } - Some(self.queued_events.drain(..).collect()) + let (ready, pending): (Vec<_>, Vec<_>) = + self.queued_events.drain(..).partition(|e| e.is_ready()); + self.queued_events = pending; + if ready.is_empty() { + return None; + } + Some(ready) } fn stop(&mut self) -> Result<(), InputError> { diff --git a/src/input/target/ultimate2_wireless.rs b/src/input/target/ultimate2_wireless.rs index c9b6f083..a6a19796 100644 --- a/src/input/target/ultimate2_wireless.rs +++ b/src/input/target/ultimate2_wireless.rs @@ -379,11 +379,18 @@ impl TargetInputDevice for Ultimate2WirelessDevice { ]) } + /// Returns any events in the queue up to the [TargetDriver] fn scheduled_events(&mut self) -> Option> { if self.queued_events.is_empty() { return None; } - Some(self.queued_events.drain(..).collect()) + let (ready, pending): (Vec<_>, Vec<_>) = + self.queued_events.drain(..).partition(|e| e.is_ready()); + self.queued_events = pending; + if ready.is_empty() { + return None; + } + Some(ready) } fn stop(&mut self) -> Result<(), InputError> { diff --git a/src/input/target/xpad.rs b/src/input/target/xpad.rs index be45c8f7..daa2f1bb 100644 --- a/src/input/target/xpad.rs +++ b/src/input/target/xpad.rs @@ -286,7 +286,13 @@ impl TargetInputDevice for XBoxController { if self.queued_events.is_empty() { return None; } - Some(self.queued_events.drain(..).collect()) + let (ready, pending): (Vec<_>, Vec<_>) = + self.queued_events.drain(..).partition(|e| e.is_ready()); + self.queued_events = pending; + if ready.is_empty() { + return None; + } + Some(ready) } /// Clear any local state on the target device.