gardesk/gardm / 654cca3

Browse files

Add cursor type support and fix large display rendering

Implement CursorType enum with Default, Text, and Pointer
variants using X11 cursor font glyphs. Add set_cursor()
method that only updates when cursor type changes.

Clear root window on startup to hide leftover content from
previous sessions (e.g., garbg wallpaper pixmaps).

Chunk put_image into 1MB segments to avoid exceeding X11
maximum request size on large displays.
Authored by mfwolffe <wolffemf@dukes.jmu.edu>
SHA
654cca3c76e4646062dadf49ec437d6746f0a65f
Parents
b08bce1
Tree
297e6c1

1 changed file

StatusFile+-
M gardm-greeter/src/window.rs 139 14
gardm-greeter/src/window.rsmodified
@@ -8,6 +8,21 @@ use x11rb::protocol::xproto::*;
88
 use x11rb::rust_connection::RustConnection;
99
 use x11rb::wrapper::ConnectionExt as _;
1010
 
11
+/// Standard X11 cursor font glyphs
12
+mod cursor_glyphs {
13
+    pub const XC_LEFT_PTR: u16 = 68;   // Default arrow cursor
14
+    pub const XC_XTERM: u16 = 152;     // I-beam text cursor
15
+    pub const XC_HAND2: u16 = 60;      // Pointing hand cursor
16
+}
17
+
18
+/// Cursor types available for the greeter
19
+#[derive(Clone, Copy, PartialEq, Eq, Debug)]
20
+pub enum CursorType {
21
+    Default,
22
+    Text,
23
+    Pointer,
24
+}
25
+
1126
 /// Greeter window wrapping X11 connection and window handle
