Skip to main content

smithay_client_toolkit/seat/
touch.rs

1use std::sync::Mutex;
2
3use wayland_client::protocol::wl_seat::WlSeat;
4
5use wayland_client::protocol::wl_surface::WlSurface;
6use wayland_client::protocol::wl_touch::{Event as TouchEvent, WlTouch};
7use wayland_client::{Connection, Dispatch, QueueHandle};
8
9use crate::dispatch2::Dispatch2;
10
11#[derive(Debug)]
12pub struct TouchData<U> {
13    seat: WlSeat,
14    inner: Mutex<TouchDataInner>,
15    udata: U,
16}
17
18impl<U> TouchData<U> {
19    /// Create the new touch data associated with the given seat.
20    pub fn new(seat: WlSeat, udata: U) -> Self {
21        Self { seat, inner: Default::default(), udata }
22    }
23
24    pub fn data(&self) -> &U {
25        &self.udata
26    }
27
28    pub fn data_mut(&mut self) -> &mut U {
29        &mut self.udata
30    }
31
32    /// Get the associated seat from the data.
33    pub fn seat(&self) -> &WlSeat {
34        &self.seat
35    }
36
37    /// Serial from the latest touch down event.
38    pub fn latest_down_serial(&self) -> Option<u32> {
39        self.inner.lock().unwrap().latest_down
40    }
41}
42
43#[derive(Debug, Default)]
44pub(crate) struct TouchDataInner {
45    events: Vec<TouchEvent>,
46    active_touch_points: Vec<i32>,
47
48    /// The serial of the latest touch down event
49    latest_down: Option<u32>,
50}
51
52pub trait TouchHandler: Sized {
53    /// New touch point.
54    ///
55    /// Indicates a new touch point has appeared on the surface, starting a touch sequence. The ID
56    /// associated with this event identifies this touch point for devices with multi-touch and
57    /// will be referenced in future events.
58    ///
59    /// The associated touch ID ceases to be valid after the touch up event with the associated ID
60    /// and may be reused for other touch points after that.
61    ///
62    /// Coordinates are surface-local.
63    #[allow(clippy::too_many_arguments)]
64    fn down(
65        &mut self,
66        conn: &Connection,
67        qh: &QueueHandle<Self>,
68        touch: &WlTouch,
69        serial: u32,
70        time: u32,
71        surface: WlSurface,
72        id: i32,
73        position: (f64, f64),
74    );
75
76    /// End of touch sequence.
77    fn up(
78        &mut self,
79        conn: &Connection,
80        qh: &QueueHandle<Self>,
81        touch: &WlTouch,
82        serial: u32,
83        time: u32,
84        id: i32,
85    );
86
87    /// Touch point motion.
88    ///
89    /// Coordinates are surface-local.
90    fn motion(
91        &mut self,
92        conn: &Connection,
93        qh: &QueueHandle<Self>,
94        touch: &WlTouch,
95        time: u32,
96        id: i32,
97        position: (f64, f64),
98    );
99
100    /// Touch point shape change.
101    ///
102    /// The shape of a touch point is approximated by an ellipse through the major and minor axis
103    /// length. Major always represents the larger of the two axis and is orthogonal to minor.
104    ///
105    /// The dimensions are specified in surface-local coordinates and the locations reported by
106    /// other events always report the center of the ellipse.
107    fn shape(
108        &mut self,
109        conn: &Connection,
110        qh: &QueueHandle<Self>,
111        touch: &WlTouch,
112        id: i32,
113        major: f64,
114        minor: f64,
115    );
116
117    /// Touch point shape orientation.
118    ///
119    /// The orientation describes the clockwise angle of a touch point's major axis to the positive
120    /// surface y-axis and is normalized to the -180° to +180° range.
121    fn orientation(
122        &mut self,
123        conn: &Connection,
124        qh: &QueueHandle<Self>,
125        touch: &WlTouch,
126        id: i32,
127        orientation: f64,
128    );
129
130    /// Cancel active touch sequence.
131    ///
132    /// This indicates that the compositor has cancelled the active touch sequence, for example due
133    /// to detection of a touch gesture.
134    fn cancel(&mut self, conn: &Connection, qh: &QueueHandle<Self>, touch: &WlTouch);
135}
136
137impl<D, U> Dispatch2<WlTouch, D> for TouchData<U>
138where
139    D: Dispatch<WlTouch, TouchData<U>> + TouchHandler,
140{
141    fn event(
142        &self,
143        data: &mut D,
144        touch: &WlTouch,
145        event: TouchEvent,
146        conn: &Connection,
147        qh: &QueueHandle<D>,
148    ) {
149        let mut guard: std::sync::MutexGuard<'_, TouchDataInner> = self.inner.lock().unwrap();
150
151        let mut save_event = false;
152        let mut process_events = false;
153
154        match &event {
155            // Buffer events until frame is received.
156            TouchEvent::Down { serial, id, .. } => {
157                guard.latest_down = Some(*serial);
158                save_event = true;
159                if let Err(insert_pos) = guard.active_touch_points.binary_search(id) {
160                    guard.active_touch_points.insert(insert_pos, *id);
161                }
162            }
163            TouchEvent::Up { id, .. } => {
164                save_event = true;
165                if let Ok(remove_pos) = guard.active_touch_points.binary_search(id) {
166                    guard.active_touch_points.remove(remove_pos);
167                }
168
169                // Weston doesn't always send a frame even after the last touch point was released:
170                // https://gitlab.freedesktop.org/wayland/weston/-/issues/44
171                // Work around this by processing pending events when there are no more touch points
172                // active.
173                if guard.active_touch_points.is_empty() {
174                    process_events = true;
175                }
176            }
177            TouchEvent::Motion { .. }
178            | TouchEvent::Shape { .. }
179            | TouchEvent::Orientation { .. } => {
180                save_event = true;
181            }
182            // Process all buffered events.
183            TouchEvent::Frame => {
184                process_events = true;
185            }
186            TouchEvent::Cancel => {
187                guard.events.clear();
188                guard.active_touch_points.clear();
189
190                data.cancel(conn, qh, touch);
191            }
192            _ => unreachable!(),
193        }
194
195        if save_event {
196            guard.events.push(event);
197        }
198
199        if process_events {
200            for event in guard.events.drain(..) {
201                process_framed_event(data, touch, conn, qh, event);
202            }
203        }
204    }
205}
206
207/// Process a single frame-buffered touch event.
208fn process_framed_event<D>(
209    data: &mut D,
210    touch: &WlTouch,
211    conn: &Connection,
212    qh: &QueueHandle<D>,
213    event: TouchEvent,
214) where
215    D: TouchHandler,
216{
217    match event {
218        TouchEvent::Down { serial, time, surface, id, x, y } => {
219            data.down(conn, qh, touch, serial, time, surface, id, (x, y));
220        }
221        TouchEvent::Up { serial, time, id } => {
222            data.up(conn, qh, touch, serial, time, id);
223        }
224        TouchEvent::Motion { time, id, x, y } => {
225            data.motion(conn, qh, touch, time, id, (x, y));
226        }
227        TouchEvent::Shape { id, major, minor } => {
228            data.shape(conn, qh, touch, id, major, minor);
229        }
230        TouchEvent::Orientation { id, orientation } => {
231            data.orientation(conn, qh, touch, id, orientation);
232        }
233        // No other events should be frame-buffered.
234        _ => unreachable!(),
235    }
236}