Skip to repository content

tenant.openagents/omega

No repository description is available.

OpenAgents Git authority 2026-07-28T04:01:00.585Z Public web read
NIP-34 coordinate30617:7649603503856e5148d571eac2766b288a8ff1e9e35d380337a1d2b0015b4f92:omega
MaintainersHidden in public view
References2 branches · 1 tag
Read-only clonegit clone https://openagents.com/git/tenant.openagents/omega.git
Browse files

cursor.rs

153 lines · 4.6 KB · rust
1use crate::linux::Globals;
2use crate::linux::{DEFAULT_CURSOR_ICON_NAME, log_cursor_icon_warning};
3use anyhow::{Context as _, anyhow};
4use gpui_util::ResultExt;
5
6use wayland_client::Connection;
7use wayland_client::protocol::wl_surface::WlSurface;
8use wayland_client::protocol::{wl_pointer::WlPointer, wl_shm::WlShm};
9use wayland_cursor::{CursorImageBuffer, CursorTheme};
10
11pub(crate) struct Cursor {
12    loaded_theme: Option<LoadedTheme>,
13    size: u32,
14    scaled_size: u32,
15    surface: WlSurface,
16    shm: WlShm,
17    connection: Connection,
18}
19
20pub(crate) struct LoadedTheme {
21    theme: CursorTheme,
22    name: Option<String>,
23    scaled_size: u32,
24}
25
26impl Drop for Cursor {
27    fn drop(&mut self) {
28        self.loaded_theme.take();
29        self.surface.destroy();
30    }
31}
32
33impl Cursor {
34    pub fn new(connection: &Connection, globals: &Globals, size: u32) -> Self {
35        let mut this = Self {
36            loaded_theme: None,
37            size,
38            scaled_size: size,
39            surface: globals.compositor.create_surface(&globals.qh, ()),
40            shm: globals.shm.clone(),
41            connection: connection.clone(),
42        };
43        this.set_theme_internal(None);
44        this
45    }
46
47    fn set_theme_internal(&mut self, theme_name: Option<String>) {
48        if let Some(loaded_theme) = self.loaded_theme.as_ref()
49            && loaded_theme.name == theme_name
50            && loaded_theme.scaled_size == self.scaled_size
51        {
52            return;
53        }
54        let result = if let Some(theme_name) = theme_name.as_ref() {
55            CursorTheme::load_from_name(
56                &self.connection,
57                self.shm.clone(),
58                theme_name,
59                self.scaled_size,
60            )
61        } else {
62            CursorTheme::load(&self.connection, self.shm.clone(), self.scaled_size)
63        };
64        if let Some(theme) = result
65            .context("Wayland: Failed to load cursor theme")
66            .log_err()
67        {
68            self.loaded_theme = Some(LoadedTheme {
69                theme,
70                name: theme_name,
71                scaled_size: self.scaled_size,
72            });
73        }
74    }
75
76    pub fn set_theme(&mut self, theme_name: String) {
77        self.set_theme_internal(Some(theme_name));
78    }
79
80    fn set_scaled_size(&mut self, scaled_size: u32) {
81        self.scaled_size = scaled_size;
82        let theme_name = self
83            .loaded_theme
84            .as_ref()
85            .and_then(|loaded_theme| loaded_theme.name.clone());
86        self.set_theme_internal(theme_name);
87    }
88
89    pub fn set_size(&mut self, size: u32) {
90        self.size = size;
91        self.set_scaled_size(size);
92    }
93
94    pub fn set_icon(
95        &mut self,
96        wl_pointer: &WlPointer,
97        serial_id: u32,
98        cursor_icon_names: &[&str],
99        scale: i32,
100    ) {
101        self.set_scaled_size(self.size * scale as u32);
102
103        let Some(loaded_theme) = &mut self.loaded_theme else {
104            log::warn!("Wayland: Unable to load cursor themes");
105            return;
106        };
107        let theme = &mut loaded_theme.theme;
108
109        let buffer: &CursorImageBuffer;
110        'outer: {
111            for cursor_icon_name in cursor_icon_names {
112                if let Some(cursor) = theme.get_cursor(cursor_icon_name) {
113                    buffer = &cursor[0];
114                    break 'outer;
115                }
116            }
117
118            if let Some(cursor) = theme.get_cursor(DEFAULT_CURSOR_ICON_NAME) {
119                buffer = &cursor[0];
120                log_cursor_icon_warning(anyhow!(
121                    "wayland: Unable to get cursor icon {:?}. \
122                    Using default cursor icon: '{}'",
123                    cursor_icon_names,
124                    DEFAULT_CURSOR_ICON_NAME
125                ));
126            } else {
127                log_cursor_icon_warning(anyhow!(
128                    "wayland: Unable to fallback on default cursor icon '{}' for theme '{}'",
129                    DEFAULT_CURSOR_ICON_NAME,
130                    loaded_theme.name.as_deref().unwrap_or("default")
131                ));
132                return;
133            }
134        }
135
136        let (width, height) = buffer.dimensions();
137        let (hot_x, hot_y) = buffer.hotspot();
138
139        self.surface.set_buffer_scale(scale);
140
141        wl_pointer.set_cursor(
142            serial_id,
143            Some(&self.surface),
144            hot_x as i32 / scale,
145            hot_y as i32 / scale,
146        );
147
148        self.surface.attach(Some(buffer), 0, 0);
149        self.surface.damage(0, 0, width as i32, height as i32);
150        self.surface.commit();
151    }
152}
153
Served at tenant.openagents/omega Member data and write actions are omitted.