1use std::{
2 fmt::{self, Display, Formatter},
3 sync::{
4 atomic::{AtomicBool, Ordering},
5 Arc, Mutex,
6 },
7};
8
9use crate::reexports::client::{
10 globals::{Global, GlobalList},
11 protocol::{wl_pointer, wl_registry::WlRegistry, wl_seat, wl_shm, wl_surface, wl_touch},
12 Connection, Dispatch, Proxy, QueueHandle,
13};
14use crate::reexports::protocols::wp::cursor_shape::v1::client::wp_cursor_shape_device_v1::WpCursorShapeDeviceV1;
15use crate::reexports::protocols::wp::cursor_shape::v1::client::wp_cursor_shape_manager_v1::WpCursorShapeManagerV1;
16use crate::{
17 compositor::SurfaceDataExt,
18 globals::GlobalData,
19 registry::{ProvidesRegistryState, RegistryHandler},
20};
21
22pub mod input_method;
23pub mod input_method_v3;
24#[cfg(feature = "xkbcommon")]
25pub mod keyboard;
26pub mod pointer;
27pub mod pointer_constraints;
28pub mod relative_pointer;
29pub mod touch;
30
31use pointer::cursor_shape::CursorShapeManager;
32use pointer::{PointerData, PointerDataExt, PointerHandler, ThemeSpec, ThemedPointer, Themes};
33use touch::{TouchData, TouchDataExt, TouchHandler};
34
35#[non_exhaustive]
36#[derive(Debug, Clone, Copy, PartialEq, Eq)]
37pub enum Capability {
38 Keyboard,
39
40 Pointer,
41
42 Touch,
43}
44
45impl Display for Capability {
46 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
47 match self {
48 Capability::Keyboard => write!(f, "keyboard"),
49 Capability::Pointer => write!(f, "pointer"),
50 Capability::Touch => write!(f, "touch"),
51 }
52 }
53}
54
55#[derive(Debug, thiserror::Error)]
56pub enum SeatError {
57 #[error("the capability \"{0}\" is not supported")]
58 UnsupportedCapability(Capability),
60
61 #[error("the seat is dead")]
63 DeadObject,
64}
65
66#[derive(Debug)]
67pub struct SeatState {
68 seats: Vec<SeatInner>,
70 cursor_shape_manager_state: CursorShapeManagerState,
71}
72
73#[derive(Debug)]
74enum CursorShapeManagerState {
75 NotPresent,
76 Pending { registry: WlRegistry, global: Global },
77 Bound(CursorShapeManager),
78}
79
80impl SeatState {
81 pub fn new<D: Dispatch<wl_seat::WlSeat, SeatData> + 'static>(
82 global_list: &GlobalList,
83 qh: &QueueHandle<D>,
84 ) -> SeatState {
85 let (seats, cursor_shape_manager) = global_list.contents().with_list(|globals| {
86 let global = globals
87 .iter()
88 .find(|global| global.interface == WpCursorShapeManagerV1::interface().name)
89 .map(|global| CursorShapeManagerState::Pending {
90 registry: global_list.registry().clone(),
91 global: global.clone(),
92 })
93 .unwrap_or(CursorShapeManagerState::NotPresent);
94
95 (
96 crate::registry::bind_all(global_list.registry(), globals, qh, 1..=10, |id| {
97 SeatData {
98 has_keyboard: Arc::new(AtomicBool::new(false)),
99 has_pointer: Arc::new(AtomicBool::new(false)),
100 has_touch: Arc::new(AtomicBool::new(false)),
101 name: Arc::new(Mutex::new(None)),
102 id,
103 }
104 })
105 .expect("failed to bind global"),
106 global,
107 )
108 });
109
110 let mut state =
111 SeatState { seats: vec![], cursor_shape_manager_state: cursor_shape_manager };
112
113 for seat in seats {
114 let data = seat.data::<SeatData>().unwrap().clone();
115
116 state.seats.push(SeatInner { seat: seat.clone(), data });
117 }
118 state
119 }
120
121 pub fn seats(&self) -> impl Iterator<Item = wl_seat::WlSeat> {
123 self.seats.iter().map(|inner| inner.seat.clone()).collect::<Vec<_>>().into_iter()
124 }
125
126 pub fn info(&self, seat: &wl_seat::WlSeat) -> Option<SeatInfo> {
130 self.seats.iter().find(|inner| &inner.seat == seat).map(|inner| {
131 let name = inner.data.name.lock().unwrap().clone();
132
133 SeatInfo {
134 name,
135 has_keyboard: inner.data.has_keyboard.load(Ordering::SeqCst),
136 has_pointer: inner.data.has_pointer.load(Ordering::SeqCst),
137 has_touch: inner.data.has_touch.load(Ordering::SeqCst),
138 }
139 })
140 }
141
142 pub fn get_pointer<D>(
148 &mut self,
149 qh: &QueueHandle<D>,
150 seat: &wl_seat::WlSeat,
151 ) -> Result<wl_pointer::WlPointer, SeatError>
152 where
153 D: Dispatch<wl_pointer::WlPointer, PointerData> + PointerHandler + 'static,
154 {
155 self.get_pointer_with_data(qh, seat, PointerData::new(seat.clone()))
156 }
157
158 pub fn get_pointer_with_theme<D, S>(
166 &mut self,
167 qh: &QueueHandle<D>,
168 seat: &wl_seat::WlSeat,
169 shm: &wl_shm::WlShm,
170 surface: wl_surface::WlSurface,
171 theme: ThemeSpec,
172 ) -> Result<ThemedPointer<PointerData>, SeatError>
173 where
174 D: Dispatch<wl_pointer::WlPointer, PointerData>
175 + Dispatch<wl_surface::WlSurface, S>
176 + Dispatch<WpCursorShapeManagerV1, GlobalData>
177 + Dispatch<WpCursorShapeDeviceV1, GlobalData>
178 + PointerHandler
179 + 'static,
180 S: SurfaceDataExt + 'static,
181 {
182 self.get_pointer_with_theme_and_data(
183 qh,
184 seat,
185 shm,
186 surface,
187 theme,
188 PointerData::new(seat.clone()),
189 )
190 }
191
192 pub fn get_pointer_with_data<D, U>(
198 &mut self,
199 qh: &QueueHandle<D>,
200 seat: &wl_seat::WlSeat,
201 pointer_data: U,
202 ) -> Result<wl_pointer::WlPointer, SeatError>
203 where
204 D: Dispatch<wl_pointer::WlPointer, U> + PointerHandler + 'static,
205 U: PointerDataExt + 'static,
206 {
207 let inner =
208 self.seats.iter().find(|inner| &inner.seat == seat).ok_or(SeatError::DeadObject)?;
209
210 if !inner.data.has_pointer.load(Ordering::SeqCst) {
211 return Err(SeatError::UnsupportedCapability(Capability::Pointer));
212 }
213
214 Ok(seat.get_pointer(qh, pointer_data))
215 }
216
217 pub fn get_pointer_with_theme_and_data<D, S, U>(
223 &mut self,
224 qh: &QueueHandle<D>,
225 seat: &wl_seat::WlSeat,
226 shm: &wl_shm::WlShm,
227 surface: wl_surface::WlSurface,
228 theme: ThemeSpec,
229 pointer_data: U,
230 ) -> Result<ThemedPointer<U>, SeatError>
231 where
232 D: Dispatch<wl_pointer::WlPointer, U>
233 + Dispatch<wl_surface::WlSurface, S>
234 + Dispatch<WpCursorShapeManagerV1, GlobalData>
235 + Dispatch<WpCursorShapeDeviceV1, GlobalData>
236 + PointerHandler
237 + 'static,
238 S: SurfaceDataExt + 'static,
239 U: PointerDataExt + 'static,
240 {
241 let inner =
242 self.seats.iter().find(|inner| &inner.seat == seat).ok_or(SeatError::DeadObject)?;
243
244 if !inner.data.has_pointer.load(Ordering::SeqCst) {
245 return Err(SeatError::UnsupportedCapability(Capability::Pointer));
246 }
247
248 let wl_ptr = seat.get_pointer(qh, pointer_data);
249
250 if let CursorShapeManagerState::Pending { registry, global } =
251 &self.cursor_shape_manager_state
252 {
253 self.cursor_shape_manager_state =
254 match crate::registry::bind_one(registry, &[global.clone()], qh, 1..=2, GlobalData)
255 {
256 Ok(bound) => {
257 CursorShapeManagerState::Bound(CursorShapeManager::from_existing(bound))
258 }
259 Err(_) => CursorShapeManagerState::NotPresent,
260 }
261 }
262
263 let shape_device =
264 if let CursorShapeManagerState::Bound(ref bound) = self.cursor_shape_manager_state {
265 Some(bound.get_shape_device(&wl_ptr, qh))
266 } else {
267 None
268 };
269
270 Ok(ThemedPointer {
271 themes: Arc::new(Mutex::new(Themes::new(theme))),
272 pointer: wl_ptr,
273 shm: shm.clone(),
274 surface,
275 shape_device,
276 _marker: std::marker::PhantomData,
277 _surface_data: std::marker::PhantomData,
278 })
279 }
280
281 pub fn get_touch<D>(
287 &mut self,
288 qh: &QueueHandle<D>,
289 seat: &wl_seat::WlSeat,
290 ) -> Result<wl_touch::WlTouch, SeatError>
291 where
292 D: Dispatch<wl_touch::WlTouch, TouchData> + TouchHandler + 'static,
293 {
294 self.get_touch_with_data(qh, seat, TouchData::new(seat.clone()))
295 }
296
297 pub fn get_touch_with_data<D, U>(
303 &mut self,
304 qh: &QueueHandle<D>,
305 seat: &wl_seat::WlSeat,
306 udata: U,
307 ) -> Result<wl_touch::WlTouch, SeatError>
308 where
309 D: Dispatch<wl_touch::WlTouch, U> + TouchHandler + 'static,
310 U: TouchDataExt + 'static,
311 {
312 let inner =
313 self.seats.iter().find(|inner| &inner.seat == seat).ok_or(SeatError::DeadObject)?;
314
315 if !inner.data.has_touch.load(Ordering::SeqCst) {
316 return Err(SeatError::UnsupportedCapability(Capability::Touch));
317 }
318
319 Ok(seat.get_touch(qh, udata))
320 }
321}
322
323pub trait SeatHandler: Sized {
324 fn seat_state(&mut self) -> &mut SeatState;
325
326 fn new_seat(&mut self, conn: &Connection, qh: &QueueHandle<Self>, seat: wl_seat::WlSeat);
331
332 fn new_capability(
336 &mut self,
337 conn: &Connection,
338 qh: &QueueHandle<Self>,
339 seat: wl_seat::WlSeat,
340 capability: Capability,
341 );
342
343 fn remove_capability(
347 &mut self,
348 conn: &Connection,
349 qh: &QueueHandle<Self>,
350 seat: wl_seat::WlSeat,
351 capability: Capability,
352 );
353
354 fn remove_seat(&mut self, conn: &Connection, qh: &QueueHandle<Self>, seat: wl_seat::WlSeat);
358}
359
360#[non_exhaustive]
362#[derive(Debug, Clone)]
363pub struct SeatInfo {
364 pub name: Option<String>,
366
367 pub has_keyboard: bool,
369
370 pub has_pointer: bool,
372
373 pub has_touch: bool,
375}
376
377impl Display for SeatInfo {
378 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
379 if let Some(ref name) = self.name {
380 write!(f, "name: \"{name}\" ")?;
381 }
382
383 write!(f, "capabilities: (")?;
384
385 if !self.has_keyboard && !self.has_pointer && !self.has_touch {
386 write!(f, "none")?;
387 } else {
388 if self.has_keyboard {
389 write!(f, "keyboard")?;
390
391 if self.has_pointer || self.has_touch {
392 write!(f, ", ")?;
393 }
394 }
395
396 if self.has_pointer {
397 write!(f, "pointer")?;
398
399 if self.has_touch {
400 write!(f, ", ")?;
401 }
402 }
403
404 if self.has_touch {
405 write!(f, "touch")?;
406 }
407 }
408
409 write!(f, ")")
410 }
411}
412
413#[derive(Debug, Clone)]
414pub struct SeatData {
415 has_keyboard: Arc<AtomicBool>,
416 has_pointer: Arc<AtomicBool>,
417 has_touch: Arc<AtomicBool>,
418 name: Arc<Mutex<Option<String>>>,
419 id: u32,
420}
421
422#[macro_export]
423macro_rules! delegate_seat {
424 ($(@<$( $lt:tt $( : $clt:tt $(+ $dlt:tt )* )? ),+>)? $ty: ty) => {
425 $crate::reexports::client::delegate_dispatch!($(@< $( $lt $( : $clt $(+ $dlt )* )? ),+ >)? $ty:
426 [
427 $crate::reexports::client::protocol::wl_seat::WlSeat: $crate::seat::SeatData
428 ] => $crate::seat::SeatState
429 );
430 };
431}
432
433#[derive(Debug)]
434struct SeatInner {
435 seat: wl_seat::WlSeat,
436 data: SeatData,
437}
438
439impl<D> Dispatch<wl_seat::WlSeat, SeatData, D> for SeatState
440where
441 D: Dispatch<wl_seat::WlSeat, SeatData> + SeatHandler,
442{
443 fn event(
444 state: &mut D,
445 seat: &wl_seat::WlSeat,
446 event: wl_seat::Event,
447 data: &SeatData,
448 conn: &Connection,
449 qh: &QueueHandle<D>,
450 ) {
451 match event {
452 wl_seat::Event::Capabilities { capabilities } => {
453 let capabilities = wl_seat::Capability::from_bits_truncate(capabilities.into());
454
455 let keyboard = capabilities.contains(wl_seat::Capability::Keyboard);
456 let has_keyboard = data.has_keyboard.load(Ordering::SeqCst);
457 let pointer = capabilities.contains(wl_seat::Capability::Pointer);
458 let has_pointer = data.has_pointer.load(Ordering::SeqCst);
459 let touch = capabilities.contains(wl_seat::Capability::Touch);
460 let has_touch = data.has_touch.load(Ordering::SeqCst);
461
462 if keyboard != has_keyboard {
464 data.has_keyboard.store(keyboard, Ordering::SeqCst);
465
466 match keyboard {
467 true => state.new_capability(conn, qh, seat.clone(), Capability::Keyboard),
468 false => {
469 state.remove_capability(conn, qh, seat.clone(), Capability::Keyboard)
470 }
471 }
472 }
473
474 if pointer != has_pointer {
475 data.has_pointer.store(pointer, Ordering::SeqCst);
476
477 match pointer {
478 true => state.new_capability(conn, qh, seat.clone(), Capability::Pointer),
479 false => {
480 state.remove_capability(conn, qh, seat.clone(), Capability::Pointer)
481 }
482 }
483 }
484
485 if touch != has_touch {
486 data.has_touch.store(touch, Ordering::SeqCst);
487
488 match touch {
489 true => state.new_capability(conn, qh, seat.clone(), Capability::Touch),
490 false => state.remove_capability(conn, qh, seat.clone(), Capability::Touch),
491 }
492 }
493 }
494
495 wl_seat::Event::Name { name } => {
496 *data.name.lock().unwrap() = Some(name);
497 }
498
499 _ => unreachable!(),
500 }
501 }
502}
503
504impl<D> RegistryHandler<D> for SeatState
505where
506 D: Dispatch<wl_seat::WlSeat, SeatData> + SeatHandler + ProvidesRegistryState + 'static,
507{
508 fn new_global(
509 state: &mut D,
510 conn: &Connection,
511 qh: &QueueHandle<D>,
512 name: u32,
513 interface: &str,
514 _: u32,
515 ) {
516 if interface == wl_seat::WlSeat::interface().name {
517 let seat = state
518 .registry()
519 .bind_specific(
520 qh,
521 name,
522 1..=7,
523 SeatData {
524 has_keyboard: Arc::new(AtomicBool::new(false)),
525 has_pointer: Arc::new(AtomicBool::new(false)),
526 has_touch: Arc::new(AtomicBool::new(false)),
527 name: Arc::new(Mutex::new(None)),
528 id: name,
529 },
530 )
531 .expect("failed to bind global");
532
533 let data = seat.data::<SeatData>().unwrap().clone();
534
535 state.seat_state().seats.push(SeatInner { seat: seat.clone(), data });
536 state.new_seat(conn, qh, seat);
537 }
538 }
539
540 fn remove_global(
541 state: &mut D,
542 conn: &Connection,
543 qh: &QueueHandle<D>,
544 name: u32,
545 interface: &str,
546 ) {
547 if interface == wl_seat::WlSeat::interface().name {
548 if let Some(seat) = state.seat_state().seats.iter().find(|inner| inner.data.id == name)
549 {
550 let seat = seat.seat.clone();
551
552 state.remove_seat(conn, qh, seat);
553 state.seat_state().seats.retain(|inner| inner.data.id != name);
554 }
555 }
556 }
557}