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
This commit is contained in:
LGUG2Z
2022-04-19 17:13:24 -07:00
parent e1c36c9190
commit 711ab8d59b
7 changed files with 94 additions and 0 deletions

View File

@@ -475,6 +475,7 @@ unmanage Unmanage a window that was forcibly m
reload-configuration Reload ~/komorebi.ahk (if it exists) reload-configuration Reload ~/komorebi.ahk (if it exists)
watch-configuration Enable or disable watching of ~/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 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 float-rule Add a rule to always float the specified application
manage-rule Add a rule to always manage 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 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 mouse-follows-focus Enable or disable mouse follows focus on all workspaces
toggle-mouse-follows-focus Toggle 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-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 notification-schema Generate a JSON Schema of subscription notifications
help Print this message or the help of the given subcommand(s) help Print this message or the help of the given subcommand(s)
``` ```

View File

@@ -57,6 +57,7 @@ pub enum SocketMessage {
ToggleMaximize, ToggleMaximize,
ToggleWindowContainerBehaviour, ToggleWindowContainerBehaviour,
WindowHidingBehaviour(HidingBehaviour), WindowHidingBehaviour(HidingBehaviour),
UnmanagedWindowOperationBehaviour(OperationBehaviour),
// Current Workspace Commands // Current Workspace Commands
ManageFocusedWindow, ManageFocusedWindow,
UnmanageFocusedWindow, UnmanageFocusedWindow,
@@ -167,6 +168,13 @@ pub enum HidingBehaviour {
Minimize, 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)] #[derive(Clone, Copy, Debug, Serialize, Deserialize, Display, EnumString, ArgEnum, JsonSchema)]
#[strum(serialize_all = "snake_case")] #[strum(serialize_all = "snake_case")]
pub enum Sizing { pub enum Sizing {

View File

@@ -681,6 +681,9 @@ impl WindowManager {
let mut hiding_behaviour = HIDING_BEHAVIOUR.lock(); let mut hiding_behaviour = HIDING_BEHAVIOUR.lock();
*hiding_behaviour = behaviour; *hiding_behaviour = behaviour;
} }
SocketMessage::UnmanagedWindowOperationBehaviour(behaviour) => {
self.unmanaged_window_operation_behaviour = behaviour;
}
SocketMessage::NotificationSchema => { SocketMessage::NotificationSchema => {
let notification = schema_for!(Notification); let notification = schema_for!(Notification);
let schema = serde_json::to_string_pretty(&notification)?; let schema = serde_json::to_string_pretty(&notification)?;

View File

@@ -22,6 +22,7 @@ use komorebi_core::CycleDirection;
use komorebi_core::DefaultLayout; use komorebi_core::DefaultLayout;
use komorebi_core::FocusFollowsMouseImplementation; use komorebi_core::FocusFollowsMouseImplementation;
use komorebi_core::Layout; use komorebi_core::Layout;
use komorebi_core::OperationBehaviour;
use komorebi_core::OperationDirection; use komorebi_core::OperationDirection;
use komorebi_core::Rect; use komorebi_core::Rect;
use komorebi_core::Sizing; use komorebi_core::Sizing;
@@ -56,6 +57,7 @@ pub struct WindowManager {
pub work_area_offset: Option<Rect>, pub work_area_offset: Option<Rect>,
pub resize_delta: i32, pub resize_delta: i32,
pub window_container_behaviour: WindowContainerBehaviour, pub window_container_behaviour: WindowContainerBehaviour,
pub unmanaged_window_operation_behaviour: OperationBehaviour,
pub focus_follows_mouse: Option<FocusFollowsMouseImplementation>, pub focus_follows_mouse: Option<FocusFollowsMouseImplementation>,
pub mouse_follows_focus: bool, pub mouse_follows_focus: bool,
pub hotwatch: Hotwatch, pub hotwatch: Hotwatch,
@@ -172,6 +174,7 @@ impl WindowManager {
virtual_desktop_id: current_virtual_desktop(), virtual_desktop_id: current_virtual_desktop(),
work_area_offset: None, work_area_offset: None,
window_container_behaviour: WindowContainerBehaviour::Create, window_container_behaviour: WindowContainerBehaviour::Create,
unmanaged_window_operation_behaviour: OperationBehaviour::Op,
resize_delta: 50, resize_delta: 50,
focus_follows_mouse: None, focus_follows_mouse: None,
mouse_follows_focus: true, 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))] #[tracing::instrument(skip(self))]
pub fn move_container_to_monitor( pub fn move_container_to_monitor(
&mut self, &mut self,
@@ -792,6 +810,8 @@ impl WindowManager {
workspace_idx: Option<usize>, workspace_idx: Option<usize>,
follow: bool, follow: bool,
) -> Result<()> { ) -> Result<()> {
self.handle_unmanaged_window_behaviour()?;
tracing::info!("moving container"); tracing::info!("moving container");
let invisible_borders = self.invisible_borders; let invisible_borders = self.invisible_borders;
@@ -835,6 +855,8 @@ impl WindowManager {
#[tracing::instrument(skip(self))] #[tracing::instrument(skip(self))]
pub fn move_container_to_workspace(&mut self, idx: usize, follow: bool) -> Result<()> { pub fn move_container_to_workspace(&mut self, idx: usize, follow: bool) -> Result<()> {
self.handle_unmanaged_window_behaviour()?;
tracing::info!("moving container"); tracing::info!("moving container");
let mouse_follows_focus = self.mouse_follows_focus; let mouse_follows_focus = self.mouse_follows_focus;
@@ -879,7 +901,10 @@ impl WindowManager {
#[tracing::instrument(skip(self))] #[tracing::instrument(skip(self))]
pub fn focus_container_in_direction(&mut self, direction: OperationDirection) -> Result<()> { pub fn focus_container_in_direction(&mut self, direction: OperationDirection) -> Result<()> {
self.handle_unmanaged_window_behaviour()?;
tracing::info!("focusing container"); tracing::info!("focusing container");
let workspace = self.focused_workspace_mut()?; let workspace = self.focused_workspace_mut()?;
let new_idx = workspace let new_idx = workspace
@@ -894,6 +919,8 @@ impl WindowManager {
#[tracing::instrument(skip(self))] #[tracing::instrument(skip(self))]
pub fn move_container_in_direction(&mut self, direction: OperationDirection) -> Result<()> { pub fn move_container_in_direction(&mut self, direction: OperationDirection) -> Result<()> {
self.handle_unmanaged_window_behaviour()?;
tracing::info!("moving container"); tracing::info!("moving container");
let workspace = self.focused_workspace_mut()?; let workspace = self.focused_workspace_mut()?;
@@ -910,6 +937,8 @@ impl WindowManager {
#[tracing::instrument(skip(self))] #[tracing::instrument(skip(self))]
pub fn focus_container_in_cycle_direction(&mut self, direction: CycleDirection) -> Result<()> { pub fn focus_container_in_cycle_direction(&mut self, direction: CycleDirection) -> Result<()> {
self.handle_unmanaged_window_behaviour()?;
tracing::info!("focusing container"); tracing::info!("focusing container");
let mut maximize_next = false; let mut maximize_next = false;
let mut monocle_next = false; let mut monocle_next = false;
@@ -945,6 +974,8 @@ impl WindowManager {
#[tracing::instrument(skip(self))] #[tracing::instrument(skip(self))]
pub fn move_container_in_cycle_direction(&mut self, direction: CycleDirection) -> Result<()> { pub fn move_container_in_cycle_direction(&mut self, direction: CycleDirection) -> Result<()> {
self.handle_unmanaged_window_behaviour()?;
tracing::info!("moving container"); tracing::info!("moving container");
let workspace = self.focused_workspace_mut()?; let workspace = self.focused_workspace_mut()?;
@@ -961,6 +992,8 @@ impl WindowManager {
#[tracing::instrument(skip(self))] #[tracing::instrument(skip(self))]
pub fn cycle_container_window_in_direction(&mut self, direction: CycleDirection) -> Result<()> { pub fn cycle_container_window_in_direction(&mut self, direction: CycleDirection) -> Result<()> {
self.handle_unmanaged_window_behaviour()?;
tracing::info!("cycling container windows"); tracing::info!("cycling container windows");
let container = self.focused_container_mut()?; let container = self.focused_container_mut()?;
@@ -983,6 +1016,8 @@ impl WindowManager {
#[tracing::instrument(skip(self))] #[tracing::instrument(skip(self))]
pub fn add_window_to_container(&mut self, direction: OperationDirection) -> Result<()> { pub fn add_window_to_container(&mut self, direction: OperationDirection) -> Result<()> {
self.handle_unmanaged_window_behaviour()?;
tracing::info!("adding window to container"); tracing::info!("adding window to container");
let workspace = self.focused_workspace_mut()?; let workspace = self.focused_workspace_mut()?;
@@ -1019,6 +1054,8 @@ impl WindowManager {
#[tracing::instrument(skip(self))] #[tracing::instrument(skip(self))]
pub fn promote_container_to_front(&mut self) -> Result<()> { pub fn promote_container_to_front(&mut self) -> Result<()> {
self.handle_unmanaged_window_behaviour()?;
tracing::info!("promoting container"); tracing::info!("promoting container");
let workspace = self.focused_workspace_mut()?; let workspace = self.focused_workspace_mut()?;
@@ -1028,6 +1065,8 @@ impl WindowManager {
#[tracing::instrument(skip(self))] #[tracing::instrument(skip(self))]
pub fn remove_window_from_container(&mut self) -> Result<()> { pub fn remove_window_from_container(&mut self) -> Result<()> {
self.handle_unmanaged_window_behaviour()?;
tracing::info!("removing window"); tracing::info!("removing window");
if self.focused_container()?.windows().len() == 1 { if self.focused_container()?.windows().len() == 1 {
@@ -1100,6 +1139,8 @@ impl WindowManager {
#[tracing::instrument(skip(self))] #[tracing::instrument(skip(self))]
pub fn toggle_monocle(&mut self) -> Result<()> { pub fn toggle_monocle(&mut self) -> Result<()> {
self.handle_unmanaged_window_behaviour()?;
let workspace = self.focused_workspace_mut()?; let workspace = self.focused_workspace_mut()?;
match workspace.monocle_container() { match workspace.monocle_container() {
@@ -1128,6 +1169,8 @@ impl WindowManager {
#[tracing::instrument(skip(self))] #[tracing::instrument(skip(self))]
pub fn toggle_maximize(&mut self) -> Result<()> { pub fn toggle_maximize(&mut self) -> Result<()> {
self.handle_unmanaged_window_behaviour()?;
let workspace = self.focused_workspace_mut()?; let workspace = self.focused_workspace_mut()?;
match workspace.maximized_window() { match workspace.maximized_window() {

View File

@@ -334,6 +334,28 @@ impl Workspace {
None 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 { pub fn contains_window(&self, hwnd: isize) -> bool {
for container in self.containers() { for container in self.containers() {
if container.contains_window(hwnd) { if container.contains_window(hwnd) {

View File

@@ -256,6 +256,10 @@ WindowHidingBehaviour(hiding_behaviour) {
Run, komorebic.exe window-hiding-behaviour %hiding_behaviour%, , Hide 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) { FloatRule(identifier, id) {
Run, komorebic.exe float-rule %identifier% "%id%", , Hide Run, komorebic.exe float-rule %identifier% "%id%", , Hide
} }

View File

@@ -36,6 +36,7 @@ use komorebi_core::CycleDirection;
use komorebi_core::DefaultLayout; use komorebi_core::DefaultLayout;
use komorebi_core::FocusFollowsMouseImplementation; use komorebi_core::FocusFollowsMouseImplementation;
use komorebi_core::HidingBehaviour; use komorebi_core::HidingBehaviour;
use komorebi_core::OperationBehaviour;
use komorebi_core::OperationDirection; use komorebi_core::OperationDirection;
use komorebi_core::Rect; use komorebi_core::Rect;
use komorebi_core::Sizing; use komorebi_core::Sizing;
@@ -114,6 +115,7 @@ gen_enum_subcommand_args! {
MouseFollowsFocus: BooleanState, MouseFollowsFocus: BooleanState,
Query: StateQuery, Query: StateQuery,
WindowHidingBehaviour: HidingBehaviour, WindowHidingBehaviour: HidingBehaviour,
UnmanagedWindowOperationBehaviour: OperationBehaviour,
} }
macro_rules! gen_target_subcommand_args { macro_rules! gen_target_subcommand_args {
@@ -622,6 +624,9 @@ enum SubCommand {
/// Set the window behaviour when switching workspaces / cycling stacks /// Set the window behaviour when switching workspaces / cycling stacks
#[clap(arg_required_else_help = true)] #[clap(arg_required_else_help = true)]
WindowHidingBehaviour(WindowHidingBehaviour), 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 /// Add a rule to always float the specified application
#[clap(arg_required_else_help = true)] #[clap(arg_required_else_help = true)]
FloatRule(FloatRule), FloatRule(FloatRule),
@@ -1162,6 +1167,12 @@ fn main() -> Result<()> {
SubCommand::WindowHidingBehaviour(arg) => { SubCommand::WindowHidingBehaviour(arg) => {
send_message(&*SocketMessage::WindowHidingBehaviour(arg.hiding_behaviour).as_bytes()?)?; send_message(&*SocketMessage::WindowHidingBehaviour(arg.hiding_behaviour).as_bytes()?)?;
} }
SubCommand::UnmanagedWindowOperationBehaviour(arg) => {
send_message(
&*SocketMessage::UnmanagedWindowOperationBehaviour(arg.operation_behaviour)
.as_bytes()?,
)?;
}
SubCommand::AhkAppSpecificConfiguration(arg) => { SubCommand::AhkAppSpecificConfiguration(arg) => {
let content = fs::read_to_string(resolve_windows_path(&arg.path)?)?; let content = fs::read_to_string(resolve_windows_path(&arg.path)?)?;
let lines = if let Some(override_path) = arg.override_path { let lines = if let Some(override_path) = arg.override_path {