diff --git a/komorebi-core/src/config_generation.rs b/komorebi-core/src/config_generation.rs index 2a370a5c8..9566bf51a 100644 --- a/komorebi-core/src/config_generation.rs +++ b/komorebi-core/src/config_generation.rs @@ -50,10 +50,22 @@ impl ApplicationOptions { } } -#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema)] +#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize, JsonSchema)] pub struct IdWithIdentifier { pub kind: ApplicationIdentifier, pub id: String, + #[serde(skip_serializing_if = "Option::is_none")] + pub matching_strategy: Option, +} + +#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize, JsonSchema)] +pub enum MatchingStrategy { + Legacy, + Equals, + StartsWith, + EndsWith, + Contains, + Regex, } #[derive(Clone, Debug, Serialize, Deserialize, JsonSchema)] @@ -62,6 +74,18 @@ pub struct IdWithIdentifierAndComment { pub id: String, #[serde(skip_serializing_if = "Option::is_none")] pub comment: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub matching_strategy: Option, +} + +impl From for IdWithIdentifier { + fn from(value: IdWithIdentifierAndComment) -> Self { + Self { + kind: value.kind, + id: value.id.clone(), + matching_strategy: value.matching_strategy, + } + } } #[derive(Clone, Debug, Serialize, Deserialize, JsonSchema)] diff --git a/komorebi-core/src/lib.rs b/komorebi-core/src/lib.rs index cf686a532..a06a73b3b 100644 --- a/komorebi-core/src/lib.rs +++ b/komorebi-core/src/lib.rs @@ -192,7 +192,17 @@ pub enum StateQuery { } #[derive( - Copy, Clone, Debug, Serialize, Deserialize, Display, EnumString, ValueEnum, JsonSchema, + Copy, + Clone, + Debug, + Eq, + PartialEq, + Serialize, + Deserialize, + Display, + EnumString, + ValueEnum, + JsonSchema, )] #[strum(serialize_all = "snake_case")] pub enum ApplicationIdentifier { diff --git a/komorebi/src/main.rs b/komorebi/src/main.rs index b05b90f89..964645774 100644 --- a/komorebi/src/main.rs +++ b/komorebi/src/main.rs @@ -45,6 +45,9 @@ use winreg::enums::HKEY_CURRENT_USER; use winreg::RegKey; use crate::hidden::Hidden; +use komorebi_core::config_generation::IdWithIdentifier; +use komorebi_core::config_generation::MatchingStrategy; +use komorebi_core::ApplicationIdentifier; use komorebi_core::HidingBehaviour; use komorebi_core::Rect; use komorebi_core::SocketMessage; @@ -106,11 +109,19 @@ lazy_static! { static ref WORKSPACE_RULES: Arc>> = Arc::new(Mutex::new(HashMap::new())); static ref MANAGE_IDENTIFIERS: Arc>> = Arc::new(Mutex::new(vec![])); - static ref FLOAT_IDENTIFIERS: Arc>> = Arc::new(Mutex::new(vec![ + static ref FLOAT_IDENTIFIERS: Arc>> = Arc::new(Mutex::new(vec![ // mstsc.exe creates these on Windows 11 when a WSL process is launched // https://github.com/LGUG2Z/komorebi/issues/74 - "OPContainerClass".to_string(), - "IHWindowClass".to_string() + IdWithIdentifier { + kind: ApplicationIdentifier::Class, + id: String::from("OPContainerClass"), + matching_strategy: Option::from(MatchingStrategy::Equals), + }, + IdWithIdentifier { + kind: ApplicationIdentifier::Class, + id: String::from("IHWindowClass"), + matching_strategy: Option::from(MatchingStrategy::Equals), + } ])); static ref PERMAIGNORE_CLASSES: Arc>> = Arc::new(Mutex::new(vec![ "Chrome_RenderWidgetHostHWND".to_string(), diff --git a/komorebi/src/process_command.rs b/komorebi/src/process_command.rs index f4944e22e..6738c46e1 100644 --- a/komorebi/src/process_command.rs +++ b/komorebi/src/process_command.rs @@ -20,6 +20,8 @@ use parking_lot::Mutex; use schemars::schema_for; use uds_windows::UnixStream; +use komorebi_core::config_generation::IdWithIdentifier; +use komorebi_core::config_generation::MatchingStrategy; use komorebi_core::ApplicationIdentifier; use komorebi_core::Axis; use komorebi_core::FocusFollowsMouseImplementation; @@ -246,8 +248,20 @@ impl WindowManager { } SocketMessage::FloatRule(identifier, ref id) => { let mut float_identifiers = FLOAT_IDENTIFIERS.lock(); - if !float_identifiers.contains(id) { - float_identifiers.push(id.to_string()); + + let mut should_push = true; + for f in &*float_identifiers { + if f.id.eq(id) { + should_push = false; + } + } + + if should_push { + float_identifiers.push(IdWithIdentifier { + kind: identifier, + id: id.clone(), + matching_strategy: Option::from(MatchingStrategy::Legacy), + }); } let invisible_borders = self.invisible_borders; diff --git a/komorebi/src/static_config.rs b/komorebi/src/static_config.rs index 34fb5d690..13bc869cf 100644 --- a/komorebi/src/static_config.rs +++ b/komorebi/src/static_config.rs @@ -34,6 +34,7 @@ use hotwatch::Hotwatch; use komorebi_core::config_generation::ApplicationConfigurationGenerator; use komorebi_core::config_generation::ApplicationOptions; use komorebi_core::config_generation::IdWithIdentifier; +use komorebi_core::config_generation::MatchingStrategy; use komorebi_core::ApplicationIdentifier; use komorebi_core::DefaultLayout; use komorebi_core::FocusFollowsMouseImplementation; @@ -139,6 +140,7 @@ impl From<&Workspace> for WorkspaceConfig { let rule = IdWithIdentifier { kind: ApplicationIdentifier::Exe, id: identifier.clone(), + matching_strategy: None, }; if *is_initial { @@ -433,7 +435,7 @@ impl From<&WindowManager> for StaticConfig { impl StaticConfig { #[allow(clippy::cognitive_complexity, clippy::too_many_lines)] - fn apply_globals(&self) -> Result<()> { + fn apply_globals(&mut self) -> Result<()> { if let Some(monitor_index_preferences) = &self.monitor_index_preferences { let mut preferences = MONITOR_INDEX_PREFERENCES.lock(); *preferences = monitor_index_preferences.clone(); @@ -508,10 +510,14 @@ impl StaticConfig { let mut object_name_change_identifiers = OBJECT_NAME_CHANGE_ON_LAUNCH.lock(); let mut layered_identifiers = LAYERED_WHITELIST.lock(); - if let Some(float) = &self.float_rules { + if let Some(float) = &mut self.float_rules { for identifier in float { - if !float_identifiers.contains(&identifier.id) { - float_identifiers.push(identifier.id.clone()); + if identifier.matching_strategy.is_none() { + identifier.matching_strategy = Option::from(MatchingStrategy::Legacy); + } + + if !float_identifiers.contains(identifier) { + float_identifiers.push(identifier.clone()); } } } @@ -569,8 +575,14 @@ impl StaticConfig { for entry in asc { if let Some(float) = entry.float_identifiers { for f in float { - if !float_identifiers.contains(&f.id) { - float_identifiers.push(f.id.clone()); + let mut without_comment: IdWithIdentifier = f.into(); + if without_comment.matching_strategy.is_none() { + without_comment.matching_strategy = + Option::from(MatchingStrategy::Legacy); + } + + if !float_identifiers.contains(&without_comment) { + float_identifiers.push(without_comment.clone()); } } } @@ -620,7 +632,7 @@ impl StaticConfig { incoming: Arc>>, ) -> Result { let content = std::fs::read_to_string(path)?; - let value: Self = serde_json::from_str(&content)?; + let mut value: Self = serde_json::from_str(&content)?; value.apply_globals()?; let socket = DATA_DIR.join("komorebi.sock"); @@ -740,7 +752,7 @@ impl StaticConfig { pub fn reload(path: &PathBuf, wm: &mut WindowManager) -> Result<()> { let content = std::fs::read_to_string(path)?; - let value: Self = serde_json::from_str(&content)?; + let mut value: Self = serde_json::from_str(&content)?; value.apply_globals()?; diff --git a/komorebi/src/window.rs b/komorebi/src/window.rs index 9539599a6..26918321e 100644 --- a/komorebi/src/window.rs +++ b/komorebi/src/window.rs @@ -7,6 +7,7 @@ use std::sync::atomic::Ordering; use color_eyre::eyre::anyhow; use color_eyre::Result; +use komorebi_core::config_generation::MatchingStrategy; use schemars::JsonSchema; use serde::ser::Error; use serde::ser::SerializeStruct; @@ -17,6 +18,7 @@ use winput::press; use winput::release; use winput::Vk; +use komorebi_core::ApplicationIdentifier; use komorebi_core::HidingBehaviour; use komorebi_core::Rect; @@ -474,16 +476,45 @@ fn window_is_eligible( { let float_identifiers = FLOAT_IDENTIFIERS.lock(); for identifier in float_identifiers.iter() { - if title.starts_with(identifier) || title.ends_with(identifier) { - should_float = true; - } - - if class.starts_with(identifier) || class.ends_with(identifier) { - should_float = true; - } - - if identifier == exe_name { - should_float = true; + match identifier.matching_strategy { + None => { + panic!("there is no matching strategy identified for this rule"); + } + Some(MatchingStrategy::Equals) => match identifier.kind { + ApplicationIdentifier::Title => { + if title.eq(&identifier.id) { + should_float = true; + } + } + ApplicationIdentifier::Class => { + if class.eq(&identifier.id) { + should_float = true; + } + } + ApplicationIdentifier::Exe => { + if exe_name.eq(&identifier.id) { + should_float = true; + } + } + }, + Some(MatchingStrategy::Legacy) => match identifier.kind { + ApplicationIdentifier::Title => { + if title.starts_with(&identifier.id) || title.ends_with(&identifier.id) { + should_float = true; + } + } + ApplicationIdentifier::Class => { + if class.starts_with(&identifier.id) || class.ends_with(&identifier.id) { + should_float = true; + } + } + ApplicationIdentifier::Exe => { + if exe_name.eq(&identifier.id) { + should_float = true; + } + } + }, + _ => unimplemented!(), } } }; diff --git a/komorebi/src/window_manager.rs b/komorebi/src/window_manager.rs index 3ac0eb1bc..3ca48817f 100644 --- a/komorebi/src/window_manager.rs +++ b/komorebi/src/window_manager.rs @@ -17,6 +17,7 @@ use schemars::JsonSchema; use serde::Serialize; use uds_windows::UnixListener; +use komorebi_core::config_generation::IdWithIdentifier; use komorebi_core::custom_layout::CustomLayout; use komorebi_core::Arrangement; use komorebi_core::Axis; @@ -93,7 +94,7 @@ pub struct State { pub mouse_follows_focus: bool, pub has_pending_raise_op: bool, pub remove_titlebars: bool, - pub float_identifiers: Vec, + pub float_identifiers: Vec, pub manage_identifiers: Vec, pub layered_whitelist: Vec, pub tray_and_multi_window_identifiers: Vec,