cursor.rs

  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}