From 4306a7bafe598fea5dc64749a758135d5109ea76 Mon Sep 17 00:00:00 2001 From: Alvin Truong Date: Sat, 22 Apr 2023 00:27:45 -0700 Subject: [PATCH] feat(workspace-rule): add ability to only apply workspace rule on first show of app --- komorebi-core/src/lib.rs | 4 +- komorebi/src/main.rs | 2 +- komorebi/src/process_command.rs | 25 ++++++-- komorebi/src/process_event.rs | 9 +++ komorebi/src/window_manager.rs | 109 +++++++++++++++++++++++--------- komorebic/src/main.rs | 25 ++++++-- 6 files changed, 134 insertions(+), 40 deletions(-) diff --git a/komorebi-core/src/lib.rs b/komorebi-core/src/lib.rs index 4e0e0962..a7086aab 100644 --- a/komorebi-core/src/lib.rs +++ b/komorebi-core/src/lib.rs @@ -127,8 +127,8 @@ pub enum SocketMessage { WorkAreaOffset(Rect), MonitorWorkAreaOffset(usize, Rect), ResizeDelta(i32), - WorkspaceRule(ApplicationIdentifier, String, usize, usize), - NamedWorkspaceRule(ApplicationIdentifier, String, String), + WorkspaceRule(ApplicationIdentifier, String, usize, usize, bool), + NamedWorkspaceRule(ApplicationIdentifier, String, String, bool), FloatRule(ApplicationIdentifier, String), ManageRule(ApplicationIdentifier, String), IdentifyObjectNameChangeApplication(ApplicationIdentifier, String), diff --git a/komorebi/src/main.rs b/komorebi/src/main.rs index 089a4b76..bf732041 100644 --- a/komorebi/src/main.rs +++ b/komorebi/src/main.rs @@ -94,7 +94,7 @@ lazy_static! { ])); static ref MONITOR_INDEX_PREFERENCES: Arc>> = Arc::new(Mutex::new(HashMap::new())); - static ref WORKSPACE_RULES: Arc>> = + static ref WORKSPACE_RULES: Arc>> = Arc::new(Mutex::new(HashMap::new())); static ref MANAGE_IDENTIFIERS: Arc>> = Arc::new(Mutex::new(vec![])); static ref FLOAT_IDENTIFIERS: Arc>> = Arc::new(Mutex::new(vec![ diff --git a/komorebi/src/process_command.rs b/komorebi/src/process_command.rs index 73bcb025..989c5c33 100644 --- a/komorebi/src/process_command.rs +++ b/komorebi/src/process_command.rs @@ -215,21 +215,38 @@ impl WindowManager { self.set_workspace_padding(monitor_idx, workspace_idx, size)?; } } - SocketMessage::WorkspaceRule(_, ref id, monitor_idx, workspace_idx) => { + SocketMessage::WorkspaceRule( + _, + ref id, + monitor_idx, + workspace_idx, + apply_on_first_show_only, + ) => { { let mut workspace_rules = WORKSPACE_RULES.lock(); - workspace_rules.insert(id.to_string(), (monitor_idx, workspace_idx)); + workspace_rules.insert( + id.to_string(), + (monitor_idx, workspace_idx, apply_on_first_show_only), + ); } self.enforce_workspace_rules()?; } - SocketMessage::NamedWorkspaceRule(_, ref id, ref workspace) => { + SocketMessage::NamedWorkspaceRule( + _, + ref id, + ref workspace, + apply_on_first_show_only, + ) => { if let Some((monitor_idx, workspace_idx)) = self.monitor_workspace_index_by_name(workspace) { { let mut workspace_rules = WORKSPACE_RULES.lock(); - workspace_rules.insert(id.to_string(), (monitor_idx, workspace_idx)); + workspace_rules.insert( + id.to_string(), + (monitor_idx, workspace_idx, apply_on_first_show_only), + ); } self.enforce_workspace_rules()?; diff --git a/komorebi/src/process_event.rs b/komorebi/src/process_event.rs index cc9fa441..55c6715c 100644 --- a/komorebi/src/process_event.rs +++ b/komorebi/src/process_event.rs @@ -150,6 +150,10 @@ impl WindowManager { WindowManagerEvent::Destroy(_, window) | WindowManagerEvent::Unmanage(window) => { self.focused_workspace_mut()?.remove_window(window.hwnd)?; self.update_focused_workspace(false)?; + + let mut already_moved_window_handles = self.already_moved_window_handles.lock(); + + already_moved_window_handles.remove(&window.hwnd); } WindowManagerEvent::Minimize(_, window) => { let mut hide = false; @@ -193,6 +197,11 @@ impl WindowManager { self.focused_workspace_mut()?.remove_window(window.hwnd)?; self.update_focused_workspace(false)?; } + + let mut already_moved_window_handles = self.already_moved_window_handles.lock(); + + already_moved_window_handles.remove(&window.hwnd); + } WindowManagerEvent::FocusChange(_, window) => { let workspace = self.focused_workspace_mut()?; diff --git a/komorebi/src/window_manager.rs b/komorebi/src/window_manager.rs index 15dd6ec5..4811af69 100644 --- a/komorebi/src/window_manager.rs +++ b/komorebi/src/window_manager.rs @@ -1,4 +1,5 @@ use std::collections::HashMap; +use std::collections::HashSet; use std::collections::VecDeque; use std::io::ErrorKind; use std::num::NonZeroUsize; @@ -71,6 +72,7 @@ pub struct WindowManager { pub virtual_desktop_id: Option>, pub has_pending_raise_op: bool, pub pending_move_op: Option<(usize, usize, usize)>, + pub already_moved_window_handles: Arc>>, } #[derive(Debug, Serialize, JsonSchema)] @@ -189,6 +191,7 @@ impl WindowManager { hotwatch: Hotwatch::new()?, has_pending_raise_op: false, pending_move_op: None, + already_moved_window_handles: Arc::new(Mutex::new(HashSet::new())), }) } @@ -494,41 +497,89 @@ impl WindowManager { for (j, workspace) in monitor.workspaces().iter().enumerate() { // And all the visible windows (at the top of a container) for window in workspace.visible_windows().into_iter().flatten() { + // TODO this function needs some TLC + let mut already_moved_window_handles = + self.already_moved_window_handles.lock(); // If the executable names or titles of any of those windows are in our rules map - if let Some((monitor_idx, workspace_idx)) = workspace_rules.get(&window.exe()?) + if let Some((monitor_idx, workspace_idx, apply_on_first_show_only)) = + workspace_rules.get(&window.exe()?) { - tracing::info!( - "{} should be on monitor {}, workspace {}", - window.title()?, - *monitor_idx, - *workspace_idx - ); + if *apply_on_first_show_only { + if !already_moved_window_handles.contains(&window.hwnd) { + tracing::info!( + "{} should be on monitor {}, workspace {}", + window.title()?, + *monitor_idx, + *workspace_idx + ); - // Create an operation outline and save it for later in the fn - to_move.push(EnforceWorkspaceRuleOp { - hwnd: window.hwnd, - origin_monitor_idx: i, - origin_workspace_idx: j, - target_monitor_idx: *monitor_idx, - target_workspace_idx: *workspace_idx, - }); - } else if let Some((monitor_idx, workspace_idx)) = + already_moved_window_handles + .insert(window.hwnd); + + // Create an operation outline and save it for later in the fn + to_move.push(EnforceWorkspaceRuleOp { + hwnd: window.hwnd, + origin_monitor_idx: i, + origin_workspace_idx: j, + target_monitor_idx: *monitor_idx, + target_workspace_idx: *workspace_idx, + }); + } + } else { + tracing::info!( + "{} should be on monitor {}, workspace {}", + window.title()?, + *monitor_idx, + *workspace_idx + ); + + // Create an operation outline and save it for later in the fn + to_move.push(EnforceWorkspaceRuleOp { + hwnd: window.hwnd, + origin_monitor_idx: i, + origin_workspace_idx: j, + target_monitor_idx: *monitor_idx, + target_workspace_idx: *workspace_idx, + }); + } + } else if let Some((monitor_idx, workspace_idx, apply_on_first_show_only)) = workspace_rules.get(&window.title()?) { - tracing::info!( - "{} should be on monitor {}, workspace {}", - window.title()?, - *monitor_idx, - *workspace_idx - ); + if *apply_on_first_show_only { + if !already_moved_window_handles.contains(&window.hwnd) { + tracing::info!( + "{} should be on monitor {}, workspace {}", + window.title()?, + *monitor_idx, + *workspace_idx + ); - to_move.push(EnforceWorkspaceRuleOp { - hwnd: window.hwnd, - origin_monitor_idx: i, - origin_workspace_idx: j, - target_monitor_idx: *monitor_idx, - target_workspace_idx: *workspace_idx, - }); + already_moved_window_handles + .insert(window.hwnd); + to_move.push(EnforceWorkspaceRuleOp { + hwnd: window.hwnd, + origin_monitor_idx: i, + origin_workspace_idx: j, + target_monitor_idx: *monitor_idx, + target_workspace_idx: *workspace_idx, + }); + } + } else { + tracing::info!( + "{} should be on monitor {}, workspace {}", + window.title()?, + *monitor_idx, + *workspace_idx + ); + + to_move.push(EnforceWorkspaceRuleOp { + hwnd: window.hwnd, + origin_monitor_idx: i, + origin_workspace_idx: j, + target_monitor_idx: *monitor_idx, + target_workspace_idx: *workspace_idx, + }); + } } } } diff --git a/komorebic/src/main.rs b/komorebic/src/main.rs index 902923fe..035103d8 100644 --- a/komorebic/src/main.rs +++ b/komorebic/src/main.rs @@ -525,6 +525,9 @@ struct WorkspaceRule { monitor: usize, /// Workspace index on the specified monitor (zero-indexed) workspace: usize, + #[clap(short,long)] + /// Only apply once on first app load + apply_on_first_show_only: bool, } #[derive(Parser, AhkFunction)] @@ -535,6 +538,9 @@ struct NamedWorkspaceRule { id: String, /// Name of a workspace workspace: String, + #[clap(short,long)] + /// Only apply once on first app load + apply_on_first_show_only: bool, } #[derive(Parser, AhkFunction)] @@ -1449,14 +1455,25 @@ fn main() -> Result<()> { } SubCommand::WorkspaceRule(arg) => { send_message( - &SocketMessage::WorkspaceRule(arg.identifier, arg.id, arg.monitor, arg.workspace) - .as_bytes()?, + &SocketMessage::WorkspaceRule( + arg.identifier, + arg.id, + arg.monitor, + arg.workspace, + arg.apply_on_first_show_only, + ) + .as_bytes()?, )?; } SubCommand::NamedWorkspaceRule(arg) => { send_message( - &SocketMessage::NamedWorkspaceRule(arg.identifier, arg.id, arg.workspace) - .as_bytes()?, + &SocketMessage::NamedWorkspaceRule( + arg.identifier, + arg.id, + arg.workspace, + arg.apply_on_first_show_only, + ) + .as_bytes()?, )?; } SubCommand::Stack(arg) => {