From 04cde3f757e09be3832442e5b6fd5f86631fa3c8 Mon Sep 17 00:00:00 2001 From: LGUG2Z Date: Sun, 29 Sep 2024 18:09:29 -0700 Subject: [PATCH] feat(wm): add all matching strats for ws rules This commit ensures that the full range of matching strategies for both Simple and Composite matching rules will be respected for both initial and persistent workspace rules. The generate-static-config command will no longer attempt to populate workspace rules, and will likely slowly be deprecated as the overwhelming majority have users have already migrated to the static configuration file format. --- komorebi/src/core/config_generation.rs | 8 ++ komorebi/src/lib.rs | 7 +- komorebi/src/process_command.rs | 163 ++++++++++++------------ komorebi/src/static_config.rs | 168 ++++++------------------- komorebi/src/window_manager.rs | 99 ++++++++------- 5 files changed, 178 insertions(+), 267 deletions(-) diff --git a/komorebi/src/core/config_generation.rs b/komorebi/src/core/config_generation.rs index 71ea8f9d..fe67134f 100644 --- a/komorebi/src/core/config_generation.rs +++ b/komorebi/src/core/config_generation.rs @@ -59,6 +59,14 @@ pub enum MatchingRule { Composite(Vec), } +#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize, JsonSchema)] +pub struct WorkspaceMatchingRule { + pub monitor_index: usize, + pub workspace_index: usize, + pub matching_rule: MatchingRule, + pub initial_only: bool, +} + #[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize, JsonSchema)] pub struct IdWithIdentifier { pub kind: ApplicationIdentifier, diff --git a/komorebi/src/lib.rs b/komorebi/src/lib.rs index 2ada8ddc..b8076102 100644 --- a/komorebi/src/lib.rs +++ b/komorebi/src/lib.rs @@ -62,6 +62,7 @@ pub use windows_api::*; use crate::core::config_generation::IdWithIdentifier; use crate::core::config_generation::MatchingRule; use crate::core::config_generation::MatchingStrategy; +use crate::core::config_generation::WorkspaceMatchingRule; use color_eyre::Result; use os_info::Version; use parking_lot::Mutex; @@ -74,8 +75,6 @@ use which::which; use winreg::enums::HKEY_CURRENT_USER; use winreg::RegKey; -type WorkspaceRule = (usize, usize, bool); - lazy_static! { static ref HIDDEN_HWNDS: Arc>> = Arc::new(Mutex::new(vec![])); static ref LAYERED_WHITELIST: Arc>> = Arc::new(Mutex::new(vec![ @@ -135,8 +134,8 @@ lazy_static! { Arc::new(Mutex::new(HashMap::new())); static ref DISPLAY_INDEX_PREFERENCES: Arc>> = Arc::new(Mutex::new(HashMap::new())); - static ref WORKSPACE_RULES: Arc>> = - Arc::new(Mutex::new(HashMap::new())); + static ref WORKSPACE_MATCHING_RULES: Arc>> = + Arc::new(Mutex::new(Vec::new())); static ref REGEX_IDENTIFIERS: Arc>> = Arc::new(Mutex::new(HashMap::new())); static ref MANAGE_IDENTIFIERS: Arc>> = Arc::new(Mutex::new(vec![])); diff --git a/komorebi/src/process_command.rs b/komorebi/src/process_command.rs index 2d137ea5..2169be97 100644 --- a/komorebi/src/process_command.rs +++ b/komorebi/src/process_command.rs @@ -44,6 +44,7 @@ use crate::border_manager; use crate::border_manager::IMPLEMENTATION; use crate::border_manager::STYLE; use crate::colour::Rgb; +use crate::config_generation::WorkspaceMatchingRule; use crate::current_virtual_desktop; use crate::notify_subscribers; use crate::stackbar_manager; @@ -81,7 +82,7 @@ use crate::SUBSCRIPTION_SOCKETS; use crate::TCP_CONNECTIONS; use crate::TRAY_AND_MULTI_WINDOW_IDENTIFIERS; use crate::WINDOWS_11; -use crate::WORKSPACE_RULES; +use crate::WORKSPACE_MATCHING_RULES; use stackbar_manager::STACKBAR_FOCUSED_TEXT_COLOUR; use stackbar_manager::STACKBAR_LABEL; use stackbar_manager::STACKBAR_MODE; @@ -269,58 +270,101 @@ impl WindowManager { self.set_workspace_padding(monitor_idx, workspace_idx, size)?; } } - SocketMessage::InitialWorkspaceRule(_, ref id, monitor_idx, workspace_idx) => { - self.handle_initial_workspace_rules(id, monitor_idx, workspace_idx)?; - } - SocketMessage::InitialNamedWorkspaceRule(_, ref id, ref workspace) => { - if let Some((monitor_idx, workspace_idx)) = - self.monitor_workspace_index_by_name(workspace) - { - self.handle_initial_workspace_rules(id, monitor_idx, workspace_idx)?; + SocketMessage::InitialWorkspaceRule(identifier, ref id, monitor_idx, workspace_idx) => { + let mut workspace_rules = WORKSPACE_MATCHING_RULES.lock(); + let workspace_matching_rule = WorkspaceMatchingRule { + monitor_index: monitor_idx, + workspace_index: workspace_idx, + matching_rule: MatchingRule::Simple(IdWithIdentifier { + kind: identifier, + id: id.to_string(), + matching_strategy: Some(MatchingStrategy::Legacy), + }), + initial_only: true, + }; + + if !workspace_rules.contains(&workspace_matching_rule) { + workspace_rules.push(workspace_matching_rule); } } - SocketMessage::WorkspaceRule(_, ref id, monitor_idx, workspace_idx) => { - self.handle_definitive_workspace_rules(id, monitor_idx, workspace_idx)?; - } - SocketMessage::NamedWorkspaceRule(_, ref id, ref workspace) => { + SocketMessage::InitialNamedWorkspaceRule(identifier, ref id, ref workspace) => { if let Some((monitor_idx, workspace_idx)) = self.monitor_workspace_index_by_name(workspace) { - self.handle_definitive_workspace_rules(id, monitor_idx, workspace_idx)?; + let mut workspace_rules = WORKSPACE_MATCHING_RULES.lock(); + let workspace_matching_rule = WorkspaceMatchingRule { + monitor_index: monitor_idx, + workspace_index: workspace_idx, + matching_rule: MatchingRule::Simple(IdWithIdentifier { + kind: identifier, + id: id.to_string(), + matching_strategy: Some(MatchingStrategy::Legacy), + }), + initial_only: true, + }; + + if !workspace_rules.contains(&workspace_matching_rule) { + workspace_rules.push(workspace_matching_rule); + } + } + } + SocketMessage::WorkspaceRule(identifier, ref id, monitor_idx, workspace_idx) => { + let mut workspace_rules = WORKSPACE_MATCHING_RULES.lock(); + let workspace_matching_rule = WorkspaceMatchingRule { + monitor_index: monitor_idx, + workspace_index: workspace_idx, + matching_rule: MatchingRule::Simple(IdWithIdentifier { + kind: identifier, + id: id.to_string(), + matching_strategy: Some(MatchingStrategy::Legacy), + }), + initial_only: false, + }; + + if !workspace_rules.contains(&workspace_matching_rule) { + workspace_rules.push(workspace_matching_rule); + } + } + SocketMessage::NamedWorkspaceRule(identifier, ref id, ref workspace) => { + if let Some((monitor_idx, workspace_idx)) = + self.monitor_workspace_index_by_name(workspace) + { + let mut workspace_rules = WORKSPACE_MATCHING_RULES.lock(); + let workspace_matching_rule = WorkspaceMatchingRule { + monitor_index: monitor_idx, + workspace_index: workspace_idx, + matching_rule: MatchingRule::Simple(IdWithIdentifier { + kind: identifier, + id: id.to_string(), + matching_strategy: Some(MatchingStrategy::Legacy), + }), + initial_only: false, + }; + + if !workspace_rules.contains(&workspace_matching_rule) { + workspace_rules.push(workspace_matching_rule); + } } } SocketMessage::ClearWorkspaceRules(monitor_idx, workspace_idx) => { - let mut workspace_rules = WORKSPACE_RULES.lock(); - let mut to_remove = vec![]; - for (id, (m_idx, w_idx, _)) in workspace_rules.iter() { - if monitor_idx == *m_idx && workspace_idx == *w_idx { - to_remove.push(id.clone()); - } - } + let mut workspace_rules = WORKSPACE_MATCHING_RULES.lock(); - for rule in to_remove { - workspace_rules.remove(&rule); - } + workspace_rules.retain(|r| { + r.monitor_index != monitor_idx && r.workspace_index != workspace_idx + }); } SocketMessage::ClearNamedWorkspaceRules(ref workspace) => { if let Some((monitor_idx, workspace_idx)) = self.monitor_workspace_index_by_name(workspace) { - let mut workspace_rules = WORKSPACE_RULES.lock(); - let mut to_remove = vec![]; - for (id, (m_idx, w_idx, _)) in workspace_rules.iter() { - if monitor_idx == *m_idx && workspace_idx == *w_idx { - to_remove.push(id.clone()); - } - } - - for rule in to_remove { - workspace_rules.remove(&rule); - } + let mut workspace_rules = WORKSPACE_MATCHING_RULES.lock(); + workspace_rules.retain(|r| { + r.monitor_index != monitor_idx && r.workspace_index != workspace_idx + }); } } SocketMessage::ClearAllWorkspaceRules => { - let mut workspace_rules = WORKSPACE_RULES.lock(); + let mut workspace_rules = WORKSPACE_MATCHING_RULES.lock(); workspace_rules.clear(); } SocketMessage::ManageRule(identifier, ref id) => { @@ -1102,7 +1146,7 @@ impl WindowManager { // Check that this is a valid static config file first if StaticConfig::read(config).is_ok() { // Clear workspace rules; these will need to be replaced - WORKSPACE_RULES.lock().clear(); + WORKSPACE_MATCHING_RULES.lock().clear(); // Pause so that restored windows come to the foreground from all workspaces self.is_paused = true; // Bring all windows to the foreground @@ -1496,51 +1540,6 @@ impl WindowManager { tracing::info!("processed"); Ok(()) } - - #[tracing::instrument(skip(self), level = "debug")] - fn handle_initial_workspace_rules( - &mut self, - id: &String, - monitor_idx: usize, - workspace_idx: usize, - ) -> Result<()> { - self.handle_workspace_rules(id, monitor_idx, workspace_idx, true)?; - - Ok(()) - } - - #[tracing::instrument(skip(self), level = "debug")] - fn handle_definitive_workspace_rules( - &mut self, - id: &String, - monitor_idx: usize, - workspace_idx: usize, - ) -> Result<()> { - self.handle_workspace_rules(id, monitor_idx, workspace_idx, false)?; - - Ok(()) - } - - #[tracing::instrument(skip(self), level = "debug")] - pub fn handle_workspace_rules( - &mut self, - id: &String, - monitor_idx: usize, - workspace_idx: usize, - initial_workspace_rule: bool, - ) -> Result<()> { - { - let mut workspace_rules = WORKSPACE_RULES.lock(); - workspace_rules.insert( - id.to_string(), - (monitor_idx, workspace_idx, initial_workspace_rule), - ); - } - - self.enforce_workspace_rules()?; - - Ok(()) - } } pub fn read_commands_uds(wm: &Arc>, mut stream: UnixStream) -> Result<()> { diff --git a/komorebi/src/static_config.rs b/komorebi/src/static_config.rs index 56397f4f..409025ed 100644 --- a/komorebi/src/static_config.rs +++ b/komorebi/src/static_config.rs @@ -45,17 +45,16 @@ use crate::REGEX_IDENTIFIERS; use crate::TRANSPARENCY_BLACKLIST; use crate::TRAY_AND_MULTI_WINDOW_IDENTIFIERS; use crate::WINDOWS_11; -use crate::WORKSPACE_RULES; +use crate::WORKSPACE_MATCHING_RULES; +use crate::config_generation::WorkspaceMatchingRule; use crate::core::config_generation::ApplicationConfiguration; use crate::core::config_generation::ApplicationConfigurationGenerator; use crate::core::config_generation::ApplicationOptions; -use crate::core::config_generation::IdWithIdentifier; use crate::core::config_generation::MatchingRule; use crate::core::config_generation::MatchingStrategy; use crate::core::resolve_home_path; use crate::core::AnimationStyle; -use crate::core::ApplicationIdentifier; use crate::core::BorderStyle; use crate::core::DefaultLayout; use crate::core::FocusFollowsMouseImplementation; @@ -121,10 +120,10 @@ pub struct WorkspaceConfig { pub workspace_padding: Option, /// Initial workspace application rules #[serde(skip_serializing_if = "Option::is_none")] - pub initial_workspace_rules: Option>, + pub initial_workspace_rules: Option>, /// Permanent workspace application rules #[serde(skip_serializing_if = "Option::is_none")] - pub workspace_rules: Option>, + pub workspace_rules: Option>, /// Apply this monitor's window-based work area offset (default: true) #[serde(skip_serializing_if = "Option::is_none")] pub apply_window_based_work_area_offset: Option, @@ -142,37 +141,6 @@ impl From<&Workspace> for WorkspaceConfig { } } - let workspace_rules = WORKSPACE_RULES.lock(); - let mut initial_ws_rules = vec![]; - let mut ws_rules = vec![]; - - for (identifier, (_, _, is_initial)) in &*workspace_rules { - if identifier.ends_with("exe") { - let rule = IdWithIdentifier { - kind: ApplicationIdentifier::Exe, - id: identifier.clone(), - matching_strategy: None, - }; - - if *is_initial { - initial_ws_rules.push(rule); - } else { - ws_rules.push(rule); - } - } - } - - let initial_ws_rules = if initial_ws_rules.is_empty() { - None - } else { - Option::from(initial_ws_rules) - }; - let ws_rules = if ws_rules.is_empty() { - None - } else { - Option::from(ws_rules) - }; - let default_container_padding = DEFAULT_CONTAINER_PADDING.load(Ordering::SeqCst); let default_workspace_padding = DEFAULT_WORKSPACE_PADDING.load(Ordering::SeqCst); @@ -208,8 +176,8 @@ impl From<&Workspace> for WorkspaceConfig { custom_layout_rules: None, container_padding, workspace_padding, - initial_workspace_rules: initial_ws_rules, - workspace_rules: ws_rules, + initial_workspace_rules: None, + workspace_rules: None, apply_window_based_work_area_offset: Some(value.apply_window_based_work_area_offset()), } } @@ -515,95 +483,6 @@ impl From<&WindowManager> for StaticConfig { monitors.push(MonitorConfig::from(m)); } - let mut to_remove = vec![]; - let mut to_add_initial = vec![]; - let mut to_add_persistent = vec![]; - - let workspace_rules = WORKSPACE_RULES.lock(); - for (m_idx, m) in monitors.iter().enumerate() { - for (w_idx, w) in m.workspaces.iter().enumerate() { - if let Some(rules) = &w.initial_workspace_rules { - for iwsr in rules { - for (identifier, (monitor_idx, workspace_idx, _)) in &*workspace_rules { - if iwsr.id.eq(identifier) - && (*monitor_idx != m_idx || *workspace_idx != w_idx) - { - to_remove.push((m_idx, w_idx, iwsr.id.clone())); - } - } - } - } - - for (identifier, (monitor_idx, workspace_idx, initial)) in &*workspace_rules { - if *initial && (*monitor_idx == m_idx && *workspace_idx == w_idx) { - to_add_initial.push((m_idx, w_idx, identifier.clone())); - } - } - - if let Some(rules) = &w.workspace_rules { - for wsr in rules { - for (identifier, (monitor_idx, workspace_idx, _)) in &*workspace_rules { - if wsr.id.eq(identifier) - && (*monitor_idx != m_idx || *workspace_idx != w_idx) - { - to_remove.push((m_idx, w_idx, wsr.id.clone())); - } - } - } - } - - for (identifier, (monitor_idx, workspace_idx, initial)) in &*workspace_rules { - if !*initial && (*monitor_idx == m_idx && *workspace_idx == w_idx) { - to_add_persistent.push((m_idx, w_idx, identifier.clone())); - } - } - } - } - - for (m_idx, w_idx, id) in to_remove { - if let Some(monitor) = monitors.get_mut(m_idx) { - if let Some(workspace) = monitor.workspaces.get_mut(w_idx) { - if workspace.workspace_rules.is_none() { - workspace.workspace_rules = Some(vec![]); - } - - if let Some(rules) = &mut workspace.workspace_rules { - rules.retain(|r| r.id != id); - for (monitor_idx, workspace_idx, id) in &to_add_persistent { - if m_idx == *monitor_idx && w_idx == *workspace_idx { - rules.push(IdWithIdentifier { - kind: ApplicationIdentifier::Exe, - id: id.clone(), - matching_strategy: None, - }) - } - } - - rules.dedup(); - } - - if workspace.initial_workspace_rules.is_none() { - workspace.workspace_rules = Some(vec![]); - } - - if let Some(rules) = &mut workspace.initial_workspace_rules { - rules.retain(|r| r.id != id); - for (monitor_idx, workspace_idx, id) in &to_add_initial { - if m_idx == *monitor_idx && w_idx == *workspace_idx { - rules.push(IdWithIdentifier { - kind: ApplicationIdentifier::Exe, - id: id.clone(), - matching_strategy: None, - }) - } - } - - rules.dedup(); - } - } - } - } - let border_colours = if border_manager::FOCUSED.load(Ordering::SeqCst) == 0 { None } else { @@ -1150,22 +1029,35 @@ impl StaticConfig { } } + let mut workspace_matching_rules = WORKSPACE_MATCHING_RULES.lock(); for (j, ws) in monitor.workspaces.iter().enumerate() { if let Some(rules) = &ws.workspace_rules { for r in rules { - wm.handle_workspace_rules(&r.id, i, j, false)?; + workspace_matching_rules.push(WorkspaceMatchingRule { + monitor_index: i, + workspace_index: j, + matching_rule: r.clone(), + initial_only: false, + }); } } if let Some(rules) = &ws.initial_workspace_rules { for r in rules { - wm.handle_workspace_rules(&r.id, i, j, true)?; + workspace_matching_rules.push(WorkspaceMatchingRule { + monitor_index: i, + workspace_index: j, + matching_rule: r.clone(), + initial_only: true, + }); } } } } } + wm.enforce_workspace_rules()?; + if value.border == Some(true) { border_manager::BORDER_ENABLED.store(true, Ordering::SeqCst); } @@ -1201,22 +1093,36 @@ impl StaticConfig { } } + let mut workspace_matching_rules = WORKSPACE_MATCHING_RULES.lock(); + workspace_matching_rules.clear(); for (j, ws) in monitor.workspaces.iter().enumerate() { if let Some(rules) = &ws.workspace_rules { for r in rules { - wm.handle_workspace_rules(&r.id, i, j, false)?; + workspace_matching_rules.push(WorkspaceMatchingRule { + monitor_index: i, + workspace_index: j, + matching_rule: r.clone(), + initial_only: false, + }); } } if let Some(rules) = &ws.initial_workspace_rules { for r in rules { - wm.handle_workspace_rules(&r.id, i, j, true)?; + workspace_matching_rules.push(WorkspaceMatchingRule { + monitor_index: i, + workspace_index: j, + matching_rule: r.clone(), + initial_only: true, + }); } } } } } + wm.enforce_workspace_rules()?; + if let Some(enabled) = value.border { border_manager::BORDER_ENABLED.store(enabled, Ordering::SeqCst); } diff --git a/komorebi/src/window_manager.rs b/komorebi/src/window_manager.rs index c9d6b3ba..9634c516 100644 --- a/komorebi/src/window_manager.rs +++ b/komorebi/src/window_manager.rs @@ -16,7 +16,6 @@ use hotwatch::notify::ErrorKind as NotifyErrorKind; use hotwatch::EventKind; use hotwatch::Hotwatch; use parking_lot::Mutex; -use regex::Regex; use schemars::JsonSchema; use serde::Deserialize; use serde::Serialize; @@ -43,12 +42,14 @@ use crate::core::WindowContainerBehaviour; use crate::border_manager; use crate::border_manager::STYLE; +use crate::config_generation::WorkspaceMatchingRule; use crate::container::Container; use crate::core::StackbarMode; use crate::current_virtual_desktop; use crate::load_configuration; use crate::monitor::Monitor; use crate::ring::Ring; +use crate::should_act_individual; use crate::stackbar_manager::STACKBAR_FOCUSED_TEXT_COLOUR; use crate::stackbar_manager::STACKBAR_LABEL; use crate::stackbar_manager::STACKBAR_MODE; @@ -67,7 +68,6 @@ use crate::BorderColours; use crate::Colour; use crate::CrossBoundaryBehaviour; use crate::Rgb; -use crate::WorkspaceRule; use crate::CUSTOM_FFM; use crate::DATA_DIR; use crate::DISPLAY_INDEX_PREFERENCES; @@ -79,9 +79,10 @@ use crate::MANAGE_IDENTIFIERS; use crate::MONITOR_INDEX_PREFERENCES; use crate::NO_TITLEBAR; use crate::OBJECT_NAME_CHANGE_ON_LAUNCH; +use crate::REGEX_IDENTIFIERS; use crate::REMOVE_TITLEBARS; use crate::TRAY_AND_MULTI_WINDOW_IDENTIFIERS; -use crate::WORKSPACE_RULES; +use crate::WORKSPACE_MATCHING_RULES; #[derive(Debug)] pub struct WindowManager { @@ -142,7 +143,7 @@ pub struct GlobalState { pub name_change_on_launch_identifiers: Vec, pub monitor_index_preferences: HashMap, pub display_index_preferences: HashMap, - pub workspace_rules: HashMap, + pub workspace_rules: Vec, pub window_hiding_behaviour: HidingBehaviour, pub configuration_dir: PathBuf, pub data_dir: PathBuf, @@ -191,7 +192,7 @@ impl Default for GlobalState { name_change_on_launch_identifiers: OBJECT_NAME_CHANGE_ON_LAUNCH.lock().clone(), monitor_index_preferences: MONITOR_INDEX_PREFERENCES.lock().clone(), display_index_preferences: DISPLAY_INDEX_PREFERENCES.lock().clone(), - workspace_rules: WORKSPACE_RULES.lock().clone(), + workspace_rules: WORKSPACE_MATCHING_RULES.lock().clone(), window_hiding_behaviour: *HIDING_BEHAVIOUR.lock(), configuration_dir: HOME_DIR.clone(), data_dir: DATA_DIR.clone(), @@ -233,7 +234,6 @@ struct EnforceWorkspaceRuleOp { target_monitor_idx: usize, target_workspace_idx: usize, } - impl EnforceWorkspaceRuleOp { const fn is_origin(&self, monitor_idx: usize, workspace_idx: usize) -> bool { self.origin_monitor_idx == monitor_idx && self.origin_workspace_idx == workspace_idx @@ -450,7 +450,8 @@ impl WindowManager { .ok_or_else(|| anyhow!("there is no monitor with that index"))? .focused_workspace_idx(); - let workspace_rules = WORKSPACE_RULES.lock(); + let workspace_matching_rules = WORKSPACE_MATCHING_RULES.lock(); + let regex_identifiers = REGEX_IDENTIFIERS.lock(); // Go through all the monitors and workspaces for (i, monitor) in self.monitors().iter().enumerate() { for (j, workspace) in monitor.workspaces().iter().enumerate() { @@ -460,63 +461,61 @@ impl WindowManager { let exe_name = window.exe()?; let title = window.title()?; let class = window.class()?; + let path = window.path()?; - let mut found_workspace_rule = workspace_rules.get(&exe_name); - - if found_workspace_rule.is_none() { - found_workspace_rule = workspace_rules.get(&title); - } - - if found_workspace_rule.is_none() { - found_workspace_rule = workspace_rules.get(&class); - } - - if found_workspace_rule.is_none() { - for (k, v) in workspace_rules.iter() { - if let Ok(re) = Regex::new(k) { - if re.is_match(&exe_name) { - found_workspace_rule = Some(v); + for rule in &*workspace_matching_rules { + let matched = match &rule.matching_rule { + MatchingRule::Simple(r) => should_act_individual( + &title, + &exe_name, + &class, + &path, + r, + ®ex_identifiers, + ), + MatchingRule::Composite(r) => { + let mut composite_results = vec![]; + for identifier in r { + composite_results.push(should_act_individual( + &title, + &exe_name, + &class, + &path, + identifier, + ®ex_identifiers, + )); } - if re.is_match(&title) { - found_workspace_rule = Some(v); - } - - if re.is_match(&class) { - found_workspace_rule = Some(v); - } + composite_results.iter().all(|&x| x) } - } - } + }; - // If the executable names or titles of any of those windows are in our rules map - if let Some((monitor_idx, workspace_idx, apply_on_first_show_only)) = - found_workspace_rule - { - if *apply_on_first_show_only { - if !already_moved_window_handles.contains(&window.hwnd) { - already_moved_window_handles.insert(window.hwnd); + if matched { + if rule.initial_only { + if !already_moved_window_handles.contains(&window.hwnd) { + already_moved_window_handles.insert(window.hwnd); + self.add_window_handle_to_move_based_on_workspace_rule( + &window.title()?, + window.hwnd, + i, + j, + rule.monitor_index, + rule.workspace_index, + &mut to_move, + ); + } + } else { self.add_window_handle_to_move_based_on_workspace_rule( &window.title()?, window.hwnd, i, j, - *monitor_idx, - *workspace_idx, + rule.monitor_index, + rule.workspace_index, &mut to_move, ); } - } else { - self.add_window_handle_to_move_based_on_workspace_rule( - &window.title()?, - window.hwnd, - i, - j, - *monitor_idx, - *workspace_idx, - &mut to_move, - ); } } }