From 0b4d39a5e7bcef0d6587704b3eec37286d3ea1f0 Mon Sep 17 00:00:00 2001 From: Adrien Prokopowicz <6529475+prokopyl@users.noreply.github.com> Date: Fri, 2 Oct 2026 05:12:28 +0200 Subject: [PATCH 01/10] wip --- src/platform/win/dpi.rs | 27 +++++++++++++++++++++++---- src/platform/win/window.rs | 13 +++++++------ 2 files changed, 30 insertions(+), 10 deletions(-) diff --git a/src/platform/win/dpi.rs b/src/platform/win/dpi.rs index 082214a2..a56da531 100644 --- a/src/platform/win/dpi.rs +++ b/src/platform/win/dpi.rs @@ -9,6 +9,7 @@ use std::ops::Deref; #[derive(Copy, Clone, Default)] pub(crate) struct DpiScalingStrategy { pub assume_96_dpi: bool, + pub should_use_host_suggested_scale_factor: bool, pub thread_dpi_awareness_context: Option, } @@ -91,9 +92,10 @@ impl DpiScalingStrategy { // These are documented to not be compatible with per-monitor awareness types, so we'll fall back to System-aware // See: https://learn.microsoft.com/en-us/windows/win32/api/windef/ne-windef-dpi_hosting_behavior#remarks if matches!(dpi_awareness_type, Some(Unaware | UnawareGDIScaled | SystemDpiAware)) { - crate::debug!("Parent has DPI Awareness Context with Per-Monitor DPI awareness, falling back to System DPI Awareness."); + crate::debug!("Parent has DPI Awareness Context without Per-Monitor DPI awareness, falling back to System DPI Awareness."); return Self { assume_96_dpi: false, + should_use_host_suggested_scale_factor: false, thread_dpi_awareness_context: Some(SystemDpiAware.into()), }; } @@ -113,7 +115,15 @@ impl DpiScalingStrategy { // If type is unknown, assume it's better than System-Aware, and we can at least fetch the actual DPI. let assume_96_dpi = matches!(dpi_awareness_type, Some(Unaware | UnawareGDIScaled)); - Self { assume_96_dpi, thread_dpi_awareness_context: Some(dpi_awareness_context) } + // If type is unknown, assume it's better than Per-Monitor-Aware-V2, and we'll get DPI_MESSAGES from the OS + let should_use_host_suggested_scale_factor = + matches!(dpi_awareness_type, Some(PerMonitorDpiAware)); + + Self { + assume_96_dpi, + should_use_host_suggested_scale_factor, + thread_dpi_awareness_context: Some(dpi_awareness_context), + } } fn get_from_process_legacy(shcore: &LazyLibraryModule) -> Self { @@ -126,12 +136,21 @@ impl DpiScalingStrategy { crate::debug!("Using legacy Process DPI Awareness: {:?}", awareness); let assume_96_dpi = matches!(awareness, None | Some(Unaware)); + let should_use_host_suggested_scale_factor = matches!(awareness, Some(PerMonitorDpiAware)); - Self { assume_96_dpi, thread_dpi_awareness_context: None } + Self { + assume_96_dpi, + should_use_host_suggested_scale_factor, + thread_dpi_awareness_context: None, + } } fn completely_unaware() -> Self { - Self { assume_96_dpi: true, thread_dpi_awareness_context: None } + Self { + assume_96_dpi: true, + should_use_host_suggested_scale_factor: false, + thread_dpi_awareness_context: None, + } } fn get_best_supported( diff --git a/src/platform/win/window.rs b/src/platform/win/window.rs index ca5aab62..06e65a03 100644 --- a/src/platform/win/window.rs +++ b/src/platform/win/window.rs @@ -106,11 +106,13 @@ impl WindowHandle { self.state.sizing_strategy } - pub fn suggest_scale_factor(&self, scale_factor: f64) -> Result<()> { + pub fn suggest_scale_factor(&self, new_host_scale_factor: f64) -> Result<()> { let current_scale_factor = self.state.scale_factor(); - self.state.fallback_scale_factor.set(Some(scale_factor)); + self.state.fallback_scale_factor.set(Some(new_host_scale_factor)); - if self.state.current_dpi.get().is_some() { + let dpi_scaling_strategy = self.state.dpi_scaling_strategy.get(); + + if !dpi_scaling_strategy.should_use_host_suggested_scale_factor { return Ok(()); } @@ -122,7 +124,7 @@ impl WindowHandle { .current_size .get() .to_logical::(current_scale_factor) - .to_physical(self.state.scale_factor()); + .to_physical(new_host_scale_factor); // This call doesn't meaningfully change the scaling factor, ignore the result if current_size == new_size { @@ -130,8 +132,7 @@ impl WindowHandle { } let _guard = self.state.originate_host_resize(); - let dpi_ctx = - DpiAwarenessGuard::new(&self.state.user32, self.state.dpi_scaling_strategy.get())?; + let dpi_ctx = DpiAwarenessGuard::new(&self.state.user32, dpi_scaling_strategy)?; hwnd.resize_and_activate(new_size, None, &dpi_ctx)?; From bee8e2a42a601101c8ed91751a70386d9d013839 Mon Sep 17 00:00:00 2001 From: Adrien Prokopowicz <6529475+prokopyl@users.noreply.github.com> Date: Fri, 2 Oct 2026 16:27:16 +0200 Subject: [PATCH 02/10] wip --- src/platform/win/dpi.rs | 5 +++++ src/wrappers/win32/dpi.rs | 8 ++++++++ 2 files changed, 13 insertions(+) diff --git a/src/platform/win/dpi.rs b/src/platform/win/dpi.rs index a56da531..cd7004a4 100644 --- a/src/platform/win/dpi.rs +++ b/src/platform/win/dpi.rs @@ -48,6 +48,11 @@ impl DpiScalingStrategy { return Self::get_from_process(user32, &shcore); }; + crate::debug!( + "Parent DPI Awareness Context detected: {:?}", + parent_dpi_ctx.get_type(user32_lib) + ); + if parent.supports_mixed_dpi_hosting_behavior(user32_lib) { Self::get_best_matching_with_dpi_parent_awareness_context( parent_dpi_ctx, diff --git a/src/wrappers/win32/dpi.rs b/src/wrappers/win32/dpi.rs index ab529977..725fe44f 100644 --- a/src/wrappers/win32/dpi.rs +++ b/src/wrappers/win32/dpi.rs @@ -3,6 +3,7 @@ use crate::platform::DpiScalingStrategy; use crate::wrappers::win32::user32::ExtendedUser32; use crate::wrappers::win32::DpiAwarenessContextType::*; use std::ffi::c_void; +use std::fmt::Debug; use std::num::NonZeroU32; use std::ptr::NonNull; use windows_core::{Error, Result}; @@ -68,6 +69,13 @@ impl Default for Dpi { } } +impl Debug for Dpi { + #[inline] + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + self.0.get().fmt(f) + } +} + /// Win8 Legacy (replaced by DpiAwarenessContext in Win10), process-wide. #[repr(i32)] #[derive(Copy, Clone, Eq, PartialEq, Debug)] From b1684fb71316d4eee8c68c3ca498c27cd3642c5d Mon Sep 17 00:00:00 2001 From: Adrien Prokopowicz <6529475+prokopyl@users.noreply.github.com> Date: Fri, 2 Oct 2026 17:47:22 +0200 Subject: [PATCH 03/10] Fix enable_non_client_dpi_scaling --- src/platform/win/dpi.rs | 9 +++++++++ src/platform/win/window.rs | 2 +- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/src/platform/win/dpi.rs b/src/platform/win/dpi.rs index cd7004a4..d354a2d1 100644 --- a/src/platform/win/dpi.rs +++ b/src/platform/win/dpi.rs @@ -10,6 +10,7 @@ use std::ops::Deref; pub(crate) struct DpiScalingStrategy { pub assume_96_dpi: bool, pub should_use_host_suggested_scale_factor: bool, + pub should_enable_nc_dpi_scaling_manually: bool, pub thread_dpi_awareness_context: Option, } @@ -101,6 +102,7 @@ impl DpiScalingStrategy { return Self { assume_96_dpi: false, should_use_host_suggested_scale_factor: false, + should_enable_nc_dpi_scaling_manually: false, thread_dpi_awareness_context: Some(SystemDpiAware.into()), }; } @@ -124,9 +126,13 @@ impl DpiScalingStrategy { let should_use_host_suggested_scale_factor = matches!(dpi_awareness_type, Some(PerMonitorDpiAware)); + let should_enable_nc_dpi_scaling_manually = + matches!(dpi_awareness_type, Some(PerMonitorDpiAware)); + Self { assume_96_dpi, should_use_host_suggested_scale_factor, + should_enable_nc_dpi_scaling_manually, thread_dpi_awareness_context: Some(dpi_awareness_context), } } @@ -142,10 +148,12 @@ impl DpiScalingStrategy { let assume_96_dpi = matches!(awareness, None | Some(Unaware)); let should_use_host_suggested_scale_factor = matches!(awareness, Some(PerMonitorDpiAware)); + let should_enable_nc_dpi_scaling_manually = matches!(awareness, Some(PerMonitorDpiAware)); Self { assume_96_dpi, should_use_host_suggested_scale_factor, + should_enable_nc_dpi_scaling_manually, thread_dpi_awareness_context: None, } } @@ -154,6 +162,7 @@ impl DpiScalingStrategy { Self { assume_96_dpi: true, should_use_host_suggested_scale_factor: false, + should_enable_nc_dpi_scaling_manually: false, thread_dpi_awareness_context: None, } } diff --git a/src/platform/win/window.rs b/src/platform/win/window.rs index 06e65a03..cf6f4be2 100644 --- a/src/platform/win/window.rs +++ b/src/platform/win/window.rs @@ -327,7 +327,7 @@ impl Drop for BaseviewWindow { impl WindowImpl for BaseviewWindow { fn non_client_create(&self, window: HWnd) -> std::result::Result<(), PlatformError> { - if self.shared_state.dpi_scaling_strategy.get().assume_96_dpi { + if self.shared_state.dpi_scaling_strategy.get().should_enable_nc_dpi_scaling_manually { window.enable_non_client_dpi_scaling(&self.shared_state.user32); } From 1b72a8264a58b1d4471727847f5ba18f267cf409 Mon Sep 17 00:00:00 2001 From: Adrien Prokopowicz <6529475+prokopyl@users.noreply.github.com> Date: Fri, 2 Oct 2026 23:21:54 +0200 Subject: [PATCH 04/10] wip (halp) --- src/platform/win/error.rs | 13 +++++++++++-- src/platform/win/window.rs | 20 +++++++++++++++++--- src/platform/win/window_state.rs | 6 +++++- src/wrappers/win32/dpi.rs | 14 +++++++++++++- src/wrappers/win32/rect.rs | 12 ++++++++++++ src/wrappers/win32/window/handle.rs | 4 ++++ 6 files changed, 62 insertions(+), 7 deletions(-) diff --git a/src/platform/win/error.rs b/src/platform/win/error.rs index adbf71f6..91742dd2 100644 --- a/src/platform/win/error.rs +++ b/src/platform/win/error.rs @@ -1,4 +1,5 @@ use crate::HandlerError; +use dpi::PhysicalSize; use std::fmt::Display; pub type Result = std::result::Result; @@ -6,7 +7,8 @@ pub type Result = std::result::Result; #[derive(Debug)] pub enum PlatformError { Win32(windows_core::Error), - ResizeFailed, + UnexpectedResizeResult { new_actual_size: PhysicalSize }, + InvalidScaleFactor, Handler(HandlerError), } @@ -27,7 +29,14 @@ impl Display for PlatformError { match self { PlatformError::Win32(e) => Display::fmt(e, f), PlatformError::Handler(e) => Display::fmt(e, f), - PlatformError::ResizeFailed => f.write_str("Window resize request failed."), + PlatformError::UnexpectedResizeResult { new_actual_size } => { + write!( + f, + "Resize operation did not result in expected size, got {:?} instead.", + new_actual_size + ) + } + PlatformError::InvalidScaleFactor => f.write_str("Invalid window scale factor."), } } } diff --git a/src/platform/win/window.rs b/src/platform/win/window.rs index cf6f4be2..11a31c14 100644 --- a/src/platform/win/window.rs +++ b/src/platform/win/window.rs @@ -76,7 +76,9 @@ impl WindowHandle { } pub fn resize(&self, new_size: Size) -> Result<()> { + crate::debug!("Resizing to {:?}", new_size); let new_size = self.state.sizing_strategy.adjust_size(new_size, self.size()).physical; + crate::debug!("Size adjusted to {:?}", new_size); if new_size == self.state.current_size.get() { return Ok(()); @@ -93,12 +95,15 @@ impl WindowHandle { let _guard = self.state.originate_host_resize(); let dpi_ctx = DpiAwarenessGuard::new(&self.state.user32, self.state.dpi_scaling_strategy.get())?; + crate::debug!("DPI: {:?}", hwnd.get_dpi(&self.state.user32)); hwnd.resize_and_activate(new_size, self.state.current_dpi.get(), &dpi_ctx)?; if self.state.current_size.get() == new_size { Ok(()) } else { - Err(PlatformError::ResizeFailed) + Err(PlatformError::UnexpectedResizeResult { + new_actual_size: self.state.current_size.get(), + }) } } @@ -107,6 +112,9 @@ impl WindowHandle { } pub fn suggest_scale_factor(&self, new_host_scale_factor: f64) -> Result<()> { + let new_dpi = Dpi::from_scale_factor(new_host_scale_factor) + .ok_or(PlatformError::InvalidScaleFactor)?; + let current_scale_factor = self.state.scale_factor(); self.state.fallback_scale_factor.set(Some(new_host_scale_factor)); @@ -116,6 +124,8 @@ impl WindowHandle { return Ok(()); } + self.state.current_dpi.set(Some(new_dpi)); + let Some(hwnd) = self.hwnd.get() else { return Ok(()) }; let current_size = self.state.current_size.get(); @@ -134,12 +144,14 @@ impl WindowHandle { let _guard = self.state.originate_host_resize(); let dpi_ctx = DpiAwarenessGuard::new(&self.state.user32, dpi_scaling_strategy)?; - hwnd.resize_and_activate(new_size, None, &dpi_ctx)?; + hwnd.resize_and_activate(new_size, Some(new_dpi), &dpi_ctx)?; if self.state.current_size.get() == new_size { Ok(()) } else { - Err(PlatformError::ResizeFailed) + Err(PlatformError::UnexpectedResizeResult { + new_actual_size: self.state.current_size.get(), + }) } } @@ -579,6 +591,8 @@ unsafe fn wnd_proc_inner( let new_size = PhysicalSize { width, height }; let current_size = window_state.shared.current_size.get(); + crate::debug!("WM_SIZE: new {new_size:?}, old {current_size:?}"); + // Only send the event if anything changed if current_size == new_size { return None; diff --git a/src/platform/win/window_state.rs b/src/platform/win/window_state.rs index 58bd85d2..d0ea47e0 100644 --- a/src/platform/win/window_state.rs +++ b/src/platform/win/window_state.rs @@ -147,7 +147,7 @@ impl WindowSharedState { Self { parented: (settings.parent.is_some() || settings.wait_for_parent).into(), is_alive: true.into(), - current_dpi: None.into(), + current_dpi: Some(Dpi::default()).into(), current_size: settings.size.to_physical(1.0).into(), fallback_scale_factor: settings.fallback_scale_factor.into(), resize_host_originated: false.into(), @@ -180,6 +180,10 @@ impl WindowSharedState { } pub fn scale_factor(&self) -> f64 { + if self.dpi_scaling_strategy.get().assume_96_dpi { + return 1.0; + } + if let Some(dpi) = self.current_dpi.get() { dpi.scale_factor() } else { diff --git a/src/wrappers/win32/dpi.rs b/src/wrappers/win32/dpi.rs index 725fe44f..586111b0 100644 --- a/src/wrappers/win32/dpi.rs +++ b/src/wrappers/win32/dpi.rs @@ -3,7 +3,7 @@ use crate::platform::DpiScalingStrategy; use crate::wrappers::win32::user32::ExtendedUser32; use crate::wrappers::win32::DpiAwarenessContextType::*; use std::ffi::c_void; -use std::fmt::Debug; +use std::fmt::{Debug, Formatter}; use std::num::NonZeroU32; use std::ptr::NonNull; use windows_core::{Error, Result}; @@ -20,6 +20,10 @@ impl Dpi { self.0.get() as f64 / USER_DEFAULT_SCREEN_DPI as f64 } + pub fn from_scale_factor(scale_factor: f64) -> Option { + Some(Dpi(NonZeroU32::new((scale_factor * USER_DEFAULT_SCREEN_DPI as f64) as u32)?)) + } + /// Windows 10, version 1607. pub fn get_system(user32: &ExtendedUser32) -> Option { if let Some(get_dpi_for_system) = user32.get_dpi_for_system { @@ -333,6 +337,14 @@ impl<'a> DpiAwarenessGuard<'a> { } } +impl Debug for DpiAwarenessGuard<'_> { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + f.debug_struct("DpiAwarenessGuard") + .field("context_type", &self.inner.map(|(c, u)| c.get_type(u))) + .finish() + } +} + impl Drop for DpiAwarenessGuard<'_> { fn drop(&mut self) { if let Some(inner) = self.inner { diff --git a/src/wrappers/win32/rect.rs b/src/wrappers/win32/rect.rs index c4bd73e6..77e95341 100644 --- a/src/wrappers/win32/rect.rs +++ b/src/wrappers/win32/rect.rs @@ -1,4 +1,5 @@ use crate::dpi::PhysicalSize; +use std::fmt::Debug; use windows_sys::Win32::Foundation::RECT; #[derive(Copy, Clone)] @@ -25,3 +26,14 @@ impl From> for Rect { }) } } + +impl Debug for Rect { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("Rect") + .field("left", &self.0.left) + .field("top", &self.0.top) + .field("right", &self.0.right) + .field("bottom", &self.0.bottom) + .finish() + } +} diff --git a/src/wrappers/win32/window/handle.rs b/src/wrappers/win32/window/handle.rs index 1f463d83..4ba22de3 100644 --- a/src/wrappers/win32/window/handle.rs +++ b/src/wrappers/win32/window/handle.rs @@ -178,8 +178,12 @@ impl HWnd { let style = self.get_style()?; let rect = Rect::from(client_size); + crate::debug!("Resize_and_activate rect: {rect:?} (dpi: {window_dpi:?}"); let rect = dpi_ctx.client_area_to_nc_area(rect, style, window_dpi)?; + crate::debug!("Resize_and_activate rect after adjust: {rect:?}"); + crate::debug!("CTX: {dpi_ctx:?}"); + self.resize_nc_and_activate(rect.size()) } From ce28aca020696b50c9be994a855eeb9f08f86f70 Mon Sep 17 00:00:00 2001 From: Adrien Prokopowicz <6529475+prokopyl@users.noreply.github.com> Date: Sat, 3 Oct 2026 01:20:45 +0200 Subject: [PATCH 05/10] I hate everything --- src/wrappers/win32/dpi.rs | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/src/wrappers/win32/dpi.rs b/src/wrappers/win32/dpi.rs index 586111b0..079974d3 100644 --- a/src/wrappers/win32/dpi.rs +++ b/src/wrappers/win32/dpi.rs @@ -171,9 +171,7 @@ impl DpiAwarenessContext { /// Windows 10, version 1607. pub fn set_thread(&self, user32: &ExtendedUser32) -> Option> { - let previous = unsafe { - user32.set_thread_dpi_awareness_context?(DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2) - }; + let previous = unsafe { user32.set_thread_dpi_awareness_context?(self.inner.as_ptr()) }; let Some(inner) = NonNull::new(previous) else { return Some(Err(Error::from_thread())) }; @@ -181,9 +179,7 @@ impl DpiAwarenessContext { } pub fn set_process(&self, user32: &ExtendedUser32) -> Option> { - let result = unsafe { - user32.set_process_dpi_awareness_context?(DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2) - }; + let result = unsafe { user32.set_process_dpi_awareness_context?(self.inner.as_ptr()) }; if result == FALSE { return Some(Err(Error::from_thread())); From aa0fdfa497e904e1dfca2634ef3cd38b5f64e324 Mon Sep 17 00:00:00 2001 From: Adrien Prokopowicz <6529475+prokopyl@users.noreply.github.com> Date: Sat, 3 Oct 2026 14:52:25 +0200 Subject: [PATCH 06/10] cleanup and fixes --- Cargo.toml | 2 +- src/platform/win/dpi.rs | 2 +- src/platform/win/window.rs | 5 ----- src/wrappers/win32/window/handle.rs | 4 ---- 4 files changed, 2 insertions(+), 11 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 5ad265be..6795df2e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -43,7 +43,7 @@ tracing = { version = "0.1", optional = true } x11rb = { version = "0.13.2", features = ["cursor", "dri3", "present", "resource_manager", "allow-unsafe-code", "dl-libxcb"], default-features = false } xkbcommon-dl = { version = "0.4.2", features = ["x11"] } x11-dl = { version = "2.21.0" } -calloop = "0.14.4" +calloop = "=0.14.4" # 0.14.5 has MSRV of 1.86 percent-encoding = "2.3.2" bytemuck = { version = "1.25.2", features = ["extern_crate_alloc"] } libloading = "0.8.9" # Libloading 0.9 is out but its MSRV is 1.88 diff --git a/src/platform/win/dpi.rs b/src/platform/win/dpi.rs index d354a2d1..36af7bad 100644 --- a/src/platform/win/dpi.rs +++ b/src/platform/win/dpi.rs @@ -122,7 +122,7 @@ impl DpiScalingStrategy { // If type is unknown, assume it's better than System-Aware, and we can at least fetch the actual DPI. let assume_96_dpi = matches!(dpi_awareness_type, Some(Unaware | UnawareGDIScaled)); - // If type is unknown, assume it's better than Per-Monitor-Aware-V2, and we'll get DPI_MESSAGES from the OS + // If type is unknown, assume it's better than Per-Monitor-Aware-V2, and we'll get WM_DPICHANGED messages from the OS let should_use_host_suggested_scale_factor = matches!(dpi_awareness_type, Some(PerMonitorDpiAware)); diff --git a/src/platform/win/window.rs b/src/platform/win/window.rs index 11a31c14..7c6252c6 100644 --- a/src/platform/win/window.rs +++ b/src/platform/win/window.rs @@ -76,9 +76,7 @@ impl WindowHandle { } pub fn resize(&self, new_size: Size) -> Result<()> { - crate::debug!("Resizing to {:?}", new_size); let new_size = self.state.sizing_strategy.adjust_size(new_size, self.size()).physical; - crate::debug!("Size adjusted to {:?}", new_size); if new_size == self.state.current_size.get() { return Ok(()); @@ -95,7 +93,6 @@ impl WindowHandle { let _guard = self.state.originate_host_resize(); let dpi_ctx = DpiAwarenessGuard::new(&self.state.user32, self.state.dpi_scaling_strategy.get())?; - crate::debug!("DPI: {:?}", hwnd.get_dpi(&self.state.user32)); hwnd.resize_and_activate(new_size, self.state.current_dpi.get(), &dpi_ctx)?; if self.state.current_size.get() == new_size { @@ -591,8 +588,6 @@ unsafe fn wnd_proc_inner( let new_size = PhysicalSize { width, height }; let current_size = window_state.shared.current_size.get(); - crate::debug!("WM_SIZE: new {new_size:?}, old {current_size:?}"); - // Only send the event if anything changed if current_size == new_size { return None; diff --git a/src/wrappers/win32/window/handle.rs b/src/wrappers/win32/window/handle.rs index 4ba22de3..1f463d83 100644 --- a/src/wrappers/win32/window/handle.rs +++ b/src/wrappers/win32/window/handle.rs @@ -178,12 +178,8 @@ impl HWnd { let style = self.get_style()?; let rect = Rect::from(client_size); - crate::debug!("Resize_and_activate rect: {rect:?} (dpi: {window_dpi:?}"); let rect = dpi_ctx.client_area_to_nc_area(rect, style, window_dpi)?; - crate::debug!("Resize_and_activate rect after adjust: {rect:?}"); - crate::debug!("CTX: {dpi_ctx:?}"); - self.resize_nc_and_activate(rect.size()) } From f515c26f18e31738ec5db0e3abdc1f103e46aa22 Mon Sep 17 00:00:00 2001 From: Adrien Prokopowicz <6529475+prokopyl@users.noreply.github.com> Date: Sat, 3 Oct 2026 15:59:19 +0200 Subject: [PATCH 07/10] Fix initial window size in high-DPI mode --- src/platform/win/window.rs | 51 +++++++++++++++++++++++++------------- 1 file changed, 34 insertions(+), 17 deletions(-) diff --git a/src/platform/win/window.rs b/src/platform/win/window.rs index 7c6252c6..b8b80496 100644 --- a/src/platform/win/window.rs +++ b/src/platform/win/window.rs @@ -299,9 +299,7 @@ impl BaseviewWindow { self.host.notify_destroyed() } - fn request_resize_from_host( - &self, new_size: WindowSize, - ) -> core::result::Result<(), HandlerError> { + fn request_host_resize(&self, new_size: WindowSize) -> core::result::Result<(), HandlerError> { if self.shared_state.resize_host_originated.get() { return Ok(()); }; @@ -309,6 +307,29 @@ impl BaseviewWindow { self.host.request_resize(new_size) } + fn adapt_host_window_to_size( + &self, previous_size: PhysicalSize, new_size: WindowSize, + ) -> core::result::Result<(), ()> { + if let Err(e) = self.request_host_resize(new_size) { + warn!("Resize request from Host failed: {}. Reverting to previous size.", e); + + if let Some(handler) = self.handler.get() { + if let Err(e) = handler.resized(new_size) { + warn!("Window Handler failed to resize to previous window size: {}", e); + } + }; + + self.window_state.shared.current_size.set(previous_size); + if let Err(e) = self.window_state.resize(previous_size.into()) { + warn!("Failed to resize back to previous window size: {}", e); + } + + return Err(()); + } + + Ok(()) + } + pub(crate) fn handle_on_frame(&self) { let Some(handler) = self.handler.get() else { return }; @@ -367,12 +388,17 @@ impl WindowImpl for BaseviewWindow { // Preemptively update so a synchronous WM_SIZE from SetWindowPos below // doesn't also emit Resized. - window_state.shared.current_size.set(new_size); + let previous_size = window_state.shared.current_size.replace(new_size); let guard = DpiAwarenessGuard::new( &window_state.shared.user32, self.shared_state.dpi_scaling_strategy.get(), )?; window.resize_and_activate(new_size, Some(dpi), &guard)?; + + let _ = self.adapt_host_window_to_size( + previous_size, + WindowSize::from_physical(new_size, dpi.scale_factor()), + ); } } @@ -608,7 +634,7 @@ unsafe fn wnd_proc_inner( return Some(-1); } - if let Err(e) = window_bv.request_resize_from_host(new_size) { + if let Err(e) = window_bv.request_host_resize(new_size) { warn!("Resize request from Host failed: {}. Reverting to previous size.", e); if let Err(e) = handler.resized(new_size) { @@ -664,20 +690,11 @@ unsafe fn wnd_proc_inner( if let Err(e) = window_state.resize(previous_size.into()) { warn!("Failed to resize back to previous window size: {}", e); } - } - - if let Err(e) = window_bv.request_resize_from_host(new_size) { - warn!("Resize request from Host failed: {}. Reverting to previous size.", e); - - if let Err(e) = handler.resized(new_size) { - warn!("Window Handler failed to resize to previous window size: {}", e); - } - window_state.shared.current_size.set(previous_size); - if let Err(e) = window_state.resize(previous_size.into()) { - warn!("Failed to resize back to previous window size: {}", e); - } + return Some(-1); + } + if let Err(()) = window_bv.adapt_host_window_to_size(previous_size, new_size) { return Some(-1); } } From 484319136252aba3c58389327677010e514f7034 Mon Sep 17 00:00:00 2001 From: Adrien Prokopowicz <6529475+prokopyl@users.noreply.github.com> Date: Sat, 3 Oct 2026 16:34:47 +0200 Subject: [PATCH 08/10] Use parent to attempt to get window DPI in parented mode --- src/platform/win/window_state.rs | 41 +++++++++++++++++++++++--------- 1 file changed, 30 insertions(+), 11 deletions(-) diff --git a/src/platform/win/window_state.rs b/src/platform/win/window_state.rs index d0ea47e0..6809e4dc 100644 --- a/src/platform/win/window_state.rs +++ b/src/platform/win/window_state.rs @@ -8,7 +8,6 @@ use crate::wrappers::win32::cursor::SystemCursor; use crate::wrappers::win32::h_instance::HInstance; use crate::wrappers::win32::window::HWnd; use crate::wrappers::win32::{Dpi, DpiAwarenessGuard, ExtendedUser32, LibraryModule}; -use crate::WindowSettings; use crate::{MouseCursor, WindowSize}; use raw_window_handle::{DisplayHandle, Win32WindowHandle}; use std::cell::{Cell, Ref, RefCell}; @@ -143,20 +142,27 @@ pub struct WindowSharedState { } impl WindowSharedState { - pub fn new(user32: LibraryModule, settings: &WindowSettings) -> Rc { - Self { - parented: (settings.parent.is_some() || settings.wait_for_parent).into(), + pub fn new(user32: LibraryModule, init: &WindowInitializer) -> Rc { + let state = Self { + sizing_strategy: SizingStrategy::from_settings(&init.settings), + user32, + + // Internal state is_alive: true.into(), - current_dpi: Some(Dpi::default()).into(), - current_size: settings.size.to_physical(1.0).into(), - fallback_scale_factor: settings.fallback_scale_factor.into(), resize_host_originated: false.into(), destroy_host_originated: false.into(), - sizing_strategy: SizingStrategy::from_settings(settings), - user32, + + // Overriden by init() below + parented: false.into(), + current_dpi: Some(Dpi::default()).into(), + current_size: init.settings.size.to_physical(1.0).into(), + fallback_scale_factor: init.settings.fallback_scale_factor.into(), dpi_scaling_strategy: DpiScalingStrategy::default().into(), - } - .into() + }; + + state.init(init); + + state.into() } pub fn init(&self, init: &WindowInitializer) { @@ -170,8 +176,21 @@ impl WindowSharedState { if strategy.assume_96_dpi { self.current_dpi.set(Some(Dpi::default())); + self.current_size.set(init.settings.size.to_physical(1.0)); + self.fallback_scale_factor.set(Some(1.0)); + } else { + let parent_dpi = parent.and_then(|p| p.get_dpi(&self.user32)); + let scale_factor = parent_dpi + .map(|dpi| dpi.scale_factor()) + .or(init.settings.fallback_scale_factor) + .unwrap_or(1.0); + + self.current_dpi.set(parent_dpi); + self.current_size.set(init.settings.size.to_physical(scale_factor)); + self.fallback_scale_factor.set(init.settings.fallback_scale_factor); } + self.parented.set(init.settings.parent.is_some() || init.settings.wait_for_parent); self.dpi_scaling_strategy.set(strategy); } From f0cc3af105440af7f8203a42d6da6ccfb1613a9f Mon Sep 17 00:00:00 2001 From: Adrien Prokopowicz <6529475+prokopyl@users.noreply.github.com> Date: Sat, 3 Oct 2026 16:51:17 +0200 Subject: [PATCH 09/10] fix --- src/platform/win/window.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/platform/win/window.rs b/src/platform/win/window.rs index b8b80496..f9873e59 100644 --- a/src/platform/win/window.rs +++ b/src/platform/win/window.rs @@ -767,7 +767,7 @@ impl WindowHandle { pub fn create_window(init: WindowInitializer) -> Result { let extended_user_32 = LibraryModule::load()?; - let shared_state = WindowSharedState::new(extended_user_32, &init.settings); + let shared_state = WindowSharedState::new(extended_user_32, &init); if init.settings.wait_for_parent && init.settings.parent.is_none() { return Ok(WindowHandle { From 7c39bb873232ef7ab1f503e102fed743c738b477 Mon Sep 17 00:00:00 2001 From: Adrien Prokopowicz <6529475+prokopyl@users.noreply.github.com> Date: Sat, 3 Oct 2026 17:09:24 +0200 Subject: [PATCH 10/10] fixes --- src/platform/win/window.rs | 39 ++++++++++++++++++++--------- src/platform/win/window_state.rs | 19 +++++++------- src/wrappers/win32/window/handle.rs | 4 +-- 3 files changed, 39 insertions(+), 23 deletions(-) diff --git a/src/platform/win/window.rs b/src/platform/win/window.rs index f9873e59..48861282 100644 --- a/src/platform/win/window.rs +++ b/src/platform/win/window.rs @@ -121,7 +121,7 @@ impl WindowHandle { return Ok(()); } - self.state.current_dpi.set(Some(new_dpi)); + self.state.current_dpi.set(new_dpi); let Some(hwnd) = self.hwnd.get() else { return Ok(()) }; @@ -141,7 +141,7 @@ impl WindowHandle { let _guard = self.state.originate_host_resize(); let dpi_ctx = DpiAwarenessGuard::new(&self.state.user32, dpi_scaling_strategy)?; - hwnd.resize_and_activate(new_size, Some(new_dpi), &dpi_ctx)?; + hwnd.resize_and_activate(new_size, new_dpi, &dpi_ctx)?; if self.state.current_size.get() == new_size { Ok(()) @@ -377,12 +377,12 @@ impl WindowImpl for BaseviewWindow { .get_dpi_for_window(window, &self.shared_state.user32); if let Some(dpi) = dpi { - if Some(dpi) != window_state.shared.current_dpi.get() { - window_state.shared.current_dpi.set(Some(dpi)); + if dpi != window_state.shared.current_dpi.get() { + window_state.shared.current_dpi.set(dpi); // We cannot create a window in "logical" pixels, and we can't DPI-scale to physical pixels because we // have no way to know where the window will end up. - // So, at window creation, we assume a DPI=96, and if it ends up wrong, we resize the window + // So, at window creation, we assume a DPI=96 (or parent DPI if available), and if it ends up wrong, we resize the window // to the actual logical size the user desired. let new_size = self.initial_size.to_physical(dpi.scale_factor()); @@ -393,12 +393,21 @@ impl WindowImpl for BaseviewWindow { &window_state.shared.user32, self.shared_state.dpi_scaling_strategy.get(), )?; - window.resize_and_activate(new_size, Some(dpi), &guard)?; + window.resize_and_activate(new_size, dpi, &guard)?; let _ = self.adapt_host_window_to_size( previous_size, WindowSize::from_physical(new_size, dpi.scale_factor()), ); + } else { + // If the host queried size before it gave us a parent, this is the one it's been using. + let previous_host_size = self.initial_size.to_physical(1.0); + let current_size = self.initial_size.to_physical(dpi.scale_factor()); + + let _ = self.adapt_host_window_to_size( + previous_host_size, + WindowSize::from_physical(current_size, dpi.scale_factor()), + ); } } @@ -670,9 +679,9 @@ unsafe fn wnd_proc_inner( let new_size = suggested_rect.size(); let changed = window_state.shared.current_size.get() != new_size - || window_state.shared.current_dpi.get() != Some(dpi); + || window_state.shared.current_dpi.get() != dpi; - window_state.shared.current_dpi.set(Some(dpi)); + window_state.shared.current_dpi.set(dpi); let previous_size = window_state.shared.current_size.replace(new_size); // Windows makes us resize the window manually. This however will not send a WM_SIZE event, @@ -737,16 +746,22 @@ unsafe fn wnd_proc_inner( if let Some(size) = sizing.min_size() { let size = size.to_physical(window_state.shared.scale_factor()); - let size = - ctx.client_area_to_nc_area(size.into(), style, dpi).unwrap().size().cast(); + let size = ctx + .client_area_to_nc_area(size.into(), style, Some(dpi)) + .unwrap() + .size() + .cast(); let pt = POINT { x: size.width, y: size.height }; (&raw mut (*info).ptMinTrackSize).write(pt); } if let Some(size) = sizing.max_size() { let size = size.to_physical(window_state.shared.scale_factor()); - let size = - ctx.client_area_to_nc_area(size.into(), style, dpi).unwrap().size().cast(); + let size = ctx + .client_area_to_nc_area(size.into(), style, Some(dpi)) + .unwrap() + .size() + .cast(); let pt = POINT { x: size.width, y: size.height }; (&raw mut (*info).ptMaxTrackSize).write(pt); } diff --git a/src/platform/win/window_state.rs b/src/platform/win/window_state.rs index 6809e4dc..48d02045 100644 --- a/src/platform/win/window_state.rs +++ b/src/platform/win/window_state.rs @@ -131,7 +131,7 @@ pub struct WindowSharedState { pub parented: Cell, pub is_alive: Cell, pub current_size: Cell>, - pub current_dpi: Cell>, // None if Win32 HiDPI isn't supported + pub current_dpi: Cell, pub fallback_scale_factor: Cell>, pub resize_host_originated: Cell, pub destroy_host_originated: Cell, @@ -154,7 +154,7 @@ impl WindowSharedState { // Overriden by init() below parented: false.into(), - current_dpi: Some(Dpi::default()).into(), + current_dpi: Dpi::default().into(), current_size: init.settings.size.to_physical(1.0).into(), fallback_scale_factor: init.settings.fallback_scale_factor.into(), dpi_scaling_strategy: DpiScalingStrategy::default().into(), @@ -175,7 +175,7 @@ impl WindowSharedState { ); if strategy.assume_96_dpi { - self.current_dpi.set(Some(Dpi::default())); + self.current_dpi.set(Dpi::default()); self.current_size.set(init.settings.size.to_physical(1.0)); self.fallback_scale_factor.set(Some(1.0)); } else { @@ -185,7 +185,7 @@ impl WindowSharedState { .or(init.settings.fallback_scale_factor) .unwrap_or(1.0); - self.current_dpi.set(parent_dpi); + self.current_dpi.set(parent_dpi.unwrap_or_default()); self.current_size.set(init.settings.size.to_physical(scale_factor)); self.fallback_scale_factor.set(init.settings.fallback_scale_factor); } @@ -199,15 +199,16 @@ impl WindowSharedState { } pub fn scale_factor(&self) -> f64 { - if self.dpi_scaling_strategy.get().assume_96_dpi { + let strategy = self.dpi_scaling_strategy.get(); + if strategy.assume_96_dpi { return 1.0; } - if let Some(dpi) = self.current_dpi.get() { - dpi.scale_factor() - } else { - self.fallback_scale_factor.get().unwrap_or(1.0) + if strategy.should_use_host_suggested_scale_factor { + return self.fallback_scale_factor.get().unwrap_or(1.0); } + + self.current_dpi.get().scale_factor() } pub fn originate_host_resize(&self) -> impl Drop + use<'_> { diff --git a/src/wrappers/win32/window/handle.rs b/src/wrappers/win32/window/handle.rs index 1f463d83..cdf17dd8 100644 --- a/src/wrappers/win32/window/handle.rs +++ b/src/wrappers/win32/window/handle.rs @@ -173,12 +173,12 @@ impl HWnd { } pub fn resize_and_activate( - &self, client_size: PhysicalSize, window_dpi: Option, dpi_ctx: &DpiAwarenessGuard, + &self, client_size: PhysicalSize, window_dpi: Dpi, dpi_ctx: &DpiAwarenessGuard, ) -> Result<()> { let style = self.get_style()?; let rect = Rect::from(client_size); - let rect = dpi_ctx.client_area_to_nc_area(rect, style, window_dpi)?; + let rect = dpi_ctx.client_area_to_nc_area(rect, style, Some(window_dpi))?; self.resize_nc_and_activate(rect.size()) }