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

commit21e33d61b3bf0426d841d0e42ee13717adf22899
parent5edafc3efb
authorLucas Galante <[email protected]>
date2026-06-15 12:02
Fix compositor focus capture in monolithic mode and untangle pointer hover focus

 src/server/cursor.rs | 29 ++---------------------------
 src/server/seat.rs   | 35 +++++++++++++++++++++++++++++++++++
 2 files changed, 37 insertions(+), 27 deletions(-)

diff --git a/src/server/cursor.rs b/src/server/cursor.rs
index fedd515..7d47b2b 100644
--- a/src/server/cursor.rs
+++ b/src/server/cursor.rs
@@ -309,33 +309,8 @@ impl Cursor {
     }
 
     pub unsafe fn update_hovered(&mut self) {
-        let lx = self.x();
-        let ly = self.y();
-        let server = (*self.seat).server;
-
-        let at_res = (*server).scene.at(lx, ly);
-        let current_focused = &(*self.seat).focused;
-
-        if let Some(result) = at_res {
-            // Find window/surface under cursor and focus
-            match result.data {
-                SceneNodeDataVal::Window(_) => {
-                    // Update hover or click focus
-                }
-                SceneNodeDataVal::LayerSurface(_) => {
-                    let surface = result.surface;
-                    if !matches!(current_focused, Focus::LayerSurface(s) if *s == surface) {
-                        (*self.seat).focus(Focus::LayerSurface(surface));
-                    }
-                }
-                SceneNodeDataVal::LockSurface(lock_surf) => {
-                    if !matches!(current_focused, Focus::LockSurface(s) if *s == lock_surf) {
-                        (*self.seat).focus(Focus::LockSurface(lock_surf));
-                    }
-                }
-                _ => {}
-            }
-        }
+        // Keyboard focus remains stable on pointer hover/motion.
+        // It is only changed on mapping, click, touch, or shortcut focus transitions.
     }
 
     pub unsafe fn update_state(&mut self) {
diff --git a/src/server/seat.rs b/src/server/seat.rs
index 1fd310d..28d3204 100644
--- a/src/server/seat.rs
+++ b/src/server/seat.rs
@@ -302,6 +302,37 @@ impl Seat {
             return;
         }
 
+        // If an exclusive layer surface is active and scheduled for focus,
+        // block any window manager or other client focus requests (via focus_requested)
+        // from stealing focus back to a regular window or shell surface.
+        if self.focus_requested {
+            if let crate::layer_shell::LayerShellSeatFocus::Exclusive(key) = self.layer_shell.scheduled_focus {
+                let server = self.server;
+                if let Some(&layer_surface) = (*server).layer_shell.surfaces.get(key) {
+                    let wlr_surf = (*(*layer_surface).wlr_layer_surface).surface;
+                    if new_focus != Focus::LayerSurface(wlr_surf) {
+                        if let Focus::Window(_) | Focus::ShellSurface(_) | Focus::OverrideRedirect(_) | Focus::None = new_focus {
+                            log::info!("[FocusDebug] Blocking window manager focus request because Exclusive layer surface {:?} is active", key);
+                            return;
+                        }
+                    }
+                }
+            }
+        }
+
+        match new_focus {
+            Focus::None => log::info!("[FocusDebug] Seat::focus set to None"),
+            Focus::LayerSurface(surface) => log::info!("[FocusDebug] Seat::focus set to LayerSurface {:?}", surface),
+            Focus::Window(window) => {
+                let title = if window.is_null() { "null".to_string() } else { (*window).get_title_string().unwrap_or_default() };
+                let app_id = if window.is_null() { "null".to_string() } else { (*window).get_app_id_string().unwrap_or_default() };
+                log::info!("[FocusDebug] Seat::focus set to Window {:?} (title={:?}, app_id={:?})", window, title, app_id);
+            }
+            Focus::LockSurface(lock) => log::info!("[FocusDebug] Seat::focus set to LockSurface {:?}", lock),
+            Focus::OverrideRedirect(or) => log::info!("[FocusDebug] Seat::focus set to OverrideRedirect {:?}", or),
+            Focus::ShellSurface(ss) => log::info!("[FocusDebug] Seat::focus set to ShellSurface {:?}", ss),
+        }
+
         match self.focused {
             Focus::None => {}
             Focus::LayerSurface(_) | Focus::Window(_) | Focus::LockSurface(_) | Focus::OverrideRedirect(_) | Focus::ShellSurface(_) => {
@@ -642,6 +673,10 @@ impl Seat {
                     self.wm_sent_y = y;
                 }
             }
+        } else {
+            crate::server::wl_list_remove(&mut self.link_sent as *mut ffi::wl_list as *mut crate::server::WlList);
+            let sent_seats = &mut (*self.server).wm.sent.seats as *mut ffi::wl_list as *mut crate::server::WlList;
+            crate::server::wl_list_insert((*sent_seats).prev, &mut self.link_sent as *mut ffi::wl_list as *mut crate::server::WlList);
         }
     }