1227
 pub struct GreeterWindow {
1328
     conn: RustConnection,
@@ -17,6 +32,11 @@ pub struct GreeterWindow {
1732
     width: u16,
1833
     height: u16,
1934
     depth: u8,
35
+    // Cursors
36
+    cursor_default: Cursor,
37
+    cursor_text: Cursor,
38
+    cursor_pointer: Cursor,
39
+    current_cursor: CursorType,
2040
 }
2141
 
2242
 impl GreeterWindow {
@@ -31,6 +51,18 @@ impl GreeterWindow {
3151
         let depth = screen.root_depth;
3252
         let visual = screen.root_visual;
3353
 
54
+        // Clear the root window to black to hide any leftover content from previous session
55
+        // garbg sets the root background using a pixmap, so we must clear that first
56
+        // BackPixmap::NONE (0) removes any background pixmap, then background_pixel takes effect
57
+        conn.change_window_attributes(
58
+            root,
59
+            &ChangeWindowAttributesAux::new()
60
+                .background_pixmap(x11rb::NONE)  // Remove any pixmap (e.g., from garbg)
61
+                .background_pixel(screen.black_pixel),
62
+        )?;
63
+        conn.clear_area(false, root, 0, 0, width, height)?;
64
+        conn.flush()?;
65
+
3466
         // Create window
3567
         let window = conn.generate_id().context("Failed to generate window ID")?;
3668
         conn.create_window(
@@ -91,6 +123,53 @@ impl GreeterWindow {
91123
         conn.create_gc(gc, window, &CreateGCAux::new())
92124
             .context("Failed to create GC")?;
93125
 
126
+        // Load cursor font and create cursors
127
+        let cursor_font = conn.generate_id().context("Failed to generate font ID")?;
128
+        conn.open_font(cursor_font, b"cursor")
129
+            .context("Failed to open cursor font")?;
130
+
131
+        let cursor_default = conn.generate_id().context("Failed to generate cursor ID")?;
132
+        conn.create_glyph_cursor(
133
+            cursor_default,
134
+            cursor_font,
135
+            cursor_font,
136
+            cursor_glyphs::XC_LEFT_PTR,
137
+            cursor_glyphs::XC_LEFT_PTR + 1,
138
+            0xFFFF, 0xFFFF, 0xFFFF, // White foreground
139
+            0, 0, 0,                 // Black background
140
+        )
141
+        .context("Failed to create default cursor")?;
142
+
143
+        let cursor_text = conn.generate_id().context("Failed to generate cursor ID")?;
144
+        conn.create_glyph_cursor(
145
+            cursor_text,
146
+            cursor_font,
147
+            cursor_font,
148
+            cursor_glyphs::XC_XTERM,
149
+            cursor_glyphs::XC_XTERM + 1,
150
+            0xFFFF, 0xFFFF, 0xFFFF,
151
+            0, 0, 0,
152
+        )
153
+        .context("Failed to create text cursor")?;
154
+
155
+        let cursor_pointer = conn.generate_id().context("Failed to generate cursor ID")?;
156
+        conn.create_glyph_cursor(
157
+            cursor_pointer,
158
+            cursor_font,
159
+            cursor_font,
160
+            cursor_glyphs::XC_HAND2,
161
+            cursor_glyphs::XC_HAND2 + 1,
162
+            0xFFFF, 0xFFFF, 0xFFFF,
163
+            0, 0, 0,
164
+        )
165
+        .context("Failed to create pointer cursor")?;
166
+
167
+        conn.close_font(cursor_font).context("Failed to close cursor font")?;
168
+
169
+        // Set initial cursor
170
+        conn.change_window_attributes(window, &ChangeWindowAttributesAux::new().cursor(cursor_default))
171
+            .context("Failed to set initial cursor")?;
172
+
94173
         // Map and flush
95174
         conn.map_window(window).context("Failed to map window")?;
96175
         conn.flush().context("Failed to flush X connection")?;
@@ -110,6 +189,10 @@ impl GreeterWindow {
110189
             width,
111190
             height,
112191
             depth,
192
+            cursor_default,
193
+            cursor_text,
194
+            cursor_pointer,
195
+            current_cursor: CursorType::Default,
113196
         })
114197
     }
115198
 
@@ -144,21 +227,43 @@ impl GreeterWindow {
144227
     }
145228
 
146229
     /// Put an ARGB image to the window
230
+    /// Splits large images into chunks to avoid exceeding X11 request limits
147231
     pub fn put_image(&self, data: &[u8]) -> Result<()> {
148
-        self.conn
149
-            .put_image(
150
-                ImageFormat::Z_PIXMAP,
151
-                self.window,
152
-                self.gc,
153
-                self.width,
154
-                self.height,
155
-                0,
156
-                0,
157
-                0,
158
-                self.depth,
159
-                data,
160
-            )
161
-            .context("Failed to put image")?;
232
+        let bytes_per_row = self.width as usize * 4;
233
+        let total_rows = self.height as usize;
234
+
235
+        // X11 max request is typically 4MB, use 1MB chunks to be safe
236
+        const MAX_CHUNK_BYTES: usize = 1024 * 1024;
237
+        let rows_per_chunk = (MAX_CHUNK_BYTES / bytes_per_row).max(1);
238
+
239
+        let mut y_offset: i16 = 0;
240
+        let mut remaining_rows = total_rows;
241
+        let mut data_offset = 0;
242
+
243
+        while remaining_rows > 0 {
244
+            let chunk_rows = remaining_rows.min(rows_per_chunk);
245
+            let chunk_bytes = chunk_rows * bytes_per_row;
246
+            let chunk_data = &data[data_offset..data_offset + chunk_bytes];
247
+
248
+            self.conn
249
+                .put_image(
250
+                    ImageFormat::Z_PIXMAP,
251
+                    self.window,
252
+                    self.gc,
253
+                    self.width,
254
+                    chunk_rows as u16,
255
+                    0,
256
+                    y_offset,
257
+                    0,
258
+                    self.depth,
259
+                    chunk_data,
260
+                )
261
+                .context("Failed to put image chunk")?;
262
+
263
+            y_offset += chunk_rows as i16;
264
+            remaining_rows -= chunk_rows;
265
+            data_offset += chunk_bytes;
266
+        }
162267
 
163268
         self.conn.flush().context("Failed to flush after put_image")?;
164269
         Ok(())
@@ -177,6 +282,26 @@ impl GreeterWindow {
177282
             .poll_for_event()
178283
             .context("Failed to poll for X11 event")
179284
     }
285
+
286
+    /// Set the cursor type (only updates if different from current)
287
+    pub fn set_cursor(&mut self, cursor_type: CursorType) {
288
+        if self.current_cursor == cursor_type {
289
+            return;
290
+        }
291
+
292
+        let cursor = match cursor_type {
293
+            CursorType::Default => self.cursor_default,
294
+            CursorType::Text => self.cursor_text,
295
+            CursorType::Pointer => self.cursor_pointer,
296
+        };
297
+
298
+        let _ = self.conn.change_window_attributes(
299
+            self.window,
300
+            &ChangeWindowAttributesAux::new().cursor(cursor),
301
+        );
302
+        let _ = self.conn.flush();
303
+        self.current_cursor = cursor_type;
304
+    }
180305
 }
181306
 
182307
 impl Drop for GreeterWindow {