diff options
| -rw-r--r-- | docs/wiki/Configuration:-Input.md | 4 | ||||
| -rw-r--r-- | niri-config/src/lib.rs | 282 | ||||
| -rw-r--r-- | src/input/mod.rs | 35 |
3 files changed, 300 insertions, 21 deletions
diff --git a/docs/wiki/Configuration:-Input.md b/docs/wiki/Configuration:-Input.md index c9ca0ba2..a03dd67b 100644 --- a/docs/wiki/Configuration:-Input.md +++ b/docs/wiki/Configuration:-Input.md @@ -37,6 +37,7 @@ input { // accel-speed 0.2 // accel-profile "flat" // scroll-factor 1.0 + // scroll-factor vertical=1.0 horizontal=-2.0 // scroll-method "two-finger" // scroll-button 273 // scroll-button-lock @@ -53,6 +54,7 @@ input { // accel-speed 0.2 // accel-profile "flat" // scroll-factor 1.0 + // scroll-factor vertical=1.0 horizontal=-2.0 // scroll-method "no-scroll" // scroll-button 273 // scroll-button-lock @@ -252,6 +254,8 @@ Settings specific to `touchpad` and `mouse`: - `scroll-factor`: <sup>Since: 0.1.10</sup> scales the scrolling speed by this value. + <sup>Since: next release</sup> You can also override horizontal and vertical scroll factor separately like so: `scroll-factor horizontal=2.0 vertical=-1.0` + Settings specific to `tablet`s: - `calibration-matrix`: <sup>Since: 25.02</sup> set to six floating point numbers to change the calibration matrix. See the [`LIBINPUT_CALIBRATION_MATRIX` documentation](https://wayland.freedesktop.org/libinput/doc/latest/device-configuration-via-udev.html) for examples. diff --git a/niri-config/src/lib.rs b/niri-config/src/lib.rs index 16ec6dca..dbd6b5ea 100644 --- a/niri-config/src/lib.rs +++ b/niri-config/src/lib.rs @@ -191,6 +191,25 @@ pub enum TrackLayout { Window, } +#[derive(knuffel::Decode, Debug, Default, Clone, Copy, PartialEq)] +pub struct ScrollFactor { + #[knuffel(argument)] + pub base: Option<FloatOrInt<0, 100>>, + #[knuffel(property)] + pub horizontal: Option<FloatOrInt<-100, 100>>, + #[knuffel(property)] + pub vertical: Option<FloatOrInt<-100, 100>>, +} + +impl ScrollFactor { + pub fn h_v_factors(&self) -> (f64, f64) { + let base_value = self.base.map(|f| f.0).unwrap_or(1.0); + let h = self.horizontal.map(|f| f.0).unwrap_or(base_value); + let v = self.vertical.map(|f| f.0).unwrap_or(base_value); + (h, v) + } +} + #[derive(knuffel::Decode, Debug, Default, PartialEq)] pub struct Touchpad { #[knuffel(child)] @@ -227,8 +246,8 @@ pub struct Touchpad { pub disabled_on_external_mouse: bool, #[knuffel(child)] pub middle_emulation: bool, - #[knuffel(child, unwrap(argument))] - pub scroll_factor: Option<FloatOrInt<0, 100>>, + #[knuffel(child)] + pub scroll_factor: Option<ScrollFactor>, } #[derive(knuffel::Decode, Debug, Default, PartialEq)] @@ -251,8 +270,8 @@ pub struct Mouse { pub left_handed: bool, #[knuffel(child)] pub middle_emulation: bool, - #[knuffel(child, unwrap(argument))] - pub scroll_factor: Option<FloatOrInt<0, 100>>, + #[knuffel(child)] + pub scroll_factor: Option<ScrollFactor>, } #[derive(knuffel::Decode, Debug, Default, PartialEq)] @@ -4055,6 +4074,237 @@ mod tests { } #[test] + fn parse_scroll_factor_combined() { + // Test combined scroll-factor syntax + let parsed = do_parse( + r#" + input { + mouse { + scroll-factor 2.0 + } + touchpad { + scroll-factor 1.5 + } + } + "#, + ); + + assert_debug_snapshot!(parsed.input.mouse.scroll_factor, @r#" + Some( + ScrollFactor { + base: Some( + FloatOrInt( + 2.0, + ), + ), + horizontal: None, + vertical: None, + }, + ) + "#); + assert_debug_snapshot!(parsed.input.touchpad.scroll_factor, @r#" + Some( + ScrollFactor { + base: Some( + FloatOrInt( + 1.5, + ), + ), + horizontal: None, + vertical: None, + }, + ) + "#); + } + + #[test] + fn parse_scroll_factor_split() { + // Test split horizontal/vertical syntax + let parsed = do_parse( + r#" + input { + mouse { + scroll-factor horizontal=2.0 vertical=-1.0 + } + touchpad { + scroll-factor horizontal=-1.5 vertical=0.5 + } + } + "#, + ); + + assert_debug_snapshot!(parsed.input.mouse.scroll_factor, @r#" + Some( + ScrollFactor { + base: None, + horizontal: Some( + FloatOrInt( + 2.0, + ), + ), + vertical: Some( + FloatOrInt( + -1.0, + ), + ), + }, + ) + "#); + assert_debug_snapshot!(parsed.input.touchpad.scroll_factor, @r#" + Some( + ScrollFactor { + base: None, + horizontal: Some( + FloatOrInt( + -1.5, + ), + ), + vertical: Some( + FloatOrInt( + 0.5, + ), + ), + }, + ) + "#); + } + + #[test] + fn parse_scroll_factor_partial() { + // Test partial specification (only one axis) + let parsed = do_parse( + r#" + input { + mouse { + scroll-factor horizontal=2.0 + } + touchpad { + scroll-factor vertical=-1.5 + } + } + "#, + ); + + assert_debug_snapshot!(parsed.input.mouse.scroll_factor, @r#" + Some( + ScrollFactor { + base: None, + horizontal: Some( + FloatOrInt( + 2.0, + ), + ), + vertical: None, + }, + ) + "#); + assert_debug_snapshot!(parsed.input.touchpad.scroll_factor, @r#" + Some( + ScrollFactor { + base: None, + horizontal: None, + vertical: Some( + FloatOrInt( + -1.5, + ), + ), + }, + ) + "#); + } + + #[test] + fn parse_scroll_factor_mixed() { + // Test mixed base + override syntax + let parsed = do_parse( + r#" + input { + mouse { + scroll-factor 2 vertical=-1 + } + touchpad { + scroll-factor 1.5 horizontal=3 + } + } + "#, + ); + + assert_debug_snapshot!(parsed.input.mouse.scroll_factor, @r#" + Some( + ScrollFactor { + base: Some( + FloatOrInt( + 2.0, + ), + ), + horizontal: None, + vertical: Some( + FloatOrInt( + -1.0, + ), + ), + }, + ) + "#); + assert_debug_snapshot!(parsed.input.touchpad.scroll_factor, @r#" + Some( + ScrollFactor { + base: Some( + FloatOrInt( + 1.5, + ), + ), + horizontal: Some( + FloatOrInt( + 3.0, + ), + ), + vertical: None, + }, + ) + "#); + } + + #[test] + fn scroll_factor_h_v_factors() { + let sf = ScrollFactor { + base: Some(FloatOrInt(2.0)), + horizontal: None, + vertical: None, + }; + assert_debug_snapshot!(sf.h_v_factors(), @r#" + ( + 2.0, + 2.0, + ) + "#); + + let sf = ScrollFactor { + base: None, + horizontal: Some(FloatOrInt(3.0)), + vertical: Some(FloatOrInt(-1.0)), + }; + assert_debug_snapshot!(sf.h_v_factors(), @r#" + ( + 3.0, + -1.0, + ) + "#); + + let sf = ScrollFactor { + base: Some(FloatOrInt(2.0)), + horizontal: Some(FloatOrInt(1.0)), + vertical: None, + }; + assert_debug_snapshot!(sf.h_v_factors(), @r" + ( + 1.0, + 2.0, + ) + "); + } + + #[test] fn parse() { let parsed = do_parse( r##" @@ -4370,9 +4620,15 @@ mod tests { disabled_on_external_mouse: true, middle_emulation: false, scroll_factor: Some( - FloatOrInt( - 0.9, - ), + ScrollFactor { + base: Some( + FloatOrInt( + 0.9, + ), + ), + horizontal: None, + vertical: None, + }, ), }, mouse: Mouse { @@ -4394,9 +4650,15 @@ mod tests { left_handed: false, middle_emulation: true, scroll_factor: Some( - FloatOrInt( - 0.2, - ), + ScrollFactor { + base: Some( + FloatOrInt( + 0.2, + ), + ), + horizontal: None, + vertical: None, + }, ), }, trackpoint: Trackpoint { diff --git a/src/input/mod.rs b/src/input/mod.rs index c2667dfc..cc07aed3 100644 --- a/src/input/mod.rs +++ b/src/input/mod.rs @@ -3086,31 +3086,44 @@ impl State { self.update_pointer_contents(); - let scroll_factor = match source { - AxisSource::Wheel => self.niri.config.borrow().input.mouse.scroll_factor, - AxisSource::Finger => self.niri.config.borrow().input.touchpad.scroll_factor, - _ => None, + let device_scroll_factor = { + let config = self.niri.config.borrow(); + match source { + AxisSource::Wheel => config.input.mouse.scroll_factor, + AxisSource::Finger => config.input.touchpad.scroll_factor, + _ => None, + } }; - let scroll_factor = scroll_factor.map(|x| x.0).unwrap_or(1.); + // Get window-specific scroll factor let window_scroll_factor = pointer .current_focus() .map(|focused| self.niri.find_root_shell_surface(&focused)) .and_then(|root| self.niri.layout.find_window_and_output(&root).unzip().0) - .and_then(|window| window.rules().scroll_factor); - let scroll_factor = scroll_factor * window_scroll_factor.unwrap_or(1.); + .and_then(|window| window.rules().scroll_factor) + .unwrap_or(1.); + + // Determine final scroll factors based on configuration + let (horizontal_factor, vertical_factor) = device_scroll_factor + .map(|x| x.h_v_factors()) + .unwrap_or((1.0, 1.0)); + let (horizontal_factor, vertical_factor) = ( + horizontal_factor * window_scroll_factor, + vertical_factor * window_scroll_factor, + ); let horizontal_amount = horizontal_amount.unwrap_or_else(|| { // Winit backend, discrete scrolling. horizontal_amount_v120.unwrap_or(0.0) / 120. * 15. - }) * scroll_factor; + }) * horizontal_factor; + let vertical_amount = vertical_amount.unwrap_or_else(|| { // Winit backend, discrete scrolling. vertical_amount_v120.unwrap_or(0.0) / 120. * 15. - }) * scroll_factor; + }) * vertical_factor; - let horizontal_amount_v120 = horizontal_amount_v120.map(|x| x * scroll_factor); - let vertical_amount_v120 = vertical_amount_v120.map(|x| x * scroll_factor); + let horizontal_amount_v120 = horizontal_amount_v120.map(|x| x * horizontal_factor); + let vertical_amount_v120 = vertical_amount_v120.map(|x| x * vertical_factor); let mut frame = AxisFrame::new(event.time_msec()).source(source); if horizontal_amount != 0.0 { |
