diff --git a/niri-config/src/input.rs b/niri-config/src/input.rs index 5a2eb1369c..35f8fc56e2 100644 --- a/niri-config/src/input.rs +++ b/niri-config/src/input.rs @@ -205,6 +205,8 @@ pub struct Touchpad { pub accel_speed: FloatOrInt<-1, 1>, #[knuffel(child, unwrap(argument, str))] pub accel_profile: Option, + #[knuffel(child, unwrap(argument))] + pub sensitivity: Option>, #[knuffel(child, unwrap(argument, str))] pub scroll_method: Option, #[knuffel(child, unwrap(argument))] @@ -233,6 +235,8 @@ pub struct Mouse { pub accel_speed: FloatOrInt<-1, 1>, #[knuffel(child, unwrap(argument, str))] pub accel_profile: Option, + #[knuffel(child, unwrap(argument))] + pub sensitivity: Option>, #[knuffel(child, unwrap(argument, str))] pub scroll_method: Option, #[knuffel(child, unwrap(argument))] @@ -257,6 +261,8 @@ pub struct Trackpoint { pub accel_speed: FloatOrInt<-1, 1>, #[knuffel(child, unwrap(argument, str))] pub accel_profile: Option, + #[knuffel(child, unwrap(argument))] + pub sensitivity: Option>, #[knuffel(child, unwrap(argument, str))] pub scroll_method: Option, #[knuffel(child, unwrap(argument))] @@ -279,6 +285,8 @@ pub struct Trackball { pub accel_speed: FloatOrInt<-1, 1>, #[knuffel(child, unwrap(argument, str))] pub accel_profile: Option, + #[knuffel(child, unwrap(argument))] + pub sensitivity: Option>, #[knuffel(child, unwrap(argument, str))] pub scroll_method: Option, #[knuffel(child, unwrap(argument))] @@ -566,6 +574,23 @@ mod tests { "#); } + #[test] + fn parse_sensitivity() { + let parsed = do_parse( + r#" + mouse { + sensitivity 2.5 + } + touchpad { + sensitivity 0.5 + } + "#, + ); + + assert_eq!(parsed.mouse.sensitivity.unwrap().0, 2.5); + assert_eq!(parsed.touchpad.sensitivity.unwrap().0, 0.5); + } + #[test] fn parse_scroll_factor_split() { // Test split horizontal/vertical syntax diff --git a/niri-config/src/lib.rs b/niri-config/src/lib.rs index 909aeb80a5..d59069e8e2 100644 --- a/niri-config/src/lib.rs +++ b/niri-config/src/lib.rs @@ -1005,6 +1005,7 @@ mod tests { accel_profile: Some( Flat, ), + sensitivity: None, scroll_method: Some( TwoFinger, ), @@ -1039,6 +1040,7 @@ mod tests { accel_profile: Some( Flat, ), + sensitivity: None, scroll_method: Some( NoScroll, ), @@ -1069,6 +1071,7 @@ mod tests { accel_profile: Some( Flat, ), + sensitivity: None, scroll_method: Some( OnButtonDown, ), @@ -1088,6 +1091,7 @@ mod tests { accel_profile: Some( Flat, ), + sensitivity: None, scroll_method: Some( Edge, ), diff --git a/resources/default-config.kdl b/resources/default-config.kdl index ccad1ac22e..c42c64dfdf 100644 --- a/resources/default-config.kdl +++ b/resources/default-config.kdl @@ -38,6 +38,7 @@ input { natural-scroll // accel-speed 0.2 // accel-profile "flat" + // sensitivity 1.0 // scroll-method "two-finger" // disabled-on-external-mouse } @@ -47,6 +48,7 @@ input { // natural-scroll // accel-speed 0.2 // accel-profile "flat" + // sensitivity 1.0 // scroll-method "no-scroll" } @@ -55,6 +57,7 @@ input { // natural-scroll // accel-speed 0.2 // accel-profile "flat" + // sensitivity 1.0 // scroll-method "on-button-down" // scroll-button 273 // scroll-button-lock diff --git a/src/input/mod.rs b/src/input/mod.rs index 3e673bdbfc..7159f73765 100644 --- a/src/input/mod.rs +++ b/src/input/mod.rs @@ -2411,7 +2411,10 @@ impl State { } } - fn on_pointer_motion(&mut self, event: I::PointerMotionEvent) { + fn on_pointer_motion(&mut self, event: I::PointerMotionEvent) + where + I::Device: 'static, + { let was_inside_hot_corner = self.niri.pointer_inside_hot_corner; // Any of the early returns here mean that the pointer is not inside the hot corner. self.niri.pointer_inside_hot_corner = false; @@ -2427,8 +2430,13 @@ impl State { let pos = pointer.current_location(); + let delta = event.delta(); + let sensitivity = + pointer_sensitivity_factor(&self.niri.config.borrow().input, &event.device()); + let delta = Point::from((delta.x * sensitivity, delta.y * sensitivity)); + // We have an output, so we can compute the new location and focus. - let mut new_pos = pos + event.delta(); + let mut new_pos = pos + delta; // We received an event for the regular pointer, so show it now. self.niri.pointer_visibility = PointerVisibility::Visible; @@ -2474,7 +2482,7 @@ impl State { self, Some(under.clone()), &RelativeMotionEvent { - delta: event.delta(), + delta, delta_unaccel: event.delta_unaccel(), utime: event.time(), }, @@ -2585,7 +2593,7 @@ impl State { self, Some(focus_surface), &RelativeMotionEvent { - delta: event.delta(), + delta, delta_unaccel: event.delta_unaccel(), utime: event.time(), }, @@ -2615,7 +2623,7 @@ impl State { self, under.surface, &RelativeMotionEvent { - delta: event.delta(), + delta, delta_unaccel: event.delta_unaccel(), utime: event.time(), }, @@ -5010,6 +5018,41 @@ pub fn apply_libinput_settings(config: &niri_config::Input, device: &mut input:: } } +fn pointer_sensitivity_factor(config: &niri_config::Input, device: &D) -> f64 { + let Some(device) = (device as &dyn Any).downcast_ref::() else { + return 1.; + }; + + let is_touchpad = device.config_tap_finger_count() > 0; + if is_touchpad { + return config.touchpad.sensitivity.map(|x| x.0).unwrap_or(1.); + } + + let mut is_trackball = false; + let mut is_trackpoint = false; + if let Some(udev_device) = unsafe { device.udev_device() } { + if udev_device.property_value("ID_INPUT_TRACKBALL").is_some() { + is_trackball = true; + } + if udev_device + .property_value("ID_INPUT_POINTINGSTICK") + .is_some() + { + is_trackpoint = true; + } + } + + if is_trackball { + config.trackball.sensitivity.map(|x| x.0).unwrap_or(1.) + } else if is_trackpoint { + config.trackpoint.sensitivity.map(|x| x.0).unwrap_or(1.) + } else if device.has_capability(input::DeviceCapability::Pointer) { + config.mouse.sensitivity.map(|x| x.0).unwrap_or(1.) + } else { + 1. + } +} + pub fn mods_with_binds(mod_key: ModKey, binds: &Binds, triggers: &[Trigger]) -> HashSet { let mut rv = HashSet::new(); for bind in &binds.0 {