gardesk/tarmac / 0fe8ed2

Browse files

keep floating windows above tiled windows during FFM focus

when focusing a tiled window while floating windows exist on the
workspace, skip AXRaise so the tiled window stays visually behind
floating windows. activate_app is still called for keyboard input
routing, but the visual z-order is preserved. floating windows get
full activation (AXRaise + activate_app) as before.

also expand enforce_floating_levels to cover all visible workspaces
across monitors and active special/scratchpad workspaces.
Authored by mfwolffe <wolffemf@dukes.jmu.edu>
SHA
0fe8ed2b39860c874ecb440ac61a585c5ff594fe
Parents
92a2007
Tree
dd91bd7

1 changed file

StatusFile+-
M tarmac/src/core/state.rs 54 21
tarmac/src/core/state.rsmodified
@@ -820,26 +820,46 @@ impl WmState {
820820
     fn focus_window_impl(&mut self, id: WindowId, activate_app: bool) {
821821
         if let Some(ax_ref) = self.ax_refs.get(&id) {
822822
             if activate_app {
823
-                // Full activation sequence for reliable cross-monitor focus:
824
-                // 1. Set AXFrontmost on the app-level AX element
825
-                // 2. NSRunningApplication.activate (brings app to foreground)
826
-                // 3. AXRaise (brings window to front of app's stack)
827
-                // 4. Set AXMain on window (makes it the key window)
828
-                // 5. Set AXFocused on window (tells AX this is focused)
829
-                //
830
-                // Setting AXFrontmost on the app AND calling activate_app
831
-                // covers both same-app (WezTerm→WezTerm) and cross-app cases.
832
-                if let Some(w) = self.registry.get(id) {
833
-                    let app_ref = unsafe {
834
-                        objc2_application_services::AXUIElement::new_application(w.app_pid)
835
-                    };
836
-                    let frontmost_key =
837
-                        objc2_core_foundation::CFString::from_static_str("AXFrontmost");
838
-                    let _ =
839
-                        crate::platform::accessibility::ax_set_bool(&app_ref, &frontmost_key, true);
840
-                    crate::platform::application::activate_app(w.app_pid);
823
+                let is_floating = self.active_workspace().is_floating(id);
824
+                let has_floating = !self.active_workspace().floating.is_empty();
825
+
826
+                if is_floating || !has_floating {
827
+                    // Full activation: no floating windows to protect, or we're
828
+                    // focusing a floating window itself — safe to raise.
829
+                    if let Some(w) = self.registry.get(id) {
830
+                        let app_ref = unsafe {
831
+                            objc2_application_services::AXUIElement::new_application(w.app_pid)
832
+                        };
833
+                        let frontmost_key =
834
+                            objc2_core_foundation::CFString::from_static_str("AXFrontmost");
835
+                        let _ = crate::platform::accessibility::ax_set_bool(
836
+                            &app_ref,
837
+                            &frontmost_key,
838
+                            true,
839
+                        );
840
+                        crate::platform::application::activate_app(w.app_pid);
841
+                    }
842
+                    let _ = ax_perform_action(ax_ref, "AXRaise");
843
+                } else {
844
+                    // Focusing a tiled window while floating windows exist:
845
+                    // activate the app for keyboard input but DON'T AXRaise,
846
+                    // so the tiled window stays behind floating windows.
847
+                    if let Some(w) = self.registry.get(id) {
848
+                        let app_ref = unsafe {
849
+                            objc2_application_services::AXUIElement::new_application(w.app_pid)
850
+                        };
851
+                        let frontmost_key =
852
+                            objc2_core_foundation::CFString::from_static_str("AXFrontmost");
853
+                        let _ = crate::platform::accessibility::ax_set_bool(
854
+                            &app_ref,
855
+                            &frontmost_key,
856
+                            true,
857
+                        );
858
+                        crate::platform::application::activate_app(w.app_pid);
859
+                    }
860
+                    // Skip AXRaise — floating windows stay visually on top
841861
                 }
842
-                let _ = ax_perform_action(ax_ref, "AXRaise");
862
+
843863
                 let main_key = objc2_core_foundation::CFString::from_static_str("AXMain");
844864
                 let _ = crate::platform::accessibility::ax_set_bool(ax_ref, &main_key, true);
845865
                 let focused_key = objc2_core_foundation::CFString::from_static_str("AXFocused");
@@ -995,8 +1015,21 @@ impl WmState {
9951015
     /// Called after every focus change since app activation can reset ordering.
9961016
     fn enforce_floating_levels(&self) {
9971017
         use crate::platform::skylight::{K_CG_FLOATING_WINDOW_LEVEL, set_window_level};
998
-        for fw in &self.active_workspace().floating {
999
-            set_window_level(fw.id, K_CG_FLOATING_WINDOW_LEVEL);
1018
+        // Reassert floating level on all visible workspaces (all monitors)
1019
+        for monitor in &self.monitors {
1020
+            for fw in &self.workspaces.get(monitor.active_workspace).floating {
1021
+                set_window_level(fw.id, K_CG_FLOATING_WINDOW_LEVEL);
1022
+            }
1023
+        }
1024
+        // Also cover active special/scratchpad workspaces
1025
+        for special_idx in self.active_specials.iter().flatten() {
1026
+            for fw in &self.workspaces.get(*special_idx).floating {
1027
+                set_window_level(fw.id, K_CG_FLOATING_WINDOW_LEVEL);
1028
+            }
1029
+            // Special workspace tiled windows also float visually
1030
+            for wid in self.workspaces.get(*special_idx).tree.windows() {
1031
+                set_window_level(wid, K_CG_FLOATING_WINDOW_LEVEL);
1032
+            }
10001033
         }
10011034
     }
10021035