mirror of
https://github.com/LGUG2Z/komorebi.git
synced 2026-03-24 18:31:22 +01:00
feat(workspace-rule): add ability to only apply workspace rule on first
show of app
This commit is contained in:
@@ -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),
|
||||
|
||||
@@ -94,7 +94,7 @@ lazy_static! {
|
||||
]));
|
||||
static ref MONITOR_INDEX_PREFERENCES: Arc<Mutex<HashMap<usize, Rect>>> =
|
||||
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()));
|
||||
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![
|
||||
|
||||
@@ -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()?;
|
||||
|
||||
@@ -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()?;
|
||||
|
||||
@@ -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<Vec<u8>>,
|
||||
pub has_pending_raise_op: bool,
|
||||
pub pending_move_op: Option<(usize, usize, usize)>,
|
||||
pub already_moved_window_handles: Arc<Mutex<HashSet<isize>>>,
|
||||
}
|
||||
|
||||
#[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,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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) => {
|
||||
|
||||
Reference in New Issue
Block a user