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 be15acdd..d0ac9773 100644 --- a/src/config/capability_map.rs +++ b/src/config/capability_map.rs @@ -236,6 +236,7 @@ pub struct DialCapability { pub struct MouseCapability { pub button: Option, pub motion: Option, + pub wheel: Option, } #[derive(Debug, Deserialize, Serialize, Clone, JsonSchema, PartialEq)] @@ -245,6 +246,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 e9995e35..c79accd5 100644 --- a/src/dbus/interface/composite_device.rs +++ b/src/dbus/interface/composite_device.rs @@ -352,6 +352,7 @@ impl CompositeDeviceInterface { 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), _ => cap.to_string(), @@ -416,6 +417,7 @@ impl CompositeDeviceInterface { 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), _ => cap.to_string(), diff --git a/src/input/capability.rs b/src/input/capability.rs index 74a536ee..d440cfeb 100644 --- a/src/input/capability.rs +++ b/src/input/capability.rs @@ -199,6 +199,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 @@ -331,6 +340,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 { @@ -338,6 +349,7 @@ impl fmt::Display for Mouse { match self { Mouse::Motion => write!(f, "Motion"), Mouse::Button(_) => write!(f, "Button"), + Mouse::Wheel(_) => write!(f, "Wheel"), } } } @@ -417,6 +429,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 f4cdbd33..644c3107 100644 --- a/src/input/composite_device/mod.rs +++ b/src/input/composite_device/mod.rs @@ -919,6 +919,7 @@ impl CompositeDevice { continue; } } + Mouse::Wheel(_) => {} }, Capability::Touchscreen(_) => (), } diff --git a/src/input/event/evdev.rs b/src/input/event/evdev.rs index 574a7e63..d0bdeb14 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], diff --git a/src/input/event/value.rs b/src/input/event/value.rs index 9640890b..a548822b 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, @@ -138,6 +139,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()), @@ -183,11 +186,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), @@ -233,6 +235,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), @@ -266,6 +269,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 { @@ -307,6 +311,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()), @@ -340,6 +345,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 @@ -395,6 +401,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 @@ -450,6 +457,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 @@ -509,6 +517,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), @@ -678,6 +687,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/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), ]) }