gardesk/tarmac / da1c094

Browse files

Route discovered windows to their actual monitor's workspace

discover_and_observe was sending every discovered window through
add_window_to_active without specifying a target workspace, so each
window landed in active_workspace_mut() — the workspace on the
monitor under the cursor at startup. Windows on other monitors got
inserted into the cursor monitor's BSP tree, then apply_layout asked
AX to move them across displays; some apps (Ghostty especially)
either reject or race that cross-display move and stay physically on
their original monitor while remaining logically in the wrong
workspace's tree. Visible to the user as 'two windows reachable via
stack-cycle but never joining the tile.'

Fix is local to the discovery loop: compute each window's monitor
from its center point and temporarily set focused_monitor to that
monitor for the duration of the add_window_to_active call. Restore
focused_monitor after the loop. add_window_to_active itself is
unchanged, so the rule path and runtime Created path keep working
exactly as before.

Refs memory observation #2542.
Authored by mfwolffe <wolffemf@dukes.jmu.edu>
SHA
da1c0948b29b641b434e6b4c3f83819a44fec101
Parents
b9e6405
Tree
74075ea

1 changed file

StatusFile+-
M tarmac/src/core/state.rs 20 0
tarmac/src/core/state.rsmodified
@@ -318,7 +318,26 @@ impl WmState {
318
         );
318
         );
319
 
319
 
320
         let windows = discover_all_windows();
320
         let windows = discover_all_windows();
321
+        // Route each discovered window to the workspace active on the
322
+        // monitor it physically lives on, not always the cursor's
323
+        // monitor's workspace. add_window_to_active resolves the target
324
+        // workspace via active_workspace_mut() which reads
325
+        // focused_monitor — temporarily point that at each window's
326
+        // monitor for the duration of the insert, then restore.
327
+        // Without this, windows on non-cursor monitors get inserted
328
+        // into the cursor monitor's BSP tree and apply_layout asks AX
329
+        // to move them across displays; some apps (Ghostty in
330
+        // particular) fail or race that move and end up logically in
331
+        // the wrong workspace's tree while remaining physically on
332
+        // their original monitor — visible to the user as windows
333
+        // that are reachable via stack-cycle but never join the tile.
334
+        let original_focused = self.focused_monitor;
321
         for w in &windows {
335
         for w in &windows {
336
+            let cx = w.x + w.width / 2.0;
337
+            let cy = w.y + w.height / 2.0;
338
+            let mi = super::monitor::index_at_point(&self.monitors, cx, cy)
339
+                .unwrap_or(original_focused);
340
+            self.focused_monitor = mi;
322
             self.add_window_to_active(
341
             self.add_window_to_active(
323
                 &w.id,
342
                 &w.id,
324
                 w.app_pid,
343
                 w.app_pid,
@@ -334,6 +353,7 @@ impl WmState {
334
                 w.ax_ref.clone(),
353
                 w.ax_ref.clone(),
335
             );
354
             );
336
         }
355
         }
356
+        self.focused_monitor = original_focused;
337
 
357
 
338
         tracing::info!(
358
         tracing::info!(
339
             windows = self.registry.count(),
359
             windows = self.registry.count(),