1use std::{
2 collections::{hash_map::Entry, HashMap},
3 env, iter, mem,
4 sync::{Arc, Mutex},
5};
6
7use wayland_client::backend::{smallvec::SmallVec, InvalidId};
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, 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::{compositor::SurfaceData, dispatch2::Dispatch2, error::GlobalError};
21
22#[doc(inline)]
23pub use cursor_icon::{CursorIcon, ParseError as CursorIconParseError};
24
25pub mod cursor_shape;
26
27use cursor_shape::cursor_icon_to_shape;
28
29pub const BTN_LEFT: u32 = 0x110;
31pub const BTN_RIGHT: u32 = 0x111;
32pub const BTN_MIDDLE: u32 = 0x112;
33pub const BTN_SIDE: u32 = 0x113;
35pub const BTN_EXTRA: u32 = 0x114;
37
38pub const BTN_FORWARD: u32 = 0x115;
40pub const BTN_BACK: u32 = 0x116;
42pub const BTN_TASK: u32 = 0x117;
43
44#[derive(Default, Debug, Clone, Copy, PartialEq)]
46pub struct AxisScroll {
47 pub absolute: f64,
49
50 pub discrete: i32,
57
58 pub value120: i32,
60
61 pub relative_direction: Option<wl_pointer::AxisRelativeDirection>,
63
64 pub stop: bool,
68}
69
70impl AxisScroll {
71 pub fn is_none(&self) -> bool {
73 *self == Self::default()
74 }
75
76 fn merge(&self, other: &Self) -> Option<Self> {
78 let direction = match (self.relative_direction, other.relative_direction) {
82 (None, other) | (other, None) => other,
83 (Some(one), Some(other)) => {
84 if one != other {
85 return None;
86 } else {
87 Some(one)
88 }
89 }
90 };
91
92 let mut ret = *self;
93 ret.absolute += other.absolute;
94 ret.discrete += other.discrete;
95 ret.value120 += other.value120;
96 ret.relative_direction = direction;
97 ret.stop |= other.stop;
98 Some(ret)
99 }
100}
101
102#[derive(Debug, Clone)]
104pub struct PointerEvent {
105 pub surface: WlSurface,
106 pub position: (f64, f64),
107 pub kind: PointerEventKind,
108}
109
110#[derive(Debug, Clone)]
111pub enum PointerEventKind {
112 Enter {
113 serial: u32,
114 },
115 Leave {
116 serial: u32,
117 },
118 Motion {
119 time: u32,
120 },
121 Press {
122 time: u32,
123 button: u32,
124 serial: u32,
125 },
126 Release {
127 time: u32,
128 button: u32,
129 serial: u32,
130 },
131 Axis {
132 time: u32,
133 horizontal: AxisScroll,
134 vertical: AxisScroll,
135 source: Option<wl_pointer::AxisSource>,
136 },
137}
138
139pub trait PointerHandler: Sized {
140 fn pointer_frame(
147 &mut self,
148 conn: &Connection,
149 qh: &QueueHandle<Self>,
150 pointer: &WlPointer,
151 events: &[PointerEvent],
152 );
153}
154
155#[derive(Debug)]
156pub struct PointerData<U> {
157 seat: WlSeat,
158 pub(crate) inner: Mutex<PointerDataInner>,
159 udata: U,
160}
161
162impl<U> PointerData<U> {
163 pub fn new(seat: WlSeat, udata: U) -> Self {
164 Self { seat, inner: Default::default(), udata }
165 }
166
167 pub fn data(&self) -> &U {
168 &self.udata
169 }
170
171 pub fn data_mut(&mut self) -> &mut U {
172 &mut self.udata
173 }
174
175 pub fn seat(&self) -> &WlSeat {
177 &self.seat
178 }
179
180 pub fn latest_enter_serial(&self) -> Option<u32> {
182 self.inner.lock().unwrap().latest_enter
183 }
184
185 pub fn latest_button_serial(&self) -> Option<u32> {
188 self.inner.lock().unwrap().latest_btn
189 }
190}
191
192#[derive(Debug, Default)]
193pub(crate) struct PointerDataInner {
194 pub(crate) surface: Option<WlSurface>,
196 pub(crate) position: (f64, f64),
198
199 pub(crate) pending: SmallVec<[PointerEvent; 3]>,
201
202 pub(crate) latest_enter: Option<u32>,
204
205 pub(crate) latest_btn: Option<u32>,
207}
208
209impl<D, U> Dispatch2<WlPointer, D> for PointerData<U>
210where
211 D: PointerHandler,
212 U: Send + Sync + 'static,
213{
214 fn event(
215 &self,
216 data: &mut D,
217 pointer: &WlPointer,
218 event: wl_pointer::Event,
219 conn: &Connection,
220 qh: &QueueHandle<D>,
221 ) {
222 let mut guard = self.inner.lock().unwrap();
223 let mut leave_surface = None;
224 let kind = match event {
225 wl_pointer::Event::Enter { surface, surface_x, surface_y, serial } => {
226 guard.surface = Some(surface);
227 guard.position = (surface_x, surface_y);
228 guard.latest_enter.replace(serial);
229
230 PointerEventKind::Enter { serial }
231 }
232
233 wl_pointer::Event::Leave { surface, serial } => {
234 if guard.surface.as_ref() == Some(&surface) {
235 guard.surface = None;
236 }
237 leave_surface = Some(surface);
238
239 PointerEventKind::Leave { serial }
240 }
241
242 wl_pointer::Event::Motion { time, surface_x, surface_y } => {
243 guard.position = (surface_x, surface_y);
244
245 PointerEventKind::Motion { time }
246 }
247
248 wl_pointer::Event::Button { time, button, state, serial } => {
249 guard.latest_btn.replace(serial);
250 match state {
251 WEnum::Value(wl_pointer::ButtonState::Pressed) => {
252 PointerEventKind::Press { time, button, serial }
253 }
254 WEnum::Value(wl_pointer::ButtonState::Released) => {
255 PointerEventKind::Release { time, button, serial }
256 }
257 WEnum::Unknown(unknown) => {
258 log::warn!(target: "sctk", "{}: invalid pointer button state: {:x}", pointer.id(), unknown);
259 return;
260 }
261 _ => unreachable!(),
262 }
263 }
264 wl_pointer::Event::Axis { time, axis, value } => match axis {
266 WEnum::Value(axis) => {
267 let (mut horizontal, mut vertical) = <(AxisScroll, AxisScroll)>::default();
268 match axis {
269 wl_pointer::Axis::VerticalScroll => {
270 vertical.absolute = value;
271 }
272 wl_pointer::Axis::HorizontalScroll => {
273 horizontal.absolute = value;
274 }
275 _ => unreachable!(),
276 };
277
278 PointerEventKind::Axis { time, horizontal, vertical, source: None }
279 }
280 WEnum::Unknown(unknown) => {
281 log::warn!(target: "sctk", "{}: invalid pointer axis: {:x}", pointer.id(), unknown);
282 return;
283 }
284 },
285
286 wl_pointer::Event::AxisSource { axis_source } => match axis_source {
287 WEnum::Value(source) => PointerEventKind::Axis {
288 horizontal: AxisScroll::default(),
289 vertical: AxisScroll::default(),
290 source: Some(source),
291 time: 0,
292 },
293 WEnum::Unknown(unknown) => {
294 log::warn!(target: "sctk", "unknown pointer axis source: {:x}", unknown);
295 return;
296 }
297 },
298
299 wl_pointer::Event::AxisStop { time, axis } => match axis {
300 WEnum::Value(axis) => {
301 let (mut horizontal, mut vertical) = <(AxisScroll, AxisScroll)>::default();
302 match axis {
303 wl_pointer::Axis::VerticalScroll => vertical.stop = true,
304 wl_pointer::Axis::HorizontalScroll => horizontal.stop = true,
305
306 _ => unreachable!(),
307 }
308
309 PointerEventKind::Axis { time, horizontal, vertical, source: None }
310 }
311
312 WEnum::Unknown(unknown) => {
313 log::warn!(target: "sctk", "{}: invalid pointer axis: {:x}", pointer.id(), unknown);
314 return;
315 }
316 },
317
318 wl_pointer::Event::AxisDiscrete { axis, discrete } => match axis {
319 WEnum::Value(axis) => {
320 let (mut horizontal, mut vertical) = <(AxisScroll, AxisScroll)>::default();
321 match axis {
322 wl_pointer::Axis::VerticalScroll => {
323 vertical.discrete = discrete;
324 }
325
326 wl_pointer::Axis::HorizontalScroll => {
327 horizontal.discrete = discrete;
328 }
329
330 _ => unreachable!(),
331 };
332
333 PointerEventKind::Axis { time: 0, horizontal, vertical, source: None }
334 }
335
336 WEnum::Unknown(unknown) => {
337 log::warn!(target: "sctk", "{}: invalid pointer axis: {:x}", pointer.id(), unknown);
338 return;
339 }
340 },
341
342 wl_pointer::Event::AxisValue120 { axis, value120 } => match axis {
343 WEnum::Value(axis) => {
344 let (mut horizontal, mut vertical) = <(AxisScroll, AxisScroll)>::default();
345 match axis {
346 wl_pointer::Axis::VerticalScroll => {
347 vertical.value120 = value120;
348 }
349
350 wl_pointer::Axis::HorizontalScroll => {
351 horizontal.value120 = value120;
352 }
353
354 _ => unreachable!(),
355 };
356
357 PointerEventKind::Axis { time: 0, horizontal, vertical, source: None }
358 }
359 WEnum::Unknown(unknown) => {
360 log::warn!(target: "sctk", "{}: invalid pointer axis: {:x}", pointer.id(), unknown);
361 return;
362 }
363 },
364
365 wl_pointer::Event::AxisRelativeDirection { axis, direction } => {
366 let direction = match direction {
367 WEnum::Value(dir) => Some(dir),
368 WEnum::Unknown(unknown) => {
369 log::warn!(target: "sctk", "{}: invalid axis direction: {:x}", pointer.id(), unknown);
370 return;
371 }
372 };
373 match axis {
374 WEnum::Value(axis) => {
375 let (mut horizontal, mut vertical) = <(AxisScroll, AxisScroll)>::default();
376 match axis {
377 wl_pointer::Axis::VerticalScroll => {
378 vertical.relative_direction = direction;
379 }
380
381 wl_pointer::Axis::HorizontalScroll => {
382 horizontal.relative_direction = direction;
383 }
384
385 _ => unreachable!(),
386 };
387
388 PointerEventKind::Axis { time: 0, horizontal, vertical, source: None }
389 }
390
391 WEnum::Unknown(unknown) => {
392 log::warn!(target: "sctk", "{}: invalid pointer axis: {:x}", pointer.id(), unknown);
393 return;
394 }
395 }
396 }
397
398 wl_pointer::Event::Frame => {
399 let pending = mem::take(&mut guard.pending);
400 drop(guard);
401 if !pending.is_empty() {
402 data.pointer_frame(conn, qh, pointer, &pending);
403 }
404 return;
405 }
406
407 _ => unreachable!(),
408 };
409
410 let surface = match (leave_surface, &guard.surface) {
411 (Some(surface), _) => surface,
412 (None, Some(surface)) => surface.clone(),
413 (None, None) => {
414 log::warn!(target: "sctk", "{}: got pointer event {:?} without an entered surface", pointer.id(), kind);
415 return;
416 }
417 };
418
419 let event = PointerEvent { surface, position: guard.position, kind };
420
421 if pointer.version() < 5 {
422 drop(guard);
423 data.pointer_frame(conn, qh, pointer, &[event]);
425 } else {
426 if let (
429 Some(PointerEvent {
430 kind:
431 PointerEventKind::Axis { time: ot, horizontal: oh, vertical: ov, source: os },
432 ..
433 }),
434 PointerEvent {
435 kind:
436 PointerEventKind::Axis { time: nt, horizontal: nh, vertical: nv, source: ns },
437 ..
438 },
439 ) = (guard.pending.last_mut(), &event)
440 {
441 if *ot == 0 {
443 *ot = *nt;
444 }
445 let nh = oh.merge(nh);
446 let nv = ov.merge(nv);
447 if let (Some(nh), Some(nv)) = (nh, nv) {
449 *oh = nh;
450 *ov = nv;
451 *os = os.or(*ns);
452 return;
453 }
454 }
455
456 guard.pending.push(event);
457 }
458 }
459}
460
461#[derive(Debug)]
463pub struct ThemedPointer<U = (), S = ()> {
464 pub(super) themes: Arc<Mutex<Themes>>,
465 pub(super) pointer: WlPointer,
467 pub(super) shm: WlShm,
468 pub(super) surface: WlSurface,
470 pub(super) shape_device: Option<WpCursorShapeDeviceV1>,
471 pub(super) _marker: std::marker::PhantomData<U>,
472 pub(super) _surface_data: std::marker::PhantomData<S>,
473}
474
475impl<U: Send + Sync + 'static, S: Send + Sync + 'static> ThemedPointer<U, S> {
476 pub fn set_cursor(&self, conn: &Connection, icon: CursorIcon) -> Result<(), PointerThemeError> {
480 let serial =
481 match self.pointer.data::<PointerData<U>>().and_then(|data| data.latest_enter_serial())
482 {
483 Some(serial) => serial,
484 None => return Err(PointerThemeError::MissingEnterSerial),
485 };
486
487 if let Some(shape_device) = self.shape_device.as_ref() {
488 shape_device.set_shape(serial, cursor_icon_to_shape(icon, shape_device.version()));
489 Ok(())
490 } else {
491 self.set_cursor_legacy(conn, serial, icon)
492 }
493 }
494
495 fn set_cursor_legacy(
498 &self,
499 conn: &Connection,
500 serial: u32,
501 icon: CursorIcon,
502 ) -> Result<(), PointerThemeError> {
503 let mut themes = self.themes.lock().unwrap();
504
505 let scale = self.surface.data::<SurfaceData<S>>().unwrap().scale_factor();
506 for cursor_icon_name in iter::once(&icon.name()).chain(icon.alt_names().iter()) {
507 if let Some(cursor) = themes
508 .get_cursor(conn, cursor_icon_name, scale as u32, &self.shm)
509 .map_err(PointerThemeError::InvalidId)?
510 {
511 let image = &cursor[0];
512 let (w, h) = image.dimensions();
513 let (hx, hy) = image.hotspot();
514
515 self.surface.set_buffer_scale(scale);
516 self.surface.attach(Some(image), 0, 0);
517
518 if self.surface.version() >= 4 {
519 self.surface.damage_buffer(0, 0, w as i32, h as i32);
520 } else {
521 self.surface.damage(0, 0, w as i32 / scale, h as i32 / scale);
523 }
524
525 self.surface.commit();
527
528 self.pointer.set_cursor(
530 serial,
531 Some(&self.surface),
532 hx as i32 / scale,
533 hy as i32 / scale,
534 );
535
536 return Ok(());
537 }
538 }
539
540 Err(PointerThemeError::CursorNotFound)
541 }
542
543 pub fn hide_cursor(&self) -> Result<(), PointerThemeError> {
547 let data = self.pointer.data::<PointerData<U>>();
548 if let Some(serial) = data.and_then(|data| data.latest_enter_serial()) {
549 self.pointer.set_cursor(serial, None, 0, 0);
550 Ok(())
551 } else {
552 Err(PointerThemeError::MissingEnterSerial)
553 }
554 }
555
556 pub fn pointer(&self) -> &WlPointer {
558 &self.pointer
559 }
560
561 pub fn surface(&self) -> &WlSurface {
563 &self.surface
564 }
565}
566
567impl<U, S> Drop for ThemedPointer<U, S> {
568 fn drop(&mut self) {
569 if let Some(shape_device) = self.shape_device.take() {
570 shape_device.destroy();
571 }
572
573 if self.pointer.version() >= 3 {
574 self.pointer.release();
575 }
576 self.surface.destroy();
577 }
578}
579
580#[derive(Debug, Default)]
582pub enum ThemeSpec<'a> {
583 Named {
585 name: &'a str,
587
588 size: u32,
593 },
594
595 #[default]
601 System,
602}
603
604#[derive(Debug, thiserror::Error)]
606pub enum PointerThemeError {
607 #[error("Invalid ObjectId")]
609 InvalidId(InvalidId),
610
611 #[error("A Global Error occured")]
613 GlobalError(GlobalError),
614
615 #[error("Cursor not found")]
617 CursorNotFound,
618
619 #[error("Missing enter event serial")]
621 MissingEnterSerial,
622}
623
624#[derive(Debug)]
625pub(crate) struct Themes {
626 name: String,
627 size: u32,
628 themes: HashMap<u32, CursorTheme>,
630}
631
632impl Default for Themes {
633 fn default() -> Self {
634 Themes::new(ThemeSpec::default())
635 }
636}
637
638impl Themes {
639 pub(crate) fn new(spec: ThemeSpec) -> Themes {
640 let (name, size) = match spec {
641 ThemeSpec::Named { name, size } => (name.into(), size),
642 ThemeSpec::System => {
643 let name = env::var("XCURSOR_THEME").ok().unwrap_or_else(|| "default".into());
644 let size = env::var("XCURSOR_SIZE").ok().and_then(|s| s.parse().ok()).unwrap_or(24);
645 (name, size)
646 }
647 };
648
649 Themes { name, size, themes: HashMap::new() }
650 }
651
652 fn get_cursor(
653 &mut self,
654 conn: &Connection,
655 name: &str,
656 scale: u32,
657 shm: &WlShm,
658 ) -> Result<Option<&Cursor>, InvalidId> {
659 if let Entry::Vacant(e) = self.themes.entry(scale) {
661 let theme = CursorTheme::load_from_name(
663 conn,
664 shm.clone(), &self.name,
666 self.size * scale,
667 )?;
668
669 e.insert(theme);
670 }
671
672 let theme = self.themes.get_mut(&scale).unwrap();
673
674 Ok(theme.get_cursor(name))
675 }
676}