Wayland compositor (wlroots)
git clone https://git.lucas.co/cce-compositor.git
refactor: Toggle arm replaced by policy dispatch
execute_action passes the command arg through to Policy::action; the
Toggle arm is deleted (DefaultPolicy decides match-existing-vs-spawn)
and config::extract_program_name moves to the policy crate as
actions::program_name, its only caller. build_action_ctx additionally
captures app_id/title/mapped; Command::Spawn applies by re-entering
execute_action's Spawn arm (fork + sh -c).
Co-Authored-By: Claude Fable 5 <[email protected]>
src/server/config.rs | 13 ----------
src/server/window_manager.rs | 56 ++++++--------------------------------------
2 files changed, 7 insertions(+), 62 deletions(-)
diff --git a/src/server/config.rs b/src/server/config.rs
index 8da85b4..e3a8b85 100644
--- a/src/server/config.rs
+++ b/src/server/config.rs
@@ -838,19 +838,6 @@ pub fn default_state_path() -> Option<String> {
}
}
-pub fn extract_program_name(cmd: &str) -> String {
- let trimmed = cmd.trim();
- if trimmed.is_empty() {
- return String::new();
- }
- let first_token = trimmed.split_whitespace().next().unwrap_or("");
- if let Some(pos) = first_token.rfind('/') {
- first_token[pos+1..].to_string()
- } else {
- first_token.to_string()
- }
-}
-
fn expand_env_vars(s: &str) -> String {
let mut result = String::with_capacity(s.len());
let chars: Vec<char> = s.chars().collect();
diff --git a/src/server/window_manager.rs b/src/server/window_manager.rs
index 45226b4..02c8db9 100644
--- a/src/server/window_manager.rs
+++ b/src/server/window_manager.rs
@@ -770,6 +770,9 @@ impl WindowManager {
let focus_cyclable = rendered.contains(&(w as usize)) && !(*w).minimized && !is_status;
windows.push(ActionWindow {
id: WindowId((*w).ref_key),
+ app_id,
+ title: (*w).get_title_string(),
+ mapped: matches!((*w).state, crate::window::WindowState::Mapped),
x: (*w).virtual_x,
y: (*w).virtual_y,
w: if (*w).box_geom.width > 0 { (*w).box_geom.width as f64 } else { 800.0 },
@@ -2057,7 +2060,7 @@ impl WindowManager {
{
use crate::policy::api::{Compositor, Policy};
let ctx = self.build_action_ctx();
- let cmds = crate::policy::actions::DefaultPolicy.action(&ctx, *action);
+ let cmds = crate::policy::actions::DefaultPolicy.action(&ctx, *action, command);
if !cmds.is_empty() {
for cmd in &cmds {
self.apply(cmd);
@@ -2092,54 +2095,6 @@ impl WindowManager {
}
}
}
- Action::Toggle => {
- if let Some(cmd) = command {
- let prog_name = crate::config::extract_program_name(cmd);
- let mut matched_win: *mut Window = std::ptr::null_mut();
- for &w in self.windows.iter() {
- if !w.is_null() && !(*w).closed && matches!((*w).state, crate::window::WindowState::Mapped) {
- let aid = (*w).get_app_id_string();
- let title = (*w).get_title_string();
-
- let mut match_aid = false;
- if let Some(ref aid_str) = aid {
- let aid_lower = aid_str.to_lowercase();
- let prog_lower = prog_name.to_lowercase();
- if aid_lower == prog_lower || aid_lower.contains(&prog_lower) || prog_lower.contains(&aid_lower) {
- match_aid = true;
- }
- } else if let Some(ref title_str) = title {
- let title_lower = title_str.to_lowercase();
- let prog_lower = prog_name.to_lowercase();
- if title_lower.contains(&prog_lower) {
- match_aid = true;
- }
- }
- if match_aid {
- matched_win = w;
- break;
- }
- }
- }
-
- if !matched_win.is_null() {
- let aid = (*matched_win).get_app_id_string().unwrap_or_default();
- log::info!("toggle: closing window {:?}", aid);
- (*matched_win).close();
- if let Some(seat) = self.first_seat() {
- if let crate::seat::Focus::Window(fw) = (*seat).focused {
- if fw == matched_win {
- self.focus_next_visible_window(seat);
- }
- }
- }
- self.dirty_windowing();
- } else {
- log::info!("toggle: spawning {}", cmd);
- self.execute_action(&Action::Spawn, Some(cmd));
- }
- }
- }
Action::WindowSwitcher => {
self.launch_window_switcher(false);
}
@@ -3696,6 +3651,9 @@ impl crate::policy::api::Compositor for WindowManager {
use crate::policy::api::Command;
unsafe {
match *cmd {
+ Command::Spawn(ref cmdline) => {
+ self.execute_action(&crate::config::Action::Spawn, Some(cmdline));
+ }
Command::SetCamera { camera, overview } => {
self.desk_pan_x = camera.pan_x;
self.desk_pan_y = camera.pan_y;