diff --git a/src/input/event/native.rs b/src/input/event/native.rs index bf31f49e..cd30a424 100644 --- a/src/input/event/native.rs +++ b/src/input/event/native.rs @@ -1,3 +1,5 @@ +use std::time::{Duration, Instant}; + use evdev::AbsoluteAxisCode; use crate::input::capability::{Capability, Gamepad, GamepadButton}; @@ -130,3 +132,35 @@ impl From for NativeEvent { } } } + +impl From for NativeEvent { + fn from(value: ScheduledNativeEvent) -> Self { + value.event + } +} + +/// A scheduled event represents an input event that should be sent sometime in +/// the future. +#[derive(Debug, Clone)] +pub struct ScheduledNativeEvent { + event: NativeEvent, + scheduled_time: Instant, + wait_time: Duration, +} + +impl ScheduledNativeEvent { + /// Create a new scheduled event with the given time to wait before being + /// emitted. + pub fn new(event: NativeEvent, wait_time: Duration) -> Self { + Self { + event, + scheduled_time: Instant::now(), + wait_time, + } + } + + /// Returns true when the scheduled event is ready to be emitted + pub fn is_ready(&self) -> bool { + self.scheduled_time.elapsed() > self.wait_time + } +} diff --git a/src/input/manager.rs b/src/input/manager.rs index 06faa352..681150ad 100644 --- a/src/input/manager.rs +++ b/src/input/manager.rs @@ -44,7 +44,7 @@ use crate::input::target::touchscreen::TouchscreenDevice; use crate::input::target::xb360::XBox360Controller; use crate::input::target::xbox_elite::XboxEliteController; use crate::input::target::xbox_series::XboxSeriesController; -use crate::input::target::TargetDeviceType; +use crate::input::target::TargetDevice; use crate::input::target::TargetDeviceTypeId; use crate::udev; use crate::udev::device::UdevDevice; @@ -356,17 +356,14 @@ impl Manager { } /// Create target input device to emulate based on the given device type. - async fn create_target_device( - &mut self, - kind: &str, - ) -> Result> { + async fn create_target_device(&mut self, kind: &str) -> Result> { log::trace!("Creating target device: {kind}"); let Ok(target_id) = TargetDeviceTypeId::try_from(kind) else { return Err("Invalid target device ID".to_string().into()); }; // Create the target device to emulate based on the kind - let device = TargetDeviceType::from_type_id(target_id, self.dbus.clone()); + let device = TargetDevice::from_type_id(target_id, self.dbus.clone()); Ok(device) } @@ -375,7 +372,7 @@ impl Manager { /// to send events to the given targets. async fn start_target_devices( &mut self, - targets: Vec, + targets: Vec, ) -> Result, Box> { let mut target_devices = HashMap::new(); for mut target in targets { diff --git a/src/input/target/dbus_new.rs b/src/input/target/dbus_new.rs new file mode 100644 index 00000000..8ba87880 --- /dev/null +++ b/src/input/target/dbus_new.rs @@ -0,0 +1,55 @@ +use zbus::Connection; + +use super::{TargetInputDevice, TargetOutputDevice}; + +/// The threshold for axis inputs to be considered "pressed" +const AXIS_THRESHOLD: f64 = 0.35; + +/// The internal emulated device state for tracking analog input +#[derive(Debug, Clone, Default)] +struct State { + pressed_left: bool, + pressed_right: bool, + pressed_up: bool, + pressed_down: bool, +} + +/// The [DBusDevice] is a virtual input device that can emit input events. It +/// is primarily used when a [CompositeDevice] is using input interception to +/// divert inputs to an overlay over DBus. +pub struct DBusDevice { + state: State, + conn: Connection, +} + +impl DBusDevice { + // Create a new [DBusDevice] instance. + pub fn new(conn: Connection) -> Self { + Self { + state: State::default(), + conn, + } + } +} + +impl TargetInputDevice for DBusDevice { + fn write_event( + &mut self, + event: crate::input::event::native::NativeEvent, + ) -> Result<(), super::InputError> { + log::trace!("Discarding event: {event:?}"); + Ok(()) + } + + fn get_capabilities( + &self, + ) -> Result, super::InputError> { + Ok(vec![]) + } + + fn stop(&self) -> Result<(), super::InputError> { + Ok(()) + } +} + +impl TargetOutputDevice for DBusDevice {} diff --git a/src/input/target/mod.rs b/src/input/target/mod.rs index 4d85fa64..bc371d50 100644 --- a/src/input/target/mod.rs +++ b/src/input/target/mod.rs @@ -1,10 +1,28 @@ +use std::{ + error::Error, + sync::{Arc, Mutex, MutexGuard}, + thread, + time::Duration, +}; + +use thiserror::Error; +use tokio::sync::mpsc::{self, error::TryRecvError}; + +use super::{ + capability::Capability, + composite_device::client::CompositeDeviceClient, + event::native::{NativeEvent, ScheduledNativeEvent}, + output_capability::OutputCapability, + output_event::OutputEvent, +}; + use std::convert::TryFrom; -use std::error::Error; use std::fmt::Display; use zbus::Connection; use self::client::TargetDeviceClient; +use self::command::TargetCommand; use self::dbus::DBusDevice; use self::dualsense::{DualSenseDevice, DualSenseHardware}; use self::keyboard::KeyboardDevice; @@ -18,6 +36,7 @@ use self::xbox_series::XboxSeriesController; pub mod client; pub mod command; pub mod dbus; +pub mod dbus_new; pub mod dualsense; pub mod keyboard; pub mod mouse; @@ -27,6 +46,70 @@ pub mod xb360; pub mod xbox_elite; pub mod xbox_series; +/// Possible errors for a target device client +#[derive(Error, Debug)] +pub enum InputError { + #[error("error occurred running device")] + DeviceError(String), +} + +impl From<&str> for InputError { + fn from(value: &str) -> Self { + InputError::DeviceError(value.to_string()) + } +} + +impl From for InputError { + fn from(value: String) -> Self { + InputError::DeviceError(value) + } +} + +impl From> for InputError { + fn from(value: Box) -> Self { + InputError::DeviceError(value.to_string()) + } +} + +impl From> for InputError { + fn from(value: Box) -> Self { + InputError::DeviceError(value.to_string()) + } +} + +/// Possible errors for a target device client +#[derive(Error, Debug)] +pub enum OutputError { + #[error("behavior is not implemented")] + NotImplemented, + #[error("error occurred running device")] + DeviceError(String), +} + +impl From<&str> for OutputError { + fn from(value: &str) -> Self { + OutputError::DeviceError(value.to_string()) + } +} + +impl From for OutputError { + fn from(value: String) -> Self { + OutputError::DeviceError(value) + } +} + +impl From> for OutputError { + fn from(value: Box) -> Self { + OutputError::DeviceError(value.to_string()) + } +} + +impl From> for OutputError { + fn from(value: Box) -> Self { + OutputError::DeviceError(value.to_string()) + } +} + /// TargetDeviceTypeId is a string representation of a supported TargetDevice. /// When a new target device is added, an entry should be added to the list of /// supported types. @@ -123,22 +206,233 @@ impl TryFrom<&str> for TargetDeviceTypeId { } } +/// A [TargetInputDevice] is a device implementation that is capable of emitting +/// input events. Input events originate from source devices, are processed by +/// a composite device, and are sent to a target device to be emitted. +pub trait TargetInputDevice { + /// Write the given input event to the virtual device + fn write_event(&mut self, event: NativeEvent) -> Result<(), InputError> { + log::trace!("Discarding event: {event:?}"); + Ok(()) + } + + /// Returns the target device input capabilities that the device can handle + fn get_capabilities(&self) -> Result, InputError> { + Ok(vec![]) + } + + /// Returns scheduled events that should be written later. This function will + /// be called every poll iteration by the [TargetDriver] and schedule the + /// events to be written at the specified time. + fn scheduled_events(&mut self) -> Option> { + None + } + + /// Stop the target device + fn stop(&self) -> Result<(), InputError> { + Ok(()) + } +} + +/// A [TargetOutputDevice] is a device implementation that is capable of emitting +/// output events such as force feedback, etc. These output events will be routed +/// to physical source devices that can handle them. +pub trait TargetOutputDevice { + /// Poll the given device for output events + fn poll(&mut self) -> Result, OutputError> { + Ok(vec![]) + } + + /// Returns the possible output events this device is capable of emitting + fn get_output_capabilities(&self) -> Result, OutputError> { + Ok(vec![]) + } +} + +/// Options for running a target device +#[derive(Debug)] +pub struct TargetDriverOptions { + pub poll_rate: Duration, + pub buffer_size: usize, +} + +impl Default for TargetDriverOptions { + fn default() -> Self { + Self { + poll_rate: Duration::from_millis(4), + buffer_size: 2048, + } + } +} + +/// A [TargetDriver] is any virtual input device that can emit input events +#[derive(Debug)] +pub struct TargetDriver { + options: TargetDriverOptions, + implementation: Arc>, + composite_device: Option, + scheduled_events: Vec, + tx: mpsc::Sender, + rx: mpsc::Receiver, +} + +impl TargetDriver { + pub fn new(device: T) -> Self { + let options = TargetDriverOptions::default(); + let (tx, rx) = mpsc::channel(options.buffer_size); + Self { + options, + implementation: Arc::new(Mutex::new(device)), + composite_device: None, + scheduled_events: Vec::new(), + rx, + tx, + } + } + + /// Returns a transmitter channel that can be used to send events to this device + pub fn client(&self) -> TargetDeviceClient { + self.tx.clone().into() + } + + /// Run the target device, consuming the device. + pub async fn run(mut self) -> Result<(), Box> { + // Spawn a blocking task to run the target device. + let task = + tokio::task::spawn_blocking(move || -> Result<(), Box> { + let mut composite_device = self.composite_device; + let mut rx = self.rx; + let mut implementation = self.implementation.lock().unwrap(); + loop { + // Find any scheduled events that are ready to be sent + let mut ready_events = vec![]; + let mut i = 0; + while i < self.scheduled_events.len() { + if self.scheduled_events[i].is_ready() { + let event = self.scheduled_events.remove(i); + ready_events.push(event); + continue; + } + i += 1; + } + for event in ready_events.drain(..) { + implementation.write_event(event.into())?; + } + + // Receive commands/input events + if let Err(e) = TargetDriver::receive_commands( + &mut composite_device, + &mut rx, + &mut implementation, + ) { + log::debug!("Error receiving commands: {e:?}"); + break; + } + + // Poll the implementation for scheduled input events + if let Some(mut scheduled_events) = implementation.scheduled_events() { + self.scheduled_events.append(&mut scheduled_events); + } + + // Poll the implementation for output events + let events = implementation.poll()?; + for event in events.into_iter() { + let Some(ref client) = composite_device else { + break; + }; + + // Send the output event to source devices + let result = client.blocking_process_output_event(event); + if let Err(e) = result { + return Err(e.to_string().into()); + } + } + + // Sleep for the configured duration + thread::sleep(self.options.poll_rate); + } + Ok(()) + }); + + // 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( + composite_device: &mut Option, + 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 { + TargetCommand::WriteEvent(event) => { + implementation.write_event(event)?; + } + TargetCommand::SetCompositeDevice(device) => { + *composite_device = Some(device); + } + TargetCommand::GetCapabilities(sender) => { + let capabilities = implementation.get_capabilities().unwrap_or_default(); + sender.blocking_send(capabilities)?; + } + TargetCommand::GetType(_) => todo!(), + TargetCommand::Stop => { + implementation.stop()?; + } + }, + Err(e) => match e { + TryRecvError::Empty => return Ok(()), + TryRecvError::Disconnected => { + log::debug!("Receive channel disconnected"); + return Err("Receive channel disconnected".into()); + } + }, + }; + + // Only process MAX_COMMANDS messages at a time + commands_processed += 1; + if commands_processed >= MAX_COMMANDS { + return Ok(()); + } + } + } +} + /// A [TargetDevice] is any virtual input device that emits input events #[derive(Debug)] -pub enum TargetDeviceType { +pub enum TargetDevice { + //Null + //DBus(TargetDriver), + //Keyboard(TargetDriver), + //Mouse(TargetDriver), + //XBox360(TargetDriver), + //XBoxElite(TargetDriver), + //XBoxSeries(TargetDriver), + //SteamDeck(TargetDriver), + //DualSense(TargetDriver), + //Touchscreen(TargetDriver), Null, - DBus(dbus::DBusDevice), - Keyboard(keyboard::KeyboardDevice), - Mouse(mouse::MouseDevice), - XBox360(xb360::XBox360Controller), - XBoxElite(xbox_elite::XboxEliteController), - XBoxSeries(xbox_series::XboxSeriesController), - SteamDeck(steam_deck::SteamDeckDevice), - DualSense(dualsense::DualSenseDevice), - Touchscreen(touchscreen::TouchscreenDevice), + DBus(DBusDevice), + Keyboard(KeyboardDevice), + Mouse(MouseDevice), + XBox360(XBox360Controller), + XBoxElite(XboxEliteController), + XBoxSeries(XboxSeriesController), + SteamDeck(SteamDeckDevice), + DualSense(DualSenseDevice), + Touchscreen(TouchscreenDevice), } -impl TargetDeviceType { +impl TargetDevice { /// Create a new target device from the given target device type id pub fn from_type_id(id: TargetDeviceTypeId, dbus: Connection) -> Self { match id.as_str() { @@ -183,17 +477,17 @@ impl TargetDeviceType { /// such as an input profile. E.g. "xb360", "xbox-elite", "ds5-edge" pub fn _type_identifiers(&self) -> Vec { match self { - TargetDeviceType::Null => vec!["null".try_into().unwrap()], - TargetDeviceType::DBus(_) => vec!["dbus".try_into().unwrap()], - TargetDeviceType::Keyboard(_) => vec!["keyboard".try_into().unwrap()], - TargetDeviceType::Mouse(_) => vec!["mouse".try_into().unwrap()], - TargetDeviceType::XBox360(_) => { + TargetDevice::Null => vec!["null".try_into().unwrap()], + TargetDevice::DBus(_) => vec!["dbus".try_into().unwrap()], + TargetDevice::Keyboard(_) => vec!["keyboard".try_into().unwrap()], + TargetDevice::Mouse(_) => vec!["mouse".try_into().unwrap()], + TargetDevice::XBox360(_) => { vec!["xb360".try_into().unwrap(), "gamepad".try_into().unwrap()] } - TargetDeviceType::XBoxElite(_) => vec!["xbox-elite".try_into().unwrap()], - TargetDeviceType::XBoxSeries(_) => vec!["xbox-series".try_into().unwrap()], - TargetDeviceType::SteamDeck(_) => vec!["deck".try_into().unwrap()], - TargetDeviceType::DualSense(_) => vec![ + TargetDevice::XBoxElite(_) => vec!["xbox-elite".try_into().unwrap()], + TargetDevice::XBoxSeries(_) => vec!["xbox-series".try_into().unwrap()], + TargetDevice::SteamDeck(_) => vec!["deck".try_into().unwrap()], + TargetDevice::DualSense(_) => vec![ "ds5".try_into().unwrap(), "ds5-usb".try_into().unwrap(), "ds5-bt".try_into().unwrap(), @@ -201,7 +495,7 @@ impl TargetDeviceType { "ds5-edge-usb".try_into().unwrap(), "ds5-edge-bt".try_into().unwrap(), ], - TargetDeviceType::Touchscreen(_) => vec!["touchscreen".try_into().unwrap()], + TargetDevice::Touchscreen(_) => vec!["touchscreen".try_into().unwrap()], } } @@ -210,64 +504,64 @@ impl TargetDeviceType { /// "gamepad0", "gamepad1", etc. when requesting a DBus path. pub fn dbus_device_class(&self) -> &str { match self { - TargetDeviceType::Null => "null", - TargetDeviceType::DBus(_) => "dbus", - TargetDeviceType::Keyboard(_) => "keyboard", - TargetDeviceType::Mouse(_) => "mouse", - TargetDeviceType::XBox360(_) => "gamepad", - TargetDeviceType::XBoxElite(_) => "gamepad", - TargetDeviceType::XBoxSeries(_) => "gamepad", - TargetDeviceType::SteamDeck(_) => "gamepad", - TargetDeviceType::DualSense(_) => "gamepad", - TargetDeviceType::Touchscreen(_) => "touchscreen", + TargetDevice::Null => "null", + TargetDevice::DBus(_) => "dbus", + TargetDevice::Keyboard(_) => "keyboard", + TargetDevice::Mouse(_) => "mouse", + TargetDevice::XBox360(_) => "gamepad", + TargetDevice::XBoxElite(_) => "gamepad", + TargetDevice::XBoxSeries(_) => "gamepad", + TargetDevice::SteamDeck(_) => "gamepad", + TargetDevice::DualSense(_) => "gamepad", + TargetDevice::Touchscreen(_) => "touchscreen", } } /// Returns a client channel that can be used to send events to this device pub fn client(&self) -> Option { match self { - TargetDeviceType::Null => None, - TargetDeviceType::DBus(device) => Some(device.client()), - TargetDeviceType::Keyboard(device) => Some(device.client()), - TargetDeviceType::Mouse(device) => Some(device.client()), - TargetDeviceType::XBox360(device) => Some(device.client()), - TargetDeviceType::XBoxElite(device) => Some(device.client()), - TargetDeviceType::XBoxSeries(device) => Some(device.client()), - TargetDeviceType::SteamDeck(device) => Some(device.client()), - TargetDeviceType::DualSense(device) => Some(device.client()), - TargetDeviceType::Touchscreen(device) => Some(device.client()), + TargetDevice::Null => None, + TargetDevice::DBus(device) => Some(device.client()), + TargetDevice::Keyboard(device) => Some(device.client()), + TargetDevice::Mouse(device) => Some(device.client()), + TargetDevice::XBox360(device) => Some(device.client()), + TargetDevice::XBoxElite(device) => Some(device.client()), + TargetDevice::XBoxSeries(device) => Some(device.client()), + TargetDevice::SteamDeck(device) => Some(device.client()), + TargetDevice::DualSense(device) => Some(device.client()), + TargetDevice::Touchscreen(device) => Some(device.client()), } } /// Creates a new instance of the device interface on DBus. pub async fn listen_on_dbus(&mut self, path: String) -> Result<(), Box> { match self { - TargetDeviceType::Null => Ok(()), - TargetDeviceType::DBus(device) => device.listen_on_dbus(path).await, - TargetDeviceType::Keyboard(device) => device.listen_on_dbus(path).await, - TargetDeviceType::Mouse(device) => device.listen_on_dbus(path).await, - TargetDeviceType::XBox360(device) => device.listen_on_dbus(path).await, - TargetDeviceType::XBoxElite(device) => device.listen_on_dbus(path).await, - TargetDeviceType::XBoxSeries(device) => device.listen_on_dbus(path).await, - TargetDeviceType::SteamDeck(device) => device.listen_on_dbus(path).await, - TargetDeviceType::DualSense(device) => device.listen_on_dbus(path).await, - TargetDeviceType::Touchscreen(device) => device.listen_on_dbus(path).await, + TargetDevice::Null => Ok(()), + TargetDevice::DBus(device) => device.listen_on_dbus(path).await, + TargetDevice::Keyboard(device) => device.listen_on_dbus(path).await, + TargetDevice::Mouse(device) => device.listen_on_dbus(path).await, + TargetDevice::XBox360(device) => device.listen_on_dbus(path).await, + TargetDevice::XBoxElite(device) => device.listen_on_dbus(path).await, + TargetDevice::XBoxSeries(device) => device.listen_on_dbus(path).await, + TargetDevice::SteamDeck(device) => device.listen_on_dbus(path).await, + TargetDevice::DualSense(device) => device.listen_on_dbus(path).await, + TargetDevice::Touchscreen(device) => device.listen_on_dbus(path).await, } } /// Run the target device pub async fn run(&mut self) -> Result<(), Box> { match self { - TargetDeviceType::Null => Ok(()), - TargetDeviceType::DBus(device) => device.run().await, - TargetDeviceType::Keyboard(device) => device.run().await, - TargetDeviceType::Mouse(device) => device.run().await, - TargetDeviceType::XBox360(device) => device.run().await, - TargetDeviceType::XBoxElite(device) => device.run().await, - TargetDeviceType::XBoxSeries(device) => device.run().await, - TargetDeviceType::SteamDeck(device) => device.run().await, - TargetDeviceType::DualSense(device) => device.run().await, - TargetDeviceType::Touchscreen(device) => device.run().await, + TargetDevice::Null => Ok(()), + TargetDevice::DBus(device) => device.run().await, + TargetDevice::Keyboard(device) => device.run().await, + TargetDevice::Mouse(device) => device.run().await, + TargetDevice::XBox360(device) => device.run().await, + TargetDevice::XBoxElite(device) => device.run().await, + TargetDevice::XBoxSeries(device) => device.run().await, + TargetDevice::SteamDeck(device) => device.run().await, + TargetDevice::DualSense(device) => device.run().await, + TargetDevice::Touchscreen(device) => device.run().await, } } }