feat(workspace-rule): add ability to only apply workspace rule on first

show of app
This commit is contained in:
Alvin Truong
2023-04-22 00:27:45 -07:00
committed by جاد
parent b647fdf01a
commit 4306a7bafe
6 changed files with 134 additions and 40 deletions

View File

@@ -127,8 +127,8 @@ pub enum SocketMessage {
WorkAreaOffset(Rect), WorkAreaOffset(Rect),
MonitorWorkAreaOffset(usize, Rect), MonitorWorkAreaOffset(usize, Rect),
ResizeDelta(i32), ResizeDelta(i32),
WorkspaceRule(ApplicationIdentifier, String, usize, usize), WorkspaceRule(ApplicationIdentifier, String, usize, usize, bool),
NamedWorkspaceRule(ApplicationIdentifier, String, String), NamedWorkspaceRule(ApplicationIdentifier, String, String, bool),
FloatRule(ApplicationIdentifier, String), FloatRule(ApplicationIdentifier, String),
ManageRule(ApplicationIdentifier, String), ManageRule(ApplicationIdentifier, String),
IdentifyObjectNameChangeApplication(ApplicationIdentifier, String), IdentifyObjectNameChangeApplication(ApplicationIdentifier, String),

View File

@@ -94,7 +94,7 @@ lazy_static! {
])); ]));
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()));
static ref WORKSPACE_RULES: Arc<Mutex<HashMap<String, (usize, usize)>>> = static ref WORKSPACE_RULES: Arc<Mutex<HashMap<String, (usize, usize, bool)>>> =
Arc::new(Mutex::new(HashMap::new())); Arc::new(Mutex::new(HashMap::new()));
static ref MANAGE_IDENTIFIERS: Arc<Mutex<Vec<String>>> = Arc::new(Mutex::new(vec![])); 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<String>>> = Arc::new(Mutex::new(vec![

View File

@@ -215,21 +215,38 @@ impl WindowManager {
self.set_workspace_padding(monitor_idx, workspace_idx, size)?; 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(); 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()?; 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)) = 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_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()?; self.enforce_workspace_rules()?;

View File

@@ -150,6 +150,10 @@ impl WindowManager {
WindowManagerEvent::Destroy(_, window) | WindowManagerEvent::Unmanage(window) => { WindowManagerEvent::Destroy(_, window) | WindowManagerEvent::Unmanage(window) => {
self.focused_workspace_mut()?.remove_window(window.hwnd)?; self.focused_workspace_mut()?.remove_window(window.hwnd)?;
self.update_focused_workspace(false)?; 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) => { WindowManagerEvent::Minimize(_, window) => {
let mut hide = false; let mut hide = false;
@@ -193,6 +197,11 @@ impl WindowManager {
self.focused_workspace_mut()?.remove_window(window.hwnd)?; self.focused_workspace_mut()?.remove_window(window.hwnd)?;
self.update_focused_workspace(false)?; 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) => { WindowManagerEvent::FocusChange(_, window) => {
let workspace = self.focused_workspace_mut()?; let workspace = self.focused_workspace_mut()?;

View File

@@ -1,4 +1,5 @@
use std::collections::HashMap; use std::collections::HashMap;
use std::collections::HashSet;
use std::collections::VecDeque; use std::collections::VecDeque;
use std::io::ErrorKind; use std::io::ErrorKind;
use std::num::NonZeroUsize; use std::num::NonZeroUsize;
@@ -71,6 +72,7 @@ pub struct WindowManager {
pub virtual_desktop_id: Option<Vec<u8>>, pub virtual_desktop_id: Option<Vec<u8>>,
pub has_pending_raise_op: bool, pub has_pending_raise_op: bool,
pub pending_move_op: Option<(usize, usize, usize)>, pub pending_move_op: Option<(usize, usize, usize)>,
pub already_moved_window_handles: Arc<Mutex<HashSet<isize>>>,
} }
#[derive(Debug, Serialize, JsonSchema)] #[derive(Debug, Serialize, JsonSchema)]
@@ -189,6 +191,7 @@ impl WindowManager {
hotwatch: Hotwatch::new()?, hotwatch: Hotwatch::new()?,
has_pending_raise_op: false, has_pending_raise_op: false,
pending_move_op: None, 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() { for (j, workspace) in monitor.workspaces().iter().enumerate() {
// And all the visible windows (at the top of a container) // And all the visible windows (at the top of a container)
for window in workspace.visible_windows().into_iter().flatten() { 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 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!( if *apply_on_first_show_only {
"{} should be on monitor {}, workspace {}", if !already_moved_window_handles.contains(&window.hwnd) {
window.title()?, tracing::info!(
*monitor_idx, "{} should be on monitor {}, workspace {}",
*workspace_idx window.title()?,
); *monitor_idx,
*workspace_idx
);
// Create an operation outline and save it for later in the fn already_moved_window_handles
to_move.push(EnforceWorkspaceRuleOp { .insert(window.hwnd);
hwnd: window.hwnd,
origin_monitor_idx: i, // Create an operation outline and save it for later in the fn
origin_workspace_idx: j, to_move.push(EnforceWorkspaceRuleOp {
target_monitor_idx: *monitor_idx, hwnd: window.hwnd,
target_workspace_idx: *workspace_idx, origin_monitor_idx: i,
}); origin_workspace_idx: j,
} else if let Some((monitor_idx, workspace_idx)) = 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()?) workspace_rules.get(&window.title()?)
{ {
tracing::info!( if *apply_on_first_show_only {
"{} should be on monitor {}, workspace {}", if !already_moved_window_handles.contains(&window.hwnd) {
window.title()?, tracing::info!(
*monitor_idx, "{} should be on monitor {}, workspace {}",
*workspace_idx window.title()?,
); *monitor_idx,
*workspace_idx
);
to_move.push(EnforceWorkspaceRuleOp { already_moved_window_handles
hwnd: window.hwnd, .insert(window.hwnd);
origin_monitor_idx: i, to_move.push(EnforceWorkspaceRuleOp {
origin_workspace_idx: j, hwnd: window.hwnd,
target_monitor_idx: *monitor_idx, origin_monitor_idx: i,
target_workspace_idx: *workspace_idx, 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,
});
}
} }
} }
} }

View File

@@ -525,6 +525,9 @@ struct WorkspaceRule {
monitor: usize, monitor: usize,
/// Workspace index on the specified monitor (zero-indexed) /// Workspace index on the specified monitor (zero-indexed)
workspace: usize, workspace: usize,
#[clap(short,long)]
/// Only apply once on first app load
apply_on_first_show_only: bool,
} }
#[derive(Parser, AhkFunction)] #[derive(Parser, AhkFunction)]
@@ -535,6 +538,9 @@ struct NamedWorkspaceRule {
id: String, id: String,
/// Name of a workspace /// Name of a workspace
workspace: String, workspace: String,
#[clap(short,long)]
/// Only apply once on first app load
apply_on_first_show_only: bool,
} }
#[derive(Parser, AhkFunction)] #[derive(Parser, AhkFunction)]
@@ -1449,14 +1455,25 @@ fn main() -> Result<()> {
} }
SubCommand::WorkspaceRule(arg) => { SubCommand::WorkspaceRule(arg) => {
send_message( send_message(
&SocketMessage::WorkspaceRule(arg.identifier, arg.id, arg.monitor, arg.workspace) &SocketMessage::WorkspaceRule(
.as_bytes()?, arg.identifier,
arg.id,
arg.monitor,
arg.workspace,
arg.apply_on_first_show_only,
)
.as_bytes()?,
)?; )?;
} }
SubCommand::NamedWorkspaceRule(arg) => { SubCommand::NamedWorkspaceRule(arg) => {
send_message( send_message(
&SocketMessage::NamedWorkspaceRule(arg.identifier, arg.id, arg.workspace) &SocketMessage::NamedWorkspaceRule(
.as_bytes()?, arg.identifier,
arg.id,
arg.workspace,
arg.apply_on_first_show_only,
)
.as_bytes()?,
)?; )?;
} }
SubCommand::Stack(arg) => { SubCommand::Stack(arg) => {