Merge pull request #490 from Smithay/vberger/fix-grabs

wayland.seat: Fix pointer grab API
This commit is contained in:
Victoria Brekenfeld 2022-02-02 16:44:58 +01:00 committed by GitHub
commit 134f726dfb
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
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

@ -361,6 +361,7 @@ pub fn start_dnd<C>(
Rc::new(RefCell::new(callback)), Rc::new(RefCell::new(callback)),
), ),
serial, serial,
0,
); );
} }
} }
@ -463,6 +464,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,
); );
} }
} }