🔒 Repository is read-only – file editing is disabled.

PaganDE/smithay-0.7.0/src/input/touch/mod.rs main

693 linii Raw ← Powrót
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693
//! Touch-related types for smithay's input abstraction

use std::collections::HashMap;
use std::fmt;
use std::sync::{Arc, Mutex};

use tracing::{info_span, instrument};
#[cfg(feature = "wayland_frontend")]
use wayland_server::Weak;

use crate::backend::input::TouchSlot;
use crate::utils::{IsAlive, Logical, Point, Serial, SerialCounter};

pub use grab::{DefaultGrab, GrabStartData, TouchDownGrab, TouchGrab};

use super::{GrabStatus, Seat, SeatHandler};

mod grab;

/// An handle to a touch handler
///
/// It can be cloned and all clones manipulate the same internal state.
///
/// This handle gives you access to an interface to send touch events to your
/// clients.
///
/// When sending events using this handle, they will be intercepted by a touch
/// grab if any is active. See the [`TouchGrab`] trait for details.
pub struct TouchHandle<D: SeatHandler> {
    pub(crate) inner: Arc<Mutex<TouchInternal<D>>>,
    #[cfg(feature = "wayland_frontend")]
    pub(crate) known_instances:
        Arc<Mutex<Vec<(Weak<wayland_server::protocol::wl_touch::WlTouch>, Option<Serial>)>>>,
    pub(crate) span: tracing::Span,
}

#[cfg(not(feature = "wayland_frontend"))]
impl<D: SeatHandler> fmt::Debug for TouchHandle<D> {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        f.debug_struct("TouchHandle").field("inner", &self.inner).finish()
    }
}

#[cfg(feature = "wayland_frontend")]
impl<D: SeatHandler> fmt::Debug for TouchHandle<D> {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        f.debug_struct("TouchHandle")
            .field("inner", &self.inner)
            .field("known_instances", &self.known_instances)
            .finish()
    }
}

impl<D: SeatHandler> Clone for TouchHandle<D> {
    #[inline]
    fn clone(&self) -> Self {
        Self {
            inner: self.inner.clone(),
            #[cfg(feature = "wayland_frontend")]
            known_instances: self.known_instances.clone(),
            span: self.span.clone(),
        }
    }
}

impl<D: SeatHandler> std::hash::Hash for TouchHandle<D> {
    #[inline]
    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
        Arc::as_ptr(&self.inner).hash(state)
    }
}

impl<D: SeatHandler> std::cmp::PartialEq for TouchHandle<D> {
    #[inline]
    fn eq(&self, other: &Self) -> bool {
        Arc::ptr_eq(&self.inner, &other.inner)
    }
}

impl<D: SeatHandler> std::cmp::Eq for TouchHandle<D> {}

pub(crate) struct TouchInternal<D: SeatHandler> {
    focus: HashMap<TouchSlot, TouchSlotState<D>>,
    seq_counter: SerialCounter,
    default_grab: Box<dyn Fn() -> Box<dyn TouchGrab<D>> + Send + 'static>,
    grab: GrabStatus<dyn TouchGrab<D>>,
}

struct TouchSlotState<D: SeatHandler> {
    focus: Option<(<D as SeatHandler>::TouchFocus, Point<f64, Logical>)>,
    frame_pending: Option<<D as SeatHandler>::TouchFocus>,
    pending: Serial,
    current: Option<Serial>,
}

impl<D: SeatHandler> fmt::Debug for TouchSlotState<D> {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        f.debug_struct("TouchSlotState")
            .field("focus", &self.focus)
            .field("frame_pending", &self.frame_pending)
            .field("pending", &self.pending)
            .field("current", &self.current)
            .finish()
    }
}

// image_callback does not implement debug, so we have to impl Debug manually
impl<D: SeatHandler> fmt::Debug for TouchInternal<D> {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        f.debug_struct("TouchInternal")
            .field("focus", &self.focus)
            .field("grab", &self.grab)
            .finish()
    }
}

