add monitor_order config for custom monitor ordering
- SHA
2af2ced1d6d942a1cf19f30cbfb16320ec9b0e6e- Parents
-
a880f57 - Tree
011fa24
2af2ced
2af2ced1d6d942a1cf19f30cbfb16320ec9b0e6ea880f57
011fa24| Status | File | + | - |
|---|---|---|---|
| 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 { | ||
| 491 | 491 | } |
| 492 | 492 | } |
| 493 | 493 | } |
| 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 | + } | |
| 494 | 509 | _ => { |
| 495 | 510 | tracing::warn!("Unknown config key: {}", key); |
| 496 | 511 | } |
gar/src/config/mod.rsmodified@@ -35,6 +35,9 @@ pub struct Config { | ||
| 35 | 35 | pub bar_height: u32, |
| 36 | 36 | // garbar integration: spawn garbar automatically if gar.bar is configured |
| 37 | 37 | 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>, | |
| 38 | 41 | // Compositor visual settings (picom) |
| 39 | 42 | // These are stored for reference and potential dynamic picom config generation |
| 40 | 43 | pub corner_radius: u32, |
@@ -410,6 +413,8 @@ impl Default for Config { | ||
| 410 | 413 | bar_height: 0, |
| 411 | 414 | // garbar not enabled by default (enabled when gar.bar table is set) |
| 412 | 415 | bar_enabled: false, |
| 416 | + // Monitor order: empty = sort by X position | |
| 417 | + monitor_order: Vec::new(), | |
| 413 | 418 | // Compositor settings (picom) - matching picom.conf defaults |
| 414 | 419 | corner_radius: 12, |
| 415 | 420 | blur_enabled: true, |
gar/src/core/mod.rsmodified@@ -120,6 +120,9 @@ impl WindowManager { | ||
| 120 | 120 | )); |
| 121 | 121 | } |
| 122 | 122 | |
| 123 | + // Sort monitors by configured order (if set) | |
| 124 | + Self::sort_monitors_by_config(&mut monitors, &config.monitor_order); | |
| 125 | + | |
| 123 | 126 | // i3-style: each monitor starts with one workspace (1, 2, 3...) |
| 124 | 127 | // Any workspace can be moved to any monitor dynamically |
| 125 | 128 | for (i, monitor) in monitors.iter_mut().enumerate() { |
@@ -230,6 +233,36 @@ impl WindowManager { | ||
| 230 | 233 | } |
| 231 | 234 | } |
| 232 | 235 | |
| 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 | + | |
| 233 | 266 | /// Calculate struts (reserved space for panels/docks) for a monitor. |
| 234 | 267 | fn calculate_struts(&self, _monitor_idx: usize) -> (u32, u32, u32, u32) { |
| 235 | 268 | if self.config.bar_height > 0 { |
@@ -268,6 +301,9 @@ impl WindowManager { | ||
| 268 | 301 | )); |
| 269 | 302 | } |
| 270 | 303 | |
| 304 | + // Sort monitors by configured order (if set) | |
| 305 | + Self::sort_monitors_by_config(&mut new_monitors, &self.config.monitor_order); | |
| 306 | + | |
| 271 | 307 | // Try to preserve workspace assignments from old monitors |
| 272 | 308 | // If we have more monitors now, new ones get next available workspaces |
| 273 | 309 | let old_mon_count = self.monitors.len(); |