1use std::{
2 collections::{hash_map::Entry, HashMap},
3 env, iter, mem,
4 sync::{Arc, Mutex},
5};
6
7use wayland_backend::{client::InvalidId, smallvec::SmallVec};
8use wayland_client::{
9 protocol::{
10 wl_pointer::{self, WlPointer},
11 wl_seat::WlSeat,
12 wl_shm::WlShm,
13 wl_surface::WlSurface,
14 },
15 Connection, Dispatch, Proxy, QueueHandle, WEnum,
16};
17use wayland_cursor::{Cursor, CursorTheme};
18use wayland_protocols::wp::cursor_shape::v1::client::wp_cursor_shape_device_v1::WpCursorShapeDeviceV1;
19
20use crate::{
21 compositor::{SurfaceData, SurfaceDataExt},
22 error::GlobalError,
23};
24
25use super::SeatState;
26
27#[doc(inline)]
28pub use cursor_icon::{CursorIcon, ParseError as CursorIconParseError};
29
30pub mod cursor_shape;
31
32use cursor_shape::cursor_icon_to_shape;
33
34pub const BTN_LEFT: u32 = 0x110;
36pub const BTN_RIGHT: u32 = 0x111;
37pub const BTN_MIDDLE: u32 = 0x112;
38pub const BTN_SIDE: u32 = 0x113;
40pub const BTN_EXTRA: u32 = 0x114;
42
43pub const BTN_FORWARD: u32 = 0x115;
45pub const BTN_BACK: u32 = 0x116;
47pub const BTN_TASK: u32 = 0x117;
48
49#[derive(Default, Debug, Clone, Copy, PartialEq)]
51pub struct AxisScroll {
52 pub absolute: f64,
54
55 pub discrete: i32,
62
63 pub value120: i32,
65
66 pub relative_direction: Option<wl_pointer::AxisRelativeDirection>,
68
69 pub stop: bool,
73}
74
75impl AxisScroll {
76 pub fn is_none(&self) -> bool {
78 *self == Self::default()
79 }
80
81 fn merge(&self, other: &Self) -> Option<Self> {
83 let direction = match (self.relative_direction, other.relative_direction) {
87 (None, other) | (other, None) => other,
88 (Some(one), Some(other)) => {
89 if one != other {
90 return None;
91 } else {
92 Some(one)
93 }
94 }
95 };
96
97 let mut ret = *self;
98 ret.absolute += other.absolute;
99 ret.discrete += other.discrete;
100 ret.value120 += other.value120;
101 ret.relative_direction = direction;
102 ret.stop |= other.stop;
103 Some(ret)
104 }
105}
106
107#[derive(Debug, Clone)]
109pub struct PointerEvent {
110 pub surface: WlSurface,
111 pub position: (f64, f64),
112 pub kind: PointerEventKind,
113}
114
115#[derive(Debug, Clone)]
116pub enum PointerEventKind {
117 Enter {
118 serial: u32,
119 },
120 Leave {
121 serial: u32,
122 },
123 Motion {
124 time: u32,
125 },
126 Press {
127 time: u32,
128 button: u32,
129 serial: u32,
130 },
131 Release {
132 time: u32,
133 button: u32,
134 serial: u32,
135 },
136 Axis {
137 time: u32,
138 horizontal: AxisScroll,
139 vertical: AxisScroll,
140 source: Option<wl_pointer::AxisSource>,
141 },
142}
143
144pub trait PointerHandler: Sized {
145 fn pointer_frame(
152 &mut self,
153 conn: &Connection,
154 qh: &QueueHandle<Self>,
155 pointer: &WlPointer,
156 events: &[PointerEvent],
157 );
158}
159
160#[derive(Debug)]
161pub struct PointerData {
162 seat: WlSeat,
163 pub(crate) inner: Mutex<PointerDataInner>,
164}
165
166impl PointerData {
167 pub fn new(seat: WlSeat) -> Self {
168 Self { seat, inner: Default::default() }
169 }
170
171 pub fn seat(&self) -> &WlSeat {
173 &self.seat
174 }
175
176 pub fn latest_enter_serial(&self) -> Option<u32> {
178 self.inner.lock().unwrap().latest_enter
179 }
180
181 pub fn latest_button_serial(&self) -> Option<u32> {
184 self.inner.lock().unwrap().latest_btn
185 }
186}
187
188pub trait PointerDataExt: Send + Sync {
189 fn pointer_data(&self) -> &PointerData;
190}
191
192impl PointerDataExt for PointerData {
193 fn pointer_data(&self) -> &PointerData {
194 self
195 }
196}
197
198#[macro_export]
199macro_rules! delegate_pointer {
200 ($(@<$( $lt:tt $( : $clt:tt $(+ $dlt:tt )* )? ),+>)? $ty: ty) => {
201 $crate::delegate_pointer!(@{ $(@< $( $lt $( : $clt $(+ $dlt )* )? ),+ >)? $ty }; pointer: []);
202 $crate::delegate_pointer!(@{ $(@< $( $lt $( : $clt $(+ $dlt )* )? ),+ >)? $ty }; pointer-only: $crate::seat::pointer::PointerData);
203 };
204 ($(@<$( $lt:tt $( : $clt:tt $(+ $dlt:tt )* )? ),+>)? $ty: ty, pointer: [$($pointer_data:ty),* $(,)?]) => {
205 $crate::delegate_pointer!(@{ $(@< $( $lt $( : $clt $(+ $dlt )* )? ),+ >)? $ty }; pointer: [ $($pointer_data),* ]);
206 };
207 (@{$($ty:tt)*}; pointer: []) => {
208 $crate::reexports::client::delegate_dispatch!($($ty)*:
209 [
210 $crate::reexports::protocols::wp::cursor_shape::v1::client::wp_cursor_shape_manager_v1::WpCursorShapeManagerV1: $crate::globals::GlobalData
211 ] => $crate::seat::pointer::cursor_shape::CursorShapeManager
212 );
213 $crate::reexports::client::delegate_dispatch!($($ty)*:
214 [
215 $crate::reexports::protocols::wp::cursor_shape::v1::client::wp_cursor_shape_device_v1::WpCursorShapeDeviceV1: $crate::globals::GlobalData
216 ] => $crate::seat::pointer::cursor_shape::CursorShapeManager
217 );
218 };
219 (@{$($ty:tt)*}; pointer-only: $pointer_data:ty) => {
220 $crate::reexports::client::delegate_dispatch!($($ty)*:
221 [
222 $crate::reexports::client::protocol::wl_pointer::WlPointer: $pointer_data
223 ] => $crate::seat::SeatState
224 );
225 };
226 (@$ty:tt; pointer: [$($pointer:ty),*]) => {
227 $crate::delegate_pointer!(@$ty; pointer: []);
228 $( $crate::delegate_pointer!(@$ty; pointer-only: $pointer); )*
229 }
230}
231
232#[derive(Debug, Default)]
233pub(crate) struct PointerDataInner {
234 pub(crate) surface: Option<WlSurface>,
236 pub(crate) position: (f64, f64),
238
239 pub(crate) pending: SmallVec<[PointerEvent; 3]>,
241
242 pub(crate) latest_enter: Option<u32>,
244
245 pub(crate) latest_btn: Option<u32>,
247}
248
249impl<D, U> Dispatch<WlPointer, U, D> for SeatState
250where
251 D: Dispatch<WlPointer, U> + PointerHandler,
252 U: PointerDataExt,
253{
254 fn event(
255 data: &mut D,
256 pointer: &WlPointer,
257 event: wl_pointer::Event,
258 udata: &U,
259 conn: &Connection,
260 qh: &QueueHandle<D>,
261 ) {
262 let udata = udata.pointer_data();
263 let mut guard = udata.inner.lock().unwrap();
264 let mut leave_surface = None;
265 let kind = match event {
266 wl_pointer::Event::Enter { surface, surface_x, surface_y, serial } => {
267 guard.surface = Some(surface);
268 guard.position = (surface_x, surface_y);
269 guard.latest_enter.replace(serial);
270
271 PointerEventKind::Enter { serial }
272 }
273
274 wl_pointer::Event::Leave { surface, serial } => {
275 if guard.surface.as_ref() == Some(&surface) {
276 guard.surface = None;
277 }
278 leave_surface = Some(surface);
279
280 PointerEventKind::Leave { serial }
281 }
282
283 wl_pointer::Event::Motion { time, surface_x, surface_y } => {
284 guard.position = (surface_x, surface_y);
285
286 PointerEventKind::Motion { time }
287 }
288
289 wl_pointer::Event::Button { time, button, state, serial } => {
290 guard.latest_btn.replace(serial);
291 match state {
292 WEnum::Value(wl_pointer::ButtonState::Pressed) => {
293 PointerEventKind::Press { time, button, serial }
294 }
295 WEnum::Value(wl_pointer::ButtonState::Released) => {
296 PointerEventKind::Release { time, button, serial }
297 }
298 WEnum::Unknown(unknown) => {
299 log::warn!(target: "sctk", "{}: invalid pointer button state: {:x}", pointer.id(), unknown);
300 return;
301 }
302 _ => unreachable!(),
303 }
304 }
305 wl_pointer::Event::Axis { time, axis, value } => match axis {
307 WEnum::Value(axis) => {
308 let (mut horizontal, mut vertical) = <(AxisScroll, AxisScroll)>::default();
309 match axis {
310 wl_pointer::Axis::VerticalScroll => {
311 vertical.absolute = value;
312 }
313 wl_pointer::Axis::HorizontalScroll => {
314 horizontal.absolute = value;
315 }
316 _ => unreachable!(),
317 };
318
319 PointerEventKind::Axis { time, horizontal, vertical, source: None }
320 }
321 WEnum::Unknown(unknown) => {
322 log::warn!(target: "sctk", "{}: invalid pointer axis: {:x}", pointer.id(), unknown);
323 return;
324 }
325 },
326
327 wl_pointer::Event::AxisSource { axis_source } => match axis_source {
328 WEnum::Value(source) => PointerEventKind::Axis {
329 horizontal: AxisScroll::default(),
330 vertical: AxisScroll::default(),
331 source: Some(source),
332 time: 0,
333 },
334 WEnum::Unknown(unknown) => {
335 log::warn!(target: "sctk", "unknown pointer axis source: {:x}", unknown);
336 return;
337 }
338 },
339
340 wl_pointer::Event::AxisStop { time, axis } => match axis {
341 WEnum::Value(axis) => {
342 let (mut horizontal, mut vertical) = <(AxisScroll, AxisScroll)>::default();
343 match axis {
344 wl_pointer::Axis::VerticalScroll => vertical.stop = true,
345 wl_pointer::Axis::HorizontalScroll => horizontal.stop = true,
346
347 _ => unreachable!(),
348 }
349
350 PointerEventKind::Axis { time, horizontal, vertical, source: None }
351 }
352
353 WEnum::Unknown(unknown) => {
354 log::warn!(target: "sctk", "{}: invalid pointer axis: {:x}", pointer.id(), unknown);
355 return;
356 }
357 },
358
359 wl_pointer::Event::AxisDiscrete { axis, discrete } => match axis {
360 WEnum::Value(axis) => {
361 let (mut horizontal, mut vertical) = <(AxisScroll, AxisScroll)>::default();
362 match axis {
363 wl_pointer::Axis::VerticalScroll => {
364 vertical.discrete = discrete;
365 }
366
367 wl_pointer::Axis::HorizontalScroll => {
368 horizontal.discrete = discrete;
369 }
370
371 _ => unreachable!(),
372 };
373
374 PointerEventKind::Axis { time: 0, horizontal, vertical, source: None }
375 }
376
377 WEnum::Unknown(unknown) => {
378 log::warn!(target: "sctk", "{}: invalid pointer axis: {:x}", pointer.id(), unknown);
379 return;
380 }
381 },
382
383 wl_pointer::Event::AxisValue120 { axis, value120 } => match axis {
384 WEnum::Value(axis) => {
385 let (mut horizontal, mut vertical) = <(AxisScroll, AxisScroll)>::default();
386 match axis {
387 wl_pointer::Axis::VerticalScroll => {
388 vertical.value120 = value120;
389 }
390
391 wl_pointer::Axis::HorizontalScroll => {
392 horizontal.value120 = value120;
393 }
394
395 _ => unreachable!(),
396 };
397
398 PointerEventKind::Axis { time: 0, horizontal, vertical, source: None }
399 }
400 WEnum::Unknown(unknown) => {
401 log::warn!(target: "sctk", "{}: invalid pointer axis: {:x}", pointer.id(), unknown);
402 return;
403 }
404 },
405
406 wl_pointer::Event::AxisRelativeDirection { axis, direction } => {
407 let direction = match direction {
408 WEnum::Value(dir) => Some(dir),
409 WEnum::Unknown(unknown) => {
410 log::warn!(target: "sctk", "{}: invalid axis direction: {:x}", pointer.id(), unknown);
411 return;
412 }
413 };
414 match axis {
415 WEnum::Value(axis) => {
416 let (mut horizontal, mut vertical) = <(AxisScroll, AxisScroll)>::default();
417 match axis {
418 wl_pointer::Axis::VerticalScroll => {
419 vertical.relative_direction = direction;
420 }
421
422 wl_pointer::Axis::HorizontalScroll => {
423 horizontal.relative_direction = direction;
424 }
425
426 _ => unreachable!(),
427 };
428
429 PointerEventKind::Axis { time: 0, horizontal, vertical, source: None }
430 }
431
432 WEnum::Unknown(unknown) => {
433 log::warn!(target: "sctk", "{}: invalid pointer axis: {:x}", pointer.id(), unknown);
434 return;
435 }
436 }
437 }
438
439 wl_pointer::Event::Frame => {
440 let pending = mem::take(&mut guard.pending);
441 drop(guard);
442 if !pending.is_empty() {
443 data.pointer_frame(conn, qh, pointer, &pending);
444 }
445 return;
446 }
447
448 _ => unreachable!(),
449 };
450
451 let surface = match (leave_surface, &guard.surface) {
452 (Some(surface), _) => surface,
453 (None, Some(surface)) => surface.clone(),
454 (None, None) => {
455 log::warn!(target: "sctk", "{}: got pointer event {:?} without an entered surface", pointer.id(), kind);
456 return;
457 }
458 };
459
460 let event = PointerEvent { surface, position: guard.position, kind };
461
462 if pointer.version() < 5 {
463 drop(guard);
464 data.pointer_frame(conn, qh, pointer, &[event]);
466 } else {
467 if let (
470 Some(PointerEvent {
471 kind:
472 PointerEventKind::Axis { time: ot, horizontal: oh, vertical: ov, source: os },
473 ..
474 }),
475 PointerEvent {
476 kind:
477 PointerEventKind::Axis { time: nt, horizontal: nh, vertical: nv, source: ns },
478 ..
479 },
480 ) = (guard.pending.last_mut(), &event)
481 {
482 if *ot == 0 {
484 *ot = *nt;
485 }
486 let nh = oh.merge(nh);
487 let nv = ov.merge(nv);
488 if let (Some(nh), Some(nv)) = (nh, nv) {
490 *oh = nh;
491 *ov = nv;
492 *os = os.or(*ns);
493 return;
494 }
495 }
496
497 guard.pending.push(event);
498 }
499 }
500}
501
502#[derive(Debug)]
504pub struct ThemedPointer<U = PointerData, S = SurfaceData> {
505 pub(super) themes: Arc<Mutex<Themes>>,
506 pub(super) pointer: WlPointer,
508 pub(super) shm: WlShm,
509 pub(super) surface: WlSurface,
511 pub(super) shape_device: Option<WpCursorShapeDeviceV1>,
512 pub(super) _marker: std::marker::PhantomData<U>,
513 pub(super) _surface_data: std::marker::PhantomData<S>,
514}
515
516impl<U: PointerDataExt + 'static, S: SurfaceDataExt + 'static> ThemedPointer<U, S> {
517 pub fn set_cursor(&self, conn: &Connection, icon: CursorIcon) -> Result<(), PointerThemeError> {
521 let serial = match self
522 .pointer
523 .data::<U>()
524 .and_then(|data| data.pointer_data().latest_enter_serial())
525 {
526 Some(serial) => serial,
527 None => return Err(PointerThemeError::MissingEnterSerial),
528 };
529
530 if let Some(shape_device) = self.shape_device.as_ref() {
531 shape_device.set_shape(serial, cursor_icon_to_shape(icon, shape_device.version()));
532 Ok(())
533 } else {
534 self.set_cursor_legacy(conn, serial, icon)
535 }
536 }
537
538 fn set_cursor_legacy(
541 &self,
542 conn: &Connection,
543 serial: u32,
544 icon: CursorIcon,
545 ) -> Result<(), PointerThemeError> {
546 let mut themes = self.themes.lock().unwrap();
547
548 let scale = self.surface.data::<S>().unwrap().surface_data().scale_factor();
549 for cursor_icon_name in iter::once(&icon.name()).chain(icon.alt_names().iter()) {
550 if let Some(cursor) = themes
551 .get_cursor(conn, cursor_icon_name, scale as u32, &self.shm)
552 .map_err(PointerThemeError::InvalidId)?
553 {
554 let image = &cursor[0];
555 let (w, h) = image.dimensions();
556 let (hx, hy) = image.hotspot();
557
558 self.surface.set_buffer_scale(scale);
559 self.surface.attach(Some(image), 0, 0);
560
561 if self.surface.version() >= 4 {
562 self.surface.damage_buffer(0, 0, w as i32, h as i32);
563 } else {
564 self.surface.damage(0, 0, w as i32 / scale, h as i32 / scale);
566 }
567
568 self.surface.commit();
570
571 self.pointer.set_cursor(
573 serial,
574 Some(&self.surface),
575 hx as i32 / scale,
576 hy as i32 / scale,
577 );
578
579 return Ok(());
580 }
581 }
582
583 Err(PointerThemeError::CursorNotFound)
584 }
585
586 pub fn hide_cursor(&self) -> Result<(), PointerThemeError> {
590 let data = self.pointer.data::<U>();
591 if let Some(serial) = data.and_then(|data| data.pointer_data().latest_enter_serial()) {
592 self.pointer.set_cursor(serial, None, 0, 0);
593 Ok(())
594 } else {
595 Err(PointerThemeError::MissingEnterSerial)
596 }
597 }
598
599 pub fn pointer(&self) -> &WlPointer {
601 &self.pointer
602 }
603
604 pub fn surface(&self) -> &WlSurface {
606 &self.surface
607 }
608}
609
610impl<U, S> Drop for ThemedPointer<U, S> {
611 fn drop(&mut self) {
612 if let Some(shape_device) = self.shape_device.take() {
613 shape_device.destroy();
614 }
615
616 if self.pointer.version() >= 3 {
617 self.pointer.release();
618 }
619 self.surface.destroy();
620 }
621}
622
623#[derive(Debug)]
625pub enum ThemeSpec<'a> {
626 Named {
628 name: &'a str,
630
631 size: u32,
636 },
637
638 System,
644}
645
646impl Default for ThemeSpec<'_> {
647 fn default() -> Self {
648 Self::System
649 }
650}
651
652#[derive(Debug, thiserror::Error)]
654pub enum PointerThemeError {
655 #[error("Invalid ObjectId")]
657 InvalidId(InvalidId),
658
659 #[error("A Global Error occured")]
661 GlobalError(GlobalError),
662
663 #[error("Cursor not found")]
665 CursorNotFound,
666
667 #[error("Missing enter event serial")]
669 MissingEnterSerial,
670}
671
672#[derive(Debug)]
673pub(crate) struct Themes {
674 name: String,
675 size: u32,
676 themes: HashMap<u32, CursorTheme>,
678}
679
680impl Default for Themes {
681 fn default() -> Self {
682 Themes::new(ThemeSpec::default())
683 }
684}
685
686impl Themes {
687 pub(crate) fn new(spec: ThemeSpec) -> Themes {
688 let (name, size) = match spec {
689 ThemeSpec::Named { name, size } => (name.into(), size),
690 ThemeSpec::System => {
691 let name = env::var("XCURSOR_THEME").ok().unwrap_or_else(|| "default".into());
692 let size = env::var("XCURSOR_SIZE").ok().and_then(|s| s.parse().ok()).unwrap_or(24);
693 (name, size)
694 }
695 };
696
697 Themes { name, size, themes: HashMap::new() }
698 }
699
700 fn get_cursor(
701 &mut self,
702 conn: &Connection,
703 name: &str,
704 scale: u32,
705 shm: &WlShm,
706 ) -> Result<Option<&Cursor>, InvalidId> {
707 if let Entry::Vacant(e) = self.themes.entry(scale) {
709 let theme = CursorTheme::load_from_name(
711 conn,
712 shm.clone(), &self.name,
714 self.size * scale,
715 )?;
716
717 e.insert(theme);
718 }
719
720 let theme = self.themes.get_mut(&scale).unwrap();
721
722 Ok(theme.get_cursor(name))
723 }
724}