/// Pointer motion event
#[derive(Debug, Clone)]
pub struct DownEvent {
    /// Slot of this event
    pub slot: TouchSlot,
    /// Location of the touch in compositor space
    pub location: Point<f64, Logical>,
    /// Serial of the event
    pub serial: Serial,
    /// Timestamp of the event, with millisecond granularity
    pub time: u32,
}

/// Pointer motion event
#[derive(Debug, Clone)]
pub struct UpEvent {
    /// Slot of this event
    pub slot: TouchSlot,
    /// Serial of the event
    pub serial: Serial,
    /// Timestamp of the event, with millisecond granularity
    pub time: u32,
}

/// Pointer motion event
#[derive(Debug, Clone)]
pub struct MotionEvent {
    /// Slot of this event
    pub slot: TouchSlot,
    /// Location of the touch in compositor space
    pub location: Point<f64, Logical>,
    /// Timestamp of the event, with millisecond granularity
    pub time: u32,
}

/// Pointer motion event
#[derive(Debug, Clone, Copy)]
pub struct ShapeEvent {
    /// Slot of this event
    pub slot: TouchSlot,
    /// Length of the major axis in surface-local coordinates
    pub major: f64,
    /// Length of the minor axis in surface-local coordinates
    pub minor: f64,
}

/// Pointer motion event
#[derive(Debug, Clone, Copy)]
pub struct OrientationEvent {
    /// Slot of this event
    pub slot: TouchSlot,
    /// Angle between major axis and positive surface y-axis in degrees
    pub orientation: f64,
}

/// Trait representing object that can receive touch interactions
pub trait TouchTarget<D>: IsAlive + PartialEq + Clone + fmt::Debug + Send
where
    D: SeatHandler,
{
    /// A new touch point has appeared on the target.
    ///
    /// This touch point is assigned a unique ID. Future events from this touch point reference this ID.
    /// The ID ceases to be valid after a touch up event and may be reused in the future.
    fn down(&self, seat: &Seat<D>, data: &mut D, event: &DownEvent, seq: Serial);

    /// The touch point has disappeared.
    ///
    /// No further events will be sent for this touch point and the touch point's ID
    /// is released and may be reused in a future touch down event.
    fn up(&self, seat: &Seat<D>, data: &mut D, event: &UpEvent, seq: Serial);

    /// A touch point has changed coordinates.
    fn motion(&self, seat: &Seat<D>, data: &mut D, event: &MotionEvent, seq: Serial);

    /// Indicates the end of a set of events that logically belong together.
    fn frame(&self, seat: &Seat<D>, data: &mut D, seq: Serial);

    /// Touch session cancelled.
    ///
    /// Touch cancellation applies to all touch points currently active on this target.
    /// The client is responsible for finalizing the touch points, future touch points on
    /// this target may reuse the touch point ID.
    fn cancel(&self, seat: &Seat<D>, data: &mut D, seq: Serial);

    /// Sent when a touch point has changed its shape.
    ///
    /// A touch point shape is approximated by an ellipse through the major and minor axis length.
    /// The major axis length describes the longer diameter of the ellipse, while the minor axis
    /// length describes the shorter diameter. Major and minor are orthogonal and both are specified
    /// in surface-local coordinates. The center of the ellipse is always at the touch point location
    /// as reported by [`TouchTarget::down`] or [`TouchTarget::motion`].
    fn shape(&self, seat: &Seat<D>, data: &mut D, event: &ShapeEvent, seq: Serial);

    /// Sent when a touch point has changed its orientation.
    ///
    /// The orientation describes the clockwise angle of a touch point's major axis to the positive surface
    /// y-axis and is normalized to the -180 to +180 degree range. The granularity of orientation depends
    /// on the touch device, some devices only support binary rotation values between 0 and 90 degrees.
    fn orientation(&self, seat: &Seat<D>, data: &mut D, event: &OrientationEvent, seq: Serial);
}

