diff --git a/komorebi/src/stackbar_manager/stackbar.rs b/komorebi/src/stackbar_manager/stackbar.rs index 2e547ebe..b02b6b16 100644 --- a/komorebi/src/stackbar_manager/stackbar.rs +++ b/komorebi/src/stackbar_manager/stackbar.rs @@ -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())); diff --git a/komorebi/src/window.rs b/komorebi/src/window.rs index 1ff84962..f4b162fd 100644 --- a/komorebi/src/window.rs +++ b/komorebi/src/window.rs @@ -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) } } diff --git a/komorebi/src/window_manager.rs b/komorebi/src/window_manager.rs index 99399021..ce21c5e9 100644 --- a/komorebi/src/window_manager.rs +++ b/komorebi/src/window_manager.rs @@ -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)?; } diff --git a/komorebi/src/windows_api.rs b/komorebi/src/windows_api.rs index 5479cedb..6fc4495d 100644 --- a/komorebi/src/windows_api.rs +++ b/komorebi/src/windows_api.rs @@ -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)))