diff --git a/Cargo.toml b/Cargo.toml index 37ad4b379f..049f093c74 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -313,6 +313,7 @@ sys-locale = "0.3.1" version = "0.53.0" features = [ "Win32_Graphics_Gdi", + "Win32_Graphics_DirectComposition", "Win32_UI_WindowsAndMessaging", "Win32_UI_Input_KeyboardAndMouse", "Win32_System_SystemServices", diff --git a/crates/gpui/src/platform/windows/platform.rs b/crates/gpui/src/platform/windows/platform.rs index 26fda0be37..92f065ec67 100644 --- a/crates/gpui/src/platform/windows/platform.rs +++ b/crates/gpui/src/platform/windows/platform.rs @@ -18,12 +18,13 @@ use parking_lot::Mutex; use time::UtcOffset; use util::{ResultExt, SemanticVersion}; use windows::Win32::{ - Foundation::{CloseHandle, GetLastError, HANDLE, HWND, WAIT_EVENT}, - System::Threading::{CreateEventW, INFINITE}, + Foundation::{CloseHandle, BOOL, HANDLE, HWND, LPARAM, TRUE}, + Graphics::DirectComposition::DCompositionWaitForCompositorClock, + System::Threading::{CreateEventW, GetCurrentThreadId, INFINITE}, UI::WindowsAndMessaging::{ - DispatchMessageW, GetMessageW, MsgWaitForMultipleObjects, PostQuitMessage, - SystemParametersInfoW, TranslateMessage, MSG, QS_ALLINPUT, SPI_GETWHEELSCROLLCHARS, - SPI_GETWHEELSCROLLLINES, SYSTEM_PARAMETERS_INFO_UPDATE_FLAGS, WM_QUIT, WM_SETTINGCHANGE, + DispatchMessageW, EnumThreadWindows, PeekMessageW, PostQuitMessage, SystemParametersInfoW, + TranslateMessage, MSG, PM_REMOVE, SPI_GETWHEELSCROLLCHARS, SPI_GETWHEELSCROLLLINES, + SYSTEM_PARAMETERS_INFO_UPDATE_FLAGS, WM_QUIT, WM_SETTINGCHANGE, }, }; @@ -169,26 +170,31 @@ impl WindowsPlatform { } } - fn wait_message(&self) -> WindowsMessageWaitResult { - let wait_result = unsafe { - MsgWaitForMultipleObjects(Some(&[self.inner.event]), false, INFINITE, QS_ALLINPUT) - }; - - match wait_result { - WAIT_EVENT(0) => WindowsMessageWaitResult::ForegroundExecution, - WAIT_EVENT(1) => { - let mut msg = MSG::default(); - unsafe { GetMessageW(&mut msg, HWND::default(), 0, 0) }; - WindowsMessageWaitResult::WindowsMessage(msg) - } - _ => { - log::error!("unhandled windows wait message: {}", wait_result.0); - WindowsMessageWaitResult::Error - } + fn run_foreground_tasks(&self) { + for runnable in self.inner.main_receiver.drain() { + runnable.run(); } } } +unsafe extern "system" fn invalidate_window_callback(hwnd: HWND, _: LPARAM) -> BOOL { + if let Some(inner) = try_get_window_inner(hwnd) { + inner.invalidate_client_area(); + } + TRUE +} + +/// invalidates all windows belonging to a thread causing a paint message to be scheduled +fn invalidate_thread_windows(win32_thread_id: u32) { + unsafe { + EnumThreadWindows( + win32_thread_id, + Some(invalidate_window_callback), + LPARAM::default(), + ) + }; +} + impl Platform for WindowsPlatform { fn background_executor(&self) -> BackgroundExecutor { self.inner.background_executor.clone() @@ -204,16 +210,21 @@ impl Platform for WindowsPlatform { fn run(&self, on_finish_launching: Box) { on_finish_launching(); - loop { - match self.wait_message() { - WindowsMessageWaitResult::ForegroundExecution => { - for runnable in self.inner.main_receiver.drain() { - runnable.run(); - } - } - WindowsMessageWaitResult::WindowsMessage(msg) => { + 'a: loop { + let mut msg = MSG::default(); + // will be 0 if woken up by self.inner.event or 1 if the compositor clock ticked + // SEE: https://learn.microsoft.com/en-us/windows/win32/directcomp/compositor-clock/compositor-clock + let wait_result = + unsafe { DCompositionWaitForCompositorClock(Some(&[self.inner.event]), INFINITE) }; + + // compositor clock ticked so we should draw a frame + if wait_result == 1 { + unsafe { invalidate_thread_windows(GetCurrentThreadId()) }; + + while unsafe { PeekMessageW(&mut msg, HWND::default(), 0, 0, PM_REMOVE) }.as_bool() + { if msg.message == WM_QUIT { - break; + break 'a; } if !self.run_immediate_msg_handlers(&msg) { @@ -221,8 +232,9 @@ impl Platform for WindowsPlatform { unsafe { DispatchMessageW(&msg) }; } } - WindowsMessageWaitResult::Error => {} } + + self.run_foreground_tasks(); } let mut callbacks = self.inner.callbacks.lock(); diff --git a/crates/gpui/src/platform/windows/window.rs b/crates/gpui/src/platform/windows/window.rs index 8747311d8b..78a57f5ac4 100644 --- a/crates/gpui/src/platform/windows/window.rs +++ b/crates/gpui/src/platform/windows/window.rs @@ -17,7 +17,8 @@ use raw_window_handle::{HasDisplayHandle, HasWindowHandle}; use windows::{ core::{w, HSTRING, PCWSTR}, Win32::{ - Foundation::{HINSTANCE, HWND, LPARAM, LRESULT, WPARAM}, + Foundation::{FALSE, HINSTANCE, HWND, LPARAM, LRESULT, WPARAM}, + Graphics::Gdi::{BeginPaint, EndPaint, InvalidateRect, PAINTSTRUCT}, System::SystemServices::{ MK_LBUTTON, MK_MBUTTON, MK_RBUTTON, MK_XBUTTON1, MK_XBUTTON2, MODIFIERKEYS_FLAGS, }, @@ -158,6 +159,12 @@ impl WindowsWindowInner { } } + /// mark window client rect to be re-drawn + /// https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-invalidaterect + pub(crate) fn invalidate_client_area(&self) { + unsafe { InvalidateRect(self.hwnd, None, FALSE) }; + } + /// returns true if message is handled and should not dispatch pub(crate) fn handle_immediate_msg(&self, msg: u32, wparam: WPARAM, lparam: LPARAM) -> bool { match msg { @@ -245,16 +252,20 @@ impl WindowsWindowInner { height: Pixels(height.0), }, 1.0, - ) + ); } + self.invalidate_client_area(); LRESULT(0) } fn handle_paint_msg(&self) -> LRESULT { + let mut paint_struct = PAINTSTRUCT::default(); + let hdc = unsafe { BeginPaint(self.hwnd, &mut paint_struct) }; let mut callbacks = self.callbacks.borrow_mut(); - if let Some(callback) = callbacks.request_frame.as_mut() { - callback() + if let Some(request_frame) = callbacks.request_frame.as_mut() { + request_frame(); } + unsafe { EndPaint(self.hwnd, &paint_struct) }; LRESULT(0) } @@ -403,18 +414,12 @@ impl WindowsWindowInner { }; if callback(PlatformInput::KeyDown(event)) { - if let Some(request_frame) = callbacks.request_frame.as_mut() { - request_frame(); - } CallbackResult::Handled { by_callback: true } } else if let Some(mut input_handler) = self.input_handler.take() { if let Some(ime_key) = ime_key { input_handler.replace_text_in_range(None, &ime_key); } self.input_handler.set(Some(input_handler)); - if let Some(request_frame) = callbacks.request_frame.as_mut() { - request_frame(); - } CallbackResult::Handled { by_callback: true } } else { CallbackResult::Handled { by_callback: false } @@ -433,9 +438,8 @@ impl WindowsWindowInner { if let Some(keystroke) = keystroke { if let Some(callback) = callbacks.input.as_mut() { let event = KeyUpEvent { keystroke }; - CallbackResult::Handled { - by_callback: callback(PlatformInput::KeyUp(event)), - } + let by_callback = callback(PlatformInput::KeyUp(event)); + CallbackResult::Handled { by_callback } } else { CallbackResult::Handled { by_callback: false } } @@ -527,12 +531,8 @@ impl WindowsWindowInner { modifiers: self.current_modifiers(), touch_phase: TouchPhase::Moved, }; - if callback(PlatformInput::ScrollWheel(event)) { - if let Some(request_frame) = callbacks.request_frame.as_mut() { - request_frame(); - } - return LRESULT(0); - } + callback(PlatformInput::ScrollWheel(event)); + return LRESULT(0); } LRESULT(1) } @@ -554,9 +554,6 @@ impl WindowsWindowInner { touch_phase: TouchPhase::Moved, }; if callback(PlatformInput::ScrollWheel(event)) { - if let Some(request_frame) = callbacks.request_frame.as_mut() { - request_frame(); - } return LRESULT(0); } }