<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> 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.

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

wayland.seat: Fix pointer grab API