Detailed changes
@@ -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<Self>, wparam: WPARAM) -> Option<isize> {
@@ -1064,18 +1036,14 @@ impl WindowsWindowInner {
lparam: LPARAM,
) -> Option<isize> {
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.
@@ -39,7 +39,6 @@ pub(crate) struct WindowsPlatform {
foreground_executor: ForegroundExecutor,
text_system: Arc<dyn PlatformTextSystem>,
direct_write_text_system: Option<Arc<DirectWriteTextSystem>>,
- windows_version: WindowsVersion,
drop_target_helper: Option<IDropTargetHelper>,
/// 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<HCURSOR>,
- pub(crate) windows_version: WindowsVersion,
pub(crate) drop_target_helper: IDropTargetHelper,
pub(crate) validation_number: usize,
pub(crate) main_receiver: PriorityQueueReceiver<RunnableVariant>,
@@ -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<Option<AutoHideTaskbarPosition>>,
}
#[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<Option<Self>> {
- 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::<APPBARDATA>() 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<DevicePixels> = 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::<APPBARDATA>() as u32,
- ..Default::default()
- };
- let ret = unsafe { SHAppBarMessage(ABM_GETSTATE, &mut info) } as u32;
- ret == ABS_AUTOHIDE
-}
@@ -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<Self> {
- 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;
@@ -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<RunnableVariant>,
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<Size<Pixels>>,
executor: ForegroundExecutor,
current_cursor: Option<HCURSOR>,
- windows_version: WindowsVersion,
drop_target_helper: IDropTargetHelper,
validation_number: usize,
main_receiver: PriorityQueueReceiver<RunnableVariant>,
@@ -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<Color>, 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;