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,
60
61 pub stop: bool,
65}
66
67impl AxisScroll {
68 pub fn is_none(&self) -> bool {
70 *self == Self::default()
71 }
72
73 fn merge(&mut self, other: &Self) {
74 self.absolute += other.absolute;
75 self.discrete += other.discrete;
76 self.stop |= other.stop;
77 }
78}
79
80#[derive(Debug, Clone)]
82pub struct PointerEvent {
83 pub surface: WlSurface,
84 pub position: (f64, f64),
85 pub kind: PointerEventKind,
86}
87
88#[derive(Debug, Clone)]
89pub enum PointerEventKind {
90 Enter {
91 serial: u32,
92 },
93 Leave {
94 serial: u32,
95 },
96 Motion {
97 time: u32,
98 },
99 Press {
100 time: u32,
101 button: u32,
102 serial: u32,
103 },
104 Release {
105 time: u32,
106 button: u32,
107 serial: u32,
108 },
109 Axis {
110 time: u32,
111 horizontal: AxisScroll,
112 vertical: AxisScroll,
113 source: Option<wl_pointer::AxisSource>,
114 },
115}
116
117pub trait PointerHandler: Sized {
118 fn pointer_frame(
125 &mut self,
126 conn: &Connection,
127 qh: &QueueHandle<Self>,
128 pointer: &WlPointer,
129 events: &[PointerEvent],
130 );
131}
132
133#[derive(Debug)]
134pub struct PointerData {
135 seat: WlSeat,
136 pub(crate) inner: Mutex<PointerDataInner>,
137}
138
139impl PointerData {
140 pub fn new(seat: WlSeat) -> Self {
141 Self { seat, inner: Default::default() }
142 }
143
144 pub fn seat(&self) -> &WlSeat {
146 &self.seat
147 }
148
149 pub fn latest_enter_serial(&self) -> Option<u32> {
151 self.inner.lock().unwrap().latest_enter
152 }
153
154 pub fn latest_button_serial(&self) -> Option<u32> {
157 self.inner.lock().unwrap().latest_btn
158 }
159}
160
161pub trait PointerDataExt: Send + Sync {
162 fn pointer_data(&self) -> &PointerData;
163}
164
165impl PointerDataExt for PointerData {
166 fn pointer_data(&self) -> &PointerData {
167 self
168 }
169}
170
171#[macro_export]
172macro_rules! delegate_pointer {
173 ($(@<$( $lt:tt $( : $clt:tt $(+ $dlt:tt )* )? ),+>)? $ty: ty) => {
174 $crate::delegate_pointer!(@{ $(@< $( $lt $( : $clt $(+ $dlt )* )? ),+ >)? $ty }; pointer: []);
175 $crate::delegate_pointer!(@{ $(@< $( $lt $( : $clt $(+ $dlt )* )? ),+ >)? $ty }; pointer-only: $crate::seat::pointer::PointerData);
176 };
177 ($(@<$( $lt:tt $( : $clt:tt $(+ $dlt:tt )* )? ),+>)? $ty: ty, pointer: [$($pointer_data:ty),* $(,)?]) => {
178 $crate::delegate_pointer!(@{ $(@< $( $lt $( : $clt $(+ $dlt )* )? ),+ >)? $ty }; pointer: [ $($pointer_data),* ]);
179 };
180 (@{$($ty:tt)*}; pointer: []) => {
181 $crate::reexports::client::delegate_dispatch!($($ty)*:
182 [
183 $crate::reexports::protocols::wp::cursor_shape::v1::client::wp_cursor_shape_manager_v1::WpCursorShapeManagerV1: $crate::globals::GlobalData
184 ] => $crate::seat::pointer::cursor_shape::CursorShapeManager
185 );
186 $crate::reexports::client::delegate_dispatch!($($ty)*:
187 [
188 $crate::reexports::protocols::wp::cursor_shape::v1::client::wp_cursor_shape_device_v1::WpCursorShapeDeviceV1: $crate::globals::GlobalData
189 ] => $crate::seat::pointer::cursor_shape::CursorShapeManager
190 );
191 };
192 (@{$($ty:tt)*}; pointer-only: $pointer_data:ty) => {
193 $crate::reexports::client::delegate_dispatch!($($ty)*:
194 [
195 $crate::reexports::client::protocol::wl_pointer::WlPointer: $pointer_data
196 ] => $crate::seat::SeatState
197 );
198 };
199 (@$ty:tt; pointer: [$($pointer:ty),*]) => {
200 $crate::delegate_pointer!(@$ty; pointer: []);
201 $( $crate::delegate_pointer!(@$ty; pointer-only: $pointer); )*
202 }
203}
204
205#[derive(Debug, Default)]
206pub(crate) struct PointerDataInner {
207 pub(crate) surface: Option<WlSurface>,
209 pub(crate) position: (f64, f64),
211
212 pub(crate) pending: SmallVec<[PointerEvent; 3]>,
214
215 pub(crate) latest_enter: Option<u32>,
217
218 pub(crate) latest_btn: Option<u32>,
220}
221
222impl<D, U> Dispatch<WlPointer, U, D> for SeatState
223where
224 D: Dispatch<WlPointer, U> + PointerHandler,
225 U: PointerDataExt,
226{
227 fn event(
228 data: &mut D,
229 pointer: &WlPointer,
230 event: wl_pointer::Event,
231 udata: &U,
232 conn: &Connection,
233 qh: &QueueHandle<D>,
234 ) {
235 let udata = udata.pointer_data();
236 let mut guard = udata.inner.lock().unwrap();
237 let mut leave_surface = None;
238 let kind = match event {
239 wl_pointer::Event::Enter { surface, surface_x, surface_y, serial } => {
240 guard.surface = Some(surface);
241 guard.position = (surface_x, surface_y);
242 guard.latest_enter.replace(serial);
243
244 PointerEventKind::Enter { serial }
245 }
246
247 wl_pointer::Event::Leave { surface, serial } => {
248 if guard.surface.as_ref() == Some(&surface) {
249 guard.surface = None;
250 }
251 leave_surface = Some(surface);
252
253 PointerEventKind::Leave { serial }
254 }
255
256 wl_pointer::Event::Motion { time, surface_x, surface_y } => {
257 guard.position = (surface_x, surface_y);
258
259 PointerEventKind::Motion { time }
260 }
261
262 wl_pointer::Event::Button { time, button, state, serial } => {
263 guard.latest_btn.replace(serial);
264 match state {
265 WEnum::Value(wl_pointer::ButtonState::Pressed) => {
266 PointerEventKind::Press { time, button, serial }
267 }
268 WEnum::Value(wl_pointer::ButtonState::Released) => {
269 PointerEventKind::Release { time, button, serial }
270 }
271 WEnum::Unknown(unknown) => {
272 log::warn!(target: "sctk", "{}: invalid pointer button state: {:x}", pointer.id(), unknown);
273 return;
274 }
275 _ => unreachable!(),
276 }
277 }
278 wl_pointer::Event::Axis { time, axis, value } => match axis {
280 WEnum::Value(axis) => {
281 let (mut horizontal, mut vertical) = <(AxisScroll, AxisScroll)>::default();
282 match axis {
283 wl_pointer::Axis::VerticalScroll => {
284 vertical.absolute = value;
285 }
286 wl_pointer::Axis::HorizontalScroll => {
287 horizontal.absolute = value;
288 }
289 _ => unreachable!(),
290 };
291
292 PointerEventKind::Axis { time, horizontal, vertical, source: None }
293 }
294 WEnum::Unknown(unknown) => {
295 log::warn!(target: "sctk", "{}: invalid pointer axis: {:x}", pointer.id(), unknown);
296 return;
297 }
298 },
299
300 wl_pointer::Event::AxisSource { axis_source } => match axis_source {
301 WEnum::Value(source) => PointerEventKind::Axis {
302 horizontal: AxisScroll::default(),
303 vertical: AxisScroll::default(),
304 source: Some(source),
305 time: 0,
306 },
307 WEnum::Unknown(unknown) => {
308 log::warn!(target: "sctk", "unknown pointer axis source: {:x}", unknown);
309 return;
310 }
311 },
312
313 wl_pointer::Event::AxisStop { time, axis } => match axis {
314 WEnum::Value(axis) => {
315 let (mut horizontal, mut vertical) = <(AxisScroll, AxisScroll)>::default();
316 match axis {
317 wl_pointer::Axis::VerticalScroll => vertical.stop = true,
318 wl_pointer::Axis::HorizontalScroll => horizontal.stop = true,
319
320 _ => unreachable!(),
321 }
322
323 PointerEventKind::Axis { time, horizontal, vertical, source: None }
324 }
325
326 WEnum::Unknown(unknown) => {
327 log::warn!(target: "sctk", "{}: invalid pointer axis: {:x}", pointer.id(), unknown);
328 return;
329 }
330 },
331
332 wl_pointer::Event::AxisDiscrete { axis, discrete } => match axis {
333 WEnum::Value(axis) => {
334 let (mut horizontal, mut vertical) = <(AxisScroll, AxisScroll)>::default();
335 match axis {
336 wl_pointer::Axis::VerticalScroll => {
337 vertical.discrete = discrete;
338 }
339
340 wl_pointer::Axis::HorizontalScroll => {
341 horizontal.discrete = discrete;
342 }
343
344 _ => unreachable!(),
345 };
346
347 PointerEventKind::Axis { time: 0, horizontal, vertical, source: None }
348 }
349
350 WEnum::Unknown(unknown) => {
351 log::warn!(target: "sctk", "{}: invalid pointer axis: {:x}", pointer.id(), unknown);
352 return;
353 }
354 },
355
356 wl_pointer::Event::Frame => {
357 let pending = mem::take(&mut guard.pending);
358 drop(guard);
359 if !pending.is_empty() {
360 data.pointer_frame(conn, qh, pointer, &pending);
361 }
362 return;
363 }
364
365 _ => unreachable!(),
366 };
367
368 let surface = match (leave_surface, &guard.surface) {
369 (Some(surface), _) => surface,
370 (None, Some(surface)) => surface.clone(),
371 (None, None) => {
372 log::warn!(target: "sctk", "{}: got pointer event {:?} without an entered surface", pointer.id(), kind);
373 return;
374 }
375 };
376
377 let event = PointerEvent { surface, position: guard.position, kind };
378
379 if pointer.version() < 5 {
380 drop(guard);
381 data.pointer_frame(conn, qh, pointer, &[event]);
383 } else {
384 if let (
387 Some(PointerEvent {
388 kind:
389 PointerEventKind::Axis { time: ot, horizontal: oh, vertical: ov, source: os },
390 ..
391 }),
392 PointerEvent {
393 kind:
394 PointerEventKind::Axis { time: nt, horizontal: nh, vertical: nv, source: ns },
395 ..
396 },
397 ) = (guard.pending.last_mut(), &event)
398 {
399 if *ot == 0 {
401 *ot = *nt;
402 }
403 oh.merge(nh);
404 ov.merge(nv);
405 *os = os.or(*ns);
406 return;
407 }
408
409 guard.pending.push(event);
410 }
411 }
412}
413
414#[derive(Debug)]
416pub struct ThemedPointer<U = PointerData, S = SurfaceData> {
417 pub(super) themes: Arc<Mutex<Themes>>,
418 pub(super) pointer: WlPointer,
420 pub(super) shm: WlShm,
421 pub(super) surface: WlSurface,
423 pub(super) shape_device: Option<WpCursorShapeDeviceV1>,
424 pub(super) _marker: std::marker::PhantomData<U>,
425 pub(super) _surface_data: std::marker::PhantomData<S>,
426}
427
428impl<U: PointerDataExt + 'static, S: SurfaceDataExt + 'static> ThemedPointer<U, S> {
429 pub fn set_cursor(&self, conn: &Connection, icon: CursorIcon) -> Result<(), PointerThemeError> {
433 let serial = match self
434 .pointer
435 .data::<U>()
436 .and_then(|data| data.pointer_data().latest_enter_serial())
437 {
438 Some(serial) => serial,
439 None => return Err(PointerThemeError::MissingEnterSerial),
440 };
441
442 if let Some(shape_device) = self.shape_device.as_ref() {
443 shape_device.set_shape(serial, cursor_icon_to_shape(icon));
444 Ok(())
445 } else {
446 self.set_cursor_legacy(conn, serial, icon)
447 }
448 }
449
450 fn set_cursor_legacy(
453 &self,
454 conn: &Connection,
455 serial: u32,
456 icon: CursorIcon,
457 ) -> Result<(), PointerThemeError> {
458 let mut themes = self.themes.lock().unwrap();
459
460 let scale = self.surface.data::<S>().unwrap().surface_data().scale_factor();
461 for cursor_icon_name in iter::once(&icon.name()).chain(icon.alt_names().iter()) {
462 if let Some(cursor) = themes
463 .get_cursor(conn, cursor_icon_name, scale as u32, &self.shm)
464 .map_err(PointerThemeError::InvalidId)?
465 {
466 let image = &cursor[0];
467 let (w, h) = image.dimensions();
468 let (hx, hy) = image.hotspot();
469
470 self.surface.set_buffer_scale(scale);
471 self.surface.attach(Some(image), 0, 0);
472
473 if self.surface.version() >= 4 {
474 self.surface.damage_buffer(0, 0, w as i32, h as i32);
475 } else {
476 self.surface.damage(0, 0, w as i32 / scale, h as i32 / scale);
478 }
479
480 self.surface.commit();
482
483 self.pointer.set_cursor(
485 serial,
486 Some(&self.surface),
487 hx as i32 / scale,
488 hy as i32 / scale,
489 );
490
491 return Ok(());
492 }
493 }
494
495 Err(PointerThemeError::CursorNotFound)
496 }
497
498 pub fn hide_cursor(&self) -> Result<(), PointerThemeError> {
502 let data = self.pointer.data::<U>();
503 if let Some(serial) = data.and_then(|data| data.pointer_data().latest_enter_serial()) {
504 self.pointer.set_cursor(serial, None, 0, 0);
505 Ok(())
506 } else {
507 Err(PointerThemeError::MissingEnterSerial)
508 }
509 }
510
511 pub fn pointer(&self) -> &WlPointer {
513 &self.pointer
514 }
515
516 pub fn surface(&self) -> &WlSurface {
518 &self.surface
519 }
520}
521
522impl<U, S> Drop for ThemedPointer<U, S> {
523 fn drop(&mut self) {
524 if let Some(shape_device) = self.shape_device.take() {
525 shape_device.destroy();
526 }
527
528 if self.pointer.version() >= 3 {
529 self.pointer.release();
530 }
531 self.surface.destroy();
532 }
533}
534
535#[derive(Debug)]
537pub enum ThemeSpec<'a> {
538 Named {
540 name: &'a str,
542
543 size: u32,
548 },
549
550 System,
556}
557
558impl<'a> Default for ThemeSpec<'a> {
559 fn default() -> Self {
560 Self::System
561 }
562}
563
564#[derive(Debug, thiserror::Error)]
566pub enum PointerThemeError {
567 #[error("Invalid ObjectId")]
569 InvalidId(InvalidId),
570
571 #[error("A Global Error occured")]
573 GlobalError(GlobalError),
574
575 #[error("Cursor not found")]
577 CursorNotFound,
578
579 #[error("Missing enter event serial")]
581 MissingEnterSerial,
582}
583
584#[derive(Debug)]
585pub(crate) struct Themes {
586 name: String,
587 size: u32,
588 themes: HashMap<u32, CursorTheme>,
590}
591
592impl Default for Themes {
593 fn default() -> Self {
594 Themes::new(ThemeSpec::default())
595 }
596}
597
598impl Themes {
599 pub(crate) fn new(spec: ThemeSpec) -> Themes {
600 let (name, size) = match spec {
601 ThemeSpec::Named { name, size } => (name.into(), size),
602 ThemeSpec::System => {
603 let name = env::var("XCURSOR_THEME").ok().unwrap_or_else(|| "default".into());
604 let size = env::var("XCURSOR_SIZE").ok().and_then(|s| s.parse().ok()).unwrap_or(24);
605 (name, size)
606 }
607 };
608
609 Themes { name, size, themes: HashMap::new() }
610 }
611
612 fn get_cursor(
613 &mut self,
614 conn: &Connection,
615 name: &str,
616 scale: u32,
617 shm: &WlShm,
618 ) -> Result<Option<&Cursor>, InvalidId> {
619 if let Entry::Vacant(e) = self.themes.entry(scale) {
621 let theme = CursorTheme::load_from_name(
623 conn,
624 shm.clone(), &self.name,
626 self.size * scale,
627 )?;
628
629 e.insert(theme);
630 }
631
632 let theme = self.themes.get_mut(&scale).unwrap();
633
634 Ok(theme.get_cursor(name))
635 }
636}