Wayland compositor (wlroots)
git clone https://git.lucas.co/cce-compositor.git
refactor: overlay arms to policy dispatch; focus helper delegates to rule
The OverlayLeft/Right legacy arms are deleted (DefaultPolicy emits
SetOverlayPosition, applied as the layout string write) and
focus_next_visible_window keeps only eligibility + the id-to-pointer
round trip, delegating the pick to focus::next_visible_focus.
Co-Authored-By: Claude Fable 5 <[email protected]>
src/server/window_manager.rs | 72 ++++++++++++++++++++++++--------------------
1 file changed, 39 insertions(+), 33 deletions(-)
diff --git a/src/server/window_manager.rs b/src/server/window_manager.rs
index 2e33ef4..664f38a 100644
--- a/src/server/window_manager.rs
+++ b/src/server/window_manager.rs
@@ -1798,33 +1798,41 @@ impl WindowManager {
self.focus_history.retain(|&w| w != window);
}
+ /// Refocus after the focused window goes away, by the policy crate's
+ /// next-visible rule (most recent eligible history entry, else the last
+ /// eligible window in window order, else clear focus). This side owns
+ /// eligibility (mapped, not minimized, not status/background).
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.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().map_or(false, |id| id.starts_with("cce-status"));
- let is_wallpaper = app_id.as_deref() == Some("cce-wallpaper");
- if !is_status_bar && !is_wallpaper {
- 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().map_or(false, |id| id.starts_with("cce-status"));
- let is_wallpaper = app_id.as_deref() == Some("cce-wallpaper");
- if !is_status_bar && !is_wallpaper {
- next_focus = w;
- }
- }
+ let eligible = |w: *mut Window| -> bool {
+ if (*w).closed || (*w).minimized || !matches!((*w).state, crate::window::WindowState::Mapped) {
+ return false;
}
- }
- if !next_focus.is_null() {
- (*seat).focus(crate::seat::Focus::Window(next_focus));
+ let app_id = (*w).get_app_id_string();
+ let is_status_bar = app_id.as_deref().map_or(false, |id| id.starts_with("cce-status"));
+ let is_wallpaper = app_id.as_deref() == Some("cce-wallpaper");
+ !is_status_bar && !is_wallpaper
+ };
+ let candidate = |w: *mut Window| crate::policy::focus::FocusCandidate {
+ id: crate::policy::api::WindowId((*w).ref_key),
+ eligible: eligible(w),
+ };
+ let history: Vec<_> = self
+ .focus_history
+ .iter()
+ .filter(|&&w| !w.is_null())
+ .map(|&w| candidate(w))
+ .collect();
+ let windows: Vec<_> = self
+ .windows
+ .iter()
+ .filter(|&&w| !w.is_null())
+ .map(|&w| candidate(w))
+ .collect();
+ let next = crate::policy::focus::next_visible_focus(&history, &windows)
+ .and_then(|id| self.windows.get(id.0).copied())
+ .unwrap_or(std::ptr::null_mut());
+ if !next.is_null() {
+ (*seat).focus(crate::seat::Focus::Window(next));
} else {
(*seat).focus(crate::seat::Focus::None);
}
@@ -2123,14 +2131,6 @@ impl WindowManager {
self.start_clean_exit();
}
Action::LayoutNext => {}
- Action::OverlayLeft => {
- self.layout.overlay_position = "left".to_string();
- self.dirty_windowing();
- }
- Action::OverlayRight => {
- self.layout.overlay_position = "right".to_string();
- self.dirty_windowing();
- }
_ => {}
}
}
@@ -3707,6 +3707,12 @@ impl crate::policy::api::Compositor for WindowManager {
}
}
}
+ Command::SetOverlayPosition(side) => {
+ self.layout.overlay_position = match side {
+ crate::policy::api::OverlaySide::Left => "left".to_string(),
+ crate::policy::api::OverlaySide::Right => "right".to_string(),
+ };
+ }
Command::Relayout => self.dirty_windowing(),
Command::RefreshCamera => {
if matches!(self.state, WindowManagerState::Idle) {