gardesk/gar / 2af2ced

Browse files

add monitor_order config for custom monitor ordering

Authored by espadonne
SHA
2af2ced1d6d942a1cf19f30cbfb16320ec9b0e6e
Parents
a880f57
Tree
011fa24

3 changed files

StatusFile+-
M gar/src/config/lua.rs 15 0
M gar/src/config/mod.rs 5 0
M gar/src/core/mod.rs 36 0
gar/src/config/lua.rsmodified
@@ -491,6 +491,21 @@ impl LuaConfig {
491491
                         }
492492
                     }
493493
                 }
494
+                // Monitor ordering: list of monitor names in left-to-right order
495
+                "monitor_order" => {
496
+                    if let Value::Table(t) = value {
497
+                        let mut order = Vec::new();
498
+                        for pair in t.pairs::<i64, String>() {
499
+                            if let Ok((_, name)) = pair {
500
+                                order.push(name);
501
+                            }
502
+                        }
503
+                        if !order.is_empty() {
504
+                            tracing::info!("Monitor order configured: {:?}", order);
505
+                            state.config.monitor_order = order;
506
+                        }
507
+                    }
508
+                }
494509
                 _ => {
495510
                     tracing::warn!("Unknown config key: {}", key);
496511
                 }
gar/src/config/mod.rsmodified
@@ -35,6 +35,9 @@ pub struct Config {
3535
     pub bar_height: u32,
3636
     // garbar integration: spawn garbar automatically if gar.bar is configured
3737
     pub bar_enabled: bool,
38
+    // Monitor ordering: list of monitor names in desired left-to-right order
39
+    // If empty, monitors are sorted by X position (default)
40
+    pub monitor_order: Vec<String>,
3841
     // Compositor visual settings (picom)
3942
     // These are stored for reference and potential dynamic picom config generation
4043
     pub corner_radius: u32,
@@ -410,6 +413,8 @@ impl Default for Config {
410413
             bar_height: 0,
411414
             // garbar not enabled by default (enabled when gar.bar table is set)
412415
             bar_enabled: false,
416
+            // Monitor order: empty = sort by X position
417
+            monitor_order: Vec::new(),
413418
             // Compositor settings (picom) - matching picom.conf defaults
414419
             corner_radius: 12,
415420
             blur_enabled: true,
gar/src/core/mod.rsmodified
@@ -120,6 +120,9 @@ impl WindowManager {
120120
             ));
121121
         }
122122
 
123
+        // Sort monitors by configured order (if set)
124
+        Self::sort_monitors_by_config(&mut monitors, &config.monitor_order);
125
+
123126
         // i3-style: each monitor starts with one workspace (1, 2, 3...)
124127
         // Any workspace can be moved to any monitor dynamically
125128
         for (i, monitor) in monitors.iter_mut().enumerate() {
@@ -230,6 +233,36 @@ impl WindowManager {
230233
         }
231234
     }
232235
 
236
+    /// Sort monitors by configured order (from gar.set("monitor_order", {...})).
237
+    /// If config order is empty or doesn't cover all monitors, falls back to X position.
238
+    fn sort_monitors_by_config(monitors: &mut Vec<Monitor>, config_order: &[String]) {
239
+        if config_order.is_empty() {
240
+            // No custom order - sort by X position (default)
241
+            monitors.sort_by_key(|m| m.geometry.x);
242
+            return;
243
+        }
244
+
245
+        monitors.sort_by(|a, b| {
246
+            let pos_a = config_order.iter().position(|n| n == &a.name);
247
+            let pos_b = config_order.iter().position(|n| n == &b.name);
248
+
249
+            match (pos_a, pos_b) {
250
+                (Some(pa), Some(pb)) => pa.cmp(&pb),
251
+                (Some(_), None) => std::cmp::Ordering::Less,
252
+                (None, Some(_)) => std::cmp::Ordering::Greater,
253
+                (None, None) => a.geometry.x.cmp(&b.geometry.x),
254
+            }
255
+        });
256
+
257
+        tracing::info!("Sorted monitors by config order:");
258
+        for (i, m) in monitors.iter().enumerate() {
259
+            tracing::info!(
260
+                "  Monitor {}: '{}' at ({}, {}) size {}x{}",
261
+                i, m.name, m.geometry.x, m.geometry.y, m.geometry.width, m.geometry.height
262
+            );
263
+        }
264
+    }
265
+
233266
     /// Calculate struts (reserved space for panels/docks) for a monitor.
234267
     fn calculate_struts(&self, _monitor_idx: usize) -> (u32, u32, u32, u32) {
235268
         if self.config.bar_height > 0 {
@@ -268,6 +301,9 @@ impl WindowManager {
268301
             ));
269302
         }
270303
 
304
+        // Sort monitors by configured order (if set)
305
+        Self::sort_monitors_by_config(&mut new_monitors, &self.config.monitor_order);
306
+
271307
         // Try to preserve workspace assignments from old monitors
272308
         // If we have more monitors now, new ones get next available workspaces
273309
         let old_mon_count = self.monitors.len();