feat(wm): forcibly manage and unmanage windows

Added commands to forcibly manage and unmanage windows if they don't get
picked up for tiling automatically. This commit adds support for running
those operations on the currently focused window, but if there is a need
to specify a hwnd to operate on, that could be added pretty easily too
in the future, though I'd like to keep the complexity of looking up and
passing hwnds to a command out of the CLI if possible.

This commit also fixes an issue with restoring floating windows. I'm not
sure what happened, but at some point, for me at least,
WindowsApi::top_visible_window started returning explorer.exe all the
time, so I've switched this out for WindowsApi::foreground_window.

I have a feeling I was using TopWindow before, thinking it was
GetForegroundWindow, which it isn't, and it wasn't reliable, so I
created the top_visible_window abstraction on top of it, which also
turned out to be unreliable. Anyway, it's working now.

I think the next step will be to create a manage-rule command to
compliment the float-rule command which users can use to handle edge
cases with their apps in their configuration.

re #16
This commit is contained in:
LGUG2Z
2021-08-19 13:01:09 -07:00
parent 42b9305dfe
commit 8ffe6f78b7
9 changed files with 81 additions and 18 deletions

View File

@@ -34,6 +34,8 @@ pub enum SocketMessage {
ToggleMonocle,
ToggleMaximize,
// Current Workspace Commands
ManageFocusedWindow,
UnmanageFocusedWindow,
AdjustContainerPadding(Sizing, i32),
AdjustWorkspacePadding(Sizing, i32),
ChangeLayout(Layout),

View File

@@ -221,6 +221,12 @@ impl WindowManager {
}
ApplicationIdentifier::Title => {}
},
SocketMessage::ManageFocusedWindow => {
self.manage_focused_window()?;
}
SocketMessage::UnmanageFocusedWindow => {
self.unmanage_focused_window()?;
}
}
tracing::info!("processed");

View File

@@ -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() {

View File

@@ -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

View File

@@ -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(&center, true)?;
window.center(&work_area)?;
window.focus()?;
Ok(())

View File

@@ -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,
}
}

View File

@@ -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<isize> {
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<isize> {
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<isize> {
Result::from(WindowsResult::from(unsafe {
GetWindow(hwnd, GW_HWNDNEXT).0
}))
}
#[allow(dead_code)]
pub fn top_visible_window() -> Result<isize> {
let hwnd = Self::top_window()?;
let mut next_hwnd = hwnd;

View File

@@ -678,7 +678,7 @@ impl Workspace {
}
pub fn remove_focused_floating_window(&mut self) -> Option<Window> {
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() {

View File

@@ -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(())