1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
|
use std::collections::HashMap;
use std::io;
use wayrs_client::connection::Connection;
use wayrs_client::protocol::*;
use wayrs_shm_alloc::ShmAlloc;
use xcursor::parser::Image;
use crate::state::State;
#[derive(Debug, thiserror::Error)]
pub enum CursorError {
#[error("cursor '{0}' not found")]
CursorNotFound(String),
#[error("theme could not be parsed")]
ThemeParseError,
#[error(transparent)]
ReadError(#[from] io::Error),
}
pub struct CursorTheme {
xcursor_size: u32,
theme: xcursor::CursorTheme,
cursors: HashMap<String, Vec<Image>>,
}
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 xcursor_size = std::env::var("XCURSOR_SIZE")
.ok()
.and_then(|x| x.parse().ok())
.unwrap_or(24);
let theme = xcursor::CursorTheme::load(theme_name);
CursorTheme {
xcursor_size,
theme,
cursors: HashMap::new(),
}
}
/// 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(())
}
#[allow(clippy::too_many_arguments)]
pub fn set_cursor(
&mut self,
conn: &mut Connection<State>,
cursor: &str,
scale: u32,
serial: u32,
shm: &mut ShmAlloc,
surface: WlSurface,
pointer: WlPointer,
) -> Result<(), CursorError> {
let target_size = self.xcursor_size * scale;
let images = self.get_images(cursor)?;
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 = &images[indx - 1];
let b = &images[indx];
if target_size - a.size < b.size - target_size {
a
} else {
b
}
}
};
let (buffer, canvas) = shm.alloc_buffer(
conn,
image.width as i32,
image.height as i32,
(image.width * 4) as i32,
wl_shm::Format::Argb8888,
);
assert_eq!(image.pixels_argb.len(), canvas.len(),);
canvas.copy_from_slice(&image.pixels_rgba);
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,
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))
}
}
|