feat(config): add support for composite rules

This commit adds support for composite rules in the static configuration
file, and for float_identifiers in the applications.yaml file.

A new enum, MatchingRule provides two variants, Simple and Composite,
and is now used in place of IdWithIdentifier throughout komorebi.

In order to keep this new enum lean, a variant for
IdWithIdentifierAndComment has not been added, and references to it in
the old config generation code have been replaced with MatchingRule.

resolve #462, resolve #715, resolve #237
This commit is contained in:
LGUG2Z
2024-03-20 12:24:12 -07:00
parent ca22cdb07f
commit d6e0ecc507
6 changed files with 426 additions and 423 deletions
+37 -24
View File
@@ -8,7 +8,9 @@ use strum::EnumString;
use crate::ApplicationIdentifier; use crate::ApplicationIdentifier;
#[derive(Clone, Debug, Serialize, Deserialize, Display, EnumString, ValueEnum, JsonSchema)] #[derive(
Clone, Copy, Debug, Serialize, Deserialize, Display, EnumString, ValueEnum, JsonSchema,
)]
#[strum(serialize_all = "snake_case")] #[strum(serialize_all = "snake_case")]
#[serde(rename_all = "snake_case")] #[serde(rename_all = "snake_case")]
pub enum ApplicationOptions { pub enum ApplicationOptions {
@@ -50,6 +52,13 @@ impl ApplicationOptions {
} }
} }
#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize, JsonSchema)]
#[serde(untagged)]
pub enum MatchingRule {
Simple(IdWithIdentifier),
Composite(Vec<IdWithIdentifier>),
}
#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize, JsonSchema)] #[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize, JsonSchema)]
pub struct IdWithIdentifier { pub struct IdWithIdentifier {
pub kind: ApplicationIdentifier, pub kind: ApplicationIdentifier,
@@ -95,7 +104,7 @@ pub struct ApplicationConfiguration {
#[serde(skip_serializing_if = "Option::is_none")] #[serde(skip_serializing_if = "Option::is_none")]
pub options: Option<Vec<ApplicationOptions>>, pub options: Option<Vec<ApplicationOptions>>,
#[serde(skip_serializing_if = "Option::is_none")] #[serde(skip_serializing_if = "Option::is_none")]
pub float_identifiers: Option<Vec<IdWithIdentifierAndComment>>, pub float_identifiers: Option<Vec<MatchingRule>>,
} }
impl ApplicationConfiguration { impl ApplicationConfiguration {
@@ -181,19 +190,21 @@ impl ApplicationConfigurationGenerator {
} }
if let Some(float_identifiers) = app.float_identifiers { if let Some(float_identifiers) = app.float_identifiers {
for float in float_identifiers { for matching_rule in float_identifiers {
let float_rule = if let MatchingRule::Simple(float) = matching_rule {
format!("komorebic.exe float-rule {} \"{}\"", float.kind, float.id); let float_rule =
format!("komorebic.exe float-rule {} \"{}\"", float.kind, float.id);
// Don't want to send duped signals especially as configs get larger // Don't want to send duped signals especially as configs get larger
if !float_rules.contains(&float_rule) { if !float_rules.contains(&float_rule) {
float_rules.push(float_rule.clone()); float_rules.push(float_rule.clone());
if let Some(comment) = float.comment { // if let Some(comment) = float.comment {
lines.push(format!("# {comment}")); // lines.push(format!("# {comment}"));
}; // };
lines.push(float_rule); lines.push(float_rule);
}
} }
} }
} }
@@ -230,21 +241,23 @@ impl ApplicationConfigurationGenerator {
} }
if let Some(float_identifiers) = app.float_identifiers { if let Some(float_identifiers) = app.float_identifiers {
for float in float_identifiers { for matching_rule in float_identifiers {
let float_rule = format!( if let MatchingRule::Simple(float) = matching_rule {
"RunWait('komorebic.exe float-rule {} \"{}\"', , \"Hide\")", let float_rule = format!(
float.kind, float.id "RunWait('komorebic.exe float-rule {} \"{}\"', , \"Hide\")",
); float.kind, float.id
);
// Don't want to send duped signals especially as configs get larger // Don't want to send duped signals especially as configs get larger
if !float_rules.contains(&float_rule) { if !float_rules.contains(&float_rule) {
float_rules.push(float_rule.clone()); float_rules.push(float_rule.clone());
if let Some(comment) = float.comment { // if let Some(comment) = float.comment {
lines.push(format!("; {comment}")); // lines.push(format!("; {comment}"));
}; // };
lines.push(float_rule); lines.push(float_rule);
}
} }
} }
} }
+30 -28
View File
@@ -46,6 +46,7 @@ pub use windows_api::*;
use color_eyre::Result; use color_eyre::Result;
use komorebi_core::config_generation::IdWithIdentifier; use komorebi_core::config_generation::IdWithIdentifier;
use komorebi_core::config_generation::MatchingRule;
use komorebi_core::config_generation::MatchingStrategy; use komorebi_core::config_generation::MatchingStrategy;
use komorebi_core::ApplicationIdentifier; use komorebi_core::ApplicationIdentifier;
use komorebi_core::HidingBehaviour; use komorebi_core::HidingBehaviour;
@@ -66,57 +67,57 @@ type WorkspaceRule = (usize, usize, bool);
lazy_static! { lazy_static! {
static ref HIDDEN_HWNDS: Arc<Mutex<Vec<isize>>> = Arc::new(Mutex::new(vec![])); static ref HIDDEN_HWNDS: Arc<Mutex<Vec<isize>>> = Arc::new(Mutex::new(vec![]));
static ref LAYERED_WHITELIST: Arc<Mutex<Vec<IdWithIdentifier>>> = Arc::new(Mutex::new(vec![ static ref LAYERED_WHITELIST: Arc<Mutex<Vec<MatchingRule>>> = Arc::new(Mutex::new(vec![
IdWithIdentifier { MatchingRule::Simple(IdWithIdentifier {
kind: ApplicationIdentifier::Exe, kind: ApplicationIdentifier::Exe,
id: String::from("steam.exe"), id: String::from("steam.exe"),
matching_strategy: Option::from(MatchingStrategy::Equals), matching_strategy: Option::from(MatchingStrategy::Equals),
}, }),
])); ]));
static ref TRAY_AND_MULTI_WINDOW_IDENTIFIERS: Arc<Mutex<Vec<IdWithIdentifier>>> = static ref TRAY_AND_MULTI_WINDOW_IDENTIFIERS: Arc<Mutex<Vec<MatchingRule>>> =
Arc::new(Mutex::new(vec![ Arc::new(Mutex::new(vec![
IdWithIdentifier { MatchingRule::Simple(IdWithIdentifier {
kind: ApplicationIdentifier::Exe, kind: ApplicationIdentifier::Exe,
id: String::from("explorer.exe"), id: String::from("explorer.exe"),
matching_strategy: Option::from(MatchingStrategy::Equals), matching_strategy: Option::from(MatchingStrategy::Equals),
}, }),
IdWithIdentifier { MatchingRule::Simple(IdWithIdentifier {
kind: ApplicationIdentifier::Exe, kind: ApplicationIdentifier::Exe,
id: String::from("firefox.exe"), id: String::from("firefox.exe"),
matching_strategy: Option::from(MatchingStrategy::Equals), matching_strategy: Option::from(MatchingStrategy::Equals),
}, }),
IdWithIdentifier { MatchingRule::Simple(IdWithIdentifier {
kind: ApplicationIdentifier::Exe, kind: ApplicationIdentifier::Exe,
id: String::from("chrome.exe"), id: String::from("chrome.exe"),
matching_strategy: Option::from(MatchingStrategy::Equals), matching_strategy: Option::from(MatchingStrategy::Equals),
}, }),
IdWithIdentifier { MatchingRule::Simple(IdWithIdentifier {
kind: ApplicationIdentifier::Exe, kind: ApplicationIdentifier::Exe,
id: String::from("idea64.exe"), id: String::from("idea64.exe"),
matching_strategy: Option::from(MatchingStrategy::Equals), matching_strategy: Option::from(MatchingStrategy::Equals),
}, }),
IdWithIdentifier { MatchingRule::Simple(IdWithIdentifier {
kind: ApplicationIdentifier::Exe, kind: ApplicationIdentifier::Exe,
id: String::from("ApplicationFrameHost.exe"), id: String::from("ApplicationFrameHost.exe"),
matching_strategy: Option::from(MatchingStrategy::Equals), matching_strategy: Option::from(MatchingStrategy::Equals),
}, }),
IdWithIdentifier { MatchingRule::Simple(IdWithIdentifier {
kind: ApplicationIdentifier::Exe, kind: ApplicationIdentifier::Exe,
id: String::from("steam.exe"), id: String::from("steam.exe"),
matching_strategy: Option::from(MatchingStrategy::Equals), matching_strategy: Option::from(MatchingStrategy::Equals),
} })
])); ]));
static ref OBJECT_NAME_CHANGE_ON_LAUNCH: Arc<Mutex<Vec<IdWithIdentifier>>> = Arc::new(Mutex::new(vec![ static ref OBJECT_NAME_CHANGE_ON_LAUNCH: Arc<Mutex<Vec<MatchingRule>>> = Arc::new(Mutex::new(vec![
IdWithIdentifier { MatchingRule::Simple(IdWithIdentifier {
kind: ApplicationIdentifier::Exe, kind: ApplicationIdentifier::Exe,
id: String::from("firefox.exe"), id: String::from("firefox.exe"),
matching_strategy: Option::from(MatchingStrategy::Equals), matching_strategy: Option::from(MatchingStrategy::Equals),
}, }),
IdWithIdentifier { MatchingRule::Simple(IdWithIdentifier {
kind: ApplicationIdentifier::Exe, kind: ApplicationIdentifier::Exe,
id: String::from("idea64.exe"), id: String::from("idea64.exe"),
matching_strategy: Option::from(MatchingStrategy::Equals), matching_strategy: Option::from(MatchingStrategy::Equals),
}, }),
])); ]));
static ref MONITOR_INDEX_PREFERENCES: Arc<Mutex<HashMap<usize, Rect>>> = static ref MONITOR_INDEX_PREFERENCES: Arc<Mutex<HashMap<usize, Rect>>> =
Arc::new(Mutex::new(HashMap::new())); Arc::new(Mutex::new(HashMap::new()));
@@ -126,25 +127,26 @@ lazy_static! {
Arc::new(Mutex::new(HashMap::new())); Arc::new(Mutex::new(HashMap::new()));
static ref REGEX_IDENTIFIERS: Arc<Mutex<HashMap<String, Regex>>> = static ref REGEX_IDENTIFIERS: Arc<Mutex<HashMap<String, Regex>>> =
Arc::new(Mutex::new(HashMap::new())); Arc::new(Mutex::new(HashMap::new()));
static ref MANAGE_IDENTIFIERS: Arc<Mutex<Vec<IdWithIdentifier>>> = Arc::new(Mutex::new(vec![])); static ref MANAGE_IDENTIFIERS: Arc<Mutex<Vec<MatchingRule>>> = Arc::new(Mutex::new(vec![]));
static ref FLOAT_IDENTIFIERS: Arc<Mutex<Vec<IdWithIdentifier>>> = Arc::new(Mutex::new(vec![ static ref FLOAT_IDENTIFIERS: Arc<Mutex<Vec<MatchingRule>>> = Arc::new(Mutex::new(vec![
// mstsc.exe creates these on Windows 11 when a WSL process is launched // mstsc.exe creates these on Windows 11 when a WSL process is launched
// https://github.com/LGUG2Z/komorebi/issues/74 // https://github.com/LGUG2Z/komorebi/issues/74
IdWithIdentifier { MatchingRule::Simple(IdWithIdentifier {
kind: ApplicationIdentifier::Class, kind: ApplicationIdentifier::Class,
id: String::from("OPContainerClass"), id: String::from("OPContainerClass"),
matching_strategy: Option::from(MatchingStrategy::Equals), matching_strategy: Option::from(MatchingStrategy::Equals),
}, }),
IdWithIdentifier { MatchingRule::Simple(IdWithIdentifier {
kind: ApplicationIdentifier::Class, kind: ApplicationIdentifier::Class,
id: String::from("IHWindowClass"), id: String::from("IHWindowClass"),
matching_strategy: Option::from(MatchingStrategy::Equals), matching_strategy: Option::from(MatchingStrategy::Equals),
} })
])); ]));
static ref PERMAIGNORE_CLASSES: Arc<Mutex<Vec<String>>> = Arc::new(Mutex::new(vec![ static ref PERMAIGNORE_CLASSES: Arc<Mutex<Vec<String>>> = Arc::new(Mutex::new(vec![
"Chrome_RenderWidgetHostHWND".to_string(), "Chrome_RenderWidgetHostHWND".to_string(),
])); ]));
static ref BORDER_OVERFLOW_IDENTIFIERS: Arc<Mutex<Vec<IdWithIdentifier>>> = Arc::new(Mutex::new(vec![])); static ref BORDER_OVERFLOW_IDENTIFIERS: Arc<Mutex<Vec<MatchingRule>>> = Arc::new(Mutex::new(vec![]));
static ref WSL2_UI_PROCESSES: Arc<Mutex<Vec<String>>> = Arc::new(Mutex::new(vec![ static ref WSL2_UI_PROCESSES: Arc<Mutex<Vec<String>>> = Arc::new(Mutex::new(vec![
"X410.exe".to_string(), "X410.exe".to_string(),
"vcxsrv.exe".to_string(), "vcxsrv.exe".to_string(),
+37 -24
View File
@@ -23,6 +23,7 @@ use uds_windows::UnixStream;
use komorebi_core::config_generation::ApplicationConfiguration; use komorebi_core::config_generation::ApplicationConfiguration;
use komorebi_core::config_generation::IdWithIdentifier; use komorebi_core::config_generation::IdWithIdentifier;
use komorebi_core::config_generation::MatchingRule;
use komorebi_core::config_generation::MatchingStrategy; use komorebi_core::config_generation::MatchingStrategy;
use komorebi_core::ApplicationIdentifier; use komorebi_core::ApplicationIdentifier;
use komorebi_core::Axis; use komorebi_core::Axis;
@@ -273,17 +274,19 @@ impl WindowManager {
let mut should_push = true; let mut should_push = true;
for m in &*manage_identifiers { for m in &*manage_identifiers {
if m.id.eq(id) { if let MatchingRule::Simple(m) = m {
should_push = false; if m.id.eq(id) {
should_push = false;
}
} }
} }
if should_push { if should_push {
manage_identifiers.push(IdWithIdentifier { manage_identifiers.push(MatchingRule::Simple(IdWithIdentifier {
kind: identifier, kind: identifier,
id: id.clone(), id: id.clone(),
matching_strategy: Option::from(MatchingStrategy::Legacy), matching_strategy: Option::from(MatchingStrategy::Legacy),
}); }));
} }
} }
SocketMessage::FloatRule(identifier, ref id) => { SocketMessage::FloatRule(identifier, ref id) => {
@@ -291,17 +294,19 @@ impl WindowManager {
let mut should_push = true; let mut should_push = true;
for f in &*float_identifiers { for f in &*float_identifiers {
if f.id.eq(id) { if let MatchingRule::Simple(f) = f {
should_push = false; if f.id.eq(id) {
should_push = false;
}
} }
} }
if should_push { if should_push {
float_identifiers.push(IdWithIdentifier { float_identifiers.push(MatchingRule::Simple(IdWithIdentifier {
kind: identifier, kind: identifier,
id: id.clone(), id: id.clone(),
matching_strategy: Option::from(MatchingStrategy::Legacy), matching_strategy: Option::from(MatchingStrategy::Legacy),
}); }));
} }
let offset = self.work_area_offset; let offset = self.work_area_offset;
@@ -1025,17 +1030,19 @@ impl WindowManager {
let mut should_push = true; let mut should_push = true;
for i in &*identifiers { for i in &*identifiers {
if i.id.eq(id) { if let MatchingRule::Simple(i) = i {
should_push = false; if i.id.eq(id) {
should_push = false;
}
} }
} }
if should_push { if should_push {
identifiers.push(IdWithIdentifier { identifiers.push(MatchingRule::Simple(IdWithIdentifier {
kind: identifier, kind: identifier,
id: id.clone(), id: id.clone(),
matching_strategy: Option::from(MatchingStrategy::Legacy), matching_strategy: Option::from(MatchingStrategy::Legacy),
}); }));
} }
} }
SocketMessage::IdentifyObjectNameChangeApplication(identifier, ref id) => { SocketMessage::IdentifyObjectNameChangeApplication(identifier, ref id) => {
@@ -1043,34 +1050,38 @@ impl WindowManager {
let mut should_push = true; let mut should_push = true;
for i in &*identifiers { for i in &*identifiers {
if i.id.eq(id) { if let MatchingRule::Simple(i) = i {
should_push = false; if i.id.eq(id) {
should_push = false;
}
} }
} }
if should_push { if should_push {
identifiers.push(IdWithIdentifier { identifiers.push(MatchingRule::Simple(IdWithIdentifier {
kind: identifier, kind: identifier,
id: id.clone(), id: id.clone(),
matching_strategy: Option::from(MatchingStrategy::Legacy), matching_strategy: Option::from(MatchingStrategy::Legacy),
}); }));
} }
} }
SocketMessage::IdentifyTrayApplication(identifier, ref id) => { SocketMessage::IdentifyTrayApplication(identifier, ref id) => {
let mut identifiers = TRAY_AND_MULTI_WINDOW_IDENTIFIERS.lock(); let mut identifiers = TRAY_AND_MULTI_WINDOW_IDENTIFIERS.lock();
let mut should_push = true; let mut should_push = true;
for i in &*identifiers { for i in &*identifiers {
if i.id.eq(id) { if let MatchingRule::Simple(i) = i {
should_push = false; if i.id.eq(id) {
should_push = false;
}
} }
} }
if should_push { if should_push {
identifiers.push(IdWithIdentifier { identifiers.push(MatchingRule::Simple(IdWithIdentifier {
kind: identifier, kind: identifier,
id: id.clone(), id: id.clone(),
matching_strategy: Option::from(MatchingStrategy::Legacy), matching_strategy: Option::from(MatchingStrategy::Legacy),
}); }));
} }
} }
SocketMessage::IdentifyLayeredApplication(identifier, ref id) => { SocketMessage::IdentifyLayeredApplication(identifier, ref id) => {
@@ -1078,17 +1089,19 @@ impl WindowManager {
let mut should_push = true; let mut should_push = true;
for i in &*identifiers { for i in &*identifiers {
if i.id.eq(id) { if let MatchingRule::Simple(i) = i {
should_push = false; if i.id.eq(id) {
should_push = false;
}
} }
} }
if should_push { if should_push {
identifiers.push(IdWithIdentifier { identifiers.push(MatchingRule::Simple(IdWithIdentifier {
kind: identifier, kind: identifier,
id: id.clone(), id: id.clone(),
matching_strategy: Option::from(MatchingStrategy::Legacy), matching_strategy: Option::from(MatchingStrategy::Legacy),
}); }));
} }
} }
SocketMessage::ManageFocusedWindow => { SocketMessage::ManageFocusedWindow => {
+126 -198
View File
@@ -34,9 +34,11 @@ use color_eyre::Result;
use crossbeam_channel::Receiver; use crossbeam_channel::Receiver;
use hotwatch::EventKind; use hotwatch::EventKind;
use hotwatch::Hotwatch; use hotwatch::Hotwatch;
use komorebi_core::config_generation::ApplicationConfiguration;
use komorebi_core::config_generation::ApplicationConfigurationGenerator; use komorebi_core::config_generation::ApplicationConfigurationGenerator;
use komorebi_core::config_generation::ApplicationOptions; use komorebi_core::config_generation::ApplicationOptions;
use komorebi_core::config_generation::IdWithIdentifier; use komorebi_core::config_generation::IdWithIdentifier;
use komorebi_core::config_generation::MatchingRule;
use komorebi_core::config_generation::MatchingStrategy; use komorebi_core::config_generation::MatchingStrategy;
use komorebi_core::resolve_home_path; use komorebi_core::resolve_home_path;
use komorebi_core::ApplicationIdentifier; use komorebi_core::ApplicationIdentifier;
@@ -269,22 +271,22 @@ pub struct StaticConfig {
pub global_work_area_offset: Option<Rect>, pub global_work_area_offset: Option<Rect>,
/// Individual window floating rules /// Individual window floating rules
#[serde(skip_serializing_if = "Option::is_none")] #[serde(skip_serializing_if = "Option::is_none")]
pub float_rules: Option<Vec<IdWithIdentifier>>, pub float_rules: Option<Vec<MatchingRule>>,
/// Individual window force-manage rules /// Individual window force-manage rules
#[serde(skip_serializing_if = "Option::is_none")] #[serde(skip_serializing_if = "Option::is_none")]
pub manage_rules: Option<Vec<IdWithIdentifier>>, pub manage_rules: Option<Vec<MatchingRule>>,
/// Identify border overflow applications /// Identify border overflow applications
#[serde(skip_serializing_if = "Option::is_none")] #[serde(skip_serializing_if = "Option::is_none")]
pub border_overflow_applications: Option<Vec<IdWithIdentifier>>, pub border_overflow_applications: Option<Vec<MatchingRule>>,
/// Identify tray and multi-window applications /// Identify tray and multi-window applications
#[serde(skip_serializing_if = "Option::is_none")] #[serde(skip_serializing_if = "Option::is_none")]
pub tray_and_multi_window_applications: Option<Vec<IdWithIdentifier>>, pub tray_and_multi_window_applications: Option<Vec<MatchingRule>>,
/// Identify applications that have the WS_EX_LAYERED extended window style /// Identify applications that have the WS_EX_LAYERED extended window style
#[serde(skip_serializing_if = "Option::is_none")] #[serde(skip_serializing_if = "Option::is_none")]
pub layered_applications: Option<Vec<IdWithIdentifier>>, pub layered_applications: Option<Vec<MatchingRule>>,
/// Identify applications that send EVENT_OBJECT_NAMECHANGE on launch (very rare) /// Identify applications that send EVENT_OBJECT_NAMECHANGE on launch (very rare)
#[serde(skip_serializing_if = "Option::is_none")] #[serde(skip_serializing_if = "Option::is_none")]
pub object_name_change_applications: Option<Vec<IdWithIdentifier>>, pub object_name_change_applications: Option<Vec<MatchingRule>>,
/// Set monitor index preferences /// Set monitor index preferences
#[serde(skip_serializing_if = "Option::is_none")] #[serde(skip_serializing_if = "Option::is_none")]
pub monitor_index_preferences: Option<HashMap<usize, Rect>>, pub monitor_index_preferences: Option<HashMap<usize, Rect>>,
@@ -452,106 +454,40 @@ impl StaticConfig {
let mut object_name_change_identifiers = OBJECT_NAME_CHANGE_ON_LAUNCH.lock(); let mut object_name_change_identifiers = OBJECT_NAME_CHANGE_ON_LAUNCH.lock();
let mut layered_identifiers = LAYERED_WHITELIST.lock(); let mut layered_identifiers = LAYERED_WHITELIST.lock();
if let Some(float) = &mut self.float_rules { if let Some(rules) = &mut self.float_rules {
for identifier in float { populate_rules(rules, &mut float_identifiers, &mut regex_identifiers)?;
if identifier.matching_strategy.is_none() {
identifier.matching_strategy = Option::from(MatchingStrategy::Legacy);
}
if !float_identifiers.contains(identifier) {
float_identifiers.push(identifier.clone());
if matches!(identifier.matching_strategy, Some(MatchingStrategy::Regex)) {
let re = Regex::new(&identifier.id)?;
regex_identifiers.insert(identifier.id.clone(), re);
}
}
}
} }
if let Some(manage) = &mut self.manage_rules { if let Some(rules) = &mut self.manage_rules {
for identifier in manage { populate_rules(rules, &mut manage_identifiers, &mut regex_identifiers)?;
if identifier.matching_strategy.is_none() {
identifier.matching_strategy = Option::from(MatchingStrategy::Legacy);
}
if !manage_identifiers.contains(identifier) {
manage_identifiers.push(identifier.clone());
if matches!(identifier.matching_strategy, Some(MatchingStrategy::Regex)) {
let re = Regex::new(&identifier.id)?;
regex_identifiers.insert(identifier.id.clone(), re);
}
}
}
} }
if let Some(identifiers) = &mut self.object_name_change_applications { if let Some(rules) = &mut self.object_name_change_applications {
for identifier in identifiers { populate_rules(
if identifier.matching_strategy.is_none() { rules,
identifier.matching_strategy = Option::from(MatchingStrategy::Legacy); &mut object_name_change_identifiers,
} &mut regex_identifiers,
)?;
if !object_name_change_identifiers.contains(identifier) {
object_name_change_identifiers.push(identifier.clone());
if matches!(identifier.matching_strategy, Some(MatchingStrategy::Regex)) {
let re = Regex::new(&identifier.id)?;
regex_identifiers.insert(identifier.id.clone(), re);
}
}
}
} }
if let Some(identifiers) = &mut self.layered_applications { if let Some(rules) = &mut self.layered_applications {
for identifier in identifiers { populate_rules(rules, &mut layered_identifiers, &mut regex_identifiers)?;
if identifier.matching_strategy.is_none() {
identifier.matching_strategy = Option::from(MatchingStrategy::Legacy);
}
if !layered_identifiers.contains(identifier) {
layered_identifiers.push(identifier.clone());
if matches!(identifier.matching_strategy, Some(MatchingStrategy::Regex)) {
let re = Regex::new(&identifier.id)?;
regex_identifiers.insert(identifier.id.clone(), re);
}
}
}
} }
if let Some(identifiers) = &mut self.border_overflow_applications { if let Some(rules) = &mut self.border_overflow_applications {
for identifier in identifiers { populate_rules(
if identifier.matching_strategy.is_none() { rules,
identifier.matching_strategy = Option::from(MatchingStrategy::Legacy); &mut border_overflow_identifiers,
} &mut regex_identifiers,
)?;
if !border_overflow_identifiers.contains(identifier) {
border_overflow_identifiers.push(identifier.clone());
if matches!(identifier.matching_strategy, Some(MatchingStrategy::Regex)) {
let re = Regex::new(&identifier.id)?;
regex_identifiers.insert(identifier.id.clone(), re);
}
}
}
} }
if let Some(identifiers) = &mut self.tray_and_multi_window_applications { if let Some(rules) = &mut self.tray_and_multi_window_applications {
for identifier in identifiers { populate_rules(
if identifier.matching_strategy.is_none() { rules,
identifier.matching_strategy = Option::from(MatchingStrategy::Legacy); &mut tray_and_multi_window_identifiers,
} &mut regex_identifiers,
)?;
if !tray_and_multi_window_identifiers.contains(identifier) {
tray_and_multi_window_identifiers.push(identifier.clone());
if matches!(identifier.matching_strategy, Some(MatchingStrategy::Regex)) {
let re = Regex::new(&identifier.id)?;
regex_identifiers.insert(identifier.id.clone(), re);
}
}
}
} }
if let Some(path) = &self.app_specific_configuration_path { if let Some(path) = &self.app_specific_configuration_path {
@@ -560,120 +496,48 @@ impl StaticConfig {
let asc = ApplicationConfigurationGenerator::load(&content)?; let asc = ApplicationConfigurationGenerator::load(&content)?;
for mut entry in asc { for mut entry in asc {
if let Some(float) = entry.float_identifiers { if let Some(rules) = &mut entry.float_identifiers {
for f in float { populate_rules(rules, &mut float_identifiers, &mut regex_identifiers)?;
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());
if matches!(
without_comment.matching_strategy,
Some(MatchingStrategy::Regex)
) {
let re = Regex::new(&without_comment.id)?;
regex_identifiers.insert(without_comment.id.clone(), re);
}
}
}
} }
if let Some(options) = entry.options {
if let Some(ref options) = entry.options {
let options = options.clone();
for o in options { for o in options {
match o { match o {
ApplicationOptions::ObjectNameChange => { ApplicationOptions::ObjectNameChange => {
if entry.identifier.matching_strategy.is_none() { populate_option(
entry.identifier.matching_strategy = &mut entry,
Option::from(MatchingStrategy::Legacy); &mut object_name_change_identifiers,
} &mut regex_identifiers,
)?;
if !object_name_change_identifiers.contains(&entry.identifier) {
object_name_change_identifiers.push(entry.identifier.clone());
if matches!(
entry.identifier.matching_strategy,
Some(MatchingStrategy::Regex)
) {
let re = Regex::new(&entry.identifier.id)?;
regex_identifiers.insert(entry.identifier.id.clone(), re);
}
}
} }
ApplicationOptions::Layered => { ApplicationOptions::Layered => {
if entry.identifier.matching_strategy.is_none() { populate_option(
entry.identifier.matching_strategy = &mut entry,
Option::from(MatchingStrategy::Legacy); &mut layered_identifiers,
} &mut regex_identifiers,
)?;
if !layered_identifiers.contains(&entry.identifier) {
layered_identifiers.push(entry.identifier.clone());
if matches!(
entry.identifier.matching_strategy,
Some(MatchingStrategy::Regex)
) {
let re = Regex::new(&entry.identifier.id)?;
regex_identifiers.insert(entry.identifier.id.clone(), re);
}
}
} }
ApplicationOptions::BorderOverflow => { ApplicationOptions::BorderOverflow => {
if entry.identifier.matching_strategy.is_none() { populate_option(
entry.identifier.matching_strategy = &mut entry,
Option::from(MatchingStrategy::Legacy); &mut border_overflow_identifiers,
} &mut regex_identifiers,
)?;
if !border_overflow_identifiers.contains(&entry.identifier) {
border_overflow_identifiers.push(entry.identifier.clone());
if matches!(
entry.identifier.matching_strategy,
Some(MatchingStrategy::Regex)
) {
let re = Regex::new(&entry.identifier.id)?;
regex_identifiers.insert(entry.identifier.id.clone(), re);
}
}
} }
ApplicationOptions::TrayAndMultiWindow => { ApplicationOptions::TrayAndMultiWindow => {
if entry.identifier.matching_strategy.is_none() { populate_option(
entry.identifier.matching_strategy = &mut entry,
Option::from(MatchingStrategy::Legacy); &mut tray_and_multi_window_identifiers,
} &mut regex_identifiers,
)?;
if !tray_and_multi_window_identifiers.contains(&entry.identifier) {
tray_and_multi_window_identifiers
.push(entry.identifier.clone());
if matches!(
entry.identifier.matching_strategy,
Some(MatchingStrategy::Regex)
) {
let re = Regex::new(&entry.identifier.id)?;
regex_identifiers.insert(entry.identifier.id.clone(), re);
}
}
} }
ApplicationOptions::Force => { ApplicationOptions::Force => {
if entry.identifier.matching_strategy.is_none() { populate_option(
entry.identifier.matching_strategy = &mut entry,
Option::from(MatchingStrategy::Legacy); &mut manage_identifiers,
} &mut regex_identifiers,
)?;
if !manage_identifiers.contains(&entry.identifier) {
manage_identifiers.push(entry.identifier.clone());
if matches!(
entry.identifier.matching_strategy,
Some(MatchingStrategy::Regex)
) {
let re = Regex::new(&entry.identifier.id)?;
regex_identifiers.insert(entry.identifier.id.clone(), re);
}
}
} }
} }
} }
@@ -901,3 +765,67 @@ impl StaticConfig {
Ok(()) Ok(())
} }
} }
fn populate_option(
entry: &mut ApplicationConfiguration,
identifiers: &mut Vec<MatchingRule>,
regex_identifiers: &mut HashMap<String, Regex>,
) -> Result<()> {
if entry.identifier.matching_strategy.is_none() {
entry.identifier.matching_strategy = Option::from(MatchingStrategy::Legacy);
}
let rule = MatchingRule::Simple(entry.identifier.clone());
if !identifiers.contains(&rule) {
identifiers.push(rule);
if matches!(
entry.identifier.matching_strategy,
Some(MatchingStrategy::Regex)
) {
let re = Regex::new(&entry.identifier.id)?;
regex_identifiers.insert(entry.identifier.id.clone(), re);
}
}
Ok(())
}
fn populate_rules(
matching_rules: &mut Vec<MatchingRule>,
identifiers: &mut Vec<MatchingRule>,
regex_identifiers: &mut HashMap<String, Regex>,
) -> Result<()> {
for matching_rule in matching_rules {
if !identifiers.contains(matching_rule) {
match matching_rule {
MatchingRule::Simple(simple) => {
if simple.matching_strategy.is_none() {
simple.matching_strategy = Option::from(MatchingStrategy::Legacy);
}
if matches!(simple.matching_strategy, Some(MatchingStrategy::Regex)) {
let re = Regex::new(&simple.id)?;
regex_identifiers.insert(simple.id.clone(), re);
}
}
MatchingRule::Composite(composite) => {
for rule in composite {
if rule.matching_strategy.is_none() {
rule.matching_strategy = Option::from(MatchingStrategy::Legacy);
}
if matches!(rule.matching_strategy, Some(MatchingStrategy::Regex)) {
let re = Regex::new(&rule.id)?;
regex_identifiers.insert(rule.id.clone(), re);
}
}
}
}
identifiers.push(matching_rule.clone());
}
}
Ok(())
}
+189 -142
View File
@@ -10,6 +10,7 @@ use color_eyre::eyre;
use color_eyre::eyre::anyhow; use color_eyre::eyre::anyhow;
use color_eyre::Result; use color_eyre::Result;
use komorebi_core::config_generation::IdWithIdentifier; use komorebi_core::config_generation::IdWithIdentifier;
use komorebi_core::config_generation::MatchingRule;
use komorebi_core::config_generation::MatchingStrategy; use komorebi_core::config_generation::MatchingStrategy;
use regex::Regex; use regex::Regex;
use schemars::JsonSchema; use schemars::JsonSchema;
@@ -506,157 +507,203 @@ pub fn should_act(
exe_name: &str, exe_name: &str,
class: &str, class: &str,
path: &str, path: &str,
identifiers: &[IdWithIdentifier], identifiers: &[MatchingRule],
regex_identifiers: &HashMap<String, Regex>, regex_identifiers: &HashMap<String, Regex>,
) -> bool { ) -> bool {
let mut should_act = false; let mut should_act = false;
for identifier in identifiers { for identifier in identifiers {
match identifier.matching_strategy { match identifier {
None => { MatchingRule::Simple(identifier) => {
panic!("there is no matching strategy identified for this rule"); if should_act_individual(
title,
exe_name,
class,
path,
identifier,
regex_identifiers,
) {
should_act = true
};
} }
Some(MatchingStrategy::Legacy) => match identifier.kind { MatchingRule::Composite(identifiers) => {
ApplicationIdentifier::Title => { let mut composite_results = vec![];
if title.starts_with(&identifier.id) || title.ends_with(&identifier.id) { for identifier in identifiers {
should_act = true; composite_results.push(should_act_individual(
} title,
exe_name,
class,
path,
identifier,
regex_identifiers,
));
} }
ApplicationIdentifier::Class => {
if class.starts_with(&identifier.id) || class.ends_with(&identifier.id) { if composite_results.iter().all(|&x| x) {
should_act = true; should_act = true;
}
} }
ApplicationIdentifier::Exe => { }
if exe_name.eq(&identifier.id) {
should_act = true;
}
}
ApplicationIdentifier::Path => {
if path.eq(&identifier.id) {
should_act = true;
}
}
},
Some(MatchingStrategy::Equals) => match identifier.kind {
ApplicationIdentifier::Title => {
if title.eq(&identifier.id) {
should_act = true;
}
}
ApplicationIdentifier::Class => {
if class.eq(&identifier.id) {
should_act = true;
}
}
ApplicationIdentifier::Exe => {
if exe_name.eq(&identifier.id) {
should_act = true;
}
}
ApplicationIdentifier::Path => {
if path.eq(&identifier.id) {
should_act = true;
}
}
},
Some(MatchingStrategy::StartsWith) => match identifier.kind {
ApplicationIdentifier::Title => {
if title.starts_with(&identifier.id) {
should_act = true;
}
}
ApplicationIdentifier::Class => {
if class.starts_with(&identifier.id) {
should_act = true;
}
}
ApplicationIdentifier::Exe => {
if exe_name.starts_with(&identifier.id) {
should_act = true;
}
}
ApplicationIdentifier::Path => {
if path.starts_with(&identifier.id) {
should_act = true;
}
}
},
Some(MatchingStrategy::EndsWith) => match identifier.kind {
ApplicationIdentifier::Title => {
if title.ends_with(&identifier.id) {
should_act = true;
}
}
ApplicationIdentifier::Class => {
if class.ends_with(&identifier.id) {
should_act = true;
}
}
ApplicationIdentifier::Exe => {
if exe_name.ends_with(&identifier.id) {
should_act = true;
}
}
ApplicationIdentifier::Path => {
if path.ends_with(&identifier.id) {
should_act = true;
}
}
},
Some(MatchingStrategy::Contains) => match identifier.kind {
ApplicationIdentifier::Title => {
if title.contains(&identifier.id) {
should_act = true;
}
}
ApplicationIdentifier::Class => {
if class.contains(&identifier.id) {
should_act = true;
}
}
ApplicationIdentifier::Exe => {
if exe_name.contains(&identifier.id) {
should_act = true;
}
}
ApplicationIdentifier::Path => {
if path.contains(&identifier.id) {
should_act = true;
}
}
},
Some(MatchingStrategy::Regex) => match identifier.kind {
ApplicationIdentifier::Title => {
if let Some(re) = regex_identifiers.get(&identifier.id) {
if re.is_match(title) {
should_act = true;
}
}
}
ApplicationIdentifier::Class => {
if let Some(re) = regex_identifiers.get(&identifier.id) {
if re.is_match(class) {
should_act = true;
}
}
}
ApplicationIdentifier::Exe => {
if let Some(re) = regex_identifiers.get(&identifier.id) {
if re.is_match(exe_name) {
should_act = true;
}
}
}
ApplicationIdentifier::Path => {
if let Some(re) = regex_identifiers.get(&identifier.id) {
if re.is_match(path) {
should_act = true;
}
}
}
},
} }
} }
should_act should_act
} }
#[allow(clippy::cognitive_complexity, clippy::too_many_lines)]
pub fn should_act_individual(
title: &str,
exe_name: &str,
class: &str,
path: &str,
identifier: &IdWithIdentifier,
regex_identifiers: &HashMap<String, Regex>,
) -> bool {
let mut should_act = false;
match identifier.matching_strategy {
None => {
panic!("there is no matching strategy identified for this rule");
}
Some(MatchingStrategy::Legacy) => match identifier.kind {
ApplicationIdentifier::Title => {
if title.starts_with(&identifier.id) || title.ends_with(&identifier.id) {
should_act = true;
}
}
ApplicationIdentifier::Class => {
if class.starts_with(&identifier.id) || class.ends_with(&identifier.id) {
should_act = true;
}
}
ApplicationIdentifier::Exe => {
if exe_name.eq(&identifier.id) {
should_act = true;
}
}
ApplicationIdentifier::Path => {
if path.eq(&identifier.id) {
should_act = true;
}
}
},
Some(MatchingStrategy::Equals) => match identifier.kind {
ApplicationIdentifier::Title => {
if title.eq(&identifier.id) {
should_act = true;
}
}
ApplicationIdentifier::Class => {
if class.eq(&identifier.id) {
should_act = true;
}
}
ApplicationIdentifier::Exe => {
if exe_name.eq(&identifier.id) {
should_act = true;
}
}
ApplicationIdentifier::Path => {
if path.eq(&identifier.id) {
should_act = true;
}
}
},
Some(MatchingStrategy::StartsWith) => match identifier.kind {
ApplicationIdentifier::Title => {
if title.starts_with(&identifier.id) {
should_act = true;
}
}
ApplicationIdentifier::Class => {
if class.starts_with(&identifier.id) {
should_act = true;
}
}
ApplicationIdentifier::Exe => {
if exe_name.starts_with(&identifier.id) {
should_act = true;
}
}
ApplicationIdentifier::Path => {
if path.starts_with(&identifier.id) {
should_act = true;
}
}
},
Some(MatchingStrategy::EndsWith) => match identifier.kind {
ApplicationIdentifier::Title => {
if title.ends_with(&identifier.id) {
should_act = true;
}
}
ApplicationIdentifier::Class => {
if class.ends_with(&identifier.id) {
should_act = true;
}
}
ApplicationIdentifier::Exe => {
if exe_name.ends_with(&identifier.id) {
should_act = true;
}
}
ApplicationIdentifier::Path => {
if path.ends_with(&identifier.id) {
should_act = true;
}
}
},
Some(MatchingStrategy::Contains) => match identifier.kind {
ApplicationIdentifier::Title => {
if title.contains(&identifier.id) {
should_act = true;
}
}
ApplicationIdentifier::Class => {
if class.contains(&identifier.id) {
should_act = true;
}
}
ApplicationIdentifier::Exe => {
if exe_name.contains(&identifier.id) {
should_act = true;
}
}
ApplicationIdentifier::Path => {
if path.contains(&identifier.id) {
should_act = true;
}
}
},
Some(MatchingStrategy::Regex) => match identifier.kind {
ApplicationIdentifier::Title => {
if let Some(re) = regex_identifiers.get(&identifier.id) {
if re.is_match(title) {
should_act = true;
}
}
}
ApplicationIdentifier::Class => {
if let Some(re) = regex_identifiers.get(&identifier.id) {
if re.is_match(class) {
should_act = true;
}
}
}
ApplicationIdentifier::Exe => {
if let Some(re) = regex_identifiers.get(&identifier.id) {
if re.is_match(exe_name) {
should_act = true;
}
}
}
ApplicationIdentifier::Path => {
if let Some(re) = regex_identifiers.get(&identifier.id) {
if re.is_match(path) {
should_act = true;
}
}
}
},
}
should_act
}
+7 -7
View File
@@ -21,7 +21,7 @@ use serde::Deserialize;
use serde::Serialize; use serde::Serialize;
use uds_windows::UnixListener; use uds_windows::UnixListener;
use komorebi_core::config_generation::IdWithIdentifier; use komorebi_core::config_generation::MatchingRule;
use komorebi_core::custom_layout::CustomLayout; use komorebi_core::custom_layout::CustomLayout;
use komorebi_core::Arrangement; use komorebi_core::Arrangement;
use komorebi_core::Axis; use komorebi_core::Axis;
@@ -97,12 +97,12 @@ pub struct State {
pub mouse_follows_focus: bool, pub mouse_follows_focus: bool,
pub has_pending_raise_op: bool, pub has_pending_raise_op: bool,
pub remove_titlebars: bool, pub remove_titlebars: bool,
pub float_identifiers: Vec<IdWithIdentifier>, pub float_identifiers: Vec<MatchingRule>,
pub manage_identifiers: Vec<IdWithIdentifier>, pub manage_identifiers: Vec<MatchingRule>,
pub layered_whitelist: Vec<IdWithIdentifier>, pub layered_whitelist: Vec<MatchingRule>,
pub tray_and_multi_window_identifiers: Vec<IdWithIdentifier>, pub tray_and_multi_window_identifiers: Vec<MatchingRule>,
pub border_overflow_identifiers: Vec<IdWithIdentifier>, pub border_overflow_identifiers: Vec<MatchingRule>,
pub name_change_on_launch_identifiers: Vec<IdWithIdentifier>, pub name_change_on_launch_identifiers: Vec<MatchingRule>,
pub monitor_index_preferences: HashMap<usize, Rect>, pub monitor_index_preferences: HashMap<usize, Rect>,
pub display_index_preferences: HashMap<usize, String>, pub display_index_preferences: HashMap<usize, String>,
} }