diff --git a/crates/gpui/src/platform/windows/events.rs b/crates/gpui/src/platform/windows/events.rs index d0504c9cf384352cc14db4b5d8c4daad53fbebb1..871be1b3fd318ac4da68fb83059818b619e053bb 100644 --- a/crates/gpui/src/platform/windows/events.rs +++ b/crates/gpui/src/platform/windows/events.rs @@ -29,7 +29,6 @@ pub(crate) const WM_GPUI_GPU_DEVICE_LOST: u32 = WM_USER + 7; pub(crate) const WM_GPUI_KEYDOWN: u32 = WM_USER + 8; const SIZE_MOVE_LOOP_TIMER_ID: usize = 1; -const AUTO_HIDE_TASKBAR_THICKNESS_PX: i32 = 1; impl WindowsWindowInner { pub(crate) fn handle_msg( @@ -668,7 +667,6 @@ impl WindowsWindowInner { } } - /// SEE: https://learn.microsoft.com/en-us/windows/win32/winmsg/wm-nccalcsize fn handle_calc_client_size( &self, handle: HWND, @@ -679,43 +677,17 @@ impl WindowsWindowInner { return None; } - let is_maximized = self.state.is_maximized(); - let insets = get_client_area_insets(handle, is_maximized, self.windows_version); - // wparam is TRUE so lparam points to an NCCALCSIZE_PARAMS structure - let mut params = lparam.0 as *mut NCCALCSIZE_PARAMS; - let mut requested_client_rect = unsafe { &mut ((*params).rgrc) }; - - requested_client_rect[0].left += insets.left; - requested_client_rect[0].top += insets.top; - requested_client_rect[0].right -= insets.right; - requested_client_rect[0].bottom -= insets.bottom; - - // Fix auto hide taskbar not showing. This solution is based on the approach - // used by Chrome. However, it may result in one row of pixels being obscured - // in our client area. But as Chrome says, "there seems to be no better solution." - if is_maximized - && let Some(taskbar_position) = self.system_settings().auto_hide_taskbar_position.get() - { - // For the auto-hide taskbar, adjust in by 1 pixel on taskbar edge, - // so the window isn't treated as a "fullscreen app", which would cause - // the taskbar to disappear. - match taskbar_position { - AutoHideTaskbarPosition::Left => { - requested_client_rect[0].left += AUTO_HIDE_TASKBAR_THICKNESS_PX - } - AutoHideTaskbarPosition::Top => { - requested_client_rect[0].top += AUTO_HIDE_TASKBAR_THICKNESS_PX - } - AutoHideTaskbarPosition::Right => { - requested_client_rect[0].right -= AUTO_HIDE_TASKBAR_THICKNESS_PX - } - AutoHideTaskbarPosition::Bottom => { - requested_client_rect[0].bottom -= AUTO_HIDE_TASKBAR_THICKNESS_PX - } + unsafe { + let params = lparam.0 as *mut NCCALCSIZE_PARAMS; + let saved_top = (*params).rgrc[0].top; + let result = DefWindowProcW(handle, WM_NCCALCSIZE, wparam, lparam); + (*params).rgrc[0].top = saved_top; + if self.state.is_maximized() { + let dpi = GetDpiForWindow(handle); + (*params).rgrc[0].top += get_frame_thicknessx(dpi); } + Some(result.0 as isize) } - - Some(0) } fn handle_activate_msg(self: &Rc, wparam: WPARAM) -> Option { @@ -1064,18 +1036,14 @@ impl WindowsWindowInner { lparam: LPARAM, ) -> Option { if wparam.0 != 0 { - let display = self.state.display.get(); self.state.click_state.system_update(wparam.0); self.state.border_offset.update(handle).log_err(); // system settings may emit a window message which wants to take the refcell self.state, so drop it - self.system_settings().update(display, wparam.0); + self.system_settings().update(wparam.0); } else { self.handle_system_theme_changed(handle, lparam)?; }; - // Force to trigger WM_NCCALCSIZE event to ensure that we handle auto hide - // taskbar correctly. - notify_frame_changed(handle); Some(0) } @@ -1513,44 +1481,6 @@ pub(crate) fn current_capslock() -> Capslock { Capslock { on } } -fn get_client_area_insets( - handle: HWND, - is_maximized: bool, - windows_version: WindowsVersion, -) -> RECT { - // For maximized windows, Windows outdents the window rect from the screen's client rect - // by `frame_thickness` on each edge, meaning `insets` must contain `frame_thickness` - // on all sides (including the top) to avoid the client area extending onto adjacent - // monitors. - // - // For non-maximized windows, things become complicated: - // - // - On Windows 10 - // The top inset must be zero, since if there is any nonclient area, Windows will draw - // a full native titlebar outside the client area. (This doesn't occur in the maximized - // case.) - // - // - On Windows 11 - // The top inset is calculated using an empirical formula that I derived through various - // tests. Without this, the top 1-2 rows of pixels in our window would be obscured. - let dpi = unsafe { GetDpiForWindow(handle) }; - let frame_thickness = get_frame_thicknessx(dpi); - let top_insets = if is_maximized { - frame_thickness - } else { - match windows_version { - WindowsVersion::Win10 => 0, - WindowsVersion::Win11 => (dpi as f32 / USER_DEFAULT_SCREEN_DPI as f32).round() as i32, - } - }; - RECT { - left: frame_thickness, - top: top_insets, - right: frame_thickness, - bottom: frame_thickness, - } -} - // there is some additional non-visible space when talking about window // borders on Windows: // - SM_CXSIZEFRAME: The resize handle. diff --git a/crates/gpui/src/platform/windows/platform.rs b/crates/gpui/src/platform/windows/platform.rs index 51c54807c4a86695d8d01629cea87058f22c6a4f..b1eb15e47d3ec6ca45333da688d01175754bb128 100644 --- a/crates/gpui/src/platform/windows/platform.rs +++ b/crates/gpui/src/platform/windows/platform.rs @@ -39,7 +39,6 @@ pub(crate) struct WindowsPlatform { foreground_executor: ForegroundExecutor, text_system: Arc, direct_write_text_system: Option>, - windows_version: WindowsVersion, drop_target_helper: Option, /// Flag to instruct the `VSyncProvider` thread to invalidate the directx devices /// as resizing them has failed, causing us to have lost at least the render target. @@ -179,7 +178,6 @@ impl WindowsPlatform { } else { HICON::default() }; - let windows_version = WindowsVersion::new().context("Error retrieve windows version")?; Ok(Self { inner, @@ -192,7 +190,6 @@ impl WindowsPlatform { text_system, direct_write_text_system, disable_direct_composition, - windows_version, drop_target_helper, invalidate_devices: Arc::new(AtomicBool::new(false)), }) @@ -221,7 +218,6 @@ impl WindowsPlatform { icon: self.icon, executor: self.foreground_executor.clone(), current_cursor: self.inner.state.current_cursor.get(), - windows_version: self.windows_version, drop_target_helper: self.drop_target_helper.clone().unwrap(), validation_number: self.inner.validation_number, main_receiver: self.inner.main_receiver.clone(), @@ -1007,7 +1003,6 @@ pub(crate) struct WindowCreationInfo { pub(crate) icon: HICON, pub(crate) executor: ForegroundExecutor, pub(crate) current_cursor: Option, - pub(crate) windows_version: WindowsVersion, pub(crate) drop_target_helper: IDropTargetHelper, pub(crate) validation_number: usize, pub(crate) main_receiver: PriorityQueueReceiver, diff --git a/crates/gpui/src/platform/windows/system_settings.rs b/crates/gpui/src/platform/windows/system_settings.rs index 0a002bab28e07d10f4e554f006d85f40c4aff19c..53214f40deb3528a569e82b64cf6f1c9f4deca7a 100644 --- a/crates/gpui/src/platform/windows/system_settings.rs +++ b/crates/gpui/src/platform/windows/system_settings.rs @@ -4,24 +4,16 @@ use std::{ }; use ::util::ResultExt; -use windows::Win32::UI::{ - Shell::{ABM_GETSTATE, ABM_GETTASKBARPOS, ABS_AUTOHIDE, APPBARDATA, SHAppBarMessage}, - WindowsAndMessaging::{ - SPI_GETWHEELSCROLLCHARS, SPI_GETWHEELSCROLLLINES, SPI_SETWORKAREA, - SYSTEM_PARAMETERS_INFO_ACTION, SYSTEM_PARAMETERS_INFO_UPDATE_FLAGS, SystemParametersInfoW, - }, +use windows::Win32::UI::WindowsAndMessaging::{ + SPI_GETWHEELSCROLLCHARS, SPI_GETWHEELSCROLLLINES, SYSTEM_PARAMETERS_INFO_ACTION, + SYSTEM_PARAMETERS_INFO_UPDATE_FLAGS, SystemParametersInfoW, }; -use crate::*; - -use super::WindowsDisplay; - /// Windows settings pulled from SystemParametersInfo /// https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-systemparametersinfow #[derive(Default, Debug, Clone)] pub(crate) struct WindowsSystemSettings { pub(crate) mouse_wheel_settings: MouseWheelSettings, - pub(crate) auto_hide_taskbar_position: Cell>, } #[derive(Default, Debug, Clone)] @@ -33,21 +25,18 @@ pub(crate) struct MouseWheelSettings { } impl WindowsSystemSettings { - pub(crate) fn new(display: WindowsDisplay) -> Self { + pub(crate) fn new() -> Self { let mut settings = Self::default(); - settings.init(display); + settings.init(); settings } - fn init(&mut self, display: WindowsDisplay) { + fn init(&mut self) { self.mouse_wheel_settings.update(); - self.auto_hide_taskbar_position - .set(AutoHideTaskbarPosition::new(display).log_err().flatten()); } - pub(crate) fn update(&self, display: WindowsDisplay, wparam: usize) { + pub(crate) fn update(&self, wparam: usize) { match SYSTEM_PARAMETERS_INFO_ACTION(wparam as u32) { - SPI_SETWORKAREA => self.update_taskbar_position(display), SPI_GETWHEELSCROLLLINES | SPI_GETWHEELSCROLLCHARS => self.update_mouse_wheel_settings(), _ => {} } @@ -56,11 +45,6 @@ impl WindowsSystemSettings { fn update_mouse_wheel_settings(&self) { self.mouse_wheel_settings.update(); } - - fn update_taskbar_position(&self, display: WindowsDisplay) { - self.auto_hide_taskbar_position - .set(AutoHideTaskbarPosition::new(display).log_err().flatten()); - } } impl MouseWheelSettings { @@ -101,100 +85,3 @@ impl MouseWheelSettings { } } } - -#[derive(Debug, Clone, Copy, Default)] -pub(crate) enum AutoHideTaskbarPosition { - Left, - Right, - Top, - #[default] - Bottom, -} - -impl AutoHideTaskbarPosition { - fn new(display: WindowsDisplay) -> anyhow::Result> { - if !check_auto_hide_taskbar_enable() { - // If auto hide taskbar is not enable, we do nothing in this case. - return Ok(None); - } - let mut info = APPBARDATA { - cbSize: std::mem::size_of::() as u32, - ..Default::default() - }; - let ret = unsafe { SHAppBarMessage(ABM_GETTASKBARPOS, &mut info) }; - if ret == 0 { - anyhow::bail!( - "Unable to retrieve taskbar position: {}", - std::io::Error::last_os_error() - ); - } - let taskbar_bounds: Bounds = Bounds::new( - point(info.rc.left.into(), info.rc.top.into()), - size( - (info.rc.right - info.rc.left).into(), - (info.rc.bottom - info.rc.top).into(), - ), - ); - let display_bounds = display.physical_bounds(); - if display_bounds.intersect(&taskbar_bounds) != taskbar_bounds { - // This case indicates that taskbar is not on the current monitor. - return Ok(None); - } - if taskbar_bounds.bottom() == display_bounds.bottom() - && taskbar_bounds.right() == display_bounds.right() - { - if taskbar_bounds.size.height < display_bounds.size.height - && taskbar_bounds.size.width == display_bounds.size.width - { - return Ok(Some(Self::Bottom)); - } - if taskbar_bounds.size.width < display_bounds.size.width - && taskbar_bounds.size.height == display_bounds.size.height - { - return Ok(Some(Self::Right)); - } - log::error!( - "Unrecognized taskbar bounds {:?} give display bounds {:?}", - taskbar_bounds, - display_bounds - ); - return Ok(None); - } - if taskbar_bounds.top() == display_bounds.top() - && taskbar_bounds.left() == display_bounds.left() - { - if taskbar_bounds.size.height < display_bounds.size.height - && taskbar_bounds.size.width == display_bounds.size.width - { - return Ok(Some(Self::Top)); - } - if taskbar_bounds.size.width < display_bounds.size.width - && taskbar_bounds.size.height == display_bounds.size.height - { - return Ok(Some(Self::Left)); - } - log::error!( - "Unrecognized taskbar bounds {:?} give display bounds {:?}", - taskbar_bounds, - display_bounds - ); - return Ok(None); - } - log::error!( - "Unrecognized taskbar bounds {:?} give display bounds {:?}", - taskbar_bounds, - display_bounds - ); - Ok(None) - } -} - -/// Check if auto hide taskbar is enable or not. -fn check_auto_hide_taskbar_enable() -> bool { - let mut info = APPBARDATA { - cbSize: std::mem::size_of::() as u32, - ..Default::default() - }; - let ret = unsafe { SHAppBarMessage(ABM_GETSTATE, &mut info) } as u32; - ret == ABS_AUTOHIDE -} diff --git a/crates/gpui/src/platform/windows/util.rs b/crates/gpui/src/platform/windows/util.rs index e5f037ad760d36a679c5ddf858c05de8933758f5..bbb219165730ff033089269737ce3925ae9e7a89 100644 --- a/crates/gpui/src/platform/windows/util.rs +++ b/crates/gpui/src/platform/windows/util.rs @@ -7,7 +7,6 @@ use windows::{ Color, ViewManagement::{UIColorType, UISettings}, }, - Wdk::System::SystemServices::RtlGetVersion, Win32::{ Foundation::*, Graphics::Dwm::*, System::LibraryLoader::LoadLibraryA, UI::WindowsAndMessaging::*, @@ -17,26 +16,6 @@ use windows::{ use crate::*; -#[derive(Debug, Clone, Copy)] -pub(crate) enum WindowsVersion { - Win10, - Win11, -} - -impl WindowsVersion { - pub(crate) fn new() -> anyhow::Result { - let mut version = unsafe { std::mem::zeroed() }; - let status = unsafe { RtlGetVersion(&mut version) }; - - status.ok()?; - if version.dwBuildNumber >= 22000 { - Ok(WindowsVersion::Win11) - } else { - Ok(WindowsVersion::Win10) - } - } -} - pub(crate) trait HiLoWord { fn hiword(&self) -> u16; fn loword(&self) -> u16; diff --git a/crates/gpui/src/platform/windows/window.rs b/crates/gpui/src/platform/windows/window.rs index 837474a9da3217f62a299a90a166e71065afa2ac..26ce31a9978d45456b404b2401f6df152172a8e0 100644 --- a/crates/gpui/src/platform/windows/window.rs +++ b/crates/gpui/src/platform/windows/window.rs @@ -80,7 +80,6 @@ pub(crate) struct WindowsWindowInner { pub(crate) hide_title_bar: bool, pub(crate) is_movable: bool, pub(crate) executor: ForegroundExecutor, - pub(crate) windows_version: WindowsVersion, pub(crate) validation_number: usize, pub(crate) main_receiver: PriorityQueueReceiver, pub(crate) platform_window_handle: HWND, @@ -239,11 +238,10 @@ impl WindowsWindowInner { hide_title_bar: context.hide_title_bar, is_movable: context.is_movable, executor: context.executor.clone(), - windows_version: context.windows_version, validation_number: context.validation_number, main_receiver: context.main_receiver.clone(), platform_window_handle: context.platform_window_handle, - system_settings: WindowsSystemSettings::new(context.display), + system_settings: WindowsSystemSettings::new(), parent_hwnd: context.parent_hwnd, })) } @@ -293,6 +291,7 @@ impl WindowsWindowInner { } } }; + set_non_rude_hwnd(this.hwnd, !this.state.is_fullscreen()); unsafe { set_window_long(this.hwnd, GWL_STYLE, style.0 as isize) }; unsafe { SetWindowPos( @@ -363,7 +362,6 @@ struct WindowCreateContext { min_size: Option>, executor: ForegroundExecutor, current_cursor: Option, - windows_version: WindowsVersion, drop_target_helper: IDropTargetHelper, validation_number: usize, main_receiver: PriorityQueueReceiver, @@ -385,7 +383,6 @@ impl WindowsWindow { icon, executor, current_cursor, - windows_version, drop_target_helper, validation_number, main_receiver, @@ -465,7 +462,6 @@ impl WindowsWindow { min_size: params.window_min_size, executor, current_cursor, - windows_version, drop_target_helper, validation_number, main_receiver, @@ -500,6 +496,7 @@ impl WindowsWindow { let this = this.unwrap(); register_drag_drop(&this)?; + set_non_rude_hwnd(hwnd, true); configure_dwm_dark_mode(hwnd, appearance); this.state.border_offset.update(hwnd)?; let placement = retrieve_window_placement( @@ -1465,6 +1462,17 @@ fn set_window_composition_attribute(hwnd: HWND, color: Option, state: u32 } } +// When the platform title bar is hidden, Windows may think that our application is meant to appear 'fullscreen' +// and will stop the taskbar from appearing on top of our window. Prevent this. +// https://devblogs.microsoft.com/oldnewthing/20250522-00/?p=111211 +fn set_non_rude_hwnd(hwnd: HWND, non_rude: bool) { + if non_rude { + unsafe { SetPropW(hwnd, w!("NonRudeHWND"), Some(HANDLE(1 as _))) }.log_err(); + } else { + unsafe { RemovePropW(hwnd, w!("NonRudeHWND")) }.log_err(); + } +} + #[cfg(test)] mod tests { use super::ClickState;