smithay/input/touch/
grab.rs

1use std::fmt;
2
3use downcast_rs::{impl_downcast, Downcast};
4
5use crate::{
6    backend::input::TouchSlot,
7    input::SeatHandler,
8    utils::{Logical, Point, Serial},
9};
10
11use super::{DownEvent, MotionEvent, OrientationEvent, ShapeEvent, TouchInnerHandle, UpEvent};
12
13/// A trait to implement a touch grab
14///
15/// In some context, it is necessary to temporarily change the behavior of the touch handler. This is
16/// typically known as a touch grab. A typical example would be, during a drag'n'drop operation,
17/// the underlying surfaces will no longer receive classic pointer event, but rather special events.
18///
19/// This trait is the interface to intercept regular touch events and change them as needed, its
20/// interface mimics the [`TouchHandle`](super::TouchHandle) interface.
21///
22/// Any interactions with [`TouchHandle`](super::TouchHandle)
23/// should be done using [`TouchInnerHandle`], as handle is borrowed/locked before grab methods are called,
24/// so calling methods on [`TouchHandle`](super::TouchHandle) would result in a deadlock.
25///
26/// If your logic decides that the grab should end, both [`TouchInnerHandle`]
27/// and [`TouchHandle`](super::TouchHandle) have
28/// a method to change it.
29///
30/// When your grab ends (either as you requested it or if it was forcefully cancelled by the server),
31/// the struct implementing this trait will be dropped. As such you should put clean-up logic in the destructor,
32/// rather than trying to guess when the grab will end.
33pub trait TouchGrab<D: SeatHandler>: Send + Downcast {
34    /// A new touch point appeared
35    ///
36    /// This method allows you attach additional behavior to a down event, possibly altering it.
37    /// You generally will want to invoke [`TouchInnerHandle::down`] as part of your processing. If you
38    /// don't, the rest of the compositor will behave as if the down event never occurred.
39    ///
40    /// Some grabs (such as drag'n'drop, shell resize and motion) drop down events while they are active,
41    /// the default touch grab will keep the focus on the surface that started the grab.
42    fn down(
43        &mut self,
44        data: &mut D,
45        handle: &mut TouchInnerHandle<'_, D>,
46        focus: Option<(<D as SeatHandler>::TouchFocus, Point<f64, Logical>)>,
47        event: &DownEvent,
48        seq: Serial,
49    );
50    /// A touch point disappeared
51    ///
52    /// This method allows you attach additional behavior to a up event, possibly altering it.
53    /// You generally will want to invoke [`TouchInnerHandle::up`] as part of your processing.
54    /// If you don't, the rest of the compositor will behave as if the up event never occurred.
55    ///
56    /// Some grabs (such as drag'n'drop, shell resize and motion) drop up events while they are active,
57    /// but will end when the touch point that initiated the grab disappeared.
58    fn up(&mut self, data: &mut D, handle: &mut TouchInnerHandle<'_, D>, event: &UpEvent, seq: Serial);
59
60    /// A touch point has changed coordinates.
61    ///
62    /// This method allows you attach additional behavior to a motion event, possibly altering it.
63    /// You generally will want to invoke [`TouchInnerHandle::motion`] as part of your processing.
64    /// If you don't, the rest of the compositor will behave as if the motion event never occurred.
65    ///
66    /// **Note** that this is **not** intended to update the focus of the touch point, the focus
67    /// is only set on a down event. The focus provided to this function can be used to find DnD
68    /// targets during touch motion.
69    fn motion(
70        &mut self,
71        data: &mut D,
72        handle: &mut TouchInnerHandle<'_, D>,
73        focus: Option<(<D as SeatHandler>::TouchFocus, Point<f64, Logical>)>,
74        event: &MotionEvent,
75        seq: Serial,
76    );
77
78    /// Marks the end of a set of events that logically belong together.
79    ///
80    /// This method allows you attach additional behavior to a frame event, possibly altering it.
81    /// You generally will want to invoke [`TouchInnerHandle::frame`] as part of your processing.
82    /// If you don't, the rest of the compositor will behave as if the frame event never occurred.
83    ///
84    /// This will to be called after one or more calls to down/motion events.
85    fn frame(&mut self, data: &mut D, handle: &mut TouchInnerHandle<'_, D>, seq: Serial);
86
87    /// A touch session has been cancelled.
88    ///
89    /// This method allows you attach additional behavior to a cancel event, possibly altering it.
90    /// You generally will want to invoke [`TouchInnerHandle::cancel`] as part of your processing.
91    /// If you don't, the rest of the compositor will behave as if the cancel event never occurred.
92    ///
93    /// Usually called in case the compositor decides the touch stream is a global gesture.
94    fn cancel(&mut self, data: &mut D, handle: &mut TouchInnerHandle<'_, D>, seq: Serial);
95
96    /// A touch point has changed its shape.
97    fn shape(&mut self, data: &mut D, handle: &mut TouchInnerHandle<'_, D>, event: &ShapeEvent, seq: Serial);
98
99    /// A touch point has changed its orientation.
100    fn orientation(
101        &mut self,
102        data: &mut D,
103        handle: &mut TouchInnerHandle<'_, D>,
104        event: &OrientationEvent,
105        seq: Serial,
106    );
107
108    /// The data about the event that started the grab.
109    fn start_data(&self) -> &GrabStartData<D>;
110
111    /// The grab has been unset or replaced with another grab.
112    fn unset(&mut self, data: &mut D);
113}
114
115impl_downcast!(TouchGrab<D> where D: SeatHandler);
116
117/// Data about the event that started the grab.
118pub struct GrabStartData<D: SeatHandler> {
119    /// The focused surface and its location, if any, at the start of the grab.
120    ///
121    /// The location coordinates are in the global compositor space.
122    pub focus: Option<(<D as SeatHandler>::TouchFocus, Point<f64, Logical>)>,
123    /// The touch point that initiated the grab.
124    pub slot: TouchSlot,
125    /// The location of the down event that initiated the grab, in the global compositor space.
126    pub location: Point<f64, Logical>,
127}
128
129impl<D: SeatHandler + 'static> fmt::Debug for GrabStartData<D> {
130    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
131        f.debug_struct("GrabStartData")
132            .field("focus", &self.focus.as_ref().map(|_| "..."))
133            .field("slot", &self.slot)
134            .field("location", &self.location)
135            .finish()
136    }
137}
138
139impl<D: SeatHandler + 'static> Clone for GrabStartData<D> {
140    fn clone(&self) -> Self {
141        GrabStartData {
142            focus: self.focus.clone(),
143            slot: self.slot,
144            location: self.location,
145        }
146    }
147}
148
149/// The default grab, the behavior when no particular grab is in progress
150#[derive(Debug)]
151pub struct DefaultGrab;
152
153impl<D: SeatHandler + 'static> TouchGrab<D> for DefaultGrab {
154    fn down(
155        &mut self,
156        data: &mut D,
157        handle: &mut TouchInnerHandle<'_, D>,
158        focus: Option<(<D as SeatHandler>::TouchFocus, Point<f64, Logical>)>,
159        event: &DownEvent,
160        seq: Serial,
161    ) {
162        handle.down(data, focus.clone(), event, seq);
163        handle.set_grab(
164            self,
165            data,
166            event.serial,
167            TouchDownGrab {
168                start_data: GrabStartData {
169                    focus,
170                    slot: event.slot,
171                    location: event.location,
172                },
173                touch_points: 1,
174            },
175        );
176    }
177
178    fn up(&mut self, data: &mut D, handle: &mut TouchInnerHandle<'_, D>, event: &UpEvent, seq: Serial) {
179        handle.up(data, event, seq)
180    }
181
182    fn motion(
183        &mut self,
184        data: &mut D,
185        handle: &mut TouchInnerHandle<'_, D>,
186        focus: Option<(<D as SeatHandler>::TouchFocus, Point<f64, Logical>)>,
187        event: &MotionEvent,
188        seq: Serial,
189    ) {
190        handle.motion(data, focus, event, seq)
191    }
192
193    fn frame(&mut self, data: &mut D, handle: &mut TouchInnerHandle<'_, D>, seq: Serial) {
194        handle.frame(data, seq)
195    }
196
197    fn cancel(&mut self, data: &mut D, handle: &mut TouchInnerHandle<'_, D>, seq: Serial) {
198        handle.cancel(data, seq)
199    }
200
201    fn shape(&mut self, data: &mut D, handle: &mut TouchInnerHandle<'_, D>, event: &ShapeEvent, seq: Serial) {
202        handle.shape(data, event, seq)
203    }
204
205    fn orientation(
206        &mut self,
207        data: &mut D,
208        handle: &mut TouchInnerHandle<'_, D>,
209        event: &OrientationEvent,
210        seq: Serial,
211    ) {
212        handle.orientation(data, event, seq)
213    }
214
215    fn start_data(&self) -> &GrabStartData<D> {
216        unreachable!()
217    }
218
219    fn unset(&mut self, _data: &mut D) {}
220}
221
222/// A touch down grab, basic grab started when an user touches a surface
223/// to maintain it focused until the user releases the touch.
224///
225/// In case the user maintains several simultaneous touch points, release
226/// the grab once all are released.
227pub struct TouchDownGrab<D: SeatHandler> {
228    /// Start date for this grab
229    pub start_data: GrabStartData<D>,
230    /// Currently active touch points
231    pub touch_points: usize,
232}
233
234impl<D: SeatHandler + 'static> fmt::Debug for TouchDownGrab<D> {
235    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
236        f.debug_struct("TouchDownGrab")
237            .field("start_data", &self.start_data)
238            .field("touch_points", &self.touch_points)
239            .finish()
240    }
241}
242
243impl<D: SeatHandler + 'static> TouchGrab<D> for TouchDownGrab<D> {
244    fn down(
245        &mut self,
246        data: &mut D,
247        handle: &mut TouchInnerHandle<'_, D>,
248        _focus: Option<(<D as SeatHandler>::TouchFocus, Point<f64, Logical>)>,
249        event: &DownEvent,
250        seq: Serial,
251    ) {
252        handle.down(data, self.start_data.focus.clone(), event, seq);
253        self.touch_points += 1;
254    }
255
256    fn up(&mut self, data: &mut D, handle: &mut TouchInnerHandle<'_, D>, event: &UpEvent, seq: Serial) {
257        handle.up(data, event, seq);
258        self.touch_points = self.touch_points.saturating_sub(1);
259        if self.touch_points == 0 {
260            handle.unset_grab(self, data);
261        }
262    }
263
264    fn motion(
265        &mut self,
266        data: &mut D,
267        handle: &mut TouchInnerHandle<'_, D>,
268        _focus: Option<(<D as SeatHandler>::TouchFocus, Point<f64, Logical>)>,
269        event: &MotionEvent,
270        seq: Serial,
271    ) {
272        handle.motion(data, self.start_data.focus.clone(), event, seq)
273    }
274
275    fn frame(&mut self, data: &mut D, handle: &mut TouchInnerHandle<'_, D>, seq: Serial) {
276        handle.frame(data, seq)
277    }
278
279    fn cancel(&mut self, data: &mut D, handle: &mut TouchInnerHandle<'_, D>, seq: Serial) {
280        handle.cancel(data, seq);
281        handle.unset_grab(self, data);
282    }
283
284    fn shape(&mut self, data: &mut D, handle: &mut TouchInnerHandle<'_, D>, event: &ShapeEvent, seq: Serial) {
285        handle.shape(data, event, seq)
286    }
287
288    fn orientation(
289        &mut self,
290        data: &mut D,
291        handle: &mut TouchInnerHandle<'_, D>,
292        event: &OrientationEvent,
293        seq: Serial,
294    ) {
295        handle.orientation(data, event, seq)
296    }
297
298    fn start_data(&self) -> &GrabStartData<D> {
299        &self.start_data
300    }
301
302    fn unset(&mut self, _data: &mut D) {}
303}