impl<D: SeatHandler + 'static> TouchHandle<D> {
    pub(crate) fn new<F>(default_grab: F) -> TouchHandle<D>
    where
        F: Fn() -> Box<dyn TouchGrab<D>> + Send + 'static,
    {
        TouchHandle {
            inner: Arc::new(Mutex::new(TouchInternal::new(default_grab))),
            #[cfg(feature = "wayland_frontend")]
            known_instances: Arc::new(Mutex::new(Vec::new())),
            span: info_span!("input_touch"),
        }
    }

    /// Change the current grab on this touch to the provided grab
    ///
    /// Overwrites any current grab.
    #[instrument(level = "debug", parent = &self.span, skip(self, data, grab))]
    pub fn set_grab<G: TouchGrab<D> + 'static>(&self, data: &mut D, grab: G, serial: Serial) {
        let seat = self.get_seat(data);
        self.inner.lock().unwrap().set_grab(data, &seat, serial, grab);
    }

    /// Remove any current grab on this touch, resetting it to the default behavior
    #[instrument(level = "debug", parent = &self.span, skip(self, data))]
    pub fn unset_grab(&self, data: &mut D) {
        let seat = self.get_seat(data);
        self.inner.lock().unwrap().unset_grab(data, &seat);
    }

    /// Check if this touch is currently grabbed with this serial
    pub fn has_grab(&self, serial: Serial) -> bool {
        let guard = self.inner.lock().unwrap();
        match guard.grab {
            GrabStatus::Active(s, _) => s == serial,
            _ => false,
        }
    }

    /// Check if this touch is currently being grabbed
    pub fn is_grabbed(&self) -> bool {
        let guard = self.inner.lock().unwrap();
        !matches!(guard.grab, GrabStatus::None)
    }

    /// Returns the start data for the grab, if any.
    pub fn grab_start_data(&self) -> Option<GrabStartData<D>> {
        let guard = self.inner.lock().unwrap();
        match &guard.grab {
            GrabStatus::Active(_, g) => Some(g.start_data().clone()),
            _ => None,
        }
    }

    /// Calls `f` with the active grab, if any.
    pub fn with_grab<T>(&self, f: impl FnOnce(Serial, &dyn TouchGrab<D>) -> T) -> Option<T> {
        let guard = self.inner.lock().unwrap();
        if let GrabStatus::Active(s, g) = &guard.grab {
            Some(f(*s, &**g))
        } else {
            None
        }
    }

    /// Notify that a new touch point appeared
    ///
    /// You provide the location of the touch, in the form of:
    ///
    /// - The coordinates of the touch in the global compositor space
    /// - The surface on top of which the touch point is, and the coordinates of its
    ///   origin in the global compositor space (or `None` of the touch is not
    ///   on top of a client surface).
    pub fn down(
        &self,
        data: &mut D,
        focus: Option<(<D as SeatHandler>::TouchFocus, Point<f64, Logical>)>,
        event: &DownEvent,
    ) {
        let mut inner = self.inner.lock().unwrap();
        let seat = self.get_seat(data);
        let seq = inner.seq_counter.next_serial();
        inner.with_grab(data, &seat, |data, handle, grab| {
            grab.down(data, handle, focus, event, seq);
        });
    }

    /// Notify that a touch point disappeared
    pub fn up(&self, data: &mut D, event: &UpEvent) {
        let mut inner = self.inner.lock().unwrap();
        let seat = self.get_seat(data);
        let seq = inner.seq_counter.next_serial();
        inner.with_grab(data, &seat, |data, handle, grab| {
            grab.up(data, handle, event, seq);
        });
    }

    /// Notify that a touch point has changed coordinates.
    ///
    /// You provide the location of the touch, in the form of:
    ///
    /// - The coordinates of the touch in the global compositor space
    /// - The surface on top of which the touch point is, and the coordinates of its
    ///   origin in the global compositor space (or `None` of the touch is not
    ///   on top of a client surface).
    ///
    /// **Note** that this will **not** update the focus of the touch point, the focus
    /// is only set on [`TouchHandle::down`]. The focus provided to this function
    /// can be used to find DnD targets during touch motion.
    pub fn motion(
        &self,
        data: &mut D,
        focus: Option<(<D as SeatHandler>::TouchFocus, Point<f64, Logical>)>,
        event: &MotionEvent,
    ) {
        let mut inner = self.inner.lock().unwrap();
        let seat = self.get_seat(data);
        let seq = inner.seq_counter.next_serial();
        inner.with_grab(data, &seat, |data, handle, grab| {
            grab.motion(data, handle, focus, event, seq);
        });
    }

    /// Notify about the end of a set of events that logically belong together.
    ///
    /// This needs to be called after one or move calls to [`TouchHandle::down`] or [`TouchHandle::motion`]
    pub fn frame(&self, data: &mut D) {
        let mut inner = self.inner.lock().unwrap();
        let seat = self.get_seat(data);
        let seq = inner.seq_counter.next_serial();
        inner.with_grab(data, &seat, |data, handle, grab| {
            grab.frame(data, handle, seq);
        });
    }

    /// Notify that the touch session has been cancelled.
    ///
    /// Use in case you decide the touch stream is a global gesture.
    /// This will remove all current focus targets, and no further events will be sent
    /// until a new touch point appears.
    pub fn cancel(&self, data: &mut D) {
        let mut inner = self.inner.lock().unwrap();
        let seat = self.get_seat(data);
        let seq = inner.seq_counter.next_serial();
        inner.with_grab(data, &seat, |data, handle, grab| {
            grab.cancel(data, handle, seq);
        });
    }

    /// Notify that a touch point has changed its shape.
    pub fn shape(&self, data: &mut D, event: &ShapeEvent) {
        let mut inner = self.inner.lock().unwrap();
        let seat = self.get_seat(data);
        let seq = inner.seq_counter.next_serial();
        inner.with_grab(data, &seat, |data, handle, grab| {
            grab.shape(data, handle, event, seq);
        });
    }

    /// Notify that a touch point has changed its orientation.
    pub fn orientation(&self, data: &mut D, event: &OrientationEvent) {
        let mut inner = self.inner.lock().unwrap();
        let seat = self.get_seat(data);
        let seq = inner.seq_counter.next_serial();
        inner.with_grab(data, &seat, |data, handle, grab| {
            grab.orientation(data, handle, event, seq);
        });
    }

    fn get_seat(&self, data: &mut D) -> Seat<D> {
        let seat_state = data.seat_state();
        seat_state
            .seats
            .iter()
            .find(|seat| seat.get_touch().map(|h| &h == self).unwrap_or(false))
            .cloned()
            .unwrap()
    }
}

