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
+3 -1
View File
@@ -180,7 +180,9 @@ impl Stackbar {
layout.top -= workspace_specific_offset + STACKBAR_TAB_HEIGHT.load_consume(); layout.top -= workspace_specific_offset + STACKBAR_TAB_HEIGHT.load_consume();
layout.left -= workspace_specific_offset; 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 { unsafe {
let hdc = GetDC(Option::from(self.hwnd())); let hdc = GetDC(Option::from(self.hwnd()));
+4 -5
View File
@@ -203,16 +203,15 @@ impl RenderDispatcher for MovementRenderDispatcher {
fn render(&self, progress: f64) -> Result<()> { fn render(&self, progress: f64) -> Result<()> {
let new_rect = self.start_rect.lerp(self.target_rect, progress, self.style); let new_rect = self.start_rect.lerp(self.target_rect, progress, self.style);
// using MoveWindow because it runs faster than SetWindowPos // MoveWindow runs faster than SetWindowPos, but it doesn't support async window pos
// so animation have more fps and feel smoother WindowsApi::position_window(self.hwnd, &new_rect, self.top, true)?;
WindowsApi::move_window(self.hwnd, &new_rect, false)?;
WindowsApi::invalidate_rect(self.hwnd, None, false); WindowsApi::invalidate_rect(self.hwnd, None, false);
Ok(()) Ok(())
} }
fn post_render(&self) -> Result<()> { 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 if ANIMATION_MANAGER
.lock() .lock()
.count_in_progress(MovementRenderDispatcher::PREFIX) .count_in_progress(MovementRenderDispatcher::PREFIX)
@@ -467,7 +466,7 @@ impl Window {
AnimationEngine::animate(render_dispatcher, duration) AnimationEngine::animate(render_dispatcher, duration)
} else { } else {
WindowsApi::position_window(self.hwnd, layout, top) WindowsApi::position_window(self.hwnd, layout, top, true)
} }
} }
+2 -2
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 { if mouse_follows_focus {
WindowsApi::center_cursor_in_rect(&rect)?; 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 { if mouse_follows_focus {
WindowsApi::center_cursor_in_rect(&rect)?; WindowsApi::center_cursor_in_rect(&rect)?;
} }
+24 -6
View File
@@ -105,7 +105,7 @@ use windows::Win32::UI::WindowsAndMessaging::SetForegroundWindow;
use windows::Win32::UI::WindowsAndMessaging::SetLayeredWindowAttributes; use windows::Win32::UI::WindowsAndMessaging::SetLayeredWindowAttributes;
use windows::Win32::UI::WindowsAndMessaging::SetWindowLongPtrW; use windows::Win32::UI::WindowsAndMessaging::SetWindowLongPtrW;
use windows::Win32::UI::WindowsAndMessaging::SetWindowPos; 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::SystemParametersInfoW;
use windows::Win32::UI::WindowsAndMessaging::WindowFromPoint; use windows::Win32::UI::WindowsAndMessaging::WindowFromPoint;
use windows::Win32::UI::WindowsAndMessaging::CW_USEDEFAULT; 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_GETFOREGROUNDLOCKTIMEOUT;
use windows::Win32::UI::WindowsAndMessaging::SPI_SETACTIVEWINDOWTRACKING; use windows::Win32::UI::WindowsAndMessaging::SPI_SETACTIVEWINDOWTRACKING;
use windows::Win32::UI::WindowsAndMessaging::SPI_SETFOREGROUNDLOCKTIMEOUT; 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_NOMOVE;
use windows::Win32::UI::WindowsAndMessaging::SWP_NOSIZE; use windows::Win32::UI::WindowsAndMessaging::SWP_NOSIZE;
use windows::Win32::UI::WindowsAndMessaging::SWP_SHOWWINDOW; use windows::Win32::UI::WindowsAndMessaging::SWP_SHOWWINDOW;
@@ -473,7 +474,12 @@ impl WindowsApi {
/// position window resizes the target window to the given layout, adjusting /// position window resizes the target window to the given layout, adjusting
/// the layout to account for any window shadow borders (the window painted /// the layout to account for any window shadow borders (the window painted
/// region will match layout on completion). /// 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 hwnd = HWND(as_ptr!(hwnd));
let mut flags = SetWindowPosition::NO_ACTIVATE 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 // 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 // effect, otherwise pass NO_Z_ORDER that will cause set_window_pos to
// ignore the z-order paramter. // 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 { if !top {
flags |= SetWindowPosition::NO_Z_ORDER; flags |= SetWindowPosition::NO_Z_ORDER;
} }
@@ -521,7 +535,8 @@ impl WindowsApi {
let flags = SetWindowPosition::NO_MOVE let flags = SetWindowPosition::NO_MOVE
| SetWindowPosition::NO_SIZE | SetWindowPosition::NO_SIZE
| SetWindowPosition::NO_ACTIVATE | SetWindowPosition::NO_ACTIVATE
| SetWindowPosition::SHOW_WINDOW; | SetWindowPosition::SHOW_WINDOW
| SetWindowPosition::ASYNC_WINDOW_POS;
let position = HWND_TOP; let position = HWND_TOP;
Self::set_window_pos( Self::set_window_pos(
@@ -538,7 +553,8 @@ impl WindowsApi {
let flags = SetWindowPosition::NO_MOVE let flags = SetWindowPosition::NO_MOVE
| SetWindowPosition::NO_SIZE | SetWindowPosition::NO_SIZE
| SetWindowPosition::NO_ACTIVATE | SetWindowPosition::NO_ACTIVATE
| SetWindowPosition::SHOW_WINDOW; | SetWindowPosition::SHOW_WINDOW
| SetWindowPosition::ASYNC_WINDOW_POS;
let position = HWND_BOTTOM; let position = HWND_BOTTOM;
Self::set_window_pos( Self::set_window_pos(
@@ -555,6 +571,7 @@ impl WindowsApi {
| SetWindowPosition::NO_ACTIVATE | SetWindowPosition::NO_ACTIVATE
| SetWindowPosition::NO_REDRAW | SetWindowPosition::NO_REDRAW
| SetWindowPosition::SHOW_WINDOW | SetWindowPosition::SHOW_WINDOW
| SetWindowPosition::ASYNC_WINDOW_POS
}; };
Self::set_window_pos( Self::set_window_pos(
@@ -581,6 +598,7 @@ impl WindowsApi {
.process() .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<()> { pub fn move_window(hwnd: isize, layout: &Rect, repaint: bool) -> Result<()> {
let hwnd = HWND(as_ptr!(hwnd)); 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 // https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-showwindow
// TODO: error handling // TODO: error handling
unsafe { unsafe {
let _ = ShowWindow(HWND(as_ptr!(hwnd)), command); let _ = ShowWindowAsync(HWND(as_ptr!(hwnd)), command);
}; };
} }
@@ -656,7 +674,7 @@ impl WindowsApi {
0, 0,
0, 0,
0, 0,
SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW, SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW | SWP_ASYNCWINDOWPOS,
) )
.process(); .process();
SetForegroundWindow(HWND(as_ptr!(hwnd))) SetForegroundWindow(HWND(as_ptr!(hwnd)))