gardesk/gar / e0f66b3

Browse files

fixes

Authored by mfwolffe <wolffemf@dukes.jmu.edu>
SHA
e0f66b34e861915965c0534f6736f8f73065be82
Parents
83272c3
Tree
01d20b4

5 changed files

StatusFile+-
M gar-session.sh 2 2
M gar/src/config/lua.rs 35 2
M gar/src/config/mod.rs 4 0
M gar/src/core/mod.rs 3 0
M gar/src/x11/events.rs 148 0
gar-session.shmodified
@@ -47,8 +47,8 @@ fi
4747
 # Set log level
4848
 export GAR_LOG=info
4949
 
50
-# Launch polybar after gar starts (needs i3 IPC socket)
51
-(sleep 0.5 && ~/.config/polybar/launch.sh) &
50
+# garbar is now launched natively by gar when gar.bar is configured in init.lua
51
+# (Legacy polybar launch removed - use gar.bar config instead)
5252
 
5353
 # Start gar
5454
 exec /home/mfwolffe/GithubOrgs/tenseleyFlow/gar/target/release/gar
gar/src/config/lua.rsmodified
@@ -114,15 +114,48 @@ impl LuaConfig {
114114
 
115115
         self.lua.load(&source).exec()?;
116116
 
117
+        // Check if gar.bar table exists - enables garbar integration
118
+        self.check_bar_config()?;
119
+
117120
         let state = self.state.lock().unwrap();
118121
         tracing::info!(
119
-            "Config loaded: {} keybinds registered",
120
-            state.keybinds.len()
122
+            "Config loaded: {} keybinds registered, bar_enabled={}",
123
+            state.keybinds.len(),
124
+            state.config.bar_enabled
121125
         );
122126
 
123127
         Ok(())
124128
     }
125129
 
130
+    /// Check if gar.bar table is configured, enabling garbar integration
131
+    fn check_bar_config(&self) -> LuaResult<()> {
132
+        let globals = self.lua.globals();
133
+        let gar: Table = globals.get("gar")?;
134
+
135
+        // Check if gar.bar exists and is a table
136
+        match gar.get::<Table>("bar") {
137
+            Ok(bar_table) => {
138
+                // gar.bar exists! Enable garbar integration
139
+                let mut state = self.state.lock().unwrap();
140
+                state.config.bar_enabled = true;
141
+
142
+                // Optionally read bar height from config for reserved space
143
+                if let Ok(height) = bar_table.get::<u32>("height") {
144
+                    state.config.bar_height = height;
145
+                    tracing::info!("garbar integration enabled (height={})", height);
146
+                } else {
147
+                    tracing::info!("garbar integration enabled (default height)");
148
+                }
149
+            }
150
+            Err(_) => {
151
+                // gar.bar not set, garbar won't be spawned
152
+                tracing::debug!("gar.bar not configured, garbar integration disabled");
153
+            }
154
+        }
155
+
156
+        Ok(())
157
+    }
158
+
126159
     /// Reload configuration (clears existing keybinds and rules)
