aboutsummaryrefslogtreecommitdiff
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
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
-rw-r--r--src/cursor.rs103
-rw-r--r--src/main.rs1
-rw-r--r--src/niri.rs20
-rw-r--r--src/utils.rs74
4 files changed, 118 insertions, 80 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)
+}
diff --git a/src/main.rs b/src/main.rs
index 0aa05921..4e994d9f 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -4,6 +4,7 @@ extern crate tracing;
mod animation;
mod backend;
mod config;
+mod cursor;
mod dbus;
mod frame_clock;
mod handlers;
diff --git a/src/niri.rs b/src/niri.rs
index eea0a2c0..dda9cd29 100644
--- a/src/niri.rs
+++ b/src/niri.rs
@@ -15,7 +15,7 @@ use smithay::backend::allocator::Fourcc;
use smithay::backend::renderer::element::surface::{
render_elements_from_surface_tree, WaylandSurfaceRenderElement,
};
-use smithay::backend::renderer::element::texture::{TextureBuffer, TextureRenderElement};
+use smithay::backend::renderer::element::texture::TextureRenderElement;
use smithay::backend::renderer::element::utils::{Relocate, RelocateRenderElement};
use smithay::backend::renderer::element::{
render_elements, AsRenderElements, Kind, RenderElement, RenderElementStates,
@@ -71,6 +71,7 @@ use zbus::fdo::RequestNameFlags;
use crate::backend::{Backend, Tty, Winit};
use crate::config::Config;
+use crate::cursor::Cursor;
use crate::dbus::gnome_shell_screenshot::{self, NiriToScreenshot, ScreenshotToNiri};
use crate::dbus::mutter_display_config::DisplayConfig;
#[cfg(feature = "xdp-gnome-screencast")]
@@ -79,7 +80,7 @@ use crate::dbus::mutter_service_channel::ServiceChannel;
use crate::frame_clock::FrameClock;
use crate::layout::{output_size, MonitorRenderElement, MonitorSet};
use crate::pw_utils::{Cast, PipeWire};
-use crate::utils::{center, get_monotonic_time, load_default_cursor, make_screenshot_path};
+use crate::utils::{center, get_monotonic_time, make_screenshot_path};
pub struct Niri {
pub config: Rc<RefCell<Config>>,
@@ -122,7 +123,7 @@ pub struct Niri {
pub seat: Seat<State>,
- pub pointer_buffer: Option<(TextureBuffer<GlesTexture>, Point<i32, Physical>)>,
+ pub default_cursor: Cursor,
pub cursor_image: CursorImageStatus,
pub dnd_icon: Option<WlSurface>,
@@ -328,6 +329,8 @@ impl Niri {
.unwrap();
seat.add_pointer();
+ let default_cursor = Cursor::load();
+
let socket_source = ListeningSocketSource::new_auto().unwrap();
let socket_name = socket_source.socket_name().to_os_string();
event_loop
@@ -675,7 +678,7 @@ impl Niri {
presentation_state,
seat,
- pointer_buffer: None,
+ default_cursor,
cursor_image: CursorImageStatus::Default,
dnd_icon: None,
@@ -985,10 +988,9 @@ impl Niri {
let output_pos = self.global_space.output_geometry(output).unwrap().loc;
let pointer_pos = self.seat.get_pointer().unwrap().current_location() - output_pos.to_f64();
- let (default_buffer, default_hotspot) = self
- .pointer_buffer
- .get_or_insert_with(|| load_default_cursor(renderer));
- let default_hotspot = default_hotspot.to_logical(1);
+ let output_scale_int = output.current_scale().integer_scale();
+ let (default_buffer, default_hotspot) = self.default_cursor.get(renderer, output_scale_int);
+ let default_hotspot = default_hotspot.to_logical(output_scale_int);
let hotspot = if let CursorImageStatus::Surface(surface) = &mut self.cursor_image {
if surface.alive() {
@@ -1015,7 +1017,7 @@ impl Niri {
CursorImageStatus::Default => vec![OutputRenderElements::DefaultPointer(
TextureRenderElement::from_texture_buffer(
pointer_pos.to_f64(),
- default_buffer,
+ &default_buffer,
None,
None,
None,
diff --git a/src/utils.rs b/src/utils.rs
index f1aca8b2..7113bc92 100644
--- a/src/utils.rs
+++ b/src/utils.rs
@@ -1,24 +1,15 @@
use std::ffi::OsStr;
-use std::fs::File;
-use std::io::{self, Read};
+use std::io;
use std::os::unix::process::CommandExt;
use std::path::PathBuf;
use std::process::{Command, Stdio};
use std::time::Duration;
-use anyhow::{anyhow, Context};
+use anyhow::Context;
use directories::UserDirs;
use nix::time::{clock_gettime, ClockId};
-use smithay::backend::allocator::Fourcc;
-use smithay::backend::renderer::element::texture::TextureBuffer;
-use smithay::backend::renderer::gles::{GlesRenderer, GlesTexture};
-use smithay::utils::{Logical, Physical, Point, Rectangle, Transform};
+use smithay::utils::{Logical, Point, Rectangle};
use time::OffsetDateTime;
-use xcursor::parser::parse_xcursor;
-use xcursor::CursorTheme;
-
-const CURSOR_SIZE: u32 = 24;
-static FALLBACK_CURSOR_DATA: &[u8] = include_bytes!("../resources/cursor.rgba");
pub fn get_monotonic_time() -> Duration {
Duration::from(clock_gettime(ClockId::CLOCK_MONOTONIC).unwrap())
@@ -28,65 +19,6 @@ pub fn center(rect: Rectangle<i32, Logical>) -> Point<i32, Logical> {
rect.loc + rect.size.downscale(2).to_point()
}
-fn load_xcursor() -> anyhow::Result<xcursor::parser::Image> {
- 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")?;
-
- let nearest_image = images
- .iter()
- .min_by_key(|image| (CURSOR_SIZE as i32 - image.size as i32).abs())
- .unwrap();
- let frame = images
- .iter()
- .find(move |image| {
- image.width == nearest_image.width && image.height == nearest_image.height
- })
- .unwrap();
- Ok(frame.clone())
-}
-
-pub fn load_default_cursor(
- renderer: &mut GlesRenderer,
-) -> (TextureBuffer<GlesTexture>, Point<i32, Physical>) {
- let frame = match load_xcursor() {
- Ok(frame) => frame,
- Err(err) => {
- warn!("error loading xcursor default cursor: {err:?}");
-
- xcursor::parser::Image {
- size: 32,
- width: 64,
- height: 64,
- xhot: 1,
- yhot: 1,
- delay: 1,
- pixels_rgba: Vec::from(FALLBACK_CURSOR_DATA),
- pixels_argb: vec![],
- }
- }
- };
-
- let texture = TextureBuffer::from_memory(
- renderer,
- &frame.pixels_rgba,
- Fourcc::Abgr8888,
- (frame.width as i32, frame.height as i32),
- false,
- 1,
- Transform::Normal,
- None,
- )
- .unwrap();
- (texture, (frame.xhot as i32, frame.yhot as i32).into())
-}
-
pub fn make_screenshot_path() -> anyhow::Result<PathBuf> {
let dirs = UserDirs::new().context("error retrieving home directory")?;
let mut path = dirs.picture_dir().map(|p| p.to_owned()).unwrap_or_else(|| {