diff --git a/komorebi-core/src/lib.rs b/komorebi-core/src/lib.rs index 0d566e49..6f785edd 100644 --- a/komorebi-core/src/lib.rs +++ b/komorebi-core/src/lib.rs @@ -34,6 +34,8 @@ pub enum SocketMessage { ToggleMonocle, ToggleMaximize, // Current Workspace Commands + ManageFocusedWindow, + UnmanageFocusedWindow, AdjustContainerPadding(Sizing, i32), AdjustWorkspacePadding(Sizing, i32), ChangeLayout(Layout), diff --git a/komorebi/src/process_command.rs b/komorebi/src/process_command.rs index c6fd01e3..46146e99 100644 --- a/komorebi/src/process_command.rs +++ b/komorebi/src/process_command.rs @@ -221,6 +221,12 @@ impl WindowManager { } ApplicationIdentifier::Title => {} }, + SocketMessage::ManageFocusedWindow => { + self.manage_focused_window()?; + } + SocketMessage::UnmanageFocusedWindow => { + self.unmanage_focused_window()?; + } } tracing::info!("processed"); diff --git a/komorebi/src/process_event.rs b/komorebi/src/process_event.rs index c4316f82..db97c080 100644 --- a/komorebi/src/process_event.rs +++ b/komorebi/src/process_event.rs @@ -101,7 +101,9 @@ impl WindowManager { } match event { - WindowManagerEvent::Minimize(_, window) | WindowManagerEvent::Destroy(_, window) => { + WindowManagerEvent::Minimize(_, window) + | WindowManagerEvent::Destroy(_, window) + | WindowManagerEvent::Unmanage(window) => { self.focused_workspace_mut()?.remove_window(window.hwnd)?; self.update_focused_workspace(false)?; } @@ -152,7 +154,7 @@ impl WindowManager { self.focused_workspace_mut()? .focus_container_by_window(window.hwnd)?; } - WindowManagerEvent::Show(_, window) => { + WindowManagerEvent::Show(_, window) | WindowManagerEvent::Manage(window) => { let mut switch_to = None; for (i, monitors) in self.monitors().iter().enumerate() { for (j, workspace) in monitors.workspaces().iter().enumerate() { @@ -296,6 +298,11 @@ impl WindowManager { WindowManagerEvent::MouseCapture(..) => {} }; + // If we unmanaged a window, it shouldn't be immediately hidden behind managed windows + if let WindowManagerEvent::Unmanage(window) = event { + window.center(&self.focused_monitor_work_area()?)?; + } + tracing::trace!("updating list of known hwnds"); let mut known_hwnds = vec![]; for monitor in self.monitors() { diff --git a/komorebi/src/window.rs b/komorebi/src/window.rs index c5ccd9fc..5c30b940 100644 --- a/komorebi/src/window.rs +++ b/komorebi/src/window.rs @@ -71,6 +71,21 @@ impl Window { HWND(self.hwnd) } + pub fn center(&mut self, work_area: &Rect) -> Result<()> { + let half_width = work_area.right / 2; + let half_weight = work_area.bottom / 2; + + self.set_position( + &Rect { + left: work_area.left + ((work_area.right - half_width) / 2), + top: work_area.top + ((work_area.bottom - half_weight) / 2), + right: half_width, + bottom: half_weight, + }, + true, + ) + } + pub fn set_position(&mut self, layout: &Rect, top: bool) -> Result<()> { // NOTE: This is how the border variable below was calculated; every time this code was // run on any window in any position, the generated border was always the same, so I am diff --git a/komorebi/src/window_manager.rs b/komorebi/src/window_manager.rs index f80e2d8c..8530aafc 100644 --- a/komorebi/src/window_manager.rs +++ b/komorebi/src/window_manager.rs @@ -28,6 +28,7 @@ use crate::ring::Ring; use crate::window::Window; use crate::window_manager_event::WindowManagerEvent; use crate::windows_api::WindowsApi; +use crate::winevent_listener::WINEVENT_CALLBACK_CHANNEL; use crate::workspace::Workspace; use crate::FLOAT_CLASSES; use crate::FLOAT_EXES; @@ -334,6 +335,20 @@ impl WindowManager { Ok(()) } + #[tracing::instrument(skip(self))] + pub fn manage_focused_window(&mut self) -> Result<()> { + let hwnd = WindowsApi::foreground_window()?; + let event = WindowManagerEvent::Manage(Window { hwnd }); + Ok(WINEVENT_CALLBACK_CHANNEL.lock().0.send(event)?) + } + + #[tracing::instrument(skip(self))] + pub fn unmanage_focused_window(&mut self) -> Result<()> { + let hwnd = WindowsApi::foreground_window()?; + let event = WindowManagerEvent::Unmanage(Window { hwnd }); + Ok(WINEVENT_CALLBACK_CHANNEL.lock().0.send(event)?) + } + #[tracing::instrument(skip(self))] pub fn update_focused_workspace(&mut self, mouse_follows_focus: bool) -> Result<()> { tracing::info!("updating"); @@ -616,7 +631,7 @@ impl WindowManager { #[tracing::instrument(skip(self))] pub fn toggle_float(&mut self) -> Result<()> { - let hwnd = WindowsApi::top_visible_window()?; + let hwnd = WindowsApi::foreground_window()?; let workspace = self.focused_workspace_mut()?; let mut is_floating_window = false; @@ -629,11 +644,11 @@ impl WindowManager { if is_floating_window { self.unfloat_window()?; - self.update_focused_workspace(true) } else { self.float_window()?; - self.update_focused_workspace(false) } + + self.update_focused_workspace(is_floating_window) } #[tracing::instrument(skip(self))] @@ -650,17 +665,7 @@ impl WindowManager { .last_mut() .context("there is no floating window")?; - let half_width = work_area.right / 2; - let half_weight = work_area.bottom / 2; - - let center = Rect { - left: work_area.left + ((work_area.right - half_width) / 2), - top: work_area.top + ((work_area.bottom - half_weight) / 2), - right: half_width, - bottom: half_weight, - }; - - window.set_position(¢er, true)?; + window.center(&work_area)?; window.focus()?; Ok(()) diff --git a/komorebi/src/window_manager_event.rs b/komorebi/src/window_manager_event.rs index 14e721de..e97c5d78 100644 --- a/komorebi/src/window_manager_event.rs +++ b/komorebi/src/window_manager_event.rs @@ -14,11 +14,19 @@ pub enum WindowManagerEvent { Show(WinEvent, Window), MoveResizeEnd(WinEvent, Window), MouseCapture(WinEvent, Window), + Manage(Window), + Unmanage(Window), } impl Display for WindowManagerEvent { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { match self { + WindowManagerEvent::Manage(window) => { + write!(f, "Manage (Window: {})", window) + } + WindowManagerEvent::Unmanage(window) => { + write!(f, "Unmanage (Window: {})", window) + } WindowManagerEvent::Destroy(winevent, window) => { write!(f, "Destroy (WinEvent: {}, Window: {})", winevent, window) } @@ -65,7 +73,9 @@ impl WindowManagerEvent { | WindowManagerEvent::Minimize(_, window) | WindowManagerEvent::Show(_, window) | WindowManagerEvent::MoveResizeEnd(_, window) - | WindowManagerEvent::MouseCapture(_, window) => window, + | WindowManagerEvent::MouseCapture(_, window) + | WindowManagerEvent::Manage(window) + | WindowManagerEvent::Unmanage(window) => window, } } diff --git a/komorebi/src/windows_api.rs b/komorebi/src/windows_api.rs index 5db45071..1966518d 100644 --- a/komorebi/src/windows_api.rs +++ b/komorebi/src/windows_api.rs @@ -42,6 +42,7 @@ use bindings::Windows::Win32::UI::WindowsAndMessaging::AllowSetForegroundWindow; use bindings::Windows::Win32::UI::WindowsAndMessaging::EnumWindows; use bindings::Windows::Win32::UI::WindowsAndMessaging::GetCursorPos; use bindings::Windows::Win32::UI::WindowsAndMessaging::GetDesktopWindow; +use bindings::Windows::Win32::UI::WindowsAndMessaging::GetForegroundWindow; use bindings::Windows::Win32::UI::WindowsAndMessaging::GetTopWindow; use bindings::Windows::Win32::UI::WindowsAndMessaging::GetWindow; use bindings::Windows::Win32::UI::WindowsAndMessaging::GetWindowLongPtrW; @@ -264,6 +265,10 @@ impl WindowsApi { Self::show_window(hwnd, SW_MAXIMIZE); } + pub fn foreground_window() -> Result { + Result::from(WindowsResult::from(unsafe { GetForegroundWindow() })) + } + pub fn set_foreground_window(hwnd: HWND) -> Result<()> { match WindowsResult::from(unsafe { SetForegroundWindow(hwnd) }) { WindowsResult::Ok(_) => Ok(()), @@ -280,6 +285,7 @@ impl WindowsApi { } } + #[allow(dead_code)] pub fn top_window() -> Result { Result::from(WindowsResult::from(unsafe { GetTopWindow(HWND::NULL).0 })) } @@ -288,12 +294,14 @@ impl WindowsApi { Result::from(WindowsResult::from(unsafe { GetDesktopWindow() })) } + #[allow(dead_code)] pub fn next_window(hwnd: HWND) -> Result { Result::from(WindowsResult::from(unsafe { GetWindow(hwnd, GW_HWNDNEXT).0 })) } + #[allow(dead_code)] pub fn top_visible_window() -> Result { let hwnd = Self::top_window()?; let mut next_hwnd = hwnd; diff --git a/komorebi/src/workspace.rs b/komorebi/src/workspace.rs index 9bc6bd32..a399ee3f 100644 --- a/komorebi/src/workspace.rs +++ b/komorebi/src/workspace.rs @@ -678,7 +678,7 @@ impl Workspace { } pub fn remove_focused_floating_window(&mut self) -> Option { - let hwnd = WindowsApi::top_visible_window().ok()?; + let hwnd = WindowsApi::foreground_window().ok()?; let mut idx = None; for (i, window) in self.floating_windows.iter().enumerate() { diff --git a/komorebic/src/main.rs b/komorebic/src/main.rs index 47fb98cd..2701688f 100644 --- a/komorebic/src/main.rs +++ b/komorebic/src/main.rs @@ -269,6 +269,10 @@ enum SubCommand { ToggleMaximize, /// Restore all hidden windows (debugging command) RestoreWindows, + /// Force komorebi to manage the focused window + Manage, + /// Unmanage a window that was forcibly managed + Unmanage, /// Reload ~/komorebi.ahk (if it exists) ReloadConfiguration, /// Toggle the automatic reloading of ~/komorebi.ahk (if it exists) @@ -543,6 +547,12 @@ fn main() -> Result<()> { .as_bytes()?, )?; } + SubCommand::Manage => { + send_message(&*SocketMessage::ManageFocusedWindow.as_bytes()?)?; + } + SubCommand::Unmanage => { + send_message(&*SocketMessage::UnmanageFocusedWindow.as_bytes()?)?; + } } Ok(())