/// This inner handle is accessed from inside a pointer grab logic, and directly
/// sends event to the client
pub struct TouchInnerHandle<'a, D: SeatHandler> {
    inner: &'a mut TouchInternal<D>,
    seat: &'a Seat<D>,
}

impl<D: SeatHandler> fmt::Debug for TouchInnerHandle<'_, D> {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        f.debug_struct("TouchInnerHandle")
            .field("inner", &self.inner)
            .field("seat", &self.seat.arc.name)
            .finish()
    }
}

impl<D: SeatHandler + 'static> TouchInnerHandle<'_, D> {
    /// Change the current grab on this pointer to the provided grab
    ///
    /// Overwrites any current grab.
    pub fn set_grab<G: TouchGrab<D> + 'static>(
        &mut self,
        handler: &mut dyn TouchGrab<D>,
        data: &mut D,
        serial: Serial,
        grab: G,
    ) {
        handler.unset(data);
        self.inner.set_grab(data, self.seat, serial, grab);
    }

    /// Remove any current grab on this pointer, resetting it to the default behavior
    ///
    /// This will also restore the focus of the underlying pointer if restore_focus
    /// is [`true`]
    pub fn unset_grab(&mut self, handler: &mut dyn TouchGrab<D>, data: &mut D) {
        handler.unset(data);
        self.inner.unset_grab(data, self.seat);
    }

    /// Notify that a new touch point appeared
    ///
    /// You provide the location of the touch, in the form of:
    ///
    /// - The coordinates of the touch in the global compositor space
    /// - The surface on top of which the touch point is, and the coordinates of its
    ///   origin in the global compositor space (or `None` of the touch is not
    ///   on top of a client surface).
    pub fn down(
        &mut self,
        data: &mut D,
        focus: Option<(<D as SeatHandler>::TouchFocus, Point<f64, Logical>)>,
        event: &DownEvent,
        seq: Serial,
    ) {
        self.inner.down(data, self.seat, focus, event, seq)
    }

    /// Notify that a touch point disappeared
    pub fn up(&mut self, data: &mut D, event: &UpEvent, seq: Serial) {
        self.inner.up(data, self.seat, event, seq)
    }

    /// Notify that a touch point has changed coordinates.
    ///
    /// You provide the location of the touch, in the form of:
    ///
    /// - The coordinates of the touch in the global compositor space
    /// - The surface on top of which the touch point is, and the coordinates of its
    ///   origin in the global compositor space (or `None` of the touch is not
    ///   on top of a client surface).
    ///
    /// **Note** that this will **not** update the focus of the touch point, the focus
    /// is only set on [`TouchHandle::down`]. The focus provided to this function
    /// can be used to find DnD targets during touch motion.
    pub fn motion(
        &mut self,
        data: &mut D,
        focus: Option<(<D as SeatHandler>::TouchFocus, Point<f64, Logical>)>,
        event: &MotionEvent,
        seq: Serial,
    ) {
        self.inner.motion(data, self.seat, focus, event, seq)
    }

    /// Notify about the end of a set of events that logically belong together.
    ///
    /// This needs to be called after one or move calls to [`TouchHandle::down`] or [`TouchHandle::motion`]
    pub fn frame(&mut self, data: &mut D, seq: Serial) {
        self.inner.frame(data, self.seat, seq)
    }

    /// Notify that a touch point has changed its shape.
    pub fn shape(&mut self, data: &mut D, event: &ShapeEvent, seq: Serial) {
        self.inner.shape(data, self.seat, event, seq)
    }

    /// Notify that a touch point has changed its orientation.
    pub fn orientation(&mut self, data: &mut D, event: &OrientationEvent, seq: Serial) {
        self.inner.orientation(data, self.seat, event, seq)
    }

    /// Notify that the touch session has been cancelled.
    ///
    /// Use in case you decide the touch stream is a global gesture.
    /// This will remove all current focus targets, and no further events will be sent
    /// until a new touch point appears.
    pub fn cancel(&mut self, data: &mut D, seq: Serial) {
        self.inner.cancel(data, self.seat, seq)
    }
}

