diff --git a/examples/udev.rs b/examples/udev.rs index d51e28f..6cbe5dc 100644 --- a/examples/udev.rs +++ b/examples/udev.rs @@ -34,8 +34,7 @@ use smithay::backend::graphics::egl::EGLGraphicsBackend; use smithay::backend::graphics::egl::wayland::{EGLDisplay, EGLWaylandExtensions, Format}; use smithay::backend::input::{self, Event, InputBackend, InputHandler, KeyState, KeyboardKeyEvent, PointerAxisEvent, PointerButtonEvent}; -use smithay::backend::libinput::{libinput_bind, LibinputInputBackend, LibinputSessionInterface, - PointerAxisEvent as LibinputPointerAxisEvent}; +use smithay::backend::libinput::{libinput_bind, LibinputInputBackend, LibinputSessionInterface}; use smithay::backend::session::{Session, SessionNotifier}; use smithay::backend::session::auto::{auto_session_bind, AutoSession}; use smithay::backend::udev::{primary_gpu, udev_backend_bind, SessionFdDrmDevice, UdevBackend, UdevHandler}; @@ -187,13 +186,63 @@ impl InputHandler for LibinputInputHandler { self.pointer.button(button, state, serial, evt.time()); } fn on_pointer_axis( - &mut self, _evlh: &mut EventLoopHandle, _: &input::Seat, evt: LibinputPointerAxisEvent + &mut self, _evlh: &mut EventLoopHandle, _: &input::Seat, evt: event::pointer::PointerAxisEvent ) { - let axis = match evt.axis() { - input::Axis::Vertical => wayland_server::protocol::wl_pointer::Axis::VerticalScroll, - input::Axis::Horizontal => wayland_server::protocol::wl_pointer::Axis::HorizontalScroll, + let source = match evt.source() { + input::AxisSource::Continuous => wayland_server::protocol::wl_pointer::AxisSource::Continuous, + input::AxisSource::Finger => wayland_server::protocol::wl_pointer::AxisSource::Finger, + input::AxisSource::Wheel | input::AxisSource::WheelTilt => { + wayland_server::protocol::wl_pointer::AxisSource::Wheel + } }; - self.pointer.axis(axis, evt.amount(), evt.time()); + let horizontal_amount = evt.amount(&input::Axis::Horizontal) + .unwrap_or_else(|| evt.amount_discrete(&input::Axis::Horizontal).unwrap() * 3.0); + let vertical_amount = evt.amount(&input::Axis::Vertical) + .unwrap_or_else(|| evt.amount_discrete(&input::Axis::Vertical).unwrap() * 3.0); + let horizontal_amount_discrete = evt.amount_discrete(&input::Axis::Horizontal); + let vertical_amount_discrete = evt.amount_discrete(&input::Axis::Vertical); + + { + let mut event = self.pointer.axis(); + event.source(source); + if horizontal_amount != 0.0 { + event.value( + wayland_server::protocol::wl_pointer::Axis::HorizontalScroll, + horizontal_amount, + evt.time(), + ); + if let Some(discrete) = horizontal_amount_discrete { + event.discrete( + wayland_server::protocol::wl_pointer::Axis::HorizontalScroll, + discrete as i32, + ); + } + } else if source == wayland_server::protocol::wl_pointer::AxisSource::Finger { + event.stop( + wayland_server::protocol::wl_pointer::Axis::HorizontalScroll, + evt.time(), + ); + } + if vertical_amount != 0.0 { + event.value( + wayland_server::protocol::wl_pointer::Axis::VerticalScroll, + vertical_amount, + evt.time(), + ); + if let Some(discrete) = vertical_amount_discrete { + event.discrete( + wayland_server::protocol::wl_pointer::Axis::VerticalScroll, + discrete as i32, + ); + } + } else if source == wayland_server::protocol::wl_pointer::AxisSource::Finger { + event.stop( + wayland_server::protocol::wl_pointer::Axis::VerticalScroll, + evt.time(), + ); + } + event.done(); + } } fn on_touch_down( &mut self, _evlh: &mut EventLoopHandle, _: &input::Seat, _: event::touch::TouchDownEvent diff --git a/examples/winit.rs b/examples/winit.rs index e27af60..24ff502 100644 --- a/examples/winit.rs +++ b/examples/winit.rs @@ -108,11 +108,49 @@ impl InputHandler for WinitInputHandler { fn on_pointer_axis( &mut self, _evlh: &mut EventLoopHandle, _: &input::Seat, evt: winit::WinitMouseWheelEvent ) { - let axis = match evt.axis() { - input::Axis::Vertical => wayland_server::protocol::wl_pointer::Axis::VerticalScroll, - input::Axis::Horizontal => wayland_server::protocol::wl_pointer::Axis::HorizontalScroll, + let source = match evt.source() { + input::AxisSource::Continuous => wayland_server::protocol::wl_pointer::AxisSource::Continuous, + input::AxisSource::Wheel => wayland_server::protocol::wl_pointer::AxisSource::Wheel, + _ => unreachable!(), //winit does not have more specific sources }; - self.pointer.axis(axis, evt.amount(), evt.time()); + let horizontal_amount = evt.amount(&input::Axis::Horizontal) + .unwrap_or_else(|| evt.amount_discrete(&input::Axis::Horizontal).unwrap() * 3.0); + let vertical_amount = evt.amount(&input::Axis::Vertical) + .unwrap_or_else(|| evt.amount_discrete(&input::Axis::Vertical).unwrap() * 3.0); + let horizontal_amount_discrete = evt.amount_discrete(&input::Axis::Horizontal); + let vertical_amount_discrete = evt.amount_discrete(&input::Axis::Vertical); + + { + let mut event = self.pointer.axis(); + event.source(source); + if horizontal_amount != 0.0 { + event.value( + wayland_server::protocol::wl_pointer::Axis::HorizontalScroll, + horizontal_amount, + evt.time(), + ); + if let Some(discrete) = horizontal_amount_discrete { + event.discrete( + wayland_server::protocol::wl_pointer::Axis::HorizontalScroll, + discrete as i32, + ); + } + } + if vertical_amount != 0.0 { + event.value( + wayland_server::protocol::wl_pointer::Axis::VerticalScroll, + vertical_amount, + evt.time(), + ); + if let Some(discrete) = vertical_amount_discrete { + event.discrete( + wayland_server::protocol::wl_pointer::Axis::VerticalScroll, + discrete as i32, + ); + } + } + event.done(); + } } fn on_touch_down( &mut self, _evlh: &mut EventLoopHandle, _: &input::Seat, _: winit::WinitTouchStartedEvent diff --git a/src/backend/input.rs b/src/backend/input.rs index 379cdfe..bed7877 100644 --- a/src/backend/input.rs +++ b/src/backend/input.rs @@ -214,26 +214,32 @@ pub enum AxisSource { /// Trait for pointer events generated by scrolling on an axis. pub trait PointerAxisEvent: Event { - /// `Axis` this event was generated for. - fn axis(&self) -> Axis; - /// Source of the scroll event. Important for interpretation of `amount`. + /// Amount of scrolling in pixels on the given `Axis`. + /// + /// Garanteed to be `Some` when source returns either `AxisSource::Finger` or `AxisSource::Continuous`. + fn amount(&self, axis: &Axis) -> Option; + + /// Amount of scrolling in discrete steps on the given `Axis`. + /// + /// Garanteed to be `Some` when source returns either `AxisSource::Wheel` or `AxisSource::WheelTilt`. + fn amount_discrete(&self, axis: &Axis) -> Option; + + /// Source of the scroll event. fn source(&self) -> AxisSource; - /// Amount of scrolling on the given `Axis`. See `source` for interpretation. - fn amount(&self) -> f64; } impl PointerAxisEvent for UnusedEvent { - fn axis(&self) -> Axis { + fn amount(&self, _axis: &Axis) -> Option { + match *self {} + } + + fn amount_discrete(&self, _axis: &Axis) -> Option { match *self {} } fn source(&self) -> AxisSource { match *self {} } - - fn amount(&self) -> f64 { - match *self {} - } } /// Trait for pointer events generated by relative device movement. diff --git a/src/backend/libinput.rs b/src/backend/libinput.rs index 84becdf..caef89d 100644 --- a/src/backend/libinput.rs +++ b/src/backend/libinput.rs @@ -1,6 +1,7 @@ //! Implementation of input backend trait for types provided by `libinput` use backend::input as backend; +use backend::input::Axis; #[cfg(feature = "backend_session")] use backend::session::{AsErrno, Session, SessionObserver}; use input as libinput; @@ -10,7 +11,6 @@ use std::hash::{Hash, Hasher}; use std::io::Error as IoError; use std::os::unix::io::RawFd; use std::path::Path; -use std::rc::Rc; use wayland_server::EventLoopHandle; use wayland_server::sources::{FdEventSource, FdEventSourceImpl, FdInterest}; @@ -73,35 +73,23 @@ impl backend::KeyboardKeyEvent for event::keyboard::KeyboardKeyEvent { } } -/// Wrapper for libinput pointer axis events to implement `backend::input::PointerAxisEvent` -pub struct PointerAxisEvent { - axis: event::pointer::Axis, - event: Rc, -} - -impl<'a> backend::Event for PointerAxisEvent { +impl<'a> backend::Event for event::pointer::PointerAxisEvent { fn time(&self) -> u32 { - use input::event::pointer::PointerEventTrait; - self.event.time() + event::pointer::PointerEventTrait::time(self) } } -impl<'a> backend::PointerAxisEvent for PointerAxisEvent { - fn axis(&self) -> backend::Axis { - self.axis.into() +impl backend::PointerAxisEvent for event::pointer::PointerAxisEvent { + fn amount(&self, axis: &Axis) -> Option { + Some(self.axis_value((*axis).into())) + } + + fn amount_discrete(&self, axis: &Axis) -> Option { + self.axis_value_discrete((*axis).into()) } fn source(&self) -> backend::AxisSource { - self.event.axis_source().into() - } - - fn amount(&self) -> f64 { - match self.source() { - backend::AxisSource::Finger | backend::AxisSource::Continuous => self.event.axis_value(self.axis), - backend::AxisSource::Wheel | backend::AxisSource::WheelTilt => { - self.event.axis_value_discrete(self.axis).unwrap() - } - } + self.axis_source().into() } } @@ -258,7 +246,7 @@ impl backend::InputBackend for LibinputInputBackend { type EventError = IoError; type KeyboardKeyEvent = event::keyboard::KeyboardKeyEvent; - type PointerAxisEvent = PointerAxisEvent; + type PointerAxisEvent = event::pointer::PointerAxisEvent; type PointerButtonEvent = event::pointer::PointerButtonEvent; type PointerMotionEvent = event::pointer::PointerMotionEvent; type PointerMotionAbsoluteEvent = event::pointer::PointerMotionAbsoluteEvent; @@ -492,37 +480,8 @@ impl backend::InputBackend for LibinputInputBackend { handler.on_pointer_move_absolute(evlh, seat, motion_abs_event); } PointerEvent::Axis(axis_event) => { - let rc_axis_event = Rc::new(axis_event); - if rc_axis_event.has_axis(Axis::Vertical) { - trace!( - self.logger, - "Calling on_pointer_axis for Axis::Vertical with {:?}", - *rc_axis_event - ); - handler.on_pointer_axis( - evlh, - seat, - self::PointerAxisEvent { - axis: Axis::Vertical, - event: rc_axis_event.clone(), - }, - ); - } - if rc_axis_event.has_axis(Axis::Horizontal) { - trace!( - self.logger, - "Calling on_pointer_axis for Axis::Horizontal with {:?}", - *rc_axis_event - ); - handler.on_pointer_axis( - evlh, - seat, - self::PointerAxisEvent { - axis: Axis::Horizontal, - event: rc_axis_event.clone(), - }, - ); - } + trace!(self.logger, "Calling on_pointer_axis with {:?}", axis_event); + handler.on_pointer_axis(evlh, seat, axis_event); } PointerEvent::Button(button_event) => { trace!( @@ -564,6 +523,15 @@ impl From for backend::Axis { } } +impl From for event::pointer::Axis { + fn from(axis: backend::Axis) -> Self { + match axis { + backend::Axis::Vertical => event::pointer::Axis::Vertical, + backend::Axis::Horizontal => event::pointer::Axis::Horizontal, + } + } +} + impl From for backend::AxisSource { fn from(libinput: event::pointer::AxisSource) -> Self { match libinput { diff --git a/src/backend/winit.rs b/src/backend/winit.rs index f3ef266..40c538f 100644 --- a/src/backend/winit.rs +++ b/src/backend/winit.rs @@ -388,7 +388,6 @@ impl PointerMotionAbsoluteEvent for WinitMouseMovedEvent { #[derive(Debug, Clone, Copy, PartialEq)] /// Winit-Backend internal event wrapping winit's types into a `PointerAxisEvent` pub struct WinitMouseWheelEvent { - axis: Axis, time: u32, delta: MouseScrollDelta, } @@ -400,10 +399,6 @@ impl BackendEvent for WinitMouseWheelEvent { } impl PointerAxisEvent for WinitMouseWheelEvent { - fn axis(&self) -> Axis { - self.axis - } - fn source(&self) -> AxisSource { match self.delta { MouseScrollDelta::LineDelta(_, _) => AxisSource::Wheel, @@ -411,12 +406,19 @@ impl PointerAxisEvent for WinitMouseWheelEvent { } } - fn amount(&self) -> f64 { - match (self.axis, self.delta) { - (Axis::Horizontal, MouseScrollDelta::LineDelta(x, _)) - | (Axis::Horizontal, MouseScrollDelta::PixelDelta(x, _)) => x as f64, - (Axis::Vertical, MouseScrollDelta::LineDelta(_, y)) - | (Axis::Vertical, MouseScrollDelta::PixelDelta(_, y)) => y as f64, + fn amount(&self, axis: &Axis) -> Option { + match (axis, self.delta) { + (&Axis::Horizontal, MouseScrollDelta::PixelDelta(x, _)) => Some(x as f64), + (&Axis::Vertical, MouseScrollDelta::PixelDelta(_, y)) => Some(y as f64), + (_, MouseScrollDelta::LineDelta(_, _)) => None, + } + } + + fn amount_discrete(&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), + (_, MouseScrollDelta::PixelDelta(_, _)) => None, } } } @@ -764,36 +766,11 @@ impl InputBackend for WinitInputBackend { }, ) } - (WindowEvent::MouseWheel { delta, .. }, Some(handler), _) => match delta { - MouseScrollDelta::LineDelta(x, y) | MouseScrollDelta::PixelDelta(x, y) => { - if x != 0.0 { - let event = WinitMouseWheelEvent { - axis: Axis::Horizontal, - time, - delta: delta, - }; - trace!( - logger, - "Calling on_pointer_axis for Axis::Horizontal with {:?}", - x - ); - handler.on_pointer_axis(evlh, seat, event); - } - if y != 0.0 { - let event = WinitMouseWheelEvent { - axis: Axis::Vertical, - time, - delta: delta, - }; - trace!( - logger, - "Calling on_pointer_axis for Axis::Vertical with {:?}", - y - ); - handler.on_pointer_axis(evlh, seat, event); - } - } - }, + (WindowEvent::MouseWheel { delta, .. }, Some(handler), _) => { + let event = WinitMouseWheelEvent { time, delta }; + trace!(logger, "Calling on_pointer_axis with {:?}", delta); + handler.on_pointer_axis(evlh, seat, event); + } (WindowEvent::MouseInput { state, button, .. }, Some(handler), _) => { trace!( logger, diff --git a/src/wayland/seat/mod.rs b/src/wayland/seat/mod.rs index 5fc6b2e..092b85b 100644 --- a/src/wayland/seat/mod.rs +++ b/src/wayland/seat/mod.rs @@ -58,7 +58,7 @@ mod keyboard; mod pointer; pub use self::keyboard::{Error as KeyboardError, KeyboardHandle, ModifiersState}; -pub use self::pointer::PointerHandle; +pub use self::pointer::{PointerAxisHandle, PointerHandle}; use wayland_server::{Client, EventLoopHandle, Global, Liveness, Resource, StateToken}; use wayland_server::protocol::{wl_keyboard, wl_pointer, wl_seat}; @@ -102,8 +102,7 @@ impl Seat { known_seats: Vec::new(), }; let token = evlh.state().insert(seat); - // TODO: support version 5 (axis) - let global = evlh.register_global(4, seat_global_bind, token.clone()); + let global = evlh.register_global(5, seat_global_bind, token.clone()); (token, global) } diff --git a/src/wayland/seat/pointer.rs b/src/wayland/seat/pointer.rs index 7fa87a6..10522e2 100644 --- a/src/wayland/seat/pointer.rs +++ b/src/wayland/seat/pointer.rs @@ -1,4 +1,4 @@ -use std::sync::{Arc, Mutex}; +use std::sync::{Arc, Mutex, MutexGuard}; use wayland_server::{Liveness, Resource}; use wayland_server::protocol::{wl_pointer, wl_surface}; @@ -73,6 +73,9 @@ impl PointerHandle { if leave { guard.with_focused_pointers(|pointer, surface| { pointer.leave(serial, surface); + if pointer.version() >= 5 { + pointer.frame(); + } }); guard.focus = None; } @@ -83,11 +86,17 @@ impl PointerHandle { guard.focus = surface.clone(); guard.with_focused_pointers(|pointer, surface| { pointer.enter(serial, surface, x, y); + if pointer.version() >= 5 { + pointer.frame(); + } }) } else { // we were on top of a surface and remained on it guard.with_focused_pointers(|pointer, _| { pointer.motion(time, x, y); + if pointer.version() >= 5 { + pointer.frame(); + } }) } } @@ -101,15 +110,20 @@ impl PointerHandle { let guard = self.inner.lock().unwrap(); guard.with_focused_pointers(|pointer, _| { pointer.button(serial, time, button, state); + if pointer.version() >= 5 { + pointer.frame(); + } }) } - /// send axis events - pub fn axis(&self, axis: wl_pointer::Axis, value: f64, time: u32) { - let guard = self.inner.lock().unwrap(); - guard.with_focused_pointers(|pointer, _| { - pointer.axis(time, axis, value); - }) + /// Start an axis frame + /// + /// A single frame will group multiple scroll events as if they happended in the same instance. + /// Dropping the returned `PointerAxisHandle` will group the events together. + pub fn axis<'a>(&'a self) -> PointerAxisHandle<'a> { + PointerAxisHandle { + inner: self.inner.lock().unwrap(), + } } pub(crate) fn cleanup_old_pointers(&self) { @@ -120,6 +134,87 @@ impl PointerHandle { } } +/// A frame of pointer axis events. +/// +/// Can be used with the builder pattern, e.g.: +/// ```ignore +/// pointer.axis() +/// .source(AxisSource::Wheel) +/// .discrete(Axis::Vertical, 6) +/// .value(Axis::Vertical, 30, time) +/// .stop(Axis::Vertical); +/// ``` +pub struct PointerAxisHandle<'a> { + inner: MutexGuard<'a, PointerInternal>, +} + +impl<'a> PointerAxisHandle<'a> { + /// Specify the source of the axis events + /// + /// This event is optional, if no source is known, you can ignore this call. + /// Only one source event is allowed per frame. + /// + /// Using the `AxisSource::Finger` requires a stop event to be send, + /// when the user lifts off the finger (not necessarily in the same frame). + pub fn source(&mut self, source: wl_pointer::AxisSource) -> &mut Self { + self.inner.with_focused_pointers(|pointer, _| { + if pointer.version() >= 5 { + pointer.axis_source(source); + } + }); + self + } + + /// Specify discrete scrolling steps additionally to the computed value. + /// + /// 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: wl_pointer::Axis, steps: i32) -> &mut Self { + self.inner.with_focused_pointers(|pointer, _| { + if pointer.version() >= 5 { + pointer.axis_discrete(axis, steps); + } + }); + self + } + + /// The actual scroll value. This event is the only required one, but can also + /// be send multiple times. The values off one frame will be accumulated by the client. + pub fn value(&mut self, axis: wl_pointer::Axis, value: f64, time: u32) -> &mut Self { + self.inner.with_focused_pointers(|pointer, _| { + pointer.axis(time, axis, value); + }); + self + } + + /// Notification of stop of scrolling on an axis. + /// + /// This event is required for sources of the `AxisSource::Finger` type + /// and otherwise optional. + pub fn stop(&mut self, axis: wl_pointer::Axis, time: u32) -> &mut Self { + self.inner.with_focused_pointers(|pointer, _| { + if pointer.version() >= 5 { + pointer.axis_stop(time, axis); + } + }); + self + } + + /// Finish this event + /// + /// This will group all axis calls together. + /// Note: They are already submitted to the client, obmitting this call just + /// leaves room for additional events. + pub fn done(&mut self) { + self.inner.with_focused_pointers(|pointer, _| { + if pointer.version() >= 5 { + pointer.frame(); + } + }) + } +} + pub(crate) fn create_pointer_handler() -> PointerHandle { PointerHandle { inner: Arc::new(Mutex::new(PointerInternal::new())),