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.
This commit is contained in:
Victor Berger 2022-02-01 19:23:01 +01:00
parent d04a999bf8
commit d797cdcdcb
4 changed files with 113 additions and 64 deletions

View File

@ -48,12 +48,15 @@ struct MoveSurfaceGrab {
impl PointerGrab for MoveSurfaceGrab { impl PointerGrab for MoveSurfaceGrab {
fn motion( fn motion(
&mut self, &mut self,
_handle: &mut PointerInnerHandle<'_>, handle: &mut PointerInnerHandle<'_>,
location: Point<f64, Logical>, location: Point<f64, Logical>,
_focus: Option<(wl_surface::WlSurface, Point<i32, Logical>)>, _focus: Option<(wl_surface::WlSurface, Point<i32, Logical>)>,
_serial: Serial, serial: Serial,
_time: u32, 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 delta = location - self.start_data.location;
let new_location = self.initial_window_location.to_f64() + delta; let new_location = self.initial_window_location.to_f64() + delta;
@ -152,6 +155,9 @@ impl PointerGrab for ResizeSurfaceGrab {
return; 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 dx, mut dy) = (location - self.start_data.location).into();
let mut new_window_width = self.initial_window_size.w; let mut new_window_width = self.initial_window_size.w;
@ -479,7 +485,7 @@ pub fn init_shell<BackendData: 'static>(display: Rc<RefCell<Display>>, log: ::sl
initial_window_location, initial_window_location,
}; };
pointer.set_grab(grab, serial); pointer.set_grab(grab, serial, 0);
} }
XdgRequest::Resize { XdgRequest::Resize {
@ -539,7 +545,7 @@ pub fn init_shell<BackendData: 'static>(display: Rc<RefCell<Display>>, log: ::sl
last_window_size: initial_window_size, last_window_size: initial_window_size,
}; };
pointer.set_grab(grab, serial); pointer.set_grab(grab, serial, 0);
} }
XdgRequest::AckConfigure { XdgRequest::AckConfigure {
@ -806,7 +812,7 @@ pub fn init_shell<BackendData: 'static>(display: Rc<RefCell<Display>>, log: ::sl
initial_window_location, initial_window_location,
}; };
pointer.set_grab(grab, serial); pointer.set_grab(grab, serial, 0);
} }
ShellRequest::Resize { ShellRequest::Resize {
@ -866,7 +872,7 @@ pub fn init_shell<BackendData: 'static>(display: Rc<RefCell<Display>>, log: ::sl
last_window_size: initial_window_size, last_window_size: initial_window_size,
}; };
pointer.set_grab(grab, serial); pointer.set_grab(grab, serial, 0);
} }
_ => (), _ => (),
} }

View File

@ -53,12 +53,15 @@ impl DnDGrab {
impl PointerGrab for DnDGrab { impl PointerGrab for DnDGrab {
fn motion( fn motion(
&mut self, &mut self,
_handle: &mut PointerInnerHandle<'_>, handle: &mut PointerInnerHandle<'_>,
location: Point<f64, Logical>, location: Point<f64, Logical>,
focus: Option<(wl_surface::WlSurface, Point<i32, Logical>)>, focus: Option<(wl_surface::WlSurface, Point<i32, Logical>)>,
serial: Serial, serial: Serial,
time: u32, time: u32,
) { ) {
// While the grab is active, no client has pointer focus
handle.motion(location, None, serial, time);
let seat_data = self let seat_data = self
.seat .seat
.user_data() .user_data()

View File

@ -356,6 +356,7 @@ pub fn start_dnd<C>(
Rc::new(RefCell::new(callback)), Rc::new(RefCell::new(callback)),
), ),
serial, serial,
0,
); );
} }
} }
@ -457,6 +458,7 @@ where
callback.clone(), callback.clone(),
), ),
serial, serial,
0,
); );
return; return;
} }

View File

