Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: load cursor from data #680

Merged
merged 4 commits into from
Apr 29, 2024
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
60 changes: 59 additions & 1 deletion wayland-cursor/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,9 @@
//! # }
//! ```

use std::borrow::Cow;
use std::env;
use std::fmt::Debug;
use std::fs::File;
use std::io::{Error as IoError, Read, Result as IoResult, Seek, SeekFrom, Write};
use std::ops::{Deref, Index};
Expand Down Expand Up @@ -81,6 +83,26 @@ pub struct CursorTheme {
pool_size: i32,
file: File,
backend: WeakBackend,
fallback: Option<FallBack>,
}

type FallBackInner = Box<dyn Fn(&str, u32) -> Option<Cow<'static, [u8]>>>;

struct FallBack(FallBackInner);

impl FallBack {
fn new<F>(fallback: F) -> Self
where
F: Fn(&str, u32) -> Option<Cow<'static, [u8]>> + 'static,
{
Self(Box::new(fallback))
}
}

impl Debug for FallBack {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str("fallback function")
}
}

impl CursorTheme {
Expand Down Expand Up @@ -160,23 +182,59 @@ impl CursorTheme {
pool_size: INITIAL_POOL_SIZE,
cursors: Vec::new(),
backend: conn.backend().downgrade(),
fallback: None,
})
}

/// Retrieve a cursor from the theme.
///
/// This method returns [`None`] if this cursor is not provided either by the theme, or by one of its parents.
///
/// If a fallback is set, it will use the data from fallback
pub fn get_cursor(&mut self, name: &str) -> Option<&Cursor> {
match self.cursors.iter().position(|cursor| cursor.name == name) {
Some(i) => Some(&self.cursors[i]),
None => {
let cursor = self.load_cursor(name, self.size)?;
let cursor = match self.load_cursor(name, self.size) {
None => {
let fallback = self.fallback.as_ref()?;
let data = fallback.0(name, self.size)?;
let images = xparser::parse_xcursor(&data)?;
let conn = Connection::from_backend(self.backend.upgrade()?);
Cursor::new(&conn, name, self, &images, self.size)
}
Some(cursor) => cursor,
};
self.cursors.push(cursor);
self.cursors.iter().last()
}
}
}

/// Set a callback to load the cursor data, in case the system theme is missing a cursor that you need.
///
/// Your callback will be invoked with he name and size of the requested cursor and should return a byte
/// array with the contents of an `xcursor` file, or `None` if you don't provide a fallback for this cursor.
///
/// For example, this defines a generic fallback cursor image and uses it for all missing cursors:
/// ```ignore
/// # use wayland_cursor::CursorTheme;
/// # use wayland_client::{Connection, backend::InvalidId, protocol::wl_shm};
/// # fn example(conn: &Connection, shm: wl_shm::WlShm, size: u32) -> Result<CursorTheme, InvalidId> {
/// # let mut theme = CursorTheme::load_or(conn, shm, "default", size)?;
/// # theme.set_callback(|name, size| {
/// # include_bytes!("./icons/default")
/// # });
/// # Ok(theme)
/// # }
/// ```
pub fn set_callback<F>(&mut self, fallback: F)
Copy link
Contributor

@MarijnS95 MarijnS95 May 7, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

While patching up the docs, is it intentional that this is called set_callback() and not set_fallback() (where the fallback is a callback that provides a fallback value when called)?

This function name and docs exclusively speak of a callback, but the get_cursor() function "referencing it" exclusively speaks of fallback, which is confusing on the user end.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

oh.. it is a mistake. can you make a pr for me? thanks

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you do it? I don't know what name you want to make this into.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

set_fallback, it was the author suggested. but then it become a mistake

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Decodetalkers done in #723!

where
F: Fn(&str, u32) -> Option<Cow<'static, [u8]>> + 'static,
{
self.fallback = Some(FallBack::new(fallback))
}

/// 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.
Expand Down
Loading