mirror of
https://github.com/LGUG2Z/komorebi.git
synced 2026-05-21 19:26:58 +02:00
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:
@@ -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
@@ -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![]));
|
||||||
|
|||||||
@@ -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
@@ -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);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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,
|
||||||
}
|
®ex_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,
|
||||||
|
®ex_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,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user