From 711ab8d59b8bf46bef28de95665b991513a897f1 Mon Sep 17 00:00:00 2001 From: LGUG2Z Date: Tue, 19 Apr 2022 17:13:24 -0700 Subject: [PATCH] feat(wm): add cmd for unmanaged hwnd op behaviour This commit adds a new command, 'unmanaged-window-operation-behaviour' which allows the user to configure their desired behaviour in situations when sending window container commands which operate on the focused window container in the workspace state, but having an unmanaged window as the foreground hwnd. The default previously was previously Op (and this remains the default with these new changes), but the user can now select NoOp, which will return an error when the focused hwnd is unmanaged and not allow any write operations to take place on the focused workspace state. resolve #133 --- README.md | 3 +++ komorebi-core/src/lib.rs | 8 ++++++ komorebi/src/process_command.rs | 3 +++ komorebi/src/window_manager.rs | 43 +++++++++++++++++++++++++++++++++ komorebi/src/workspace.rs | 22 +++++++++++++++++ komorebic.lib.sample.ahk | 4 +++ komorebic/src/main.rs | 11 +++++++++ 7 files changed, 94 insertions(+) diff --git a/README.md b/README.md index f3660c9d..26913ae7 100644 --- a/README.md +++ b/README.md @@ -475,6 +475,7 @@ unmanage Unmanage a window that was forcibly m reload-configuration Reload ~/komorebi.ahk (if it exists) watch-configuration Enable or disable watching of ~/komorebi.ahk (if it exists) window-hiding-behaviour Set the window behaviour when switching workspaces / cycling stacks +unmanaged-window-operation-behaviour Set the operation behaviour when the focused window is not managed float-rule Add a rule to always float the specified application manage-rule Add a rule to always manage the specified application workspace-rule Add a rule to associate an application with a workspace @@ -487,6 +488,8 @@ toggle-focus-follows-mouse Toggle focus follows mouse for the op mouse-follows-focus Enable or disable mouse follows focus on all workspaces toggle-mouse-follows-focus Toggle mouse follows focus on all workspaces ahk-library Generate a library of AutoHotKey helper functions +ahk-app-specific-configuration Generate common app-specific configurations and fixes to use in komorebi.ahk +format-app-specific-configuration Format a YAML file for use with the 'ahk-app-specific-configuration' command notification-schema Generate a JSON Schema of subscription notifications help Print this message or the help of the given subcommand(s) ``` diff --git a/komorebi-core/src/lib.rs b/komorebi-core/src/lib.rs index 2ed4cbf9..7f7a41cc 100644 --- a/komorebi-core/src/lib.rs +++ b/komorebi-core/src/lib.rs @@ -57,6 +57,7 @@ pub enum SocketMessage { ToggleMaximize, ToggleWindowContainerBehaviour, WindowHidingBehaviour(HidingBehaviour), + UnmanagedWindowOperationBehaviour(OperationBehaviour), // Current Workspace Commands ManageFocusedWindow, UnmanageFocusedWindow, @@ -167,6 +168,13 @@ pub enum HidingBehaviour { Minimize, } +#[derive(Clone, Copy, Debug, Serialize, Deserialize, Display, EnumString, ArgEnum, JsonSchema)] +#[strum(serialize_all = "snake_case")] +pub enum OperationBehaviour { + Op, + NoOp, +} + #[derive(Clone, Copy, Debug, Serialize, Deserialize, Display, EnumString, ArgEnum, JsonSchema)] #[strum(serialize_all = "snake_case")] pub enum Sizing { diff --git a/komorebi/src/process_command.rs b/komorebi/src/process_command.rs index a8ceffd0..064d571b 100644 --- a/komorebi/src/process_command.rs +++ b/komorebi/src/process_command.rs @@ -681,6 +681,9 @@ impl WindowManager { let mut hiding_behaviour = HIDING_BEHAVIOUR.lock(); *hiding_behaviour = behaviour; } + SocketMessage::UnmanagedWindowOperationBehaviour(behaviour) => { + self.unmanaged_window_operation_behaviour = behaviour; + } SocketMessage::NotificationSchema => { let notification = schema_for!(Notification); let schema = serde_json::to_string_pretty(¬ification)?; diff --git a/komorebi/src/window_manager.rs b/komorebi/src/window_manager.rs index f849c571..3e0fd19b 100644 --- a/komorebi/src/window_manager.rs +++ b/komorebi/src/window_manager.rs @@ -22,6 +22,7 @@ use komorebi_core::CycleDirection; use komorebi_core::DefaultLayout; use komorebi_core::FocusFollowsMouseImplementation; use komorebi_core::Layout; +use komorebi_core::OperationBehaviour; use komorebi_core::OperationDirection; use komorebi_core::Rect; use komorebi_core::Sizing; @@ -56,6 +57,7 @@ pub struct WindowManager { pub work_area_offset: Option, pub resize_delta: i32, pub window_container_behaviour: WindowContainerBehaviour, + pub unmanaged_window_operation_behaviour: OperationBehaviour, pub focus_follows_mouse: Option, pub mouse_follows_focus: bool, pub hotwatch: Hotwatch, @@ -172,6 +174,7 @@ impl WindowManager { virtual_desktop_id: current_virtual_desktop(), work_area_offset: None, window_container_behaviour: WindowContainerBehaviour::Create, + unmanaged_window_operation_behaviour: OperationBehaviour::Op, resize_delta: 50, focus_follows_mouse: None, mouse_follows_focus: true, @@ -785,6 +788,21 @@ impl WindowManager { } } + #[tracing::instrument(skip(self))] + fn handle_unmanaged_window_behaviour(&self) -> Result<()> { + if let OperationBehaviour::NoOp = self.unmanaged_window_operation_behaviour { + let workspace = self.focused_workspace()?; + let focused_hwnd = WindowsApi::foreground_window()?; + if !workspace.contains_managed_window(focused_hwnd) { + return Err(anyhow!( + "ignoring commands while active window is not managed by komorebi" + )); + } + } + + Ok(()) + } + #[tracing::instrument(skip(self))] pub fn move_container_to_monitor( &mut self, @@ -792,6 +810,8 @@ impl WindowManager { workspace_idx: Option, follow: bool, ) -> Result<()> { + self.handle_unmanaged_window_behaviour()?; + tracing::info!("moving container"); let invisible_borders = self.invisible_borders; @@ -835,6 +855,8 @@ impl WindowManager { #[tracing::instrument(skip(self))] pub fn move_container_to_workspace(&mut self, idx: usize, follow: bool) -> Result<()> { + self.handle_unmanaged_window_behaviour()?; + tracing::info!("moving container"); let mouse_follows_focus = self.mouse_follows_focus; @@ -879,7 +901,10 @@ impl WindowManager { #[tracing::instrument(skip(self))] pub fn focus_container_in_direction(&mut self, direction: OperationDirection) -> Result<()> { + self.handle_unmanaged_window_behaviour()?; + tracing::info!("focusing container"); + let workspace = self.focused_workspace_mut()?; let new_idx = workspace @@ -894,6 +919,8 @@ impl WindowManager { #[tracing::instrument(skip(self))] pub fn move_container_in_direction(&mut self, direction: OperationDirection) -> Result<()> { + self.handle_unmanaged_window_behaviour()?; + tracing::info!("moving container"); let workspace = self.focused_workspace_mut()?; @@ -910,6 +937,8 @@ impl WindowManager { #[tracing::instrument(skip(self))] pub fn focus_container_in_cycle_direction(&mut self, direction: CycleDirection) -> Result<()> { + self.handle_unmanaged_window_behaviour()?; + tracing::info!("focusing container"); let mut maximize_next = false; let mut monocle_next = false; @@ -945,6 +974,8 @@ impl WindowManager { #[tracing::instrument(skip(self))] pub fn move_container_in_cycle_direction(&mut self, direction: CycleDirection) -> Result<()> { + self.handle_unmanaged_window_behaviour()?; + tracing::info!("moving container"); let workspace = self.focused_workspace_mut()?; @@ -961,6 +992,8 @@ impl WindowManager { #[tracing::instrument(skip(self))] pub fn cycle_container_window_in_direction(&mut self, direction: CycleDirection) -> Result<()> { + self.handle_unmanaged_window_behaviour()?; + tracing::info!("cycling container windows"); let container = self.focused_container_mut()?; @@ -983,6 +1016,8 @@ impl WindowManager { #[tracing::instrument(skip(self))] pub fn add_window_to_container(&mut self, direction: OperationDirection) -> Result<()> { + self.handle_unmanaged_window_behaviour()?; + tracing::info!("adding window to container"); let workspace = self.focused_workspace_mut()?; @@ -1019,6 +1054,8 @@ impl WindowManager { #[tracing::instrument(skip(self))] pub fn promote_container_to_front(&mut self) -> Result<()> { + self.handle_unmanaged_window_behaviour()?; + tracing::info!("promoting container"); let workspace = self.focused_workspace_mut()?; @@ -1028,6 +1065,8 @@ impl WindowManager { #[tracing::instrument(skip(self))] pub fn remove_window_from_container(&mut self) -> Result<()> { + self.handle_unmanaged_window_behaviour()?; + tracing::info!("removing window"); if self.focused_container()?.windows().len() == 1 { @@ -1100,6 +1139,8 @@ impl WindowManager { #[tracing::instrument(skip(self))] pub fn toggle_monocle(&mut self) -> Result<()> { + self.handle_unmanaged_window_behaviour()?; + let workspace = self.focused_workspace_mut()?; match workspace.monocle_container() { @@ -1128,6 +1169,8 @@ impl WindowManager { #[tracing::instrument(skip(self))] pub fn toggle_maximize(&mut self) -> Result<()> { + self.handle_unmanaged_window_behaviour()?; + let workspace = self.focused_workspace_mut()?; match workspace.maximized_window() { diff --git a/komorebi/src/workspace.rs b/komorebi/src/workspace.rs index 89870baa..34899877 100644 --- a/komorebi/src/workspace.rs +++ b/komorebi/src/workspace.rs @@ -334,6 +334,28 @@ impl Workspace { None } + pub fn contains_managed_window(&self, hwnd: isize) -> bool { + for container in self.containers() { + if container.contains_window(hwnd) { + return true; + } + } + + if let Some(window) = self.maximized_window() { + if hwnd == window.hwnd { + return true; + } + } + + if let Some(container) = self.monocle_container() { + if container.contains_window(hwnd) { + return true; + } + } + + false + } + pub fn contains_window(&self, hwnd: isize) -> bool { for container in self.containers() { if container.contains_window(hwnd) { diff --git a/komorebic.lib.sample.ahk b/komorebic.lib.sample.ahk index d62d43f0..8424799a 100644 --- a/komorebic.lib.sample.ahk +++ b/komorebic.lib.sample.ahk @@ -256,6 +256,10 @@ WindowHidingBehaviour(hiding_behaviour) { Run, komorebic.exe window-hiding-behaviour %hiding_behaviour%, , Hide } +UnmanagedWindowOperationBehaviour(operation_behaviour) { + Run, komorebic.exe unmanaged-window-operation-behaviour %operation_behaviour%, , Hide +} + FloatRule(identifier, id) { Run, komorebic.exe float-rule %identifier% "%id%", , Hide } diff --git a/komorebic/src/main.rs b/komorebic/src/main.rs index a0ec0ef8..556a3195 100644 --- a/komorebic/src/main.rs +++ b/komorebic/src/main.rs @@ -36,6 +36,7 @@ use komorebi_core::CycleDirection; use komorebi_core::DefaultLayout; use komorebi_core::FocusFollowsMouseImplementation; use komorebi_core::HidingBehaviour; +use komorebi_core::OperationBehaviour; use komorebi_core::OperationDirection; use komorebi_core::Rect; use komorebi_core::Sizing; @@ -114,6 +115,7 @@ gen_enum_subcommand_args! { MouseFollowsFocus: BooleanState, Query: StateQuery, WindowHidingBehaviour: HidingBehaviour, + UnmanagedWindowOperationBehaviour: OperationBehaviour, } macro_rules! gen_target_subcommand_args { @@ -622,6 +624,9 @@ enum SubCommand { /// Set the window behaviour when switching workspaces / cycling stacks #[clap(arg_required_else_help = true)] WindowHidingBehaviour(WindowHidingBehaviour), + /// Set the operation behaviour when the focused window is not managed + #[clap(arg_required_else_help = true)] + UnmanagedWindowOperationBehaviour(UnmanagedWindowOperationBehaviour), /// Add a rule to always float the specified application #[clap(arg_required_else_help = true)] FloatRule(FloatRule), @@ -1162,6 +1167,12 @@ fn main() -> Result<()> { SubCommand::WindowHidingBehaviour(arg) => { send_message(&*SocketMessage::WindowHidingBehaviour(arg.hiding_behaviour).as_bytes()?)?; } + SubCommand::UnmanagedWindowOperationBehaviour(arg) => { + send_message( + &*SocketMessage::UnmanagedWindowOperationBehaviour(arg.operation_behaviour) + .as_bytes()?, + )?; + } SubCommand::AhkAppSpecificConfiguration(arg) => { let content = fs::read_to_string(resolve_windows_path(&arg.path)?)?; let lines = if let Some(override_path) = arg.override_path {