diff --git a/rootfs/usr/share/inputplumber/profiles/mouse_keyboard_wasd.yaml b/rootfs/usr/share/inputplumber/profiles/mouse_keyboard_wasd.yaml index 986a42e7..1e905de7 100644 --- a/rootfs/usr/share/inputplumber/profiles/mouse_keyboard_wasd.yaml +++ b/rootfs/usr/share/inputplumber/profiles/mouse_keyboard_wasd.yaml @@ -177,7 +177,13 @@ mapping: button: RightBumper target_events: - mouse: - button: WheelUp + wheel: + name: Vertical + direction: up + #- mouse: + # wheel: + # name: Horizontal + # direction: right - name: LB source_event: @@ -185,4 +191,10 @@ mapping: button: LeftBumper target_events: - mouse: - button: WheelDown + wheel: + name: Vertical + direction: down + #- mouse: + # wheel: + # name: Horizontal + # direction: left diff --git a/src/config/capability_map.rs b/src/config/capability_map.rs index e4d0435e..057dc337 100644 --- a/src/config/capability_map.rs +++ b/src/config/capability_map.rs @@ -275,6 +275,7 @@ pub struct MouseCapability { pub button: Option, #[serde(skip_serializing_if = "Option::is_none")] pub motion: Option, + pub wheel: Option, } #[derive(Debug, Deserialize, Serialize, Clone, JsonSchema, PartialEq)] @@ -286,6 +287,13 @@ pub struct MouseMotionCapability { pub speed_pps: Option, } +#[derive(Debug, Deserialize, Serialize, Clone, JsonSchema, PartialEq)] +#[serde(rename_all = "snake_case")] +pub struct MouseWheelCapability { + pub name: String, + pub direction: Option, +} + #[derive(Debug, Deserialize, Serialize, Clone, JsonSchema, PartialEq)] #[serde(rename_all = "snake_case")] pub struct TouchpadCapability { diff --git a/src/dbus/interface/composite_device.rs b/src/dbus/interface/composite_device.rs index 55730bdf..e3db7acc 100644 --- a/src/dbus/interface/composite_device.rs +++ b/src/dbus/interface/composite_device.rs @@ -13,7 +13,7 @@ use crate::{ config::DeviceProfile, dbus::polkit::check_polkit, input::{ - capability::{Capability, Gamepad, Mouse}, + capability::Capability, composite_device::{client::CompositeDeviceClient, InterceptMode}, event::{native::NativeEvent, value::InputValue}, }, @@ -465,23 +465,7 @@ impl CompositeDeviceInterface { let mut capability_strings = HashSet::new(); for cap in capabilities { - let str = match cap { - Capability::Gamepad(gamepad) => match gamepad { - Gamepad::Button(button) => format!("Gamepad:Button:{}", button), - Gamepad::Axis(axis) => format!("Gamepad:Axis:{}", axis), - Gamepad::Trigger(trigger) => format!("Gamepad:Trigger:{}", trigger), - Gamepad::Accelerometer => "Gamepad:Accelerometer".to_string(), - Gamepad::Gyro => "Gamepad:Gyro".to_string(), - Gamepad::Dial(dial) => format!("Gamepad:Dial:{dial}"), - }, - Capability::Mouse(mouse) => match mouse { - Mouse::Motion => "Mouse:Motion".to_string(), - Mouse::Button(button) => format!("Mouse:Button:{}", button), - }, - Capability::Keyboard(key) => format!("Keyboard:{}", key), - Capability::DBus(action) => format!("DBus:{}", action.as_str()), - _ => cap.to_string(), - }; + let str = cap.to_capability_string(); capability_strings.insert(str); } diff --git a/src/drivers/lego/driver.rs b/src/drivers/lego/driver.rs index 4a91d03b..c59d425d 100644 --- a/src/drivers/lego/driver.rs +++ b/src/drivers/lego/driver.rs @@ -162,9 +162,9 @@ impl Driver { let input_report = XInputDataReport::unpack(&buf)?; // Print input report for debugging - //log::trace!("--- Input report ---"); - //log::trace!("{input_report}"); - //log::trace!(" ---- End Report ----"); + //log::debug!("--- Input report ---"); + //log::debug!("{input_report}"); + //log::debug!(" ---- End Report ----"); // Update the state let old_dinput_state = self.update_xinput_state(input_report); @@ -408,8 +408,14 @@ impl Driver { }))); } if state.mouse_z != old_state.mouse_z { + let value = state.mouse_z.wrapping_sub(128) as i8; + let value = match value { + 127 => -1, + v => v, + }; + events.push(Event::Trigger(TriggerEvent::MouseWheel(MouseWheelInput { - value: state.mouse_z, + value, }))); } diff --git a/src/drivers/lego/hid_report.rs b/src/drivers/lego/hid_report.rs index e57bf4cd..efe271ea 100644 --- a/src/drivers/lego/hid_report.rs +++ b/src/drivers/lego/hid_report.rs @@ -434,8 +434,8 @@ pub struct XInputDataReport { #[packed_field(bytes = "24")] pub unk_23: u8, - #[packed_field(bytes = "25")] - pub mouse_z: i8, + #[packed_field(bytes = "25", endian = "lsb")] + pub mouse_z: u8, #[packed_field(bytes = "26..=27", endian = "msb")] pub touch_x: u16, diff --git a/src/drivers/lego/mod.rs b/src/drivers/lego/mod.rs index ed5407c0..ef79c6b8 100644 --- a/src/drivers/lego/mod.rs +++ b/src/drivers/lego/mod.rs @@ -43,7 +43,6 @@ const HID_TIMEOUT: i32 = 10; const XINPUT_COMMAND_ID: u8 = 0x74; // Input report axis ranges -pub const MOUSE_WHEEL_MAX: f64 = 120.0; pub const PAD_FORCE_MAX: f64 = 127.0; pub const PAD_FORCE_NORMAL: u8 = 32; /* Simulated average pressure */ pub const PAD_X_MAX: f64 = 1024.0; diff --git a/src/input/capability.rs b/src/input/capability.rs index 5206b40d..fb722452 100644 --- a/src/input/capability.rs +++ b/src/input/capability.rs @@ -56,6 +56,7 @@ impl Capability { Capability::Mouse(mouse) => match mouse { Mouse::Motion => "Mouse:Motion".to_string(), Mouse::Button(button) => format!("Mouse:Button:{button}"), + Mouse::Wheel(wheel) => format!("Mouse:Wheel:{wheel}"), }, Capability::Keyboard(key) => format!("Keyboard:{key}"), Capability::None => "None".to_string(), @@ -267,6 +268,15 @@ impl From for Capability { let button = button.unwrap(); return Capability::Mouse(Mouse::Button(button)); } + + // Wheel + if let Some(wheel) = mouse.wheel.as_ref() { + let Ok(wheel) = MouseWheel::from_str(&wheel.name) else { + log::error!("Invalid or unimplemented wheel: {}", wheel.name); + return Capability::NotImplemented; + }; + return Capability::Mouse(Mouse::Wheel(wheel)); + } } // DBus @@ -437,6 +447,8 @@ pub enum Mouse { Motion, /// Mouse Buttons are typically binary mouse input that represents button presses Button(MouseButton), + /// Mouse wheel input is relative + Wheel(MouseWheel), } impl fmt::Display for Mouse { @@ -444,6 +456,7 @@ impl fmt::Display for Mouse { match self { Mouse::Motion => write!(f, "Motion"), Mouse::Button(_) => write!(f, "Button"), + Mouse::Wheel(_) => write!(f, "Wheel"), } } } @@ -523,6 +536,35 @@ impl FromStr for MouseButton { } } +#[derive(Clone, Debug, PartialEq, Eq, Hash)] +pub enum MouseWheel { + // Mouse wheel up or down + Vertical, + // Mouse wheel left or right + Horizontal, +} + +impl fmt::Display for MouseWheel { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match self { + MouseWheel::Vertical => write!(f, "Vertical"), + MouseWheel::Horizontal => write!(f, "Horizontal"), + } + } +} + +impl FromStr for MouseWheel { + type Err = (); + + fn from_str(s: &str) -> Result { + match s { + "Vertical" => Ok(MouseWheel::Vertical), + "Horizontal" => Ok(MouseWheel::Horizontal), + _ => Err(()), + } + } +} + /// Gamepad Buttons typically use binary input that represents button presses #[derive(Clone, Debug, PartialEq, Eq, Hash)] pub enum GamepadButton { diff --git a/src/input/composite_device/mod.rs b/src/input/composite_device/mod.rs index df06becc..c39e4ab6 100644 --- a/src/input/composite_device/mod.rs +++ b/src/input/composite_device/mod.rs @@ -988,6 +988,7 @@ impl CompositeDevice { continue; } } + Mouse::Wheel(_) => {} }, Capability::Touchscreen(_) => (), Capability::Gyroscope(_) => (), @@ -1933,16 +1934,16 @@ impl CompositeDevice { fn is_new_active_event(&mut self, cap: &Capability, is_pressed: bool) -> bool { let active = self.active_inputs.contains(cap); if is_pressed && !active { - log::debug!("New active capability: {cap:?}"); + log::trace!("New active capability: {cap:?}"); self.active_inputs.push(cap.clone()); } // Ignore up events for actions we've already handled. if !is_pressed && !active { - log::debug!("Blocked up event for capability: {cap:?}"); + log::trace!("Blocked up event for capability: {cap:?}"); return false; } if !is_pressed && active { - log::debug!("Removed inactive capability: {cap:?}"); + log::trace!("Removed inactive capability: {cap:?}"); let index = self.active_inputs.iter().position(|r| r == cap).unwrap(); self.active_inputs.remove(index); } @@ -1956,12 +1957,12 @@ impl CompositeDevice { intercept: bool, ) -> Result> { if self.intercept_activation_caps.len() == 1 { - log::debug!("Checking single intercept event."); + log::trace!("Checking single intercept event."); return self .is_intercept_event_single(event, is_pressed, intercept) .await; } - log::debug!("Checking multi intercept event."); + log::trace!("Checking multi intercept event."); self.is_intercept_event_multi(event, is_pressed, intercept) .await } @@ -1976,7 +1977,6 @@ impl CompositeDevice { // Check if we have met the criteria for InterceptMode:Always if intercept && self.intercept_activation_caps.contains(&cap) && is_pressed { log::debug!("Found matching intercept event: {:?}", cap); - log::debug!("It is a DOWN event!"); // Stop here if this is a repeat event. if self.intercept_active_inputs.contains(&cap) { log::debug!("The event is already in the list. Skipping."); @@ -1999,8 +1999,6 @@ impl CompositeDevice { { // Check if we already sent the intercept event. We might not be in the same intercept mode // so dont check intercept. - log::debug!("It is an UP event!"); - log::trace!("Remove from intercept active inputs: {cap:?}"); let index = self .intercept_active_inputs diff --git a/src/input/event/evdev.rs b/src/input/event/evdev.rs index 7c11b682..7b374a40 100644 --- a/src/input/event/evdev.rs +++ b/src/input/event/evdev.rs @@ -6,7 +6,7 @@ use evdev::{AbsInfo, AbsoluteAxisCode, EventType, InputEvent, KeyCode, RelativeA use crate::input::capability::{ Capability, Gamepad, GamepadAxis, GamepadButton, GamepadDial, GamepadTrigger, Keyboard, Mouse, - MouseButton, Touch, TouchButton, Touchpad, + MouseButton, MouseWheel, Touch, TouchButton, Touchpad, }; use super::{native::NativeEvent, value::InputValue}; @@ -465,6 +465,12 @@ impl EvdevEvent { EventType::RELATIVE => match RelativeAxisCode(code) { RelativeAxisCode::REL_X => Capability::Mouse(Mouse::Motion), RelativeAxisCode::REL_Y => Capability::Mouse(Mouse::Motion), + RelativeAxisCode::REL_WHEEL => { + Capability::Mouse(Mouse::Wheel(MouseWheel::Vertical)) + } + RelativeAxisCode::REL_HWHEEL => { + Capability::Mouse(Mouse::Wheel(MouseWheel::Horizontal)) + } _ => Capability::NotImplemented, }, EventType::MISC => Capability::NotImplemented, @@ -561,6 +567,7 @@ fn event_type_from_capability(capability: Capability) -> Option { Capability::Mouse(mouse) => match mouse { Mouse::Motion => Some(EventType::RELATIVE), Mouse::Button(_) => Some(EventType::KEY), + Mouse::Wheel(_) => Some(EventType::RELATIVE), }, Capability::Gamepad(gamepad) => match gamepad { Gamepad::Button(button) => match button { @@ -701,6 +708,11 @@ fn event_codes_from_capability(capability: Capability) -> Vec { MouseButton::Extra => vec![KeyCode::BTN_EXTRA.0], MouseButton::Side => vec![KeyCode::BTN_SIDE.0], }, + + Mouse::Wheel(wheel) => match wheel { + MouseWheel::Vertical => vec![RelativeAxisCode::REL_WHEEL.0], + MouseWheel::Horizontal => vec![RelativeAxisCode::REL_HWHEEL.0], + }, }, Capability::Keyboard(key) => match key { Keyboard::Key0 => vec![KeyCode::KEY_0.0], @@ -975,6 +987,8 @@ fn input_event_from_value( EventType::RELATIVE => match RelativeAxisCode(code) { RelativeAxisCode::REL_X => x.map(|v| v as i32), RelativeAxisCode::REL_Y => y.map(|v| v as i32), + RelativeAxisCode::REL_HWHEEL => x.map(|v| v as i32), + RelativeAxisCode::REL_WHEEL => y.map(|v| v as i32), _ => None, }, _ => None, diff --git a/src/input/event/value.rs b/src/input/event/value.rs index 7aa98a55..a7842741 100644 --- a/src/input/event/value.rs +++ b/src/input/event/value.rs @@ -6,6 +6,7 @@ use crate::{ use super::dbus::Action; /// Possible errors while doing input value translation +#[derive(Debug)] pub enum TranslationError { /// Translation not yet implemented NotImplemented, @@ -205,6 +206,8 @@ impl InputValue { Mouse::Motion => Err(TranslationError::NotImplemented), // Gamepad Button -> Mouse Button Mouse::Button(_) => Ok(self.clone()), + // Gamepad Button -> Mouse Wheel + Mouse::Wheel(_) => self.translate_button_to_wheel(target_config), }, // Gamepad Button -> Keyboard Capability::Keyboard(_) => Ok(self.clone()), @@ -254,11 +257,10 @@ impl InputValue { }, // Axis -> Mouse Capability::Mouse(mouse) => match mouse { - // Axis -> Mouse Motion Mouse::Motion => self .translate_axis_to_mouse_motion(source_config, target_config), - // Axis -> Mouse Button Mouse::Button(_) => self.translate_axis_to_button(source_config), + Mouse::Wheel(_) => Err(TranslationError::NotImplemented), }, // Axis -> Keyboard Capability::Keyboard(_) => self.translate_axis_to_button(source_config), @@ -308,6 +310,7 @@ impl InputValue { Mouse::Motion => Err(TranslationError::NotImplemented), // Trigger -> Mouse Button Mouse::Button(_) => self.translate_trigger_to_button(source_config), + Mouse::Wheel(_) => Err(TranslationError::NotImplemented), }, // Trigger -> Keyboard Capability::Keyboard(_) => self.translate_trigger_to_button(source_config), @@ -345,6 +348,7 @@ impl InputValue { Capability::Mouse(mouse) => match mouse { Mouse::Motion => Err(TranslationError::NotImplemented), Mouse::Button(_) => self.translate_dial_to_button(source_config), + Mouse::Wheel(_) => Err(TranslationError::NotImplemented), }, Capability::Keyboard(_) => self.translate_dial_to_button(source_config), Capability::Touchpad(touch) => match touch { @@ -388,6 +392,7 @@ impl InputValue { Capability::Mouse(mouse) => match mouse { Mouse::Motion => Err(TranslationError::NotImplemented), Mouse::Button(_) => Ok(self.clone()), + Mouse::Wheel(_) => self.translate_button_to_wheel(target_config), }, // Keyboard Key -> Keyboard Capability::Keyboard(_) => Ok(self.clone()), @@ -425,6 +430,7 @@ impl InputValue { // Touchscreen Motion -> Mouse Motion Mouse::Motion => Err(TranslationError::NotImplemented), Mouse::Button(_) => Err(TranslationError::NotImplemented), + Mouse::Wheel(_) => Err(TranslationError::NotImplemented), }, Capability::Keyboard(_) => Err(TranslationError::NotImplemented), // Touchpad Motion -> Touchpad @@ -484,6 +490,7 @@ impl InputValue { // Touchscreen Motion -> Mouse Motion Mouse::Motion => Err(TranslationError::NotImplemented), Mouse::Button(_) => Err(TranslationError::NotImplemented), + Mouse::Wheel(_) => Err(TranslationError::NotImplemented), }, Capability::Keyboard(_) => Err(TranslationError::NotImplemented), // Touchpad Motion -> Touchpad @@ -543,6 +550,7 @@ impl InputValue { // Touchscreen Motion -> Mouse Motion Mouse::Motion => Err(TranslationError::NotImplemented), Mouse::Button(_) => Err(TranslationError::NotImplemented), + Mouse::Wheel(_) => Err(TranslationError::NotImplemented), }, Capability::Keyboard(_) => Err(TranslationError::NotImplemented), // Touchpad Motion -> Touchpad @@ -606,6 +614,7 @@ impl InputValue { Mouse::Motion => Err(TranslationError::NotImplemented), // Touchscreen Motion -> Mouse Button Mouse::Button(_) => Err(TranslationError::NotImplemented), + Mouse::Wheel(_) => Err(TranslationError::NotImplemented), }, // Touchscreen Motion -> Keyboard Capability::Keyboard(_) => Err(TranslationError::NotImplemented), @@ -781,6 +790,37 @@ impl InputValue { } } + /// Translate the button value into an axis value based on the given config + fn translate_button_to_wheel( + &self, + target_config: &CapabilityConfig, + ) -> Result { + let Some(mouse_config) = target_config.mouse.as_ref() else { + return Err(TranslationError::InvalidTargetConfig( + "No mouse config to translate button to mouse wheel".to_string(), + )); + }; + let Some(wheel) = mouse_config.wheel.as_ref() else { + return Err(TranslationError::InvalidTargetConfig( + "No mouse config found".to_string(), + )); + }; + let Some(direction) = wheel.direction.as_ref() else { + return Err(TranslationError::InvalidTargetConfig( + "No wheel direction found to translate button to mouse wheel".to_string(), + )); + }; + match direction.as_str() { + "up" => Ok(InputValue::Float(1.0)), + "down" => Ok(InputValue::Float(-1.0)), + "left" => Ok(InputValue::Float(-1.0)), + "right" => Ok(InputValue::Float(1.0)), + _ => Err(TranslationError::InvalidTargetConfig( + "Invalid mouse wheel config. Must be of value: up or down (Vertical) or left or right (Horizontal)".to_string(), + )), + } + } + /// Translate the button value into an axis value based on the given config fn translate_button_to_axis( &self, diff --git a/src/input/source/hidraw/legion_go.rs b/src/input/source/hidraw/legion_go.rs index 68527253..95e1b91c 100644 --- a/src/input/source/hidraw/legion_go.rs +++ b/src/input/source/hidraw/legion_go.rs @@ -1,12 +1,13 @@ use std::collections::HashSet; use std::{error::Error, fmt::Debug}; +use crate::input::capability::MouseWheel; use crate::{ drivers::lego::{ driver::Driver, event::{self, AxisEvent}, - MOUSE_WHEEL_MAX, PAD_FORCE_MAX, PAD_X_MAX, PAD_Y_MAX, STICK_X_MAX, STICK_X_MIN, - STICK_Y_MAX, STICK_Y_MIN, TRIGG_MAX, + PAD_FORCE_MAX, PAD_X_MAX, PAD_Y_MAX, STICK_X_MAX, STICK_X_MIN, STICK_Y_MAX, STICK_Y_MIN, + TRIGG_MAX, }, input::{ capability::{ @@ -209,9 +210,10 @@ impl LegionGoController { Capability::Gamepad(Gamepad::Trigger(GamepadTrigger::RightTrigger)), normalize_trigger_value(trigg), ), - event::TriggerEvent::MouseWheel(_) => { - NativeEvent::new(Capability::NotImplemented, InputValue::Bool(false)) - } + event::TriggerEvent::MouseWheel(_) => NativeEvent::new( + Capability::Mouse(Mouse::Wheel(MouseWheel::Vertical)), + normalize_trigger_value(trigg), + ), event::TriggerEvent::RpadForce(_) => NativeEvent::new( Capability::Gamepad(Gamepad::Trigger(GamepadTrigger::RightTouchpadForce)), normalize_trigger_value(trigg), @@ -347,10 +349,10 @@ fn normalize_trigger_value(event: event::TriggerEvent) -> InputValue { let max = TRIGG_MAX; InputValue::Float(normalize_unsigned_value(value.value as f64, max)) } - event::TriggerEvent::MouseWheel(value) => { - let max = MOUSE_WHEEL_MAX; - InputValue::Float(normalize_unsigned_value(value.value as f64, max)) - } + event::TriggerEvent::MouseWheel(value) => InputValue::Vector2 { + 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)) @@ -397,6 +399,7 @@ pub const CAPABILITIES: &[Capability] = &[ Capability::Gyroscope(Source::Center), Capability::Gyroscope(Source::Left), Capability::Gyroscope(Source::Right), + Capability::Mouse(Mouse::Wheel(MouseWheel::Vertical)), Capability::Touchpad(Touchpad::RightPad(Touch::Button(TouchButton::Press))), Capability::Touchpad(Touchpad::RightPad(Touch::Motion)), ]; diff --git a/src/input/target/mouse.rs b/src/input/target/mouse.rs index cedf70f3..c23ebff2 100644 --- a/src/input/target/mouse.rs +++ b/src/input/target/mouse.rs @@ -9,7 +9,7 @@ use evdev::{ use crate::{ dbus::interface::{target::mouse::TargetMouseInterface, DBusInterfaceManager}, input::{ - capability::{Capability, Mouse, MouseButton}, + capability::{Capability, Mouse, MouseButton, MouseWheel}, composite_device::client::CompositeDeviceClient, event::{evdev::EvdevEvent, native::NativeEvent, value::InputValue}, output_event::OutputEvent, @@ -179,6 +179,8 @@ impl TargetInputDevice for MouseDevice { Capability::Mouse(Mouse::Button(MouseButton::Extra)), Capability::Mouse(Mouse::Button(MouseButton::WheelUp)), Capability::Mouse(Mouse::Button(MouseButton::WheelDown)), + Capability::Mouse(Mouse::Wheel(MouseWheel::Vertical)), + Capability::Mouse(Mouse::Wheel(MouseWheel::Horizontal)), Capability::Mouse(Mouse::Motion), ]) }