<NME> pointer.rs
<BEF> use std::{fmt, sync::Mutex};

use wayland_server::{
    backend::{ClientId, ObjectId},
    protocol::{
        wl_pointer::{
            self, Axis as WlAxis, AxisSource as WlAxisSource, ButtonState as WlButtonState, Request,
            WlPointer,
        },
        wl_surface::WlSurface,
    },
    Dispatch, DisplayHandle, Resource,
};

use crate::{
    backend::input::{Axis, AxisSource, ButtonState},
    input::{
        pointer::{
            AxisFrame, ButtonEvent, CursorImageAttributes, CursorImageStatus, MotionEvent, PointerHandle,
            PointerInternal, PointerTarget,
        },
        Seat,
    },
    utils::Serial,
    wayland::compositor,
};

use super::{SeatHandler, SeatState, WaylandFocus};

impl<D: SeatHandler> PointerHandle<D> {
    pub(crate) fn new_pointer(&self, pointer: WlPointer) {
        let mut guard = self.known_pointers.lock().unwrap();
        guard.push(pointer);
    }
}

/// WlSurface role of a cursor image icon
pub const CURSOR_IMAGE_ROLE: &str = "cursor_image";

fn for_each_focused_pointers<D: SeatHandler + 'static>(
    seat: &Seat<D>,
    surface: &WlSurface,
    mut f: impl FnMut(WlPointer),
) {
    if let Some(pointer) = seat.get_pointer() {
        let inner = pointer.known_pointers.lock().unwrap();
        for ptr in &*inner {
            if ptr.id().same_client_as(&surface.id()) {
                f(ptr.clone())
            }
        }
    }
}

