gardesk/garbg / 220d439

Browse files

Use RetainPermanent mode to persist wallpaper across disconnects

Authored by mfwolffe <wolffemf@dukes.jmu.edu>
SHA
220d439d30e468c2a2abeb9a0aae036f178d5739
Parents
6b29169
Tree
32d830c

3 changed files

StatusFile+-
M garbg/src/main.rs 9 0
M garbg/src/x11/connection.rs 67 3
M garbg/src/x11/mod.rs 1 1
garbg/src/main.rsmodified
@@ -455,8 +455,17 @@ fn set_single_wallpaper(
455455
     }
456456
 
457457
     // Static image handling
458
+    use garbg::x11::CloseDownMode;
459
+
458460
     let mut conn = Connection::new()?;
459461
 
462
+    // Use RetainPermanent mode so the pixmap survives after we exit.
463
+    // This prevents black backgrounds when garbar reloads or screen redraws occur.
464
+    conn.set_close_down_mode(CloseDownMode::RetainPermanent)?;
465
+
466
+    // Clean up any old pixmap from previous garbg runs
467
+    conn.cleanup_old_pixmap()?;
468
+
460469
     let image = if source.starts_with("http://") || source.starts_with("https://") {
461470
         fetch_image_from_url(source)?
462471
     } else {
garbg/src/x11/connection.rsmodified
@@ -55,6 +55,15 @@ impl Atoms {
5555
     }
5656
 }
5757
 
58
+/// Close-down mode for X11 connections
59
+#[derive(Debug, Clone, Copy, PartialEq, Eq)]
60
+pub enum CloseDownMode {
61
+    /// Free all resources when client disconnects (default)
62
+    Destroy,
63
+    /// Keep resources alive after client disconnects (for wallpaper persistence)
64
+    RetainPermanent,
65
+}
66
+
5867
 /// X11 connection wrapper for garbg
5968
 pub struct Connection {
6069
     conn: RustConnection,
@@ -66,6 +75,8 @@ pub struct Connection {
6675
     atoms: Atoms,
6776
     /// Currently set root pixmap (if any)
6877
     current_pixmap: Option<Pixmap>,
78
+    /// Close-down mode (determines if pixmap survives client disconnect)
79
+    close_down_mode: CloseDownMode,
6980
 }
7081
 
7182
 impl Connection {
@@ -111,9 +122,59 @@ impl Connection {
111122
             gc,
112123
             atoms,
113124
             current_pixmap: None,
125
+            close_down_mode: CloseDownMode::Destroy,
114126
         })
115127
     }
116128
 
129
+    /// Set the close-down mode for this connection.
130
+    ///
131
+    /// - `Destroy`: Free all resources when client disconnects (default, good for daemons)
132
+    /// - `RetainPermanent`: Keep pixmap alive after disconnect (good for one-shot commands)
133
+    ///
134
+    /// When using `RetainPermanent`, the pixmap will persist and garbg will clean up
135
+    /// old pixmaps from previous runs by reading `_XROOTPMAP_ID`.
136
+    pub fn set_close_down_mode(&mut self, mode: CloseDownMode) -> Result<()> {
137
+        self.close_down_mode = mode;
138
+
139
+        let x11_mode = match mode {
140
+            CloseDownMode::Destroy => x11rb::protocol::xproto::CloseDown::DESTROY_ALL,
141
+            CloseDownMode::RetainPermanent => x11rb::protocol::xproto::CloseDown::RETAIN_PERMANENT,
142
+        };
143
+
144
+        self.conn.set_close_down_mode(x11_mode)?;
145
+        self.conn.flush()?;
146
+
147
+        Ok(())
148
+    }
149
+
150
+    /// Clean up any pixmap left by a previous garbg run.
151
+    ///
152
+    /// This reads the pixmap ID from `_XROOTPMAP_ID` and frees it if it exists.
153
+    /// Should be called before setting a new wallpaper when using `RetainPermanent` mode.
154
+    pub fn cleanup_old_pixmap(&self) -> Result<()> {
155
+        // Try to read the old pixmap ID from the root window property
156
+        let reply = self.conn.get_property(
157
+            false,
158
+            self.root,
159
+            self.atoms.xrootpmap_id,
160
+            AtomEnum::PIXMAP,
161
+            0,
162
+            1,
163
+        )?.reply()?;
164
+
165
+        if reply.format == 32 && reply.length == 1 {
166
+            if let Some(pixmap_id) = reply.value32().and_then(|mut iter| iter.next()) {
167
+                if pixmap_id != 0 {
168
+                    // Kill the old pixmap (ignore errors - it might already be freed)
169
+                    let _ = self.conn.kill_client(pixmap_id);
170
+                    tracing::debug!("Cleaned up old pixmap: {}", pixmap_id);
171
+                }
172
+            }
173
+        }
174
+
175
+        Ok(())
176
+    }
177
+
117178
     /// Get screen dimensions (width, height)
118179
     pub fn screen_dimensions(&self) -> (u16, u16) {
119180
         let screen = &self.conn.setup().roots[self.screen_num];
@@ -253,9 +314,12 @@ impl Connection {
253314
 
254315
 impl Drop for Connection {
255316
     fn drop(&mut self) {
256
-        // Clean up the pixmap when we're done
257
-        if let Some(pixmap) = self.current_pixmap.take() {
258
-            let _ = self.conn.free_pixmap(pixmap);
317
+        // Only clean up the pixmap if we're in Destroy mode.
318
+        // In RetainPermanent mode, we leave it alive for persistence.
319
+        if self.close_down_mode == CloseDownMode::Destroy {
320
+            if let Some(pixmap) = self.current_pixmap.take() {
321
+                let _ = self.conn.free_pixmap(pixmap);
322
+            }
259323
         }
260324
         let _ = self.conn.free_gc(self.gc);
261325
     }
garbg/src/x11/mod.rsmodified
@@ -9,7 +9,7 @@ mod monitors;
99
 mod animation;
1010
 mod compositor;
1111
 
12
-pub use connection::{Connection, X11Error};
12
+pub use connection::{Connection, X11Error, CloseDownMode};
1313
 pub use renderer::Renderer;
1414
 pub use monitors::Monitor;
1515
 pub use animation::{AnimationRenderer, DoubleBuffer};