impl<D: SeatHandler + 'static> TouchInternal<D> {
    fn new<F>(default_grab: F) -> Self
    where
        F: Fn() -> Box<dyn TouchGrab<D>> + Send + 'static,
    {
        Self {
            focus: Default::default(),
            seq_counter: SerialCounter::new(),
            default_grab: Box::new(default_grab),
            grab: GrabStatus::None,
        }
    }

    fn set_grab<G: TouchGrab<D> + 'static>(
        &mut self,
        data: &mut D,
        _seat: &Seat<D>,
        serial: Serial,
        grab: G,
    ) {
        if let GrabStatus::Active(_, handler) = &mut self.grab {
            handler.unset(data);
        }
        self.grab = GrabStatus::Active(serial, Box::new(grab));
    }

    fn unset_grab(&mut self, data: &mut D, _seat: &Seat<D>) {
        if let GrabStatus::Active(_, handler) = &mut self.grab {
            handler.unset(data);
        }
        self.grab = GrabStatus::None;
    }

    fn down(
        &mut self,
        data: &mut D,
        seat: &Seat<D>,
        focus: Option<(<D as SeatHandler>::TouchFocus, Point<f64, Logical>)>,
        event: &DownEvent,
        seq: Serial,
    ) {
        self.focus
            .entry(event.slot)
            .and_modify(|state| {
                state.pending = seq;
                state.frame_pending = None;
                state.focus.clone_from(&focus);
            })
            .or_insert_with(|| TouchSlotState {
                focus,
                frame_pending: None,
                pending: seq,
                current: None,
            });
        let state = self.focus.get(&event.slot).unwrap();
        if let Some((focus, loc)) = state.focus.as_ref() {
            let mut new_event = event.clone();
            new_event.location -= *loc;
            focus.down(seat, data, &new_event, seq);
        }
    }

    fn up(&mut self, data: &mut D, seat: &Seat<D>, event: &UpEvent, seq: Serial) {
        let Some(state) = self.focus.get_mut(&event.slot) else {
            return;
        };
        state.pending = seq;
        if let Some((focus, _)) = state.focus.take() {
            focus.up(seat, data, event, seq);

            // Keep the focus around to be able to send a frame event after up, but move
            // it out of the current focus to prevent sending other events.
            state.frame_pending = Some(focus);
        }
    }

    fn motion(
        &mut self,
        data: &mut D,
        seat: &Seat<D>,
        _focus: Option<(<D as SeatHandler>::TouchFocus, Point<f64, Logical>)>,
        event: &MotionEvent,
        seq: Serial,
    ) {
        let Some(state) = self.focus.get_mut(&event.slot) else {
            return;
        };
        state.pending = seq;
        if let Some((focus, loc)) = state.focus.as_ref() {
            let mut new_event = event.clone();
            new_event.location -= *loc;
            focus.motion(seat, data, &new_event, seq);
        }
    }

    fn frame(&mut self, data: &mut D, seat: &Seat<D>, seq: Serial) {
        for state in self.focus.values_mut() {
            if state.current.map(|c| c >= state.pending).unwrap_or(false) {
                continue;
            }
            state.current = Some(seq);

            // Send the frame event for any stored focus in the up handler
            if let Some(focus) = state.frame_pending.take() {
                focus.frame(seat, data, seq);
            }

            if let Some((focus, _)) = state.focus.as_ref() {
                focus.frame(seat, data, seq);
            }
        }
    }

    fn cancel(&mut self, data: &mut D, seat: &Seat<D>, seq: Serial) {
        for state in self.focus.values_mut() {
            if state.current.map(|c| c >= state.pending).unwrap_or(false) {
                continue;
            }
            state.current = Some(seq);
            if let Some((focus, _)) = state.focus.take() {
                focus.cancel(seat, data, seq);
            }
        }
    }

    fn shape(&mut self, data: &mut D, seat: &Seat<D>, event: &ShapeEvent, seq: Serial) {
        let Some(state) = self.focus.get_mut(&event.slot) else {
            return;
        };
        state.pending = seq;
        if let Some((focus, _)) = state.focus.as_ref() {
            focus.shape(seat, data, event, seq);
        }
    }

    fn orientation(&mut self, data: &mut D, seat: &Seat<D>, event: &OrientationEvent, seq: Serial) {
        let Some(state) = self.focus.get_mut(&event.slot) else {
            return;
        };
        state.pending = seq;
        if let Some((focus, _)) = state.focus.as_ref() {
            focus.orientation(seat, data, event, seq);
        }
    }

    fn with_grab<F>(&mut self, data: &mut D, seat: &Seat<D>, f: F)
    where
        F: FnOnce(&mut D, &mut TouchInnerHandle<'_, D>, &mut dyn TouchGrab<D>),
    {
        let mut grab = std::mem::replace(&mut self.grab, GrabStatus::Borrowed);
        match grab {
            GrabStatus::Borrowed => panic!("Accessed a touch grab from within a touch grab access."),
            GrabStatus::Active(_, ref mut handler) => {
                // If this grab is associated with a surface that is no longer alive, discard it
                if let Some((ref focus, _)) = handler.start_data().focus {
                    if !focus.alive() {
                        handler.unset(data);
                        self.grab = GrabStatus::None;
                        let mut default_grab = (self.default_grab)();
                        f(
                            data,
                            &mut TouchInnerHandle { inner: self, seat },
                            &mut *default_grab,
                        );
                        return;
                    }
                }
                f(data, &mut TouchInnerHandle { inner: self, seat }, &mut **handler);
            }
            GrabStatus::None => {
                let mut default_grab = (self.default_grab)();
                f(
                    data,
                    &mut TouchInnerHandle { inner: self, seat },
                    &mut *default_grab,
                );
            }
        }

        if let GrabStatus::Borrowed = self.grab {
            // the grab has not been ended nor replaced, put it back in place
            self.grab = grab;
        }
    }
}