diff --git a/rootfs/usr/share/inputplumber/devices/50-legion_go.yaml b/rootfs/usr/share/inputplumber/devices/50-legion_go.yaml index fd747b25..ddacee3b 100644 --- a/rootfs/usr/share/inputplumber/devices/50-legion_go.yaml +++ b/rootfs/usr/share/inputplumber/devices/50-legion_go.yaml @@ -71,9 +71,9 @@ source_devices: exclude: - "*" - ## HIDRAWs + # HIDRAWs + ## Gamepad ## XInput - Connected 0x6182 (Go PID) - # Gamepad - group: gamepad unique: false hidraw: @@ -82,7 +82,6 @@ source_devices: interface_num: 2 ## XInput - Connected 0x61eb (Go 2 PID) - # Gamepad - group: gamepad unique: false hidraw: @@ -91,7 +90,6 @@ source_devices: interface_num: 2 ## DInput - Attached 0x6183 (Go PID) - # Gamepad - group: gamepad # Dinput report unique: false hidraw: @@ -106,7 +104,6 @@ source_devices: interface_num: 2 ## DInput - Attached 0x61ec (Go 2 PID) - # Gamepad - group: gamepad # Dinput report unique: false hidraw: @@ -121,7 +118,6 @@ source_devices: interface_num: 2 ## DInput - Detached 0x6184 (Go PID) - # Gamepad - group: gamepad unique: false hidraw: @@ -130,7 +126,6 @@ source_devices: interface_num: 2 ## DInput - Detached 0x61ed (Go 2 PID) - # Gamepad - group: gamepad unique: false hidraw: @@ -139,7 +134,6 @@ source_devices: interface_num: 2 ## FPS Mode - 0x6185 (Go PID) - # Gamepad - group: gamepad unique: false hidraw: @@ -148,7 +142,6 @@ source_devices: interface_num: 2 ## FPS Mode - 0x61ee (Go 2 PID) - # Gamepad - group: gamepad unique: false hidraw: @@ -156,6 +149,71 @@ source_devices: product_id: 0x61ee interface_num: 2 + ## Touchpad + ## XInput - Connected 0x6182 (Go PID) + - group: gamepad + unique: false + hidraw: + vendor_id: 0x17ef + product_id: 0x6182 + interface_num: 1 + + ## XInput - Connected 0x61eb (Go 2 PID) + - group: gamepad + unique: false + hidraw: + vendor_id: 0x17ef + product_id: 0x61eb + interface_num: 1 + + ## DInput - Attached 0x6183 (Go PID) + - group: gamepad # Dinput report + unique: false + hidraw: + vendor_id: 0x17ef + product_id: 0x6183 + interface_num: 1 + + ## DInput - Attached 0x61ec (Go 2 PID) + - group: gamepad # Dinput report + unique: false + hidraw: + vendor_id: 0x17ef + product_id: 0x61ec + interface_num: 1 + + ## DInput - Detached 0x6184 (Go PID) + - group: gamepad + unique: false + hidraw: + vendor_id: 0x17ef + product_id: 0x6184 + interface_num: 1 + + ## DInput - Detached 0x61ed (Go 2 PID) + - group: gamepad + unique: false + hidraw: + vendor_id: 0x17ef + product_id: 0x61ed + interface_num: 1 + + ## FPS Mode - 0x6185 (Go PID) + - group: gamepad + unique: false + hidraw: + vendor_id: 0x17ef + product_id: 0x6185 + interface_num: 1 + + ## FPS Mode - 0x61ee (Go 2 PID) + - group: gamepad + unique: false + hidraw: + vendor_id: 0x17ef + product_id: 0x61ee + interface_num: 1 + # IMU - group: imu iio: diff --git a/rootfs/usr/share/inputplumber/devices/50-legion_go_2.yaml b/rootfs/usr/share/inputplumber/devices/50-legion_go_2.yaml index 4d6caabf..f569d0e5 100644 --- a/rootfs/usr/share/inputplumber/devices/50-legion_go_2.yaml +++ b/rootfs/usr/share/inputplumber/devices/50-legion_go_2.yaml @@ -71,9 +71,9 @@ source_devices: exclude: - "*" - ## HIDRAWs + # HIDRAWs + ## Gamepad ## XInput - Connected 0x61eb (Go 2 PID) - # Gamepad - group: gamepad unique: false hidraw: @@ -81,8 +81,7 @@ source_devices: product_id: 0x61eb interface_num: 2 - ## DInput - Attached 0x61ec (Go 2 PID) - # Gamepad + ## DInput - Attached - group: gamepad # Dinput report unique: false hidraw: @@ -96,8 +95,7 @@ source_devices: product_id: 0x61ec interface_num: 2 - ## DInput - Detached 0x61ed (Go 2 PID) - # Gamepad + ## DInput - Detached - group: gamepad unique: false hidraw: @@ -105,8 +103,7 @@ source_devices: product_id: 0x61ed interface_num: 2 - ## FPS Mode - 0x61ee (Go 2 PID) - # Gamepad + ## FPS Mode - group: gamepad unique: false hidraw: @@ -114,6 +111,39 @@ source_devices: product_id: 0x61ee interface_num: 2 + ## Touchpad + ## XInput - Connected + - group: gamepad + unique: false + hidraw: + vendor_id: 0x17ef + product_id: 0x61eb + interface_num: 1 + + ## DInput - Attached + - group: gamepad # Dinput report + unique: false + hidraw: + vendor_id: 0x17ef + product_id: 0x61ec + interface_num: 1 + + ## DInput - Detached + - group: gamepad + unique: false + hidraw: + vendor_id: 0x17ef + product_id: 0x61ed + interface_num: 1 + + ## FPS Mode + - group: gamepad + unique: false + hidraw: + vendor_id: 0x17ef + product_id: 0x61ee + interface_num: 1 + # Optional configuration for the composite device options: # If true, InputPlumber will automatically try to manage the input device. If diff --git a/src/drivers/lego/go1_driver.rs b/src/drivers/lego/go1_driver.rs index 9bd9bd69..ce67dc63 100644 --- a/src/drivers/lego/go1_driver.rs +++ b/src/drivers/lego/go1_driver.rs @@ -3,7 +3,6 @@ use std::{error::Error, ffi::CString}; use hidapi::HidDevice; use packed_struct::PackedStruct; -use tokio::time::Instant; use crate::input::capability::Capability; use crate::udev::device::UdevDevice; @@ -11,11 +10,10 @@ use crate::udev::device::UdevDevice; use super::{ event::{ AxisEvent, BinaryInput, Event, GamepadButtonEvent, JoyAxisInput, MouseWheelInput, - TouchAxisInput, TouchButtonEvent, TriggerEvent, TriggerInput, + TriggerEvent, TriggerInput, }, hid_report::{GamepadMode, XInputDataReport}, - CLICK_DELAY, GO1_PIDS, GP_IID, HID_TIMEOUT, PAD_FORCE_NORMAL, RELEASE_DELAY, VID, - XINPUT_COMMAND_ID, XINPUT_DATA, XINPUT_PACKET_SIZE, + GO1_PIDS, GP_IID, GP_TIMEOUT, VID, XINPUT_COMMAND_ID, XINPUT_DATA, XINPUT_PACKET_SIZE, }; pub struct Driver { @@ -23,16 +21,6 @@ pub struct Driver { hid_device: HidDevice, /// List of events that should not be generated filtered_events: HashSet, - /// Timestamp of the first touch event. - first_touch: Instant, - /// Whether or not we are currently holding a click-to-click. - is_clicked: bool, - /// Whether or not we are detecting a touch event currently. - is_touching: bool, - /// Timestamp of the last touch event. - last_touch: Instant, - /// Whether or not a touch event was started that hasn't been cleared. - touch_started: bool, /// State for the internal gamepad controller state: Option, } @@ -55,11 +43,6 @@ impl Driver { Ok(Self { hid_device, filtered_events: Default::default(), - first_touch: Instant::now(), - is_clicked: false, - is_touching: false, - last_touch: Instant::now(), - touch_started: false, state: None, }) } @@ -76,7 +59,7 @@ impl Driver { pub fn poll(&mut self) -> Result, Box> { // Read data from the device into a buffer let mut buf = [0; XINPUT_PACKET_SIZE]; - let bytes_read = self.hid_device.read_timeout(&mut buf[..], HID_TIMEOUT)?; + let bytes_read = self.hid_device.read_timeout(&mut buf[..], GP_TIMEOUT)?; if bytes_read > XINPUT_PACKET_SIZE { return Err("Invalid packet size for X-Input Data.".into()); @@ -352,14 +335,18 @@ impl Driver { }))); } if state.mouse_z != old_state.mouse_z { - log::debug!("Raw mouse value: {:b}, {}", state.mouse_z, !state.mouse_z); + log::trace!( + "Raw scroll wheel value: {:b}, {}", + state.mouse_z, + !state.mouse_z + ); let value = state.mouse_z.wrapping_sub(128) as i8; let value = match value { 64..=127 => value - 127 - 1, v => v, }; - log::debug!("Normalized mouse value: {value}"); + log::trace!("Normalized scroll wheel value: {value}"); events.push(Event::Trigger(TriggerEvent::MouseWheel(MouseWheelInput { value, }))); @@ -372,101 +359,7 @@ impl Driver { log::trace!("Left controller connected state: {:?}", state.l_con_state); log::trace!("Right controller connected state: {:?}", state.r_con_state); } - - // Touchpad events - // Detect if we are touching or not, x, y will always be 0, 0 when the pad is not - // touched. - self.is_touching = state.touch_x != 0 && state.touch_y != 0; - - // Handle touching - if self.is_touching { - self.last_touch = Instant::now(); - - // If this is the first event of a new touch, log the time. - if !self.touch_started { - log::trace!("START Touch"); - log::trace!("Last touch elapsed: {:?}", self.last_touch.elapsed()); - - self.touch_started = true; - self.first_touch = Instant::now(); - } - // Handle tap to click - } else if !self.is_touching - && self.touch_started - && self.first_touch.elapsed() < CLICK_DELAY - { - // Handle double click - if self.is_clicked && self.first_touch.elapsed() < RELEASE_DELAY { - log::trace!("Double Click"); - let mut new_events = self.release_click(); - events.append(&mut new_events); - } - let mut click_events = self.start_click(); - events.append(&mut click_events); - // Handle release events - } else if !self.is_touching && self.last_touch.elapsed() > RELEASE_DELAY { - // Unclick if we we clicking and are no longer touching. - if self.is_clicked { - let mut new_events = self.release_click(); - events.append(&mut new_events); - } - - // Clear this touch sequence - if self.touch_started { - self.touch_started = false; - log::trace!("END Touch"); - } - } - - if state.touch_x != old_state.touch_x || state.touch_y != old_state.touch_y { - events.push(Event::Axis(AxisEvent::Touchpad(TouchAxisInput { - index: 0, - is_touching: self.is_touching, - x: state.touch_x, - y: state.touch_y, - }))); - } - } - events - } - - fn start_click(&mut self) -> Vec { - if self.is_clicked { - log::trace!("Rejecting extra click"); - return vec![]; } - log::trace!("Started CLICK event."); - log::trace!("First touch elapsed: {:?}", self.first_touch.elapsed()); - log::trace!("Last touch elapsed: {:?}", self.last_touch.elapsed()); - self.is_clicked = true; - let mut events = Vec::new(); - - let event = Event::TouchButton(TouchButtonEvent::Left(BinaryInput { pressed: true })); - events.push(event); - // The touchpad doesn't have a force sensor. The deck target wont produce a "click" - // event in desktop or lizard mode without a force value. Simulate a 1/4 press to work - // around this. - let event = Event::Trigger(TriggerEvent::RpadForce(TriggerInput { - value: PAD_FORCE_NORMAL, - })); - events.push(event); - events - } - - fn release_click(&mut self) -> Vec { - log::trace!("Released CLICK event."); - log::trace!("First touch elapsed: {:?}", self.first_touch.elapsed()); - log::trace!("Last touch elapsed: {:?}", self.last_touch.elapsed()); - self.is_clicked = false; - self.touch_started = false; - let mut events = Vec::new(); - let event = Event::TouchButton(TouchButtonEvent::Left(BinaryInput { pressed: false })); - events.push(event); - // The touchpad doesn't have a force sensor. The deck target wont produce a "click" - // event in desktop or lizard mode without a force value. Simulate a 1/4 press to work - // around this. - let event = Event::Trigger(TriggerEvent::RpadForce(TriggerInput { value: 0 })); - events.push(event); events } } diff --git a/src/drivers/lego/go2_driver.rs b/src/drivers/lego/go2_driver.rs index fddb1a75..c8cf05af 100644 --- a/src/drivers/lego/go2_driver.rs +++ b/src/drivers/lego/go2_driver.rs @@ -3,19 +3,19 @@ use std::{error::Error, ffi::CString}; use hidapi::HidDevice; use packed_struct::PackedStruct; -use tokio::time::Instant; +use crate::drivers::lego::GP_TIMEOUT; use crate::input::capability::{Capability, Source}; use crate::udev::device::UdevDevice; use super::{ event::{ AxisEvent, BinaryInput, Event, GamepadButtonEvent, ImuAxisInput, JoyAxisInput, - MouseWheelInput, TouchAxisInput, TouchButtonEvent, TriggerEvent, TriggerInput, + MouseWheelInput, TriggerEvent, TriggerInput, }, hid_report::{GamepadMode, XInputDataReport}, - CLICK_DELAY, DEFAULT_EVENT_FILTER, GO2_PIDS, GP_IID, HID_TIMEOUT, PAD_FORCE_NORMAL, - RELEASE_DELAY, VID, XINPUT_COMMAND_ID, XINPUT_DATA, XINPUT_PACKET_SIZE, + DEFAULT_EVENT_FILTER, GO2_PIDS, GP_IID, VID, XINPUT_COMMAND_ID, XINPUT_DATA, + XINPUT_PACKET_SIZE, }; pub struct Driver { @@ -25,16 +25,6 @@ pub struct Driver { udev_device: UdevDevice, /// List of events that should not be generated filtered_events: HashSet, - /// Timestamp of the first touch event. - first_touch: Instant, - /// Whether or not we are currently holding a click-to-click. - is_clicked: bool, - /// Whether or not we are detecting a touch event currently. - is_touching: bool, - /// Timestamp of the last touch event. - last_touch: Instant, - /// Whether or not a touch event was started that hasn't been cleared. - touch_started: bool, /// State for the internal gamepad controller state: Option, } @@ -58,11 +48,6 @@ impl Driver { udev_device, hid_device, filtered_events: Default::default(), - first_touch: Instant::now(), - is_clicked: false, - is_touching: false, - last_touch: Instant::now(), - touch_started: false, state: None, }) } @@ -109,7 +94,7 @@ impl Driver { pub fn poll(&mut self) -> Result, Box> { // Read data from the device into a buffer let mut buf = [0; XINPUT_PACKET_SIZE]; - let bytes_read = self.hid_device.read_timeout(&mut buf[..], HID_TIMEOUT)?; + let bytes_read = self.hid_device.read_timeout(&mut buf[..], GP_TIMEOUT)?; if bytes_read > XINPUT_PACKET_SIZE { return Err("Invalid packet size for X-Input Data.".into()); @@ -404,14 +389,18 @@ impl Driver { }))); } if state.mouse_z != old_state.mouse_z { - log::debug!("Raw mouse value: {:b}, {}", state.mouse_z, !state.mouse_z); + log::trace!( + "Raw scroll wheel value: {:b}, {}", + state.mouse_z, + !state.mouse_z + ); let value = state.mouse_z.wrapping_sub(128) as i8; let value = match value { 64..=127 => value - 127 - 1, v => v, }; - log::debug!("Normalized mouse value: {value}"); + log::trace!("Normalized scroll wheel value: {value}"); events.push(Event::Trigger(TriggerEvent::MouseWheel(MouseWheelInput { value, }))); @@ -509,101 +498,7 @@ impl Driver { yaw: (state.left_gyro_z + state.right_gyro_z) / 2, }))) } - - // Touchpad events - // Detect if we are touching or not, x, y will always be 0, 0 when the pad is not - // touched. - self.is_touching = state.touch_x != 0 && state.touch_y != 0; - - // Handle touching - if self.is_touching { - self.last_touch = Instant::now(); - - // If this is the first event of a new touch, log the time. - if !self.touch_started { - log::trace!("START Touch"); - log::trace!("Last touch elapsed: {:?}", self.last_touch.elapsed()); - - self.touch_started = true; - self.first_touch = Instant::now(); - } - // Handle tap to click - } else if !self.is_touching - && self.touch_started - && self.first_touch.elapsed() < CLICK_DELAY - { - // Handle double click - if self.is_clicked && self.first_touch.elapsed() < RELEASE_DELAY { - log::trace!("Double Click"); - let mut new_events = self.release_click(); - events.append(&mut new_events); - } - let mut click_events = self.start_click(); - events.append(&mut click_events); - // Handle release events - } else if !self.is_touching && self.last_touch.elapsed() > RELEASE_DELAY { - // Unclick if we we clicking and are no longer touching. - if self.is_clicked { - let mut new_events = self.release_click(); - events.append(&mut new_events); - } - - // Clear this touch sequence - if self.touch_started { - self.touch_started = false; - log::trace!("END Touch"); - } - } - - if state.touch_x != old_state.touch_x || state.touch_y != old_state.touch_y { - events.push(Event::Axis(AxisEvent::Touchpad(TouchAxisInput { - index: 0, - is_touching: self.is_touching, - x: state.touch_x, - y: state.touch_y, - }))); - } - } - events - } - - fn start_click(&mut self) -> Vec { - if self.is_clicked { - log::trace!("Rejecting extra click"); - return vec![]; } - log::trace!("Started CLICK event."); - log::trace!("First touch elapsed: {:?}", self.first_touch.elapsed()); - log::trace!("Last touch elapsed: {:?}", self.last_touch.elapsed()); - self.is_clicked = true; - let mut events = Vec::new(); - - let event = Event::TouchButton(TouchButtonEvent::Left(BinaryInput { pressed: true })); - events.push(event); - // The touchpad doesn't have a force sensor. The deck target wont produce a "click" - // event in desktop or lizard mode without a force value. Simulate a 1/4 press to work - // around this. - let event = Event::Trigger(TriggerEvent::RpadForce(TriggerInput { - value: PAD_FORCE_NORMAL, - })); - events.push(event); - events - } - - fn release_click(&mut self) -> Vec { - log::trace!("Released CLICK event."); - log::trace!("First touch elapsed: {:?}", self.first_touch.elapsed()); - log::trace!("Last touch elapsed: {:?}", self.last_touch.elapsed()); - self.is_clicked = false; - self.touch_started = false; - let mut events = Vec::new(); - let event = Event::TouchButton(TouchButtonEvent::Left(BinaryInput { pressed: false })); - events.push(event); - // The touchpad doesn't have a force sensor. The deck target wont produce a "click" - // event in desktop or lizard mode without a force value. Simulate a 1/4 press to work - // around this. - let event = Event::Trigger(TriggerEvent::RpadForce(TriggerInput { value: 0 })); - events.push(event); events } } diff --git a/src/drivers/lego/go_touchpad_driver.rs b/src/drivers/lego/go_touchpad_driver.rs new file mode 100644 index 00000000..776fe108 --- /dev/null +++ b/src/drivers/lego/go_touchpad_driver.rs @@ -0,0 +1,221 @@ +use std::{error::Error, ffi::CString, time::Instant}; + +use hidapi::HidDevice; +use packed_struct::PackedStruct; + +use crate::drivers::lego::{ + event::{TriggerEvent, TriggerInput}, + PAD_FORCE_NORMAL, RELEASE_DELAY, +}; + +use super::{ + event::{AxisEvent, BinaryInput, Event, TouchAxisInput, TouchButtonEvent}, + hid_report::TouchpadDataReport, + GO1_PIDS, GO2_PIDS, TOUCHPAD_DATA, TOUCHPAD_PACKET_SIZE, TP_IID, TP_TIMEOUT, VID, +}; + +pub struct Driver { + /// HIDRAW device instance + device: HidDevice, + /// Timestamp of the first touch event. + first_touch: Instant, + /// Whether or not we are currently holding a click-to-click. + is_clicked: bool, + /// Whether or not we are detecting a touch event currently. + is_touching: bool, + /// Timestamp of the last touch event. + last_touch: Instant, + /// Whether or not a touch event was started that hasn't been cleared. + touch_started: bool, + /// State for the touchpad device + touchpad_state: Option, +} + +impl Driver { + pub fn new(path: String) -> Result> { + let fmtpath = path.clone(); + let path = CString::new(path)?; + let api = hidapi::HidApi::new()?; + let device = api.open_path(&path)?; + let info = device.get_device_info()?; + + if info.vendor_id() != VID + || (!GO1_PIDS.contains(&info.product_id()) && !GO2_PIDS.contains(&info.product_id())) + || info.interface_number() != TP_IID + { + return Err(format!("Device '{fmtpath}' is not a Legion Go Touchpad").into()); + } + + Ok(Self { + device, + first_touch: Instant::now(), + is_clicked: false, + is_touching: false, + last_touch: Instant::now(), + touch_started: false, + touchpad_state: None, + }) + } + + /// Poll the device and read input reports + pub fn poll(&mut self) -> Result, Box> { + // Read data from the device into a buffer + let mut buf = [0; TOUCHPAD_PACKET_SIZE]; + let bytes_read = self.device.read_timeout(&mut buf[..], TP_TIMEOUT)?; + + let report_id = buf[0]; + let slice = &buf[..bytes_read]; + log::trace!("Got Report ID: {report_id}"); + log::trace!("Got Report Size: {bytes_read}"); + + let old_state = match report_id { + TOUCHPAD_DATA => { + if bytes_read != TOUCHPAD_PACKET_SIZE { + return Err("Invalid packet size for Keyboard or Touchpad Data.".into()); + } + // Handle the incoming input report + let sized_buf = slice.try_into()?; + + self.handle_touchinput_report(sized_buf)? + } + _ => self.touchpad_state, + }; + + Ok(self.translate_touch(old_state)) + } + + /// Unpacks the buffer into a [TouchpadDataReport] structure and updates + /// the internal touchpad_state + fn handle_touchinput_report( + &mut self, + buf: [u8; TOUCHPAD_PACKET_SIZE], + ) -> Result, Box> { + let input_report = TouchpadDataReport::unpack(&buf)?; + + // Print input report for debugging + //log::trace!("--- Input report ---"); + //log::trace!("{input_report}"); + //log::trace!("---- End Report ----"); + + // Update the state + Ok(self.update_touchpad_state(input_report)) + } + + /// Update touchinput state + fn update_touchpad_state( + &mut self, + input_report: TouchpadDataReport, + ) -> Option { + let old_state = self.touchpad_state; + self.touchpad_state = Some(input_report); + old_state + } + + /// Translate the state into individual events + fn translate_touch(&mut self, old_state: Option) -> Vec { + let mut events = Vec::new(); + let Some(mut state) = self.touchpad_state else { + return events; + }; + + let Some(old_state) = old_state else { + return events; + }; + + // Translate state changes into events if they have changed + self.is_touching = old_state.scan_time != state.scan_time; + + if state.touch_x_0 != old_state.touch_x_0 || state.touch_y_0 != old_state.touch_y_0 { + events.push(Event::Axis(AxisEvent::Touchpad(TouchAxisInput { + index: 0, + is_touching: self.is_touching, + x: state.touch_x_0, + y: state.touch_y_0, + }))); + } + + if self.is_touching && !self.touch_started { + // If this is the first event of a new touch, log the time. + if !self.touch_started { + log::trace!("START Touch"); + log::trace!("Last touch elapsed: {:?}", self.last_touch.elapsed()); + + self.touch_started = true; + self.first_touch = Instant::now(); + } + } else if !self.is_touching { + if self.touch_started { + // Handle tap to click + if self.first_touch.elapsed() < RELEASE_DELAY && !self.is_clicked { + let mut click_events = self.start_click(); + events.append(&mut click_events); + return events; + } + + // Handle release events + self.touch_started = false; + state.touch_x_0 = 0; + state.touch_y_0 = 0; + self.touchpad_state = Some(state); + events.push(Event::Axis(AxisEvent::Touchpad(TouchAxisInput { + index: 0, + is_touching: self.is_touching, + x: state.touch_x_0, + y: state.touch_y_0, + }))); + log::trace!("First touch elapsed: {:?}", self.first_touch.elapsed()); + log::trace!("END Touch"); + } + + // Unclick if we were clicking and are no longer touching. + if self.is_clicked { + let mut new_events = self.release_click(); + events.append(&mut new_events); + } + } + + if self.is_touching { + self.last_touch = Instant::now(); + } + events + } + + fn start_click(&mut self) -> Vec { + if self.is_clicked { + log::trace!("Rejecting extra click"); + return vec![]; + } + log::trace!("Started CLICK event."); + log::trace!("First touch elapsed: {:?}", self.first_touch.elapsed()); + log::trace!("Last touch elapsed: {:?}", self.last_touch.elapsed()); + self.is_clicked = true; + let mut events = Vec::new(); + + let event = Event::TouchButton(TouchButtonEvent::Left(BinaryInput { pressed: true })); + events.push(event); + // The touchpad doesn't have a force sensor. The deck target wont produce a "click" + // event in desktop or lizard mode without a force value. Simulate a 1/4 press to work + // around this. + let event = Event::Trigger(TriggerEvent::RpadForce(TriggerInput { + value: PAD_FORCE_NORMAL, + })); + events.push(event); + events + } + + fn release_click(&mut self) -> Vec { + log::trace!("Released CLICK event."); + log::trace!("First touch elapsed: {:?}", self.first_touch.elapsed()); + log::trace!("Last touch elapsed: {:?}", self.last_touch.elapsed()); + self.is_clicked = false; + let mut events = Vec::new(); + let event = Event::TouchButton(TouchButtonEvent::Left(BinaryInput { pressed: false })); + events.push(event); + // The touchpad doesn't have a force sensor. The deck target wont produce a "click" + // event in desktop or lizard mode without a force value. Simulate a 1/4 press to work + // around this. + let event = Event::Trigger(TriggerEvent::RpadForce(TriggerInput { value: 0 })); + events.push(event); + events + } +} diff --git a/src/drivers/lego/hid_report.rs b/src/drivers/lego/hid_report.rs index f00e266e..755f2587 100644 --- a/src/drivers/lego/hid_report.rs +++ b/src/drivers/lego/hid_report.rs @@ -116,187 +116,6 @@ impl Display for ConnectedState { } } -//XInputData -// -// -// No Input -// # ReportID: 4 / 0xffa00003: 60 , 116 , 1 , 0 , 100 , 4 , 99 , 4 , 1 , 1 , 1 , 2 , 2 , -128 , -128 , -128 , -128 , 0 , 0 , 0 , 0 , 0 , 0 , 2 , -128 , 0 , 0 , 0 , 0 , -128 , -128 , -128 , -128 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 -// E: 000015.817820 64 04 3c 74 01 00 64 04 63 04 01 01 01 02 02 80 80 80 80 00 00 00 00 00 00 02 80 00 00 00 00 80 80 80 80 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 -// -// Axes -// -// Left Stick Right -// # ReportID: 4 / 0xffa00003: 60 , 116 , 1 , 0 , 100 , 4 , 100 , 4 , 1 , 1 , 1 , 2 , 2 , -1 , -128 , -128 , -128 , 0 , 0 , 0 , 0 , 0 , 0 , 2 , -128 , 0 , 0 , 0 , 0 , -128 , -128 , -128 , -128 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 -// E: 000003.938371 64 04 3c 74 01 00 64 04 64 04 01 01 01 02 02 ff 80 80 80 00 00 00 00 00 00 02 80 00 00 00 00 80 80 80 80 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 -// -// Left Stick Left -// # ReportID: 4 / 0xffa00003: 60 , 116 , 1 , 0 , 100 , 4 , 100 , 4 , 1 , 1 , 1 , 2 , 2 , 0 , -111 , -128 , -128 , 0 , 0 , 0 , 0 , 0 , 0 , 2 , -128 , 0 , 0 , 0 , 0 , -128 , -128 , -128 , -128 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 -// E: 000009.335838 64 04 3c 74 01 00 64 04 64 04 01 01 01 02 02 00 91 80 80 00 00 00 00 00 00 02 80 00 00 00 00 80 80 80 80 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 -// -// Left Stick Up -// # ReportID: 4 / 0xffa00003: 60 , 116 , 1 , 0 , 100 , 4 , 100 , 4 , 1 , 1 , 1 , 2 , 2 , -124 , 0 , -128 , -128 , 0 , 0 , 0 , 0 , 0 , 0 , 2 , -128 , 0 , 0 , 0 , 0 , -128 , -128 , -128 , -128 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 -// E: 000002.496028 64 04 3c 74 01 00 64 04 64 04 01 01 01 02 02 84 00 80 80 00 00 00 00 00 00 02 80 00 00 00 00 80 80 80 80 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 -// -// Left Stick Down -// # ReportID: 4 / 0xffa00003: 60 , 116 , 1 , 0 , 100 , 4 , 100 , 4 , 1 , 1 , 1 , 2 , 2 , -125 , -1 , -128 , -128 , 0 , 0 , 0 , 0 , 0 , 0 , 2 , -128 , 0 , 0 , 0 , 0 , -128 , -128 , -128 , -128 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 -// E: 000002.015918 64 04 3c 74 01 00 64 04 64 04 01 01 01 02 02 83 ff 80 80 00 00 00 00 00 00 02 80 00 00 00 00 80 80 80 80 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 -// -// -// Right Stick Right -// # ReportID: 4 / 0xffa00003: 60 , 116 , 1 , 0 , 100 , 4 , 100 , 4 , 1 , 1 , 1 , 2 , 2 , -128 , -128 , -1 , -128 , 0 , 0 , 0 , 0 , 0 , 0 , 2 , -128 , 0 , 0 , 0 , 0 , -128 , -128 , -128 , -128 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 -// E: 000002.666060 64 04 3c 74 01 00 64 04 64 04 01 01 01 02 02 80 80 ff 80 00 00 00 00 00 00 02 80 00 00 00 00 80 80 80 80 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 -// -// Right Stick Left -// # ReportID: 4 / 0xffa00003: 60 , 116 , 1 , 0 , 100 , 4 , 100 , 4 , 1 , 1 , 1 , 2 , 2 , -128 , -128 , 0 , -125 , 0 , 0 , 0 , 0 , 0 , 0 , 2 , -128 , 0 , 0 , 0 , 0 , -128 , -128 , -128 , -128 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 -// E: 000002.159802 64 04 3c 74 01 00 64 04 64 04 01 01 01 02 02 80 80 00 83 00 00 00 00 00 00 02 80 00 00 00 00 80 80 80 80 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 -// -// Right Stick Up -// # ReportID: 4 / 0xffa00003: 60 , 116 , 1 , 0 , 100 , 4 , 100 , 4 , 1 , 1 , 1 , 2 , 2 , -128 , -128 , 117 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 2 , -128 , 0 , 0 , 0 , 0 , -128 , -128 , -128 , -128 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 -// E: 000002.015937 64 04 3c 74 01 00 64 04 64 04 01 01 01 02 02 80 80 75 00 00 00 00 00 00 00 02 80 00 00 00 00 80 80 80 80 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 -// -// Right Stick Down -// # ReportID: 4 / 0xffa00003: 60 , 116 , 1 , 0 , 100 , 4 , 100 , 4 , 1 , 1 , 1 , 2 , 2 , -128 , -128 , 125 , -1 , 0 , 0 , 0 , 0 , 0 , 0 , 2 , -128 , 0 , 0 , 0 , 0 , -128 , -128 , -128 , -128 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 -// E: 000001.800014 64 04 3c 74 01 00 64 04 64 04 01 01 01 02 02 80 80 7d ff 00 00 00 00 00 00 02 80 00 00 00 00 80 80 80 80 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 -// -// Touchpad -// -// Touchpad Top Left -// # ReportID: 4 / 0xffa00003: 60 , 116 , 1 , 0 , 100 , 4 , 100 , 4 , 1 , 1 , 1 , 2 , 2 , -128 , -128 , -128 , -128 , 0 , 0 , 0 , 0 , 0 , 0 , 2 , -128 , 0 , 81 , 0 , 107 , -128 , -128 , -128 , -128 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 -// E: 000312.287997 64 04 3c 74 01 00 64 04 64 04 01 01 01 02 02 80 80 80 80 00 00 00 00 00 00 02 80 00 51 00 6b 80 80 80 80 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 -// -// Touchpad Top Right -// # ReportID: 4 / 0xffa00003: 60 , 116 , 1 , 0 , 100 , 4 , 100 , 4 , 1 , 1 , 1 , 2 , 2 , -128 , -128 , -128 , -128 , 0 , 0 , 0 , 0 , 0 , 0 , 2 , -128 , 3 , -116 , 0 , 102 , -128 , -128 , -128 , -128 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 -// E: 000004.201679 64 04 3c 74 01 00 64 04 64 04 01 01 01 02 02 80 80 80 80 00 00 00 00 00 00 02 80 03 8c 00 66 80 80 80 80 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 -// -// Touchpad Bottom Left -// # ReportID: 4 / 0xffa00003: 60 , 116 , 1 , 0 , 100 , 4 , 100 , 4 , 1 , 1 , 1 , 2 , 2 , -128 , -128 , -128 , -128 , 0 , 0 , 0 , 0 , 0 , 0 , 2 , -128 , 0 , 114 , 3 , -109 , -128 , -128 , -128 , -128 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 -// E: 000002.543948 64 04 3c 74 01 00 64 04 64 04 01 01 01 02 02 80 80 80 80 00 00 00 00 00 00 02 80 00 72 03 93 80 80 80 80 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 -// -// Touchpad Bottom Right -// # ReportID: 4 / 0xffa00003: 60 , 116 , 1 , 0 , 100 , 4 , 100 , 4 , 1 , 1 , 1 , 2 , 2 , -128 , -128 , -128 , -128 , 0 , 0 , 0 , 0 , 0 , 0 , 2 , -128 , 3 , -43 , 3 , -64 , -128 , -128 , -128 , -128 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 -// E: 000004.488500 64 04 3c 74 01 00 64 04 64 04 01 01 01 02 02 80 80 80 80 00 00 00 00 00 00 02 80 03 d5 03 c0 80 80 80 80 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 -// -// Touchpad Hold has no effect -// -// Mouse -// -// Mouse Z (Wheel) -// ReportID: 4 / 0xffa00003: 60 , 116 , 1 , 0 , 100 , 4 , 100 , 4 , 1 , 1 , 1 , 2 , 2 , -128 , -128 , -128 , -128 , 0 , 0 , 0 , 0 , 0 , 0 , 2 , -1 , 0 , 0 , 0 , 0 , -128 , -128 , -109 , 125 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 -// E: 000017.760114 64 04 3c 74 01 00 64 04 64 04 01 01 01 02 02 80 80 80 80 00 00 00 00 00 00 02 ff 00 00 00 00 80 80 93 7d 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 -// -// Battery Indicators -// 5 and 7 are left/right controller battery level, 0-100 -// -// Buttons -// -// Legion -// # ReportID: 4 / 0xffa00003: 60 , 116 , 1 , 0 , 100 , 4 , 99 , 4 , 1 , 1 , 1 , 2 , 2 , -128 , -128 , -128 , -128 , -128 , 0 , 0 , 0 , 0 , 0 , 2 , -128 , 0 , 0 , 0 , 0 , -128 , -128 , -128 , -128 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 -// E: 000006.985969 64 04 3c 74 01 00 64 04 63 04 01 01 01 02 02 80 80 80 80 80 00 00 00 00 00 02 80 00 00 00 00 80 80 80 80 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 -// -// Quick Settings -// # ReportID: 4 / 0xffa00003: 60 , 116 , 1 , 0 , 100 , 4 , 99 , 4 , 1 , 1 , 1 , 2 , 2 , -128 , -128 , -128 , -128 , 64 , 0 , 0 , 0 , 0 , 0 , 2 , -128 , 0 , 0 , 0 , 0 , -128 , -128 , -128 , -128 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 -// E: 000004.727910 64 04 3c 74 01 00 64 04 63 04 01 01 01 02 02 80 80 80 80 40 00 00 00 00 00 02 80 00 00 00 00 80 80 80 80 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 -// -// Left Stick -// # ReportID: 4 / 0xffa00003: 60 , 116 , 1 , 0 , 100 , 4 , 100 , 4 , 1 , 1 , 1 , 2 , 2 , 122 , 122 , -128 , -128 , 32 , 0 , 0 , 0 , 0 , 0 , 2 , -128 , 0 , 0 , 0 , 0 , -128 , -128 , -128 , -128 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 -// E: 000033.939427 64 04 3c 74 01 00 64 04 64 04 01 01 01 02 02 7a 7a 80 80 20 00 00 00 00 00 02 80 00 00 00 00 80 80 80 80 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 -// -// Right Stick -// # ReportID: 4 / 0xffa00003: 60 , 116 , 1 , 0 , 100 , 4 , 100 , 4 , 1 , 1 , 1 , 2 , 2 , -128 , -128 , 127 , -128 , 16 , 0 , 0 , 0 , 0 , 0 , 2 , -128 , 0 , 0 , 0 , 0 , -128 , -128 , -128 , -128 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 -// E: 000004.656012 64 04 3c 74 01 00 64 04 64 04 01 01 01 02 02 80 80 7f 80 10 00 00 00 00 00 02 80 00 00 00 00 80 80 80 80 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 -// -// D Pad Up -// # ReportID: 4 / 0xffa00003: 60 , 116 , 1 , 0 , 100 , 4 , 100 , 4 , 1 , 1 , 1 , 2 , 2 , -128 , -128 , -128 , -128 , 8 , 0 , 0 , 0 , 0 , 0 , 2 , -128 , 0 , 0 , 0 , 0 , -128 , -128 , -128 , -128 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 -// E: 000124.808576 64 04 3c 74 01 00 64 04 64 04 01 01 01 02 02 80 80 80 80 08 00 00 00 00 00 02 80 00 00 00 00 80 80 80 80 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 -// -// D Pad Down -// # ReportID: 4 / 0xffa00003: 60 , 116 , 1 , 0 , 100 , 4 , 100 , 4 , 1 , 1 , 1 , 2 , 2 , -128 , -128 , -128 , -128 , 4 , 0 , 0 , 0 , 0 , 0 , 2 , -128 , 0 , 0 , 0 , 0 , -128 , -128 , -128 , -128 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 -// E: 000002.087996 64 04 3c 74 01 00 64 04 64 04 01 01 01 02 02 80 80 80 80 04 00 00 00 00 00 02 80 00 00 00 00 80 80 80 80 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 -// -// D Pad Left -// # ReportID: 4 / 0xffa00003: 60 , 116 , 1 , 0 , 100 , 4 , 100 , 4 , 1 , 1 , 1 , 2 , 2 , -128 , -128 , -128 , -128 , 2 , 0 , 0 , 0 , 0 , 0 , 2 , -128 , 0 , 0 , 0 , 0 , -128 , -128 , -128 , -128 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 -// E: 000004.823918 64 04 3c 74 01 00 64 04 64 04 01 01 01 02 02 80 80 80 80 02 00 00 00 00 00 02 80 00 00 00 00 80 80 80 80 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 -// -// D Pad Right -// E: 000001.895884 64 04 3c 74 01 00 64 04 64 04 01 01 01 02 02 80 80 80 80 01 00 00 00 00 00 02 80 00 00 00 00 80 80 80 80 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 -// # ReportID: 4 / 0xffa00003: 60 , 116 , 1 , 0 , 100 , 4 , 100 , 4 , 1 , 1 , 1 , 2 , 2 , -128 , -128 , -128 , -128 , 1 , 0 , 0 , 0 , 0 , 0 , 2 , -128 , 0 , 0 , 0 , 0 , -128 , -128 , -128 , -128 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 -// E: 000001.919879 64 04 3c 74 01 00 64 04 64 04 01 01 01 02 02 80 80 80 80 01 00 00 00 00 00 02 80 00 00 00 00 80 80 80 80 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 -// -// A -// # ReportID: 4 / 0xffa00003: 60 , 116 , 1 , 0 , 100 , 4 , 100 , 4 , 1 , 1 , 1 , 2 , 2 , -128 , -128 , -128 , -128 , 0 , -128 , 0 , 0 , 0 , 0 , 2 , -128 , 0 , 0 , 0 , 0 , -128 , -128 , -128 , -128 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 -// E: 000002.471810 64 04 3c 74 01 00 64 04 64 04 01 01 01 02 02 80 80 80 80 00 80 00 00 00 00 02 80 00 00 00 00 80 80 80 80 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 -// -// B -// # ReportID: 4 / 0xffa00003: 60 , 116 , 1 , 0 , 100 , 4 , 100 , 4 , 1 , 1 , 1 , 2 , 2 , -128 , -128 , -128 , -128 , 0 , 64 , 0 , 0 , 0 , 0 , 2 , -128 , 0 , 0 , 0 , 0 , -128 , -128 , -128 , -128 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 -// E: 000002.135728 64 04 3c 74 01 00 64 04 64 04 01 01 01 02 02 80 80 80 80 00 40 00 00 00 00 02 80 00 00 00 00 80 80 80 80 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 -// -// X -// # ReportID: 4 / 0xffa00003: 60 , 116 , 1 , 0 , 100 , 4 , 100 , 4 , 1 , 1 , 1 , 2 , 2 , -128 , -128 , -128 , -128 , 0 , 32 , 0 , 0 , 0 , 0 , 2 , -128 , 0 , 0 , 0 , 0 , -128 , -128 , -128 , -128 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 -// E: 000002.447984 64 04 3c 74 01 00 64 04 64 04 01 01 01 02 02 80 80 80 80 00 20 00 00 00 00 02 80 00 00 00 00 80 80 80 80 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 -// -// Y -// # ReportID: 4 / 0xffa00003: 60 , 116 , 1 , 0 , 100 , 4 , 100 , 4 , 1 , 1 , 1 , 2 , 2 , -128 , -128 , -128 , -128 , 0 , 16 , 0 , 0 , 0 , 0 , 2 , -128 , 0 , 0 , 0 , 0 , -128 , -128 , -128 , -128 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 -// E: 000002.039957 64 04 3c 74 01 00 64 04 64 04 01 01 01 02 02 80 80 80 80 00 10 00 00 00 00 02 80 00 00 00 00 80 80 80 80 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 -// -// LB -// # ReportID: 4 / 0xffa00003: 60 , 116 , 1 , 0 , 100 , 4 , 100 , 4 , 1 , 1 , 1 , 2 , 2 , -128 , -128 , -128 , -128 , 0 , 8 , 0 , 0 , 0 , 0 , 2 , -128 , 0 , 0 , 0 , 0 , -128 , -128 , -128 , -128 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 -// E: 000011.640162 64 04 3c 74 01 00 64 04 64 04 01 01 01 02 02 80 80 80 80 00 08 00 00 00 00 02 80 00 00 00 00 80 80 80 80 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 -// -// Left Trigger -// # ReportID: 4 / 0xffa00003: 60 , 116 , 1 , 0 , 100 , 4 , 100 , 4 , 1 , 1 , 1 , 2 , 2 , -128 , 126 , -128 , -128 , 0 , 4 , 0 , 0 , -1 , 0 , 2 , -128 , 0 , 0 , 0 , 0 , -128 , -128 , -128 , -128 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 -// E: 000008.231988 64 04 3c 74 01 00 64 04 64 04 01 01 01 02 02 80 7e 80 80 00 04 00 00 ff 00 02 80 00 00 00 00 80 80 80 80 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 -// -// RB -// # ReportID: 4 / 0xffa00003: 60 , 116 , 1 , 0 , 100 , 4 , 99 , 4 , 1 , 1 , 1 , 2 , 2 , -128 , -128 , -128 , -128 , 0 , 2 , 0 , 0 , 0 , 0 , 2 , -128 , 0 , 0 , 0 , 0 , -128 , -128 , -128 , -128 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 -// E: 000018.817796 64 04 3c 74 01 00 64 04 63 04 01 01 01 02 02 80 80 80 80 00 02 00 00 00 00 02 80 00 00 00 00 80 80 80 80 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 -// -// Right Trigger -// # ReportID: 4 / 0xffa00003: 60 , 116 , 1 , 0 , 100 , 4 , 100 , 4 , 1 , 1 , 1 , 2 , 2 , -128 , -128 , -128 , -128 , 0 , 1 , 0 , 0 , 0 , -1 , 2 , -128 , 0 , 0 , 0 , 0 , -128 , -128 , -128 , -128 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 -// E: 000001.728122 64 04 3c 74 01 00 64 04 64 04 01 01 01 02 02 80 80 80 80 00 01 00 00 00 ff 02 80 00 00 00 00 80 80 80 80 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 -// -// Y1 -// # ReportID: 4 / 0xffa00003: 60 , 116 , 1 , 0 , 100 , 4 , 99 , 4 , 1 , 1 , 1 , 2 , 2 , -128 , -128 , -128 , -128 , 0 , 0 , -128 , 0 , 0 , 0 , 2 , -128 , 0 , 0 , 0 , 0 , -128 , -128 , -128 , -128 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 -// E: 000002.543996 64 04 3c 74 01 00 64 04 63 04 01 01 01 02 02 80 80 80 80 00 00 80 00 00 00 02 80 00 00 00 00 80 80 80 80 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 -// -// Y2 -// # ReportID: 4 / 0xffa00003: 60 , 116 , 1 , 0 , 100 , 4 , 99 , 4 , 1 , 1 , 1 , 2 , 2 , -128 , -128 , -128 , -128 , 0 , 0 , 64 , 0 , 0 , 0 , 2 , -128 , 0 , 0 , 0 , 0 , -128 , -128 , -128 , -128 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 -// E: 000002.807928 64 04 3c 74 01 00 64 04 63 04 01 01 01 02 02 80 80 80 80 00 00 40 00 00 00 02 80 00 00 00 00 80 80 80 80 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 -// -// Y3 -// # ReportID: 4 / 0xffa00003: 60 , 116 , 1 , 0 , 100 , 4 , 99 , 4 , 1 , 1 , 1 , 2 , 2 , -128 , -128 , -128 , -128 , 0 , 0 , 32 , 0 , 0 , 0 , 2 , -128 , 0 , 0 , 0 , 0 , -128 , -128 , -128 , -128 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 -// E: 000002.496009 64 04 3c 74 01 00 64 04 63 04 01 01 01 02 02 80 80 80 80 00 00 20 00 00 00 02 80 00 00 00 00 80 80 80 80 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 -// -// M1 -// # ReportID: 4 / 0xffa00003: 60 , 116 , 1 , 0 , 99 , 4 , 99 , 4 , 1 , 1 , 1 , 2 , 2 , -128 , -128 , -128 , -128 , 0 , 0 , 16 , 0 , 0 , 0 , 2 , -128 , 0 , 0 , 0 , 0 , -128 , -128 , -128 , -128 , 43 , 0 , -113 , 1 , 123 , -16 , 55 , 0 , 0 , 0 , 0 , 0 , 4 , -94 , -2 , 46 , 2 , -8 , -16 , 105 , 0 , 2 , 0 , 1 , 0 , 5 , 0 , 0 , 0 , 0 -// E: 000003.703990 64 04 3c 74 01 00 63 04 63 04 01 01 01 02 02 80 80 80 80 00 00 10 00 00 00 02 80 00 00 00 00 80 80 80 80 2b 00 8f 01 7b f0 37 00 00 00 00 00 04 a2 fe 2e 02 f8 f0 69 00 02 00 01 00 05 00 00 00 00 -// -// M2 -// # ReportID: 4 / 0xffa00003: 60 , 116 , 1 , 0 , 100 , 4 , 99 , 4 , 1 , 1 , 1 , 2 , 2 , -128 , -128 , -128 , -128 , 0 , 0 , 8 , 0 , 0 , 0 , 2 , -128 , 0 , 0 , 0 , 0 , -128 , -128 , -128 , -128 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 -// E: 000008.759948 64 04 3c 74 01 00 64 04 63 04 01 01 01 02 02 80 80 80 80 00 00 08 00 00 00 02 80 00 00 00 00 80 80 80 80 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 -// -// M3 -// # ReportID: 4 / 0xffa00003: 60 , 116 , 1 , 0 , 100 , 4 , 100 , 4 , 1 , 1 , 1 , 2 , 2 , -128 , -128 , -128 , -128 , 0 , 0 , 4 , 0 , 0 , 0 , 2 , -128 , 0 , 0 , 0 , 0 , -128 , -128 , -128 , -128 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 -// E: 000004.079905 64 04 3c 74 01 00 64 04 64 04 01 01 01 02 02 80 80 80 80 00 00 04 00 00 00 02 80 00 00 00 00 80 80 80 80 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 -// -// Select -// # ReportID: 4 / 0xffa00003: 60 , 116 , 1 , 0 , 100 , 4 , 99 , 4 , 1 , 1 , 1 , 2 , 2 , -128 , -128 , -128 , -128 , 0 , 0 , 2 , 0 , 0 , 0 , 2 , -128 , 0 , 0 , 0 , 0 , -128 , -128 , -128 , -128 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 -// E: 000004.440004 64 04 3c 74 01 00 64 04 63 04 01 01 01 02 z02 80 80 80 80 00 00 02 00 00 00 02 80 00 00 00 00 80 80 80 80 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 -// -// Start -// # ReportID: 4 / 0xffa00003: 60 , 116 , 1 , 0 , 100 , 4 , 99 , 4 , 1 , 1 , 1 , 2 , 2 , -128 , -128 , -128 , -128 , 0 , 0 , 1 , 0 , 0 , 0 , 2 , -128 , 0 , 0 , 0 , 0 , -128 , -128 , -128 , -128 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 -// E: 000003.383926 64 04 3c 74 01 00 64 04 63 04 01 01 01 02 02 80 80 80 80 00 00 01 00 00 00 02 80 00 00 00 00 80 80 80 80 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 -// -// Mouse Wheel Click -// # ReportID: 4 / 0xffa00003: 60 , 116 , 1 , 0 , 100 , 4 , 100 , 4 , 1 , 1 , 1 , 2 , 2 , -128 , -128 , -128 , -128 , 0 , 0 , 0 , -128 , 0 , 0 , 2 , -128 , 0 , 0 , 0 , 0 , -128 , -128 , -128 , -128 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 -// E: 000005.111268 64 04 3c 74 01 00 64 04 64 04 01 01 01 02 02 80 80 80 80 00 00 00 80 00 00 02 80 00 00 00 00 80 80 80 80 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 -// -// Show Desktop -// # ReportID: 4 / 0xffa00003: 60 , 116 , 1 , 0 , 100 , 4 , 100 , 4 , 1 , 1 , 1 , 2 , 2 , -128 , -128 , -128 , -128 , 0 , 0 , 0 , 64 , 0 , 0 , 2 , -128 , 0 , 0 , 0 , 0 , -128 , -128 , -128 , -128 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 -// E: 000006.407970 64 04 3c 74 01 00 64 04 64 04 01 01 01 02 02 80 80 80 80 00 00 00 40 00 00 02 80 00 00 00 00 80 80 80 80 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 -// -// Alt Tab -// # ReportID: 4 / 0xffa00003: 60 , 116 , 1 , 0 , 100 , 4 , 100 , 4 , 1 , 1 , 1 , 2 , 2 , -128 , -128 , -128 , -128 , 0 , 0 , 0 , 32 , 0 , 0 , 2 , -128 , 0 , 0 , 0 , 0 , -128 , -128 , -128 , -128 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 -// E: 000018.074026 64 04 3c 74 01 00 64 04 64 04 01 01 01 02 02 80 80 80 80 00 00 00 20 00 00 02 80 00 00 00 00 80 80 80 80 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 #[derive(PackedStruct, Debug, Copy, Clone, PartialEq)] #[packed_struct(bit_numbering = "msb0", size_bytes = "60")] pub struct XInputDataReport { @@ -355,7 +174,7 @@ pub struct XInputDataReport { #[packed_field(bytes = "17", endian = "lsb")] pub r_stick_y: u8, - // BYTE 18: Legion (-128) quick settings (64) left stick (32) right stick (16) Dpad up (8) down (4) left (2) right (1) + // BYTE 18 #[packed_field(bits = "144")] pub legion: bool, #[packed_field(bits = "145")] @@ -373,7 +192,7 @@ pub struct XInputDataReport { #[packed_field(bits = "151")] pub right: bool, - // BYTE 19: A (-128) B (64) X (32) Y (16) LB (8) Trigger Left (4) RB (2) Trigger Right (1) (Digital Triggers) + // BYTE 19 #[packed_field(bits = "152")] pub a: bool, #[packed_field(bits = "153")] @@ -391,7 +210,7 @@ pub struct XInputDataReport { #[packed_field(bits = "159")] pub d_trigger_r: bool, - // BYTE 20: Y1 (-128) Y2 (64) Y3 (32) M2 (8) M3 (4) View (2) Menu (1) + // BYTE 20 #[packed_field(bits = "160")] pub y1: bool, #[packed_field(bits = "161")] @@ -409,7 +228,7 @@ pub struct XInputDataReport { #[packed_field(bits = "167")] pub menu: bool, - // BYTE 21: Mouse Wheel Click (-128) + // BYTE 21 #[packed_field(bits = "168")] pub mouse_click: bool, #[packed_field(bits = "169")] @@ -479,3 +298,63 @@ pub struct XInputDataReport { #[packed_field(bytes = "58..=59", endian = "msb")] pub right_gyro_z: i16, } + +#[derive(PackedStruct, Debug, Copy, Clone, PartialEq)] +#[packed_struct(bit_numbering = "msb0", size_bytes = "20")] +pub struct TouchpadDataReport { + #[packed_field(bytes = "0")] + pub report_id: u8, + + // BYTE 1 + #[packed_field(bits = "13")] + pub contact_id_0: bool, + #[packed_field(bits = "14")] + pub tip_switch_0: bool, + #[packed_field(bits = "15")] + pub confidence_0: bool, + + #[packed_field(bytes = "2..=3", endian = "lsb")] + pub touch_x_0: u16, + #[packed_field(bytes = "4..=5", endian = "lsb")] + pub touch_y_0: u16, + + // BYTE 6 + #[packed_field(bits = "53")] + pub contact_id_1: bool, + #[packed_field(bits = "54")] + pub tip_switch_1: bool, + #[packed_field(bits = "55")] + pub confidence_1: bool, + + // BYTES 7-8 + #[packed_field(bytes = "7..=8", endian = "lsb")] + pub touch_x_1: u16, + + // BYTES 9-10 + #[packed_field(bytes = "9..=10", endian = "lsb")] + pub touch_y_1: u16, + + // BYTE 11 + #[packed_field(bits = "93")] + pub contact_id_2: bool, + #[packed_field(bits = "94")] + pub tip_switch_2: bool, + #[packed_field(bits = "95")] + pub confidence_2: bool, + + // BYTES 12-13 + #[packed_field(bytes = "12..=13", endian = "lsb")] + pub touch_x_2: u16, + + // BYTES 14-15 + #[packed_field(bytes = "14..=15", endian = "lsb")] + pub touch_y_2: u16, + + // BYTES 16-17 + #[packed_field(bytes = "16..=17", endian = "lsb")] + pub scan_time: u16, + + // BYTE 18 + #[packed_field(bytes = "18")] + pub contact_count: u8, +} diff --git a/src/drivers/lego/mod.rs b/src/drivers/lego/mod.rs index 4151b073..45a29b71 100644 --- a/src/drivers/lego/mod.rs +++ b/src/drivers/lego/mod.rs @@ -1,6 +1,7 @@ pub mod event; pub mod go1_driver; pub mod go2_driver; +pub mod go_touchpad_driver; pub mod hid_report; use std::time::Duration; @@ -9,6 +10,7 @@ use crate::input::capability::{Capability, Source}; // Hardware ID's pub const VID: u16 = 0x17ef; +pub const TP_IID: i32 = 0x01; pub const GP_IID: i32 = 0x02; // Go 1 @@ -37,15 +39,18 @@ pub const GO2_PIDS: [u16; 4] = [ LEGO_2_FPS_PID, ]; -const CLICK_DELAY: Duration = Duration::from_millis(150); -const RELEASE_DELAY: Duration = Duration::from_millis(30); +const RELEASE_DELAY: Duration = Duration::from_millis(150); // Report ID's +pub const TOUCHPAD_DATA: u8 = 0x01; pub const XINPUT_DATA: u8 = 0x04; // Input report sizes +const TOUCHPAD_PACKET_SIZE: usize = 20; const XINPUT_PACKET_SIZE: usize = 60; -const HID_TIMEOUT: i32 = 10; + +const GP_TIMEOUT: i32 = 10; +const TP_TIMEOUT: i32 = 5; // HID Command ID's const XINPUT_COMMAND_ID: u8 = 0x74; diff --git a/src/input/source/hidraw.rs b/src/input/source/hidraw.rs index 92a7b003..0127e9cd 100644 --- a/src/input/source/hidraw.rs +++ b/src/input/source/hidraw.rs @@ -7,6 +7,7 @@ pub mod gpd_win_mini_touchpad; pub mod horipad_steam; pub mod legion_go; pub mod legion_go2; +pub mod legion_go_tp; pub mod legos_imu; pub mod legos_touchpad; pub mod legos_xinput; @@ -25,9 +26,11 @@ use crate::{ constants::BUS_SOURCES_PREFIX, drivers, input::{ - capability::Capability, composite_device::client::CompositeDeviceClient, - info::DeviceInfoRef, output_capability::OutputCapability, - source::hidraw::ultimate_2::Ultimate2, + capability::Capability, + composite_device::client::CompositeDeviceClient, + info::DeviceInfoRef, + output_capability::OutputCapability, + source::hidraw::{legion_go_tp::LegionGoTouchpad, ultimate_2::Ultimate2}, }, udev::device::UdevDevice, }; @@ -56,6 +59,7 @@ enum DriverType { LegionGoSImu, LegionGoSTouchpad, LegionGoSXInput, + LegionGoTouchpad, OrangePiNeo, OxpHid, RogAlly, @@ -81,6 +85,7 @@ pub enum HidRawDevice { LegionGoSImu(SourceDriver), LegionGoSTouchpad(SourceDriver), LegionGoSXInput(SourceDriver), + LegionGoTouchpad(SourceDriver), OrangePiNeo(SourceDriver), OxpHid(SourceDriver), RogAlly(SourceDriver), @@ -105,6 +110,7 @@ impl SourceDeviceCompatible for HidRawDevice { HidRawDevice::LegionGoSImu(source_driver) => source_driver.info_ref(), HidRawDevice::LegionGoSTouchpad(source_driver) => source_driver.info_ref(), HidRawDevice::LegionGoSXInput(source_driver) => source_driver.info_ref(), + HidRawDevice::LegionGoTouchpad(source_driver) => source_driver.info_ref(), HidRawDevice::OrangePiNeo(source_driver) => source_driver.info_ref(), HidRawDevice::OxpHid(source_driver) => source_driver.info_ref(), HidRawDevice::RogAlly(source_driver) => source_driver.info_ref(), @@ -129,6 +135,7 @@ impl SourceDeviceCompatible for HidRawDevice { HidRawDevice::LegionGoSImu(source_driver) => source_driver.get_id(), HidRawDevice::LegionGoSTouchpad(source_driver) => source_driver.get_id(), HidRawDevice::LegionGoSXInput(source_driver) => source_driver.get_id(), + HidRawDevice::LegionGoTouchpad(source_driver) => source_driver.get_id(), HidRawDevice::OrangePiNeo(source_driver) => source_driver.get_id(), HidRawDevice::OxpHid(source_driver) => source_driver.get_id(), HidRawDevice::RogAlly(source_driver) => source_driver.get_id(), @@ -153,6 +160,7 @@ impl SourceDeviceCompatible for HidRawDevice { HidRawDevice::LegionGoSImu(source_driver) => source_driver.client(), HidRawDevice::LegionGoSTouchpad(source_driver) => source_driver.client(), HidRawDevice::LegionGoSXInput(source_driver) => source_driver.client(), + HidRawDevice::LegionGoTouchpad(source_driver) => source_driver.client(), HidRawDevice::OrangePiNeo(source_driver) => source_driver.client(), HidRawDevice::OxpHid(source_driver) => source_driver.client(), HidRawDevice::RogAlly(source_driver) => source_driver.client(), @@ -177,6 +185,7 @@ impl SourceDeviceCompatible for HidRawDevice { HidRawDevice::LegionGoSImu(source_driver) => source_driver.run().await, HidRawDevice::LegionGoSTouchpad(source_driver) => source_driver.run().await, HidRawDevice::LegionGoSXInput(source_driver) => source_driver.run().await, + HidRawDevice::LegionGoTouchpad(source_driver) => source_driver.run().await, HidRawDevice::OrangePiNeo(source_driver) => source_driver.run().await, HidRawDevice::OxpHid(source_driver) => source_driver.run().await, HidRawDevice::RogAlly(source_driver) => source_driver.run().await, @@ -203,6 +212,7 @@ impl SourceDeviceCompatible for HidRawDevice { HidRawDevice::LegionGoSImu(source_driver) => source_driver.get_capabilities(), HidRawDevice::LegionGoSTouchpad(source_driver) => source_driver.get_capabilities(), HidRawDevice::LegionGoSXInput(source_driver) => source_driver.get_capabilities(), + HidRawDevice::LegionGoTouchpad(source_driver) => source_driver.get_capabilities(), HidRawDevice::OrangePiNeo(source_driver) => source_driver.get_capabilities(), HidRawDevice::OxpHid(source_driver) => source_driver.get_capabilities(), HidRawDevice::RogAlly(source_driver) => source_driver.get_capabilities(), @@ -235,6 +245,9 @@ impl SourceDeviceCompatible for HidRawDevice { source_driver.get_output_capabilities() } HidRawDevice::LegionGoSXInput(source_driver) => source_driver.get_output_capabilities(), + HidRawDevice::LegionGoTouchpad(source_driver) => { + source_driver.get_output_capabilities() + } HidRawDevice::OrangePiNeo(source_driver) => source_driver.get_output_capabilities(), HidRawDevice::OxpHid(source_driver) => source_driver.get_output_capabilities(), HidRawDevice::RogAlly(source_driver) => source_driver.get_output_capabilities(), @@ -259,6 +272,7 @@ impl SourceDeviceCompatible for HidRawDevice { HidRawDevice::LegionGoSImu(source_driver) => source_driver.get_device_path(), HidRawDevice::LegionGoSTouchpad(source_driver) => source_driver.get_device_path(), HidRawDevice::LegionGoSXInput(source_driver) => source_driver.get_device_path(), + HidRawDevice::LegionGoTouchpad(source_driver) => source_driver.get_device_path(), HidRawDevice::OrangePiNeo(source_driver) => source_driver.get_device_path(), HidRawDevice::OxpHid(source_driver) => source_driver.get_device_path(), HidRawDevice::RogAlly(source_driver) => source_driver.get_device_path(), @@ -331,15 +345,33 @@ impl HidRawDevice { Ok(Self::SteamDeck(source_device)) } DriverType::LegionGo => { + let options = SourceDriverOptions { + poll_rate: Duration::from_millis(4), + buffer_size: 2048, + }; let device = LegionGoController::new(device_info.clone())?; - let source_device = - SourceDriver::new(composite_device, device, device_info.into(), conf); + let source_device = SourceDriver::new_with_options( + composite_device, + device, + device_info.into(), + options, + conf, + ); Ok(Self::LegionGo(source_device)) } DriverType::LegionGo2 => { + let options = SourceDriverOptions { + poll_rate: Duration::from_millis(4), + buffer_size: 2048, + }; let device = LegionGo2Controller::new(device_info.clone())?; - let source_device = - SourceDriver::new(composite_device, device, device_info.into(), conf); + let source_device = SourceDriver::new_with_options( + composite_device, + device, + device_info.into(), + options, + conf, + ); Ok(Self::LegionGo2(source_device)) } DriverType::LegionGoSImu => { @@ -387,6 +419,21 @@ impl HidRawDevice { ); Ok(Self::LegionGoSXInput(source_device)) } + DriverType::LegionGoTouchpad => { + let options = SourceDriverOptions { + poll_rate: Duration::from_millis(4), + buffer_size: 2048, + }; + let device = LegionGoTouchpad::new(device_info.clone())?; + let source_device = SourceDriver::new_with_options( + composite_device, + device, + device_info.into(), + options, + conf, + ); + Ok(Self::LegionGoTouchpad(source_device)) + } DriverType::OrangePiNeo => { let device = OrangePiNeoTouchpad::new(device_info.clone())?; let source_device = @@ -526,6 +573,15 @@ impl HidRawDevice { return DriverType::LegionGo2; } + // Legion Go Touchpad + if vid == drivers::lego::VID + && (drivers::lego::GO1_PIDS.contains(&pid) || drivers::lego::GO2_PIDS.contains(&pid)) + && iid == drivers::lego::TP_IID + { + log::info!("Detected Legion Go Touchpad"); + return DriverType::LegionGoTouchpad; + } + // Legion Go S IMU if vid == drivers::legos::VID && drivers::legos::PIDS.contains(&pid) diff --git a/src/input/source/hidraw/legion_go.rs b/src/input/source/hidraw/legion_go.rs index d5e933a1..37f735b2 100644 --- a/src/input/source/hidraw/legion_go.rs +++ b/src/input/source/hidraw/legion_go.rs @@ -5,8 +5,7 @@ use crate::{ drivers::lego::{ event::{self, AxisEvent}, go1_driver::Driver, - PAD_FORCE_MAX, PAD_X_MAX, PAD_Y_MAX, STICK_X_MAX, STICK_X_MIN, STICK_Y_MAX, STICK_Y_MIN, - TRIGG_MAX, + STICK_X_MAX, STICK_X_MIN, STICK_Y_MAX, STICK_Y_MIN, TRIGG_MAX, }, input::{ capability::{ @@ -179,10 +178,7 @@ impl LegionGoController { Capability::Mouse(Mouse::Wheel), normalize_trigger_value(trigg), ), - event::TriggerEvent::RpadForce(_) => NativeEvent::new( - Capability::Gamepad(Gamepad::Trigger(GamepadTrigger::RightTouchpadForce)), - normalize_trigger_value(trigg), - ), + _ => NativeEvent::new(Capability::NotImplemented, InputValue::None), }, event::Event::TouchButton(button) => match button { event::TouchButtonEvent::Left(value) => NativeEvent::new( @@ -252,28 +248,6 @@ fn normalize_axis_value(event: AxisEvent) -> InputValue { InputValue::Vector2 { x, y } } - AxisEvent::Touchpad(value) => { - let max = PAD_X_MAX; - let x = normalize_unsigned_value(value.x as f64, max); - - let max = PAD_Y_MAX; - let y = normalize_unsigned_value(value.y as f64, max); - - // If this is an UP event, don't override the position of X/Y - let (x, y) = if !value.is_touching { - (None, None) - } else { - (Some(x), Some(y)) - }; - - InputValue::Touch { - index: value.index, - is_touching: value.is_touching, - pressure: Some(1.0), - x, - y, - } - } _ => InputValue::None, } } @@ -294,10 +268,7 @@ fn normalize_trigger_value(event: event::TriggerEvent) -> InputValue { x: None, y: Some(value.value as f64), }, - event::TriggerEvent::RpadForce(value) => { - let max = PAD_FORCE_MAX; - InputValue::Float(normalize_unsigned_value(value.value as f64, max)) - } + _ => InputValue::None, } } /// List of all capabilities that the Legion Go driver implements @@ -331,9 +302,6 @@ pub const CAPABILITIES: &[Capability] = &[ Capability::Gamepad(Gamepad::Button(GamepadButton::Start)), Capability::Gamepad(Gamepad::Button(GamepadButton::West)), Capability::Gamepad(Gamepad::Trigger(GamepadTrigger::LeftTrigger)), - Capability::Gamepad(Gamepad::Trigger(GamepadTrigger::RightTouchpadForce)), Capability::Gamepad(Gamepad::Trigger(GamepadTrigger::RightTrigger)), Capability::Mouse(Mouse::Wheel), - Capability::Touchpad(Touchpad::RightPad(Touch::Button(TouchButton::Press))), - Capability::Touchpad(Touchpad::RightPad(Touch::Motion)), ]; diff --git a/src/input/source/hidraw/legion_go2.rs b/src/input/source/hidraw/legion_go2.rs index c9ba70ed..c9bfd40b 100644 --- a/src/input/source/hidraw/legion_go2.rs +++ b/src/input/source/hidraw/legion_go2.rs @@ -5,8 +5,7 @@ use crate::{ drivers::lego::{ event::{self, AxisEvent}, go2_driver::Driver, - PAD_FORCE_MAX, PAD_X_MAX, PAD_Y_MAX, STICK_X_MAX, STICK_X_MIN, STICK_Y_MAX, STICK_Y_MIN, - TRIGG_MAX, + STICK_X_MAX, STICK_X_MIN, STICK_Y_MAX, STICK_Y_MIN, TRIGG_MAX, }, input::{ capability::{ @@ -213,10 +212,7 @@ impl LegionGo2Controller { Capability::Mouse(Mouse::Wheel), normalize_trigger_value(trigg), ), - event::TriggerEvent::RpadForce(_) => NativeEvent::new( - Capability::Gamepad(Gamepad::Trigger(GamepadTrigger::RightTouchpadForce)), - normalize_trigger_value(trigg), - ), + _ => NativeEvent::new(Capability::NotImplemented, InputValue::Bool(false)), }, event::Event::TouchButton(button) => match button { event::TouchButtonEvent::Left(value) => NativeEvent::new( @@ -311,28 +307,7 @@ fn normalize_axis_value(event: AxisEvent) -> InputValue { z: Some(value.yaw as f64), } } - AxisEvent::Touchpad(value) => { - let max = PAD_X_MAX; - let x = normalize_unsigned_value(value.x as f64, max); - - let max = PAD_Y_MAX; - let y = normalize_unsigned_value(value.y as f64, max); - - // If this is an UP event, don't override the position of X/Y - let (x, y) = if !value.is_touching { - (None, None) - } else { - (Some(x), Some(y)) - }; - - InputValue::Touch { - index: value.index, - is_touching: value.is_touching, - pressure: Some(1.0), - x, - y, - } - } + _ => InputValue::None, } } @@ -352,10 +327,7 @@ fn normalize_trigger_value(event: event::TriggerEvent) -> InputValue { x: None, y: Some(value.value as f64), }, - event::TriggerEvent::RpadForce(value) => { - let max = PAD_FORCE_MAX; - InputValue::Float(normalize_unsigned_value(value.value as f64, max)) - } + _ => InputValue::None, } } /// List of all capabilities that the Legion Go driver implements @@ -393,12 +365,9 @@ pub const CAPABILITIES: &[Capability] = &[ Capability::Gamepad(Gamepad::Button(GamepadButton::Start)), Capability::Gamepad(Gamepad::Button(GamepadButton::West)), Capability::Gamepad(Gamepad::Trigger(GamepadTrigger::LeftTrigger)), - Capability::Gamepad(Gamepad::Trigger(GamepadTrigger::RightTouchpadForce)), Capability::Gamepad(Gamepad::Trigger(GamepadTrigger::RightTrigger)), Capability::Gyroscope(Source::Center), Capability::Gyroscope(Source::Left), Capability::Gyroscope(Source::Right), Capability::Mouse(Mouse::Wheel), - Capability::Touchpad(Touchpad::RightPad(Touch::Button(TouchButton::Press))), - Capability::Touchpad(Touchpad::RightPad(Touch::Motion)), ]; diff --git a/src/input/source/hidraw/legion_go_tp.rs b/src/input/source/hidraw/legion_go_tp.rs new file mode 100644 index 00000000..7dc6dda1 --- /dev/null +++ b/src/input/source/hidraw/legion_go_tp.rs @@ -0,0 +1,135 @@ +use std::{error::Error, fmt::Debug}; + +use crate::{ + drivers::lego::{event, go_touchpad_driver::Driver, PAD_FORCE_MAX, PAD_X_MAX, PAD_Y_MAX}, + input::{ + capability::{Capability, Gamepad, GamepadTrigger, Touch, TouchButton, Touchpad}, + event::{native::NativeEvent, value::InputValue}, + source::{InputError, SourceInputDevice, SourceOutputDevice}, + }, + udev::device::UdevDevice, +}; + +/// Legion Go Controller source device implementation +pub struct LegionGoTouchpad { + driver: Driver, +} + +impl LegionGoTouchpad { + /// Create a new Legion controller source device with the given udev + /// device information + pub fn new(device_info: UdevDevice) -> Result> { + let driver = Driver::new(device_info.devnode())?; + Ok(Self { driver }) + } +} + +impl SourceInputDevice for LegionGoTouchpad { + /// Poll the source device for input events + fn poll(&mut self) -> Result, InputError> { + let events = self.driver.poll()?; + let native_events = translate_events(events); + Ok(native_events) + } + + /// Returns the possible input events this device is capable of emitting + fn get_capabilities(&self) -> Result, InputError> { + Ok(CAPABILITIES.into()) + } +} + +impl SourceOutputDevice for LegionGoTouchpad {} + +impl Debug for LegionGoTouchpad { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("LegionGoTouchpad").finish() + } +} + +// Returns a value between 0.0 and 1.0 based on the given value with its +// maximum. +fn normalize_unsigned_value(raw_value: f64, max: f64) -> f64 { + raw_value / max +} + +/// Normalize the value to something between -1.0 and 1.0 based on the +/// minimum and maximum axis ranges. +fn normalize_axis_value(event: event::AxisEvent) -> InputValue { + match event { + event::AxisEvent::Touchpad(value) => { + let max = PAD_X_MAX; + let x = normalize_unsigned_value(value.x as f64, max); + + let max = PAD_Y_MAX; + let y = normalize_unsigned_value(value.y as f64, max); + + // If this is an UP event, don't override the position of X/Y + let (x, y) = if !value.is_touching { + (None, None) + } else { + (Some(x), Some(y)) + }; + + InputValue::Touch { + index: value.index, + is_touching: value.is_touching, + pressure: Some(1.0), + x, + y, + } + } + _ => InputValue::None, + } +} + +/// Translate the given Legion Go events into native events +fn translate_events(events: Vec) -> Vec { + events.into_iter().map(translate_event).collect() +} + +/// Translate the given Legion Go event into a native event +fn translate_event(event: event::Event) -> NativeEvent { + match event { + event::Event::GamepadButton(_) => { + NativeEvent::new(Capability::NotImplemented, InputValue::Bool(false)) + } + event::Event::Axis(axis) => match axis.clone() { + event::AxisEvent::Touchpad(_) => NativeEvent::new( + Capability::Touchpad(Touchpad::RightPad(Touch::Motion)), + normalize_axis_value(axis), + ), + _ => NativeEvent::new(Capability::NotImplemented, InputValue::Bool(false)), + }, + event::Event::Trigger(trigg) => match trigg.clone() { + event::TriggerEvent::RpadForce(_) => NativeEvent::new( + Capability::Gamepad(Gamepad::Trigger(GamepadTrigger::RightTouchpadForce)), + normalize_trigger_value(trigg), + ), + _ => NativeEvent::new(Capability::NotImplemented, InputValue::Bool(false)), + }, + event::Event::TouchButton(button) => match button { + event::TouchButtonEvent::Left(value) => NativeEvent::new( + Capability::Touchpad(Touchpad::RightPad(Touch::Button(TouchButton::Press))), + InputValue::Bool(value.pressed), + ), + }, + } +} + +/// Normalize the trigger value to something between 0.0 and 1.0 based on the +/// Legion Go's maximum axis ranges. +fn normalize_trigger_value(event: event::TriggerEvent) -> InputValue { + match event { + event::TriggerEvent::RpadForce(value) => { + let max = PAD_FORCE_MAX; + InputValue::Float(normalize_unsigned_value(value.value as f64, max)) + } + _ => InputValue::None, + } +} +/// List of all capabilities that the Legion Go driver implements +pub const CAPABILITIES: &[Capability] = &[ + Capability::Gamepad(Gamepad::Trigger(GamepadTrigger::RightTouchpadForce)), + Capability::Touchpad(Touchpad::RightPad(Touch::Button(TouchButton::Press))), + Capability::Touchpad(Touchpad::RightPad(Touch::Motion)), +]; diff --git a/src/input/source/mod.rs b/src/input/source/mod.rs index ab8f3c2e..a95a44a4 100644 --- a/src/input/source/mod.rs +++ b/src/input/source/mod.rs @@ -384,6 +384,8 @@ impl SourceDriver log::error!("Failed to set default event filter for {device_id}: {e}"); }; loop { + let poll_time_start = std::time::Instant::now(); + // Create a context with performance metrics for each event let mut context = if metrics_enabled { Some(EventContext::new()) @@ -455,8 +457,15 @@ impl SourceDriver break; } - // Sleep for the configured duration - thread::sleep(self.options.poll_rate); + // Sleep up to the configured duration after polling timeout + if let Some(remaining) = self + .options + .poll_rate + .checked_sub(poll_time_start.elapsed()) + { + log::trace!("Remaining sleep time: {remaining:?}"); + thread::sleep(remaining); + } } Ok(())