@ -91,6 +91,76 @@ impl PointerInternal {
} }
} }
fn set_grab<G: PointerGrab + 'static>(&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<f64, Logical>,
focus: Option<(WlSurface, Point<i32, Logical>)>,
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<F>(&self, mut f: F) fn with_focused_pointers<F>(&self, mut f: F)
where where
F: FnMut(&WlPointer, &WlSurface), F: FnMut(&WlPointer, &WlSurface),
@ -160,13 +230,13 @@ impl PointerHandle {
/// Change the current grab on this pointer to the provided grab /// Change the current grab on this pointer to the provided grab
/// ///
/// Overwrites any current grab. /// Overwrites any current grab.
pub fn set_grab<G: PointerGrab + 'static>(&self, grab: G, serial: Serial) { pub fn set_grab<G: PointerGrab + 'static>(&self, grab: G, serial: Serial, time: u32) {
self.inner.borrow_mut().grab = GrabStatus::Active(serial, Box::new(grab)); self.inner.borrow_mut().set_grab(serial, grab, time);
} }
/// Remove any current grab on this pointer, resetting it to the default behavior /// Remove any current grab on this pointer, resetting it to the default behavior
pub fn unset_grab(&self) { pub fn unset_grab(&self, serial: Serial, time: u32) {
self.inner.borrow_mut().grab = GrabStatus::None; self.inner.borrow_mut().unset_grab(serial, time);
} }
/// Check if this pointer is currently grabbed with this serial /// 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. /// rather than trying to guess when the grab will end.
pub trait PointerGrab { pub trait PointerGrab {
/// A motion was reported /// 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( fn motion(
&mut self, &mut self,
handle: &mut PointerInnerHandle<'_>, handle: &mut PointerInnerHandle<'_>,
@ -292,6 +369,10 @@ pub trait PointerGrab {
time: u32, time: u32,
); );
/// A button press was reported /// 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( fn button(
&mut self, &mut self,
handle: &mut PointerInnerHandle<'_>, handle: &mut PointerInnerHandle<'_>,
@ -301,6 +382,10 @@ pub trait PointerGrab {
time: u32, time: u32,
); );
/// An axis scroll was reported /// 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); fn axis(&mut self, handle: &mut PointerInnerHandle<'_>, details: AxisFrame);
/// The data about the event that started the grab. /// The data about the event that started the grab.
fn start_data(&self) -> &GrabStartData; fn start_data(&self) -> &GrabStartData;
@ -317,19 +402,15 @@ impl<'a> PointerInnerHandle<'a> {
/// Change the current grab on this pointer to the provided grab /// Change the current grab on this pointer to the provided grab
/// ///
/// Overwrites any current grab. /// Overwrites any current grab.
pub fn set_grab<G: PointerGrab + 'static>(&mut self, serial: Serial, grab: G) { pub fn set_grab<G: PointerGrab + 'static>(&mut self, serial: Serial, grab: G, time: u32) {
self.inner.grab = GrabStatus::Active(serial, Box::new(grab)); self.inner.set_grab(serial, grab, time);
} }
/// Remove any current grab on this pointer, resetting it to the default behavior /// Remove any current grab on this pointer, resetting it to the default behavior
/// ///
/// This will also restore the focus of the underlying pointer /// This will also restore the focus of the underlying pointer
pub fn unset_grab(&mut self, serial: Serial, time: u32) { pub fn unset_grab(&mut self, serial: Serial, time: u32) {
self.inner.grab = GrabStatus::None; self.inner.unset_grab(serial, time);
// restore the focus
let location = self.current_location();
let focus = self.inner.pending_focus.clone();
self.motion(location, focus, serial, time);
} }
/// Access the current focus of this pointer /// Access the current focus of this pointer
@ -368,51 +449,7 @@ impl<'a> PointerInnerHandle<'a> {
serial: Serial, serial: Serial,
time: u32, time: u32,
) { ) {
// do we leave a surface ? self.inner.motion(location, focus, serial, time);
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();
}
})
}
}
} }
/// Notify that a button was pressed /// Notify that a button was pressed
@ -687,6 +724,7 @@ impl PointerGrab for DefaultGrab {
location: handle.current_location(), location: handle.current_location(),
}, },
}, },
time,
); );
} }
} }