fix(wm): fix unresponsiveness by using asynchronous window handling

Unresponsive windows might cause Komorebi to hang because functions like
SetWindowPos wait for the target window's WindowProc.

I changed most SetWindowPos calls to use the SWP_ASYNCWINDOWPOS flag,
which should avoid hanging, and I changed the one
WindowsApi::move_window call to use position_window instead. I also
changed the ShowWindow to ShowWindowAsync.

The only issue I noticed was that it caused the stackbar to disappear
(and/or flicker), probably because the window is modified right after
moving it, so I disabled the async position_window for that.
This commit is contained in:
Kuukunen
2025-04-18 15:44:37 +09:00
committed by Jeezy
parent 31752e422a
commit 4ca2e8388b
4 changed files with 33 additions and 14 deletions

View File

@@ -180,7 +180,9 @@ impl Stackbar {
layout.top -= workspace_specific_offset + STACKBAR_TAB_HEIGHT.load_consume();
layout.left -= workspace_specific_offset;
WindowsApi::position_window(self.hwnd, &layout, false)?;
// Async causes the stackbar to disappear or flicker because we modify it right after,
// so we have to do a synchronous call
WindowsApi::position_window(self.hwnd, &layout, false, false)?;
unsafe {
let hdc = GetDC(Option::from(self.hwnd()));

View File

@@ -203,16 +203,15 @@ impl RenderDispatcher for MovementRenderDispatcher {
fn render(&self, progress: f64) -> Result<()> {
let new_rect = self.start_rect.lerp(self.target_rect, progress, self.style);
// using MoveWindow because it runs faster than SetWindowPos
// so animation have more fps and feel smoother
WindowsApi::move_window(self.hwnd, &new_rect, false)?;
// MoveWindow runs faster than SetWindowPos, but it doesn't support async window pos
WindowsApi::position_window(self.hwnd, &new_rect, self.top, true)?;
WindowsApi::invalidate_rect(self.hwnd, None, false);
Ok(())
}
fn post_render(&self) -> Result<()> {
WindowsApi::position_window(self.hwnd, &self.target_rect, self.top)?;
WindowsApi::position_window(self.hwnd, &self.target_rect, self.top, true)?;
if ANIMATION_MANAGER
.lock()
.count_in_progress(MovementRenderDispatcher::PREFIX)
@@ -467,7 +466,7 @@ impl Window {
AnimationEngine::animate(render_dispatcher, duration)
} else {
WindowsApi::position_window(self.hwnd, layout, top)
WindowsApi::position_window(self.hwnd, layout, top, true)
}
}

View File

@@ -1517,7 +1517,7 @@ impl WindowManager {
}
}
WindowsApi::position_window(window.hwnd, &rect, false)?;
WindowsApi::position_window(window.hwnd, &rect, false, true)?;
if mouse_follows_focus {
WindowsApi::center_cursor_in_rect(&rect)?;
}
@@ -2521,7 +2521,7 @@ impl WindowManager {
}
}
WindowsApi::position_window(window.hwnd, &rect, false)?;
WindowsApi::position_window(window.hwnd, &rect, false, true)?;
if mouse_follows_focus {
WindowsApi::center_cursor_in_rect(&rect)?;
}

View File

@@ -105,7 +105,7 @@ use windows::Win32::UI::WindowsAndMessaging::SetForegroundWindow;
use windows::Win32::UI::WindowsAndMessaging::SetLayeredWindowAttributes;
use windows::Win32::UI::WindowsAndMessaging::SetWindowLongPtrW;
use windows::Win32::UI::WindowsAndMessaging::SetWindowPos;
use windows::Win32::UI::WindowsAndMessaging::ShowWindow;
use windows::Win32::UI::WindowsAndMessaging::ShowWindowAsync;
use windows::Win32::UI::WindowsAndMessaging::SystemParametersInfoW;
use windows::Win32::UI::WindowsAndMessaging::WindowFromPoint;
use windows::Win32::UI::WindowsAndMessaging::CW_USEDEFAULT;
@@ -125,6 +125,7 @@ use windows::Win32::UI::WindowsAndMessaging::SPI_GETACTIVEWINDOWTRACKING;
use windows::Win32::UI::WindowsAndMessaging::SPI_GETFOREGROUNDLOCKTIMEOUT;
use windows::Win32::UI::WindowsAndMessaging::SPI_SETACTIVEWINDOWTRACKING;
use windows::Win32::UI::WindowsAndMessaging::SPI_SETFOREGROUNDLOCKTIMEOUT;
use windows::Win32::UI::WindowsAndMessaging::SWP_ASYNCWINDOWPOS;
use windows::Win32::UI::WindowsAndMessaging::SWP_NOMOVE;
use windows::Win32::UI::WindowsAndMessaging::SWP_NOSIZE;
use windows::Win32::UI::WindowsAndMessaging::SWP_SHOWWINDOW;
@@ -473,7 +474,12 @@ impl WindowsApi {
/// position window resizes the target window to the given layout, adjusting
/// the layout to account for any window shadow borders (the window painted
/// region will match layout on completion).
pub fn position_window(hwnd: isize, layout: &Rect, top: bool) -> Result<()> {
pub fn position_window(
hwnd: isize,
layout: &Rect,
top: bool,
async_window_pos: bool,
) -> Result<()> {
let hwnd = HWND(as_ptr!(hwnd));
let mut flags = SetWindowPosition::NO_ACTIVATE
@@ -484,6 +490,14 @@ impl WindowsApi {
// If the request is to place the window on top, then HWND_TOP will take
// effect, otherwise pass NO_Z_ORDER that will cause set_window_pos to
// ignore the z-order paramter.
// By default SetWindowPos waits for target window's WindowProc thread
// to process the message, so we have to use ASYNC_WINDOW_POS to avoid
// blocking our thread in case the target window is not responding.
if async_window_pos {
flags |= SetWindowPosition::ASYNC_WINDOW_POS;
}
if !top {
flags |= SetWindowPosition::NO_Z_ORDER;
}
@@ -521,7 +535,8 @@ impl WindowsApi {
let flags = SetWindowPosition::NO_MOVE
| SetWindowPosition::NO_SIZE
| SetWindowPosition::NO_ACTIVATE
| SetWindowPosition::SHOW_WINDOW;
| SetWindowPosition::SHOW_WINDOW
| SetWindowPosition::ASYNC_WINDOW_POS;
let position = HWND_TOP;
Self::set_window_pos(
@@ -538,7 +553,8 @@ impl WindowsApi {
let flags = SetWindowPosition::NO_MOVE
| SetWindowPosition::NO_SIZE
| SetWindowPosition::NO_ACTIVATE
| SetWindowPosition::SHOW_WINDOW;
| SetWindowPosition::SHOW_WINDOW
| SetWindowPosition::ASYNC_WINDOW_POS;
let position = HWND_BOTTOM;
Self::set_window_pos(
@@ -555,6 +571,7 @@ impl WindowsApi {
| SetWindowPosition::NO_ACTIVATE
| SetWindowPosition::NO_REDRAW
| SetWindowPosition::SHOW_WINDOW
| SetWindowPosition::ASYNC_WINDOW_POS
};
Self::set_window_pos(
@@ -581,6 +598,7 @@ impl WindowsApi {
.process()
}
/// move_windows calls MoveWindow, but cannot be called with async window pos, so it might hang
pub fn move_window(hwnd: isize, layout: &Rect, repaint: bool) -> Result<()> {
let hwnd = HWND(as_ptr!(hwnd));
@@ -599,7 +617,7 @@ impl WindowsApi {
// https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-showwindow
// TODO: error handling
unsafe {
let _ = ShowWindow(HWND(as_ptr!(hwnd)), command);
let _ = ShowWindowAsync(HWND(as_ptr!(hwnd)), command);
};
}
@@ -656,7 +674,7 @@ impl WindowsApi {
0,
0,
0,
SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW,
SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW | SWP_ASYNCWINDOWPOS,
)
.process();
SetForegroundWindow(HWND(as_ptr!(hwnd)))