diff --git a/wayland-cursor/src/lib.rs b/wayland-cursor/src/lib.rs index ff6ae747273..4c6ef7d7cc5 100644 --- a/wayland-cursor/src/lib.rs +++ b/wayland-cursor/src/lib.rs @@ -45,6 +45,7 @@ //! # } //! ``` +use std::borrow::Cow; use std::env; use std::fs::File; use std::io::{Error as IoError, Read, Result as IoResult, Seek, SeekFrom, Write}; @@ -83,6 +84,22 @@ pub struct CursorTheme { backend: WeakBackend, } +/// An error that occurs when a binding a global fails. +#[derive(Debug)] +pub enum CursorLoadError { + /// When give a icon name, but XCursorTheme cannot find a path + IconPathNotFound(String), + + /// When the path finded before, but it cannot be opened + IconFileCannotOpen(std::io::Error), + + /// When the file is opened, but cannot get the data + IconFileNotReadable(std::io::Error), + + /// When the data of icon is get, but xparser cannot parse it + IconFileNotParsable, +} + impl CursorTheme { /// Load a cursor theme from system defaults. /// @@ -177,6 +194,63 @@ impl CursorTheme { } } + /// try to load data from theme, else, it will try to load from fallback data + /// + /// This method returns [`None`] if this cursor is not provided either by the theme, or by one of its parents, + /// and the data by fallback function cannot parse it + /// fallback function will receive a enum which discribe the error, you can print some messages + /// or do some other things before return the finally fallback data + pub fn get_cursor_with_default_data(&mut self, name: &str, fallback: F) -> Option<&Cursor> + where + F: Fn(CursorLoadError) -> Cow<'static, [u8]>, + { + match self.cursors.iter().position(|cursor| cursor.name == name) { + Some(i) => Some(&self.cursors[i]), + None => { + let cursor = self.load_cursor_with_default_data(name, self.size, fallback)?; + self.cursors.push(cursor); + self.cursors.iter().last() + } + } + } + + fn load_cursor_with_default_data( + &mut self, + name: &str, + size: u32, + fallback: F, + ) -> Option + where + F: Fn(CursorLoadError) -> Cow<'static, [u8]>, + { + let conn = Connection::from_backend(self.backend.upgrade()?); + let err_reason = 'fallback: { + let Some(icon_path) = XCursorTheme::load(&self.name).load_icon(name) else { + break 'fallback CursorLoadError::IconPathNotFound(name.to_owned()); + }; + let mut icon_file = match File::open(icon_path) { + Ok(icon_file) => icon_file, + Err(e) => { + break 'fallback CursorLoadError::IconFileCannotOpen(e); + } + }; + let mut buf = Vec::new(); + let Some(images) = ({ + if let Err(e) = icon_file.read_to_end(&mut buf) { + break 'fallback CursorLoadError::IconFileNotReadable(e); + } + xparser::parse_xcursor(&buf) + }) else { + break 'fallback CursorLoadError::IconFileNotParsable; + }; + + return Some(Cursor::new(&conn, name, self, &images, size)); + }; + let data = fallback(err_reason); + let images = xparser::parse_xcursor(data.as_ref())?; + Some(Cursor::new(&conn, name, self, &images, size)) + } + /// This function loads a cursor, parses it and pushes the images onto the shm pool. /// /// Keep in mind that if the cursor is already loaded, the function will make a duplicate.