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.
This commit is contained in:
LGUG2Z
2024-09-29 18:09:29 -07:00
parent b7198242ff
commit 04cde3f757
5 changed files with 178 additions and 267 deletions
+8
View File
@@ -59,6 +59,14 @@ pub enum MatchingRule {
Composite(Vec<IdWithIdentifier>), Composite(Vec<IdWithIdentifier>),
} }
#[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)] #[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize, JsonSchema)]
pub struct IdWithIdentifier { pub struct IdWithIdentifier {
pub kind: ApplicationIdentifier, pub kind: ApplicationIdentifier,
+3 -4
View File
@@ -62,6 +62,7 @@ pub use windows_api::*;
use crate::core::config_generation::IdWithIdentifier; use crate::core::config_generation::IdWithIdentifier;
use crate::core::config_generation::MatchingRule; use crate::core::config_generation::MatchingRule;
use crate::core::config_generation::MatchingStrategy; use crate::core::config_generation::MatchingStrategy;
use crate::core::config_generation::WorkspaceMatchingRule;
use color_eyre::Result; use color_eyre::Result;
use os_info::Version; use os_info::Version;
use parking_lot::Mutex; use parking_lot::Mutex;
@@ -74,8 +75,6 @@ use which::which;
use winreg::enums::HKEY_CURRENT_USER; use winreg::enums::HKEY_CURRENT_USER;
use winreg::RegKey; use winreg::RegKey;
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<MatchingRule>>> = Arc::new(Mutex::new(vec![ static ref LAYERED_WHITELIST: Arc<Mutex<Vec<MatchingRule>>> = Arc::new(Mutex::new(vec![
@@ -135,8 +134,8 @@ lazy_static! {
Arc::new(Mutex::new(HashMap::new())); Arc::new(Mutex::new(HashMap::new()));
static ref DISPLAY_INDEX_PREFERENCES: Arc<Mutex<HashMap<usize, String>>> = static ref DISPLAY_INDEX_PREFERENCES: Arc<Mutex<HashMap<usize, String>>> =
Arc::new(Mutex::new(HashMap::new())); Arc::new(Mutex::new(HashMap::new()));
static ref WORKSPACE_RULES: Arc<Mutex<HashMap<String, WorkspaceRule>>> = static ref WORKSPACE_MATCHING_RULES: Arc<Mutex<Vec<WorkspaceMatchingRule>>> =
Arc::new(Mutex::new(HashMap::new())); Arc::new(Mutex::new(Vec::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<MatchingRule>>> = Arc::new(Mutex::new(vec![])); static ref MANAGE_IDENTIFIERS: Arc<Mutex<Vec<MatchingRule>>> = Arc::new(Mutex::new(vec![]));
+81 -82
View File
@@ -44,6 +44,7 @@ use crate::border_manager;
use crate::border_manager::IMPLEMENTATION; use crate::border_manager::IMPLEMENTATION;
use crate::border_manager::STYLE; use crate::border_manager::STYLE;
use crate::colour::Rgb; use crate::colour::Rgb;
use crate::config_generation::WorkspaceMatchingRule;
use crate::current_virtual_desktop; use crate::current_virtual_desktop;
use crate::notify_subscribers; use crate::notify_subscribers;
use crate::stackbar_manager; use crate::stackbar_manager;
@@ -81,7 +82,7 @@ use crate::SUBSCRIPTION_SOCKETS;
use crate::TCP_CONNECTIONS; use crate::TCP_CONNECTIONS;
use crate::TRAY_AND_MULTI_WINDOW_IDENTIFIERS; use crate::TRAY_AND_MULTI_WINDOW_IDENTIFIERS;
use crate::WINDOWS_11; use crate::WINDOWS_11;
use crate::WORKSPACE_RULES; use crate::WORKSPACE_MATCHING_RULES;
use stackbar_manager::STACKBAR_FOCUSED_TEXT_COLOUR; use stackbar_manager::STACKBAR_FOCUSED_TEXT_COLOUR;
use stackbar_manager::STACKBAR_LABEL; use stackbar_manager::STACKBAR_LABEL;
use stackbar_manager::STACKBAR_MODE; use stackbar_manager::STACKBAR_MODE;
@@ -269,58 +270,101 @@ impl WindowManager {
self.set_workspace_padding(monitor_idx, workspace_idx, size)?; self.set_workspace_padding(monitor_idx, workspace_idx, size)?;
} }
} }
SocketMessage::InitialWorkspaceRule(_, ref id, monitor_idx, workspace_idx) => { SocketMessage::InitialWorkspaceRule(identifier, ref id, monitor_idx, workspace_idx) => {
self.handle_initial_workspace_rules(id, monitor_idx, workspace_idx)?; let mut workspace_rules = WORKSPACE_MATCHING_RULES.lock();
} let workspace_matching_rule = WorkspaceMatchingRule {
SocketMessage::InitialNamedWorkspaceRule(_, ref id, ref workspace) => { monitor_index: monitor_idx,
if let Some((monitor_idx, workspace_idx)) = workspace_index: workspace_idx,
self.monitor_workspace_index_by_name(workspace) matching_rule: MatchingRule::Simple(IdWithIdentifier {
{ kind: identifier,
self.handle_initial_workspace_rules(id, monitor_idx, workspace_idx)?; 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) => { SocketMessage::InitialNamedWorkspaceRule(identifier, ref id, ref workspace) => {
self.handle_definitive_workspace_rules(id, monitor_idx, workspace_idx)?;
}
SocketMessage::NamedWorkspaceRule(_, ref id, ref workspace) => {
if let Some((monitor_idx, workspace_idx)) = if let Some((monitor_idx, workspace_idx)) =
self.monitor_workspace_index_by_name(workspace) 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) => { SocketMessage::ClearWorkspaceRules(monitor_idx, workspace_idx) => {
let mut workspace_rules = WORKSPACE_RULES.lock(); let mut workspace_rules = WORKSPACE_MATCHING_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.retain(|r| {
workspace_rules.remove(&rule); r.monitor_index != monitor_idx && r.workspace_index != workspace_idx
} });
} }
SocketMessage::ClearNamedWorkspaceRules(ref workspace) => { SocketMessage::ClearNamedWorkspaceRules(ref workspace) => {
if let Some((monitor_idx, workspace_idx)) = if let Some((monitor_idx, workspace_idx)) =
self.monitor_workspace_index_by_name(workspace) self.monitor_workspace_index_by_name(workspace)
{ {
let mut workspace_rules = WORKSPACE_RULES.lock(); let mut workspace_rules = WORKSPACE_MATCHING_RULES.lock();
let mut to_remove = vec![]; workspace_rules.retain(|r| {
for (id, (m_idx, w_idx, _)) in workspace_rules.iter() { r.monitor_index != monitor_idx && r.workspace_index != workspace_idx
if monitor_idx == *m_idx && workspace_idx == *w_idx { });
to_remove.push(id.clone());
}
}
for rule in to_remove {
workspace_rules.remove(&rule);
}
} }
} }
SocketMessage::ClearAllWorkspaceRules => { SocketMessage::ClearAllWorkspaceRules => {
let mut workspace_rules = WORKSPACE_RULES.lock(); let mut workspace_rules = WORKSPACE_MATCHING_RULES.lock();
workspace_rules.clear(); workspace_rules.clear();
} }
SocketMessage::ManageRule(identifier, ref id) => { SocketMessage::ManageRule(identifier, ref id) => {
@@ -1102,7 +1146,7 @@ impl WindowManager {
// Check that this is a valid static config file first // Check that this is a valid static config file first
if StaticConfig::read(config).is_ok() { if StaticConfig::read(config).is_ok() {
// Clear workspace rules; these will need to be replaced // 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 // Pause so that restored windows come to the foreground from all workspaces
self.is_paused = true; self.is_paused = true;
// Bring all windows to the foreground // Bring all windows to the foreground
@@ -1496,51 +1540,6 @@ impl WindowManager {
tracing::info!("processed"); tracing::info!("processed");
Ok(()) 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<Mutex<WindowManager>>, mut stream: UnixStream) -> Result<()> { pub fn read_commands_uds(wm: &Arc<Mutex<WindowManager>>, mut stream: UnixStream) -> Result<()> {
+37 -131
View File
@@ -45,17 +45,16 @@ use crate::REGEX_IDENTIFIERS;
use crate::TRANSPARENCY_BLACKLIST; use crate::TRANSPARENCY_BLACKLIST;
use crate::TRAY_AND_MULTI_WINDOW_IDENTIFIERS; use crate::TRAY_AND_MULTI_WINDOW_IDENTIFIERS;
use crate::WINDOWS_11; 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::ApplicationConfiguration;
use crate::core::config_generation::ApplicationConfigurationGenerator; use crate::core::config_generation::ApplicationConfigurationGenerator;
use crate::core::config_generation::ApplicationOptions; use crate::core::config_generation::ApplicationOptions;
use crate::core::config_generation::IdWithIdentifier;
use crate::core::config_generation::MatchingRule; use crate::core::config_generation::MatchingRule;
use crate::core::config_generation::MatchingStrategy; use crate::core::config_generation::MatchingStrategy;
use crate::core::resolve_home_path; use crate::core::resolve_home_path;
use crate::core::AnimationStyle; use crate::core::AnimationStyle;
use crate::core::ApplicationIdentifier;
use crate::core::BorderStyle; use crate::core::BorderStyle;
use crate::core::DefaultLayout; use crate::core::DefaultLayout;
use crate::core::FocusFollowsMouseImplementation; use crate::core::FocusFollowsMouseImplementation;
@@ -121,10 +120,10 @@ pub struct WorkspaceConfig {
pub workspace_padding: Option<i32>, pub workspace_padding: Option<i32>,
/// Initial workspace application rules /// Initial workspace application rules
#[serde(skip_serializing_if = "Option::is_none")] #[serde(skip_serializing_if = "Option::is_none")]
pub initial_workspace_rules: Option<Vec<IdWithIdentifier>>, pub initial_workspace_rules: Option<Vec<MatchingRule>>,
/// Permanent workspace application rules /// Permanent workspace application rules
#[serde(skip_serializing_if = "Option::is_none")] #[serde(skip_serializing_if = "Option::is_none")]
pub workspace_rules: Option<Vec<IdWithIdentifier>>, pub workspace_rules: Option<Vec<MatchingRule>>,
/// Apply this monitor's window-based work area offset (default: true) /// Apply this monitor's window-based work area offset (default: true)
#[serde(skip_serializing_if = "Option::is_none")] #[serde(skip_serializing_if = "Option::is_none")]
pub apply_window_based_work_area_offset: Option<bool>, pub apply_window_based_work_area_offset: Option<bool>,
@@ -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_container_padding = DEFAULT_CONTAINER_PADDING.load(Ordering::SeqCst);
let default_workspace_padding = DEFAULT_WORKSPACE_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, custom_layout_rules: None,
container_padding, container_padding,
workspace_padding, workspace_padding,
initial_workspace_rules: initial_ws_rules, initial_workspace_rules: None,
workspace_rules: ws_rules, workspace_rules: None,
apply_window_based_work_area_offset: Some(value.apply_window_based_work_area_offset()), 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)); 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 { let border_colours = if border_manager::FOCUSED.load(Ordering::SeqCst) == 0 {
None None
} else { } else {
@@ -1150,22 +1029,35 @@ impl StaticConfig {
} }
} }
let mut workspace_matching_rules = WORKSPACE_MATCHING_RULES.lock();
for (j, ws) in monitor.workspaces.iter().enumerate() { for (j, ws) in monitor.workspaces.iter().enumerate() {
if let Some(rules) = &ws.workspace_rules { if let Some(rules) = &ws.workspace_rules {
for r in 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 { if let Some(rules) = &ws.initial_workspace_rules {
for r in 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) { if value.border == Some(true) {
border_manager::BORDER_ENABLED.store(true, Ordering::SeqCst); 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() { for (j, ws) in monitor.workspaces.iter().enumerate() {
if let Some(rules) = &ws.workspace_rules { if let Some(rules) = &ws.workspace_rules {
for r in 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 { if let Some(rules) = &ws.initial_workspace_rules {
for r in 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 { if let Some(enabled) = value.border {
border_manager::BORDER_ENABLED.store(enabled, Ordering::SeqCst); border_manager::BORDER_ENABLED.store(enabled, Ordering::SeqCst);
} }
+49 -50
View File
@@ -16,7 +16,6 @@ use hotwatch::notify::ErrorKind as NotifyErrorKind;
use hotwatch::EventKind; use hotwatch::EventKind;
use hotwatch::Hotwatch; use hotwatch::Hotwatch;
use parking_lot::Mutex; use parking_lot::Mutex;
use regex::Regex;
use schemars::JsonSchema; use schemars::JsonSchema;
use serde::Deserialize; use serde::Deserialize;
use serde::Serialize; use serde::Serialize;
@@ -43,12 +42,14 @@ use crate::core::WindowContainerBehaviour;
use crate::border_manager; use crate::border_manager;
use crate::border_manager::STYLE; use crate::border_manager::STYLE;
use crate::config_generation::WorkspaceMatchingRule;
use crate::container::Container; use crate::container::Container;
use crate::core::StackbarMode; use crate::core::StackbarMode;
use crate::current_virtual_desktop; use crate::current_virtual_desktop;
use crate::load_configuration; use crate::load_configuration;
use crate::monitor::Monitor; use crate::monitor::Monitor;
use crate::ring::Ring; use crate::ring::Ring;
use crate::should_act_individual;
use crate::stackbar_manager::STACKBAR_FOCUSED_TEXT_COLOUR; use crate::stackbar_manager::STACKBAR_FOCUSED_TEXT_COLOUR;
use crate::stackbar_manager::STACKBAR_LABEL; use crate::stackbar_manager::STACKBAR_LABEL;
use crate::stackbar_manager::STACKBAR_MODE; use crate::stackbar_manager::STACKBAR_MODE;
@@ -67,7 +68,6 @@ use crate::BorderColours;
use crate::Colour; use crate::Colour;
use crate::CrossBoundaryBehaviour; use crate::CrossBoundaryBehaviour;
use crate::Rgb; use crate::Rgb;
use crate::WorkspaceRule;
use crate::CUSTOM_FFM; use crate::CUSTOM_FFM;
use crate::DATA_DIR; use crate::DATA_DIR;
use crate::DISPLAY_INDEX_PREFERENCES; use crate::DISPLAY_INDEX_PREFERENCES;
@@ -79,9 +79,10 @@ use crate::MANAGE_IDENTIFIERS;
use crate::MONITOR_INDEX_PREFERENCES; use crate::MONITOR_INDEX_PREFERENCES;
use crate::NO_TITLEBAR; use crate::NO_TITLEBAR;
use crate::OBJECT_NAME_CHANGE_ON_LAUNCH; use crate::OBJECT_NAME_CHANGE_ON_LAUNCH;
use crate::REGEX_IDENTIFIERS;
use crate::REMOVE_TITLEBARS; use crate::REMOVE_TITLEBARS;
use crate::TRAY_AND_MULTI_WINDOW_IDENTIFIERS; use crate::TRAY_AND_MULTI_WINDOW_IDENTIFIERS;
use crate::WORKSPACE_RULES; use crate::WORKSPACE_MATCHING_RULES;
#[derive(Debug)] #[derive(Debug)]
pub struct WindowManager { pub struct WindowManager {
@@ -142,7 +143,7 @@ pub struct GlobalState {
pub name_change_on_launch_identifiers: Vec<MatchingRule>, 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>,
pub workspace_rules: HashMap<String, WorkspaceRule>, pub workspace_rules: Vec<WorkspaceMatchingRule>,
pub window_hiding_behaviour: HidingBehaviour, pub window_hiding_behaviour: HidingBehaviour,
pub configuration_dir: PathBuf, pub configuration_dir: PathBuf,
pub data_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(), name_change_on_launch_identifiers: OBJECT_NAME_CHANGE_ON_LAUNCH.lock().clone(),
monitor_index_preferences: MONITOR_INDEX_PREFERENCES.lock().clone(), monitor_index_preferences: MONITOR_INDEX_PREFERENCES.lock().clone(),
display_index_preferences: DISPLAY_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(), window_hiding_behaviour: *HIDING_BEHAVIOUR.lock(),
configuration_dir: HOME_DIR.clone(), configuration_dir: HOME_DIR.clone(),
data_dir: DATA_DIR.clone(), data_dir: DATA_DIR.clone(),
@@ -233,7 +234,6 @@ struct EnforceWorkspaceRuleOp {
target_monitor_idx: usize, target_monitor_idx: usize,
target_workspace_idx: usize, target_workspace_idx: usize,
} }
impl EnforceWorkspaceRuleOp { impl EnforceWorkspaceRuleOp {
const fn is_origin(&self, monitor_idx: usize, workspace_idx: usize) -> bool { const fn is_origin(&self, monitor_idx: usize, workspace_idx: usize) -> bool {
self.origin_monitor_idx == monitor_idx && self.origin_workspace_idx == workspace_idx 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"))? .ok_or_else(|| anyhow!("there is no monitor with that index"))?
.focused_workspace_idx(); .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 // Go through all the monitors and workspaces
for (i, monitor) in self.monitors().iter().enumerate() { for (i, monitor) in self.monitors().iter().enumerate() {
for (j, workspace) in monitor.workspaces().iter().enumerate() { for (j, workspace) in monitor.workspaces().iter().enumerate() {
@@ -460,63 +461,61 @@ impl WindowManager {
let exe_name = window.exe()?; let exe_name = window.exe()?;
let title = window.title()?; let title = window.title()?;
let class = window.class()?; let class = window.class()?;
let path = window.path()?;
let mut found_workspace_rule = workspace_rules.get(&exe_name); for rule in &*workspace_matching_rules {
let matched = match &rule.matching_rule {
if found_workspace_rule.is_none() { MatchingRule::Simple(r) => should_act_individual(
found_workspace_rule = workspace_rules.get(&title); &title,
} &exe_name,
&class,
if found_workspace_rule.is_none() { &path,
found_workspace_rule = workspace_rules.get(&class); r,
} &regex_identifiers,
),
if found_workspace_rule.is_none() { MatchingRule::Composite(r) => {
for (k, v) in workspace_rules.iter() { let mut composite_results = vec![];
if let Ok(re) = Regex::new(k) { for identifier in r {
if re.is_match(&exe_name) { composite_results.push(should_act_individual(
found_workspace_rule = Some(v); &title,
&exe_name,
&class,
&path,
identifier,
&regex_identifiers,
));
} }
if re.is_match(&title) { composite_results.iter().all(|&x| x)
found_workspace_rule = Some(v);
}
if re.is_match(&class) {
found_workspace_rule = Some(v);
}
} }
} };
}
// If the executable names or titles of any of those windows are in our rules map if matched {
if let Some((monitor_idx, workspace_idx, apply_on_first_show_only)) = if rule.initial_only {
found_workspace_rule if !already_moved_window_handles.contains(&window.hwnd) {
{ already_moved_window_handles.insert(window.hwnd);
if *apply_on_first_show_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( self.add_window_handle_to_move_based_on_workspace_rule(
&window.title()?, &window.title()?,
window.hwnd, window.hwnd,
i, i,
j, j,
*monitor_idx, rule.monitor_index,
*workspace_idx, rule.workspace_index,
&mut to_move, &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,
);
} }
} }
} }