git.lucas.co / cce-compositor
Wayland compositor (wlroots)
git clone https://git.lucas.co/cce-compositor.git

commit5708914d67c05cf67c424772124fdfaabaafd05d
parentcdfdaa9af5
authorLucas Galante <[email protected]>
date2026-06-25 12:45
Focus most recently focused window when closing pinned or system windows

 src/server/seat.rs           |  5 +++++
 src/server/server.rs         |  1 +
 src/server/window.rs         |  1 +
 src/server/window_manager.rs | 28 +++++++++++++++++++++++++++-
 4 files changed, 34 insertions(+), 1 deletion(-)

diff --git a/src/server/seat.rs b/src/server/seat.rs
index 3e1b1e2..405c4eb 100644
--- a/src/server/seat.rs
+++ b/src/server/seat.rs
@@ -357,6 +357,11 @@ impl Seat {
         }
 
         self.focused = new_focus;
+        if let Focus::Window(window) = new_focus {
+            if !window.is_null() {
+                (*self.server).wm.record_focus(window);
+            }
+        }
         (*self.server).wm.update_status();
 
         match new_focus {
diff --git a/src/server/server.rs b/src/server/server.rs
index 07a0bd2..fd13441 100644
--- a/src/server/server.rs
+++ b/src/server/server.rs
@@ -826,6 +826,7 @@ impl Default for Server {
             std::ptr::write_bytes(server.as_mut_ptr(), 0, 1);
             // Overwrite collections and SlotMap with valid instances to avoid UB/segfaults from null pointers
             std::ptr::write(&mut (*server.as_mut_ptr()).wm.windows, crate::slotmap::SlotMap::new());
+            std::ptr::write(&mut (*server.as_mut_ptr()).wm.focus_history, Vec::new());
             std::ptr::write(&mut (*server.as_mut_ptr()).wm.mode_rules, Vec::new());
             std::ptr::write(&mut (*server.as_mut_ptr()).wm.keybinds, Vec::new());
             std::ptr::write(&mut (*server.as_mut_ptr()).wm.pointer_binds, Vec::new());
diff --git a/src/server/window.rs b/src/server/window.rs
index f31702c..7368a58 100644
--- a/src/server/window.rs
+++ b/src/server/window.rs
@@ -790,6 +790,7 @@ impl Window {
 
         (*window).node.deinit();
 
+        (*(*window).server).wm.remove_from_history(window);
         (*(*window).server).wm.windows.remove((*window).ref_key);
 
         let _ = Box::from_raw(window);
diff --git a/src/server/window_manager.rs b/src/server/window_manager.rs
index 741a36c..463d2ae 100644
--- a/src/server/window_manager.rs
+++ b/src/server/window_manager.rs
@@ -71,6 +71,7 @@ pub struct WindowManager {
     pub object: *mut ffi::wl_resource,
     pub state: WindowManagerState,
     pub windows: SlotMap<*mut Window>,
+    pub focus_history: Vec<*mut Window>,
     pub scheduled: WindowManagerScheduled,
     pub sent: WindowManagerSent,
     pub rendering_scheduled: WindowManagerRenderingScheduled,
@@ -121,6 +122,7 @@ impl WindowManager {
         self.object = std::ptr::null_mut();
         self.state = WindowManagerState::Idle;
         self.windows = SlotMap::new();
+        self.focus_history = Vec::new();
         self.scheduled = WindowManagerScheduled {
             dirty: false,
             dirty_lazy: false,
@@ -1233,14 +1235,38 @@ fn get_closest_tag(x: f64, y: f64) -> i32 {
         }
     }
 
+    pub unsafe fn record_focus(&mut self, window: *mut Window) {
+        if window.is_null() {
+            return;
+        }
+        self.focus_history.retain(|&w| w != window);
+        self.focus_history.insert(0, window);
+    }
+
+    pub unsafe fn remove_from_history(&mut self, window: *mut Window) {
+        self.focus_history.retain(|&w| w != window);
+    }
+
     pub unsafe fn focus_next_visible_window(&mut self, seat: *mut crate::seat::Seat) {
         let mut next_focus: *mut Window = std::ptr::null_mut();
-        for &w in self.windows.iter() {
+        for &w in self.focus_history.iter() {
             if !w.is_null() && !(*w).closed && !(*w).minimized && matches!((*w).state, crate::window::WindowState::Mapped) {
                 let app_id = (*w).get_app_id_string();
                 let is_status_bar = app_id.as_deref() == Some("cce-status-interface");
                 if !is_status_bar {
                     next_focus = w;
+                    break;
+                }
+            }
+        }
+        if next_focus.is_null() {
+            for &w in self.windows.iter() {
+                if !w.is_null() && !(*w).closed && !(*w).minimized && matches!((*w).state, crate::window::WindowState::Mapped) {
+                    let app_id = (*w).get_app_id_string();
+                    let is_status_bar = app_id.as_deref() == Some("cce-status-interface");
+                    if !is_status_bar {
+                        next_focus = w;
+                    }
                 }
             }
         }