mirror of
https://github.com/LGUG2Z/komorebi.git
synced 2026-05-07 03:13:31 +02:00
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:
@@ -34,6 +34,8 @@ pub enum SocketMessage {
|
||||
ToggleMonocle,
|
||||
ToggleMaximize,
|
||||
// Current Workspace Commands
|
||||
ManageFocusedWindow,
|
||||
UnmanageFocusedWindow,
|
||||
AdjustContainerPadding(Sizing, i32),
|
||||
AdjustWorkspacePadding(Sizing, i32),
|
||||
ChangeLayout(Layout),
|
||||
|
||||
@@ -221,6 +221,12 @@ impl WindowManager {
|
||||
}
|
||||
ApplicationIdentifier::Title => {}
|
||||
},
|
||||
SocketMessage::ManageFocusedWindow => {
|
||||
self.manage_focused_window()?;
|
||||
}
|
||||
SocketMessage::UnmanageFocusedWindow => {
|
||||
self.unmanage_focused_window()?;
|
||||
}
|
||||
}
|
||||
|
||||
tracing::info!("processed");
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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(())
|
||||
|
||||
@@ -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,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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(())
|
||||
|
||||
Reference in New Issue
Block a user