diff --git a/src/input/event/native.rs b/src/input/event/native.rs index bf31f49e..fb644fa5 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,45 @@ 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, + } + } + + /// Create a new scheduled event with the given timestamp and wait time before + /// being emitted + pub fn new_with_time(event: NativeEvent, timestamp: Instant, wait_time: Duration) -> Self { + Self { + event, + scheduled_time: timestamp, + 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..a38af57a --- /dev/null +++ b/src/input/target/dbus_new.rs @@ -0,0 +1,287 @@ +use std::error::Error; + +use zbus::blocking::Connection; + +use crate::{ + dbus::interface::target::dbus::TargetDBusInterface, + input::{ + capability::{Capability, Gamepad}, + event::{ + dbus::{Action, DBusEvent}, + native::NativeEvent, + value::InputValue, + }, + }, +}; + +use super::{client::TargetDeviceClient, 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. +#[derive(Debug)] +pub struct DBusDevice { + state: State, + conn: Connection, + dbus_path: Option, +} + +impl DBusDevice { + // Create a new [DBusDevice] instance. + pub fn new(conn: Connection) -> Self { + Self { + state: State::default(), + conn, + dbus_path: None, + } + } + + /// Translate the given native event into one or more dbus events + fn translate_event(&mut self, event: NativeEvent) -> Vec { + // Check to see if this is an axis event, which requires special + // handling. + let source_cap = event.as_capability(); + let is_axis_event = match source_cap { + Capability::Gamepad(gamepad) => matches!(gamepad, Gamepad::Axis(_)), + _ => false, + }; + + let mut translated = vec![]; + let events = DBusEvent::from_native_event(event); + for mut event in events { + if !is_axis_event { + translated.push(event); + continue; + } + + // Axis input is a special case, where we need to keep track of the + // current state of the axis, and only emit events whenever the axis + // passes or falls below the defined threshold. + let include_event = match event.action { + Action::Left => { + if self.state.pressed_left && event.as_f64() < AXIS_THRESHOLD { + event.value = InputValue::Float(0.0); + self.state.pressed_left = false; + true + } else if !self.state.pressed_left && event.as_f64() > AXIS_THRESHOLD { + event.value = InputValue::Float(1.0); + self.state.pressed_left = true; + true + } else { + false + } + } + Action::Right => { + if self.state.pressed_right && event.as_f64() < AXIS_THRESHOLD { + event.value = InputValue::Float(0.0); + self.state.pressed_right = false; + true + } else if !self.state.pressed_right && event.as_f64() > AXIS_THRESHOLD { + event.value = InputValue::Float(1.0); + self.state.pressed_right = true; + true + } else { + false + } + } + Action::Up => { + if self.state.pressed_up && event.as_f64() < AXIS_THRESHOLD { + event.value = InputValue::Float(0.0); + self.state.pressed_up = false; + true + } else if !self.state.pressed_up && event.as_f64() > AXIS_THRESHOLD { + event.value = InputValue::Float(1.0); + self.state.pressed_up = true; + true + } else { + false + } + } + Action::Down => { + if self.state.pressed_down && event.as_f64() < AXIS_THRESHOLD { + event.value = InputValue::Float(0.0); + self.state.pressed_down = false; + true + } else if !self.state.pressed_down && event.as_f64() > AXIS_THRESHOLD { + event.value = InputValue::Float(1.0); + self.state.pressed_down = true; + true + } else { + false + } + } + _ => true, + }; + + if include_event { + translated.push(event); + } + } + + translated + } + + /// Writes the given event to DBus + fn write_dbus_event(&self, event: DBusEvent) -> Result<(), Box> { + // Only send valid events + let valid = !matches!(event.action, Action::None); + if !valid { + return Ok(()); + } + + // DBus events can only be written if there is a DBus path reference. + let Some(path) = self.dbus_path.clone() else { + return Err("No dbus path exists to send events to".into()); + }; + + let conn = self.conn.clone(); + // Get the object instance at the given path so we can send DBus signal + // updates + let iface_ref = conn + .object_server() + .interface::<_, TargetDBusInterface>(path.as_str())?; + + // Send the input event signal based on the type of value + tokio::task::spawn(async move { + let result = match event.value { + InputValue::Bool(value) => { + let value = match value { + true => 1.0, + false => 0.0, + }; + TargetDBusInterface::input_event( + iface_ref.signal_context(), + event.action.as_string(), + value, + ) + .await + } + InputValue::Float(value) => { + TargetDBusInterface::input_event( + iface_ref.signal_context(), + event.action.as_string(), + value, + ) + .await + } + InputValue::Touch { + index, + is_touching, + pressure, + x, + y, + } => { + // Send the input event signal + TargetDBusInterface::touch_event( + iface_ref.signal_context(), + event.action.as_string(), + index as u32, + is_touching, + pressure.unwrap_or(1.0), + x.unwrap_or(0.0), + y.unwrap_or(0.0), + ) + .await + } + _ => Ok(()), + }; + if let Err(e) = result { + log::error!("Failed to send event: {e:?}"); + } + }); + + Ok(()) + } +} + +impl TargetInputDevice for DBusDevice { + fn start_dbus_interface( + &mut self, + dbus: Connection, + path: String, + _client: TargetDeviceClient, + ) { + log::debug!("Starting dbus interface: {path}"); + let iface = TargetDBusInterface::new(); + if let Err(e) = dbus.object_server().at(path.clone(), iface) { + log::debug!("Failed to start dbus interface {path}: {e:?}"); + } else { + log::debug!("Started dbus interface on {path}"); + self.dbus_path = Some(path); + }; + } + + fn write_event( + &mut self, + event: crate::input::event::native::NativeEvent, + ) -> Result<(), super::InputError> { + log::trace!("Got event to emit: {:?}", event); + let dbus_events = self.translate_event(event); + for dbus_event in dbus_events { + log::trace!("Writing DBus event: {dbus_event:?}"); + self.write_dbus_event(dbus_event)?; + } + + Ok(()) + } + + fn get_capabilities( + &self, + ) -> Result, super::InputError> { + let capabilities = vec![ + Capability::DBus(Action::Guide), + Capability::DBus(Action::Quick), + Capability::DBus(Action::Quick2), + Capability::DBus(Action::Context), + Capability::DBus(Action::Option), + Capability::DBus(Action::Select), + Capability::DBus(Action::Accept), + Capability::DBus(Action::Back), + Capability::DBus(Action::ActOn), + Capability::DBus(Action::Left), + Capability::DBus(Action::Right), + Capability::DBus(Action::Up), + Capability::DBus(Action::Down), + Capability::DBus(Action::L1), + Capability::DBus(Action::L2), + Capability::DBus(Action::L3), + Capability::DBus(Action::R1), + Capability::DBus(Action::R2), + Capability::DBus(Action::R3), + Capability::DBus(Action::VolumeUp), + Capability::DBus(Action::VolumeDown), + Capability::DBus(Action::VolumeMute), + Capability::DBus(Action::Keyboard), + Capability::DBus(Action::Screenshot), + Capability::DBus(Action::Touch), + ]; + + Ok(capabilities) + } + + fn stop_dbus_interface(&mut self, dbus: Connection, path: String) { + log::debug!("Stopping dbus interface for {path}"); + let result = dbus + .object_server() + .remove::(path.clone()); + if let Err(e) = result { + log::error!("Failed to stop dbus interface {path}: {e:?}"); + } else { + log::debug!("Stopped dbus interface for {path}"); + }; + } +} + +impl TargetOutputDevice for DBusDevice {} diff --git a/src/input/target/dualsense_new.rs b/src/input/target/dualsense_new.rs new file mode 100644 index 00000000..275d13e9 --- /dev/null +++ b/src/input/target/dualsense_new.rs @@ -0,0 +1,1111 @@ +use std::{cmp::Ordering, error::Error, fmt::Debug, fs::File}; + +use packed_struct::prelude::*; +use rand::Rng; +use uhid_virt::{Bus, CreateParams, StreamError, UHIDDevice}; + +use crate::{ + drivers::dualsense::{ + driver::{ + DS5_ACC_RES_PER_G, DS5_EDGE_NAME, DS5_EDGE_PID, DS5_EDGE_VERSION, DS5_EDGE_VID, + DS5_NAME, DS5_PID, DS5_TOUCHPAD_HEIGHT, DS5_TOUCHPAD_WIDTH, DS5_VERSION, DS5_VID, + OUTPUT_REPORT_BT_SIZE, OUTPUT_REPORT_USB_SHORT_SIZE, OUTPUT_REPORT_USB_SIZE, + STICK_X_MAX, STICK_X_MIN, STICK_Y_MAX, STICK_Y_MIN, TRIGGER_MAX, + }, + hid_report::{ + Direction, PackedInputDataReport, USBPackedInputDataReport, UsbPackedOutputReport, + UsbPackedOutputReportShort, + }, + report_descriptor::{ + DS_BT_DESCRIPTOR, DS_EDGE_BT_DESCRIPTOR, DS_EDGE_USB_DESCRIPTOR, DS_USB_DESCRIPTOR, + }, + }, + input::{ + capability::{ + Capability, Gamepad, GamepadAxis, GamepadButton, GamepadTrigger, Touch, TouchButton, + Touchpad, + }, + event::{ + native::{NativeEvent, ScheduledNativeEvent}, + value::InputValue, + }, + output_capability::{OutputCapability, LED}, + output_event::OutputEvent, + }, +}; + +use super::{InputError, OutputError, TargetInputDevice, TargetOutputDevice}; + +/// The type of DualSense device to emulate. Currently two models are supported: +/// DualSense and DualSense Edge. +#[derive(Debug, Copy, Clone, PartialEq)] +pub enum ModelType { + Normal, + Edge, +} + +/// The DualSense device can be emulated using either the USB or Bluetooth buses +#[derive(Debug, Copy, Clone, PartialEq)] +pub enum BusType { + Usb, + Bluetooth, +} + +/// The [DualSenseHardware] defines the kind of DualSense controller to emulate +#[derive(Debug, Copy, Clone, PartialEq)] +pub struct DualSenseHardware { + model: ModelType, + bus_type: BusType, + mac_addr: [u8; 6], +} + +impl DualSenseHardware { + pub fn new(model: ModelType, bus_type: BusType) -> Self { + // "e8:47:3a:d6:e7:74" + //let mac_addr = [0x74, 0xe7, 0xd6, 0x3a, 0x47, 0xe8]; + let mut rng = rand::thread_rng(); + let mac_addr: [u8; 6] = [ + rng.gen(), + rng.gen(), + rng.gen(), + rng.gen(), + rng.gen(), + rng.gen(), + ]; + log::debug!( + "Creating new DualSense Edge device using MAC Address: {:?}", + mac_addr + ); + + Self { + model, + bus_type, + mac_addr, + } + } +} + +impl Default for DualSenseHardware { + fn default() -> Self { + let mut rng = rand::thread_rng(); + let mac_addr: [u8; 6] = [ + rng.gen(), + rng.gen(), + rng.gen(), + rng.gen(), + rng.gen(), + rng.gen(), + ]; + Self { + model: ModelType::Normal, + bus_type: BusType::Usb, + mac_addr, + } + } +} + +/// The [DualSenseDevice] is a target input device implementation that emulates +/// a Playstation DualSense controller using uhid. +pub struct DualSenseDevice { + device: UHIDDevice, + state: PackedInputDataReport, + timestamp: u8, + hardware: DualSenseHardware, +} + +impl DualSenseDevice { + pub fn new(hardware: DualSenseHardware) -> Result> { + let device = DualSenseDevice::create_virtual_device(&hardware)?; + Ok(Self { + device, + state: PackedInputDataReport::Usb(USBPackedInputDataReport::new()), + timestamp: 0, + hardware, + }) + } + + /// Create the virtual device to emulate + fn create_virtual_device( + hardware: &DualSenseHardware, + ) -> Result, Box> { + let device = UHIDDevice::create(CreateParams { + name: match hardware.model { + ModelType::Edge => String::from(DS5_EDGE_NAME), + ModelType::Normal => String::from(DS5_NAME), + }, + phys: String::from(""), + uniq: format!( + "{:02x}:{:02x}:{:02x}:{:02x}:{:02x}:{:02x}", + hardware.mac_addr[5], + hardware.mac_addr[4], + hardware.mac_addr[3], + hardware.mac_addr[2], + hardware.mac_addr[1], + hardware.mac_addr[0], + ), + bus: match hardware.bus_type { + BusType::Bluetooth => Bus::BLUETOOTH, + BusType::Usb => Bus::USB, + }, + vendor: match hardware.model { + ModelType::Edge => DS5_EDGE_VID as u32, + ModelType::Normal => DS5_VID as u32, + }, + product: match hardware.model { + ModelType::Edge => DS5_EDGE_PID as u32, + ModelType::Normal => DS5_PID as u32, + }, + version: match hardware.model { + ModelType::Edge => DS5_EDGE_VERSION as u32, + ModelType::Normal => DS5_VERSION as u32, + }, + country: 0, + rd_data: match hardware.model { + ModelType::Edge => match hardware.bus_type { + BusType::Bluetooth => DS_EDGE_BT_DESCRIPTOR.to_vec(), + BusType::Usb => DS_EDGE_USB_DESCRIPTOR.to_vec(), + }, + ModelType::Normal => match hardware.bus_type { + BusType::Bluetooth => DS_BT_DESCRIPTOR.to_vec(), + BusType::Usb => DS_USB_DESCRIPTOR.to_vec(), + }, + }, + })?; + + Ok(device) + } + + /// Write the current device state to the device + fn write_state(&mut self) -> Result<(), Box> { + match self.state { + PackedInputDataReport::Usb(state) => { + let data = state.pack()?; + + // Write the state to the virtual HID + if let Err(e) = self.device.write(&data) { + let err = format!("Failed to write input data report: {:?}", e); + return Err(err.into()); + } + } + PackedInputDataReport::Bluetooth(state) => { + let data = state.pack()?; + + // Write the state to the virtual HID + if let Err(e) = self.device.write(&data) { + let err = format!("Failed to write input data report: {:?}", e); + return Err(err.into()); + } + } + }; + + Ok(()) + } + + /// Update the internal controller state when events are emitted. + fn update_state(&mut self, event: NativeEvent) { + let value = event.get_value(); + let capability = event.as_capability(); + let state = self.state.state_mut(); + match capability { + Capability::None => (), + Capability::NotImplemented => (), + Capability::Sync => (), + Capability::Gamepad(gamepad) => match gamepad { + Gamepad::Button(btn) => match btn { + GamepadButton::South => state.cross = event.pressed(), + GamepadButton::East => state.circle = event.pressed(), + GamepadButton::North => state.square = event.pressed(), + GamepadButton::West => state.triangle = event.pressed(), + GamepadButton::Start => state.options = event.pressed(), + GamepadButton::Select => state.create = event.pressed(), + GamepadButton::Guide => state.ps = event.pressed(), + GamepadButton::QuickAccess => (), + GamepadButton::DPadUp => match state.dpad { + Direction::North => { + if !event.pressed() { + state.dpad = Direction::None + } + } + Direction::NorthEast => { + if !event.pressed() { + state.dpad = Direction::East + } + } + Direction::East => { + if event.pressed() { + state.dpad = Direction::NorthEast + } + } + Direction::SouthEast => { + if event.pressed() { + state.dpad = Direction::NorthEast + } + } + Direction::South => { + if event.pressed() { + state.dpad = Direction::North + } + } + Direction::SouthWest => { + if event.pressed() { + state.dpad = Direction::NorthWest + } + } + Direction::West => { + if event.pressed() { + state.dpad = Direction::NorthWest + } + } + Direction::NorthWest => { + if !event.pressed() { + state.dpad = Direction::West + } + } + Direction::None => { + if event.pressed() { + state.dpad = Direction::North + } + } + }, + GamepadButton::DPadDown => match state.dpad { + Direction::North => { + if event.pressed() { + state.dpad = Direction::South + } + } + Direction::NorthEast => { + if event.pressed() { + state.dpad = Direction::SouthEast + } + } + Direction::East => { + if event.pressed() { + state.dpad = Direction::SouthEast + } + } + Direction::SouthEast => { + if !event.pressed() { + state.dpad = Direction::East + } + } + Direction::South => { + if !event.pressed() { + state.dpad = Direction::None + } + } + Direction::SouthWest => { + if !event.pressed() { + state.dpad = Direction::West + } + } + Direction::West => { + if event.pressed() { + state.dpad = Direction::SouthWest + } + } + Direction::NorthWest => { + if event.pressed() { + state.dpad = Direction::SouthWest + } + } + Direction::None => { + if event.pressed() { + state.dpad = Direction::South + } + } + }, + GamepadButton::DPadLeft => match state.dpad { + Direction::North => { + if event.pressed() { + state.dpad = Direction::NorthWest + } + } + Direction::NorthEast => { + if event.pressed() { + state.dpad = Direction::NorthWest + } + } + Direction::East => { + if event.pressed() { + state.dpad = Direction::West + } + } + Direction::SouthEast => { + if event.pressed() { + state.dpad = Direction::SouthWest + } + } + Direction::South => { + if event.pressed() { + state.dpad = Direction::SouthWest + } + } + Direction::SouthWest => { + if !event.pressed() { + state.dpad = Direction::South + } + } + Direction::West => { + if !event.pressed() { + state.dpad = Direction::None + } + } + Direction::NorthWest => { + if !event.pressed() { + state.dpad = Direction::North + } + } + Direction::None => { + if event.pressed() { + state.dpad = Direction::West + } + } + }, + GamepadButton::DPadRight => match state.dpad { + Direction::North => { + if event.pressed() { + state.dpad = Direction::NorthEast + } + } + Direction::NorthEast => { + if !event.pressed() { + state.dpad = Direction::North + } + } + Direction::East => { + if !event.pressed() { + state.dpad = Direction::None + } + } + Direction::SouthEast => { + if !event.pressed() { + state.dpad = Direction::South + } + } + Direction::South => { + if event.pressed() { + state.dpad = Direction::SouthEast + } + } + Direction::SouthWest => { + if event.pressed() { + state.dpad = Direction::SouthEast + } + } + Direction::West => { + if event.pressed() { + state.dpad = Direction::East + } + } + Direction::NorthWest => { + if event.pressed() { + state.dpad = Direction::NorthEast + } + } + Direction::None => { + if event.pressed() { + state.dpad = Direction::East + } + } + }, + GamepadButton::LeftBumper => state.l1 = event.pressed(), + GamepadButton::LeftTrigger => state.l2 = event.pressed(), + GamepadButton::LeftPaddle1 => state.left_fn = event.pressed(), + GamepadButton::LeftPaddle2 => state.left_paddle = event.pressed(), + GamepadButton::LeftStick => state.l3 = event.pressed(), + GamepadButton::LeftStickTouch => (), + GamepadButton::RightBumper => state.r1 = event.pressed(), + GamepadButton::RightTrigger => state.r2 = event.pressed(), + GamepadButton::RightPaddle1 => state.right_fn = event.pressed(), + GamepadButton::RightPaddle2 => state.right_paddle = event.pressed(), + GamepadButton::RightStick => state.r3 = event.pressed(), + GamepadButton::RightStickTouch => (), + GamepadButton::LeftPaddle3 => (), + GamepadButton::RightPaddle3 => (), + _ => (), + }, + Gamepad::Axis(axis) => match axis { + GamepadAxis::LeftStick => { + if let InputValue::Vector2 { x, y } = value { + if let Some(x) = x { + let value = denormalize_signed_value(x, STICK_X_MIN, STICK_X_MAX); + state.joystick_l_x = value + } + if let Some(y) = y { + let value = denormalize_signed_value(y, STICK_Y_MIN, STICK_Y_MAX); + state.joystick_l_y = value + } + } + } + GamepadAxis::RightStick => { + if let InputValue::Vector2 { x, y } = value { + if let Some(x) = x { + let value = denormalize_signed_value(x, STICK_X_MIN, STICK_X_MAX); + state.joystick_r_x = value + } + if let Some(y) = y { + let value = denormalize_signed_value(y, STICK_Y_MIN, STICK_Y_MAX); + state.joystick_r_y = value + } + } + } + GamepadAxis::Hat0 => { + if let InputValue::Vector2 { x, y } = value { + if let Some(x) = x { + let value = denormalize_signed_value(x, -1.0, 1.0); + match value.cmp(&0) { + Ordering::Less => match state.dpad { + Direction::North => state.dpad = Direction::NorthWest, + Direction::South => state.dpad = Direction::SouthWest, + _ => state.dpad = Direction::West, + }, + Ordering::Equal => match state.dpad { + Direction::NorthWest => state.dpad = Direction::North, + Direction::SouthWest => state.dpad = Direction::South, + Direction::NorthEast => state.dpad = Direction::North, + Direction::SouthEast => state.dpad = Direction::South, + Direction::East => state.dpad = Direction::None, + Direction::West => state.dpad = Direction::None, + _ => (), + }, + Ordering::Greater => match state.dpad { + Direction::North => state.dpad = Direction::NorthEast, + Direction::South => state.dpad = Direction::SouthEast, + _ => state.dpad = Direction::East, + }, + } + } + if let Some(y) = y { + let value = denormalize_signed_value(y, -1.0, 1.0); + match value.cmp(&0) { + Ordering::Less => match state.dpad { + Direction::East => state.dpad = Direction::NorthEast, + Direction::West => state.dpad = Direction::NorthWest, + _ => state.dpad = Direction::North, + }, + Ordering::Equal => match state.dpad { + Direction::NorthWest => state.dpad = Direction::West, + Direction::SouthWest => state.dpad = Direction::West, + Direction::NorthEast => state.dpad = Direction::East, + Direction::SouthEast => state.dpad = Direction::East, + Direction::North => state.dpad = Direction::None, + Direction::South => state.dpad = Direction::None, + _ => (), + }, + Ordering::Greater => match state.dpad { + Direction::East => state.dpad = Direction::SouthEast, + Direction::West => state.dpad = Direction::SouthWest, + _ => state.dpad = Direction::South, + }, + } + } + } + } + GamepadAxis::Hat1 => (), + GamepadAxis::Hat2 => (), + GamepadAxis::Hat3 => (), + }, + Gamepad::Trigger(trigger) => match trigger { + GamepadTrigger::LeftTrigger => { + if let InputValue::Float(normal_value) = value { + let value = denormalize_unsigned_value(normal_value, TRIGGER_MAX); + state.l2_trigger = value + } + } + GamepadTrigger::LeftTouchpadForce => (), + GamepadTrigger::LeftStickForce => (), + GamepadTrigger::RightTrigger => { + if let InputValue::Float(normal_value) = value { + let value = denormalize_unsigned_value(normal_value, TRIGGER_MAX); + state.r2_trigger = value + } + } + GamepadTrigger::RightTouchpadForce => (), + GamepadTrigger::RightStickForce => (), + }, + Gamepad::Accelerometer => { + if let InputValue::Vector3 { x, y, z } = value { + if let Some(x) = x { + state.accel_x = Integer::from_primitive(denormalize_accel_value(x)) + } + if let Some(y) = y { + state.accel_y = Integer::from_primitive(denormalize_accel_value(y)) + } + if let Some(z) = z { + state.accel_z = Integer::from_primitive(denormalize_accel_value(z)) + } + } + } + Gamepad::Gyro => { + if let InputValue::Vector3 { x, y, z } = value { + if let Some(x) = x { + state.gyro_x = Integer::from_primitive(denormalize_gyro_value(x)); + } + if let Some(y) = y { + state.gyro_y = Integer::from_primitive(denormalize_gyro_value(y)) + } + if let Some(z) = z { + state.gyro_z = Integer::from_primitive(denormalize_gyro_value(z)) + } + } + } + }, + Capability::Touchpad(touch) => { + match touch { + Touchpad::CenterPad(touch_event) => { + match touch_event { + Touch::Motion => { + if let InputValue::Touch { + index, + is_touching, + pressure: _, + x, + y, + } = value + { + // Check to see if this is the start of any touch + let was_touching = state.touch_data.has_touches(); + + let idx = index as usize; + // TouchData has an array size of 2, ignore more than 2 touch events. + if idx > 1 { + return; + } + if let Some(x) = x { + state.touch_data.touch_finger_data[idx] + .set_x(denormalize_touch_value(x, DS5_TOUCHPAD_WIDTH)); + } + if let Some(y) = y { + state.touch_data.touch_finger_data[idx] + .set_y(denormalize_touch_value(y, DS5_TOUCHPAD_HEIGHT)); + } + + if is_touching { + state.touch_data.touch_finger_data[idx].context = 127; + } else { + state.touch_data.touch_finger_data[idx].context = 128; + } + + // Reset the timestamp back to zero when all touches + // have completed + let now_touching = state.touch_data.has_touches(); + if was_touching && !now_touching { + self.timestamp = 0; + } + } + } + Touch::Button(button) => match button { + TouchButton::Touch => (), + TouchButton::Press => state.touchpad = event.pressed(), + }, + } + } + // Not supported + Touchpad::RightPad(_) => {} + + Touchpad::LeftPad(_) => {} + } + } + Capability::Mouse(_) => (), + Capability::Keyboard(_) => (), + Capability::DBus(_) => (), + Capability::Touchscreen(_) => (), + }; + } + + /// Handle [OutputEvent::Output] events from the HIDRAW device. These are + /// events which should be forwarded back to source devices. + fn handle_output(&mut self, data: Vec) -> Result, Box> { + // Validate the output report size + let _expected_report_size = match self.hardware.bus_type { + BusType::Bluetooth => OUTPUT_REPORT_BT_SIZE, + BusType::Usb => OUTPUT_REPORT_USB_SIZE, + }; + + // The first byte should be the report id + let Some(report_id) = data.first() else { + log::warn!("Received empty output report."); + return Ok(vec![]); + }; + + log::debug!("Got output report with ID: {report_id}"); + + match *report_id { + OUTPUT_REPORT_USB => { + log::debug!("Received USB output report with length: {}", data.len()); + let state = match data.len() { + OUTPUT_REPORT_USB_SIZE => { + let buf: [u8; OUTPUT_REPORT_USB_SIZE] = data.try_into().unwrap(); + let report = UsbPackedOutputReport::unpack(&buf)?; + report.state + } + OUTPUT_REPORT_USB_SHORT_SIZE => { + let buf: [u8; OUTPUT_REPORT_USB_SHORT_SIZE] = data.try_into().unwrap(); + let report = UsbPackedOutputReportShort::unpack(&buf)?; + + // NOTE: Hack for supporting Steam Input rumble + let mut state = report.state; + if !state.allow_audio_control + && !state.allow_mic_volume + && !state.allow_speaker_volume + && !state.allow_headphone_volume + && !state.allow_left_trigger_ffb + && !state.allow_right_trigger_ffb + && !state.use_rumble_not_haptics + && !state.enable_rumble_emulation + { + state.use_rumble_not_haptics = true; + } + state + } + _ => { + log::warn!("Failed to unpack output report. Expected size {OUTPUT_REPORT_USB_SIZE} or {OUTPUT_REPORT_USB_SHORT_SIZE}, got {}.", data.len()); + return Ok(vec![]); + } + }; + + log::trace!("{}", state); + + // Send the output report to the composite device so it can + // be processed by source devices. + let event = OutputEvent::DualSense(state); + return Ok(vec![event]); + } + OUTPUT_REPORT_BT => { + log::debug!( + "Received Bluetooth output report with length: {}", + data.len() + ); + // + } + _ => { + log::debug!("Unknown output report: {report_id}"); + } + } + + Ok(vec![]) + } + + /// Handle [OutputEvent::GetReport] events from the HIDRAW device + fn handle_get_report( + &mut self, + id: u32, + report_number: u8, + _report_type: uhid_virt::ReportType, + ) -> Result<(), Box> { + // Handle report pairing requests + let data = match report_number { + // Pairing information report + FEATURE_REPORT_PAIRING_INFO => { + log::debug!("Got report pairing report request"); + // TODO: Can we define this somewhere as a const? + let data = vec![ + FEATURE_REPORT_PAIRING_INFO, + self.hardware.mac_addr[0], + self.hardware.mac_addr[1], + self.hardware.mac_addr[2], + self.hardware.mac_addr[3], + self.hardware.mac_addr[4], + self.hardware.mac_addr[5], + 0x08, + 0x25, + 0x00, + 0x1e, + 0x00, + 0xee, + 0x74, + 0xd0, + 0xbc, + 0x00, + 0x00, + 0x00, + 0x00, + ]; + + // If this is a bluetooth gamepad, include the crc + if self.hardware.bus_type == BusType::Bluetooth { + // TODO: Handle bluetooth CRC32 + } + + data + } + // Firmware information report + FEATURE_REPORT_FIRMWARE_INFO => { + log::debug!("Got report firmware info request"); + // TODO: Can we define this somewhere as a const? + let data = vec![ + FEATURE_REPORT_FIRMWARE_INFO, + 0x4a, + 0x75, + 0x6e, + 0x20, + 0x31, + 0x39, + 0x20, + 0x32, + 0x30, + 0x32, + 0x33, + 0x31, + 0x34, + 0x3a, + 0x34, + 0x37, + 0x3a, + 0x33, + 0x34, + 0x03, + 0x00, + 0x44, + 0x00, + 0x08, + 0x02, + 0x00, + 0x01, + 0x36, + 0x00, + 0x00, + 0x01, + 0xc1, + 0xc8, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x54, + 0x01, + 0x00, + 0x00, + 0x14, + 0x00, + 0x00, + 0x00, + 0x0b, + 0x00, + 0x01, + 0x00, + 0x06, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + ]; + + // If this is a bluetooth gamepad, include the crc + if self.hardware.bus_type == BusType::Bluetooth { + // TODO: Handle bluetooth CRC32 + } + + data + } + // Calibration report + FEATURE_REPORT_CALIBRATION => { + log::debug!("Got report request for calibration"); + // TODO: Can we define this somewhere as a const? + let data = vec![ + FEATURE_REPORT_CALIBRATION, + 0xff, + 0xfc, + 0xff, + 0xfe, + 0xff, + 0x83, + 0x22, + 0x78, + 0xdd, + 0x92, + 0x22, + 0x5f, + 0xdd, + 0x95, + 0x22, + 0x6d, + 0xdd, + 0x1c, + 0x02, + 0x1c, + 0x02, + 0xf2, + 0x1f, + 0xed, + 0xdf, + 0xe3, + 0x20, + 0xda, + 0xe0, + 0xee, + 0x1f, + 0xdf, + 0xdf, + 0x0b, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + ]; + + // If this is a bluetooth gamepad, include the crc + if self.hardware.bus_type == BusType::Bluetooth { + // TODO: Handle bluetooth CRC32 + } + + data + } + _ => { + let err = format!("Unknown get report request with report number: {report_number}"); + return Err(err.into()); + } + }; + + // Write the report reply to the HIDRAW device + if let Err(e) = self.device.write_get_report_reply(id, 0, data) { + log::warn!("Failed to write get report reply: {:?}", e); + return Err(e.to_string().into()); + } + + Ok(()) + } +} + +impl TargetInputDevice for DualSenseDevice { + fn write_event(&mut self, event: NativeEvent) -> Result<(), InputError> { + self.update_state(event); + + // Check if the timestamp needs to be updated + if self.state.state().touch_data.has_touches() { + self.timestamp = self.timestamp.wrapping_add(3); // TODO: num? + self.state.state_mut().touch_data.timestamp = self.timestamp; + } + + Ok(()) + } + + fn get_capabilities(&self) -> Result, InputError> { + Ok(vec![ + Capability::Gamepad(Gamepad::Accelerometer), + Capability::Gamepad(Gamepad::Axis(GamepadAxis::LeftStick)), + Capability::Gamepad(Gamepad::Axis(GamepadAxis::RightStick)), + Capability::Gamepad(Gamepad::Button(GamepadButton::DPadDown)), + Capability::Gamepad(Gamepad::Button(GamepadButton::DPadLeft)), + Capability::Gamepad(Gamepad::Button(GamepadButton::DPadRight)), + Capability::Gamepad(Gamepad::Button(GamepadButton::DPadUp)), + Capability::Gamepad(Gamepad::Button(GamepadButton::East)), + Capability::Gamepad(Gamepad::Button(GamepadButton::Guide)), + Capability::Gamepad(Gamepad::Button(GamepadButton::LeftBumper)), + Capability::Gamepad(Gamepad::Button(GamepadButton::LeftPaddle1)), + Capability::Gamepad(Gamepad::Button(GamepadButton::LeftPaddle2)), + Capability::Gamepad(Gamepad::Button(GamepadButton::LeftStick)), + Capability::Gamepad(Gamepad::Button(GamepadButton::LeftTrigger)), + Capability::Gamepad(Gamepad::Button(GamepadButton::North)), + Capability::Gamepad(Gamepad::Button(GamepadButton::RightBumper)), + Capability::Gamepad(Gamepad::Button(GamepadButton::RightPaddle1)), + Capability::Gamepad(Gamepad::Button(GamepadButton::RightPaddle2)), + Capability::Gamepad(Gamepad::Button(GamepadButton::RightStick)), + Capability::Gamepad(Gamepad::Button(GamepadButton::RightTrigger)), + Capability::Gamepad(Gamepad::Button(GamepadButton::Select)), + Capability::Gamepad(Gamepad::Button(GamepadButton::South)), + Capability::Gamepad(Gamepad::Button(GamepadButton::Start)), + Capability::Gamepad(Gamepad::Button(GamepadButton::West)), + Capability::Gamepad(Gamepad::Gyro), + Capability::Gamepad(Gamepad::Trigger(GamepadTrigger::LeftTrigger)), + Capability::Gamepad(Gamepad::Trigger(GamepadTrigger::RightTrigger)), + Capability::Touchpad(Touchpad::CenterPad(Touch::Button(TouchButton::Press))), + Capability::Touchpad(Touchpad::CenterPad(Touch::Button(TouchButton::Touch))), + Capability::Touchpad(Touchpad::CenterPad(Touch::Motion)), + ]) + } + + fn scheduled_events(&mut self) -> Option> { + None + } + + fn stop(&mut self) -> Result<(), InputError> { + let _ = self.device.destroy(); + Ok(()) + } +} + +impl TargetOutputDevice for DualSenseDevice { + /// Handle reading from the device and processing input events from source + /// devices. + /// https://www.kernel.org/doc/html/latest/hid/uhid.html#read + fn poll(&mut self) -> Result, OutputError> { + // Write the current state + self.write_state()?; + + // Read output events + let event = match self.device.read() { + Ok(event) => event, + Err(err) => match err { + StreamError::Io(_e) => { + return Ok(vec![]); + } + StreamError::UnknownEventType(e) => { + log::debug!("Unknown event type: {:?}", e); + return Ok(vec![]); + } + }, + }; + + match event { + // This is sent when the HID device is started. Consider this as an answer to + // UHID_CREATE. This is always the first event that is sent. + uhid_virt::OutputEvent::Start { dev_flags: _ } => { + log::debug!("Start event received"); + Ok(vec![]) + } + // This is sent when the HID device is stopped. Consider this as an answer to + // UHID_DESTROY. + uhid_virt::OutputEvent::Stop => { + log::debug!("Stop event received"); + Ok(vec![]) + } + // This is sent when the HID device is opened. That is, the data that the HID + // device provides is read by some other process. You may ignore this event but + // it is useful for power-management. As long as you haven't received this event + // there is actually no other process that reads your data so there is no need to + // send UHID_INPUT events to the kernel. + uhid_virt::OutputEvent::Open => { + log::debug!("Open event received"); + Ok(vec![]) + } + // This is sent when there are no more processes which read the HID data. It is + // the counterpart of UHID_OPEN and you may as well ignore this event. + uhid_virt::OutputEvent::Close => { + log::debug!("Close event received"); + Ok(vec![]) + } + // This is sent if the HID device driver wants to send raw data to the I/O + // device. You should read the payload and forward it to the device. + uhid_virt::OutputEvent::Output { data } => { + log::trace!("Got output data: {:?}", data); + let result = self.handle_output(data); + match result { + Ok(events) => Ok(events), + Err(e) => { + let err = format!("Failed process output event: {:?}", e); + Err(err.into()) + } + } + } + // This event is sent if the kernel driver wants to perform a GET_REPORT request + // on the control channel as described in the HID specs. The report-type and + // report-number are available in the payload. + // The kernel serializes GET_REPORT requests so there will never be two in + // parallel. However, if you fail to respond with a UHID_GET_REPORT_REPLY, the + // request might silently time out. + // Once you read a GET_REPORT request, you shall forward it to the HID device and + // remember the "id" field in the payload. Once your HID device responds to the + // GET_REPORT (or if it fails), you must send a UHID_GET_REPORT_REPLY to the + // kernel with the exact same "id" as in the request. If the request already + // timed out, the kernel will ignore the response silently. The "id" field is + // never re-used, so conflicts cannot happen. + uhid_virt::OutputEvent::GetReport { + id, + report_number, + report_type, + } => { + log::trace!( + "Received GetReport event: id: {id}, num: {report_number}, type: {:?}", + report_type + ); + let result = self.handle_get_report(id, report_number, report_type); + if let Err(e) = result { + let err = format!("Failed to process GetReport event: {:?}", e); + return Err(err.into()); + } + Ok(vec![]) + } + // This is the SET_REPORT equivalent of UHID_GET_REPORT. On receipt, you shall + // send a SET_REPORT request to your HID device. Once it replies, you must tell + // the kernel about it via UHID_SET_REPORT_REPLY. + // The same restrictions as for UHID_GET_REPORT apply. + uhid_virt::OutputEvent::SetReport { + id, + report_number, + report_type, + data, + } => { + log::trace!("Received SetReport event: id: {id}, num: {report_number}, type: {:?}, data: {:?}", report_type, data); + Ok(vec![]) + } + } + } + + fn get_output_capabilities(&self) -> Result, OutputError> { + Ok(vec![ + OutputCapability::ForceFeedback, + OutputCapability::LED(LED::Color), + ]) + } +} + +impl Debug for DualSenseDevice { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("DualSenseDevice") + .field("state", &self.state) + .field("timestamp", &self.timestamp) + .field("hardware", &self.hardware) + .finish() + } +} + +/// Convert the given normalized value between -1.0 - 1.0 to the real value +/// based on the given minimum and maximum axis range. Playstation gamepads +/// use a range from 0-255, with 127 being the "nuetral" point. +fn denormalize_signed_value(normal_value: f64, min: f64, max: f64) -> u8 { + let mid = (max + min) / 2.0; + let normal_value_abs = normal_value.abs(); + if normal_value >= 0.0 { + let maximum = max - mid; + let value = normal_value * maximum + mid; + value as u8 + } else { + let minimum = min - mid; + let value = normal_value_abs * minimum + mid; + value as u8 + } +} + +/// De-normalizes the given value from 0.0 - 1.0 into a real value based on +/// the maximum axis range. +fn denormalize_unsigned_value(normal_value: f64, max: f64) -> u8 { + (normal_value * max).round() as u8 +} + +/// De-normalizes the given value from 0.0 - 1.0 into a real value based on +/// the maximum axis range. +fn denormalize_touch_value(normal_value: f64, max: f64) -> u16 { + (normal_value * max).round() as u16 +} + +/// De-normalizes the given value in meters per second into a real value that +/// the DS5 controller understands. +/// DualSense accelerometer values are measured in [DS5_ACC_RES_PER_G] +/// units of G acceleration (1G == 9.8m/s). InputPlumber accelerometer +/// values are measured in units of meters per second. To denormalize +/// the value, it needs to be converted into G units (by dividing by 9.8), +/// then multiplying that value by the [DS5_ACC_RES_PER_G]. +fn denormalize_accel_value(value_meters_sec: f64) -> i16 { + let value_g = value_meters_sec / 9.8; + let value = value_g * DS5_ACC_RES_PER_G as f64; + value as i16 +} + +/// DualSense gyro values are measured in units of degrees per second. +/// InputPlumber gyro values are also measured in degrees per second. +fn denormalize_gyro_value(value_degrees_sec: f64) -> i16 { + let value = value_degrees_sec; + value as i16 +} diff --git a/src/input/target/keyboard_new.rs b/src/input/target/keyboard_new.rs new file mode 100644 index 00000000..e4a95f73 --- /dev/null +++ b/src/input/target/keyboard_new.rs @@ -0,0 +1,418 @@ +use std::{collections::HashMap, error::Error}; + +use evdev::{ + uinput::{VirtualDevice, VirtualDeviceBuilder}, + AbsInfo, AbsoluteAxisCode, AttributeSet, InputEvent, KeyCode, +}; +use zbus::blocking::Connection; + +use crate::{ + dbus::interface::target::keyboard::TargetKeyboardInterface, + input::{ + capability::{Capability, Keyboard}, + event::{evdev::EvdevEvent, native::NativeEvent}, + }, +}; + +use super::{client::TargetDeviceClient, InputError, TargetInputDevice, TargetOutputDevice}; + +#[derive(Debug)] +pub struct KeyboardDevice { + device: VirtualDevice, + axis_map: HashMap, +} + +impl KeyboardDevice { + pub fn new() -> Result> { + let device = KeyboardDevice::create_virtual_device()?; + Ok(Self { + device, + axis_map: HashMap::new(), + }) + } +} + +impl KeyboardDevice { + /// Translate the given native event into an evdev event + fn translate_event(&self, event: NativeEvent) -> Vec { + EvdevEvent::from_native_event(event, self.axis_map.clone()) + .into_iter() + .map(|event| event.as_input_event()) + .collect() + } + + /// Create the virtual device to emulate + fn create_virtual_device() -> Result> { + let mut keys = AttributeSet::::new(); + keys.insert(KeyCode::KEY_ESC); + keys.insert(KeyCode::KEY_1); + keys.insert(KeyCode::KEY_2); + keys.insert(KeyCode::KEY_3); + keys.insert(KeyCode::KEY_4); + keys.insert(KeyCode::KEY_5); + keys.insert(KeyCode::KEY_6); + keys.insert(KeyCode::KEY_7); + keys.insert(KeyCode::KEY_8); + keys.insert(KeyCode::KEY_9); + keys.insert(KeyCode::KEY_0); + keys.insert(KeyCode::KEY_MINUS); + keys.insert(KeyCode::KEY_EQUAL); + keys.insert(KeyCode::KEY_BACKSPACE); + keys.insert(KeyCode::KEY_TAB); + keys.insert(KeyCode::KEY_Q); + keys.insert(KeyCode::KEY_W); + keys.insert(KeyCode::KEY_E); + keys.insert(KeyCode::KEY_R); + keys.insert(KeyCode::KEY_T); + keys.insert(KeyCode::KEY_Y); + keys.insert(KeyCode::KEY_U); + keys.insert(KeyCode::KEY_I); + keys.insert(KeyCode::KEY_O); + keys.insert(KeyCode::KEY_P); + keys.insert(KeyCode::KEY_LEFTBRACE); + keys.insert(KeyCode::KEY_RIGHTBRACE); + keys.insert(KeyCode::KEY_ENTER); + keys.insert(KeyCode::KEY_LEFTCTRL); + keys.insert(KeyCode::KEY_A); + keys.insert(KeyCode::KEY_S); + keys.insert(KeyCode::KEY_D); + keys.insert(KeyCode::KEY_F); + keys.insert(KeyCode::KEY_G); + keys.insert(KeyCode::KEY_H); + keys.insert(KeyCode::KEY_J); + keys.insert(KeyCode::KEY_K); + keys.insert(KeyCode::KEY_L); + keys.insert(KeyCode::KEY_SEMICOLON); + keys.insert(KeyCode::KEY_APOSTROPHE); + keys.insert(KeyCode::KEY_GRAVE); + keys.insert(KeyCode::KEY_LEFTSHIFT); + keys.insert(KeyCode::KEY_BACKSLASH); + keys.insert(KeyCode::KEY_Z); + keys.insert(KeyCode::KEY_X); + keys.insert(KeyCode::KEY_C); + keys.insert(KeyCode::KEY_V); + keys.insert(KeyCode::KEY_B); + keys.insert(KeyCode::KEY_N); + keys.insert(KeyCode::KEY_M); + keys.insert(KeyCode::KEY_COMMA); + keys.insert(KeyCode::KEY_DOT); + keys.insert(KeyCode::KEY_SLASH); + keys.insert(KeyCode::KEY_RIGHTSHIFT); + keys.insert(KeyCode::KEY_KPASTERISK); + keys.insert(KeyCode::KEY_LEFTALT); + keys.insert(KeyCode::KEY_SPACE); + keys.insert(KeyCode::KEY_CAPSLOCK); + keys.insert(KeyCode::KEY_F1); + keys.insert(KeyCode::KEY_F2); + keys.insert(KeyCode::KEY_F3); + keys.insert(KeyCode::KEY_F4); + keys.insert(KeyCode::KEY_F5); + keys.insert(KeyCode::KEY_F6); + keys.insert(KeyCode::KEY_F7); + keys.insert(KeyCode::KEY_F8); + keys.insert(KeyCode::KEY_F9); + keys.insert(KeyCode::KEY_F10); + keys.insert(KeyCode::KEY_NUMLOCK); + keys.insert(KeyCode::KEY_SCROLLLOCK); + keys.insert(KeyCode::KEY_KP7); + keys.insert(KeyCode::KEY_KP8); + keys.insert(KeyCode::KEY_KP9); + keys.insert(KeyCode::KEY_KPMINUS); + keys.insert(KeyCode::KEY_KP4); + keys.insert(KeyCode::KEY_KP5); + keys.insert(KeyCode::KEY_KP6); + keys.insert(KeyCode::KEY_KPPLUS); + keys.insert(KeyCode::KEY_KP1); + keys.insert(KeyCode::KEY_KP2); + keys.insert(KeyCode::KEY_KP3); + keys.insert(KeyCode::KEY_KP0); + keys.insert(KeyCode::KEY_KPDOT); + keys.insert(KeyCode::KEY_ZENKAKUHANKAKU); + keys.insert(KeyCode::KEY_102ND); + keys.insert(KeyCode::KEY_F11); + keys.insert(KeyCode::KEY_F12); + keys.insert(KeyCode::KEY_RO); + keys.insert(KeyCode::KEY_KATAKANA); + keys.insert(KeyCode::KEY_HIRAGANA); + keys.insert(KeyCode::KEY_HENKAN); + keys.insert(KeyCode::KEY_KATAKANAHIRAGANA); + keys.insert(KeyCode::KEY_MUHENKAN); + keys.insert(KeyCode::KEY_KPJPCOMMA); + keys.insert(KeyCode::KEY_KPENTER); + keys.insert(KeyCode::KEY_RIGHTCTRL); + keys.insert(KeyCode::KEY_KPSLASH); + keys.insert(KeyCode::KEY_SYSRQ); + keys.insert(KeyCode::KEY_RIGHTALT); + keys.insert(KeyCode::KEY_HOME); + keys.insert(KeyCode::KEY_UP); + keys.insert(KeyCode::KEY_PAGEUP); + keys.insert(KeyCode::KEY_LEFT); + keys.insert(KeyCode::KEY_RIGHT); + keys.insert(KeyCode::KEY_END); + keys.insert(KeyCode::KEY_DOWN); + keys.insert(KeyCode::KEY_PAGEDOWN); + keys.insert(KeyCode::KEY_INSERT); + keys.insert(KeyCode::KEY_DELETE); + keys.insert(KeyCode::KEY_MUTE); + keys.insert(KeyCode::KEY_VOLUMEDOWN); + keys.insert(KeyCode::KEY_VOLUMEUP); + keys.insert(KeyCode::KEY_POWER); + keys.insert(KeyCode::KEY_KPEQUAL); + keys.insert(KeyCode::KEY_PAUSE); + keys.insert(KeyCode::KEY_KPCOMMA); + keys.insert(KeyCode::KEY_HANJA); + keys.insert(KeyCode::KEY_YEN); + keys.insert(KeyCode::KEY_LEFTMETA); + keys.insert(KeyCode::KEY_RIGHTMETA); + keys.insert(KeyCode::KEY_COMPOSE); + keys.insert(KeyCode::KEY_STOP); + keys.insert(KeyCode::KEY_AGAIN); + keys.insert(KeyCode::KEY_PROPS); + keys.insert(KeyCode::KEY_UNDO); + keys.insert(KeyCode::KEY_FRONT); + keys.insert(KeyCode::KEY_COPY); + keys.insert(KeyCode::KEY_OPEN); + keys.insert(KeyCode::KEY_PASTE); + keys.insert(KeyCode::KEY_FIND); + keys.insert(KeyCode::KEY_CUT); + keys.insert(KeyCode::KEY_HELP); + keys.insert(KeyCode::KEY_CALC); + keys.insert(KeyCode::KEY_SLEEP); + keys.insert(KeyCode::KEY_WWW); + keys.insert(KeyCode::KEY_BACK); + keys.insert(KeyCode::KEY_FORWARD); + keys.insert(KeyCode::KEY_EJECTCD); + keys.insert(KeyCode::KEY_NEXTSONG); + keys.insert(KeyCode::KEY_PLAYPAUSE); + keys.insert(KeyCode::KEY_PREVIOUSSONG); + keys.insert(KeyCode::KEY_STOPCD); + keys.insert(KeyCode::KEY_REFRESH); + keys.insert(KeyCode::KEY_EDIT); + keys.insert(KeyCode::KEY_SCROLLUP); + keys.insert(KeyCode::KEY_SCROLLDOWN); + keys.insert(KeyCode::KEY_KPLEFTPAREN); + keys.insert(KeyCode::KEY_KPRIGHTPAREN); + keys.insert(KeyCode::KEY_F13); + keys.insert(KeyCode::KEY_F14); + keys.insert(KeyCode::KEY_F15); + keys.insert(KeyCode::KEY_F16); + keys.insert(KeyCode::KEY_F17); + keys.insert(KeyCode::KEY_F18); + keys.insert(KeyCode::KEY_F19); + keys.insert(KeyCode::KEY_F20); + keys.insert(KeyCode::KEY_F21); + keys.insert(KeyCode::KEY_F22); + keys.insert(KeyCode::KEY_F23); + keys.insert(KeyCode::KEY_F24); + keys.insert(KeyCode::KEY_PROG1); + + let device = VirtualDeviceBuilder::new()? + .name("InputPlumber Keyboard") + .with_keys(&keys)? + .build()?; + + Ok(device) + } +} + +impl TargetInputDevice for KeyboardDevice { + fn start_dbus_interface(&mut self, dbus: Connection, path: String, client: TargetDeviceClient) { + log::debug!("Starting dbus interface: {path}"); + let iface = TargetKeyboardInterface::new(client); + if let Err(e) = dbus.object_server().at(path.clone(), iface) { + log::debug!("Failed to start dbus interface {path}: {e:?}"); + } else { + log::debug!("Started dbus interface on {path}"); + }; + } + + fn write_event(&mut self, event: NativeEvent) -> Result<(), InputError> { + log::trace!("Received event: {event:?}"); + let evdev_events = self.translate_event(event); + if let Err(e) = self.device.emit(evdev_events.as_slice()) { + return Err(e.to_string().into()); + } + + Ok(()) + } + + fn get_capabilities(&self) -> Result, InputError> { + Ok(vec![ + Capability::Keyboard(Keyboard::KeyEsc), + Capability::Keyboard(Keyboard::Key1), + Capability::Keyboard(Keyboard::Key2), + Capability::Keyboard(Keyboard::Key3), + Capability::Keyboard(Keyboard::Key4), + Capability::Keyboard(Keyboard::Key5), + Capability::Keyboard(Keyboard::Key6), + Capability::Keyboard(Keyboard::Key7), + Capability::Keyboard(Keyboard::Key8), + Capability::Keyboard(Keyboard::Key9), + Capability::Keyboard(Keyboard::Key0), + Capability::Keyboard(Keyboard::KeyMinus), + Capability::Keyboard(Keyboard::KeyEqual), + Capability::Keyboard(Keyboard::KeyBackspace), + Capability::Keyboard(Keyboard::KeyTab), + Capability::Keyboard(Keyboard::KeyQ), + Capability::Keyboard(Keyboard::KeyW), + Capability::Keyboard(Keyboard::KeyE), + Capability::Keyboard(Keyboard::KeyR), + Capability::Keyboard(Keyboard::KeyT), + Capability::Keyboard(Keyboard::KeyY), + Capability::Keyboard(Keyboard::KeyU), + Capability::Keyboard(Keyboard::KeyI), + Capability::Keyboard(Keyboard::KeyO), + Capability::Keyboard(Keyboard::KeyP), + Capability::Keyboard(Keyboard::KeyLeftBrace), + Capability::Keyboard(Keyboard::KeyRightBrace), + Capability::Keyboard(Keyboard::KeyEnter), + Capability::Keyboard(Keyboard::KeyLeftCtrl), + Capability::Keyboard(Keyboard::KeyA), + Capability::Keyboard(Keyboard::KeyS), + Capability::Keyboard(Keyboard::KeyD), + Capability::Keyboard(Keyboard::KeyF), + Capability::Keyboard(Keyboard::KeyG), + Capability::Keyboard(Keyboard::KeyH), + Capability::Keyboard(Keyboard::KeyJ), + Capability::Keyboard(Keyboard::KeyK), + Capability::Keyboard(Keyboard::KeyL), + Capability::Keyboard(Keyboard::KeySemicolon), + Capability::Keyboard(Keyboard::KeyApostrophe), + Capability::Keyboard(Keyboard::KeyGrave), + Capability::Keyboard(Keyboard::KeyLeftShift), + Capability::Keyboard(Keyboard::KeyBackslash), + Capability::Keyboard(Keyboard::KeyZ), + Capability::Keyboard(Keyboard::KeyX), + Capability::Keyboard(Keyboard::KeyC), + Capability::Keyboard(Keyboard::KeyV), + Capability::Keyboard(Keyboard::KeyB), + Capability::Keyboard(Keyboard::KeyN), + Capability::Keyboard(Keyboard::KeyM), + Capability::Keyboard(Keyboard::KeyComma), + Capability::Keyboard(Keyboard::KeyDot), + Capability::Keyboard(Keyboard::KeySlash), + Capability::Keyboard(Keyboard::KeyRightShift), + Capability::Keyboard(Keyboard::KeyKpAsterisk), + Capability::Keyboard(Keyboard::KeyLeftAlt), + Capability::Keyboard(Keyboard::KeySpace), + Capability::Keyboard(Keyboard::KeyCapslock), + Capability::Keyboard(Keyboard::KeyF1), + Capability::Keyboard(Keyboard::KeyF2), + Capability::Keyboard(Keyboard::KeyF3), + Capability::Keyboard(Keyboard::KeyF4), + Capability::Keyboard(Keyboard::KeyF5), + Capability::Keyboard(Keyboard::KeyF6), + Capability::Keyboard(Keyboard::KeyF7), + Capability::Keyboard(Keyboard::KeyF8), + Capability::Keyboard(Keyboard::KeyF9), + Capability::Keyboard(Keyboard::KeyF10), + Capability::Keyboard(Keyboard::KeyNumlock), + Capability::Keyboard(Keyboard::KeyScrollLock), + Capability::Keyboard(Keyboard::KeyKp7), + Capability::Keyboard(Keyboard::KeyKp8), + Capability::Keyboard(Keyboard::KeyKp9), + Capability::Keyboard(Keyboard::KeyKpMinus), + Capability::Keyboard(Keyboard::KeyKp4), + Capability::Keyboard(Keyboard::KeyKp5), + Capability::Keyboard(Keyboard::KeyKp6), + Capability::Keyboard(Keyboard::KeyKpPlus), + Capability::Keyboard(Keyboard::KeyKp1), + Capability::Keyboard(Keyboard::KeyKp2), + Capability::Keyboard(Keyboard::KeyKp3), + Capability::Keyboard(Keyboard::KeyKp0), + Capability::Keyboard(Keyboard::KeyKpDot), + Capability::Keyboard(Keyboard::KeyZenkakuhankaku), + Capability::Keyboard(Keyboard::Key102nd), + Capability::Keyboard(Keyboard::KeyF11), + Capability::Keyboard(Keyboard::KeyF12), + Capability::Keyboard(Keyboard::KeyRo), + Capability::Keyboard(Keyboard::KeyKatakana), + Capability::Keyboard(Keyboard::KeyHiragana), + Capability::Keyboard(Keyboard::KeyHenkan), + Capability::Keyboard(Keyboard::KeyKatakanaHiragana), + Capability::Keyboard(Keyboard::KeyMuhenkan), + Capability::Keyboard(Keyboard::KeyKpJpComma), + Capability::Keyboard(Keyboard::KeyKpEnter), + Capability::Keyboard(Keyboard::KeyRightCtrl), + Capability::Keyboard(Keyboard::KeyKpSlash), + Capability::Keyboard(Keyboard::KeySysrq), + Capability::Keyboard(Keyboard::KeyRightAlt), + Capability::Keyboard(Keyboard::KeyHome), + Capability::Keyboard(Keyboard::KeyUp), + Capability::Keyboard(Keyboard::KeyPageUp), + Capability::Keyboard(Keyboard::KeyLeft), + Capability::Keyboard(Keyboard::KeyRight), + Capability::Keyboard(Keyboard::KeyEnd), + Capability::Keyboard(Keyboard::KeyDown), + Capability::Keyboard(Keyboard::KeyPageDown), + Capability::Keyboard(Keyboard::KeyInsert), + Capability::Keyboard(Keyboard::KeyDelete), + Capability::Keyboard(Keyboard::KeyMute), + Capability::Keyboard(Keyboard::KeyVolumeDown), + Capability::Keyboard(Keyboard::KeyVolumeUp), + Capability::Keyboard(Keyboard::KeyPower), + Capability::Keyboard(Keyboard::KeyKpEqual), + Capability::Keyboard(Keyboard::KeyPause), + Capability::Keyboard(Keyboard::KeyKpComma), + Capability::Keyboard(Keyboard::KeyHanja), + Capability::Keyboard(Keyboard::KeyYen), + Capability::Keyboard(Keyboard::KeyLeftMeta), + Capability::Keyboard(Keyboard::KeyRightMeta), + Capability::Keyboard(Keyboard::KeyCompose), + Capability::Keyboard(Keyboard::KeyStop), + Capability::Keyboard(Keyboard::KeyAgain), + Capability::Keyboard(Keyboard::KeyProps), + Capability::Keyboard(Keyboard::KeyUndo), + Capability::Keyboard(Keyboard::KeyFront), + Capability::Keyboard(Keyboard::KeyCopy), + Capability::Keyboard(Keyboard::KeyOpen), + Capability::Keyboard(Keyboard::KeyPaste), + Capability::Keyboard(Keyboard::KeyFind), + Capability::Keyboard(Keyboard::KeyCut), + Capability::Keyboard(Keyboard::KeyHelp), + Capability::Keyboard(Keyboard::KeyCalc), + Capability::Keyboard(Keyboard::KeySleep), + Capability::Keyboard(Keyboard::KeyWww), + Capability::Keyboard(Keyboard::KeyBack), + Capability::Keyboard(Keyboard::KeyForward), + Capability::Keyboard(Keyboard::KeyEjectCD), + Capability::Keyboard(Keyboard::KeyNextSong), + Capability::Keyboard(Keyboard::KeyPlayPause), + Capability::Keyboard(Keyboard::KeyPreviousSong), + Capability::Keyboard(Keyboard::KeyStopCD), + Capability::Keyboard(Keyboard::KeyRefresh), + Capability::Keyboard(Keyboard::KeyEdit), + Capability::Keyboard(Keyboard::KeyScrollUp), + Capability::Keyboard(Keyboard::KeyScrollDown), + Capability::Keyboard(Keyboard::KeyKpLeftParen), + Capability::Keyboard(Keyboard::KeyKpRightParen), + Capability::Keyboard(Keyboard::KeyF13), + Capability::Keyboard(Keyboard::KeyF14), + Capability::Keyboard(Keyboard::KeyF15), + Capability::Keyboard(Keyboard::KeyF16), + Capability::Keyboard(Keyboard::KeyF17), + Capability::Keyboard(Keyboard::KeyF18), + Capability::Keyboard(Keyboard::KeyF19), + Capability::Keyboard(Keyboard::KeyF20), + Capability::Keyboard(Keyboard::KeyF21), + Capability::Keyboard(Keyboard::KeyF22), + Capability::Keyboard(Keyboard::KeyF23), + Capability::Keyboard(Keyboard::KeyF24), + Capability::Keyboard(Keyboard::KeyProg1), + ]) + } + + fn stop_dbus_interface(&mut self, dbus: Connection, path: String) { + log::debug!("Stopping dbus interface for {path}"); + let result = dbus + .object_server() + .remove::(path.clone()); + if let Err(e) = result { + log::error!("Failed to stop dbus interface {path}: {e:?}"); + } else { + log::debug!("Stopped dbus interface for {path}"); + }; + } +} + +impl TargetOutputDevice for KeyboardDevice {} diff --git a/src/input/target/mod.rs b/src/input/target/mod.rs index 4d85fa64..3b27261e 100644 --- a/src/input/target/mod.rs +++ b/src/input/target/mod.rs @@ -1,10 +1,30 @@ +use std::{ + error::Error, + sync::{Arc, Mutex, MutexGuard}, + thread, + time::Duration, +}; + +use thiserror::Error; +use tokio::sync::mpsc::{self, error::TryRecvError}; + +use crate::dbus::interface::target::gamepad::TargetGamepadInterface; + +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 zbus::{blocking, zvariant::ObjectPath, Connection}; use self::client::TargetDeviceClient; +use self::command::TargetCommand; use self::dbus::DBusDevice; use self::dualsense::{DualSenseDevice, DualSenseHardware}; use self::keyboard::KeyboardDevice; @@ -18,15 +38,84 @@ use self::xbox_series::XboxSeriesController; pub mod client; pub mod command; pub mod dbus; +pub mod dbus_new; pub mod dualsense; +pub mod dualsense_new; pub mod keyboard; +pub mod keyboard_new; pub mod mouse; +pub mod mouse_new; pub mod steam_deck; +pub mod steam_deck_new; pub mod touchscreen; 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 +212,279 @@ 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 { + /// Start the DBus interface for this target device + fn start_dbus_interface( + &mut self, + dbus: blocking::Connection, + path: String, + client: TargetDeviceClient, + ) { + log::debug!("Starting dbus interface: {path}"); + log::trace!("Using device client: {client:?}"); + let name = "Gamepad".to_string(); + let iface = TargetGamepadInterface::new(name); + if let Err(e) = dbus.object_server().at(path.clone(), iface) { + log::debug!("Failed to start dbus interface {path}: {e:?}"); + } else { + log::debug!("Started dbus interface on {path}"); + }; + } + + /// 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 DBus interface for this target device + fn stop_dbus_interface(&mut self, dbus: blocking::Connection, path: String) { + log::debug!("Stopping dbus interface for {path}"); + let result = dbus + .object_server() + .remove::(path.clone()); + if let Err(e) = result { + log::error!("Failed to stop dbus interface {path}: {e:?}"); + } else { + log::debug!("Stopped dbus interface for {path}"); + }; + } + + /// Stop the target device + fn stop(&mut 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, + dbus: Connection, + dbus_path: String, + implementation: Arc>, + composite_device: Option, + scheduled_events: Vec, + tx: mpsc::Sender, + rx: mpsc::Receiver, +} + +impl TargetDriver { + pub fn new(device: T, dbus: Connection, dbus_path: String) -> Self { + let options = TargetDriverOptions::default(); + let (tx, rx) = mpsc::channel(options.buffer_size); + Self { + options, + dbus, + dbus_path, + 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 client = self.client(); + 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(); + let dbus = blocking::Connection::from(self.dbus.clone()); + + // Start the DBus interface for the device + implementation.start_dbus_interface(dbus.clone(), self.dbus_path.clone(), client); + + 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); + } + + // Stop the device + implementation.stop_dbus_interface(dbus, self.dbus_path.clone()); + implementation.stop()?; + + 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 +529,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 +547,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 +556,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, } } } diff --git a/src/input/target/mouse_new.rs b/src/input/target/mouse_new.rs new file mode 100644 index 00000000..f17bd24b --- /dev/null +++ b/src/input/target/mouse_new.rs @@ -0,0 +1,228 @@ +use std::{collections::HashMap, error::Error, time::Instant}; + +use evdev::{ + uinput::{VirtualDevice, VirtualDeviceBuilder}, + AbsInfo, AbsoluteAxisCode, AttributeSet, InputEvent, KeyCode, RelativeAxisCode, +}; +use zbus::blocking::Connection; + +use crate::{ + dbus::interface::target::mouse::TargetMouseInterface, + input::{ + capability::{Capability, Mouse, MouseButton}, + event::{evdev::EvdevEvent, native::NativeEvent, value::InputValue}, + output_event::OutputEvent, + }, +}; + +use super::{ + client::TargetDeviceClient, InputError, OutputError, TargetInputDevice, TargetOutputDevice, +}; + +/// The [MouseMotionState] keeps track of the mouse velocity from translated +/// input events (like a joystick), and sends mouse motion events to the +/// [MouseDevice] based on the current velocity. +#[derive(Debug, Default)] +pub struct MouseMotionState { + mouse_remainder: (f64, f64), + mouse_velocity: (f64, f64), +} + +/// [MouseDevice] is a target virtual mouse that can be used to send mouse input +#[derive(Debug)] +pub struct MouseDevice { + device: VirtualDevice, + state: MouseMotionState, + axis_map: HashMap, + last_poll: Instant, +} + +impl MouseDevice { + pub fn new() -> Result> { + let device = MouseDevice::create_virtual_device()?; + Ok(Self { + device, + state: MouseMotionState::default(), + axis_map: HashMap::new(), + last_poll: Instant::now(), + }) + } + + /// Translate the given native event into an evdev event + fn translate_event(&self, event: NativeEvent) -> Vec { + EvdevEvent::from_native_event(event, self.axis_map.clone()) + .into_iter() + .map(|event| event.as_input_event()) + .collect() + } + + /// Create the virtual device to emulate + fn create_virtual_device() -> Result> { + let mut buttons = AttributeSet::::new(); + buttons.insert(KeyCode::BTN_LEFT); + buttons.insert(KeyCode::BTN_RIGHT); + buttons.insert(KeyCode::BTN_MIDDLE); + buttons.insert(KeyCode::BTN_SIDE); + buttons.insert(KeyCode::BTN_EXTRA); + let device = VirtualDeviceBuilder::new()? + .name("InputPlumber Mouse") + .with_keys(&buttons)? + .with_relative_axes(&AttributeSet::from_iter([ + RelativeAxisCode::REL_X, + RelativeAxisCode::REL_Y, + RelativeAxisCode::REL_WHEEL, + RelativeAxisCode::REL_HWHEEL, + ]))? + .build()?; + + Ok(device) + } + + /// Processes the given mouse motion or button input event. + fn update_state(&mut self, event: NativeEvent) { + // Get the mouse position from the event value + let value = event.get_value(); + let (x, y) = match value { + InputValue::Vector2 { x, y } => (x, y), + InputValue::Vector3 { x, y, z: _ } => (x, y), + _ => (None, None), + }; + + // Update the mouse velocity + if let Some(x) = x { + self.state.mouse_velocity.0 = x; + log::trace!("Updating mouse state: {:?}", self.state.mouse_velocity); + } + if let Some(y) = y { + self.state.mouse_velocity.1 = y; + log::trace!("Updating mouse state: {:?}", self.state.mouse_velocity); + } + } +} + +impl TargetInputDevice for MouseDevice { + fn start_dbus_interface( + &mut self, + dbus: Connection, + path: String, + _client: TargetDeviceClient, + ) { + log::debug!("Starting dbus interface: {path}"); + let iface = TargetMouseInterface::new(); + if let Err(e) = dbus.object_server().at(path.clone(), iface) { + log::debug!("Failed to start dbus interface {path}: {e:?}"); + } else { + log::debug!("Started dbus interface on {path}"); + }; + } + + fn write_event(&mut self, event: NativeEvent) -> Result<(), InputError> { + log::trace!("Received event: {event:?}"); + + // Check if this event needs to be processed by the + // mouse state. + if event.is_translated() + && matches!(event.as_capability(), Capability::Mouse(Mouse::Motion)) + { + log::trace!("Got translated mouse motion event: {:?}", event); + self.update_state(event); + return Ok(()); + } + + // Translate and emit the event(s) + let evdev_events = self.translate_event(event); + if let Err(e) = self.device.emit(evdev_events.as_slice()) { + return Err(e.to_string().into()); + } + + Ok(()) + } + + fn get_capabilities(&self) -> Result, InputError> { + Ok(vec![ + Capability::Mouse(Mouse::Button(MouseButton::Left)), + Capability::Mouse(Mouse::Button(MouseButton::Right)), + Capability::Mouse(Mouse::Button(MouseButton::Middle)), + Capability::Mouse(Mouse::Button(MouseButton::Side)), + Capability::Mouse(Mouse::Button(MouseButton::Extra)), + Capability::Mouse(Mouse::Button(MouseButton::WheelUp)), + Capability::Mouse(Mouse::Button(MouseButton::WheelDown)), + Capability::Mouse(Mouse::Motion), + ]) + } + + fn stop_dbus_interface(&mut self, dbus: Connection, path: String) { + log::debug!("Stopping dbus interface for {path}"); + let result = dbus + .object_server() + .remove::(path.clone()); + if let Err(e) = result { + log::error!("Failed to stop dbus interface {path}: {e:?}"); + } else { + log::debug!("Stopped dbus interface for {path}"); + }; + } +} + +impl TargetOutputDevice for MouseDevice { + /// Move the mouse based on the given input event translation + fn poll(&mut self) -> Result, OutputError> { + // Calculate the delta between the last poll + let delta = self.last_poll.elapsed(); + self.last_poll = Instant::now(); + + // Calculate how much the mouse should move based on the current mouse velocity + let mut pixels_to_move = (0.0, 0.0); + pixels_to_move.0 = delta.as_secs_f64() * self.state.mouse_velocity.0; + pixels_to_move.1 = delta.as_secs_f64() * self.state.mouse_velocity.1; + + // Get the fractional value of the position so we can accumulate them + // in between invocations + let mut x = pixels_to_move.0 as i32; // E.g. 3.14 -> 3 + let mut y = pixels_to_move.1 as i32; + self.state.mouse_remainder.0 += pixels_to_move.0 - x as f64; + self.state.mouse_remainder.1 += pixels_to_move.1 - y as f64; + + // Keep track of relative mouse movements to keep around fractional values + if self.state.mouse_remainder.0 >= 1.0 { + x += 1; + self.state.mouse_remainder.0 -= 1.0; + } + if self.state.mouse_remainder.0 <= -1.0 { + x -= 1; + self.state.mouse_remainder.0 += 1.0; + } + if self.state.mouse_remainder.1 >= 1.0 { + y += 1; + self.state.mouse_remainder.1 -= 1.0; + } + if self.state.mouse_remainder.1 <= -1.0 { + y -= 1; + self.state.mouse_remainder.1 += 1.0; + } + + // Send events to the device if the mouse state has changed + if x != 0 { + let value = InputValue::Vector2 { + x: Some(x as f64), + y: None, + }; + let event = NativeEvent::new(Capability::Mouse(Mouse::Motion), value); + if let Err(e) = self.write_event(event) { + return Err(e.to_string().into()); + } + } + if y != 0 { + let value = InputValue::Vector2 { + x: None, + y: Some(y as f64), + }; + let event = NativeEvent::new(Capability::Mouse(Mouse::Motion), value); + if let Err(e) = self.write_event(event) { + return Err(e.to_string().into()); + } + } + + Ok(vec![]) + } +} diff --git a/src/input/target/steam_deck_new.rs b/src/input/target/steam_deck_new.rs new file mode 100644 index 00000000..8be68435 --- /dev/null +++ b/src/input/target/steam_deck_new.rs @@ -0,0 +1,859 @@ +use packed_struct::{ + types::{Integer, SizedInteger}, + PackedStruct, +}; +use std::{ + cmp::Ordering, + collections::HashMap, + error::Error, + fmt::Debug, + time::{Duration, Instant}, +}; +use virtual_usb::{ + usb::{ + hid::{HidInterfaceBuilder, HidReportType, HidRequest, HidSubclass, InterfaceProtocol}, + ConfigurationBuilder, DeviceClass, Direction, EndpointBuilder, LangId, SynchronizationType, + TransferType, Type, UsageType, + }, + usbip::UsbIpDirection, + vhci_hcd::load_vhci_hcd, + virtual_usb::{Reply, VirtualUSBDevice, VirtualUSBDeviceBuilder, Xfer}, +}; + +use crate::{ + drivers::steam_deck::{ + driver::{PID, VID}, + hid_report::{ + PackedInputDataReport, ReportType, PAD_FORCE_MAX, PAD_X_MAX, PAD_X_MIN, PAD_Y_MAX, + PAD_Y_MIN, STICK_FORCE_MAX, STICK_X_MAX, STICK_X_MIN, STICK_Y_MAX, STICK_Y_MIN, + TRIGG_MAX, + }, + report_descriptor::{CONTROLLER_DESCRIPTOR, KEYBOARD_DESCRIPTOR, MOUSE_DESCRIPTOR}, + }, + input::{ + capability::{ + Capability, Gamepad, GamepadAxis, GamepadButton, GamepadTrigger, Touch, TouchButton, + Touchpad, + }, + event::{ + native::{NativeEvent, ScheduledNativeEvent}, + value::InputValue, + }, + output_event::OutputEvent, + }, +}; + +use super::{InputError, OutputError, TargetInputDevice, TargetOutputDevice}; + +// The minimum amount of time that button up events must wait after +// a button down event. +const MIN_FRAME_TIME: Duration = Duration::from_millis(80); + +pub struct SteamDeckDevice { + device: VirtualUSBDevice, + state: PackedInputDataReport, + /// Steam will send 'SetReport' commands with a report type, so it can fetch + /// a particular result with 'GetReport' + current_report: ReportType, + lizard_mode_enabled: bool, + serial_number: String, + queued_events: Vec, + pressed_events: HashMap, +} + +impl SteamDeckDevice { + pub fn new() -> Result> { + // Ensure the vhci_hcd kernel module is loaded + log::debug!("Ensuring vhci_hcd kernel module is loaded"); + if let Err(e) = load_vhci_hcd() { + return Err(e.to_string().into()); + } + + // Create and start the virtual USB device + let mut device = SteamDeckDevice::create_virtual_device()?; + device.start()?; + + Ok(Self { + device, + state: PackedInputDataReport::default(), + current_report: ReportType::InputData, + lizard_mode_enabled: false, + serial_number: "INPU7PLUMB3R".to_string(), + queued_events: vec![], + pressed_events: HashMap::new(), + }) + } + + /// Create the virtual device to emulate + fn create_virtual_device() -> Result> { + // Configuration values can be obtained from a real device with "sudo lsusb -v" + let virtual_device = VirtualUSBDeviceBuilder::new(VID, PID) + .class(DeviceClass::UseInterface) + .supported_langs(vec![LangId::EnglishUnitedStates]) + .manufacturer("Valve Software") + .product("Steam Controller") + .max_packet_size(64) + .configuration( + ConfigurationBuilder::new() + .max_power(500) + // Mouse (iface 0) + .interface( + HidInterfaceBuilder::new() + .country_code(0) + .protocol(InterfaceProtocol::Mouse) + .subclass(HidSubclass::None) + .report_descriptor(&MOUSE_DESCRIPTOR) + .endpoint_descriptor( + EndpointBuilder::new() + .address_num(1) + .direction(Direction::In) + .transfer_type(TransferType::Interrupt) + .sync_type(SynchronizationType::NoSynchronization) + .usage_type(UsageType::Data) + .max_packet_size(0x0008) + .build(), + ) + .build(), + ) + // Keyboard (iface 1) + .interface( + HidInterfaceBuilder::new() + .country_code(33) + .protocol(InterfaceProtocol::Keyboard) + .subclass(HidSubclass::Boot) + .report_descriptor(&KEYBOARD_DESCRIPTOR) + .endpoint_descriptor( + EndpointBuilder::new() + .address_num(2) + .direction(Direction::In) + .transfer_type(TransferType::Interrupt) + .sync_type(SynchronizationType::NoSynchronization) + .usage_type(UsageType::Data) + .max_packet_size(0x0008) + .build(), + ) + .build(), + ) + // Controller (iface 2) + .interface( + HidInterfaceBuilder::new() + .country_code(33) + .protocol(InterfaceProtocol::None) + .subclass(HidSubclass::None) + .report_descriptor(&CONTROLLER_DESCRIPTOR) + .endpoint_descriptor( + EndpointBuilder::new() + .address_num(3) + .direction(Direction::In) + .transfer_type(TransferType::Interrupt) + .sync_type(SynchronizationType::NoSynchronization) + .usage_type(UsageType::Data) + .max_packet_size(0x0040) + .build(), + ) + .build(), + ) + // CDC + //.interface(HidInterfaceBuilder::new().build()) + // CDC Data + //.interface(HidInterfaceBuilder::new().build()) + .build(), + ) + .build(); + + Ok(virtual_device) + } + + /// Update the virtual device with its current state, and read unhandled + /// USB transfers. + fn update(&mut self) -> Result<(), Box> { + // Increment the frame + let frame = self.state.frame.to_primitive(); + self.state.frame = Integer::from_primitive(frame.wrapping_add(1)); + + // Read from the device + let xfer = self.device.blocking_read()?; + + // Handle any non-standard transfers + if let Some(xfer) = xfer { + let reply = self.handle_xfer(xfer); + + // Write to the device if a reply is necessary + if let Some(reply) = reply { + self.device.write(reply)?; + } + } + + Ok(()) + } + + /// Handle any non-standard transfers + fn handle_xfer(&mut self, xfer: Xfer) -> Option { + match xfer.direction() { + UsbIpDirection::Out => { + self.handle_xfer_out(xfer); + None + } + UsbIpDirection::In => self.handle_xfer_in(xfer), + } + } + + /// Handle any non-standard IN transfers (device -> host) for the gamepad iface + fn handle_xfer_in(&self, xfer: Xfer) -> Option { + // IN transfers do not have a setup request. + let endpoint = xfer.ep; + + // If a setup header exists, we need to reply to it. + if xfer.header().is_some() { + return self.handle_xfer_in_request(xfer); + }; + + // Create a reply based on the endpoint + let reply = match endpoint { + // Gamepad + 3 => self.handle_xfer_in_gamepad(xfer), + // All other endpoints, write empty data for now + _ => Reply::from_xfer(xfer, &[]), + }; + + Some(reply) + } + + // Handle IN transfers (device -> host) for feature requests + fn handle_xfer_in_request(&self, xfer: Xfer) -> Option { + let setup = xfer.header()?; + + // Only handle Class requests + if setup.request_type() != Type::Class { + log::warn!("Unknown request type"); + return Some(Reply::from_xfer(xfer, &[])); + } + + // Interpret the setup request as an HID request + let request = HidRequest::from(setup); + + let reply = match request { + HidRequest::Unknown => { + log::warn!("Unknown HID request!"); + Reply::from_xfer(xfer, &[]) + } + HidRequest::GetReport(req) => { + //log::trace!("GetReport: {req}"); + let interface = req.interface.to_primitive(); + //log::trace!("Got GetReport data for iface {interface}"); + let report_type = req.report_type; + + // Handle GetReport + match report_type { + HidReportType::Input => Reply::from_xfer(xfer, &[]), + HidReportType::Output => Reply::from_xfer(xfer, &[]), + HidReportType::Feature => { + // Reply based on the currently set report + match self.current_report { + ReportType::GetAttrib => { + log::debug!("Sending attribute data"); + // No idea what these bytes mean, but this is + // what is sent from the real device. + let data = [ + ReportType::GetAttrib as u8, + 0x2d, + 0x01, + 0x05, + 0x12, + 0x00, + 0x00, + 0x02, + 0x00, + 0x00, + 0x00, + 0x00, + 0x0a, + 0x2b, + 0x12, + 0xa9, + 0x62, + 0x04, + 0xad, + 0xf1, + 0xe4, + 0x65, + 0x09, + 0x2e, + 0x00, + 0x00, + 0x00, + 0x0b, + 0xa0, + 0x0f, + 0x00, + 0x00, + 0x0d, + 0x00, + 0x00, + 0x00, + 0x00, + 0x0c, + 0x00, + 0x00, + 0x00, + 0x00, + 0x0e, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + ]; + Reply::from_xfer(xfer, &data) + } + ReportType::GetSerial => { + // Reply with the serial number + // [ReportType::GetSerial, 0x14, 0x01, ..serial?]? + log::debug!("Sending serial number: {}", self.serial_number); + let mut data = vec![ReportType::GetSerial as u8, 0x14, 0x01]; + let mut serial_data = self.serial_number.as_bytes().to_vec(); + data.append(&mut serial_data); + data.resize(64, 0); + Reply::from_xfer(xfer, data.as_slice()) + } + // Don't care about other types + _ => Reply::from_xfer(xfer, &[]), + } + } + } + } + // Ignore other types of requests + _ => Reply::from_xfer(xfer, &[]), + }; + + Some(reply) + } + + // Handle IN transfers (device -> host) for the gamepad interface + fn handle_xfer_in_gamepad(&self, xfer: Xfer) -> Reply { + // Pack the state + let report_data = match self.state.pack() { + Ok(data) => data, + Err(e) => { + log::error!("Failed to pack input data report: {e:?}"); + return Reply::from_xfer(xfer, &[]); + } + }; + + Reply::from_xfer(xfer, &report_data) + } + + /// Handle any non-standard OUT transfers (host -> device) for the gamepad iface. + /// Out transfers do not have any replies. + fn handle_xfer_out(&mut self, xfer: Xfer) { + // OUT transfers (host -> device) are generally always to ep 0 + //log::trace!("Got OUT transfer for endpoint: {}", xfer.ep); + + let Some(setup) = xfer.header() else { + log::debug!("No setup request in OUT xfer"); + return; + }; + + // Only handle Class requests + if setup.request_type() != Type::Class { + log::debug!("Unknown request type"); + return; + } + + // Interpret the setup request as an HID request + let request = HidRequest::from(setup); + + match request { + HidRequest::Unknown => { + log::warn!("Unknown HID request!"); + } + HidRequest::SetIdle(req) => { + //log::trace!("SetIdle: {req}"); + } + // The host wants to set the given report on the device + HidRequest::SetReport(req) => { + //log::trace!("SetReport: {req}"); + let interface = req.interface.to_primitive(); + let data = xfer.data; + //log::trace!("Got SetReport data for iface {interface}: {data:?}"); + + // The first byte contains the report type + let Some(first_byte) = data.first() else { + log::debug!("Unable to determine report type from empty report"); + return; + }; + + let Ok(report_type) = ReportType::try_from(*first_byte) else { + log::debug!("Invalid report type: {first_byte}"); + return; + }; + + // https://github.com/libsdl-org/SDL/blob/f0363a0466f72655a1081fb96a90e1b9602ee571/src/joystick/hidapi/SDL_hidapi_steamdeck.c + match report_type { + ReportType::InputData => (), + ReportType::SetMappings => (), + // ClearMappings gets called to take the controller out of lizard + // mode so that Steam can control it directly. + ReportType::ClearMappings => { + //log::trace!("Disabling lizard mode"); + self.lizard_mode_enabled = false; + } + ReportType::GetMappings => (), + ReportType::GetAttrib => { + log::debug!("Attribute requested"); + self.current_report = ReportType::GetAttrib; + } + ReportType::GetAttribLabel => (), + // DefaultMappings sets the device in lizard mode, so it can run + // without Steam. + ReportType::DefaultMappings => { + log::debug!("Setting lizard mode enabled"); + self.lizard_mode_enabled = true; + } + ReportType::FactoryReset => (), + // When Steam boots up, it writes to a register with this data: + // Got SetReport data: [135, 3, 8, 7, 0, 0, 0, ...] + ReportType::WriteRegister => (), + ReportType::ClearRegister => (), + ReportType::ReadRegister => (), + ReportType::GetRegisterLabel => (), + ReportType::GetRegisterMax => (), + ReportType::GetRegisterDefault => (), + ReportType::SetMode => (), + ReportType::DefaultMouse => (), + ReportType::TriggerHapticPulse => (), + ReportType::RequestCommStatus => (), + // Configure the next GET_REPORT call to return the serial + // number. + ReportType::GetSerial => { + log::debug!("Serial number requested"); + self.current_report = ReportType::GetSerial; + } + ReportType::TriggerHapticCommand => (), + ReportType::TriggerRumbleCommand => (), + } + } + // Ignore other types of requests + _ => {} + } + } + + /// Update the internal controller state when events are emitted. + fn update_state(&mut self, event: NativeEvent) { + let value = event.get_value(); + let capability = event.as_capability(); + match capability { + Capability::None => (), + Capability::NotImplemented => (), + Capability::Sync => (), + Capability::DBus(_) => (), + Capability::Gamepad(gamepad) => match gamepad { + Gamepad::Button(btn) => match btn { + GamepadButton::South => self.state.a = event.pressed(), + GamepadButton::East => self.state.b = event.pressed(), + GamepadButton::North => self.state.x = event.pressed(), + GamepadButton::West => self.state.y = event.pressed(), + GamepadButton::Start => self.state.menu = event.pressed(), + GamepadButton::Select => self.state.options = event.pressed(), + GamepadButton::Guide => self.state.steam = event.pressed(), + GamepadButton::QuickAccess => self.state.quick_access = event.pressed(), + GamepadButton::DPadUp => self.state.up = event.pressed(), + GamepadButton::DPadDown => self.state.down = event.pressed(), + GamepadButton::DPadLeft => self.state.left = event.pressed(), + GamepadButton::DPadRight => self.state.right = event.pressed(), + GamepadButton::LeftBumper => self.state.l1 = event.pressed(), + GamepadButton::LeftTrigger => self.state.l2 = event.pressed(), + GamepadButton::LeftPaddle1 => self.state.l4 = event.pressed(), + GamepadButton::LeftPaddle2 => self.state.l5 = event.pressed(), + GamepadButton::LeftStick => self.state.l3 = event.pressed(), + GamepadButton::LeftStickTouch => self.state.l_stick_touch = event.pressed(), + GamepadButton::RightBumper => self.state.r1 = event.pressed(), + GamepadButton::RightTrigger => self.state.r2 = event.pressed(), + GamepadButton::RightPaddle1 => self.state.r4 = event.pressed(), + GamepadButton::RightPaddle2 => self.state.r5 = event.pressed(), + GamepadButton::RightStick => self.state.r3 = event.pressed(), + GamepadButton::RightStickTouch => self.state.r_stick_touch = event.pressed(), + GamepadButton::LeftPaddle3 => (), + GamepadButton::RightPaddle3 => (), + _ => (), + }, + Gamepad::Axis(axis) => match axis { + GamepadAxis::LeftStick => { + if let InputValue::Vector2 { x, y } = value { + if let Some(x) = x { + let value = denormalize_signed_value(x, STICK_X_MIN, STICK_X_MAX); + self.state.l_stick_x = Integer::from_primitive(value); + } + if let Some(y) = y { + let value = denormalize_signed_value(y, STICK_Y_MIN, STICK_Y_MAX); + self.state.l_stick_y = Integer::from_primitive(value); + } + } + } + GamepadAxis::RightStick => { + if let InputValue::Vector2 { x, y } = value { + if let Some(x) = x { + let value = denormalize_signed_value(x, STICK_X_MIN, STICK_X_MAX); + self.state.r_stick_x = Integer::from_primitive(value); + } + if let Some(y) = y { + let value = denormalize_signed_value(y, STICK_Y_MIN, STICK_Y_MAX); + self.state.r_stick_y = Integer::from_primitive(value); + } + } + } + GamepadAxis::Hat0 => { + if let InputValue::Vector2 { x, y } = value { + if let Some(x) = x { + let value = denormalize_signed_value(x, -1.0, 1.0); + match value.cmp(&0) { + Ordering::Less => { + self.state.left = true; + self.state.right = false; + } + Ordering::Equal => { + self.state.left = false; + self.state.right = false; + } + Ordering::Greater => { + self.state.right = true; + self.state.left = false; + } + } + } + if let Some(y) = y { + let value = denormalize_signed_value(y, -1.0, 1.0); + match value.cmp(&0) { + Ordering::Less => { + self.state.up = true; + self.state.down = false; + } + Ordering::Equal => { + self.state.down = false; + self.state.up = false; + } + Ordering::Greater => { + self.state.down = true; + self.state.up = false; + } + } + } + } + } + GamepadAxis::Hat1 => (), + GamepadAxis::Hat2 => (), + GamepadAxis::Hat3 => (), + }, + Gamepad::Trigger(trigger) => match trigger { + GamepadTrigger::LeftTrigger => { + if let InputValue::Float(value) = value { + self.state.l2 = value > 0.8; + let value = denormalize_unsigned_value(value, TRIGG_MAX); + self.state.l_trigg = Integer::from_primitive(value); + } + } + GamepadTrigger::LeftTouchpadForce => { + if let InputValue::Float(value) = value { + let value = denormalize_unsigned_value(value, PAD_FORCE_MAX); + self.state.l_pad_force = Integer::from_primitive(value); + } + } + GamepadTrigger::LeftStickForce => { + if let InputValue::Float(value) = value { + let value = denormalize_unsigned_value(value, STICK_FORCE_MAX); + self.state.l_stick_force = Integer::from_primitive(value); + } + } + GamepadTrigger::RightTrigger => { + if let InputValue::Float(value) = value { + self.state.r2 = value > 0.8; + let value = denormalize_unsigned_value(value, TRIGG_MAX); + self.state.r_trigg = Integer::from_primitive(value); + } + } + GamepadTrigger::RightTouchpadForce => { + if let InputValue::Float(value) = value { + let value = denormalize_unsigned_value(value, PAD_FORCE_MAX); + self.state.r_pad_force = Integer::from_primitive(value); + } + } + GamepadTrigger::RightStickForce => { + if let InputValue::Float(value) = value { + let value = denormalize_unsigned_value(value, STICK_FORCE_MAX); + self.state.r_stick_force = Integer::from_primitive(value); + } + } + }, + Gamepad::Accelerometer => { + if let InputValue::Vector3 { x, y, z } = value { + if let Some(x) = x { + self.state.accel_x = Integer::from_primitive(x as i16); + } + if let Some(y) = y { + self.state.accel_y = Integer::from_primitive(y as i16); + } + if let Some(z) = z { + self.state.accel_z = Integer::from_primitive(z as i16); + } + } + } + Gamepad::Gyro => { + if let InputValue::Vector3 { x, y, z } = value { + if let Some(x) = x { + self.state.pitch = Integer::from_primitive(x as i16); + } + if let Some(y) = y { + self.state.yaw = Integer::from_primitive(y as i16); + } + if let Some(z) = z { + self.state.roll = Integer::from_primitive(z as i16); + } + } + } + }, + Capability::Mouse(_) => (), + Capability::Keyboard(_) => (), + Capability::Touchpad(touch) => match touch { + Touchpad::LeftPad(touch_event) => match touch_event { + Touch::Motion => { + if let InputValue::Touch { + index: _, + is_touching, + pressure: _, + x, + y, + } = value + { + self.state.l_pad_touch = is_touching; + if let Some(x) = x { + let value = + denormalize_unsigned_to_signed_value(x, PAD_X_MIN, PAD_X_MAX); + self.state.l_pad_x = Integer::from_primitive(value); + }; + if let Some(y) = y { + let value = + denormalize_unsigned_to_signed_value(y, PAD_Y_MIN, PAD_Y_MAX); + self.state.l_pad_y = Integer::from_primitive(value); + }; + } + } + Touch::Button(button) => match button { + TouchButton::Touch => self.state.l_pad_touch = event.pressed(), + TouchButton::Press => self.state.l_pad_press = event.pressed(), + }, + }, + Touchpad::RightPad(touch_event) => match touch_event { + Touch::Motion => { + if let InputValue::Touch { + index: _, + is_touching, + pressure: _, + x, + y, + } = value + { + self.state.r_pad_touch = is_touching; + if let Some(x) = x { + let value = + denormalize_unsigned_to_signed_value(x, PAD_X_MIN, PAD_X_MAX); + self.state.r_pad_x = Integer::from_primitive(value); + }; + if let Some(y) = y { + let value = + denormalize_unsigned_to_signed_value(y, PAD_Y_MIN, PAD_Y_MAX); + self.state.r_pad_y = Integer::from_primitive(value); + }; + } + } + Touch::Button(button) => match button { + TouchButton::Touch => self.state.r_pad_touch = event.pressed(), + TouchButton::Press => self.state.r_pad_press = event.pressed(), + }, + }, + // Treat center pad as a right pad + Touchpad::CenterPad(_) => (), + }, + Capability::Touchscreen(_) => (), + }; + } +} + +impl TargetInputDevice for SteamDeckDevice { + fn write_event(&mut self, event: NativeEvent) -> Result<(), InputError> { + log::trace!("Received event: {event:?}"); + + // Check to see if this is a button event + // In some cases, a button down and button up event can happen within + // the same "frame", which would result in no net state change. This + // allows us to process up events at a later time. + let cap = event.as_capability(); + if let Capability::Gamepad(Gamepad::Button(_)) = cap { + if event.pressed() { + log::trace!("Button down: {cap:?}"); + // Keep track of button down events + self.pressed_events.insert(cap.clone(), Instant::now()); + } else { + log::trace!("Button up: {cap:?}"); + // If the event is a button up event, check to + // see if we received a down event in the same + // frame. + if let Some(last_pressed) = self.pressed_events.get(&cap) { + log::trace!("Button was pressed {:?} ago", last_pressed.elapsed()); + if last_pressed.elapsed() < MIN_FRAME_TIME { + log::trace!("Button up & down event received in the same frame. Queueing event for the next frame."); + let scheduled_event = ScheduledNativeEvent::new_with_time( + event, + *last_pressed, + MIN_FRAME_TIME, + ); + self.queued_events.push(scheduled_event); + return Ok(()); + } else { + log::trace!("Removing button from pressed"); + // Button up event should be processed now + self.pressed_events.remove(&cap); + } + } + } + } + + // Update device state with input events + self.update_state(event); + + Ok(()) + } + + fn get_capabilities(&self) -> Result, InputError> { + Ok(vec![ + Capability::Gamepad(Gamepad::Accelerometer), + Capability::Gamepad(Gamepad::Axis(GamepadAxis::LeftStick)), + Capability::Gamepad(Gamepad::Axis(GamepadAxis::RightStick)), + Capability::Gamepad(Gamepad::Button(GamepadButton::DPadDown)), + Capability::Gamepad(Gamepad::Button(GamepadButton::DPadLeft)), + Capability::Gamepad(Gamepad::Button(GamepadButton::DPadRight)), + Capability::Gamepad(Gamepad::Button(GamepadButton::DPadUp)), + Capability::Gamepad(Gamepad::Button(GamepadButton::East)), + Capability::Gamepad(Gamepad::Button(GamepadButton::Guide)), + Capability::Gamepad(Gamepad::Button(GamepadButton::LeftBumper)), + Capability::Gamepad(Gamepad::Button(GamepadButton::LeftPaddle1)), + Capability::Gamepad(Gamepad::Button(GamepadButton::LeftPaddle2)), + Capability::Gamepad(Gamepad::Button(GamepadButton::LeftStick)), + Capability::Gamepad(Gamepad::Button(GamepadButton::LeftStickTouch)), + Capability::Gamepad(Gamepad::Button(GamepadButton::LeftTrigger)), + Capability::Gamepad(Gamepad::Button(GamepadButton::North)), + Capability::Gamepad(Gamepad::Button(GamepadButton::QuickAccess)), + Capability::Gamepad(Gamepad::Button(GamepadButton::RightBumper)), + Capability::Gamepad(Gamepad::Button(GamepadButton::RightPaddle1)), + Capability::Gamepad(Gamepad::Button(GamepadButton::RightPaddle2)), + Capability::Gamepad(Gamepad::Button(GamepadButton::RightStick)), + Capability::Gamepad(Gamepad::Button(GamepadButton::RightStickTouch)), + Capability::Gamepad(Gamepad::Button(GamepadButton::RightTrigger)), + Capability::Gamepad(Gamepad::Button(GamepadButton::Select)), + Capability::Gamepad(Gamepad::Button(GamepadButton::South)), + Capability::Gamepad(Gamepad::Button(GamepadButton::Start)), + Capability::Gamepad(Gamepad::Button(GamepadButton::West)), + Capability::Gamepad(Gamepad::Gyro), + Capability::Gamepad(Gamepad::Trigger(GamepadTrigger::LeftTrigger)), + Capability::Gamepad(Gamepad::Trigger(GamepadTrigger::RightTrigger)), + Capability::Touchpad(Touchpad::LeftPad(Touch::Button(TouchButton::Press))), + Capability::Touchpad(Touchpad::LeftPad(Touch::Button(TouchButton::Touch))), + Capability::Touchpad(Touchpad::LeftPad(Touch::Motion)), + Capability::Touchpad(Touchpad::RightPad(Touch::Button(TouchButton::Press))), + Capability::Touchpad(Touchpad::RightPad(Touch::Button(TouchButton::Touch))), + Capability::Touchpad(Touchpad::RightPad(Touch::Motion)), + ]) + } + + fn scheduled_events(&mut self) -> Option> { + if self.queued_events.is_empty() { + return None; + } + Some(self.queued_events.drain(..).collect()) + } + + fn stop(&mut self) -> Result<(), InputError> { + self.device.stop(); + Ok(()) + } +} + +impl TargetOutputDevice for SteamDeckDevice { + /// Update the virtual device with its current state, and read unhandled + /// USB transfers. + fn poll(&mut self) -> Result, OutputError> { + // Increment the frame + let frame = self.state.frame.to_primitive(); + self.state.frame = Integer::from_primitive(frame.wrapping_add(1)); + + // Read from the device + let xfer = self.device.blocking_read()?; + + // Handle any non-standard transfers + if let Some(xfer) = xfer { + let reply = self.handle_xfer(xfer); + + // Write to the device if a reply is necessary + if let Some(reply) = reply { + self.device.write(reply)?; + } + } + + Ok(vec![]) + } +} + +impl Debug for SteamDeckDevice { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("SteamDeckDevice") + .field("device", &self.device) + .field("state", &self.state) + .field("lizard_mode_enabled", &self.lizard_mode_enabled) + .field("serial_number", &self.serial_number) + .finish() + } +} + +/// Convert the given normalized signed value to the real value based on the given +/// minimum and maximum axis range. +fn denormalize_signed_value(normal_value: f64, min: f64, max: f64) -> i16 { + let mid = (max + min) / 2.0; + let normal_value_abs = normal_value.abs(); + if normal_value >= 0.0 { + let maximum = max - mid; + let value = normal_value * maximum + mid; + value as i16 + } else { + let minimum = min - mid; + let value = normal_value_abs * minimum + mid; + value as i16 + } +} + +/// Convert the given normalized unsigned value to the real value based on the given +/// minimum and maximum axis range. +fn denormalize_unsigned_to_signed_value(normal_value: f64, min: f64, max: f64) -> i16 { + let normal_value = (normal_value * 2.0) - 1.0; + denormalize_signed_value(normal_value, min, max) +} + +/// De-normalizes the given value from 0.0 - 1.0 into a real value based on +/// the maximum axis range. +fn denormalize_unsigned_value(normal_value: f64, max: f64) -> u16 { + (normal_value * max).round() as u16 +}