From 012878fed4d2a60b37a137bd3e01d0b5157a5e5e Mon Sep 17 00:00:00 2001 From: Ian Douglas Scott Date: Mon, 4 Dec 2023 13:08:59 -0800 Subject: [PATCH] High res scrolling support This adds support for `wl_pointer` version 8, calling `wl_pointer::axis_value120` with the v120 values from libinput. For clients not binding version 8, values are accumulated to multiples of 120 to send the old `axis_discrete` event. This helps even for clients using `wl_pointer::axis` with old versions of `wl_pointer`, since the old libinput event only was emitted where there was a whole discrete scroll step. Raises libinput requirement to 1.19, which should be widely available now. Removes handling of versions before that. It should be possible to support high res scrolling in the X11 and Winit backends, where the platform supports it, but for now this just converts the low-res value to v120. --- Cargo.toml | 5 +- anvil/src/input_handler.rs | 12 ++-- smallvil/src/input.rs | 12 ++-- src/backend/input/mod.rs | 4 +- src/backend/libinput/mod.rs | 113 ++++++++++++++++++++++++------------ src/backend/winit/input.rs | 7 ++- src/backend/x11/input.rs | 4 +- src/input/pointer/mod.rs | 12 ++-- src/wayland/seat/mod.rs | 2 +- src/wayland/seat/pointer.rs | 58 ++++++++++++++++-- 10 files changed, 156 insertions(+), 73 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index bdaab2e1c830..24c3b759cbe6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -39,7 +39,7 @@ drm-ffi = { version = "0.7.0", optional = true } errno = "0.3.5" gbm = { version = "0.14.0", optional = true, default-features = false, features = ["drm-support"] } glow = { version = "0.12", optional = true } -input = { version = "0.8.2", default-features = false, features=["libinput_1_14"], optional = true } +input = { version = "0.8.2", default-features = false, features=["libinput_1_19"], optional = true } indexmap = "2.0" lazy_static = "1" libc = "0.2.103" @@ -94,7 +94,6 @@ backend_udev = ["udev", "input/udev"] backend_vulkan = ["ash", "scopeguard"] backend_session_libseat = ["backend_session", "libseat"] desktop = [] -libinput_1_19 = ["input/libinput_1_19"] renderer_gl = ["gl_generator", "backend_egl"] renderer_glow = ["renderer_gl", "glow"] renderer_multi = ["backend_drm"] @@ -104,7 +103,7 @@ use_bindgen = ["drm-ffi/use_bindgen", "gbm/use_bindgen", "input/gen"] wayland_frontend = ["wayland-server", "wayland-protocols", "wayland-protocols-wlr", "wayland-protocols-misc", "tempfile"] x11rb_event_source = ["x11rb"] xwayland = ["encoding_rs", "wayland_frontend", "x11rb/composite", "x11rb/xfixes", "x11rb_event_source", "scopeguard"] -test_all_features = ["default", "use_system_lib", "renderer_glow", "libinput_1_19", "renderer_test"] +test_all_features = ["default", "use_system_lib", "renderer_glow", "renderer_test"] [[example]] name = "minimal" diff --git a/anvil/src/input_handler.rs b/anvil/src/input_handler.rs index 90a25fcad9c7..190d914bf35c 100644 --- a/anvil/src/input_handler.rs +++ b/anvil/src/input_handler.rs @@ -384,25 +384,25 @@ impl AnvilState { fn on_pointer_axis(&mut self, _dh: &DisplayHandle, evt: B::PointerAxisEvent) { let horizontal_amount = evt .amount(input::Axis::Horizontal) - .unwrap_or_else(|| evt.amount_discrete(input::Axis::Horizontal).unwrap_or(0.0) * 3.0); + .unwrap_or_else(|| evt.amount_v120(input::Axis::Horizontal).unwrap_or(0.0) * 3.0 / 120.); let vertical_amount = evt .amount(input::Axis::Vertical) - .unwrap_or_else(|| evt.amount_discrete(input::Axis::Vertical).unwrap_or(0.0) * 3.0); - let horizontal_amount_discrete = evt.amount_discrete(input::Axis::Horizontal); - let vertical_amount_discrete = evt.amount_discrete(input::Axis::Vertical); + .unwrap_or_else(|| evt.amount_v120(input::Axis::Vertical).unwrap_or(0.0) * 3.0 / 120.); + let horizontal_amount_discrete = evt.amount_v120(input::Axis::Horizontal); + let vertical_amount_discrete = evt.amount_v120(input::Axis::Vertical); { let mut frame = AxisFrame::new(evt.time_msec()).source(evt.source()); if horizontal_amount != 0.0 { frame = frame.value(Axis::Horizontal, horizontal_amount); if let Some(discrete) = horizontal_amount_discrete { - frame = frame.discrete(Axis::Horizontal, discrete as i32); + frame = frame.v120(Axis::Horizontal, discrete as i32); } } if vertical_amount != 0.0 { frame = frame.value(Axis::Vertical, vertical_amount); if let Some(discrete) = vertical_amount_discrete { - frame = frame.discrete(Axis::Vertical, discrete as i32); + frame = frame.v120(Axis::Vertical, discrete as i32); } } if evt.source() == AxisSource::Finger { diff --git a/smallvil/src/input.rs b/smallvil/src/input.rs index 8b43cf8f3440..e1b6c8733f11 100644 --- a/smallvil/src/input.rs +++ b/smallvil/src/input.rs @@ -100,24 +100,24 @@ impl Smallvil { let horizontal_amount = event .amount(Axis::Horizontal) - .unwrap_or_else(|| event.amount_discrete(Axis::Horizontal).unwrap_or(0.0) * 3.0); + .unwrap_or_else(|| event.amount_v120(Axis::Horizontal).unwrap_or(0.0) * 3.0 / 120.); let vertical_amount = event .amount(Axis::Vertical) - .unwrap_or_else(|| event.amount_discrete(Axis::Vertical).unwrap_or(0.0) * 3.0); - let horizontal_amount_discrete = event.amount_discrete(Axis::Horizontal); - let vertical_amount_discrete = event.amount_discrete(Axis::Vertical); + .unwrap_or_else(|| event.amount_v120(Axis::Vertical).unwrap_or(0.0) * 3.0 / 120.); + let horizontal_amount_discrete = event.amount_v120(Axis::Horizontal); + let vertical_amount_discrete = event.amount_v120(Axis::Vertical); let mut frame = AxisFrame::new(event.time_msec()).source(source); if horizontal_amount != 0.0 { frame = frame.value(Axis::Horizontal, horizontal_amount); if let Some(discrete) = horizontal_amount_discrete { - frame = frame.discrete(Axis::Horizontal, discrete as i32); + frame = frame.v120(Axis::Horizontal, discrete as i32); } } if vertical_amount != 0.0 { frame = frame.value(Axis::Vertical, vertical_amount); if let Some(discrete) = vertical_amount_discrete { - frame = frame.discrete(Axis::Vertical, discrete as i32); + frame = frame.v120(Axis::Vertical, discrete as i32); } } diff --git a/src/backend/input/mod.rs b/src/backend/input/mod.rs index 842dc284cfe8..0174611274c0 100644 --- a/src/backend/input/mod.rs +++ b/src/backend/input/mod.rs @@ -353,7 +353,7 @@ pub trait PointerAxisEvent: Event { /// Amount of scrolling in discrete steps on the given [`Axis`]. /// /// Guaranteed to be `Some` when source returns either [`AxisSource::Wheel`] or [`AxisSource::WheelTilt`]. - fn amount_discrete(&self, axis: Axis) -> Option; + fn amount_v120(&self, axis: Axis) -> Option; /// Source of the scroll event. fn source(&self) -> AxisSource; @@ -364,7 +364,7 @@ impl PointerAxisEvent for UnusedEvent { match *self {} } - fn amount_discrete(&self, _axis: Axis) -> Option { + fn amount_v120(&self, _axis: Axis) -> Option { match *self {} } diff --git a/src/backend/libinput/mod.rs b/src/backend/libinput/mod.rs index 752e18cb364a..3e18f62d8039 100644 --- a/src/backend/libinput/mod.rs +++ b/src/backend/libinput/mod.rs @@ -1,7 +1,3 @@ -// TODO: Update to use new `Scroll*` events instead of now deprecated -// `PointerAxis*`. -#![cfg_attr(feature = "libinput_1_19", allow(deprecated))] - //! Implementation of input backend trait for types provided by `libinput` use crate::backend::input::{self as backend, Axis, InputBackend, InputEvent}; @@ -127,37 +123,80 @@ impl backend::KeyboardKeyEvent for event::keyboard::Keyboa } } -impl backend::Event for event::pointer::PointerAxisEvent { +/// Generic pointer scroll event from libinput +#[derive(Debug, PartialEq, Eq, Hash)] +pub enum PointerScrollAxis { + /// Scroll event from pointer wheel + Wheel(event::pointer::PointerScrollWheelEvent), + /// Scroll event from pointer finger + Finger(event::pointer::PointerScrollFingerEvent), + /// Continuous scroll event + Continuous(event::pointer::PointerScrollContinuousEvent), +} + +impl PointerScrollAxis { + fn has_axis(&self, axis: event::pointer::Axis) -> bool { + use input::event::pointer::PointerScrollEvent; + match self { + Self::Wheel(evt) => evt.has_axis(axis), + Self::Finger(evt) => evt.has_axis(axis), + Self::Continuous(evt) => evt.has_axis(axis), + } + } +} + +impl backend::Event for PointerScrollAxis { fn time(&self) -> u64 { - event::pointer::PointerEventTrait::time_usec(self) + match self { + Self::Wheel(evt) => event::pointer::PointerEventTrait::time_usec(evt), + Self::Finger(evt) => event::pointer::PointerEventTrait::time_usec(evt), + Self::Continuous(evt) => event::pointer::PointerEventTrait::time_usec(evt), + } } fn device(&self) -> libinput::Device { - event::EventTrait::device(self) + match self { + Self::Wheel(evt) => event::EventTrait::device(evt), + Self::Finger(evt) => event::EventTrait::device(evt), + Self::Continuous(evt) => event::EventTrait::device(evt), + } } } -impl backend::PointerAxisEvent for event::pointer::PointerAxisEvent { +impl backend::PointerAxisEvent for PointerScrollAxis { fn amount(&self, axis: Axis) -> Option { + use input::event::pointer::PointerScrollEvent; let axis = axis.into(); if self.has_axis(axis) { - Some(self.axis_value(axis)) + Some(match self { + Self::Wheel(evt) => evt.scroll_value(axis), + Self::Finger(evt) => evt.scroll_value(axis), + Self::Continuous(evt) => evt.scroll_value(axis), + }) } else { None } } - fn amount_discrete(&self, axis: Axis) -> Option { + fn amount_v120(&self, axis: Axis) -> Option { let axis = axis.into(); if self.has_axis(axis) { - self.axis_value_discrete(axis) + match self { + Self::Wheel(evt) => Some(evt.scroll_value_v120(axis)), + Self::Finger(_evt) => None, + Self::Continuous(_evt) => None, + } } else { None } } fn source(&self) -> backend::AxisSource { - self.axis_source().into() + match self { + Self::Wheel(_) => backend::AxisSource::Wheel, + Self::Finger(_) => backend::AxisSource::Finger, + Self::Continuous(_) => backend::AxisSource::Continuous, + } } } @@ -356,7 +395,6 @@ impl backend::Event for event::gesture::GesturePinchEndEve impl backend::GesturePinchEndEvent for event::gesture::GesturePinchEndEvent {} -#[cfg(feature = "libinput_1_19")] impl backend::Event for event::gesture::GestureHoldBeginEvent { fn time(&self) -> u64 { event::gesture::GestureEventTrait::time_usec(self) @@ -367,10 +405,8 @@ impl backend::Event for event::gesture::GestureHoldBeginEv } } -#[cfg(feature = "libinput_1_19")] impl backend::GestureHoldBeginEvent for event::gesture::GestureHoldBeginEvent {} -#[cfg(feature = "libinput_1_19")] impl backend::Event for event::gesture::GestureHoldEndEvent { fn time(&self) -> u64 { event::gesture::GestureEventTrait::time_usec(self) @@ -381,7 +417,6 @@ impl backend::Event for event::gesture::GestureHoldEndEven } } -#[cfg(feature = "libinput_1_19")] impl backend::GestureHoldEndEvent for event::gesture::GestureHoldEndEvent {} impl backend::Event for event::touch::TouchDownEvent { @@ -507,7 +542,7 @@ impl backend::TouchFrameEvent for event::touch::TouchFrame impl InputBackend for LibinputInputBackend { type Device = libinput::Device; type KeyboardKeyEvent = event::keyboard::KeyboardKeyEvent; - type PointerAxisEvent = event::pointer::PointerAxisEvent; + type PointerAxisEvent = PointerScrollAxis; type PointerButtonEvent = event::pointer::PointerButtonEvent; type PointerMotionEvent = event::pointer::PointerMotionEvent; type PointerMotionAbsoluteEvent = event::pointer::PointerMotionAbsoluteEvent; @@ -518,13 +553,7 @@ impl InputBackend for LibinputInputBackend { type GesturePinchBeginEvent = event::gesture::GesturePinchBeginEvent; type GesturePinchUpdateEvent = event::gesture::GesturePinchUpdateEvent; type GesturePinchEndEvent = event::gesture::GesturePinchEndEvent; - #[cfg(not(feature = "libinput_1_19"))] - type GestureHoldBeginEvent = backend::UnusedEvent; - #[cfg(not(feature = "libinput_1_19"))] - type GestureHoldEndEvent = backend::UnusedEvent; - #[cfg(feature = "libinput_1_19")] type GestureHoldBeginEvent = event::gesture::GestureHoldBeginEvent; - #[cfg(feature = "libinput_1_19")] type GestureHoldEndEvent = event::gesture::GestureHoldEndEvent; type TouchDownEvent = event::touch::TouchDownEvent; @@ -567,17 +596,6 @@ impl From for event::pointer::Axis { } } -impl From for backend::AxisSource { - fn from(libinput: event::pointer::AxisSource) -> Self { - match libinput { - event::pointer::AxisSource::Finger => backend::AxisSource::Finger, - event::pointer::AxisSource::Continuous => backend::AxisSource::Continuous, - event::pointer::AxisSource::Wheel => backend::AxisSource::Wheel, - event::pointer::AxisSource::WheelTilt => backend::AxisSource::WheelTilt, - } - } -} - impl From for backend::ButtonState { fn from(libinput: event::pointer::ButtonState) -> Self { match libinput { @@ -696,8 +714,29 @@ impl EventSource for LibinputInputBackend { &mut (), ); } - event::PointerEvent::Axis(axis_event) => { - callback(InputEvent::PointerAxis { event: axis_event }, &mut ()); + event::PointerEvent::ScrollWheel(scroll_event) => { + callback( + InputEvent::PointerAxis { + event: PointerScrollAxis::Wheel(scroll_event), + }, + &mut (), + ); + } + event::PointerEvent::ScrollFinger(scroll_event) => { + callback( + InputEvent::PointerAxis { + event: PointerScrollAxis::Finger(scroll_event), + }, + &mut (), + ); + } + event::PointerEvent::ScrollContinuous(scroll_event) => { + callback( + InputEvent::PointerAxis { + event: PointerScrollAxis::Continuous(scroll_event), + }, + &mut (), + ); } event::PointerEvent::Button(button_event) => { callback(InputEvent::PointerButton { event: button_event }, &mut ()); @@ -725,11 +764,9 @@ impl EventSource for LibinputInputBackend { event::GestureEvent::Pinch(event::gesture::GesturePinchEvent::End(event)) => { callback(InputEvent::GesturePinchEnd { event }, &mut ()); } - #[cfg(feature = "libinput_1_19")] event::GestureEvent::Hold(event::gesture::GestureHoldEvent::Begin(event)) => { callback(InputEvent::GestureHoldBegin { event }, &mut ()); } - #[cfg(feature = "libinput_1_19")] event::GestureEvent::Hold(event::gesture::GestureHoldEvent::End(event)) => { callback(InputEvent::GestureHoldEnd { event }, &mut ()); } diff --git a/src/backend/winit/input.rs b/src/backend/winit/input.rs index 855a64203c6e..63ce2e97a14d 100644 --- a/src/backend/winit/input.rs +++ b/src/backend/winit/input.rs @@ -148,10 +148,11 @@ impl PointerAxisEvent for WinitMouseWheelEvent { } } - fn amount_discrete(&self, axis: Axis) -> Option { + // TODO: Use high-res scroll where backend supports it + fn amount_v120(&self, axis: Axis) -> Option { match (axis, self.delta) { - (Axis::Horizontal, MouseScrollDelta::LineDelta(x, _)) => Some(x as f64), - (Axis::Vertical, MouseScrollDelta::LineDelta(_, y)) => Some(y as f64), + (Axis::Horizontal, MouseScrollDelta::LineDelta(x, _)) => Some(x as f64 * 120.), + (Axis::Vertical, MouseScrollDelta::LineDelta(_, y)) => Some(y as f64 * 120.), (_, MouseScrollDelta::PixelDelta(_)) => None, } } diff --git a/src/backend/x11/input.rs b/src/backend/x11/input.rs index bfcb1318f477..cd2c11a4833b 100644 --- a/src/backend/x11/input.rs +++ b/src/backend/x11/input.rs @@ -120,9 +120,9 @@ impl PointerAxisEvent for X11MouseWheelEvent { None } - fn amount_discrete(&self, axis: Axis) -> Option { + fn amount_v120(&self, axis: Axis) -> Option { if self.axis == axis { - Some(self.amount) + Some(self.amount * 120.) } else { Some(0.0) } diff --git a/src/input/pointer/mod.rs b/src/input/pointer/mod.rs index b1c7530848ba..0079956a36ed 100644 --- a/src/input/pointer/mod.rs +++ b/src/input/pointer/mod.rs @@ -919,7 +919,7 @@ pub struct AxisFrame { /// Raw scroll value per axis of the event pub axis: (f64, f64), /// Discrete representation of scroll value per axis, if available - pub discrete: Option<(i32, i32)>, + pub v120: Option<(i32, i32)>, /// If the axis is considered having stoped movement /// /// Only useful in conjunction of AxisSource::Finger events @@ -1021,7 +1021,7 @@ impl AxisFrame { source: None, time, axis: (0.0, 0.0), - discrete: None, + v120: None, stop: (false, false), } } @@ -1043,14 +1043,14 @@ impl AxisFrame { /// This event is optional and gives the client additional information about /// the nature of the axis event. E.g. a scroll wheel might issue separate steps, /// while a touchpad may never issue this event as it has no steps. - pub fn discrete(mut self, axis: Axis, steps: i32) -> Self { - let discrete = self.discrete.get_or_insert_with(Default::default); + pub fn v120(mut self, axis: Axis, steps: i32) -> Self { + let v120 = self.v120.get_or_insert_with(Default::default); match axis { Axis::Horizontal => { - discrete.0 = steps; + v120.0 = steps; } Axis::Vertical => { - discrete.1 = steps; + v120.1 = steps; } }; self diff --git a/src/wayland/seat/mod.rs b/src/wayland/seat/mod.rs index 987e4888dd14..0c61621f9aa8 100644 --- a/src/wayland/seat/mod.rs +++ b/src/wayland/seat/mod.rs @@ -161,7 +161,7 @@ impl SeatState { { let Seat { arc } = self.new_seat(name); - let global_id = display.create_global::(7, SeatGlobalData { arc: arc.clone() }); + let global_id = display.create_global::(8, SeatGlobalData { arc: arc.clone() }); arc.inner.lock().unwrap().global = Some(global_id); Seat { arc } diff --git a/src/wayland/seat/pointer.rs b/src/wayland/seat/pointer.rs index a6a628f3bc76..084d1d4b5b93 100644 --- a/src/wayland/seat/pointer.rs +++ b/src/wayland/seat/pointer.rs @@ -39,6 +39,13 @@ use crate::{ use super::{SeatHandler, SeatState, WaylandFocus}; +// Use to accumulate discrete values for `wl_pointer` < 8 +#[derive(Default)] +struct V120UserData { + x: i32, + y: i32, +} + impl PointerHandle { pub(crate) fn new_pointer(&self, pointer: WlPointer) { let mut guard = self.known_pointers.lock().unwrap(); @@ -197,6 +204,11 @@ where } }); } + compositor::with_states(self, |states| { + if let Some(data) = states.data_map.get::>() { + *data.lock().unwrap() = Default::default(); + } + }); } fn motion(&self, seat: &Seat, _data: &mut D, event: &MotionEvent) { for_each_focused_pointers(seat, self, |ptr| { @@ -241,20 +253,54 @@ where ptr.axis_source(source); } // axis discrete - if let Some((x, y)) = details.discrete { - if x != 0 { - ptr.axis_discrete(WlAxis::HorizontalScroll, x); - } - if y != 0 { - ptr.axis_discrete(WlAxis::VerticalScroll, y); + if let Some((x, y)) = details.v120 { + if ptr.version() >= 8 { + if x != 0 { + ptr.axis_value120(WlAxis::HorizontalScroll, x); + } + if y != 0 { + ptr.axis_value120(WlAxis::HorizontalScroll, y); + } + } else { + compositor::with_states(self, |states| { + let mut data = states + .data_map + .get_or_insert_threadsafe(Mutex::::default) + .lock() + .unwrap(); + + data.x += x; + if data.x >= 120 { + ptr.axis_discrete(WlAxis::HorizontalScroll, data.x / 120); + data.x %= 120; + } + + data.y += y; + if data.y >= 120 { + ptr.axis_discrete(WlAxis::VerticalScroll, y / 120); + data.y %= 120; + } + }); } } // stop if details.stop.0 { ptr.axis_stop(details.time, WlAxis::HorizontalScroll); + + compositor::with_states(self, |states| { + if let Some(data) = states.data_map.get::>() { + data.lock().unwrap().x = 0; + } + }); } if details.stop.1 { ptr.axis_stop(details.time, WlAxis::VerticalScroll); + + compositor::with_states(self, |states| { + if let Some(data) = states.data_map.get::>() { + data.lock().unwrap().y = 0; + } + }); } } // axis