From e27142508e760f955b162cd98dda1d2b360dc021 Mon Sep 17 00:00:00 2001 From: Ian Douglas Scott Date: Fri, 14 Oct 2022 15:04:34 -0700 Subject: [PATCH] Implement relative pointer protocol Add helper for the relative pointer protocol, and a way to send relative pointer events to clients. This also exposes unaccelerated vectors and microsecond timestamps in pointer events from the libinput backend. Enables `wlcs` relative pointer test, though what `wlcs` currently handles seems to be limited at this point. Adds relative pointer support for Anvil, exposing the global only on the `udev` backend so clients won't use the protocol but get no events. --- .github/workflows/ci.yml | 2 +- anvil/src/focus.rs | 14 +- anvil/src/input_handler.rs | 14 +- anvil/src/shell/element.rs | 17 +- anvil/src/shell/grabs.rs | 22 +- anvil/src/state.rs | 14 +- anvil/src/udev.rs | 2 + smallvil/src/grabs/move_grab.rs | 12 +- smallvil/src/grabs/resize_grab.rs | 12 +- src/backend/input/mod.rs | 29 +++ src/backend/libinput/mod.rs | 13 ++ src/desktop/wayland/layer.rs | 7 +- src/desktop/wayland/popup/grab.rs | 12 +- src/desktop/wayland/window.rs | 7 +- src/input/mod.rs | 9 +- src/input/pointer/grab.rs | 34 +++- src/input/pointer/mod.rs | 66 ++++++ src/wayland/data_device/dnd_grab.rs | 12 +- src/wayland/data_device/server_dnd_grab.rs | 12 +- src/wayland/mod.rs | 1 + src/wayland/relative_pointer.rs | 222 +++++++++++++++++++++ src/wayland/seat/pointer.rs | 37 +++- src/xwayland/xwm/surface.rs | 8 +- wlcs_anvil/src/main_loop.rs | 18 +- 24 files changed, 571 insertions(+), 25 deletions(-) create mode 100644 src/wayland/relative_pointer.rs diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index afd440ff55d2..7d9d7dab31e3 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -379,7 +379,7 @@ jobs: gtest_filter: "XdgOutputV1Test*" flag: output - job_name: "Pointer input tests" - gtest_filter: "*/SurfacePointerMotionTest*" + gtest_filter: "*/SurfacePointerMotionTest*:RelativePointer*" flag: pointer-input name: "WLCS: ${{ matrix.job_name }}" diff --git a/anvil/src/focus.rs b/anvil/src/focus.rs index ed62f532b3f2..537b145178f7 100644 --- a/anvil/src/focus.rs +++ b/anvil/src/focus.rs @@ -3,7 +3,7 @@ pub use smithay::{ desktop::{LayerSurface, PopupKind}, input::{ keyboard::{KeyboardTarget, KeysymHandle, ModifiersState}, - pointer::{AxisFrame, ButtonEvent, MotionEvent, PointerTarget}, + pointer::{AxisFrame, ButtonEvent, MotionEvent, PointerTarget, RelativeMotionEvent}, Seat, }, reexports::wayland_server::{backend::ObjectId, protocol::wl_surface::WlSurface, Resource}, @@ -64,6 +64,18 @@ impl PointerTarget> for FocusTarge FocusTarget::Popup(p) => PointerTarget::motion(p.wl_surface(), seat, data, event), } } + fn relative_motion( + &self, + seat: &Seat>, + data: &mut AnvilState, + event: &RelativeMotionEvent, + ) { + match self { + FocusTarget::Window(w) => PointerTarget::relative_motion(w, seat, data, event), + FocusTarget::LayerSurface(l) => PointerTarget::relative_motion(l.wl_surface(), seat, data, event), + FocusTarget::Popup(p) => PointerTarget::relative_motion(p.wl_surface(), seat, data, event), + } + } fn button( &self, seat: &Seat>, diff --git a/anvil/src/input_handler.rs b/anvil/src/input_handler.rs index 703be55448fd..448826af2dec 100644 --- a/anvil/src/input_handler.rs +++ b/anvil/src/input_handler.rs @@ -13,7 +13,7 @@ use smithay::{ desktop::{layer_map_for_output, WindowSurfaceType}, input::{ keyboard::{keysyms as xkb, FilterResult, Keysym, ModifiersState}, - pointer::{AxisFrame, ButtonEvent, MotionEvent}, + pointer::{AxisFrame, ButtonEvent, MotionEvent, RelativeMotionEvent}, }, output::Scale, reexports::wayland_server::{protocol::wl_pointer, DisplayHandle}, @@ -646,13 +646,23 @@ impl AnvilState { if let Some(ptr) = self.seat.get_pointer() { ptr.motion( self, - under, + under.clone(), &MotionEvent { location: self.pointer_location, serial, time: evt.time(), }, ); + + ptr.relative_motion( + self, + under, + &RelativeMotionEvent { + delta: evt.delta(), + delta_unaccel: evt.delta_unaccel(), + utime: evt.utime(), + }, + ) } } diff --git a/anvil/src/shell/element.rs b/anvil/src/shell/element.rs index 75ad553af00f..c95a66ed3851 100644 --- a/anvil/src/shell/element.rs +++ b/anvil/src/shell/element.rs @@ -14,7 +14,7 @@ use smithay::{ desktop::{space::SpaceElement, utils::OutputPresentationFeedback, Window, WindowSurfaceType}, input::{ keyboard::{KeyboardTarget, KeysymHandle, ModifiersState}, - pointer::{AxisFrame, ButtonEvent, MotionEvent, PointerTarget}, + pointer::{AxisFrame, ButtonEvent, MotionEvent, PointerTarget, RelativeMotionEvent}, Seat, }, output::Output, @@ -227,6 +227,21 @@ impl PointerTarget> for Wind }; } } + fn relative_motion( + &self, + seat: &Seat>, + data: &mut AnvilState, + event: &RelativeMotionEvent, + ) { + let state = self.decoration_state(); + if !state.is_ssd || state.ptr_entered_window { + match self { + WindowElement::Wayland(w) => PointerTarget::relative_motion(w, seat, data, event), + #[cfg(feature = "xwayland")] + WindowElement::X11(w) => PointerTarget::relative_motion(w, seat, data, event), + } + } + } fn button(&self, seat: &Seat>, data: &mut AnvilState, event: &ButtonEvent) { let mut state = self.decoration_state(); if state.is_ssd { diff --git a/anvil/src/shell/grabs.rs b/anvil/src/shell/grabs.rs index f865eca80f9c..2b62c06afbee 100644 --- a/anvil/src/shell/grabs.rs +++ b/anvil/src/shell/grabs.rs @@ -4,7 +4,7 @@ use smithay::{ desktop::space::SpaceElement, input::pointer::{ AxisFrame, ButtonEvent, GrabStartData as PointerGrabStartData, MotionEvent, PointerGrab, - PointerInnerHandle, + PointerInnerHandle, RelativeMotionEvent, }, reexports::wayland_protocols::xdg::shell::server::xdg_toplevel, utils::{IsAlive, Logical, Point, Serial, Size}, @@ -43,6 +43,16 @@ impl PointerGrab> for MoveSurfaceG .map_element(self.window.clone(), new_location.to_i32_round(), true); } + fn relative_motion( + &mut self, + data: &mut AnvilState, + handle: &mut PointerInnerHandle<'_, AnvilState>, + focus: Option<(FocusTarget, Point)>, + event: &RelativeMotionEvent, + ) { + handle.relative_motion(data, focus, event); + } + fn button( &mut self, data: &mut AnvilState, @@ -241,6 +251,16 @@ impl PointerGrab> for ResizeSurfac } } + fn relative_motion( + &mut self, + data: &mut AnvilState, + handle: &mut PointerInnerHandle<'_, AnvilState>, + focus: Option<(FocusTarget, Point)>, + event: &RelativeMotionEvent, + ) { + handle.relative_motion(data, focus, event); + } + fn button( &mut self, data: &mut AnvilState, diff --git a/anvil/src/state.rs b/anvil/src/state.rs index 1234365e87ff..e7f0a77348d4 100644 --- a/anvil/src/state.rs +++ b/anvil/src/state.rs @@ -8,9 +8,9 @@ use smithay::{ backend::renderer::element::{default_primary_scanout_output_compare, RenderElementStates}, delegate_compositor, delegate_data_device, delegate_fractional_scale, delegate_input_method_manager, delegate_keyboard_shortcuts_inhibit, delegate_layer_shell, delegate_output, delegate_presentation, - delegate_primary_selection, delegate_seat, delegate_shm, delegate_tablet_manager, - delegate_text_input_manager, delegate_viewporter, delegate_virtual_keyboard_manager, - delegate_xdg_activation, delegate_xdg_decoration, delegate_xdg_shell, + delegate_primary_selection, delegate_relative_pointer, delegate_seat, delegate_shm, + delegate_tablet_manager, delegate_text_input_manager, delegate_viewporter, + delegate_virtual_keyboard_manager, delegate_xdg_activation, delegate_xdg_decoration, delegate_xdg_shell, desktop::{ utils::{ surface_presentation_feedback_flags_from_states, surface_primary_scanout_output, @@ -46,6 +46,7 @@ use smithay::{ output::OutputManagerState, presentation::PresentationState, primary_selection::{set_primary_focus, PrimarySelectionHandler, PrimarySelectionState}, + relative_pointer::RelativePointerManagerState, seat::WaylandFocus, shell::{ wlr_layer::WlrLayerShellState, @@ -224,6 +225,8 @@ delegate_keyboard_shortcuts_inhibit!(@ AnvilStat delegate_virtual_keyboard_manager!(@ AnvilState); +delegate_relative_pointer!(@ AnvilState); + delegate_viewporter!(@ AnvilState); impl XdgActivationHandler for AnvilState { @@ -449,6 +452,10 @@ impl AnvilState { TextInputManagerState::new::(&dh); InputMethodManagerState::new::(&dh); VirtualKeyboardManagerState::new::(&dh, |_client| true); + // Expose global only if backend supports relative motion events + if BackendData::HAS_RELATIVE_MOTION { + RelativePointerManagerState::new::(&dh); + } // init input let seat_name = backend_data.seat_name(); @@ -634,6 +641,7 @@ pub fn take_presentation_feedback( } pub trait Backend { + const HAS_RELATIVE_MOTION: bool = false; fn seat_name(&self) -> String; fn reset_buffers(&mut self, output: &Output); fn early_import(&mut self, surface: &WlSurface); diff --git a/anvil/src/udev.rs b/anvil/src/udev.rs index 0a032cf6a9a7..3939170c2bba 100644 --- a/anvil/src/udev.rs +++ b/anvil/src/udev.rs @@ -125,6 +125,8 @@ impl DmabufHandler for AnvilState { delegate_dmabuf!(AnvilState); impl Backend for UdevData { + const HAS_RELATIVE_MOTION: bool = true; + fn seat_name(&self) -> String { self.session.seat() } diff --git a/smallvil/src/grabs/move_grab.rs b/smallvil/src/grabs/move_grab.rs index 1b9f9733cf1e..c9aacbec98ca 100644 --- a/smallvil/src/grabs/move_grab.rs +++ b/smallvil/src/grabs/move_grab.rs @@ -3,7 +3,7 @@ use smithay::{ desktop::Window, input::pointer::{ AxisFrame, ButtonEvent, GrabStartData as PointerGrabStartData, MotionEvent, PointerGrab, - PointerInnerHandle, + PointerInnerHandle, RelativeMotionEvent, }, reexports::wayland_server::protocol::wl_surface::WlSurface, utils::{Logical, Point}, @@ -32,6 +32,16 @@ impl PointerGrab for MoveSurfaceGrab { .map_element(self.window.clone(), new_location.to_i32_round(), true); } + fn relative_motion( + &mut self, + data: &mut Smallvil, + handle: &mut PointerInnerHandle<'_, Smallvil>, + focus: Option<(WlSurface, Point)>, + event: &RelativeMotionEvent, + ) { + handle.relative_motion(data, focus, event); + } + fn button( &mut self, data: &mut Smallvil, diff --git a/smallvil/src/grabs/resize_grab.rs b/smallvil/src/grabs/resize_grab.rs index f8601afcb145..ad9b0e99eaf6 100644 --- a/smallvil/src/grabs/resize_grab.rs +++ b/smallvil/src/grabs/resize_grab.rs @@ -3,7 +3,7 @@ use smithay::{ desktop::{Space, Window}, input::pointer::{ AxisFrame, ButtonEvent, GrabStartData as PointerGrabStartData, MotionEvent, PointerGrab, - PointerInnerHandle, + PointerInnerHandle, RelativeMotionEvent, }, reexports::{ wayland_protocols::xdg::shell::server::xdg_toplevel, wayland_server::protocol::wl_surface::WlSurface, @@ -125,6 +125,16 @@ impl PointerGrab for ResizeSurfaceGrab { xdg.send_configure(); } + fn relative_motion( + &mut self, + data: &mut Smallvil, + handle: &mut PointerInnerHandle<'_, Smallvil>, + focus: Option<(WlSurface, Point)>, + event: &RelativeMotionEvent, + ) { + handle.relative_motion(data, focus, event); + } + fn button( &mut self, data: &mut Smallvil, diff --git a/src/backend/input/mod.rs b/src/backend/input/mod.rs index 53b09b015540..8e3fc948f972 100644 --- a/src/backend/input/mod.rs +++ b/src/backend/input/mod.rs @@ -259,10 +259,27 @@ pub trait PointerMotionEvent: Event { (self.delta_x(), self.delta_y()).into() } + /// Unaccelerated delta between the last and new pointer device position + fn delta_unaccel(&self) -> Point { + (self.delta_x_unaccel(), self.delta_y_unaccel()).into() + } + /// Delta on the x axis between the last and new pointer device position interpreted as pixel movement fn delta_x(&self) -> f64; + /// Delta on the y axis between the last and new pointer device position interpreted as pixel movement fn delta_y(&self) -> f64; + + /// Unaccelerated delta on the x axis between the last and new pointer device position + fn delta_x_unaccel(&self) -> f64; + + /// Unaccelerated delta on the y axis between the last and new pointer device position + fn delta_y_unaccel(&self) -> f64; + + /// Returns an upward counting variable useful for event ordering, in microseconds. + /// + /// Makes no guarantees about actual time passed between events. + fn utime(&self) -> u64; } impl PointerMotionEvent for UnusedEvent { @@ -273,6 +290,18 @@ impl PointerMotionEvent for UnusedEvent { fn delta_y(&self) -> f64 { match *self {} } + + fn delta_x_unaccel(&self) -> f64 { + match *self {} + } + + fn delta_y_unaccel(&self) -> f64 { + match *self {} + } + + fn utime(&self) -> u64 { + match *self {} + } } /// Trait for pointer events generated by absolute device positioning. diff --git a/src/backend/libinput/mod.rs b/src/backend/libinput/mod.rs index c517d1afde59..6b9518c9e4b9 100644 --- a/src/backend/libinput/mod.rs +++ b/src/backend/libinput/mod.rs @@ -193,9 +193,22 @@ impl backend::PointerMotionEvent for event::pointer::Point fn delta_x(&self) -> f64 { self.dx() } + fn delta_y(&self) -> f64 { self.dy() } + + fn delta_x_unaccel(&self) -> f64 { + self.dx_unaccelerated() + } + + fn delta_y_unaccel(&self) -> f64 { + self.dy_unaccelerated() + } + + fn utime(&self) -> u64 { + event::pointer::PointerEventTrait::time_usec(self) + } } impl backend::Event for event::pointer::PointerMotionAbsoluteEvent { diff --git a/src/desktop/wayland/layer.rs b/src/desktop/wayland/layer.rs index be5b9d12ed83..1774940d7058 100644 --- a/src/desktop/wayland/layer.rs +++ b/src/desktop/wayland/layer.rs @@ -3,7 +3,7 @@ use crate::{ desktop::{utils::*, PopupManager}, input::{ keyboard::{KeyboardTarget, KeysymHandle, ModifiersState}, - pointer::{AxisFrame, ButtonEvent, MotionEvent, PointerTarget}, + pointer::{AxisFrame, ButtonEvent, MotionEvent, PointerTarget, RelativeMotionEvent}, Seat, SeatHandler, }, output::{Output, WeakOutput}, @@ -669,6 +669,11 @@ impl PointerTarget for LayerSurface { fn motion(&self, seat: &Seat, data: &mut D, event: &MotionEvent) { PointerTarget::::enter(self, seat, data, event) } + fn relative_motion(&self, seat: &Seat, data: &mut D, event: &RelativeMotionEvent) { + if let Some(surface) = self.0.focused_surface.lock().unwrap().as_ref() { + PointerTarget::::relative_motion(surface, seat, data, event) + } + } fn button(&self, seat: &Seat, data: &mut D, event: &ButtonEvent) { if let Some(surface) = self.0.focused_surface.lock().unwrap().as_ref() { PointerTarget::::button(surface, seat, data, event) diff --git a/src/desktop/wayland/popup/grab.rs b/src/desktop/wayland/popup/grab.rs index f3662fca1992..437ddfe932d6 100644 --- a/src/desktop/wayland/popup/grab.rs +++ b/src/desktop/wayland/popup/grab.rs @@ -14,7 +14,7 @@ use crate::{ }, pointer::{ AxisFrame, ButtonEvent, GrabStartData as PointerGrabStartData, MotionEvent, PointerGrab, - PointerInnerHandle, + PointerInnerHandle, RelativeMotionEvent, }, SeatHandler, }, @@ -573,6 +573,16 @@ where } } + fn relative_motion( + &mut self, + data: &mut D, + handle: &mut PointerInnerHandle<'_, D>, + focus: Option<(::PointerFocus, Point)>, + event: &RelativeMotionEvent, + ) { + handle.relative_motion(data, focus, event); + } + fn button(&mut self, data: &mut D, handle: &mut PointerInnerHandle<'_, D>, event: &ButtonEvent) { let serial = event.serial; let time = event.time; diff --git a/src/desktop/wayland/window.rs b/src/desktop/wayland/window.rs index 813f86bd57f6..e9cb0916a275 100644 --- a/src/desktop/wayland/window.rs +++ b/src/desktop/wayland/window.rs @@ -3,7 +3,7 @@ use crate::{ desktop::{space::RenderZindex, utils::*, PopupManager}, input::{ keyboard::{KeyboardTarget, KeysymHandle, ModifiersState}, - pointer::{AxisFrame, ButtonEvent, MotionEvent, PointerTarget}, + pointer::{AxisFrame, ButtonEvent, MotionEvent, PointerTarget, RelativeMotionEvent}, Seat, SeatHandler, }, output::Output, @@ -276,6 +276,11 @@ impl PointerTarget for Window { fn motion(&self, seat: &Seat, data: &mut D, event: &MotionEvent) { PointerTarget::::enter(self, seat, data, event) } + fn relative_motion(&self, seat: &Seat, data: &mut D, event: &RelativeMotionEvent) { + if let Some(surface) = self.0.focused_surface.lock().unwrap().as_ref() { + PointerTarget::::relative_motion(surface, seat, data, event) + } + } fn button(&self, seat: &Seat, data: &mut D, event: &ButtonEvent) { if let Some(surface) = self.0.focused_surface.lock().unwrap().as_ref() { PointerTarget::::button(surface, seat, data, event) diff --git a/src/input/mod.rs b/src/input/mod.rs index 6709bb150ff1..919f1dc6b00f 100644 --- a/src/input/mod.rs +++ b/src/input/mod.rs @@ -19,7 +19,7 @@ //! use smithay::input::{Seat, SeatState, SeatHandler, pointer::CursorImageStatus}; //! # use smithay::backend::input::KeyState; //! # use smithay::input::{ -//! # pointer::{PointerTarget, AxisFrame, MotionEvent, ButtonEvent}, +//! # pointer::{PointerTarget, AxisFrame, MotionEvent, ButtonEvent, RelativeMotionEvent}, //! # keyboard::{KeyboardTarget, KeysymHandle, ModifiersState}, //! # }; //! # use smithay::utils::{IsAlive, Serial}; @@ -45,6 +45,7 @@ //! # impl PointerTarget for Target { //! # fn enter(&self, seat: &Seat, data: &mut State, event: &MotionEvent) {} //! # fn motion(&self, seat: &Seat, data: &mut State, event: &MotionEvent) {} +//! # fn relative_motion(&self, seat: &Seat, data: &mut State, event: &RelativeMotionEvent) {} //! # fn button(&self, seat: &Seat, data: &mut State, event: &ButtonEvent) {} //! # fn axis(&self, seat: &Seat, data: &mut State, frame: AxisFrame) {} //! # fn leave(&self, seat: &Seat, data: &mut State, serial: Serial, time: u32) {} @@ -318,7 +319,7 @@ impl Seat { /// # use smithay::input::{Seat, SeatState, SeatHandler, pointer::CursorImageStatus}; /// # use smithay::backend::input::KeyState; /// # use smithay::input::{ - /// # pointer::{PointerTarget, AxisFrame, MotionEvent, ButtonEvent}, + /// # pointer::{PointerTarget, AxisFrame, MotionEvent, ButtonEvent, RelativeMotionEvent}, /// # keyboard::{KeyboardTarget, KeysymHandle, ModifiersState}, /// # }; /// # use smithay::utils::{IsAlive, Serial}; @@ -331,6 +332,7 @@ impl Seat { /// # impl PointerTarget for Target { /// # fn enter(&self, seat: &Seat, data: &mut State, event: &MotionEvent) {} /// # fn motion(&self, seat: &Seat, data: &mut State, event: &MotionEvent) {} + /// # fn relative_motion(&self, seat: &Seat, data: &mut State, event: &RelativeMotionEvent) {} /// # fn button(&self, seat: &Seat, data: &mut State, event: &ButtonEvent) {} /// # fn axis(&self, seat: &Seat, data: &mut State, frame: AxisFrame) {} /// # fn leave(&self, seat: &Seat, data: &mut State, serial: Serial, time: u32) {} @@ -413,7 +415,7 @@ impl Seat { /// # use smithay::input::{Seat, SeatState, SeatHandler, keyboard::XkbConfig, pointer::CursorImageStatus}; /// # use smithay::backend::input::KeyState; /// # use smithay::input::{ - /// # pointer::{PointerTarget, AxisFrame, MotionEvent, ButtonEvent}, + /// # pointer::{PointerTarget, AxisFrame, MotionEvent, ButtonEvent, RelativeMotionEvent}, /// # keyboard::{KeyboardTarget, KeysymHandle, ModifiersState}, /// # }; /// # use smithay::utils::{IsAlive, Serial}; @@ -426,6 +428,7 @@ impl Seat { /// # impl PointerTarget for Target { /// # fn enter(&self, seat: &Seat, data: &mut State, event: &MotionEvent) {} /// # fn motion(&self, seat: &Seat, data: &mut State, event: &MotionEvent) {} + /// # fn relative_motion(&self, seat: &Seat, data: &mut State, event: &RelativeMotionEvent) {} /// # fn button(&self, seat: &Seat, data: &mut State, event: &ButtonEvent) {} /// # fn axis(&self, seat: &Seat, data: &mut State, frame: AxisFrame) {} /// # fn leave(&self, seat: &Seat, data: &mut State, serial: Serial, time: u32) {} diff --git a/src/input/pointer/grab.rs b/src/input/pointer/grab.rs index 4129373f07f6..dce9a454b80b 100644 --- a/src/input/pointer/grab.rs +++ b/src/input/pointer/grab.rs @@ -7,7 +7,7 @@ use crate::{ utils::{Logical, Point}, }; -use super::{AxisFrame, ButtonEvent, Focus, MotionEvent, PointerInnerHandle}; +use super::{AxisFrame, ButtonEvent, Focus, MotionEvent, PointerInnerHandle, RelativeMotionEvent}; /// A trait to implement a pointer grab /// @@ -45,6 +45,18 @@ pub trait PointerGrab: Send { focus: Option<(::PointerFocus, Point)>, event: &MotionEvent, ); + /// Relative motion was reported + /// + /// This method allows you attach additional behavior to a relative motion event, possibly altering it. + /// You generally will want to invoke `PointerInnerHandle::relative_motion()` as part of your processing. + /// If you don't, the rest of the compositor will behave as if the motion event never occurred. + fn relative_motion( + &mut self, + data: &mut D, + handle: &mut PointerInnerHandle<'_, D>, + focus: Option<(::PointerFocus, Point)>, + event: &RelativeMotionEvent, + ); /// A button press was reported /// /// This method allows you attach additional behavior to a button event, possibly altering it. @@ -124,6 +136,16 @@ impl PointerGrab for DefaultGrab { handle.motion(data, focus, event); } + fn relative_motion( + &mut self, + data: &mut D, + handle: &mut PointerInnerHandle<'_, D>, + focus: Option<(::PointerFocus, Point)>, + event: &RelativeMotionEvent, + ) { + handle.relative_motion(data, focus, event); + } + fn button(&mut self, data: &mut D, handle: &mut PointerInnerHandle<'_, D>, event: &ButtonEvent) { handle.button(data, event); if event.state == ButtonState::Pressed { @@ -171,6 +193,16 @@ impl PointerGrab for ClickGrab { handle.motion(data, self.start_data.focus.clone(), event); } + fn relative_motion( + &mut self, + data: &mut D, + handle: &mut PointerInnerHandle<'_, D>, + focus: Option<(::PointerFocus, Point)>, + event: &RelativeMotionEvent, + ) { + handle.relative_motion(data, focus, event); + } + fn button(&mut self, data: &mut D, handle: &mut PointerInnerHandle<'_, D>, event: &ButtonEvent) { handle.button(data, event); if handle.current_pressed().is_empty() { diff --git a/src/input/pointer/mod.rs b/src/input/pointer/mod.rs index d398b055efa6..de38463be2fa 100644 --- a/src/input/pointer/mod.rs +++ b/src/input/pointer/mod.rs @@ -32,6 +32,8 @@ pub struct PointerHandle { pub(crate) inner: Arc>>, #[cfg(feature = "wayland_frontend")] pub(crate) known_pointers: Arc>>, + #[cfg(feature = "wayland_frontend")] + pub(crate) known_relative_pointers: Arc>>, } #[cfg(not(feature = "wayland_frontend"))] @@ -55,6 +57,7 @@ where f.debug_struct("PointerHandle") .field("inner", &self.inner) .field("known_pointers", &self.known_pointers) + .field("known_relative_pointers", &self.known_relative_pointers) .finish() } } @@ -65,6 +68,8 @@ impl Clone for PointerHandle { inner: self.inner.clone(), #[cfg(feature = "wayland_frontend")] known_pointers: self.known_pointers.clone(), + #[cfg(feature = "wayland_frontend")] + known_relative_pointers: self.known_relative_pointers.clone(), } } } @@ -84,6 +89,8 @@ where fn enter(&self, seat: &Seat, data: &mut D, event: &MotionEvent); /// A pointer of a given seat moved over this handler fn motion(&self, seat: &Seat, data: &mut D, event: &MotionEvent); + /// A pointer of a given seat that provides relative motion moved over this handler + fn relative_motion(&self, seat: &Seat, data: &mut D, event: &RelativeMotionEvent); /// A pointer of a given seat clicked a button fn button(&self, seat: &Seat, data: &mut D, event: &ButtonEvent); /// A pointer of a given seat scrolled on an axis @@ -98,6 +105,8 @@ impl PointerHandle { inner: Arc::new(Mutex::new(PointerInternal::new())), #[cfg(feature = "wayland_frontend")] known_pointers: Arc::new(Mutex::new(Vec::new())), + #[cfg(feature = "wayland_frontend")] + known_relative_pointers: Arc::new(Mutex::new(Vec::new())), } } @@ -169,6 +178,25 @@ impl PointerHandle { }); } + /// Notify about relative pointer motion + /// + /// This will internally send the appropriate button event to the client + /// objects matching with the currently focused surface, if the client uses + /// the relative pointer protocol. + pub fn relative_motion( + &self, + data: &mut D, + focus: Option<(::PointerFocus, Point)>, + event: &RelativeMotionEvent, + ) { + let mut inner = self.inner.lock().unwrap(); + inner.pending_focus = focus.clone(); + let seat = self.get_seat(data); + inner.with_grab(&seat, move |mut handle, grab| { + grab.relative_motion(data, &mut handle, focus, event); + }); + } + /// Notify that a button was pressed /// /// This will internally send the appropriate button event to the client @@ -304,6 +332,20 @@ impl<'a, D: SeatHandler + 'static> PointerInnerHandle<'a, D> { self.inner.motion(data, self.seat, focus, event); } + /// Notify about relative pointer motion + /// + /// This will internally send the appropriate button event to the client + /// objects matching with the currently focused surface, if the client uses + /// the relative pointer protocol. + pub fn relative_motion( + &mut self, + data: &mut D, + focus: Option<(::PointerFocus, Point)>, + event: &RelativeMotionEvent, + ) { + self.inner.relative_motion(data, self.seat, focus, event); + } + /// Notify that a button was pressed /// /// This will internally send the appropriate button event to the client @@ -449,6 +491,18 @@ impl PointerInternal { } } + fn relative_motion( + &mut self, + data: &mut D, + seat: &Seat, + _focus: Option<(::PointerFocus, Point)>, + event: &RelativeMotionEvent, + ) { + if let Some((focused, _)) = self.focus.as_mut() { + focused.relative_motion(seat, data, event); + } + } + fn with_grab(&mut self, seat: &Seat, f: F) where F: FnOnce(PointerInnerHandle<'_, D>, &mut dyn PointerGrab), @@ -487,6 +541,7 @@ pub enum Focus { /// Clear the current focus Clear, } + /// Pointer motion event #[derive(Debug, Clone)] pub struct MotionEvent { @@ -498,6 +553,17 @@ pub struct MotionEvent { pub time: u32, } +/// Relative pointer motion event +#[derive(Debug, Clone)] +pub struct RelativeMotionEvent { + /// Motional vector + pub delta: Point, + /// Unaccelerated motion vector + pub delta_unaccel: Point, + /// Timestamp in microseconds + pub utime: u64, +} + /// Pointer button event /// Mouse button click and release notifications. diff --git a/src/wayland/data_device/dnd_grab.rs b/src/wayland/data_device/dnd_grab.rs index e9facef887d6..96417be196ec 100644 --- a/src/wayland/data_device/dnd_grab.rs +++ b/src/wayland/data_device/dnd_grab.rs @@ -19,7 +19,7 @@ use crate::{ input::{ pointer::{ AxisFrame, ButtonEvent, GrabStartData as PointerGrabStartData, MotionEvent, PointerGrab, - PointerInnerHandle, + PointerInnerHandle, RelativeMotionEvent, }, Seat, SeatHandler, }, @@ -182,6 +182,16 @@ where } } + fn relative_motion( + &mut self, + data: &mut D, + handle: &mut PointerInnerHandle<'_, D>, + focus: Option<(::PointerFocus, Point)>, + event: &RelativeMotionEvent, + ) { + handle.relative_motion(data, focus, event); + } + fn button(&mut self, data: &mut D, handle: &mut PointerInnerHandle<'_, D>, event: &ButtonEvent) { if handle.current_pressed().is_empty() { // the user dropped, proceed to the drop diff --git a/src/wayland/data_device/server_dnd_grab.rs b/src/wayland/data_device/server_dnd_grab.rs index 57b032068d52..13423e804d20 100644 --- a/src/wayland/data_device/server_dnd_grab.rs +++ b/src/wayland/data_device/server_dnd_grab.rs @@ -17,7 +17,7 @@ use wayland_server::{ use crate::input::{ pointer::{ AxisFrame, ButtonEvent, GrabStartData as PointerGrabStartData, MotionEvent, PointerGrab, - PointerInnerHandle, + PointerInnerHandle, RelativeMotionEvent, }, Seat, SeatHandler, }; @@ -157,6 +157,16 @@ where } } + fn relative_motion( + &mut self, + data: &mut D, + handle: &mut PointerInnerHandle<'_, D>, + focus: Option<(::PointerFocus, Point)>, + event: &RelativeMotionEvent, + ) { + handle.relative_motion(data, focus, event); + } + fn button(&mut self, data: &mut D, handle: &mut PointerInnerHandle<'_, D>, event: &ButtonEvent) { let serial = event.serial; let time = event.time; diff --git a/src/wayland/mod.rs b/src/wayland/mod.rs index d8651a7733e5..8c75af30de02 100644 --- a/src/wayland/mod.rs +++ b/src/wayland/mod.rs @@ -55,6 +55,7 @@ pub mod keyboard_shortcuts_inhibit; pub mod output; pub mod presentation; pub mod primary_selection; +pub mod relative_pointer; pub mod seat; pub mod shell; pub mod shm; diff --git a/src/wayland/relative_pointer.rs b/src/wayland/relative_pointer.rs new file mode 100644 index 000000000000..db041bfbde3f --- /dev/null +++ b/src/wayland/relative_pointer.rs @@ -0,0 +1,222 @@ +//! Utilities for relative pointer support +//! +//! [PointerHandle::relative_motion] sends relative pointer events to any +//! [ZwpRelativePointerV1] objects created by the client. +//! +//! ``` +//! extern crate wayland_server; +//! extern crate smithay; +//! +//! use smithay::wayland::relative_pointer::RelativePointerManagerState; +//! use smithay::delegate_relative_pointer; +//! # use smithay::backend::input::KeyState; +//! # use smithay::input::{ +//! # pointer::{PointerTarget, AxisFrame, MotionEvent, ButtonEvent, RelativeMotionEvent}, +//! # keyboard::{KeyboardTarget, KeysymHandle, ModifiersState}, +//! # Seat, SeatHandler, SeatState, +//! # }; +//! # use smithay::utils::{IsAlive, Serial}; +//! +//! # #[derive(Debug, Clone, PartialEq)] +//! # struct Target; +//! # impl IsAlive for Target { +//! # fn alive(&self) -> bool { true } +//! # } +//! # impl PointerTarget for Target { +//! # fn enter(&self, seat: &Seat, data: &mut State, event: &MotionEvent) {} +//! # fn motion(&self, seat: &Seat, data: &mut State, event: &MotionEvent) {} +//! # fn relative_motion(&self, seat: &Seat, data: &mut State, event: &RelativeMotionEvent) {} +//! # fn button(&self, seat: &Seat, data: &mut State, event: &ButtonEvent) {} +//! # fn axis(&self, seat: &Seat, data: &mut State, frame: AxisFrame) {} +//! # fn leave(&self, seat: &Seat, data: &mut State, serial: Serial, time: u32) {} +//! # } +//! # impl KeyboardTarget for Target { +//! # fn enter(&self, seat: &Seat, data: &mut State, keys: Vec>, serial: Serial) {} +//! # fn leave(&self, seat: &Seat, data: &mut State, serial: Serial) {} +//! # fn key( +//! # &self, +//! # seat: &Seat, +//! # data: &mut State, +//! # key: KeysymHandle<'_>, +//! # state: KeyState, +//! # serial: Serial, +//! # time: u32, +//! # ) {} +//! # fn modifiers(&self, seat: &Seat, data: &mut State, modifiers: ModifiersState, serial: Serial) {} +//! # } +//! # struct State { +//! # seat_state: SeatState, +//! # }; +//! # let mut display = wayland_server::Display::::new().unwrap(); +//! # impl SeatHandler for State { +//! # type KeyboardFocus = Target; +//! # type PointerFocus = Target; +//! # +//! # fn seat_state(&mut self) -> &mut SeatState { +//! # &mut self.seat_state +//! # } +//! # } +//! let state = RelativePointerManagerState::new::(&display.handle()); +//! +//! delegate_relative_pointer!(State); +//! ``` + +use std::fmt; +use wayland_protocols::wp::relative_pointer::zv1::server::{ + zwp_relative_pointer_manager_v1::{self, ZwpRelativePointerManagerV1}, + zwp_relative_pointer_v1::{self, ZwpRelativePointerV1}, +}; +use wayland_server::{ + backend::{ClientId, GlobalId, ObjectId}, + Client, DataInit, Dispatch, DisplayHandle, GlobalDispatch, New, Resource, +}; + +use crate::{ + input::{pointer::PointerHandle, SeatHandler}, + wayland::seat::PointerUserData, +}; + +const MANAGER_VERSION: u32 = 1; + +/// User data of ZwpRelativePointerV1 object +pub struct RelativePointerUserData { + handle: Option>, +} + +impl fmt::Debug for RelativePointerUserData +where + ::PointerFocus: fmt::Debug, +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("RelativePointerUserData") + .field("handle", &self.handle) + .finish() + } +} + +/// State of the relative pointer manager +#[derive(Debug)] +pub struct RelativePointerManagerState { + global: GlobalId, +} + +impl RelativePointerManagerState { + /// Register new [ZwpRelativePointerV1] global + pub fn new(display: &DisplayHandle) -> Self + where + D: GlobalDispatch, + D: Dispatch, + D: Dispatch>, + D: SeatHandler, + D: 'static, + { + let global = display.create_global::(MANAGER_VERSION, ()); + + Self { global } + } + + /// [ZwpRelativePointerV1] GlobalId getter + pub fn global(&self) -> GlobalId { + self.global.clone() + } +} + +impl Dispatch for RelativePointerManagerState +where + D: Dispatch, + D: Dispatch>, + D: SeatHandler, + D: 'static, +{ + fn request( + _state: &mut D, + _client: &wayland_server::Client, + _relative_pointer_manager: &ZwpRelativePointerManagerV1, + request: zwp_relative_pointer_manager_v1::Request, + _data: &(), + _dh: &DisplayHandle, + data_init: &mut wayland_server::DataInit<'_, D>, + ) { + match request { + zwp_relative_pointer_manager_v1::Request::GetRelativePointer { id, pointer } => { + let handle = &pointer.data::>().unwrap().handle; + let user_data = RelativePointerUserData { + handle: handle.clone(), + }; + let pointer = data_init.init(id, user_data); + if let Some(handle) = handle { + handle.new_relative_pointer(pointer); + } + } + zwp_relative_pointer_manager_v1::Request::Destroy => {} + _ => unreachable!(), + } + } +} + +impl GlobalDispatch for RelativePointerManagerState +where + D: GlobalDispatch + + Dispatch + + SeatHandler + + 'static, +{ + fn bind( + _state: &mut D, + _dh: &DisplayHandle, + _client: &Client, + resource: New, + _global_data: &(), + data_init: &mut DataInit<'_, D>, + ) { + data_init.init(resource, ()); + } +} + +impl Dispatch, D> for RelativePointerManagerState +where + D: Dispatch>, + D: SeatHandler, + D: 'static, +{ + fn request( + _state: &mut D, + _client: &wayland_server::Client, + _relative_pointer: &ZwpRelativePointerV1, + request: zwp_relative_pointer_v1::Request, + _data: &RelativePointerUserData, + _dh: &DisplayHandle, + _data_init: &mut wayland_server::DataInit<'_, D>, + ) { + match request { + zwp_relative_pointer_v1::Request::Destroy => {} + _ => unreachable!(), + } + } + + fn destroyed(_state: &mut D, _: ClientId, object_id: ObjectId, data: &RelativePointerUserData) { + if let Some(ref handle) = data.handle { + handle + .known_relative_pointers + .lock() + .unwrap() + .retain(|p| p.id() != object_id); + } + } +} + +/// Macro to delegate implementation of the relative pointer protocol +#[macro_export] +macro_rules! delegate_relative_pointer { + ($(@<$( $lt:tt $( : $clt:tt $(+ $dlt:tt )* )? ),+>)? $ty: ty) => { + $crate::reexports::wayland_server::delegate_global_dispatch!($(@< $( $lt $( : $clt $(+ $dlt )* )? ),+ >)? $ty: [ + $crate::reexports::wayland_protocols::wp::relative_pointer::zv1::server::zwp_relative_pointer_manager_v1::ZwpRelativePointerManagerV1: () + ] => $crate::wayland::relative_pointer::RelativePointerManagerState); + $crate::reexports::wayland_server::delegate_dispatch!($(@< $( $lt $( : $clt $(+ $dlt )* )? ),+ >)? $ty: [ + $crate::reexports::wayland_protocols::wp::relative_pointer::zv1::server::zwp_relative_pointer_manager_v1::ZwpRelativePointerManagerV1: () + ] => $crate::wayland::relative_pointer::RelativePointerManagerState); + $crate::reexports::wayland_server::delegate_dispatch!($(@< $( $lt $( : $clt $(+ $dlt )* )? ),+ >)? $ty: [ + $crate::reexports::wayland_protocols::wp::relative_pointer::zv1::server::zwp_relative_pointer_v1::ZwpRelativePointerV1: $crate::wayland::relative_pointer::RelativePointerUserData + ] => $crate::wayland::relative_pointer::RelativePointerManagerState); + }; +} diff --git a/src/wayland/seat/pointer.rs b/src/wayland/seat/pointer.rs index 710163d25742..29e84b217178 100644 --- a/src/wayland/seat/pointer.rs +++ b/src/wayland/seat/pointer.rs @@ -1,5 +1,6 @@ use std::{fmt, sync::Mutex}; +use wayland_protocols::wp::relative_pointer::zv1::server::zwp_relative_pointer_v1::ZwpRelativePointerV1; use wayland_server::{ backend::{ClientId, ObjectId}, protocol::{ @@ -17,7 +18,7 @@ use crate::{ input::{ pointer::{ AxisFrame, ButtonEvent, CursorImageAttributes, CursorImageStatus, MotionEvent, PointerHandle, - PointerInternal, PointerTarget, + PointerInternal, PointerTarget, RelativeMotionEvent, }, Seat, }, @@ -32,6 +33,11 @@ impl PointerHandle { let mut guard = self.known_pointers.lock().unwrap(); guard.push(pointer); } + + pub(crate) fn new_relative_pointer(&self, pointer: ZwpRelativePointerV1) { + let mut guard = self.known_relative_pointers.lock().unwrap(); + guard.push(pointer); + } } /// WlSurface role of a cursor image icon @@ -52,6 +58,21 @@ fn for_each_focused_pointers( } } +fn for_each_focused_relative_pointers( + seat: &Seat, + surface: &WlSurface, + mut f: impl FnMut(ZwpRelativePointerV1), +) { + if let Some(pointer) = seat.get_pointer() { + let inner = pointer.known_relative_pointers.lock().unwrap(); + for ptr in &*inner { + if ptr.id().same_client_as(&surface.id()) { + f(ptr.clone()) + } + } + } +} + #[cfg(feature = "wayland_frontend")] impl PointerTarget for WlSurface where @@ -81,6 +102,20 @@ where } }) } + fn relative_motion(&self, seat: &Seat, _data: &mut D, event: &RelativeMotionEvent) { + for_each_focused_relative_pointers(seat, self, |ptr| { + let utime_hi = (event.utime >> 32) as u32; + let utime_lo = (event.utime & 0xffffffff) as u32; + ptr.relative_motion( + utime_hi, + utime_lo, + event.delta.x, + event.delta.y, + event.delta_unaccel.x, + event.delta_unaccel.y, + ); + }) + } fn button(&self, seat: &Seat, _data: &mut D, event: &ButtonEvent) { for_each_focused_pointers(seat, self, |ptr| { ptr.button(event.serial.into(), event.time, event.button, event.state.into()); diff --git a/src/xwayland/xwm/surface.rs b/src/xwayland/xwm/surface.rs index 659a7d070aa1..36b22e1bc394 100644 --- a/src/xwayland/xwm/surface.rs +++ b/src/xwayland/xwm/surface.rs @@ -2,7 +2,7 @@ use crate::{ backend::{input::KeyState, renderer::element::Id}, input::{ keyboard::{KeyboardTarget, KeysymHandle, ModifiersState}, - pointer::{AxisFrame, ButtonEvent, MotionEvent, PointerTarget}, + pointer::{AxisFrame, ButtonEvent, MotionEvent, PointerTarget, RelativeMotionEvent}, Seat, SeatHandler, }, utils::{user_data::UserDataMap, IsAlive, Logical, Rectangle, Serial, Size}, @@ -932,6 +932,12 @@ impl PointerTarget for X11Surface { } } + fn relative_motion(&self, seat: &Seat, data: &mut D, event: &RelativeMotionEvent) { + if let Some(surface) = self.state.lock().unwrap().wl_surface.as_ref() { + PointerTarget::relative_motion(surface, seat, data, event); + } + } + fn button(&self, seat: &Seat, data: &mut D, event: &ButtonEvent) { if let Some(surface) = self.state.lock().unwrap().wl_surface.as_ref() { PointerTarget::button(surface, seat, data, event); diff --git a/wlcs_anvil/src/main_loop.rs b/wlcs_anvil/src/main_loop.rs index 0f0cdd71b242..cdcfaf69e4b7 100644 --- a/wlcs_anvil/src/main_loop.rs +++ b/wlcs_anvil/src/main_loop.rs @@ -9,7 +9,9 @@ use smithay::{ input::ButtonState, renderer::{damage::DamageTrackedRenderer, element::AsRenderElements}, }, - input::pointer::{ButtonEvent, CursorImageAttributes, CursorImageStatus, MotionEvent}, + input::pointer::{ + ButtonEvent, CursorImageAttributes, CursorImageStatus, MotionEvent, RelativeMotionEvent, + }, output::{Mode, Output, PhysicalProperties, Subpixel}, reexports::{ calloop::{ @@ -248,15 +250,25 @@ fn handle_event( let serial = SCOUNTER.next_serial(); let under = state.surface_under(); let time = Duration::from(state.clock.now()).as_millis() as u32; - state.seat.get_pointer().unwrap().motion( + let ptr = state.seat.get_pointer().unwrap(); + ptr.motion( state, - under, + under.clone(), &MotionEvent { location: state.pointer_location, serial, time, }, ); + ptr.relative_motion( + state, + under, + &RelativeMotionEvent { + delta: delta, + delta_unaccel: delta, + utime: state.start_time.elapsed().as_micros() as u64, + }, + ) } WlcsEvent::PointerButtonDown { button_id, .. } => { let serial = SCOUNTER.next_serial();