use std::fmt; use downcast_rs::{impl_downcast, Downcast}; use crate::{ backend::input::TouchSlot, input::SeatHandler, utils::{Logical, Point, Serial}, }; use super::{DownEvent, MotionEvent, OrientationEvent, ShapeEvent, TouchInnerHandle, UpEvent}; /// A trait to implement a touch grab /// /// In some context, it is necessary to temporarily change the behavior of the touch handler. This is /// typically known as a touch grab. A typical example would be, during a drag'n'drop operation, /// the underlying surfaces will no longer receive classic pointer event, but rather special events. /// /// This trait is the interface to intercept regular touch events and change them as needed, its /// interface mimics the [`TouchHandle`](super::TouchHandle) interface. /// /// Any interactions with [`TouchHandle`](super::TouchHandle) /// should be done using [`TouchInnerHandle`], as handle is borrowed/locked before grab methods are called, /// so calling methods on [`TouchHandle`](super::TouchHandle) would result in a deadlock. /// /// If your logic decides that the grab should end, both [`TouchInnerHandle`] /// and [`TouchHandle`](super::TouchHandle) have /// a method to change it. /// /// When your grab ends (either as you requested it or if it was forcefully cancelled by the server), /// the struct implementing this trait will be dropped. As such you should put clean-up logic in the destructor, /// rather than trying to guess when the grab will end. pub trait TouchGrab: Send + Downcast { /// A new touch point appeared /// /// This method allows you attach additional behavior to a down event, possibly altering it. /// You generally will want to invoke [`TouchInnerHandle::down`] as part of your processing. If you /// don't, the rest of the compositor will behave as if the down event never occurred. /// /// Some grabs (such as drag'n'drop, shell resize and motion) drop down events while they are active, /// the default touch grab will keep the focus on the surface that started the grab. fn down( &mut self, data: &mut D, handle: &mut TouchInnerHandle<'_, D>, focus: Option<(::TouchFocus, Point)>, event: &DownEvent, seq: Serial, ); /// A touch point disappeared /// /// This method allows you attach additional behavior to a up event, possibly altering it. /// You generally will want to invoke [`TouchInnerHandle::up`] as part of your processing. /// If you don't, the rest of the compositor will behave as if the up event never occurred. /// /// Some grabs (such as drag'n'drop, shell resize and motion) drop up events while they are active, /// but will end when the touch point that initiated the grab disappeared. fn up(&mut self, data: &mut D, handle: &mut TouchInnerHandle<'_, D>, event: &UpEvent, seq: Serial); /// A touch point has changed coordinates. /// /// This method allows you attach additional behavior to a motion event, possibly altering it. /// You generally will want to invoke [`TouchInnerHandle::motion`] as part of your processing. /// If you don't, the rest of the compositor will behave as if the motion event never occurred. /// /// **Note** that this is **not** intended to update the focus of the touch point, the focus /// is only set on a down event. The focus provided to this function can be used to find DnD /// targets during touch motion. fn motion( &mut self, data: &mut D, handle: &mut TouchInnerHandle<'_, D>, focus: Option<(::TouchFocus, Point)>, event: &MotionEvent, seq: Serial, ); /// Marks the end of a set of events that logically belong together. /// /// This method allows you attach additional behavior to a frame event, possibly altering it. /// You generally will want to invoke [`TouchInnerHandle::frame`] as part of your processing. /// If you don't, the rest of the compositor will behave as if the frame event never occurred. /// /// This will to be called after one or more calls to down/motion events. fn frame(&mut self, data: &mut D, handle: &mut TouchInnerHandle<'_, D>, seq: Serial); /// A touch session has been cancelled. /// /// This method allows you attach additional behavior to a cancel event, possibly altering it. /// You generally will want to invoke [`TouchInnerHandle::cancel`] as part of your processing. /// If you don't, the rest of the compositor will behave as if the cancel event never occurred. /// /// Usually called in case the compositor decides the touch stream is a global gesture. fn cancel(&mut self, data: &mut D, handle: &mut TouchInnerHandle<'_, D>, seq: Serial); /// A touch point has changed its shape. fn shape(&mut self, data: &mut D, handle: &mut TouchInnerHandle<'_, D>, event: &ShapeEvent, seq: Serial); /// A touch point has changed its orientation. fn orientation( &mut self, data: &mut D, handle: &mut TouchInnerHandle<'_, D>, event: &OrientationEvent, seq: Serial, ); /// The data about the event that started the grab. fn start_data(&self) -> &GrabStartData; /// The grab has been unset or replaced with another grab. fn unset(&mut self, data: &mut D); } impl_downcast!(TouchGrab where D: SeatHandler); /// Data about the event that started the grab. pub struct GrabStartData { /// The focused surface and its location, if any, at the start of the grab. /// /// The location coordinates are in the global compositor space. pub focus: Option<(::TouchFocus, Point)>, /// The touch point that initiated the grab. pub slot: TouchSlot, /// The location of the down event that initiated the grab, in the global compositor space. pub location: Point, } impl fmt::Debug for GrabStartData { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("GrabStartData") .field("focus", &self.focus.as_ref().map(|_| "...")) .field("slot", &self.slot) .field("location", &self.location) .finish() } } impl Clone for GrabStartData { fn clone(&self) -> Self { GrabStartData { focus: self.focus.clone(), slot: self.slot, location: self.location, } } } /// The default grab, the behavior when no particular grab is in progress #[derive(Debug)] pub struct DefaultGrab; impl TouchGrab for DefaultGrab { fn down( &mut self, data: &mut D, handle: &mut TouchInnerHandle<'_, D>, focus: Option<(::TouchFocus, Point)>, event: &DownEvent, seq: Serial, ) { handle.down(data, focus.clone(), event, seq); handle.set_grab( self, data, event.serial, TouchDownGrab { start_data: GrabStartData { focus, slot: event.slot, location: event.location, }, touch_points: 1, }, ); } fn up(&mut self, data: &mut D, handle: &mut TouchInnerHandle<'_, D>, event: &UpEvent, seq: Serial) { handle.up(data, event, seq) } fn motion( &mut self, data: &mut D, handle: &mut TouchInnerHandle<'_, D>, focus: Option<(::TouchFocus, Point)>, event: &MotionEvent, seq: Serial, ) { handle.motion(data, focus, event, seq) } fn frame(&mut self, data: &mut D, handle: &mut TouchInnerHandle<'_, D>, seq: Serial) { handle.frame(data, seq) } fn cancel(&mut self, data: &mut D, handle: &mut TouchInnerHandle<'_, D>, seq: Serial) { handle.cancel(data, seq) } fn shape(&mut self, data: &mut D, handle: &mut TouchInnerHandle<'_, D>, event: &ShapeEvent, seq: Serial) { handle.shape(data, event, seq) } fn orientation( &mut self, data: &mut D, handle: &mut TouchInnerHandle<'_, D>, event: &OrientationEvent, seq: Serial, ) { handle.orientation(data, event, seq) } fn start_data(&self) -> &GrabStartData { unreachable!() } fn unset(&mut self, _data: &mut D) {} } /// A touch down grab, basic grab started when an user touches a surface /// to maintain it focused until the user releases the touch. /// /// In case the user maintains several simultaneous touch points, release /// the grab once all are released. pub struct TouchDownGrab { /// Start date for this grab pub start_data: GrabStartData, /// Currently active touch points pub touch_points: usize, } impl fmt::Debug for TouchDownGrab { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("TouchDownGrab") .field("start_data", &self.start_data) .field("touch_points", &self.touch_points) .finish() } } impl TouchGrab for TouchDownGrab { fn down( &mut self, data: &mut D, handle: &mut TouchInnerHandle<'_, D>, _focus: Option<(::TouchFocus, Point)>, event: &DownEvent, seq: Serial, ) { handle.down(data, self.start_data.focus.clone(), event, seq); self.touch_points += 1; } fn up(&mut self, data: &mut D, handle: &mut TouchInnerHandle<'_, D>, event: &UpEvent, seq: Serial) { handle.up(data, event, seq); self.touch_points = self.touch_points.saturating_sub(1); if self.touch_points == 0 { handle.unset_grab(self, data); } } fn motion( &mut self, data: &mut D, handle: &mut TouchInnerHandle<'_, D>, _focus: Option<(::TouchFocus, Point)>, event: &MotionEvent, seq: Serial, ) { handle.motion(data, self.start_data.focus.clone(), event, seq) } fn frame(&mut self, data: &mut D, handle: &mut TouchInnerHandle<'_, D>, seq: Serial) { handle.frame(data, seq) } fn cancel(&mut self, data: &mut D, handle: &mut TouchInnerHandle<'_, D>, seq: Serial) { handle.cancel(data, seq); handle.unset_grab(self, data); } fn shape(&mut self, data: &mut D, handle: &mut TouchInnerHandle<'_, D>, event: &ShapeEvent, seq: Serial) { handle.shape(data, event, seq) } fn orientation( &mut self, data: &mut D, handle: &mut TouchInnerHandle<'_, D>, event: &OrientationEvent, seq: Serial, ) { handle.orientation(data, event, seq) } fn start_data(&self) -> &GrabStartData { &self.start_data } fn unset(&mut self, _data: &mut D) {} }