1use crate::Globals;
2use crate::platform::linux::{DEFAULT_CURSOR_ICON_NAME, log_cursor_icon_warning};
3use anyhow::anyhow;
4use util::ResultExt;
5
6use wayland_client::Connection;
7use wayland_client::protocol::wl_surface::WlSurface;
8use wayland_client::protocol::{wl_pointer::WlPointer, wl_shm::WlShm};
9use wayland_cursor::{CursorImageBuffer, CursorTheme};
10
11pub(crate) struct Cursor {
12 theme: Option<CursorTheme>,
13 theme_name: Option<String>,
14 theme_size: u32,
15 surface: WlSurface,
16 size: u32,
17 shm: WlShm,
18 connection: Connection,
19}
20
21impl Drop for Cursor {
22 fn drop(&mut self) {
23 self.theme.take();
24 self.surface.destroy();
25 }
26}
27
28impl Cursor {
29 pub fn new(connection: &Connection, globals: &Globals, size: u32) -> Self {
30 Self {
31 theme: CursorTheme::load(&connection, globals.shm.clone(), size).log_err(),
32 theme_name: None,
33 theme_size: size,
34 surface: globals.compositor.create_surface(&globals.qh, ()),
35 shm: globals.shm.clone(),
36 connection: connection.clone(),
37 size,
38 }
39 }
40
41 pub fn set_theme(&mut self, theme_name: &str) {
42 if let Some(theme) = CursorTheme::load_from_name(
43 &self.connection,
44 self.shm.clone(),
45 theme_name,
46 self.theme_size,
47 )
48 .log_err()
49 {
50 self.theme = Some(theme);
51 self.theme_name = Some(theme_name.to_string());
52 } else if let Some(theme) =
53 CursorTheme::load(&self.connection, self.shm.clone(), self.theme_size).log_err()
54 {
55 self.theme = Some(theme);
56 self.theme_name = None;
57 }
58 }
59
60 fn set_theme_size(&mut self, theme_size: u32) {
61 self.theme = self
62 .theme_name
63 .as_ref()
64 .and_then(|name| {
65 CursorTheme::load_from_name(
66 &self.connection,
67 self.shm.clone(),
68 name.as_str(),
69 theme_size,
70 )
71 .log_err()
72 })
73 .or_else(|| {
74 CursorTheme::load(&self.connection, self.shm.clone(), theme_size).log_err()
75 });
76 }
77
78 pub fn set_size(&mut self, size: u32) {
79 self.size = size;
80 self.set_theme_size(size);
81 }
82
83 pub fn set_icon(
84 &mut self,
85 wl_pointer: &WlPointer,
86 serial_id: u32,
87 mut cursor_icon_names: &[&str],
88 scale: i32,
89 ) {
90 self.set_theme_size(self.size * scale as u32);
91
92 let Some(theme) = &mut self.theme else {
93 log::warn!("Wayland: Unable to load cursor themes");
94 return;
95 };
96
97 let mut buffer: &CursorImageBuffer;
98 'outer: {
99 for cursor_icon_name in cursor_icon_names {
100 if let Some(cursor) = theme.get_cursor(cursor_icon_name) {
101 buffer = &cursor[0];
102 break 'outer;
103 }
104 }
105
106 if let Some(cursor) = theme.get_cursor(DEFAULT_CURSOR_ICON_NAME) {
107 buffer = &cursor[0];
108 log_cursor_icon_warning(anyhow!(
109 "wayland: Unable to get cursor icon {:?}. \
110 Using default cursor icon: '{}'",
111 cursor_icon_names,
112 DEFAULT_CURSOR_ICON_NAME
113 ));
114 } else {
115 log_cursor_icon_warning(anyhow!(
116 "wayland: Unable to fallback on default cursor icon '{}' for theme '{}'",
117 DEFAULT_CURSOR_ICON_NAME,
118 self.theme_name.as_deref().unwrap_or("default")
119 ));
120 return;
121 }
122 }
123
124 let (width, height) = buffer.dimensions();
125 let (hot_x, hot_y) = buffer.hotspot();
126
127 self.surface.set_buffer_scale(scale);
128
129 wl_pointer.set_cursor(
130 serial_id,
131 Some(&self.surface),
132 hot_x as i32 / scale,
133 hot_y as i32 / scale,
134 );
135
136 self.surface.attach(Some(&buffer), 0, 0);
137 self.surface.damage(0, 0, width as i32, height as i32);
138 self.surface.commit();
139 }
140}