From d797cdcdcbdc16378231e48eb31f3cc7a5cdca49 Mon Sep 17 00:00:00 2001 From: Victor Berger Date: Tue, 1 Feb 2022 19:23:01 +0100 Subject: [PATCH] wayland.seat: Fix pointer grab API The pointer grab mecanism had a few inconsistencies in its behavior when a grab is set or unset. THis commit uniformizes the behavior, and also introduces a synthetic motion event when the grab is set, allowing the newly set grab to change the focus or the pointer location if needed. Also adjust the DnDGrab, as well as the resize and move grabs from anvil to unset the focus while they are active, matching weston's behavior. --- anvil/src/shell.rs | 20 ++-- src/wayland/data_device/dnd_grab.rs | 5 +- src/wayland/data_device/mod.rs | 2 + src/wayland/seat/pointer.rs | 150 +++++++++++++++++----------- 4 files changed, 113 insertions(+), 64 deletions(-) diff --git a/anvil/src/shell.rs b/anvil/src/shell.rs index 627b498..0e31f3f 100644 --- a/anvil/src/shell.rs +++ b/anvil/src/shell.rs @@ -48,12 +48,15 @@ struct MoveSurfaceGrab { impl PointerGrab for MoveSurfaceGrab { fn motion( &mut self, - _handle: &mut PointerInnerHandle<'_>, + handle: &mut PointerInnerHandle<'_>, location: Point, _focus: Option<(wl_surface::WlSurface, Point)>, - _serial: Serial, - _time: u32, + serial: Serial, + time: u32, ) { + // While the grab is active, no client has pointer focus + handle.motion(location, None, serial, time); + let delta = location - self.start_data.location; let new_location = self.initial_window_location.to_f64() + delta; @@ -152,6 +155,9 @@ impl PointerGrab for ResizeSurfaceGrab { return; } + // While the grab is active, no client has pointer focus + handle.motion(location, None, serial, time); + let (mut dx, mut dy) = (location - self.start_data.location).into(); let mut new_window_width = self.initial_window_size.w; @@ -479,7 +485,7 @@ pub fn init_shell(display: Rc>, log: ::sl initial_window_location, }; - pointer.set_grab(grab, serial); + pointer.set_grab(grab, serial, 0); } XdgRequest::Resize { @@ -539,7 +545,7 @@ pub fn init_shell(display: Rc>, log: ::sl last_window_size: initial_window_size, }; - pointer.set_grab(grab, serial); + pointer.set_grab(grab, serial, 0); } XdgRequest::AckConfigure { @@ -806,7 +812,7 @@ pub fn init_shell(display: Rc>, log: ::sl initial_window_location, }; - pointer.set_grab(grab, serial); + pointer.set_grab(grab, serial, 0); } ShellRequest::Resize { @@ -866,7 +872,7 @@ pub fn init_shell(display: Rc>, log: ::sl last_window_size: initial_window_size, }; - pointer.set_grab(grab, serial); + pointer.set_grab(grab, serial, 0); } _ => (), } diff --git a/src/wayland/data_device/dnd_grab.rs b/src/wayland/data_device/dnd_grab.rs index c15daff..bc1d363 100644 --- a/src/wayland/data_device/dnd_grab.rs +++ b/src/wayland/data_device/dnd_grab.rs @@ -53,12 +53,15 @@ impl DnDGrab { impl PointerGrab for DnDGrab { fn motion( &mut self, - _handle: &mut PointerInnerHandle<'_>, + handle: &mut PointerInnerHandle<'_>, location: Point, focus: Option<(wl_surface::WlSurface, Point)>, serial: Serial, time: u32, ) { + // While the grab is active, no client has pointer focus + handle.motion(location, None, serial, time); + let seat_data = self .seat .user_data() diff --git a/src/wayland/data_device/mod.rs b/src/wayland/data_device/mod.rs index d74d504..bdb9d6e 100644 --- a/src/wayland/data_device/mod.rs +++ b/src/wayland/data_device/mod.rs @@ -356,6 +356,7 @@ pub fn start_dnd( Rc::new(RefCell::new(callback)), ), serial, + 0, ); } } @@ -457,6 +458,7 @@ where callback.clone(), ), serial, + 0, ); return; } diff --git a/src/wayland/seat/pointer.rs b/src/wayland/seat/pointer.rs index 8d604b5..2eb18a3 100644 --- a/src/wayland/seat/pointer.rs +++ b/src/wayland/seat/pointer.rs @@ -91,6 +91,76 @@ impl PointerInternal { } } + fn set_grab(&mut self, serial: Serial, grab: G, time: u32) { + self.grab = GrabStatus::Active(serial, Box::new(grab)); + // generate a move to let the grab change the focus or move the pointer as result of its initialization + let location = self.location; + let focus = self.focus.clone(); + self.motion(location, focus, serial, time); + } + + fn unset_grab(&mut self, serial: Serial, time: u32) { + self.grab = GrabStatus::None; + // restore the focus + let location = self.location; + let focus = self.pending_focus.clone(); + self.motion(location, focus, serial, time); + } + + fn motion( + &mut self, + location: Point, + focus: Option<(WlSurface, Point)>, + serial: Serial, + time: u32, + ) { + // do we leave a surface ? + let mut leave = true; + self.location = location; + if let Some((ref current_focus, _)) = self.focus { + if let Some((ref surface, _)) = focus { + if current_focus.as_ref().equals(surface.as_ref()) { + leave = false; + } + } + } + if leave { + self.with_focused_pointers(|pointer, surface| { + pointer.leave(serial.into(), surface); + if pointer.as_ref().version() >= 5 { + pointer.frame(); + } + }); + self.focus = None; + (self.image_callback)(CursorImageStatus::Default); + } + + // do we enter one ? + if let Some((surface, surface_location)) = focus { + let entered = self.focus.is_none(); + // in all cases, update the focus, the coordinates of the surface + // might have changed + self.focus = Some((surface, surface_location)); + let (x, y) = (location - surface_location.to_f64()).into(); + if entered { + self.with_focused_pointers(|pointer, surface| { + pointer.enter(serial.into(), surface, x, y); + if pointer.as_ref().version() >= 5 { + pointer.frame(); + } + }) + } else { + // we were on top of a surface and remained on it + self.with_focused_pointers(|pointer, _| { + pointer.motion(time, x, y); + if pointer.as_ref().version() >= 5 { + pointer.frame(); + } + }) + } + } + } + fn with_focused_pointers(&self, mut f: F) where F: FnMut(&WlPointer, &WlSurface), @@ -160,13 +230,13 @@ impl PointerHandle { /// Change the current grab on this pointer to the provided grab /// /// Overwrites any current grab. - pub fn set_grab(&self, grab: G, serial: Serial) { - self.inner.borrow_mut().grab = GrabStatus::Active(serial, Box::new(grab)); + pub fn set_grab(&self, grab: G, serial: Serial, time: u32) { + self.inner.borrow_mut().set_grab(serial, grab, time); } /// Remove any current grab on this pointer, resetting it to the default behavior - pub fn unset_grab(&self) { - self.inner.borrow_mut().grab = GrabStatus::None; + pub fn unset_grab(&self, serial: Serial, time: u32) { + self.inner.borrow_mut().unset_grab(serial, time); } /// Check if this pointer is currently grabbed with this serial @@ -283,6 +353,13 @@ pub struct GrabStartData { /// rather than trying to guess when the grab will end. pub trait PointerGrab { /// A motion was reported + /// + /// This method allows you attach additional behavior to a motion event, possibly altering it. + /// You generally will want to invoke `PointerInnerHandle::motion()` as part of your processing. If you + /// don't, the rest of the compositor will behave as if the motion event never occurred. + /// + /// Some grabs (such as drag'n'drop, shell resize and motion) unset the focus while they are active, + /// this is achieved by just setting the focus to `None` when invoking `PointerInnerHandle::motion()`. fn motion( &mut self, handle: &mut PointerInnerHandle<'_>, @@ -292,6 +369,10 @@ pub trait PointerGrab { time: u32, ); /// A button press was reported + /// + /// This method allows you attach additional behavior to a button event, possibly altering it. + /// You generally will want to invoke `PointerInnerHandle::button()` as part of your processing. If you + /// don't, the rest of the compositor will behave as if the button event never occurred. fn button( &mut self, handle: &mut PointerInnerHandle<'_>, @@ -301,6 +382,10 @@ pub trait PointerGrab { time: u32, ); /// An axis scroll was reported + /// + /// This method allows you attach additional behavior to an axis event, possibly altering it. + /// You generally will want to invoke `PointerInnerHandle::axis()` as part of your processing. If you + /// don't, the rest of the compositor will behave as if the axis event never occurred. fn axis(&mut self, handle: &mut PointerInnerHandle<'_>, details: AxisFrame); /// The data about the event that started the grab. fn start_data(&self) -> &GrabStartData; @@ -317,19 +402,15 @@ impl<'a> PointerInnerHandle<'a> { /// Change the current grab on this pointer to the provided grab /// /// Overwrites any current grab. - pub fn set_grab(&mut self, serial: Serial, grab: G) { - self.inner.grab = GrabStatus::Active(serial, Box::new(grab)); + pub fn set_grab(&mut self, serial: Serial, grab: G, time: u32) { + self.inner.set_grab(serial, grab, time); } /// Remove any current grab on this pointer, resetting it to the default behavior /// /// This will also restore the focus of the underlying pointer pub fn unset_grab(&mut self, serial: Serial, time: u32) { - self.inner.grab = GrabStatus::None; - // restore the focus - let location = self.current_location(); - let focus = self.inner.pending_focus.clone(); - self.motion(location, focus, serial, time); + self.inner.unset_grab(serial, time); } /// Access the current focus of this pointer @@ -368,51 +449,7 @@ impl<'a> PointerInnerHandle<'a> { serial: Serial, time: u32, ) { - // do we leave a surface ? - let mut leave = true; - self.inner.location = location; - if let Some((ref current_focus, _)) = self.inner.focus { - if let Some((ref surface, _)) = focus { - if current_focus.as_ref().equals(surface.as_ref()) { - leave = false; - } - } - } - if leave { - self.inner.with_focused_pointers(|pointer, surface| { - pointer.leave(serial.into(), surface); - if pointer.as_ref().version() >= 5 { - pointer.frame(); - } - }); - self.inner.focus = None; - (self.inner.image_callback)(CursorImageStatus::Default); - } - - // do we enter one ? - if let Some((surface, surface_location)) = focus { - let entered = self.inner.focus.is_none(); - // in all cases, update the focus, the coordinates of the surface - // might have changed - self.inner.focus = Some((surface, surface_location)); - let (x, y) = (location - surface_location.to_f64()).into(); - if entered { - self.inner.with_focused_pointers(|pointer, surface| { - pointer.enter(serial.into(), surface, x, y); - if pointer.as_ref().version() >= 5 { - pointer.frame(); - } - }) - } else { - // we were on top of a surface and remained on it - self.inner.with_focused_pointers(|pointer, _| { - pointer.motion(time, x, y); - if pointer.as_ref().version() >= 5 { - pointer.frame(); - } - }) - } - } + self.inner.motion(location, focus, serial, time); } /// Notify that a button was pressed @@ -687,6 +724,7 @@ impl PointerGrab for DefaultGrab { location: handle.current_location(), }, }, + time, ); } }