#[cfg(feature = "wayland_frontend")]
impl<D> PointerTarget<D> for WlSurface
where
    D: SeatHandler + 'static,
{
    fn enter(&self, seat: &Seat<D>, _data: &mut D, event: &MotionEvent) {
        for_each_focused_pointers(seat, self, |ptr| {
            ptr.enter(event.serial.into(), self, event.location.x, event.location.y);
            if ptr.version() >= 5 {
                ptr.frame();
            }
        })
    }
    fn leave(&self, seat: &Seat<D>, _data: &mut D, serial: Serial, _time: u32) {
        for_each_focused_pointers(seat, self, |ptr| {
            ptr.leave(serial.into(), self);
            if ptr.version() >= 5 {
                ptr.frame();
            }
        })
    }
    fn motion(&self, seat: &Seat<D>, _data: &mut D, event: &MotionEvent) {
        for_each_focused_pointers(seat, self, |ptr| {
            ptr.motion(event.time, event.location.x, event.location.y);
            if ptr.version() >= 5 {
                ptr.frame();
            }
        })
    }
    fn button(&self, seat: &Seat<D>, _data: &mut D, event: &ButtonEvent) {
        for_each_focused_pointers(seat, self, |ptr| {
            ptr.button(event.serial.into(), event.time, event.button, event.state.into());
            if ptr.version() >= 5 {
                ptr.frame();
            }
        })
        }
    }

    fn with_focused_pointers<F>(&self, mut f: F)
    where
        F: FnMut(&WlPointer, &WlSurface),
        _dh: &DisplayHandle,
        _data_init: &mut wayland_server::DataInit<'_, D>,
    ) {
        match request {
            Request::SetCursor {
                surface,
                hotspot_x,
                hotspot_y,
                ..
            } => {
                if let Some(ref handle) = data.handle {
                    let seat = {
                        let seat_state = state.seat_state();
                        seat_state
                            .seats
                            .iter()
                            .find(|seat| seat.get_pointer().map(|h| &h == handle).unwrap_or(false))
                            .cloned()
                    };

                    let guard = handle.inner.lock().unwrap();
                    // only allow setting the cursor icon if the current pointer focus
                    // is of the same client
                    let PointerInternal { ref focus, .. } = *guard;
                    if let Some((ref focus, _)) = *focus {
                        if focus.same_client_as(&pointer.id()) {
                            match surface {
                                Some(surface) => {
                                    // tolerate re-using the same surface
                                    if compositor::give_role(&surface, CURSOR_IMAGE_ROLE).is_err()
                                        && compositor::get_role(&surface) != Some(CURSOR_IMAGE_ROLE)
                                    {
                                        pointer.post_error(
                                            wl_pointer::Error::Role,
                                            "Given wl_surface has another role.",
                                        );
                                        return;
                                    }
                                    compositor::with_states(&surface, |states| {
                                        states.data_map.insert_if_missing_threadsafe(|| {
                                            Mutex::new(CursorImageAttributes {
                                                hotspot: (0, 0).into(),
                                            })
                                        });
                                        states
                                            .data_map
                                            .get::<Mutex<CursorImageAttributes>>()
                                            .unwrap()
                                            .lock()
                                            .unwrap()
                                            .hotspot = (hotspot_x, hotspot_y).into();
                                    });

                                    if let Some(seat) = seat {
                                        state.cursor_image(&seat, CursorImageStatus::Surface(surface));
                                    }
                                }
                                None => {
                                    if let Some(seat) = seat {
                                        state.cursor_image(&seat, CursorImageStatus::Hidden);
                                    }
                                }
                            }
    /// Change the current grab on this pointer to the provided grab
    ///
    /// Overwrites any current grab.
    pub fn set_grab<G: PointerGrab + 'static>(&self, grab: G, serial: Serial) {
        self.inner.borrow_mut().grab = GrabStatus::Active(serial, Box::new(grab));
    }

    /// 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;
    }

    /// Check if this pointer is currently grabbed with this serial
            handle
                .known_pointers
                .lock()
                .unwrap()
                .retain(|p| p.id() != object_id);
        }
    }
}

impl From<Axis> for WlAxis {
    fn from(axis: Axis) -> WlAxis {
        match axis {
            Axis::Horizontal => WlAxis::HorizontalScroll,
            Axis::Vertical => WlAxis::VerticalScroll,
        }
    }
}

#[derive(Debug, thiserror::Error)]
#[error("Unknown Axis {0:?}")]
pub struct UnknownAxis(WlAxis);

impl TryFrom<WlAxis> for Axis {
    type Error = UnknownAxis;
    fn try_from(value: WlAxis) -> Result<Self, Self::Error> {
        match value {
            WlAxis::HorizontalScroll => Ok(Axis::Horizontal),
            WlAxis::VerticalScroll => Ok(Axis::Vertical),
            x => Err(UnknownAxis(x)),
        }
    }
}

impl From<AxisSource> for WlAxisSource {
    fn from(axis: AxisSource) -> WlAxisSource {
        match axis {
            AxisSource::Wheel => WlAxisSource::Wheel,
            AxisSource::Finger => WlAxisSource::Finger,
            AxisSource::Continuous => WlAxisSource::Continuous,
            AxisSource::WheelTilt => WlAxisSource::WheelTilt,
        }
    }
}

impl From<ButtonState> for WlButtonState {
    fn from(state: ButtonState) -> WlButtonState {
        match state {
            ButtonState::Pressed => WlButtonState::Pressed,
            ButtonState::Released => WlButtonState::Released,
        }
    }
}

#[derive(Debug, thiserror::Error)]
#[error("Unknown ButtonState {0:?}")]
pub struct UnknownButtonState(WlButtonState);

impl TryFrom<WlButtonState> for ButtonState {
    type Error = UnknownButtonState;
    fn try_from(value: WlButtonState) -> Result<Self, Self::Error> {
        match value {
            WlButtonState::Pressed => Ok(ButtonState::Pressed),
            WlButtonState::Released => Ok(ButtonState::Released),
            x => Err(UnknownButtonState(x)),
        }
    }
}
/// rather than trying to guess when the grab will end.
pub trait PointerGrab {
    /// A motion was reported
    fn motion(
        &mut self,
        handle: &mut PointerInnerHandle<'_>,
        time: u32,
    );
    /// A button press was reported
    fn button(
        &mut self,
        handle: &mut PointerInnerHandle<'_>,
        time: u32,
    );
    /// An axis scroll was reported
    fn axis(&mut self, handle: &mut PointerInnerHandle<'_>, details: AxisFrame);
    /// The data about the event that started the grab.
    fn start_data(&self) -> &GrabStartData;
    /// Change the current grab on this pointer to the provided grab
    ///
    /// Overwrites any current grab.
    pub fn set_grab<G: PointerGrab + 'static>(&mut self, serial: Serial, grab: G) {
        self.inner.grab = GrabStatus::Active(serial, Box::new(grab));
    }

    /// 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);
    }

    /// Access the current focus of this pointer
        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();
                    }
                })
            }
        }
    }

    /// Notify that a button was pressed
                        location: handle.current_location(),
                    },
                },
            );
        }
    }

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

wayland.seat: Fix pointer grab API
<DFF> @@ -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)
     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<G: PointerGrab + 'static>(&self, grab: G, serial: Serial) {
-        self.inner.borrow_mut().grab = GrabStatus::Active(serial, Box::new(grab));
+    pub fn set_grab<G: PointerGrab + 'static>(&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<G: PointerGrab + 'static>(&mut self, serial: Serial, grab: G) {
-        self.inner.grab = GrabStatus::Active(serial, Box::new(grab));
+    pub fn set_grab<G: PointerGrab + 'static>(&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,
             );
         }
     }

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