1use std::{
2 fmt::{self, Display, Formatter},
3 slice,
4 sync::{
5 atomic::{AtomicBool, Ordering},
6 Arc, Mutex,
7 },
8};
9
10use crate::reexports::client::{
11 globals::{Global, GlobalList},
12 protocol::{wl_pointer, wl_registry::WlRegistry, wl_seat, wl_shm, wl_surface, wl_touch},
13 Connection, Dispatch, Proxy, QueueHandle,
14};
15use crate::reexports::protocols::wp::cursor_shape::v1::client::wp_cursor_shape_device_v1::WpCursorShapeDeviceV1;
16use crate::reexports::protocols::wp::cursor_shape::v1::client::wp_cursor_shape_manager_v1::WpCursorShapeManagerV1;
17use crate::{
18 compositor::SurfaceData,
19 dispatch2::Dispatch2,
20 globals::GlobalData,
21 registry::{ProvidesRegistryState, RegistryHandler},
22};
23
24pub mod input_method;
25pub mod input_method_v3;
26#[cfg(feature = "xkbcommon")]
27pub mod keyboard;
28pub mod keyboard_filter;
29pub mod pointer;
30pub mod pointer_constraints;
31pub mod relative_pointer;
32pub mod touch;
33
34use pointer::cursor_shape::CursorShapeManager;
35use pointer::{PointerData, PointerHandler, ThemeSpec, ThemedPointer, Themes};
36use touch::{TouchData, TouchHandler};
37
38#[non_exhaustive]
39#[derive(Debug, Clone, Copy, PartialEq, Eq)]
40pub enum Capability {
41 Keyboard,
42
43 Pointer,
44
45 Touch,
46}
47
48impl Display for Capability {
49 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
50 match self {
51 Capability::Keyboard => write!(f, "keyboard"),
52 Capability::Pointer => write!(f, "pointer"),
53 Capability::Touch => write!(f, "touch"),
54 }
55 }
56}
57
58#[derive(Debug, thiserror::Error)]
59pub enum SeatError {
60 #[error("the capability \"{0}\" is not supported")]
61 UnsupportedCapability(Capability),
63
64 #[error("the seat is dead")]
66 DeadObject,
67}
68
69#[derive(Debug)]
70pub struct SeatState {
71 seats: Vec<SeatInner>,
73 cursor_shape_manager_state: CursorShapeManagerState,
74}
75
76#[derive(Debug)]
77enum CursorShapeManagerState {
78 NotPresent,
79 Pending { registry: WlRegistry, global: Global },
80 Bound(CursorShapeManager),
81}
82
83impl SeatState {
84 pub fn new<D: Dispatch<wl_seat::WlSeat, SeatData> + 'static>(
85 global_list: &GlobalList,
86 qh: &QueueHandle<D>,
87 ) -> SeatState {
88 let (seats, cursor_shape_manager) = global_list.contents().with_list(|globals| {
89 let global = globals
90 .iter()
91 .find(|global| global.interface == WpCursorShapeManagerV1::interface().name)
92 .map(|global| CursorShapeManagerState::Pending {
93 registry: global_list.registry().clone(),
94 global: global.clone(),
95 })
96 .unwrap_or(CursorShapeManagerState::NotPresent);
97
98 (
99 crate::registry::bind_all(global_list.registry(), globals, qh, 1..=10, |id| {
100 SeatData {
101 has_keyboard: Arc::new(AtomicBool::new(false)),
102 has_pointer: Arc::new(AtomicBool::new(false)),
103 has_touch: Arc::new(AtomicBool::new(false)),
104 name: Arc::new(Mutex::new(None)),
105 id,
106 }
107 })
108 .expect("failed to bind global"),
109 global,
110 )
111 });
112
113 let mut state =
114 SeatState { seats: vec![], cursor_shape_manager_state: cursor_shape_manager };
115
116 for seat in seats {
117 let data = seat.data::<SeatData>().unwrap().clone();
118
119 state.seats.push(SeatInner { seat: seat.clone(), data });
120 }
121 state
122 }
123
124 pub fn seats(&self) -> impl Iterator<Item = wl_seat::WlSeat> {
126 self.seats.iter().map(|inner| inner.seat.clone()).collect::<Vec<_>>().into_iter()
127 }
128
129 pub fn info(&self, seat: &wl_seat::WlSeat) -> Option<SeatInfo> {
133 self.seats.iter().find(|inner| &inner.seat == seat).map(|inner| {
134 let name = inner.data.name.lock().unwrap().clone();
135
136 SeatInfo {
137 name,
138 has_keyboard: inner.data.has_keyboard.load(Ordering::SeqCst),
139 has_pointer: inner.data.has_pointer.load(Ordering::SeqCst),
140 has_touch: inner.data.has_touch.load(Ordering::SeqCst),
141 }
142 })
143 }
144
145 pub fn get_pointer<D>(
151 &mut self,
152 qh: &QueueHandle<D>,
153 seat: &wl_seat::WlSeat,
154 ) -> Result<wl_pointer::WlPointer, SeatError>
155 where
156 D: Dispatch<wl_pointer::WlPointer, PointerData<()>> + PointerHandler + 'static,
157 {
158 self.get_pointer_with_data(qh, seat, ())
159 }
160
161 pub fn get_pointer_with_theme<D, S>(
169 &mut self,
170 qh: &QueueHandle<D>,
171 seat: &wl_seat::WlSeat,
172 shm: &wl_shm::WlShm,
173 surface: wl_surface::WlSurface,
174 theme: ThemeSpec,
175 ) -> Result<ThemedPointer<()>, SeatError>
176 where
177 D: Dispatch<wl_pointer::WlPointer, PointerData<()>>
178 + Dispatch<wl_surface::WlSurface, SurfaceData<S>>
179 + Dispatch<WpCursorShapeManagerV1, GlobalData>
180 + Dispatch<WpCursorShapeDeviceV1, GlobalData>
181 + PointerHandler
182 + 'static,
183 {
184 self.get_pointer_with_theme_and_data(qh, seat, shm, surface, theme, ())
185 }
186
187 pub fn get_pointer_with_data<D, U>(
193 &mut self,
194 qh: &QueueHandle<D>,
195 seat: &wl_seat::WlSeat,
196 pointer_data: U,
197 ) -> Result<wl_pointer::WlPointer, SeatError>
198 where
199 D: Dispatch<wl_pointer::WlPointer, PointerData<U>> + PointerHandler + 'static,
200 U: Send + Sync + 'static,
201 {
202 let inner =
203 self.seats.iter().find(|inner| &inner.seat == seat).ok_or(SeatError::DeadObject)?;
204
205 if !inner.data.has_pointer.load(Ordering::SeqCst) {
206 return Err(SeatError::UnsupportedCapability(Capability::Pointer));
207 }
208
209 let pointer_data = PointerData::new(seat.clone(), pointer_data);
210 Ok(seat.get_pointer(qh, pointer_data))
211 }
212
213 pub fn get_pointer_with_theme_and_data<D, U>(
219 &mut self,
220 qh: &QueueHandle<D>,
221 seat: &wl_seat::WlSeat,
222 shm: &wl_shm::WlShm,
223 surface: wl_surface::WlSurface,
224 theme: ThemeSpec,
225 pointer_data: U,
226 ) -> Result<ThemedPointer<U>, SeatError>
227 where
228 D: Dispatch<wl_pointer::WlPointer, PointerData<U>>
229 + Dispatch<WpCursorShapeManagerV1, GlobalData>
230 + Dispatch<WpCursorShapeDeviceV1, GlobalData>
231 + PointerHandler
232 + 'static,
233 U: Send + Sync + 'static,
234 {
235 let inner =
236 self.seats.iter().find(|inner| &inner.seat == seat).ok_or(SeatError::DeadObject)?;
237
238 if !inner.data.has_pointer.load(Ordering::SeqCst) {
239 return Err(SeatError::UnsupportedCapability(Capability::Pointer));
240 }
241
242 let pointer_data = PointerData::new(seat.clone(), pointer_data);
243 let wl_ptr = seat.get_pointer(qh, pointer_data);
244
245 if let CursorShapeManagerState::Pending { registry, global } =
246 &self.cursor_shape_manager_state
247 {
248 self.cursor_shape_manager_state = match crate::registry::bind_one(
249 registry,
250 slice::from_ref(global),
251 qh,
252 1..=2,
253 GlobalData,
254 ) {
255 Ok(bound) => {
256 CursorShapeManagerState::Bound(CursorShapeManager::from_existing(bound))
257 }
258 Err(_) => CursorShapeManagerState::NotPresent,
259 }
260 }
261
262 let shape_device =
263 if let CursorShapeManagerState::Bound(ref bound) = self.cursor_shape_manager_state {
264 Some(bound.get_shape_device(&wl_ptr, qh))
265 } else {
266 None
267 };
268
269 Ok(ThemedPointer {
270 themes: Arc::new(Mutex::new(Themes::new(theme))),
271 pointer: wl_ptr,
272 shm: shm.clone(),
273 surface,
274 shape_device,
275 _marker: std::marker::PhantomData,
276 _surface_data: std::marker::PhantomData,
277 })
278 }
279
280 pub fn get_touch<D>(
286 &mut self,
287 qh: &QueueHandle<D>,
288 seat: &wl_seat::WlSeat,
289 ) -> Result<wl_touch::WlTouch, SeatError>
290 where
291 D: Dispatch<wl_touch::WlTouch, TouchData<()>> + TouchHandler + 'static,
292 {
293 self.get_touch_with_data(qh, seat, ())
294 }
295
296 pub fn get_touch_with_data<D, U>(
302 &mut self,
303 qh: &QueueHandle<D>,
304 seat: &wl_seat::WlSeat,
305 udata: U,
306 ) -> Result<wl_touch::WlTouch, SeatError>
307 where
308 D: Dispatch<wl_touch::WlTouch, TouchData<U>> + TouchHandler + 'static,
309 U: Send + Sync + 'static,
310 {
311 let inner =
312 self.seats.iter().find(|inner| &inner.seat == seat).ok_or(SeatError::DeadObject)?;
313
314 if !inner.data.has_touch.load(Ordering::SeqCst) {
315 return Err(SeatError::UnsupportedCapability(Capability::Touch));
316 }
317
318 let data = TouchData::new(seat.clone(), udata);
319 Ok(seat.get_touch(qh, data))
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#[derive(Debug)]
423struct SeatInner {
424 seat: wl_seat::WlSeat,
425 data: SeatData,
426}
427
428impl<D> Dispatch2<wl_seat::WlSeat, D> for SeatData
429where
430 D: SeatHandler,
431{
432 fn event(
433 &self,
434 state: &mut D,
435 seat: &wl_seat::WlSeat,
436 event: wl_seat::Event,
437 conn: &Connection,
438 qh: &QueueHandle<D>,
439 ) {
440 match event {
441 wl_seat::Event::Capabilities { capabilities } => {
442 let capabilities = wl_seat::Capability::from_bits_truncate(capabilities.into());
443
444 let keyboard = capabilities.contains(wl_seat::Capability::Keyboard);
445 let has_keyboard = self.has_keyboard.load(Ordering::SeqCst);
446 let pointer = capabilities.contains(wl_seat::Capability::Pointer);
447 let has_pointer = self.has_pointer.load(Ordering::SeqCst);
448 let touch = capabilities.contains(wl_seat::Capability::Touch);
449 let has_touch = self.has_touch.load(Ordering::SeqCst);
450
451 if keyboard != has_keyboard {
453 self.has_keyboard.store(keyboard, Ordering::SeqCst);
454
455 match keyboard {
456 true => state.new_capability(conn, qh, seat.clone(), Capability::Keyboard),
457 false => {
458 state.remove_capability(conn, qh, seat.clone(), Capability::Keyboard)
459 }
460 }
461 }
462
463 if pointer != has_pointer {
464 self.has_pointer.store(pointer, Ordering::SeqCst);
465
466 match pointer {
467 true => state.new_capability(conn, qh, seat.clone(), Capability::Pointer),
468 false => {
469 state.remove_capability(conn, qh, seat.clone(), Capability::Pointer)
470 }
471 }
472 }
473
474 if touch != has_touch {
475 self.has_touch.store(touch, Ordering::SeqCst);
476
477 match touch {
478 true => state.new_capability(conn, qh, seat.clone(), Capability::Touch),
479 false => state.remove_capability(conn, qh, seat.clone(), Capability::Touch),
480 }
481 }
482 }
483
484 wl_seat::Event::Name { name } => {
485 *self.name.lock().unwrap() = Some(name);
486 }
487
488 _ => unreachable!(),
489 }
490 }
491}
492
493impl<D> RegistryHandler<D> for SeatState
494where
495 D: Dispatch<wl_seat::WlSeat, SeatData> + SeatHandler + ProvidesRegistryState + 'static,
496{
497 fn new_global(
498 state: &mut D,
499 conn: &Connection,
500 qh: &QueueHandle<D>,
501 name: u32,
502 interface: &str,
503 _: u32,
504 ) {
505 if interface == wl_seat::WlSeat::interface().name {
506 let seat = state
507 .registry()
508 .bind_specific(
509 qh,
510 name,
511 1..=7,
512 SeatData {
513 has_keyboard: Arc::new(AtomicBool::new(false)),
514 has_pointer: Arc::new(AtomicBool::new(false)),
515 has_touch: Arc::new(AtomicBool::new(false)),
516 name: Arc::new(Mutex::new(None)),
517 id: name,
518 },
519 )
520 .expect("failed to bind global");
521
522 let data = seat.data::<SeatData>().unwrap().clone();
523
524 state.seat_state().seats.push(SeatInner { seat: seat.clone(), data });
525 state.new_seat(conn, qh, seat);
526 }
527 }
528
529 fn remove_global(
530 state: &mut D,
531 conn: &Connection,
532 qh: &QueueHandle<D>,
533 name: u32,
534 interface: &str,
535 ) {
536 if interface == wl_seat::WlSeat::interface().name {
537 if let Some(seat) = state.seat_state().seats.iter().find(|inner| inner.data.id == name)
538 {
539 let seat = seat.seat.clone();
540
541 state.remove_seat(conn, qh, seat);
542 state.seat_state().seats.retain(|inner| inner.data.id != name);
543 }
544 }
545 }
546}