127160
     pub fn reload(&self) -> LuaResult<()> {
128161
         {
gar/src/config/mod.rsmodified
@@ -32,6 +32,8 @@ pub struct Config {
3232
     pub mouse_follows_focus: bool,
3333
     // Manual bar/panel reserved space (overrides struts)
3434
     pub bar_height: u32,
35
+    // garbar integration: spawn garbar automatically if gar.bar is configured
36
+    pub bar_enabled: bool,
3537
     // Compositor visual settings (picom)
3638
     // These are stored for reference and potential dynamic picom config generation
3739
     pub corner_radius: u32,
@@ -405,6 +407,8 @@ impl Default for Config {
405407
             mouse_follows_focus: false,
406408
             // Manual bar height (0 = use struts from dock windows)
407409
             bar_height: 0,
410
+            // garbar not enabled by default (enabled when gar.bar table is set)
411
+            bar_enabled: false,
408412
             // Compositor settings (picom) - matching picom.conf defaults
409413
             corner_radius: 12,
410414
             blur_enabled: true,
gar/src/core/mod.rsmodified
@@ -45,6 +45,8 @@ pub struct WindowManager {
4545
     pub dock_struts: HashMap<XWindow, Strut>,
4646
     /// Current edge being displayed (for cursor changes on floating window edges)
4747
     pub current_edge_cursor: Option<(XWindow, crate::x11::events::ResizeEdge)>,
48
+    /// garbar child process (managed automatically when gar.bar is configured)
49
+    pub garbar_process: Option<std::process::Child>,
4850
 }
4951
 
5052
 impl WindowManager {
@@ -140,6 +142,7 @@ impl WindowManager {
140142
             focus_history: Vec::new(),
141143
             dock_struts: HashMap::new(),
142144
             current_edge_cursor: None,
145
+            garbar_process: None,
143146
         })
144147
     }
145148
 
gar/src/x11/events.rsmodified
@@ -71,6 +71,109 @@ fn stop_graphical_session() {
7171
         }
7272
     }
7373
 }
74
+
75
+/// Spawn garbar as a child process.
76
+/// Returns the child process handle if successful.
77
+fn spawn_garbar() -> Option<std::process::Child> {
78
+    tracing::info!("Spawning garbar...");
79
+
80
+    // Try to find garbar in PATH or common locations
81
+    let garbar_cmd = which_garbar().unwrap_or_else(|| "garbar".to_string());
82
+
83
+    // Determine i3 IPC socket path (same as gar's i3_server.rs)
84
+    let i3sock = std::env::var("XDG_RUNTIME_DIR")
85
+        .map(|dir| format!("{}/gar-i3.sock", dir))
86
+        .unwrap_or_else(|_| "/tmp/gar-i3.sock".to_string());
87
+
88
+    match Command::new(&garbar_cmd)
89
+        .arg("daemon")
90
+        .env("I3SOCK", &i3sock)
91
+        .spawn()
92
+    {
93
+        Ok(child) => {
94
+            tracing::info!("garbar started (PID {}), I3SOCK={}", child.id(), i3sock);
95
+            Some(child)
96
+        }
97
+        Err(e) => {
98
+            tracing::error!("Failed to spawn garbar: {}", e);
99
+            tracing::info!("Hint: Ensure garbar is installed and in PATH, or use 'cargo install --path garbar'");
100
+            None
101
+        }
102
+    }
103
+}
104
+
105
+/// Find garbar executable
106
+fn which_garbar() -> Option<String> {
107
+    // Check if garbar is in PATH
108
+    if Command::new("which")
109
+        .arg("garbar")
110
+        .output()
111
+        .map(|o| o.status.success())
112
+        .unwrap_or(false)
113
+    {
114
+        return Some("garbar".to_string());
115
+    }
116
+
117
+    // Check common cargo install location
118
+    if let Ok(home) = std::env::var("HOME") {
119
+        let cargo_bin = format!("{}/.cargo/bin/garbar", home);
120
+        if std::path::Path::new(&cargo_bin).exists() {
121
+            return Some(cargo_bin);
122
+        }
123
+    }
124
+
125
+    // Check /usr/local/bin
126
+    if std::path::Path::new("/usr/local/bin/garbar").exists() {
127
+        return Some("/usr/local/bin/garbar".to_string());
128
+    }
129
+
130
+    None
131
+}
132
+
133
+/// Stop garbar gracefully by sending SIGTERM.
134
+fn stop_garbar(child: &mut std::process::Child) {
135
+    tracing::info!("Stopping garbar (PID {})...", child.id());
136
+
137
+    // Send SIGTERM for graceful shutdown
138
+    unsafe {
139
+        libc::kill(child.id() as i32, libc::SIGTERM);
140
+    }
141
+
142
+    // Wait briefly for it to exit
143
+    match child.try_wait() {
144
+        Ok(Some(status)) => {
145
+            tracing::info!("garbar exited with status: {}", status);
146
+        }
147
+        Ok(None) => {
148
+            // Give it a moment to shut down
149
+            std::thread::sleep(std::time::Duration::from_millis(100));
150
+            match child.try_wait() {
151
+                Ok(Some(status)) => {
152
+                    tracing::info!("garbar exited with status: {}", status);
153
+                }
154
+                Ok(None) => {
155
+                    // Force kill if still running
156
+                    tracing::warn!("garbar did not exit gracefully, sending SIGKILL");
157
+                    let _ = child.kill();
158
+                }
159
+                Err(e) => {
160
+                    tracing::warn!("Error waiting for garbar: {}", e);
161
+                }
162
+            }
163
+        }
164
+        Err(e) => {
165
+            tracing::warn!("Error checking garbar status: {}", e);
166
+        }
167
+    }
168
+}
169
+
170
+/// Signal garbar to reload its configuration (SIGHUP).
171
+fn reload_garbar(child: &std::process::Child) {
172
+    tracing::info!("Signaling garbar to reload (PID {})...", child.id());
173
+    unsafe {
174
+        libc::kill(child.id() as i32, libc::SIGHUP);
175
+    }
176
+}
74177
 use x11rb::protocol::xproto::{
75178
     ButtonPressEvent, ButtonReleaseEvent, ClientMessageEvent, ConfigureRequestEvent,
76179
     ConfigureWindowAux, ConnectionExt, DestroyNotifyEvent, EnterNotifyEvent, EventMask,
@@ -1562,6 +1665,40 @@ impl WindowManager {
15621665
         // Re-apply layout with new settings
15631666
         self.apply_layout()?;
15641667
 
1668
+        // Handle garbar lifecycle based on new config
1669
+        if self.config.bar_enabled {
1670
+            // Check if garbar is still running
1671
+            let garbar_alive = if let Some(ref mut child) = self.garbar_process {
1672
+                match child.try_wait() {
1673
+                    Ok(None) => true,  // Still running
1674
+                    Ok(Some(status)) => {
1675
+                        tracing::info!("garbar exited with status {}, will respawn", status);
1676
+                        false
1677
+                    }
1678
+                    Err(e) => {
1679
+                        tracing::warn!("Failed to check garbar status: {}", e);
1680
+                        false
1681
+                    }
1682
+                }
1683
+            } else {
1684
+                false
1685
+            };
1686
+
1687
+            if garbar_alive {
1688
+                // garbar running, signal it to reload
1689
+                if let Some(ref child) = self.garbar_process {
1690
+                    reload_garbar(child);
1691
+                }
1692
+            } else {
1693
+                // garbar not running, spawn it
1694
+                self.garbar_process = spawn_garbar();
1695
+            }
1696
+        } else if let Some(ref mut child) = self.garbar_process {
1697
+            // bar_enabled is now false, stop garbar
1698
+            stop_garbar(child);
1699
+            self.garbar_process = None;
1700
+        }
1701
+
15651702
         tracing::info!("Configuration reloaded");
15661703
         Ok(())
15671704
     }
@@ -1582,6 +1719,11 @@ impl WindowManager {
15821719
         // This allows user services (like garbg) bound to graphical-session.target to start
15831720
         start_graphical_session();
15841721
 
1722
+        // Spawn garbar if gar.bar is configured
1723
+        if self.config.bar_enabled {
1724
+            self.garbar_process = spawn_garbar();
1725
+        }
1726
+
15851727
         while self.running {
15861728
             // Handle X11 events (non-blocking poll)
15871729
             while let Some(event) = self.conn.conn.poll_for_event()? {
@@ -1601,6 +1743,12 @@ impl WindowManager {
16011743
             std::thread::sleep(std::time::Duration::from_millis(10));
16021744
         }
16031745
 
1746
+        // Stop garbar if it was spawned
1747
+        if let Some(ref mut child) = self.garbar_process {
1748
+            stop_garbar(child);
1749
+        }
1750
+        self.garbar_process = None;
1751
+
16041752
         // Signal systemd that graphical session has ended
16051753
         // This stops user services bound to graphical-session.target (like garbg)
16061754
         stop_graphical_session();