summaryrefslogtreecommitdiff
path: root/src/cursor.rs
diff options
context:
space:
mode:
authorMaxVerevkin <maxxverrr@gmail.com>2023-01-31 11:47:12 +0200
committerMaxVerevkin <maxxverrr@gmail.com>2023-01-31 11:51:15 +0200
commit92a7a55e18752147f71fa4155ee74b948d78a3de (patch)
treeb39e8226469100c78a7c2dc06cb99032c1333ba3 /src/cursor.rs
parentf0ba3e6661342668b951369b14353549701eb549 (diff)
downloadi3bar-mango-92a7a55e18752147f71fa4155ee74b948d78a3de.tar.gz
Make cursor.rs more generic
Probably will move it into a separate crate (wayrs_cursor) at some point.
Diffstat (limited to 'src/cursor.rs')
-rw-r--r--src/cursor.rs120
1 files changed, 70 insertions, 50 deletions
diff --git a/src/cursor.rs b/src/cursor.rs
index 3a827d0..1ca63ac 100644
--- a/src/cursor.rs
+++ b/src/cursor.rs
@@ -1,11 +1,8 @@
+use std::collections::HashMap;
use std::io;
use wayrs_client::connection::Connection;
-use wayrs_client::protocol::wl_compositor::WlCompositor;
-use wayrs_client::protocol::wl_pointer::WlPointer;
-use wayrs_client::protocol::wl_shm::Format;
-use wayrs_client::protocol::wl_surface::WlSurface;
-
+use wayrs_client::protocol::*;
use wayrs_shm_alloc::ShmAlloc;
use xcursor::parser::Image;
@@ -14,65 +11,67 @@ use crate::state::State;
#[derive(Debug, thiserror::Error)]
pub enum CursorError {
- #[error("theme not found")]
- ThemeNotFound,
+ #[error("cursor '{0}' not found")]
+ CursorNotFound(String),
#[error("theme could not be parsed")]
ThemeParseError,
#[error(transparent)]
ReadError(#[from] io::Error),
}
-pub struct Cursor {
- pub surface: WlSurface,
- pub images: Vec<Image>,
+pub struct CursorTheme {
+ xcursor_size: u32,
+ theme: xcursor::CursorTheme,
+ cursors: HashMap<String, Vec<Image>>,
}
-impl Cursor {
- pub fn new(
- conn: &mut Connection<State>,
- compositor: WlCompositor,
- ) -> Result<Self, CursorError> {
+impl CursorTheme {
+ pub fn new() -> Self {
let theme_name = std::env::var("XCURSOR_THEME").ok();
let theme_name = theme_name.as_deref().unwrap_or("default");
- let theme_path = xcursor::CursorTheme::load(theme_name)
- .load_icon("default")
- .ok_or(CursorError::ThemeNotFound)?;
- let raw_theme = std::fs::read(theme_path)?;
+ let xcursor_size = std::env::var("XCURSOR_SIZE")
+ .ok()
+ .and_then(|x| x.parse().ok())
+ .unwrap_or(24);
- let mut images =
- xcursor::parser::parse_xcursor(&raw_theme).ok_or(CursorError::ThemeParseError)?;
- images.sort_unstable_by_key(|img| img.size);
+ let theme = xcursor::CursorTheme::load(theme_name);
- let surface = compositor.create_surface(conn);
+ CursorTheme {
+ xcursor_size,
+ theme,
+ cursors: HashMap::new(),
+ }
+ }
- Ok(Cursor { surface, images })
+ /// Use this to handle errors ahead of time.
+ pub fn ensure_cursor_is_loaded(&mut self, cursor: &str) -> Result<(), CursorError> {
+ let _ = self.get_images(cursor)?;
+ Ok(())
}
- pub fn set(
- &self,
+ #[allow(clippy::too_many_arguments)]
+ pub fn set_cursor(
+ &mut self,
conn: &mut Connection<State>,
- serial: u32,
- pointer: WlPointer,
+ cursor: &str,
scale: u32,
+ serial: u32,
shm: &mut ShmAlloc,
- ) {
- let target_size = std::env::var("XCURSOR_SIZE")
- .ok()
- .and_then(|x| x.parse().ok())
- .unwrap_or(24)
- * scale;
+ surface: WlSurface,
+ pointer: WlPointer,
+ ) -> Result<(), CursorError> {
+ let target_size = self.xcursor_size * scale;
+
+ let images = self.get_images(cursor)?;
- let image = match self
- .images
- .binary_search_by_key(&target_size, |img| img.size)
- {
- Ok(indx) => &self.images[indx],
- Err(indx) if indx == 0 => self.images.first().unwrap(),
- Err(indx) if indx >= self.images.len() => self.images.last().unwrap(),
+ let image = match images.binary_search_by_key(&target_size, |img| img.size) {
+ Ok(indx) => &images[indx],
+ Err(indx) if indx == 0 => images.first().unwrap(),
+ Err(indx) if indx >= images.len() => images.last().unwrap(),
Err(indx) => {
- let a = &self.images[indx - 1];
- let b = &self.images[indx];
+ let a = &images[indx - 1];
+ let b = &images[indx];
if target_size - a.size < b.size - target_size {
a
} else {
@@ -86,24 +85,45 @@ impl Cursor {
image.width as i32,
image.height as i32,
(image.width * 4) as i32,
- Format::Argb8888,
+ wl_shm::Format::Argb8888,
);
assert_eq!(image.pixels_argb.len(), canvas.len(),);
canvas.copy_from_slice(&image.pixels_rgba);
- self.surface.attach(conn, buffer.wl, 0, 0);
- self.surface
- .damage_buffer(conn, 0, 0, image.width as i32, image.height as i32);
- self.surface.set_buffer_scale(conn, scale as i32);
- self.surface.commit(conn);
+ surface.attach(conn, buffer.wl, 0, 0);
+ surface.damage(conn, 0, 0, i32::MAX, i32::MAX);
+ surface.set_buffer_scale(conn, scale as i32);
+ surface.commit(conn);
pointer.set_cursor(
conn,
serial,
- self.surface,
+ surface,
(image.xhot / scale) as i32,
(image.yhot / scale) as i32,
);
+
+ Ok(())
+ }
+
+ fn get_images(&mut self, cursor: &'_ str) -> Result<&[Image], CursorError> {
+ // Borrow checker does't allow `if let Some(...) = ...` here for some reason :(
+ if self.cursors.get(cursor).is_some() {
+ return Ok(self.cursors.get(cursor).unwrap());
+ }
+
+ let theme_path = self
+ .theme
+ .load_icon(cursor)
+ .ok_or_else(|| CursorError::CursorNotFound(cursor.to_owned()))?;
+
+ let raw_theme = std::fs::read(theme_path)?;
+
+ let mut images =
+ xcursor::parser::parse_xcursor(&raw_theme).ok_or(CursorError::ThemeParseError)?;
+ images.sort_unstable_by_key(|img| img.size);
+
+ Ok(self.cursors.entry(cursor.to_owned()).or_insert(images))
}
}