windows: More precise handling of WM_SETTINGCHANGE and appearance updates (#33829)

This PR adds more fine-grained handling of the `WM_SETTINGCHANGE`
message.
Plus, we now only trigger the `appearance_changed` callback when the
actual window appearance has changed, rather than calling it every time.


Release Notes:

- N/A
This commit is contained in:
张小白 2025-07-03 17:27:27 +08:00 committed by GitHub
parent 968587a745
commit cdb7564d89
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 89 additions and 46 deletions

View file

@ -93,7 +93,7 @@ pub(crate) fn handle_msg(
WM_IME_STARTCOMPOSITION => handle_ime_position(handle, state_ptr), WM_IME_STARTCOMPOSITION => handle_ime_position(handle, state_ptr),
WM_IME_COMPOSITION => handle_ime_composition(handle, lparam, state_ptr), WM_IME_COMPOSITION => handle_ime_composition(handle, lparam, state_ptr),
WM_SETCURSOR => handle_set_cursor(handle, lparam, state_ptr), WM_SETCURSOR => handle_set_cursor(handle, lparam, state_ptr),
WM_SETTINGCHANGE => handle_system_settings_changed(handle, lparam, state_ptr), WM_SETTINGCHANGE => handle_system_settings_changed(handle, wparam, lparam, state_ptr),
WM_INPUTLANGCHANGE => handle_input_language_changed(lparam, state_ptr), WM_INPUTLANGCHANGE => handle_input_language_changed(lparam, state_ptr),
WM_GPUI_CURSOR_STYLE_CHANGED => handle_cursor_changed(lparam, state_ptr), WM_GPUI_CURSOR_STYLE_CHANGED => handle_cursor_changed(lparam, state_ptr),
_ => None, _ => None,
@ -1152,37 +1152,23 @@ fn handle_set_cursor(
fn handle_system_settings_changed( fn handle_system_settings_changed(
handle: HWND, handle: HWND,
wparam: WPARAM,
lparam: LPARAM, lparam: LPARAM,
state_ptr: Rc<WindowsWindowStatePtr>, state_ptr: Rc<WindowsWindowStatePtr>,
) -> Option<isize> { ) -> Option<isize> {
let mut lock = state_ptr.state.borrow_mut(); if wparam.0 != 0 {
let display = lock.display; let mut lock = state_ptr.state.borrow_mut();
// system settings let display = lock.display;
lock.system_settings.update(display); lock.system_settings.update(display, wparam.0);
// mouse double click lock.click_state.system_update(wparam.0);
lock.click_state.system_update(); lock.border_offset.update(handle).log_err();
// window border offset } else {
lock.border_offset.update(handle).log_err(); handle_system_theme_changed(handle, lparam, state_ptr)?;
drop(lock); };
// lParam is a pointer to a string that indicates the area containing the system parameter
// that was changed.
let parameter = PCWSTR::from_raw(lparam.0 as _);
if unsafe { !parameter.is_null() && !parameter.is_empty() } {
if let Some(parameter_string) = unsafe { parameter.to_string() }.log_err() {
log::info!("System settings changed: {}", parameter_string);
match parameter_string.as_str() {
"ImmersiveColorSet" => {
handle_system_theme_changed(handle, state_ptr);
}
_ => {}
}
}
}
// Force to trigger WM_NCCALCSIZE event to ensure that we handle auto hide // Force to trigger WM_NCCALCSIZE event to ensure that we handle auto hide
// taskbar correctly. // taskbar correctly.
notify_frame_changed(handle); notify_frame_changed(handle);
Some(0) Some(0)
} }
@ -1199,17 +1185,34 @@ fn handle_system_command(wparam: WPARAM, state_ptr: Rc<WindowsWindowStatePtr>) -
fn handle_system_theme_changed( fn handle_system_theme_changed(
handle: HWND, handle: HWND,
lparam: LPARAM,
state_ptr: Rc<WindowsWindowStatePtr>, state_ptr: Rc<WindowsWindowStatePtr>,
) -> Option<isize> { ) -> Option<isize> {
let mut callback = state_ptr // lParam is a pointer to a string that indicates the area containing the system parameter
.state // that was changed.
.borrow_mut() let parameter = PCWSTR::from_raw(lparam.0 as _);
.callbacks if unsafe { !parameter.is_null() && !parameter.is_empty() } {
.appearance_changed if let Some(parameter_string) = unsafe { parameter.to_string() }.log_err() {
.take()?; log::info!("System settings changed: {}", parameter_string);
callback(); match parameter_string.as_str() {
state_ptr.state.borrow_mut().callbacks.appearance_changed = Some(callback); "ImmersiveColorSet" => {
configure_dwm_dark_mode(handle); let new_appearance = system_appearance()
.context("unable to get system appearance when handling ImmersiveColorSet")
.log_err()?;
let mut lock = state_ptr.state.borrow_mut();
if new_appearance != lock.appearance {
lock.appearance = new_appearance;
let mut callback = lock.callbacks.appearance_changed.take()?;
drop(lock);
callback();
state_ptr.state.borrow_mut().callbacks.appearance_changed = Some(callback);
configure_dwm_dark_mode(handle, new_appearance);
}
}
_ => {}
}
}
}
Some(0) Some(0)
} }

View file

@ -32,14 +32,32 @@ pub(crate) struct MouseWheelSettings {
impl WindowsSystemSettings { impl WindowsSystemSettings {
pub(crate) fn new(display: WindowsDisplay) -> Self { pub(crate) fn new(display: WindowsDisplay) -> Self {
let mut settings = Self::default(); let mut settings = Self::default();
settings.update(display); settings.init(display);
settings settings
} }
pub(crate) fn update(&mut self, display: WindowsDisplay) { fn init(&mut self, display: WindowsDisplay) {
self.mouse_wheel_settings.update(); self.mouse_wheel_settings.update();
self.auto_hide_taskbar_position = AutoHideTaskbarPosition::new(display).log_err().flatten(); self.auto_hide_taskbar_position = AutoHideTaskbarPosition::new(display).log_err().flatten();
} }
pub(crate) fn update(&mut self, display: WindowsDisplay, wparam: usize) {
match wparam {
// SPI_SETWORKAREA
47 => self.update_taskbar_position(display),
// SPI_GETWHEELSCROLLLINES, SPI_GETWHEELSCROLLCHARS
104 | 108 => self.update_mouse_wheel_settings(),
_ => {}
}
}
fn update_mouse_wheel_settings(&mut self) {
self.mouse_wheel_settings.update();
}
fn update_taskbar_position(&mut self, display: WindowsDisplay) {
self.auto_hide_taskbar_position = AutoHideTaskbarPosition::new(display).log_err().flatten();
}
} }
impl MouseWheelSettings { impl MouseWheelSettings {

View file

@ -144,8 +144,8 @@ pub(crate) fn load_cursor(style: CursorStyle) -> Option<HCURSOR> {
} }
/// This function is used to configure the dark mode for the window built-in title bar. /// This function is used to configure the dark mode for the window built-in title bar.
pub(crate) fn configure_dwm_dark_mode(hwnd: HWND) { pub(crate) fn configure_dwm_dark_mode(hwnd: HWND, appearance: WindowAppearance) {
let dark_mode_enabled: BOOL = match system_appearance().log_err().unwrap_or_default() { let dark_mode_enabled: BOOL = match appearance {
WindowAppearance::Dark | WindowAppearance::VibrantDark => true.into(), WindowAppearance::Dark | WindowAppearance::VibrantDark => true.into(),
WindowAppearance::Light | WindowAppearance::VibrantLight => false.into(), WindowAppearance::Light | WindowAppearance::VibrantLight => false.into(),
}; };

View file

@ -37,6 +37,7 @@ pub struct WindowsWindowState {
pub min_size: Option<Size<Pixels>>, pub min_size: Option<Size<Pixels>>,
pub fullscreen_restore_bounds: Bounds<Pixels>, pub fullscreen_restore_bounds: Bounds<Pixels>,
pub border_offset: WindowBorderOffset, pub border_offset: WindowBorderOffset,
pub appearance: WindowAppearance,
pub scale_factor: f32, pub scale_factor: f32,
pub restore_from_minimized: Option<Box<dyn FnMut(RequestFrameOptions)>>, pub restore_from_minimized: Option<Box<dyn FnMut(RequestFrameOptions)>>,
@ -84,6 +85,7 @@ impl WindowsWindowState {
display: WindowsDisplay, display: WindowsDisplay,
gpu_context: &BladeContext, gpu_context: &BladeContext,
min_size: Option<Size<Pixels>>, min_size: Option<Size<Pixels>>,
appearance: WindowAppearance,
) -> Result<Self> { ) -> Result<Self> {
let scale_factor = { let scale_factor = {
let monitor_dpi = unsafe { GetDpiForWindow(hwnd) } as f32; let monitor_dpi = unsafe { GetDpiForWindow(hwnd) } as f32;
@ -118,6 +120,7 @@ impl WindowsWindowState {
logical_size, logical_size,
fullscreen_restore_bounds, fullscreen_restore_bounds,
border_offset, border_offset,
appearance,
scale_factor, scale_factor,
restore_from_minimized, restore_from_minimized,
min_size, min_size,
@ -206,6 +209,7 @@ impl WindowsWindowStatePtr {
context.display, context.display,
context.gpu_context, context.gpu_context,
context.min_size, context.min_size,
context.appearance,
)?); )?);
Ok(Rc::new_cyclic(|this| Self { Ok(Rc::new_cyclic(|this| Self {
@ -338,6 +342,7 @@ struct WindowCreateContext<'a> {
main_receiver: flume::Receiver<Runnable>, main_receiver: flume::Receiver<Runnable>,
gpu_context: &'a BladeContext, gpu_context: &'a BladeContext,
main_thread_id_win32: u32, main_thread_id_win32: u32,
appearance: WindowAppearance,
} }
impl WindowsWindow { impl WindowsWindow {
@ -387,6 +392,7 @@ impl WindowsWindow {
} else { } else {
WindowsDisplay::primary_monitor().unwrap() WindowsDisplay::primary_monitor().unwrap()
}; };
let appearance = system_appearance().unwrap_or_default();
let mut context = WindowCreateContext { let mut context = WindowCreateContext {
inner: None, inner: None,
handle, handle,
@ -403,6 +409,7 @@ impl WindowsWindow {
main_receiver, main_receiver,
gpu_context, gpu_context,
main_thread_id_win32, main_thread_id_win32,
appearance,
}; };
let lpparam = Some(&context as *const _ as *const _); let lpparam = Some(&context as *const _ as *const _);
let creation_result = unsafe { let creation_result = unsafe {
@ -426,7 +433,7 @@ impl WindowsWindow {
let state_ptr = context.inner.take().unwrap()?; let state_ptr = context.inner.take().unwrap()?;
let hwnd = creation_result?; let hwnd = creation_result?;
register_drag_drop(state_ptr.clone())?; register_drag_drop(state_ptr.clone())?;
configure_dwm_dark_mode(hwnd); configure_dwm_dark_mode(hwnd, appearance);
state_ptr.state.borrow_mut().border_offset.update(hwnd)?; state_ptr.state.borrow_mut().border_offset.update(hwnd)?;
let placement = retrieve_window_placement( let placement = retrieve_window_placement(
hwnd, hwnd,
@ -543,7 +550,7 @@ impl PlatformWindow for WindowsWindow {
} }
fn appearance(&self) -> WindowAppearance { fn appearance(&self) -> WindowAppearance {
system_appearance().log_err().unwrap_or_default() self.0.state.borrow().appearance
} }
fn display(&self) -> Option<Rc<dyn PlatformDisplay>> { fn display(&self) -> Option<Rc<dyn PlatformDisplay>> {
@ -951,7 +958,7 @@ impl IDropTarget_Impl for WindowsDragDropHandler_Impl {
} }
} }
#[derive(Debug)] #[derive(Debug, Clone, Copy)]
pub(crate) struct ClickState { pub(crate) struct ClickState {
button: MouseButton, button: MouseButton,
last_click: Instant, last_click: Instant,
@ -993,10 +1000,25 @@ impl ClickState {
self.current_count self.current_count
} }
pub fn system_update(&mut self) { pub fn system_update(&mut self, wparam: usize) {
self.double_click_spatial_tolerance_width = unsafe { GetSystemMetrics(SM_CXDOUBLECLK) }; match wparam {
self.double_click_spatial_tolerance_height = unsafe { GetSystemMetrics(SM_CYDOUBLECLK) }; // SPI_SETDOUBLECLKWIDTH
self.double_click_interval = Duration::from_millis(unsafe { GetDoubleClickTime() } as u64); 29 => {
self.double_click_spatial_tolerance_width =
unsafe { GetSystemMetrics(SM_CXDOUBLECLK) }
}
// SPI_SETDOUBLECLKHEIGHT
30 => {
self.double_click_spatial_tolerance_height =
unsafe { GetSystemMetrics(SM_CYDOUBLECLK) }
}
// SPI_SETDOUBLECLICKTIME
32 => {
self.double_click_interval =
Duration::from_millis(unsafe { GetDoubleClickTime() } as u64)
}
_ => {}
}
} }
#[inline] #[inline]