feat(rules): add explicit matching strategies

This commit is the first in a series of commits which will pave the way
for regex rule matching support in komorebi.

For now, in order to maintain backwards compat and not break anything,
all rules without a matching strategy will get assigned as using the
"Legacy" strategy.

This and the "Equals" strategy are the only two which have been
implemented so far.

There should not be any breaking changes in this commit, not any
functionality lost for users with pre-existing configurations.

re #60
This commit is contained in:
LGUG2Z
2023-09-22 15:05:17 -07:00
parent 57cc02f083
commit 0797316ee6
7 changed files with 129 additions and 26 deletions

View File

@@ -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<MatchingStrategy>,
}
#[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<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub matching_strategy: Option<MatchingStrategy>,
}
impl From<IdWithIdentifierAndComment> 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)]

View File

@@ -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 {

View File

@@ -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<Mutex<HashMap<String, WorkspaceRule>>> =
Arc::new(Mutex::new(HashMap::new()));
static ref MANAGE_IDENTIFIERS: Arc<Mutex<Vec<String>>> = Arc::new(Mutex::new(vec![]));
static ref FLOAT_IDENTIFIERS: Arc<Mutex<Vec<String>>> = Arc::new(Mutex::new(vec![
static ref FLOAT_IDENTIFIERS: Arc<Mutex<Vec<IdWithIdentifier>>> = 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<Mutex<Vec<String>>> = Arc::new(Mutex::new(vec![
"Chrome_RenderWidgetHostHWND".to_string(),

View File

@@ -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;

View File

@@ -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<Mutex<Receiver<WindowManagerEvent>>>,
) -> Result<WindowManager> {
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()?;

View File

@@ -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!(),
}
}
};

View File

@@ -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<String>,
pub float_identifiers: Vec<IdWithIdentifier>,
pub manage_identifiers: Vec<String>,
pub layered_whitelist: Vec<String>,
pub tray_and_multi_window_identifiers: Vec<String>,