From 2df1cbaeeb97b453884614e8403a609741fb0ae5 Mon Sep 17 00:00:00 2001 From: William Edwards Date: Wed, 28 May 2025 10:48:38 -0700 Subject: [PATCH] feat(Source Device): add async implementation --- src/dbus/interface/source/iio_imu.rs | 2 +- src/drivers/iio_imu/driver.rs | 25 +- src/input/composite_device/mod.rs | 214 ++++------ src/input/manager.rs | 5 +- src/input/source/evdev.rs | 23 +- src/input/source/evdev/blocked.rs | 20 +- src/input/source/evdev/gamepad.rs | 22 +- src/input/source/evdev/keyboard.rs | 15 +- src/input/source/evdev/touchscreen.rs | 13 +- src/input/source/hidraw.rs | 103 ++--- src/input/source/hidraw/blocked.rs | 16 +- src/input/source/hidraw/dualsense.rs | 40 +- .../source/hidraw/flydigi_vader_4_pro.rs | 22 +- src/input/source/hidraw/fts3528.rs | 22 +- src/input/source/hidraw/horipad_steam.rs | 22 +- .../source/hidraw/lego_dinput_combined.rs | 22 +- src/input/source/hidraw/lego_dinput_split.rs | 22 +- src/input/source/hidraw/lego_fps_mode.rs | 22 +- src/input/source/hidraw/lego_xinput.rs | 22 +- src/input/source/hidraw/legos_config.rs | 15 +- src/input/source/hidraw/legos_imu.rs | 34 +- src/input/source/hidraw/legos_touchpad.rs | 34 +- src/input/source/hidraw/legos_xinput.rs | 54 ++- src/input/source/hidraw/msi_claw.rs | 29 +- src/input/source/hidraw/opineo.rs | 21 +- src/input/source/hidraw/rog_ally.rs | 16 +- src/input/source/hidraw/steam_deck.rs | 43 +- src/input/source/hidraw/xpad_uhid.rs | 37 +- src/input/source/hidraw/zotac_zone.rs | 16 +- src/input/source/iio.rs | 14 +- src/input/source/iio/accel_gyro_3d.rs | 27 +- src/input/source/iio/bmi_imu.rs | 27 +- src/input/source/led.rs | 12 +- src/input/source/led/multicolor.rs | 15 +- src/input/source/mod.rs | 393 +++++++++--------- 35 files changed, 766 insertions(+), 673 deletions(-) diff --git a/src/dbus/interface/source/iio_imu.rs b/src/dbus/interface/source/iio_imu.rs index 1a17dafa..c5c62bd5 100644 --- a/src/dbus/interface/source/iio_imu.rs +++ b/src/dbus/interface/source/iio_imu.rs @@ -21,7 +21,7 @@ impl SourceIioImuInterface { pub async fn listen_on_dbus( conn: Connection, device: UdevDevice, - ) -> Result<(), Box> { + ) -> Result<(), Box> { let iface = SourceIioImuInterface::new(device); let Ok(id) = iface.id() else { return Ok(()); diff --git a/src/drivers/iio_imu/driver.rs b/src/drivers/iio_imu/driver.rs index 665589ad..dd7b10ee 100644 --- a/src/drivers/iio_imu/driver.rs +++ b/src/drivers/iio_imu/driver.rs @@ -1,4 +1,9 @@ -use std::{collections::HashMap, error::Error, time::Duration}; +use std::{ + collections::HashMap, + error::Error, + sync::{Arc, Mutex}, + time::Duration, +}; use industrial_io::{Channel, ChannelType, Device, Direction}; @@ -12,9 +17,9 @@ use super::{ /// Driver for reading IIO IMU data pub struct Driver { mount_matrix: MountMatrix, - accel: HashMap, + accel: HashMap>>, accel_info: HashMap, - gyro: HashMap, + gyro: HashMap>>, gyro_info: HashMap, pub sample_delay: Duration, } @@ -116,7 +121,7 @@ impl Driver { let Some(info) = self.accel_info.get(id) else { continue; }; - let data = channel.attr_read_int("raw")?; + let data = channel.lock().unwrap().attr_read_int("raw")?; // processed_value = (raw + offset) * scale let value = (data + info.offset) as f64 * info.scale; @@ -144,7 +149,7 @@ impl Driver { let Some(info) = self.gyro_info.get(id) else { continue; }; - let data = channel.attr_read_int("raw")?; + let data = channel.lock().unwrap().attr_read_int("raw")?; // processed_value = (raw + offset) * scale let value = (data + info.offset) as f64 * info.scale; @@ -194,7 +199,10 @@ impl Driver { fn get_channels_with_type( device: &Device, channel_type: ChannelType, -) -> (HashMap, HashMap) { +) -> ( + HashMap>>, + HashMap, +) { let mut channels = HashMap::new(); let mut channel_info = HashMap::new(); device @@ -276,8 +284,11 @@ fn get_channels_with_type( scales_avail, }; channel_info.insert(id.clone(), info); - channels.insert(id, channel); + channels.insert(id, Arc::new(Mutex::new(channel))); }); (channels, channel_info) } + +// A mutex is used to access channels +unsafe impl Send for Driver {} diff --git a/src/input/composite_device/mod.rs b/src/input/composite_device/mod.rs index c8368a3b..84ab0d68 100644 --- a/src/input/composite_device/mod.rs +++ b/src/input/composite_device/mod.rs @@ -22,7 +22,7 @@ use zbus::Connection; use crate::{ config::{ - capability_map::CapabilityMapConfig, path::get_profiles_path, CompositeDeviceConfig, + self, capability_map::CapabilityMapConfig, path::get_profiles_path, CompositeDeviceConfig, DeviceProfile, ProfileMapping, }, dbus::interface::{ @@ -41,7 +41,7 @@ use crate::{ }, target::TargetDeviceTypeId, }, - udev::{device::UdevDevice, hide_device, unhide_device}, + udev::device::UdevDevice, }; use self::{client::CompositeDeviceClient, command::CompositeCommand}; @@ -116,11 +116,6 @@ pub struct CompositeDevice { /// Map of source device id to their respective transmitter channel. /// E.g. {"evdev://event0": } source_devices: HashMap, - /// Source devices that this composite device will consume. - source_devices_discovered: Vec, - /// Source devices that should be hidden before they are started. This - /// is a list of devnode paths to hide (e.g. ["/dev/input/event10", "/dev/hidraw1"]) - source_devices_to_hide: Vec, /// HashSet of source devices that are blocked from passing their input events to target /// events. source_devices_blocked: HashSet, @@ -157,7 +152,7 @@ pub struct CompositeDevice { } impl CompositeDevice { - pub fn new( + pub async fn new( conn: Connection, manager: mpsc::Sender, config: CompositeDeviceConfig, @@ -186,8 +181,6 @@ impl CompositeDevice { tx: tx.clone(), rx, source_devices: HashMap::new(), - source_devices_discovered: Vec::new(), - source_devices_to_hide: Vec::new(), source_devices_blocked: HashSet::new(), source_device_paths: Vec::new(), source_device_tasks: JoinSet::new(), @@ -241,7 +234,7 @@ impl CompositeDevice { } } - if let Err(e) = device.add_source_device(device_info) { + if let Err(e) = device.add_source_device(device_info).await { return Err(e.to_string().into()); } @@ -278,9 +271,6 @@ impl CompositeDevice { let dbus_path = self.dbus_path.clone(); - // Start all source devices - self.run_source_devices().await?; - // Set persist value from config if set, used to determine // if CompositeDevice self-closes after all SourceDevices have // been removed. @@ -301,7 +291,7 @@ impl CompositeDevice { break; } let mut devices_removed = false; - //log::trace!("Received {num} command(s)"); + log::debug!("Received {num} command(s)"); for cmd in buffer.drain(..) { log::trace!("Received command: {:?}", cmd); match cmd { @@ -538,18 +528,6 @@ impl CompositeDevice { log::debug!("Stopping target devices"); self.targets.stop().await; - // Unhide all source devices - for source_path in self.source_device_paths.clone() { - if source_path.starts_with("/sys/bus/iio/devices") { - log::debug!("Skipping unhiding IIO device: {source_path}"); - continue; - } - log::debug!("Un-hiding device: {}", source_path); - if let Err(e) = unhide_device(source_path.clone()).await { - log::debug!("Unable to unhide device {source_path}: {:?}", e); - } - } - // Send stop command to all source devices for (path, source) in &self.source_devices { log::debug!("Stopping source device: {path}"); @@ -591,54 +569,6 @@ impl CompositeDevice { self.source_device_paths.clone() } - /// Start and run the source devices that this composite device will - /// consume. - async fn run_source_devices(&mut self) -> Result<(), Box> { - // Hide the device if specified - for source_path in self.source_devices_to_hide.drain(..) { - log::debug!("Hiding device: {}", source_path); - if let Err(e) = hide_device(source_path.as_str()).await { - log::warn!("Failed to hide device '{source_path}': {e:?}"); - } - log::debug!("Finished hiding device: {source_path}"); - } - - log::debug!("Starting new source devices"); - // Start listening for events from all source devices - let sources = self.source_devices_discovered.drain(..); - for source_device in sources { - let device_id = source_device.get_id(); - // If the source device is blocked, don't bother running it - if self.source_devices_blocked.contains(&device_id) { - log::debug!("Source device '{device_id}' blocked. Skipping running."); - continue; - } - - let source_tx = source_device.client(); - self.source_devices.insert(device_id.clone(), source_tx); - let tx = self.tx.clone(); - - // Add the IIO IMU Dbus interface. We do this here because it needs the source - // device transmitter and this is the only place we can refrence it at the moment. - let device = source_device.get_device_ref().clone(); - if let SourceDevice::Iio(_) = source_device { - SourceIioImuInterface::listen_on_dbus(self.conn.clone(), device.clone()).await?; - } - - self.source_device_tasks.spawn(async move { - if let Err(e) = source_device.run().await { - log::error!("Failed running device: {:?}", e); - } - log::debug!("Source device closed"); - if let Err(e) = tx.send(CompositeCommand::SourceDeviceStopped(device)).await { - log::error!("Failed to send device stop command: {:?}", e); - } - }); - } - log::debug!("All source device tasks started"); - Ok(()) - } - /// Process a single event from a source device. Events are piped through /// a translation layer, then dispatched to the appropriate target device(s) async fn process_event( @@ -1472,10 +1402,9 @@ impl CompositeDevice { /// Executed whenever a source device is added to this [CompositeDevice]. async fn on_source_device_added(&mut self, device: UdevDevice) -> Result<(), Box> { - if let Err(e) = self.add_source_device(device) { + if let Err(e) = self.add_source_device(device).await { return Err(e.to_string().into()); } - self.run_source_devices().await?; // Signal to DBus that source devices have changed self.signal_sources_changed().await; @@ -1538,72 +1467,55 @@ impl CompositeDevice { Ok(()) } - /// Creates and adds a source device using the given [SourceDeviceInfo] - fn add_source_device( + /// Creates, adds, and starts a source device using the given [SourceDeviceInfo] + async fn add_source_device( &mut self, device: UdevDevice, ) -> Result<(), Box> { // Check to see if this source device should be blocked. - let mut is_blocked = false; - let mut is_blocked_evdev = false; let source_config = self.config.get_matching_device(&device); - if let Some(source_config) = source_config.as_ref() { - if let Some(blocked) = source_config.blocked { - is_blocked = blocked; - } - } - - let subsystem = device.subsystem(); - - // Hide the device if specified - let should_passthru = source_config + let is_blocked = source_config .as_ref() - .and_then(|c| c.passthrough) + .and_then(|conf| conf.blocked) .unwrap_or(false); - let should_hide = !should_passthru && subsystem.as_str() != "iio"; - if should_hide { - let source_path = device.devnode(); - self.source_devices_to_hide.push(source_path); + + // Create the source device + let client = self.client(); + let source_device = Self::create_source_device(client, device.clone(), source_config)?; + let id = source_device.get_id(); + let capabilities = source_device.get_capabilities()?; + let path = source_device.get_device_path(); + let source_tx = source_device.client(); + self.source_devices.insert(id.clone(), source_tx); + + // Add the IIO IMU Dbus interface. We do this here because it needs the source + // device transmitter and this is the only place we can refrence it at the moment. + if let SourceDevice::Iio(_) = source_device { + SourceIioImuInterface::listen_on_dbus(self.conn.clone(), device.clone()).await?; } - let source_device = match subsystem.as_str() { - "input" => { - log::debug!("Adding EVDEV source device: {:?}", device.name()); - if is_blocked { - is_blocked_evdev = true; - } - let device = EventDevice::new(device, self.client(), source_config.clone())?; - SourceDevice::Event(device) - } - "hidraw" => { - log::debug!("Adding HIDRAW source device: {:?}", device.name()); - let device = HidRawDevice::new(device, self.client(), source_config.clone())?; - SourceDevice::HidRaw(device) - } - "iio" => { - log::debug!("Adding IIO source device: {:?}", device.name()); - let device = IioDevice::new(device, self.client(), source_config.clone())?; - SourceDevice::Iio(device) - } - "leds" => { - log::debug!("Adding LED source device: {:?}", device.sysname()); - let device = LedDevice::new(device, self.client(), source_config.clone())?; - SourceDevice::Led(device) + // Spawn the task and run the source device + let composite_device_tx = self.tx.clone(); + let device_info = device.clone(); + let device_path = path.clone(); + tokio::task::spawn(async move { + // Run the source device + if let Err(e) = source_device.run().await { + log::error!("Failed running device: {e:?}"); } - _ => { - return Err(format!( - "Unspported subsystem: {subsystem}, unable to add source device {}", - device.name() - ) - .into()) + log::debug!("Source device closed: {device_path}"); + let result = composite_device_tx + .send(CompositeCommand::SourceDeviceStopped(device_info)) + .await; + if let Err(e) = result { + log::debug!("Failed to send source device stopped signal to device: {e}"); } - }; + }); // Get the capabilities of the source device. // TODO: When we *remove* a source device, we also need to remove // capabilities if !is_blocked { - let capabilities = source_device.get_capabilities()?; for cap in capabilities { if self.translatable_capabilities.contains(&cap) { continue; @@ -1613,15 +1525,11 @@ impl CompositeDevice { } // Check if this device should be blocked from sending events to target devices. - let id = source_device.get_id(); - if let Some(device_config) = self - .config - .get_matching_device(source_device.get_device_ref()) - { + if let Some(device_config) = self.config.get_matching_device(&device) { if let Some(blocked) = device_config.blocked { // Blocked event devices should still be run so they can be // EVIOGRAB'd - if blocked && !is_blocked_evdev { + if blocked { self.source_devices_blocked.insert(id.clone()); } } @@ -1630,14 +1538,48 @@ impl CompositeDevice { // TODO: Based on the capability map in the config, translate // the capabilities. // Keep track of the source device - let device_path = source_device.get_device_path(); - self.source_devices_discovered.push(source_device); - self.source_device_paths.push(device_path); + self.source_device_paths.push(path); self.source_devices_used.push(id); Ok(()) } + /// Create the source device from the given udev device and configuration + fn create_source_device( + client: CompositeDeviceClient, + device: UdevDevice, + config: Option, + ) -> Result> { + let subsystem = device.subsystem(); + match subsystem.as_str() { + "input" => { + log::debug!("Adding EVDEV source device: {:?}", device.name()); + let device = EventDevice::new(device, client, config)?; + Ok(SourceDevice::Event(device)) + } + "hidraw" => { + log::debug!("Adding HIDRAW source device: {:?}", device.name()); + let device = HidRawDevice::new(device, client, config)?; + Ok(SourceDevice::HidRaw(device)) + } + "iio" => { + log::debug!("Adding IIO source device: {:?}", device.name()); + let device = IioDevice::new(device, client, config)?; + Ok(SourceDevice::Iio(device)) + } + "leds" => { + log::debug!("Adding LED source device: {:?}", device.sysname()); + let device = LedDevice::new(device, client, config)?; + Ok(SourceDevice::Led(device)) + } + _ => Err(format!( + "Unspported subsystem: {subsystem}, unable to add source device {}", + device.name() + ) + .into()), + } + } + /// Load the given device profile pub fn load_device_profile( &mut self, diff --git a/src/input/manager.rs b/src/input/manager.rs index af8922f1..dce205e5 100644 --- a/src/input/manager.rs +++ b/src/input/manager.rs @@ -551,7 +551,8 @@ impl Manager { device, self.next_composite_dbus_path()?, capability_map, - )?; + ) + .await?; // Check to see if there's already a CompositeDevice for // these source devices. @@ -809,7 +810,7 @@ impl Manager { let composite_path = String::from(device.dbus_path()); let composite_path_clone = composite_path.clone(); let tx = self.tx.clone(); - let task = tokio::spawn(async move { + let task = tokio::task::spawn(async move { if let Err(e) = device.run().await { log::error!("Error running {composite_path}: {}", e.to_string()); } diff --git a/src/input/source/evdev.rs b/src/input/source/evdev.rs index dca8f840..ee350f90 100644 --- a/src/input/source/evdev.rs +++ b/src/input/source/evdev.rs @@ -3,7 +3,7 @@ pub mod gamepad; pub mod keyboard; pub mod touchscreen; -use std::{collections::HashMap, error::Error, time::Duration}; +use std::{collections::HashMap, error::Error}; use evdev::{Device, EventType}; use keyboard::KeyboardEventDevice; @@ -15,13 +15,13 @@ use crate::{ capability_map::{load_capability_mappings, CapabilityMapConfig}, }, constants::BUS_SOURCES_PREFIX, - input::composite_device::client::CompositeDeviceClient, + input::{capability::Capability, composite_device::client::CompositeDeviceClient}, udev::device::UdevDevice, }; use self::{blocked::BlockedEventDevice, gamepad::GamepadEventDevice}; -use super::{SourceDeviceCompatible, SourceDriver, SourceDriverOptions}; +use super::{InputError, SourceDeviceCompatible, SourceDriver, SourceDriverOptions}; /// List of available drivers enum DriverType { @@ -70,16 +70,14 @@ impl SourceDeviceCompatible for EventDevice { async fn run(self) -> Result<(), Box> { match self { - EventDevice::Blocked(source_driver) => source_driver.run().await, - EventDevice::Gamepad(source_driver) => source_driver.run().await, - EventDevice::Touchscreen(source_driver) => source_driver.run().await, - EventDevice::Keyboard(source_driver) => source_driver.run().await, + EventDevice::Blocked(mut source_driver) => source_driver.run().await, + EventDevice::Gamepad(mut source_driver) => source_driver.run().await, + EventDevice::Touchscreen(mut source_driver) => source_driver.run().await, + EventDevice::Keyboard(mut source_driver) => source_driver.run().await, } } - fn get_capabilities( - &self, - ) -> Result, super::InputError> { + fn get_capabilities(&self) -> Result, InputError> { match self { EventDevice::Blocked(source_driver) => source_driver.get_capabilities(), EventDevice::Gamepad(source_driver) => source_driver.get_capabilities(), @@ -119,10 +117,7 @@ impl EventDevice { match driver_type { DriverType::Blocked => { - let options = SourceDriverOptions { - poll_rate: Duration::from_millis(200), - buffer_size: 4096, - }; + let options = SourceDriverOptions { buffer_size: 4096 }; let device = BlockedEventDevice::new(device_info.clone())?; let source_device = SourceDriver::new_with_options( composite_device, diff --git a/src/input/source/evdev/blocked.rs b/src/input/source/evdev/blocked.rs index f89c9664..a79520ed 100644 --- a/src/input/source/evdev/blocked.rs +++ b/src/input/source/evdev/blocked.rs @@ -3,17 +3,13 @@ use std::{error::Error, fmt::Debug}; use evdev::Device; use crate::{ - input::{ - capability::Capability, - event::native::NativeEvent, - source::{InputError, SourceInputDevice, SourceOutputDevice}, - }, + input::source::{SourceInputDevice, SourceOutputDevice}, udev::device::UdevDevice, }; /// Source device implementation to block evdev events pub struct BlockedEventDevice { - device: Device, + _device: Device, } impl BlockedEventDevice { @@ -25,19 +21,11 @@ impl BlockedEventDevice { device.grab()?; log::info!("Blocking input events from {path}"); - Ok(Self { device }) + Ok(Self { _device: device }) } } -impl SourceInputDevice for BlockedEventDevice { - fn poll(&mut self) -> Result, InputError> { - Ok(vec![]) - } - - fn get_capabilities(&self) -> Result, InputError> { - Ok(vec![]) - } -} +impl SourceInputDevice for BlockedEventDevice {} impl SourceOutputDevice for BlockedEventDevice {} diff --git a/src/input/source/evdev/gamepad.rs b/src/input/source/evdev/gamepad.rs index 23ee2f4e..48ad409e 100644 --- a/src/input/source/evdev/gamepad.rs +++ b/src/input/source/evdev/gamepad.rs @@ -1,4 +1,5 @@ use std::fmt::Debug; +use std::time::Duration; use std::{collections::HashMap, error::Error, os::fd::AsRawFd}; use evdev::{ @@ -8,6 +9,7 @@ use evdev::{ use nix::fcntl::{FcntlArg, OFlag}; use packed_struct::types::SizedInteger; use packed_struct::PrimitiveEnum; +use tokio::time::{interval, Interval}; use crate::config::capability_map::CapabilityMapConfigV2; use crate::drivers::steam_deck::hid_report::{ @@ -34,6 +36,7 @@ pub struct GamepadEventDevice { ff_effects_dualsense: Option, ff_effects_deck: Option, hat_state: HashMap, + interval: Interval, } impl GamepadEventDevice { @@ -64,6 +67,9 @@ impl GamepadEventDevice { // Create an event translator if a capability map was given let translator = capability_map.map(|map| EventTranslator::new(&map, axes_info.clone())); + // Set polling interval + let interval = interval(Duration::from_millis(10)); + Ok(Self { device, axes_info, @@ -72,6 +78,7 @@ impl GamepadEventDevice { ff_effects_dualsense: None, ff_effects_deck: None, hat_state: HashMap::new(), + interval, }) } @@ -353,7 +360,8 @@ impl GamepadEventDevice { impl SourceInputDevice for GamepadEventDevice { /// Poll the given input device for input events - fn poll(&mut self) -> Result, InputError> { + async fn poll(&mut self) -> Result, InputError> { + self.interval.tick().await; let mut native_events = vec![]; // Poll the translator for any scheduled events @@ -498,7 +506,7 @@ impl SourceOutputDevice for GamepadEventDevice { /// Write the given output event to the source device. Output events are /// events that flow from an application (like a game) to the physical /// input device, such as force feedback events. - fn write_event(&mut self, event: OutputEvent) -> Result<(), OutputError> { + async fn write_event(&mut self, event: OutputEvent) -> Result<(), OutputError> { log::trace!("Received output event: {:?}", event); // Only process output events if FF is supported @@ -550,7 +558,7 @@ impl SourceOutputDevice for GamepadEventDevice { /// Upload the given force feedback effect data to the source device. Returns /// a device-specific id of the uploaded effect if it is successful. - fn upload_effect(&mut self, effect: FFEffectData) -> Result { + async fn upload_effect(&mut self, effect: FFEffectData) -> Result { log::trace!("Uploading FF effect data"); if self.device.supported_ff().is_none() { log::debug!("Device does not support FF effects"); @@ -567,7 +575,11 @@ impl SourceOutputDevice for GamepadEventDevice { } /// Update the effect with the given id using the given effect data. - fn update_effect(&mut self, effect_id: i16, effect: FFEffectData) -> Result<(), OutputError> { + async fn update_effect( + &mut self, + effect_id: i16, + effect: FFEffectData, + ) -> Result<(), OutputError> { log::trace!("Update FF effect {effect_id}"); if self.device.supported_ff().is_none() { log::debug!("Device does not support FF effects"); @@ -586,7 +598,7 @@ impl SourceOutputDevice for GamepadEventDevice { } /// Erase the effect with the given id from the source device. - fn erase_effect(&mut self, effect_id: i16) -> Result<(), OutputError> { + async fn erase_effect(&mut self, effect_id: i16) -> Result<(), OutputError> { log::trace!("Erasing FF effect data"); if self.device.supported_ff().is_none() { log::debug!("Device does not support FF effects"); diff --git a/src/input/source/evdev/keyboard.rs b/src/input/source/evdev/keyboard.rs index a36149aa..13e987f2 100644 --- a/src/input/source/evdev/keyboard.rs +++ b/src/input/source/evdev/keyboard.rs @@ -1,9 +1,11 @@ use std::fmt::Debug; use std::os::fd::AsRawFd; +use std::time::Duration; use std::{collections::HashMap, error::Error}; use evdev::{Device, EventType, InputEvent}; use nix::fcntl::{FcntlArg, OFlag}; +use tokio::time::{interval, Interval}; use crate::config::capability_map::CapabilityMapConfigV2; use crate::{ @@ -23,6 +25,7 @@ use crate::{ pub struct KeyboardEventDevice { device: Device, translator: Option, + interval: Interval, } impl KeyboardEventDevice { @@ -51,7 +54,14 @@ impl KeyboardEventDevice { // Create an event translator if a capability map was given let translator = capability_map.map(|map| EventTranslator::new(&map, HashMap::new())); - Ok(Self { device, translator }) + // Set polling interval + let interval = interval(Duration::from_millis(10)); + + Ok(Self { + device, + translator, + interval, + }) } /// Translate the given evdev event into a native event @@ -73,7 +83,8 @@ impl KeyboardEventDevice { impl SourceInputDevice for KeyboardEventDevice { /// Poll the given input device for input events - fn poll(&mut self) -> Result, InputError> { + async fn poll(&mut self) -> Result, InputError> { + self.interval.tick().await; let mut native_events = vec![]; // Poll the translator for any scheduled events diff --git a/src/input/source/evdev/touchscreen.rs b/src/input/source/evdev/touchscreen.rs index 2fad1e6d..eb84f717 100644 --- a/src/input/source/evdev/touchscreen.rs +++ b/src/input/source/evdev/touchscreen.rs @@ -1,5 +1,6 @@ use std::collections::HashSet; use std::fmt::Debug; +use std::time::Duration; use std::{collections::HashMap, error::Error, os::fd::AsRawFd}; use evdev::{ @@ -7,6 +8,7 @@ use evdev::{ SynchronizationCode, }; use nix::fcntl::{FcntlArg, OFlag}; +use tokio::time::{interval, Interval}; use crate::config::capability_map::CapabilityMapConfigV2; use crate::config::TouchscreenConfig; @@ -107,6 +109,7 @@ pub struct TouchscreenEventDevice { touch_state: [TouchState; 10], // NOTE: Max of 10 touch inputs dirty_states: HashSet, last_touch_idx: usize, + interval: Interval, } impl TouchscreenEventDevice { @@ -184,6 +187,9 @@ impl TouchscreenEventDevice { // Create an event translator if a capability map was given let translator = capability_map.map(|map| EventTranslator::new(&map, axes_info.clone())); + // Set polling interval + let interval = interval(Duration::from_millis(10)); + Ok(Self { device, orientation, @@ -192,6 +198,7 @@ impl TouchscreenEventDevice { touch_state: Default::default(), dirty_states: HashSet::with_capacity(10), last_touch_idx: 0, + interval, }) } @@ -304,7 +311,8 @@ impl TouchscreenEventDevice { impl SourceInputDevice for TouchscreenEventDevice { /// Poll the given input device for input events - fn poll(&mut self) -> Result, InputError> { + async fn poll(&mut self) -> Result, InputError> { + self.interval.tick().await; let mut native_events = vec![]; // Poll the translator for any scheduled events @@ -331,6 +339,7 @@ impl SourceInputDevice for TouchscreenEventDevice { let events: Vec = events.into_iter().collect(); events }; + log::trace!("Read events from device: {events:?}"); // Convert the events into native events if no translator exists if self.translator.is_none() { @@ -371,6 +380,8 @@ impl SourceInputDevice for TouchscreenEventDevice { .collect(); native_events.extend(translated_events); + log::trace!("Sending events: {native_events:?}"); + Ok(native_events) } diff --git a/src/input/source/hidraw.rs b/src/input/source/hidraw.rs index de64713c..9383a0d2 100644 --- a/src/input/source/hidraw.rs +++ b/src/input/source/hidraw.rs @@ -18,7 +18,7 @@ pub mod steam_deck; pub mod xpad_uhid; pub mod zotac_zone; -use std::{error::Error, time::Duration}; +use std::error::Error; use blocked::BlockedHidrawDevice; use flydigi_vader_4_pro::Vader4Pro; @@ -32,8 +32,11 @@ use xpad_uhid::XpadUhid; use zotac_zone::ZotacZone; use crate::{ - config, constants::BUS_SOURCES_PREFIX, drivers, - input::composite_device::client::CompositeDeviceClient, udev::device::UdevDevice, + config, + constants::BUS_SOURCES_PREFIX, + drivers, + input::{capability::Capability, composite_device::client::CompositeDeviceClient}, + udev::device::UdevDevice, }; use self::{ @@ -43,7 +46,7 @@ use self::{ legos_xinput::LegionSXInputController, opineo::OrangePiNeoTouchpad, steam_deck::DeckController, }; -use super::{SourceDeviceCompatible, SourceDriver, SourceDriverOptions}; +use super::{InputError, SourceDeviceCompatible, SourceDriver, SourceDriverOptions}; /// List of available drivers enum DriverType { @@ -168,31 +171,29 @@ impl SourceDeviceCompatible for HidRawDevice { async fn run(self) -> Result<(), Box> { match self { - HidRawDevice::Blocked(source_driver) => source_driver.run().await, - HidRawDevice::DualSense(source_driver) => source_driver.run().await, - HidRawDevice::Fts3528Touchscreen(source_driver) => source_driver.run().await, - HidRawDevice::HoripadSteam(source_driver) => source_driver.run().await, - HidRawDevice::LegionGoDCombined(source_driver) => source_driver.run().await, - HidRawDevice::LegionGoDSplit(source_driver) => source_driver.run().await, - HidRawDevice::LegionGoFPS(source_driver) => source_driver.run().await, - HidRawDevice::LegionGoSConfig(source_driver) => source_driver.run().await, - HidRawDevice::LegionGoSImu(source_driver) => source_driver.run().await, - HidRawDevice::LegionGoSTouchpad(source_driver) => source_driver.run().await, - HidRawDevice::LegionGoSXInput(source_driver) => source_driver.run().await, - HidRawDevice::LegionGoXInput(source_driver) => source_driver.run().await, - HidRawDevice::MsiClaw(source_driver) => source_driver.run().await, - HidRawDevice::OrangePiNeo(source_driver) => source_driver.run().await, - HidRawDevice::RogAlly(source_driver) => source_driver.run().await, - HidRawDevice::SteamDeck(source_driver) => source_driver.run().await, - HidRawDevice::Vader4Pro(source_driver) => source_driver.run().await, - HidRawDevice::XpadUhid(source_driver) => source_driver.run().await, - HidRawDevice::ZotacZone(source_driver) => source_driver.run().await, + HidRawDevice::Blocked(mut source_driver) => source_driver.run().await, + HidRawDevice::DualSense(mut source_driver) => source_driver.run().await, + HidRawDevice::Fts3528Touchscreen(mut source_driver) => source_driver.run().await, + HidRawDevice::HoripadSteam(mut source_driver) => source_driver.run().await, + HidRawDevice::LegionGoDCombined(mut source_driver) => source_driver.run().await, + HidRawDevice::LegionGoDSplit(mut source_driver) => source_driver.run().await, + HidRawDevice::LegionGoFPS(mut source_driver) => source_driver.run().await, + HidRawDevice::LegionGoSConfig(mut source_driver) => source_driver.run().await, + HidRawDevice::LegionGoSImu(mut source_driver) => source_driver.run().await, + HidRawDevice::LegionGoSTouchpad(mut source_driver) => source_driver.run().await, + HidRawDevice::LegionGoSXInput(mut source_driver) => source_driver.run().await, + HidRawDevice::LegionGoXInput(mut source_driver) => source_driver.run().await, + HidRawDevice::MsiClaw(mut source_driver) => source_driver.run().await, + HidRawDevice::OrangePiNeo(mut source_driver) => source_driver.run().await, + HidRawDevice::RogAlly(mut source_driver) => source_driver.run().await, + HidRawDevice::SteamDeck(mut source_driver) => source_driver.run().await, + HidRawDevice::Vader4Pro(mut source_driver) => source_driver.run().await, + HidRawDevice::XpadUhid(mut source_driver) => source_driver.run().await, + HidRawDevice::ZotacZone(mut source_driver) => source_driver.run().await, } } - fn get_capabilities( - &self, - ) -> Result, super::InputError> { + fn get_capabilities(&self) -> Result, InputError> { match self { HidRawDevice::Blocked(source_driver) => source_driver.get_capabilities(), HidRawDevice::DualSense(source_driver) => source_driver.get_capabilities(), @@ -256,10 +257,7 @@ impl HidRawDevice { match driver_type { DriverType::Unknown => Err("No driver for hidraw interface found".into()), DriverType::Blocked => { - let options = SourceDriverOptions { - poll_rate: Duration::from_millis(200), - buffer_size: 4096, - }; + let options = SourceDriverOptions { buffer_size: 4096 }; let device = BlockedHidrawDevice::new(device_info.clone())?; let source_device = SourceDriver::new_with_options( composite_device, @@ -271,10 +269,7 @@ impl HidRawDevice { Ok(Self::Blocked(source_device)) } DriverType::DualSense => { - let options = SourceDriverOptions { - poll_rate: Duration::from_millis(1), - buffer_size: 2048, - }; + let options = SourceDriverOptions { buffer_size: 2048 }; let device = DualSenseController::new(device_info.clone())?; let source_device = SourceDriver::new_with_options( composite_device, @@ -286,10 +281,7 @@ impl HidRawDevice { Ok(Self::DualSense(source_device)) } DriverType::SteamDeck => { - let options = SourceDriverOptions { - poll_rate: Duration::from_millis(1), - buffer_size: 2048, - }; + let options = SourceDriverOptions { buffer_size: 2048 }; let device = DeckController::new(device_info.clone())?; let source_device = SourceDriver::new_with_options( composite_device, @@ -321,10 +313,7 @@ impl HidRawDevice { Ok(Self::LegionGoXInput(source_device)) } DriverType::LegionGoSConfig => { - let options = SourceDriverOptions { - poll_rate: Duration::from_secs(1), - buffer_size: 2048, - }; + let options = SourceDriverOptions { buffer_size: 2048 }; let device = LegionSConfigController::new(device_info.clone())?; let source_device = SourceDriver::new_with_options( composite_device, @@ -336,10 +325,7 @@ impl HidRawDevice { Ok(Self::LegionGoSConfig(source_device)) } DriverType::LegionGoSImu => { - let options = SourceDriverOptions { - poll_rate: Duration::from_millis(4), - buffer_size: 2048, - }; + let options = SourceDriverOptions { buffer_size: 2048 }; let device = LegionSImuController::new(device_info.clone())?; let source_device = SourceDriver::new_with_options( composite_device, @@ -351,10 +337,7 @@ impl HidRawDevice { Ok(Self::LegionGoSImu(source_device)) } DriverType::LegionGoSTouchpad => { - let options = SourceDriverOptions { - poll_rate: Duration::from_millis(8), - buffer_size: 2048, - }; + let options = SourceDriverOptions { buffer_size: 2048 }; let device = LegionSTouchpadController::new(device_info.clone())?; let source_device = SourceDriver::new_with_options( composite_device, @@ -366,10 +349,7 @@ impl HidRawDevice { Ok(Self::LegionGoSTouchpad(source_device)) } DriverType::LegionGoSXInput => { - let options = SourceDriverOptions { - poll_rate: Duration::from_millis(4), - buffer_size: 2048, - }; + let options = SourceDriverOptions { buffer_size: 2048 }; let device = LegionSXInputController::new(device_info.clone())?; let source_device = SourceDriver::new_with_options( composite_device, @@ -402,10 +382,7 @@ impl HidRawDevice { } DriverType::RogAlly => { let device = RogAlly::new(device_info.clone())?; - let options = SourceDriverOptions { - poll_rate: Duration::from_millis(500), - buffer_size: 1024, - }; + let options = SourceDriverOptions { buffer_size: 1024 }; let source_device = SourceDriver::new_with_options( composite_device, device, @@ -422,10 +399,7 @@ impl HidRawDevice { } DriverType::Vader4Pro => { let device = Vader4Pro::new(device_info.clone())?; - let options = SourceDriverOptions { - poll_rate: Duration::from_millis(0), - buffer_size: 1024, - }; + let options = SourceDriverOptions { buffer_size: 1024 }; let source_device = SourceDriver::new_with_options( composite_device, device, @@ -437,10 +411,7 @@ impl HidRawDevice { } DriverType::ZotacZone => { let device = ZotacZone::new(device_info.clone())?; - let options = SourceDriverOptions { - poll_rate: Duration::from_millis(300), - buffer_size: 1024, - }; + let options = SourceDriverOptions { buffer_size: 1024 }; let source_device = SourceDriver::new_with_options( composite_device, device, diff --git a/src/input/source/hidraw/blocked.rs b/src/input/source/hidraw/blocked.rs index ee0b19ad..fa8d4c76 100644 --- a/src/input/source/hidraw/blocked.rs +++ b/src/input/source/hidraw/blocked.rs @@ -1,11 +1,7 @@ use std::{error::Error, fmt::Debug}; use crate::{ - input::{ - capability::Capability, - event::native::NativeEvent, - source::{InputError, SourceInputDevice, SourceOutputDevice}, - }, + input::source::{SourceInputDevice, SourceOutputDevice}, udev::device::UdevDevice, }; @@ -23,14 +19,6 @@ impl BlockedHidrawDevice { } } -impl SourceInputDevice for BlockedHidrawDevice { - fn poll(&mut self) -> Result, InputError> { - Ok(vec![]) - } - - fn get_capabilities(&self) -> Result, InputError> { - Ok(vec![]) - } -} +impl SourceInputDevice for BlockedHidrawDevice {} impl SourceOutputDevice for BlockedHidrawDevice {} diff --git a/src/input/source/hidraw/dualsense.rs b/src/input/source/hidraw/dualsense.rs index 6a26a5c2..e14596ee 100644 --- a/src/input/source/hidraw/dualsense.rs +++ b/src/input/source/hidraw/dualsense.rs @@ -1,8 +1,11 @@ use std::fmt::Debug; +use std::sync::{Arc, Mutex}; +use std::time::Duration; use std::{collections::HashMap, error::Error}; use evdev::{FFEffectData, FFEffectKind}; use packed_struct::types::SizedInteger; +use tokio::time::{interval, Interval}; use crate::drivers::dualsense::driver::{DS5_EDGE_PID, DS5_PID, DS5_VID}; use crate::drivers::steam_deck::hid_report::PackedRumbleReport; @@ -27,18 +30,21 @@ pub const PIDS: [u16; 2] = [DS5_EDGE_PID, DS5_PID]; /// Sony Playstation DualSense Controller source device implementation pub struct DualSenseController { - driver: Driver, + driver: Arc>, ff_evdev_effects: HashMap, + interval: Interval, } impl DualSenseController { /// Create a new DualSense controller source device with the given udev /// device information pub fn new(device_info: UdevDevice) -> Result> { - let driver = Driver::new(device_info.devnode())?; + let driver = Arc::new(Mutex::new(Driver::new(device_info.devnode())?)); + let interval = interval(Duration::from_millis(1)); Ok(Self { driver, ff_evdev_effects: HashMap::new(), + interval, }) } @@ -78,7 +84,7 @@ impl DualSenseController { // The value determines if the effect should be playing or not. if value == 0 { log::trace!("Stopping rumble"); - if let Err(e) = self.driver.rumble(0, 0) { + if let Err(e) = self.driver.lock().unwrap().rumble(0, 0) { log::debug!("Failed to stop rumble: {:?}", e); return Ok(()); } @@ -118,7 +124,12 @@ impl DualSenseController { let right_speed = weak_magnitude / u8::MAX as u16 + 1; // Do rumble - if let Err(e) = self.driver.rumble(left_speed as u8, right_speed as u8) { + if let Err(e) = self + .driver + .lock() + .unwrap() + .rumble(left_speed as u8, right_speed as u8) + { let err = format!("Failed to do rumble: {:?}", e); return Err(err.into()); } @@ -133,6 +144,8 @@ impl DualSenseController { let left_speed = report.left_speed.to_primitive() / u8::MAX as u16 + 1; let right_speed = report.right_speed.to_primitive() / u8::MAX as u16 + 1; self.driver + .lock() + .unwrap() .rumble(left_speed as u8, right_speed as u8) .map_err(|e| e.to_string())?; Ok(()) @@ -141,8 +154,9 @@ impl DualSenseController { impl SourceInputDevice for DualSenseController { /// Poll the given input device for input events - fn poll(&mut self) -> Result, InputError> { - let events = self.driver.poll()?; + async fn poll(&mut self) -> Result, InputError> { + self.interval.tick().await; + let events = self.driver.lock().unwrap().poll()?; let native_events = translate_events(events); Ok(native_events) @@ -158,13 +172,13 @@ impl SourceOutputDevice for DualSenseController { /// Write the given output event to the source device. Output events are /// events that flow from an application (like a game) to the physical /// input device, such as force feedback events. - fn write_event(&mut self, event: OutputEvent) -> Result<(), OutputError> { + async fn write_event(&mut self, event: OutputEvent) -> Result<(), OutputError> { log::trace!("Received output event: {:?}", event); match event { OutputEvent::Evdev(input_event) => Ok(self.process_evdev_ff(input_event)?), OutputEvent::DualSense(report) => { log::debug!("Received DualSense output report"); - Ok(self.driver.write(report)?) + Ok(self.driver.lock().unwrap().write(report)?) } OutputEvent::Uinput(_) => Ok(()), OutputEvent::SteamDeckHaptics(_report) => Ok(()), @@ -180,7 +194,7 @@ impl SourceOutputDevice for DualSenseController { /// Upload the given force feedback effect data to the source device. Returns /// a device-specific id of the uploaded effect if it is successful. - fn upload_effect(&mut self, effect: FFEffectData) -> Result { + async fn upload_effect(&mut self, effect: FFEffectData) -> Result { log::debug!("Uploading FF effect data"); let id = self.next_ff_effect_id(); if id == -1 { @@ -192,14 +206,18 @@ impl SourceOutputDevice for DualSenseController { } /// Update the effect with the given id using the given effect data. - fn update_effect(&mut self, effect_id: i16, effect: FFEffectData) -> Result<(), OutputError> { + async fn update_effect( + &mut self, + effect_id: i16, + effect: FFEffectData, + ) -> Result<(), OutputError> { log::debug!("Updating FF effect data with id {effect_id}"); self.ff_evdev_effects.insert(effect_id, effect); Ok(()) } /// Erase the effect with the given id from the source device. - fn erase_effect(&mut self, effect_id: i16) -> Result<(), OutputError> { + async fn erase_effect(&mut self, effect_id: i16) -> Result<(), OutputError> { log::debug!("Erasing FF effect data"); self.ff_evdev_effects.remove(&effect_id); Ok(()) diff --git a/src/input/source/hidraw/flydigi_vader_4_pro.rs b/src/input/source/hidraw/flydigi_vader_4_pro.rs index 335f4297..2acdee70 100644 --- a/src/input/source/hidraw/flydigi_vader_4_pro.rs +++ b/src/input/source/hidraw/flydigi_vader_4_pro.rs @@ -1,4 +1,11 @@ -use std::{error::Error, fmt::Debug}; +use std::{ + error::Error, + fmt::Debug, + sync::{Arc, Mutex}, + time::Duration, +}; + +use tokio::time::{interval, Interval}; use crate::{ drivers::flydigi_vader_4_pro::{ @@ -15,15 +22,17 @@ use crate::{ /// Vader4Pro source device implementation pub struct Vader4Pro { - driver: Driver, + driver: Arc>, + interval: Interval, } impl Vader4Pro { /// Create a new source device with the given udev /// device information pub fn new(device_info: UdevDevice) -> Result> { - let driver = Driver::new(device_info)?; - Ok(Self { driver }) + let driver = Arc::new(Mutex::new(Driver::new(device_info)?)); + let interval = interval(Duration::from_millis(1)); + Ok(Self { driver, interval }) } } @@ -31,8 +40,9 @@ impl SourceOutputDevice for Vader4Pro {} impl SourceInputDevice for Vader4Pro { /// Poll the given input device for input events - fn poll(&mut self) -> Result, InputError> { - let events = match self.driver.poll() { + async fn poll(&mut self) -> Result, InputError> { + self.interval.tick().await; + let events = match self.driver.lock().unwrap().poll() { Ok(events) => events, Err(err) => { log::error!("Got error polling!: {err:?}"); diff --git a/src/input/source/hidraw/fts3528.rs b/src/input/source/hidraw/fts3528.rs index 2140bbfa..37c08206 100644 --- a/src/input/source/hidraw/fts3528.rs +++ b/src/input/source/hidraw/fts3528.rs @@ -1,4 +1,11 @@ -use std::{error::Error, fmt::Debug}; +use std::{ + error::Error, + fmt::Debug, + sync::{Arc, Mutex}, + time::Duration, +}; + +use tokio::time::{interval, Interval}; use crate::{ drivers::fts3528::{ @@ -17,22 +24,25 @@ use crate::{ /// FTS3528 Touchscreen source device implementation pub struct Fts3528Touchscreen { - driver: Driver, + driver: Arc>, + interval: Interval, } impl Fts3528Touchscreen { /// Create a new FTS3528 touchscreen source device with the given udev /// device information pub fn new(device_info: UdevDevice) -> Result> { - let driver = Driver::new(device_info.devnode())?; - Ok(Self { driver }) + let driver = Arc::new(Mutex::new(Driver::new(device_info.devnode())?)); + let interval = interval(Duration::from_micros(2500)); + Ok(Self { driver, interval }) } } impl SourceInputDevice for Fts3528Touchscreen { /// Poll the given input device for input events - fn poll(&mut self) -> Result, InputError> { - let events = self.driver.poll()?; + async fn poll(&mut self) -> Result, InputError> { + self.interval.tick().await; + let events = self.driver.lock().unwrap().poll()?; let native_events = translate_events(events); Ok(native_events) } diff --git a/src/input/source/hidraw/horipad_steam.rs b/src/input/source/hidraw/horipad_steam.rs index 9965ff3d..1f9cd6a4 100644 --- a/src/input/source/hidraw/horipad_steam.rs +++ b/src/input/source/hidraw/horipad_steam.rs @@ -1,4 +1,11 @@ -use std::{error::Error, fmt::Debug}; +use std::{ + error::Error, + fmt::Debug, + sync::{Arc, Mutex}, + time::Duration, +}; + +use tokio::time::{interval, Interval}; use crate::{ drivers::horipad_steam::{ @@ -15,15 +22,17 @@ use crate::{ /// HoripadSteam source device implementation pub struct HoripadSteam { - driver: Driver, + driver: Arc>, + interval: Interval, } impl HoripadSteam { /// Create a new source device with the given udev /// device information pub fn new(device_info: UdevDevice) -> Result> { - let driver = Driver::new(device_info)?; - Ok(Self { driver }) + let driver = Arc::new(Mutex::new(Driver::new(device_info)?)); + let interval = interval(Duration::from_micros(2500)); + Ok(Self { driver, interval }) } } @@ -31,8 +40,9 @@ impl SourceOutputDevice for HoripadSteam {} impl SourceInputDevice for HoripadSteam { /// Poll the given input device for input events - fn poll(&mut self) -> Result, InputError> { - let events = self.driver.poll()?; + async fn poll(&mut self) -> Result, InputError> { + self.interval.tick().await; + let events = self.driver.lock().unwrap().poll()?; let native_events = translate_events(events); Ok(native_events) } diff --git a/src/input/source/hidraw/lego_dinput_combined.rs b/src/input/source/hidraw/lego_dinput_combined.rs index 8a1ee774..8fb139b0 100644 --- a/src/input/source/hidraw/lego_dinput_combined.rs +++ b/src/input/source/hidraw/lego_dinput_combined.rs @@ -1,4 +1,11 @@ -use std::{error::Error, fmt::Debug}; +use std::{ + error::Error, + fmt::Debug, + sync::{Arc, Mutex}, + time::Duration, +}; + +use tokio::time::{interval, Interval}; use crate::{ drivers::lego::{ @@ -18,22 +25,25 @@ use crate::{ /// Legion Go Controller source device implementation pub struct LegionControllerDCombined { - driver: Driver, + driver: Arc>, + interval: Interval, } impl LegionControllerDCombined { /// Create a new Legion controller source device with the given udev /// device information pub fn new(device_info: UdevDevice) -> Result> { - let driver = Driver::new(device_info.devnode())?; - Ok(Self { driver }) + let driver = Arc::new(Mutex::new(Driver::new(device_info.devnode())?)); + let interval = interval(Duration::from_micros(2500)); + Ok(Self { driver, interval }) } } impl SourceInputDevice for LegionControllerDCombined { /// Poll the source device for input events - fn poll(&mut self) -> Result, InputError> { - let events = self.driver.poll()?; + async fn poll(&mut self) -> Result, InputError> { + self.interval.tick().await; + let events = self.driver.lock().unwrap().poll()?; let native_events = translate_events(events); Ok(native_events) } diff --git a/src/input/source/hidraw/lego_dinput_split.rs b/src/input/source/hidraw/lego_dinput_split.rs index 383a8045..f786655a 100644 --- a/src/input/source/hidraw/lego_dinput_split.rs +++ b/src/input/source/hidraw/lego_dinput_split.rs @@ -1,4 +1,11 @@ -use std::{error::Error, fmt::Debug}; +use std::{ + error::Error, + fmt::Debug, + sync::{Arc, Mutex}, + time::Duration, +}; + +use tokio::time::{interval, Interval}; use crate::{ drivers::lego::{ @@ -18,22 +25,25 @@ use crate::{ /// Legion Go Controller source device implementation pub struct LegionControllerDSplit { - driver: Driver, + driver: Arc>, + interval: Interval, } impl LegionControllerDSplit { /// Create a new Legion controller source device with the given udev /// device information pub fn new(device_info: UdevDevice) -> Result> { - let driver = Driver::new(device_info.devnode())?; - Ok(Self { driver }) + let driver = Arc::new(Mutex::new(Driver::new(device_info.devnode())?)); + let interval = interval(Duration::from_micros(2500)); + Ok(Self { driver, interval }) } } impl SourceInputDevice for LegionControllerDSplit { /// Poll the source device for input events - fn poll(&mut self) -> Result, InputError> { - let events = self.driver.poll()?; + async fn poll(&mut self) -> Result, InputError> { + self.interval.tick().await; + let events = self.driver.lock().unwrap().poll()?; let native_events = translate_events(events); Ok(native_events) } diff --git a/src/input/source/hidraw/lego_fps_mode.rs b/src/input/source/hidraw/lego_fps_mode.rs index 47ed4cfa..fdce4196 100644 --- a/src/input/source/hidraw/lego_fps_mode.rs +++ b/src/input/source/hidraw/lego_fps_mode.rs @@ -1,4 +1,11 @@ -use std::{error::Error, fmt::Debug}; +use std::{ + error::Error, + fmt::Debug, + sync::{Arc, Mutex}, + time::Duration, +}; + +use tokio::time::{interval, Interval}; use crate::{ drivers::lego::{ @@ -18,22 +25,25 @@ use crate::{ /// Legion Go Controller source device implementation pub struct LegionControllerFPS { - driver: Driver, + driver: Arc>, + interval: Interval, } impl LegionControllerFPS { /// Create a new Legion controller source device with the given udev /// device information pub fn new(device_info: UdevDevice) -> Result> { - let driver = Driver::new(device_info.devnode())?; - Ok(Self { driver }) + let driver = Arc::new(Mutex::new(Driver::new(device_info.devnode())?)); + let interval = interval(Duration::from_micros(2500)); + Ok(Self { driver, interval }) } } impl SourceInputDevice for LegionControllerFPS { /// Poll the source device for input events - fn poll(&mut self) -> Result, InputError> { - let events = self.driver.poll()?; + async fn poll(&mut self) -> Result, InputError> { + self.interval.tick().await; + let events = self.driver.lock().unwrap().poll()?; let native_events = translate_events(events); Ok(native_events) } diff --git a/src/input/source/hidraw/lego_xinput.rs b/src/input/source/hidraw/lego_xinput.rs index 4426317e..0386acbe 100644 --- a/src/input/source/hidraw/lego_xinput.rs +++ b/src/input/source/hidraw/lego_xinput.rs @@ -1,4 +1,11 @@ -use std::{error::Error, fmt::Debug}; +use std::{ + error::Error, + fmt::Debug, + sync::{Arc, Mutex}, + time::Duration, +}; + +use tokio::time::{interval, Interval}; use crate::{ drivers::lego::{ @@ -18,22 +25,25 @@ use crate::{ /// Legion Go Controller source device implementation pub struct LegionControllerX { - driver: Driver, + driver: Arc>, + interval: Interval, } impl LegionControllerX { /// Create a new Legion controller source device with the given udev /// device information pub fn new(device_info: UdevDevice) -> Result> { - let driver = Driver::new(device_info.devnode())?; - Ok(Self { driver }) + let driver = Arc::new(Mutex::new(Driver::new(device_info.devnode())?)); + let interval = interval(Duration::from_micros(2500)); + Ok(Self { driver, interval }) } } impl SourceInputDevice for LegionControllerX { /// Poll the source device for input events - fn poll(&mut self) -> Result, InputError> { - let events = self.driver.poll()?; + async fn poll(&mut self) -> Result, InputError> { + self.interval.tick().await; + let events = self.driver.lock().unwrap().poll()?; let native_events = translate_events(events); Ok(native_events) } diff --git a/src/input/source/hidraw/legos_config.rs b/src/input/source/hidraw/legos_config.rs index 3829b64d..8f8fe610 100644 --- a/src/input/source/hidraw/legos_config.rs +++ b/src/input/source/hidraw/legos_config.rs @@ -2,11 +2,7 @@ use std::{error::Error, fmt::Debug}; use crate::{ drivers::legos::config_driver::ConfigDriver, - input::{ - capability::Capability, - event::native::NativeEvent, - source::{InputError, SourceInputDevice, SourceOutputDevice}, - }, + input::source::{SourceInputDevice, SourceOutputDevice}, udev::device::UdevDevice, }; @@ -29,14 +25,7 @@ impl Debug for LegionSConfigController { f.debug_struct("LegionSConfig").finish() } } -impl SourceInputDevice for LegionSConfigController { - fn poll(&mut self) -> Result, InputError> { - Ok(vec![]) - } - fn get_capabilities(&self) -> Result, InputError> { - Ok(vec![]) - } -} +impl SourceInputDevice for LegionSConfigController {} impl SourceOutputDevice for LegionSConfigController {} diff --git a/src/input/source/hidraw/legos_imu.rs b/src/input/source/hidraw/legos_imu.rs index 22925859..01d7139c 100644 --- a/src/input/source/hidraw/legos_imu.rs +++ b/src/input/source/hidraw/legos_imu.rs @@ -1,34 +1,43 @@ -use std::{error::Error, fmt::Debug}; +use std::{ + error::Error, + fmt::Debug, + sync::{Arc, Mutex}, + time::Duration, +}; + +use tokio::time::{interval, Interval}; use crate::{ drivers::legos::{event, imu_driver::IMUDriver}, input::{ capability::{Capability, Gamepad}, event::{native::NativeEvent, value::InputValue}, - output_event::OutputEvent, - source::{InputError, OutputError, SourceInputDevice, SourceOutputDevice}, + source::{InputError, SourceInputDevice, SourceOutputDevice}, }, udev::device::UdevDevice, }; /// Legion Go Controller source device implementation pub struct LegionSImuController { - driver: IMUDriver, + driver: Arc>, + interval: Interval, } impl LegionSImuController { /// Create a new Legion controller source device with the given udev /// device information pub fn new(device_info: UdevDevice) -> Result> { - let driver = IMUDriver::new(device_info.devnode())?; - Ok(Self { driver }) + let driver = Arc::new(Mutex::new(IMUDriver::new(device_info.devnode())?)); + let interval = interval(Duration::from_micros(2500)); + Ok(Self { driver, interval }) } } impl SourceInputDevice for LegionSImuController { /// Poll the source device for input events - fn poll(&mut self) -> Result, InputError> { - let events = self.driver.poll()?; + async fn poll(&mut self) -> Result, InputError> { + self.interval.tick().await; + let events = self.driver.lock().unwrap().poll()?; let native_events = translate_events(events); Ok(native_events) } @@ -39,14 +48,7 @@ impl SourceInputDevice for LegionSImuController { } } -impl SourceOutputDevice for LegionSImuController { - /// Write the given output event to the source device. Output events are - /// events that flow from an application (like a game) to the physical - /// input device, such as force feedback events. - fn write_event(&mut self, _event: OutputEvent) -> Result<(), OutputError> { - Ok(()) - } -} +impl SourceOutputDevice for LegionSImuController {} impl Debug for LegionSImuController { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { diff --git a/src/input/source/hidraw/legos_touchpad.rs b/src/input/source/hidraw/legos_touchpad.rs index cde7fe6d..a033bdc9 100644 --- a/src/input/source/hidraw/legos_touchpad.rs +++ b/src/input/source/hidraw/legos_touchpad.rs @@ -1,34 +1,43 @@ -use std::{error::Error, fmt::Debug}; +use std::{ + error::Error, + fmt::Debug, + sync::{Arc, Mutex}, + time::Duration, +}; + +use tokio::time::{interval, Interval}; use crate::{ drivers::legos::{event, touchpad_driver::TouchpadDriver, PAD_FORCE_MAX, PAD_MOTION_MAX}, input::{ capability::{Capability, Gamepad, GamepadTrigger, Touch, TouchButton, Touchpad}, event::{native::NativeEvent, value::InputValue}, - output_event::OutputEvent, - source::{InputError, OutputError, SourceInputDevice, SourceOutputDevice}, + source::{InputError, SourceInputDevice, SourceOutputDevice}, }, udev::device::UdevDevice, }; /// Legion Go Controller source device implementation pub struct LegionSTouchpadController { - driver: TouchpadDriver, + driver: Arc>, + interval: Interval, } impl LegionSTouchpadController { /// Create a new Legion controller source device with the given udev /// device information pub fn new(device_info: UdevDevice) -> Result> { - let driver = TouchpadDriver::new(device_info.devnode())?; - Ok(Self { driver }) + let driver = Arc::new(Mutex::new(TouchpadDriver::new(device_info.devnode())?)); + let interval = interval(Duration::from_micros(2500)); + Ok(Self { driver, interval }) } } impl SourceInputDevice for LegionSTouchpadController { /// Poll the source device for input events - fn poll(&mut self) -> Result, InputError> { - let events = self.driver.poll()?; + async fn poll(&mut self) -> Result, InputError> { + self.interval.tick().await; + let events = self.driver.lock().unwrap().poll()?; let native_events = translate_events(events); Ok(native_events) } @@ -39,14 +48,7 @@ impl SourceInputDevice for LegionSTouchpadController { } } -impl SourceOutputDevice for LegionSTouchpadController { - /// Write the given output event to the source device. Output events are - /// events that flow from an application (like a game) to the physical - /// input device, such as force feedback events. - fn write_event(&mut self, _event: OutputEvent) -> Result<(), OutputError> { - Ok(()) - } -} +impl SourceOutputDevice for LegionSTouchpadController {} impl Debug for LegionSTouchpadController { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { diff --git a/src/input/source/hidraw/legos_xinput.rs b/src/input/source/hidraw/legos_xinput.rs index ea635d8f..6fc249d7 100644 --- a/src/input/source/hidraw/legos_xinput.rs +++ b/src/input/source/hidraw/legos_xinput.rs @@ -1,7 +1,14 @@ -use std::{collections::HashMap, error::Error, fmt::Debug}; +use std::{ + collections::HashMap, + error::Error, + fmt::Debug, + sync::{Arc, Mutex}, + time::Duration, +}; use evdev::{FFEffectData, FFEffectKind, InputEvent}; use packed_struct::{types::SizedInteger, PrimitiveEnum}; +use tokio::time::{interval, Interval}; use crate::{ drivers::{ @@ -23,18 +30,21 @@ use crate::{ /// Legion Go Controller source device implementation pub struct LegionSXInputController { - driver: XInputDriver, + driver: Arc>, ff_evdev_effects: HashMap, + interval: Interval, } impl LegionSXInputController { /// Create a new Legion controller source device with the given udev /// device information pub fn new(device_info: UdevDevice) -> Result> { - let driver = XInputDriver::new(device_info.devnode())?; + let driver = Arc::new(Mutex::new(XInputDriver::new(device_info.devnode())?)); + let interval = interval(Duration::from_micros(2500)); Ok(Self { driver, ff_evdev_effects: HashMap::new(), + interval, }) } @@ -58,7 +68,7 @@ impl LegionSXInputController { // The value determines if the effect should be playing or not. if value == 0 { - if let Err(e) = self.driver.haptic_rumble(0, 0) { + if let Err(e) = self.driver.lock().unwrap().haptic_rumble(0, 0) { log::debug!("Failed to stop haptic rumble: {:?}", e); return Ok(()); } @@ -97,7 +107,12 @@ impl LegionSXInputController { let right_speed = (weak_magnitude / 256) as u8; // Do rumble - if let Err(e) = self.driver.haptic_rumble(left_speed, right_speed) { + if let Err(e) = self + .driver + .lock() + .unwrap() + .haptic_rumble(left_speed, right_speed) + { let err = format!("Failed to do haptic rumble: {:?}", e); return Err(err.into()); } @@ -116,7 +131,12 @@ impl LegionSXInputController { let left_speed = report.rumble_emulation_left; let right_speed = report.rumble_emulation_right; - if let Err(e) = self.driver.haptic_rumble(left_speed, right_speed) { + if let Err(e) = self + .driver + .lock() + .unwrap() + .haptic_rumble(left_speed, right_speed) + { let err = format!("Failed to do haptic rumble: {:?}", e); return Err(err.into()); } @@ -135,9 +155,13 @@ impl LegionSXInputController { let new_gain = new_gain as u8; match report.side { - PadSide::Left => self.driver.haptic_rumble(new_gain, 0)?, - PadSide::Right => self.driver.haptic_rumble(0, new_gain)?, - PadSide::Both => self.driver.haptic_rumble(new_gain, new_gain)?, + PadSide::Left => self.driver.lock().unwrap().haptic_rumble(new_gain, 0)?, + PadSide::Right => self.driver.lock().unwrap().haptic_rumble(0, new_gain)?, + PadSide::Both => self + .driver + .lock() + .unwrap() + .haptic_rumble(new_gain, new_gain)?, } Ok(()) @@ -146,8 +170,9 @@ impl LegionSXInputController { impl SourceInputDevice for LegionSXInputController { /// Poll the source device for input events - fn poll(&mut self) -> Result, InputError> { - let events = self.driver.poll()?; + async fn poll(&mut self) -> Result, InputError> { + self.interval.tick().await; + let events = self.driver.lock().unwrap().poll()?; let native_events = translate_events(events); Ok(native_events) } @@ -162,7 +187,7 @@ impl SourceOutputDevice for LegionSXInputController { /// Write the given output event to the source device. Output events are /// events that flow from an application (like a game) to the physical /// input device, such as force feedback events. - fn write_event(&mut self, event: OutputEvent) -> Result<(), OutputError> { + async fn write_event(&mut self, event: OutputEvent) -> Result<(), OutputError> { log::trace!("Received output event: {:?}", event); match event { OutputEvent::Evdev(input_event) => { @@ -179,7 +204,10 @@ impl SourceOutputDevice for LegionSXInputController { OutputEvent::SteamDeckRumble(report) => { let l_speed = (report.left_speed.to_primitive() / 256) as u8; let r_speed = (report.right_speed.to_primitive() / 256) as u8; - self.driver.haptic_rumble(l_speed, r_speed)?; + self.driver + .lock() + .unwrap() + .haptic_rumble(l_speed, r_speed)?; } } diff --git a/src/input/source/hidraw/msi_claw.rs b/src/input/source/hidraw/msi_claw.rs index 42ea7de6..8fbd24fd 100644 --- a/src/input/source/hidraw/msi_claw.rs +++ b/src/input/source/hidraw/msi_claw.rs @@ -1,4 +1,11 @@ -use std::{error::Error, fmt::Debug}; +use std::{ + error::Error, + fmt::Debug, + sync::{Arc, Mutex}, + time::Duration, +}; + +use tokio::time::{interval, Interval}; use crate::{ drivers::msi_claw::{ @@ -6,7 +13,6 @@ use crate::{ hid_report::{GamepadMode, MkeysFunction}, }, input::{ - capability::Capability, event::native::NativeEvent, source::{InputError, SourceInputDevice, SourceOutputDevice}, }, @@ -14,7 +20,8 @@ use crate::{ }; pub struct MsiClaw { - driver: Driver, + driver: Arc>, + interval: Interval, } impl MsiClaw { @@ -27,7 +34,11 @@ impl MsiClaw { if let Err(e) = driver.get_mode() { log::error!("Failed to send get gamepad mode request: {e}"); } - Ok(Self { driver }) + let interval = interval(Duration::from_millis(40)); + Ok(Self { + driver: Arc::new(Mutex::new(driver)), + interval, + }) } } @@ -36,17 +47,15 @@ impl Debug for MsiClaw { f.debug_struct("MsiClaw").finish() } } + impl SourceInputDevice for MsiClaw { - fn poll(&mut self) -> Result, InputError> { - if let Err(e) = self.driver.poll() { + async fn poll(&mut self) -> Result, InputError> { + self.interval.tick().await; + if let Err(e) = self.driver.lock().unwrap().poll() { log::error!("Error polling: {e}"); } Ok(vec![]) } - - fn get_capabilities(&self) -> Result, InputError> { - Ok(vec![]) - } } impl SourceOutputDevice for MsiClaw {} diff --git a/src/input/source/hidraw/opineo.rs b/src/input/source/hidraw/opineo.rs index 4c7194fb..d923acb8 100644 --- a/src/input/source/hidraw/opineo.rs +++ b/src/input/source/hidraw/opineo.rs @@ -1,4 +1,11 @@ -use std::{error::Error, fmt::Debug}; +use std::{ + error::Error, + fmt::Debug, + sync::{Arc, Mutex}, + time::Duration, +}; + +use tokio::time::{interval, Interval}; use crate::{ drivers::opineo::{ @@ -23,8 +30,9 @@ enum TouchpadSide { /// OrangePi Neo Touchpad source device implementation pub struct OrangePiNeoTouchpad { - driver: Driver, + driver: Arc>, side: TouchpadSide, + interval: Interval, } impl OrangePiNeoTouchpad { @@ -45,19 +53,22 @@ impl OrangePiNeoTouchpad { TouchpadSide::Unknown } }; - let driver = Driver::new(device_info)?; + let driver = Arc::new(Mutex::new(Driver::new(device_info)?)); + let interval = interval(Duration::from_micros(2500)); Ok(Self { driver, side: touchpad_side, + interval, }) } } impl SourceInputDevice for OrangePiNeoTouchpad { /// Poll the given input device for input events - fn poll(&mut self) -> Result, InputError> { - let events = self.driver.poll()?; + async fn poll(&mut self) -> Result, InputError> { + self.interval.tick().await; + let events = self.driver.lock().unwrap().poll()?; let native_events = translate_events(events, self.side); Ok(native_events) } diff --git a/src/input/source/hidraw/rog_ally.rs b/src/input/source/hidraw/rog_ally.rs index 45837d89..840a4813 100644 --- a/src/input/source/hidraw/rog_ally.rs +++ b/src/input/source/hidraw/rog_ally.rs @@ -2,11 +2,7 @@ use std::{error::Error, fmt::Debug}; use crate::{ drivers::rog_ally::driver::Driver, - input::{ - capability::Capability, - event::native::NativeEvent, - source::{InputError, SourceInputDevice, SourceOutputDevice}, - }, + input::source::{SourceInputDevice, SourceOutputDevice}, udev::device::UdevDevice, }; @@ -29,14 +25,6 @@ impl Debug for RogAlly { f.debug_struct("RogAlly").finish() } } -impl SourceInputDevice for RogAlly { - fn poll(&mut self) -> Result, InputError> { - Ok(vec![]) - } - - fn get_capabilities(&self) -> Result, InputError> { - Ok(vec![]) - } -} +impl SourceInputDevice for RogAlly {} impl SourceOutputDevice for RogAlly {} diff --git a/src/input/source/hidraw/steam_deck.rs b/src/input/source/hidraw/steam_deck.rs index 61e4c12f..cc1377fc 100644 --- a/src/input/source/hidraw/steam_deck.rs +++ b/src/input/source/hidraw/steam_deck.rs @@ -9,6 +9,7 @@ use std::{ use evdev::{FFEffectData, FFEffectKind, InputEvent}; use packed_struct::PackedStruct; +use tokio::time::{interval, Interval}; use crate::{ drivers::{ @@ -37,18 +38,20 @@ pub const VID: u16 = 0x28de; pub const PID: u16 = 0x1205; pub struct DeckController { - driver: Driver, + driver: Arc>, device_info: UdevDevice, lizard_mode_started: bool, lizard_mode_running: Arc>, ff_evdev_effects: HashMap, + interval: Interval, } impl DeckController { /// Create a new Deck Controller source device with the given udev /// device information pub fn new(device_info: UdevDevice) -> Result> { - let driver = Driver::new(device_info.devnode())?; + let driver = Arc::new(Mutex::new(Driver::new(device_info.devnode())?)); + let interval = interval(Duration::from_millis(1)); Ok(Self { driver, @@ -56,6 +59,7 @@ impl DeckController { lizard_mode_started: false, lizard_mode_running: Arc::new(Mutex::new(false)), ff_evdev_effects: HashMap::new(), + interval, }) } @@ -120,7 +124,7 @@ impl DeckController { // The value determines if the effect should be playing or not. if value == 0 { - if let Err(e) = self.driver.haptic_rumble(0, 0) { + if let Err(e) = self.driver.lock().unwrap().haptic_rumble(0, 0) { log::debug!("Failed to stop haptic rumble: {:?}", e); return Ok(()); } @@ -159,7 +163,12 @@ impl DeckController { let right_speed = weak_magnitude; // Do rumble - if let Err(e) = self.driver.haptic_rumble(left_speed, right_speed) { + if let Err(e) = self + .driver + .lock() + .unwrap() + .haptic_rumble(left_speed, right_speed) + { let err = format!("Failed to do haptic rumble: {:?}", e); return Err(err.into()); } @@ -178,7 +187,12 @@ impl DeckController { let left_speed = report.rumble_emulation_left as u16 * 256; let right_speed = report.rumble_emulation_right as u16 * 256; - if let Err(e) = self.driver.haptic_rumble(left_speed, right_speed) { + if let Err(e) = self + .driver + .lock() + .unwrap() + .haptic_rumble(left_speed, right_speed) + { let err = format!("Failed to do haptic rumble: {:?}", e); return Err(err.into()); } @@ -189,13 +203,14 @@ impl DeckController { impl SourceInputDevice for DeckController { /// Poll the given input device for input events - fn poll(&mut self) -> Result, InputError> { + async fn poll(&mut self) -> Result, InputError> { // Spawn a blocking task to handle lizard mode if !self.lizard_mode_started { self.start_lizard_task(); } + self.interval.tick().await; - let events = self.driver.poll()?; + let events = self.driver.lock().unwrap().poll()?; let native_events = translate_events(events); Ok(native_events) } @@ -210,7 +225,7 @@ impl SourceOutputDevice for DeckController { /// Write the given output event to the source device. Output events are /// events that flow from an application (like a game) to the physical /// input device, such as force feedback events. - fn write_event(&mut self, event: OutputEvent) -> Result<(), OutputError> { + async fn write_event(&mut self, event: OutputEvent) -> Result<(), OutputError> { log::trace!("Received output event: {:?}", event); match event { OutputEvent::Evdev(input_event) => { @@ -225,11 +240,11 @@ impl SourceOutputDevice for DeckController { OutputEvent::Uinput(_) => (), OutputEvent::SteamDeckHaptics(packed_haptic_report) => { let report = packed_haptic_report.pack().map_err(|e| e.to_string())?; - self.driver.write(&report)?; + self.driver.lock().unwrap().write(&report)?; } OutputEvent::SteamDeckRumble(packed_rumble_report) => { let report = packed_rumble_report.pack().map_err(|e| e.to_string())?; - self.driver.write(&report)?; + self.driver.lock().unwrap().write(&report)?; } } @@ -238,7 +253,7 @@ impl SourceOutputDevice for DeckController { /// Upload the given force feedback effect data to the source device. Returns /// a device-specific id of the uploaded effect if it is successful. - fn upload_effect(&mut self, effect: evdev::FFEffectData) -> Result { + async fn upload_effect(&mut self, effect: evdev::FFEffectData) -> Result { log::debug!("Uploading FF effect data"); let id = self.next_ff_effect_id(); if id == -1 { @@ -250,7 +265,7 @@ impl SourceOutputDevice for DeckController { } /// Update the effect with the given id using the given effect data. - fn update_effect( + async fn update_effect( &mut self, effect_id: i16, effect: evdev::FFEffectData, @@ -261,14 +276,14 @@ impl SourceOutputDevice for DeckController { } /// Erase the effect with the given id from the source device. - fn erase_effect(&mut self, effect_id: i16) -> Result<(), OutputError> { + async fn erase_effect(&mut self, effect_id: i16) -> Result<(), OutputError> { log::debug!("Erasing FF effect data"); self.ff_evdev_effects.remove(&effect_id); Ok(()) } /// Stop the source device and terminate the lizard mode task - fn stop(&mut self) -> Result<(), OutputError> { + async fn stop(&mut self) -> Result<(), OutputError> { *self.lizard_mode_running.lock().unwrap() = false; Ok(()) } diff --git a/src/input/source/hidraw/xpad_uhid.rs b/src/input/source/hidraw/xpad_uhid.rs index e3255f00..1f8a9436 100644 --- a/src/input/source/hidraw/xpad_uhid.rs +++ b/src/input/source/hidraw/xpad_uhid.rs @@ -1,6 +1,13 @@ -use std::{collections::HashMap, error::Error, fmt::Debug}; +use std::{ + collections::HashMap, + error::Error, + fmt::Debug, + sync::{Arc, Mutex}, + time::Duration, +}; use evdev::{FFEffectData, FFEffectKind}; +use tokio::time::{interval, Interval}; use crate::{ drivers::xpad_uhid::{ @@ -18,18 +25,21 @@ use crate::{ /// XpadUhid source device implementation pub struct XpadUhid { - driver: Driver, + driver: Arc>, ff_evdev_effects: HashMap, + interval: Interval, } impl XpadUhid { /// Create a new source device with the given udev /// device information pub fn new(device_info: UdevDevice) -> Result> { - let driver = Driver::new(device_info)?; + let driver = Arc::new(Mutex::new(Driver::new(device_info)?)); + let interval = interval(Duration::from_micros(2500)); Ok(Self { driver, ff_evdev_effects: HashMap::new(), + interval, }) } @@ -69,7 +79,7 @@ impl XpadUhid { // The value determines if the effect should be playing or not. if value == 0 { log::trace!("Stopping rumble"); - if let Err(e) = self.driver.rumble(0, 0) { + if let Err(e) = self.driver.lock().unwrap().rumble(0, 0) { log::debug!("Failed to stop rumble: {:?}", e); return Ok(()); } @@ -111,7 +121,7 @@ impl XpadUhid { let right_speed = right_speed.round() as u8; // Do rumble - if let Err(e) = self.driver.rumble(left_speed, right_speed) { + if let Err(e) = self.driver.lock().unwrap().rumble(left_speed, right_speed) { let err = format!("Failed to do rumble: {:?}", e); return Err(err.into()); } @@ -124,8 +134,9 @@ impl XpadUhid { impl SourceInputDevice for XpadUhid { /// Poll the given input device for input events - fn poll(&mut self) -> Result, InputError> { - let events = self.driver.poll()?; + async fn poll(&mut self) -> Result, InputError> { + self.interval.tick().await; + let events = self.driver.lock().unwrap().poll()?; let native_events = translate_events(events); Ok(native_events) } @@ -140,7 +151,7 @@ impl SourceOutputDevice for XpadUhid { /// Write the given output event to the source device. Output events are /// events that flow from an application (like a game) to the physical /// input device, such as force feedback events. - fn write_event(&mut self, event: OutputEvent) -> Result<(), OutputError> { + async fn write_event(&mut self, event: OutputEvent) -> Result<(), OutputError> { log::trace!("Received output event: {:?}", event); match event { OutputEvent::Evdev(input_event) => Ok(self.process_evdev_ff(input_event)?), @@ -153,7 +164,7 @@ impl SourceOutputDevice for XpadUhid { /// Upload the given force feedback effect data to the source device. Returns /// a device-specific id of the uploaded effect if it is successful. - fn upload_effect(&mut self, effect: FFEffectData) -> Result { + async fn upload_effect(&mut self, effect: FFEffectData) -> Result { log::debug!("Uploading FF effect data"); let id = self.next_ff_effect_id(); if id == -1 { @@ -165,14 +176,18 @@ impl SourceOutputDevice for XpadUhid { } /// Update the effect with the given id using the given effect data. - fn update_effect(&mut self, effect_id: i16, effect: FFEffectData) -> Result<(), OutputError> { + async fn update_effect( + &mut self, + effect_id: i16, + effect: FFEffectData, + ) -> Result<(), OutputError> { log::debug!("Updating FF effect data with id {effect_id}"); self.ff_evdev_effects.insert(effect_id, effect); Ok(()) } /// Erase the effect with the given id from the source device. - fn erase_effect(&mut self, effect_id: i16) -> Result<(), OutputError> { + async fn erase_effect(&mut self, effect_id: i16) -> Result<(), OutputError> { log::debug!("Erasing FF effect data"); self.ff_evdev_effects.remove(&effect_id); Ok(()) diff --git a/src/input/source/hidraw/zotac_zone.rs b/src/input/source/hidraw/zotac_zone.rs index 53d43f8a..4e10b54e 100644 --- a/src/input/source/hidraw/zotac_zone.rs +++ b/src/input/source/hidraw/zotac_zone.rs @@ -2,11 +2,7 @@ use std::{error::Error, fmt::Debug}; use crate::{ drivers::zotac_zone::driver::Driver, - input::{ - capability::Capability, - event::native::NativeEvent, - source::{InputError, SourceInputDevice, SourceOutputDevice}, - }, + input::source::{SourceInputDevice, SourceOutputDevice}, udev::device::UdevDevice, }; @@ -29,14 +25,6 @@ impl Debug for ZotacZone { } } -impl SourceInputDevice for ZotacZone { - fn poll(&mut self) -> Result, InputError> { - Ok(vec![]) - } - - fn get_capabilities(&self) -> Result, InputError> { - Ok(vec![]) - } -} +impl SourceInputDevice for ZotacZone {} impl SourceOutputDevice for ZotacZone {} diff --git a/src/input/source/iio.rs b/src/input/source/iio.rs index 4acad5f8..29b69dac 100644 --- a/src/input/source/iio.rs +++ b/src/input/source/iio.rs @@ -6,13 +6,15 @@ use std::error::Error; use glob_match::glob_match; use crate::{ - config, constants::BUS_SOURCES_PREFIX, input::composite_device::client::CompositeDeviceClient, + config, + constants::BUS_SOURCES_PREFIX, + input::{capability::Capability, composite_device::client::CompositeDeviceClient}, udev::device::UdevDevice, }; use self::{accel_gyro_3d::AccelGyro3dImu, bmi_imu::BmiImu}; -use super::{SourceDeviceCompatible, SourceDriver}; +use super::{InputError, SourceDeviceCompatible, SourceDriver}; /// List of available drivers enum DriverType { @@ -52,14 +54,12 @@ impl SourceDeviceCompatible for IioDevice { async fn run(self) -> Result<(), Box> { match self { - IioDevice::BmiImu(source_driver) => source_driver.run().await, - IioDevice::AccelGryo3D(source_driver) => source_driver.run().await, + IioDevice::BmiImu(mut source_driver) => source_driver.run().await, + IioDevice::AccelGryo3D(mut source_driver) => source_driver.run().await, } } - fn get_capabilities( - &self, - ) -> Result, super::InputError> { + fn get_capabilities(&self) -> Result, InputError> { match self { IioDevice::BmiImu(source_driver) => source_driver.get_capabilities(), IioDevice::AccelGryo3D(source_driver) => source_driver.get_capabilities(), diff --git a/src/input/source/iio/accel_gyro_3d.rs b/src/input/source/iio/accel_gyro_3d.rs index 90ab6345..5404607f 100644 --- a/src/input/source/iio/accel_gyro_3d.rs +++ b/src/input/source/iio/accel_gyro_3d.rs @@ -1,4 +1,12 @@ -use std::{error::Error, f64::consts::PI, fmt::Debug}; +use std::{ + error::Error, + f64::consts::PI, + fmt::Debug, + sync::{Arc, Mutex}, + time::Duration, +}; + +use tokio::time::{interval, Interval}; use crate::{ config, @@ -12,7 +20,8 @@ use crate::{ }; pub struct AccelGyro3dImu { - driver: Driver, + driver: Arc>, + interval: Interval, } impl AccelGyro3dImu { @@ -40,16 +49,18 @@ impl AccelGyro3dImu { let id = device_info.sysname(); let name = device_info.name(); - let driver = Driver::new(id, name, mount_matrix)?; + let driver = Arc::new(Mutex::new(Driver::new(id, name, mount_matrix)?)); + let interval = interval(Duration::from_millis(10)); - Ok(Self { driver }) + Ok(Self { driver, interval }) } } impl SourceInputDevice for AccelGyro3dImu { /// Poll the given input device for input events - fn poll(&mut self) -> Result, InputError> { - let events = self.driver.poll()?; + async fn poll(&mut self) -> Result, InputError> { + self.interval.tick().await; + let events = self.driver.lock().unwrap().poll()?; let native_events = translate_events(events); Ok(native_events) } @@ -68,10 +79,6 @@ impl Debug for AccelGyro3dImu { } } -// NOTE: Mark this struct as thread-safe as it will only ever be called from -// a single thread. -unsafe impl Send for AccelGyro3dImu {} - /// Translate the given driver events into native events fn translate_events(events: Vec) -> Vec { events.into_iter().map(translate_event).collect() diff --git a/src/input/source/iio/bmi_imu.rs b/src/input/source/iio/bmi_imu.rs index 20eff34e..6aff2677 100644 --- a/src/input/source/iio/bmi_imu.rs +++ b/src/input/source/iio/bmi_imu.rs @@ -1,4 +1,12 @@ -use std::{error::Error, f64::consts::PI, fmt::Debug}; +use std::{ + error::Error, + f64::consts::PI, + fmt::Debug, + sync::{Arc, Mutex}, + time::Duration, +}; + +use tokio::time::{interval, Interval}; use crate::{ config, @@ -12,7 +20,8 @@ use crate::{ }; pub struct BmiImu { - driver: Driver, + driver: Arc>, + interval: Interval, } impl BmiImu { @@ -40,16 +49,18 @@ impl BmiImu { let id = device_info.sysname(); let name = device_info.name(); - let driver = Driver::new(id, name, mount_matrix)?; + let driver = Arc::new(Mutex::new(Driver::new(id, name, mount_matrix)?)); + let interval = interval(Duration::from_millis(10)); - Ok(Self { driver }) + Ok(Self { driver, interval }) } } impl SourceInputDevice for BmiImu { /// Poll the given input device for input events - fn poll(&mut self) -> Result, InputError> { - let events = self.driver.poll()?; + async fn poll(&mut self) -> Result, InputError> { + self.interval.tick().await; + let events = self.driver.lock().unwrap().poll()?; let native_events = translate_events(events); Ok(native_events) } @@ -68,10 +79,6 @@ impl Debug for BmiImu { } } -// NOTE: Mark this struct as thread-safe as it will only ever be called from -// a single thread. -unsafe impl Send for BmiImu {} - /// Translate the given driver events into native events fn translate_events(events: Vec) -> Vec { events.into_iter().map(translate_event).collect() diff --git a/src/input/source/led.rs b/src/input/source/led.rs index b7b79257..5968af82 100644 --- a/src/input/source/led.rs +++ b/src/input/source/led.rs @@ -1,8 +1,10 @@ pub mod multicolor; use self::multicolor::LedMultiColor; -use super::{SourceDeviceCompatible, SourceDriver}; +use super::{InputError, SourceDeviceCompatible, SourceDriver}; use crate::{ - config, constants::BUS_SOURCES_PREFIX, input::composite_device::client::CompositeDeviceClient, + config, + constants::BUS_SOURCES_PREFIX, + input::{capability::Capability, composite_device::client::CompositeDeviceClient}, udev::device::UdevDevice, }; use std::error::Error; @@ -37,13 +39,11 @@ impl SourceDeviceCompatible for LedDevice { async fn run(self) -> Result<(), Box> { match self { - LedDevice::MultiColor(source_driver) => source_driver.run().await, + LedDevice::MultiColor(mut source_driver) => source_driver.run().await, } } - fn get_capabilities( - &self, - ) -> Result, super::InputError> { + fn get_capabilities(&self) -> Result, InputError> { match self { LedDevice::MultiColor(source_driver) => source_driver.get_capabilities(), } diff --git a/src/input/source/led/multicolor.rs b/src/input/source/led/multicolor.rs index c542d8db..4913a929 100644 --- a/src/input/source/led/multicolor.rs +++ b/src/input/source/led/multicolor.rs @@ -1,8 +1,7 @@ use crate::{ input::{ - capability::Capability, output_event::OutputEvent, - source::{InputError, OutputError, SourceInputDevice, SourceOutputDevice}, + source::{OutputError, SourceInputDevice, SourceOutputDevice}, }, udev::device::UdevDevice, }; @@ -251,18 +250,10 @@ impl Debug for LedMultiColor { } } -impl SourceInputDevice for LedMultiColor { - fn poll(&mut self) -> Result, InputError> { - Ok(Vec::new()) - } - - fn get_capabilities(&self) -> Result, InputError> { - Ok(Vec::new()) - } -} +impl SourceInputDevice for LedMultiColor {} impl SourceOutputDevice for LedMultiColor { - fn write_event(&mut self, event: OutputEvent) -> Result<(), OutputError> { + async fn write_event(&mut self, event: OutputEvent) -> Result<(), OutputError> { log::trace!("Received output event: {event:?}"); match event { OutputEvent::DualSense(report) => { diff --git a/src/input/source/mod.rs b/src/input/source/mod.rs index e3a15b37..8791d348 100644 --- a/src/input/source/mod.rs +++ b/src/input/source/mod.rs @@ -1,19 +1,14 @@ -use std::{ - collections::HashSet, - env, - error::Error, - str::FromStr, - sync::{Arc, Mutex, MutexGuard}, - thread, - time::Duration, -}; +use std::{collections::HashSet, env, error::Error, future::Future, str::FromStr, time::Duration}; use ::evdev::FFEffectData; use led::LedDevice; use thiserror::Error; -use tokio::sync::mpsc::{self, error::TryRecvError}; +use tokio::sync::mpsc; -use crate::{config, udev::device::UdevDevice}; +use crate::{ + config, + udev::{device::UdevDevice, hide_device, unhide_device}, +}; use self::{ client::SourceDeviceClient, command::SourceCommand, evdev::EventDevice, hidraw::HidRawDevice, @@ -36,8 +31,6 @@ pub mod led; /// Size of the [SourceCommand] buffer for receiving output events const BUFFER_SIZE: usize = 2048; -/// Default poll rate (2.5ms/400Hz) -const POLL_RATE: Duration = Duration::from_micros(2500); /// Possible errors for a source device client #[derive(Error, Debug)] @@ -104,14 +97,35 @@ impl From> for OutputError { } } +/// Options for running a source device +#[derive(Debug)] +pub struct SourceDriverOptions { + pub buffer_size: usize, +} + +impl Default for SourceDriverOptions { + fn default() -> Self { + Self { + buffer_size: BUFFER_SIZE, + } + } +} + /// A [SourceInputDevice] is a device implementation that is capable of emitting /// input events. pub trait SourceInputDevice { - /// Poll the given input device for input events - fn poll(&mut self) -> Result, InputError>; + /// Poll the source device for input events + fn poll(&mut self) -> impl Future, InputError>> + Send { + async { + tokio::time::sleep(Duration::from_secs(60 * 60)).await; + Ok(Vec::new()) + } + } - /// Returns the possible input events this device is capable of emitting - fn get_capabilities(&self) -> Result, InputError>; + /// Input capabilities of the source device + fn get_capabilities(&self) -> Result, InputError> { + Ok(Vec::new()) + } } /// A [SourceOutputDevice] is a device implementation that can handle output events @@ -120,73 +134,66 @@ pub trait SourceOutputDevice { /// Write the given output event to the source device. Output events are /// events that flow from an application (like a game) to the physical /// input device, such as force feedback events. - fn write_event(&mut self, event: OutputEvent) -> Result<(), OutputError> { + fn write_event(&mut self, event: OutputEvent) -> impl Future> { //log::trace!("Received output event: {event:?}"); let _ = event; - Ok(()) + async { Ok(()) } } /// Upload the given force feedback effect data to the source device. Returns /// a device-specific id of the uploaded effect if it is successful. Return /// -1 if this device does not support FF events. - fn upload_effect(&mut self, effect: FFEffectData) -> Result { + fn upload_effect( + &mut self, + effect: FFEffectData, + ) -> impl Future> { //log::trace!("Received upload effect: {effect:?}"); let _ = effect; - Ok(-1) + async { Ok(-1) } } /// Update the effect with the given id using the given effect data. - fn update_effect(&mut self, effect_id: i16, effect: FFEffectData) -> Result<(), OutputError> { + fn update_effect( + &mut self, + effect_id: i16, + effect: FFEffectData, + ) -> impl Future> { //log::trace!("Received update effect: {effect_id:?} {effect:?}"); let _ = effect; let _ = effect_id; - Ok(()) + async { Ok(()) } } /// Erase the effect with the given id from the source device. - fn erase_effect(&mut self, effect_id: i16) -> Result<(), OutputError> { + fn erase_effect(&mut self, effect_id: i16) -> impl Future> { //log::trace!("Received erase effect: {effect_id:?}"); let _ = effect_id; - Ok(()) + async { Ok(()) } } /// Stop the source device. - fn stop(&mut self) -> Result<(), OutputError> { - Ok(()) - } -} - -/// Options for running a source device -#[derive(Debug)] -pub struct SourceDriverOptions { - pub poll_rate: Duration, - pub buffer_size: usize, -} - -impl Default for SourceDriverOptions { - fn default() -> Self { - Self { - poll_rate: POLL_RATE, - buffer_size: BUFFER_SIZE, - } + fn stop(&mut self) -> impl Future> { + async { Ok(()) } } } /// A [SourceDriver] is any physical input device that emits input events #[derive(Debug)] pub struct SourceDriver { - options: SourceDriverOptions, + config: Option, + is_hidden: bool, event_filter_enabled: bool, event_include_list: HashSet, event_exclude_list: HashSet, - implementation: Arc>, + implementation: T, device_info: UdevDevice, composite_device: CompositeDeviceClient, tx: mpsc::Sender, rx: mpsc::Receiver, + metrics_enabled: bool, } -impl SourceDriver { +impl SourceDriver { /// Create a new source device with the given implementation pub fn new( composite_device: CompositeDeviceClient, @@ -244,16 +251,23 @@ impl SourceDriver } } + let metrics_enabled = match env::var("ENABLE_METRICS") { + Ok(value) => value.as_str() == "1", + Err(_) => false, + }; + Self { + config, + is_hidden: false, event_filter_enabled, event_include_list: events_include, event_exclude_list: events_exclude, - options, - implementation: Arc::new(Mutex::new(device)), + implementation: device, device_info, composite_device, tx, rx, + metrics_enabled, } } @@ -299,7 +313,7 @@ impl SourceDriver /// Returns the possible input events this device is capable of emitting pub fn get_capabilities(&self) -> Result, InputError> { - let caps = { self.implementation.lock().unwrap().get_capabilities()? }; + let caps = self.implementation.get_capabilities()?; if self.event_filter_enabled { return Ok(caps @@ -328,160 +342,168 @@ impl SourceDriver &self.device_info } - /// Run the source device, consuming the device. - pub async fn run(self) -> Result<(), Box> { - let device_id = self.get_id(); - let metrics_enabled = match env::var("ENABLE_METRICS") { - Ok(value) => value.as_str() == "1", - Err(_) => false, - }; - - // Spawn a blocking task to run the source device. - let task = - tokio::task::spawn_blocking(move || -> Result<(), Box> { - let mut rx = self.rx; - let mut implementation = self.implementation.lock().unwrap(); - loop { - // Create a context with performance metrics for each event - let mut context = if metrics_enabled { - Some(EventContext::new()) - } else { - None - }; - if let Some(ref mut context) = context { - let root_span = context.metrics_mut().create_span("root"); - root_span.start(); - } - - // Poll the implementation for events - if let Some(ref mut context) = context { - let poll_span = context - .metrics_mut() - .create_child_span("root", "source_poll"); - poll_span.start(); - } - let events = implementation.poll()?; - if let Some(ref mut context) = context { - let poll_span = context.metrics_mut().get_mut("source_poll").unwrap(); - poll_span.finish(); - } + /// Run the source device + pub async fn run(&mut self) -> Result<(), Box> { + // Hide the device if specified + let should_passthru = self + .config + .as_ref() + .and_then(|c| c.passthrough) + .unwrap_or(false); + let subsystem = self.device_info.subsystem(); + let should_hide = !should_passthru && subsystem.as_str() != "iio"; + if should_hide { + let source_path = self.device_info.devnode(); + if let Err(e) = hide_device(source_path.as_str()).await { + log::warn!("Failed to hide device '{source_path}': {e:?}"); + } else { + log::debug!("Finished hiding device: {source_path}"); + self.is_hidden = true; + } + } - // Process each event - for mut event in events.into_iter() { - if self.event_filter_enabled - && Self::should_filter( - &self.event_exclude_list, - &self.event_include_list, - &event.as_capability(), - ) - { - continue; - } - if let Some(ref context) = context { - let mut context = context.clone(); - let send_span = context - .metrics_mut() - .create_child_span("root", "source_send"); - send_span.start(); - event.set_context(context); - } - let event = Event::Native(event); - let result = self - .composite_device - .blocking_process_event(device_id.clone(), event); - if let Err(e) = result { - return Err(e.to_string().into()); - } - } + // TODO: If the source device is blocked, don't bother polling it. - // Receive commands/output events - if let Err(e) = SourceDriver::receive_commands(&mut rx, &mut implementation) { - log::debug!("Error receiving commands: {:?}", e); - break; - } + // Run the main loop + loop { + // Create a context with performance metrics for each event + let mut context = if self.metrics_enabled { + let mut context = EventContext::new(); + { + let root_span = context.metrics_mut().create_span("root"); + root_span.start(); + } + let poll_span = context + .metrics_mut() + .create_child_span("root", "source_poll"); + poll_span.start(); + Some(context) + } else { + None + }; - // Sleep for the configured duration - thread::sleep(self.options.poll_rate); + tokio::select! { + // Poll the implementation for events + result = self.implementation.poll() => { + let events = result?; + self.process_events(&mut context, events).await?; + } + // Receive commands/output events + result = self.rx.recv() => { + let Some(cmd) = result else { + return Err("Receive channel disconnected".into()); + }; + self.process_command(cmd).await?; } + } + } + } - Ok(()) + /// Process the given events and write them to the composite device + async fn process_events( + &self, + context: &mut Option, + events: Vec, + ) -> Result<(), Box> { + if let Some(context) = context { + let poll_span = context.metrics_mut().get_mut("source_poll").unwrap(); + poll_span.finish(); + } + let device_id = self.get_id(); + for mut event in events.into_iter() { + if self.event_filter_enabled + && Self::should_filter( + &self.event_exclude_list, + &self.event_include_list, + &event.as_capability(), + ) + { + continue; + } + if let Some(context) = context { + let mut context = context.clone(); + let send_span = context + .metrics_mut() + .create_child_span("root", "source_send"); + send_span.start(); + event.set_context(context); + } + let event = Event::Native(event); + let composite_device = self.composite_device.clone(); + let device_id = device_id.clone(); + // NOTE: Spawning a task to send the event to the composite device + // appears to be significantly more performant. + // TODO: Don't spawn a task for each event + tokio::task::spawn(async move { + let _ = composite_device.process_event(device_id, event).await; }); - - // Wait for the device to finish running. - if let Err(e) = task.await? { - return Err(e.to_string().into()); } Ok(()) } - /// Read commands sent to this device from the channel until it is - /// empty. - fn receive_commands( - rx: &mut mpsc::Receiver, - implementation: &mut MutexGuard<'_, T>, - ) -> Result<(), Box> { - const MAX_COMMANDS: u8 = 64; - let mut commands_processed = 0; - loop { - match rx.try_recv() { - Ok(cmd) => match cmd { - SourceCommand::UploadEffect(data, composite_dev) => { - let res = match implementation.upload_effect(data) { - Ok(id) => composite_dev.send(Ok(id)), - Err(e) => { - let err = format!("Failed to upload effect: {:?}", e); - composite_dev.send(Err(err.into())) - } - }; - if let Err(err) = res { - log::error!("Failed to send upload result: {:?}", err); - } - } - SourceCommand::UpdateEffect(effect_id, data) => { - implementation.update_effect(effect_id, data)?; - } - SourceCommand::EraseEffect(id, composite_dev) => { - let res = match implementation.erase_effect(id) { - Ok(_) => Ok(()), - Err(e) => { - let err = format!("Failed to erase effect: {e:?}"); - composite_dev.send(Err(err.into())) - } - }; - if let Err(err) = res { - log::error!("Failed to send erase result: {:?}", err); - } - } - SourceCommand::WriteEvent(event) => { - log::trace!("Received output event: {:?}", event); - implementation.write_event(event)?; - } - SourceCommand::Stop => { - implementation.stop()?; - return Err("Device stopped".into()); + /// Read commands sent to this device from the channel + async fn process_command(&mut self, cmd: SourceCommand) -> Result<(), Box> { + match cmd { + SourceCommand::UploadEffect(data, composite_dev) => { + let res = match self.implementation.upload_effect(data).await { + Ok(id) => composite_dev.send(Ok(id)), + Err(e) => { + let err = format!("Failed to upload effect: {:?}", e); + composite_dev.send(Err(err.into())) } - }, - Err(e) => match e { - TryRecvError::Empty => return Ok(()), - TryRecvError::Disconnected => { - log::debug!("Receive channel disconnected"); - return Err("Receive channel disconnected".into()); + }; + if let Err(err) = res { + log::error!("Failed to send upload result: {:?}", err); + } + } + SourceCommand::UpdateEffect(effect_id, data) => { + self.implementation.update_effect(effect_id, data).await?; + } + SourceCommand::EraseEffect(id, composite_dev) => { + let res = match self.implementation.erase_effect(id).await { + Ok(_) => Ok(()), + Err(e) => { + let err = format!("Failed to erase effect: {e:?}"); + composite_dev.send(Err(err.into())) } - }, - }; - - // Only process MAX_COMMANDS messages at a time - commands_processed += 1; - if commands_processed >= MAX_COMMANDS { - return Ok(()); + }; + if let Err(err) = res { + log::error!("Failed to send erase result: {:?}", err); + } + } + SourceCommand::WriteEvent(event) => { + log::trace!("Received output event: {:?}", event); + self.implementation.write_event(event).await?; + } + SourceCommand::Stop => { + self.implementation.stop().await?; + return Err("Device stopped".into()); } } + + Ok(()) + } +} + +impl Drop for SourceDriver { + fn drop(&mut self) { + // Unhide the device + if !self.is_hidden { + return; + } + let source_path = self.device_info.devnode(); + tokio::task::spawn(async move { + if let Err(e) = unhide_device(source_path).await { + log::warn!("Unable to unhide device: {e}"); + } + }); } } -pub(crate) trait SourceDeviceCompatible { +pub trait SourceDeviceCompatible { /// Returns a copy of the UdevDevice + #[allow(dead_code)] fn get_device_ref(&self) -> &UdevDevice; /// Returns a unique identifier for the source device. @@ -491,7 +513,7 @@ pub(crate) trait SourceDeviceCompatible { fn client(&self) -> SourceDeviceClient; /// Run the source device - async fn run(self) -> Result<(), Box>; + fn run(self) -> impl Future>>; /// Returns the capabilities that this source device can fulfill. fn get_capabilities(&self) -> Result, InputError>; @@ -511,6 +533,7 @@ pub enum SourceDevice { impl SourceDevice { /// Returns a copy of the UdevDevice + #[allow(dead_code)] pub fn get_device_ref(&self) -> &UdevDevice { match self { SourceDevice::Event(device) => device.get_device_ref(),