aboutsummaryrefslogtreecommitdiff
path: root/src/cursor.rs
diff options
context:
space:
mode:
authorIvan Molodetskikh <yalterz@gmail.com>2023-10-01 07:59:28 +0400
committerIvan Molodetskikh <yalterz@gmail.com>2023-10-01 08:03:12 +0400
commit8d443c2e841505dd9a09d50dbb24a2a5956ecbd7 (patch)
tree09d98ab7566dc72a34842c5f1083355e62e2eca3 /src/cursor.rs
parentd39da3f46112395e5e359efd756e9c510c03e243 (diff)
downloadniri-8d443c2e841505dd9a09d50dbb24a2a5956ecbd7.tar.gz
niri-8d443c2e841505dd9a09d50dbb24a2a5956ecbd7.tar.bz2
niri-8d443c2e841505dd9a09d50dbb24a2a5956ecbd7.zip
Make default cursor respect output scale
First part of https://github.com/YaLTeR/niri/issues/16
Diffstat (limited to 'src/cursor.rs')
-rw-r--r--src/cursor.rs103
1 files changed, 103 insertions, 0 deletions
diff --git a/src/cursor.rs b/src/cursor.rs
new file mode 100644
index 00000000..454cd99b
--- /dev/null
+++ b/src/cursor.rs
@@ -0,0 +1,103 @@
+use std::collections::HashMap;
+use std::fs::File;
+use std::io::Read;
+
+use anyhow::{anyhow, Context};
+use smithay::backend::allocator::Fourcc;
+use smithay::backend::renderer::element::texture::TextureBuffer;
+use smithay::backend::renderer::gles::{GlesRenderer, GlesTexture};
+use smithay::utils::{Physical, Point, Transform};
+use xcursor::parser::{parse_xcursor, Image};
+use xcursor::CursorTheme;
+
+const CURSOR_SIZE: i32 = 24;
+static FALLBACK_CURSOR_DATA: &[u8] = include_bytes!("../resources/cursor.rgba");
+
+pub struct Cursor {
+ images: Vec<Image>,
+ cache: HashMap<i32, (TextureBuffer<GlesTexture>, Point<i32, Physical>)>,
+}
+
+impl Cursor {
+ pub fn load() -> Self {
+ let images = match load_xcursor() {
+ Ok(images) => images,
+ Err(err) => {
+ warn!("error loading xcursor default cursor: {err:?}");
+
+ vec![Image {
+ size: 32,
+ width: 64,
+ height: 64,
+ xhot: 1,
+ yhot: 1,
+ delay: 1,
+ pixels_rgba: Vec::from(FALLBACK_CURSOR_DATA),
+ pixels_argb: vec![],
+ }]
+ }
+ };
+
+ Self {
+ images,
+ cache: HashMap::new(),
+ }
+ }
+
+ pub fn get(
+ &mut self,
+ renderer: &mut GlesRenderer,
+ scale: i32,
+ ) -> (TextureBuffer<GlesTexture>, Point<i32, Physical>) {
+ self.cache
+ .entry(scale)
+ .or_insert_with_key(|scale| {
+ let _span = tracy_client::span!("create cursor texture");
+
+ let size = CURSOR_SIZE * scale;
+
+ let nearest_image = self
+ .images
+ .iter()
+ .min_by_key(|image| (size - image.size as i32).abs())
+ .unwrap();
+ let frame = self
+ .images
+ .iter()
+ .find(move |image| {
+ image.width == nearest_image.width && image.height == nearest_image.height
+ })
+ .unwrap();
+
+ let texture = TextureBuffer::from_memory(
+ renderer,
+ &frame.pixels_rgba,
+ Fourcc::Abgr8888,
+ (frame.width as i32, frame.height as i32),
+ false,
+ *scale,
+ Transform::Normal,
+ None,
+ )
+ .unwrap();
+ (texture, (frame.xhot as i32, frame.yhot as i32).into())
+ })
+ .clone()
+ }
+}
+
+fn load_xcursor() -> anyhow::Result<Vec<Image>> {
+ let _span = tracy_client::span!();
+
+ let theme = CursorTheme::load("default");
+ let path = theme
+ .load_icon("default")
+ .ok_or_else(|| anyhow!("no default icon"))?;
+ let mut file = File::open(path).context("error opening cursor icon file")?;
+ let mut buf = vec![];
+ file.read_to_end(&mut buf)
+ .context("error reading cursor icon file")?;
+ let images = parse_xcursor(&buf).context("error parsing cursor icon file")?;
+
+ Ok(images)
+}