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