From b6c7548666930ade655dfc25b89a7481896d8524 Mon Sep 17 00:00:00 2001 From: Adrien Prokopowicz <6529475+prokopyl@users.noreply.github.com> Date: Mon, 15 Jun 2026 02:35:13 +0200 Subject: [PATCH 1/2] Dynamically load recent user32 functions --- src/win/window.rs | 30 ++++++++---- src/wrappers/win32.rs | 2 + src/wrappers/win32/dpi.rs | 47 ++++++++++++------- src/wrappers/win32/user32.rs | 73 +++++++++++++++++++++++++++++ src/wrappers/win32/window/handle.rs | 16 +++++-- 5 files changed, 139 insertions(+), 29 deletions(-) create mode 100644 src/wrappers/win32/user32.rs diff --git a/src/win/window.rs b/src/win/window.rs index cdad8794..0b53967f 100644 --- a/src/win/window.rs +++ b/src/win/window.rs @@ -40,7 +40,8 @@ use crate::wrappers::win32::cursor::SystemCursor; use crate::gl::GlContext; use crate::wrappers::win32::window::*; use crate::wrappers::win32::{ - ole_initialize, run_thread_message_loop_until, Dpi, DpiAwarenessContext, Rect, WindowStyle, + ole_initialize, run_thread_message_loop_until, Dpi, DpiAwarenessContext, ExtendedUser32, Rect, + WindowStyle, }; #[allow(non_snake_case)] @@ -125,7 +126,7 @@ impl WindowImpl for BaseviewWindow { self._keyboard_hook.set(Some(hook::init_keyboard_hook(hwnd))); // Now we can get the actual dpi of the window. - let dpi = window.get_dpi()?; + let dpi = window.get_dpi(&self.window_state.user32)?; let mut dpi_changed = false; if dpi != window_state.current_dpi.get() { @@ -144,7 +145,7 @@ impl WindowImpl for BaseviewWindow { // Preemptively update so a synchronous WM_SIZE from SetWindowPos below // doesn't also emit Resized. window_state.current_size.set(new_size); - window.resize_and_activate(new_size, dpi)?; + window.resize_and_activate(new_size, dpi, &window_state.user32)?; } } @@ -404,7 +405,7 @@ unsafe fn wnd_proc_inner( let suggested_nc_rect = Rect((lparam as *const RECT).read()); let dpi = Dpi((wparam & 0xFFFF) as u16 as u32); - let dpi_ctx = DpiAwarenessContext::new().unwrap(); + let dpi_ctx = DpiAwarenessContext::new(&window_state.user32).unwrap(); let style = window.get_style().unwrap(); let suggested_rect = dpi_ctx.nc_area_to_client_area(suggested_nc_rect, style, dpi).unwrap(); @@ -473,6 +474,8 @@ pub(super) struct WindowState { handler: RefCell>>, scale_policy: WindowScalePolicy, + user32: ExtendedUser32, + /// Tasks that should be executed at the end of `wnd_proc`. This is needed to avoid mutably /// borrowing the fields from `WindowState` more than once. For instance, when the window /// handler requests a resize in response to a keyboard event, the window state will already be @@ -485,7 +488,9 @@ pub(super) struct WindowState { } impl WindowState { - pub fn new(hwnd: HWND, current_size: PhySize, scale_policy: WindowScalePolicy) -> Self { + pub fn new( + hwnd: HWND, current_size: PhySize, scale_policy: WindowScalePolicy, user32: ExtendedUser32, + ) -> Self { Self { hwnd, current_dpi: Dpi::default().into(), @@ -496,6 +501,7 @@ impl WindowState { cursor_icon: Cell::new(MouseCursor::Default), handler: RefCell::new(None), scale_policy, + user32, deferred_tasks: RefCell::new(VecDeque::with_capacity(4)), @@ -552,7 +558,7 @@ impl WindowState { let window_info = WindowInfo::from_logical_size(size, dpi.scale_factor()); let new_size = window_info.physical_size(); - window.resize_and_activate(new_size, dpi).unwrap(); + window.resize_and_activate(new_size, dpi, &self.user32).unwrap(); } WindowTask::Focus => window.set_focus().unwrap(), } @@ -609,6 +615,7 @@ impl Window<'_> { B: FnOnce(&mut crate::Window) -> H, B: Send + 'static, { + let extended_user_32 = ExtendedUser32::load().unwrap(); let title = HSTRING::from(options.title); let scaling_factor = match options.scale { @@ -620,17 +627,24 @@ impl Window<'_> { WindowInfo::from_logical_size(options.size, scaling_factor).physical_size(); let style = if parented { WindowStyle::parented() } else { WindowStyle::embedded() }; - let dpi_ctx = DpiAwarenessContext::new().unwrap(); + let dpi_ctx = DpiAwarenessContext::new(&extended_user_32).unwrap(); let rect = dpi_ctx.client_area_to_nc_area(window_size.into(), style, Dpi::default()).unwrap(); + drop(dpi_ctx); + let is_open = Rc::new(Cell::new(true)); let parent_handle = ParentHandle { is_open: is_open.clone() }; let initializer = move |hwnd: HWnd| { - let window_state = Rc::new(WindowState::new(hwnd.as_raw(), window_size, options.scale)); + let window_state = Rc::new(WindowState::new( + hwnd.as_raw(), + window_size, + options.scale, + extended_user_32, + )); BaseviewWindow { window_state, diff --git a/src/wrappers/win32.rs b/src/wrappers/win32.rs index 6818f6e0..829413d0 100644 --- a/src/wrappers/win32.rs +++ b/src/wrappers/win32.rs @@ -3,12 +3,14 @@ mod dpi; pub mod h_instance; mod rect; mod style; +mod user32; pub mod uuid; pub mod window; pub use dpi::*; pub use rect::Rect; pub use style::*; +pub use user32::*; use std::ptr::null_mut; use windows_core::{Error, Result, HRESULT}; diff --git a/src/wrappers/win32/dpi.rs b/src/wrappers/win32/dpi.rs index e594e495..b0a30d3d 100644 --- a/src/wrappers/win32/dpi.rs +++ b/src/wrappers/win32/dpi.rs @@ -1,8 +1,9 @@ use super::*; +use crate::wrappers::win32::user32::ExtendedUser32; use windows_core::{Error, Result}; use windows_sys::Win32::Foundation::RECT; use windows_sys::Win32::UI::HiDpi::*; -use windows_sys::Win32::UI::WindowsAndMessaging::USER_DEFAULT_SCREEN_DPI; +use windows_sys::Win32::UI::WindowsAndMessaging::{AdjustWindowRectEx, USER_DEFAULT_SCREEN_DPI}; #[derive(Copy, Clone, Eq, PartialEq)] pub struct Dpi(pub u32); @@ -19,34 +20,45 @@ impl Default for Dpi { } } -pub struct DpiAwarenessContext { +pub struct DpiAwarenessContext<'a> { previous: DPI_AWARENESS_CONTEXT, + user32: &'a ExtendedUser32, } -impl DpiAwarenessContext { - pub fn new() -> Result { - let mut previous = - unsafe { SetThreadDpiAwarenessContext(DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2) }; +impl<'a> DpiAwarenessContext<'a> { + pub fn new(user32: &'a ExtendedUser32) -> Result { + let Some(set_thread_dpi_awareness_context) = user32.set_thread_dpi_awareness_context else { + return Ok(Self { previous: DPI_AWARENESS_CONTEXT::default(), user32 }); + }; - if previous.is_null() { - previous = - unsafe { SetThreadDpiAwarenessContext(DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE) }; - } + let previous = + unsafe { set_thread_dpi_awareness_context(DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2) }; if previous.is_null() { return Err(Error::from_win32()); } - Ok(DpiAwarenessContext { previous }) + Ok(DpiAwarenessContext { previous, user32 }) } pub fn client_area_to_nc_area( &self, mut rect: Rect, style: WindowStyle, dpi: Dpi, ) -> Result { - // AdjustWindowRectExForDpi takes the current DPI awareness context in consideration. + let Some(adjust_window_rect_ex_for_dpi) = self.user32.adjust_window_rect_ex_for_dpi else { + let result = unsafe { AdjustWindowRectEx(&mut rect.0, style.style, 0, style.style_ex) }; + + if result == 0 { + return Err(Error::from_win32()); + } + + return Ok(rect); + }; + + // adjust_window_rect_ex_for_dpi takes the current DPI awareness context in consideration. // Therefore, this method taking &self enforces that the DPI aware context is correct. - let result = - unsafe { AdjustWindowRectExForDpi(&mut rect.0, style.style, 0, style.style_ex, dpi.0) }; + let result = unsafe { + adjust_window_rect_ex_for_dpi(&mut rect.0, style.style, 0, style.style_ex, dpi.0) + }; if result == 0 { return Err(Error::from_win32()); @@ -67,8 +79,11 @@ impl DpiAwarenessContext { } } -impl Drop for DpiAwarenessContext { +impl Drop for DpiAwarenessContext<'_> { fn drop(&mut self) { - let _ = unsafe { SetThreadDpiAwarenessContext(self.previous) }; + if let Some(set_thread_dpi_awareness_context) = self.user32.set_thread_dpi_awareness_context + { + let _ = unsafe { set_thread_dpi_awareness_context(self.previous) }; + } } } diff --git a/src/wrappers/win32/user32.rs b/src/wrappers/win32/user32.rs new file mode 100644 index 00000000..f42ef4f8 --- /dev/null +++ b/src/wrappers/win32/user32.rs @@ -0,0 +1,73 @@ +use std::ffi::c_void; +use std::mem::transmute; +use std::ptr::NonNull; +use windows_core::{s, Error, PCSTR}; +use windows_sys::core::BOOL; +use windows_sys::Win32::Foundation::{FreeLibrary, HWND, RECT}; +use windows_sys::Win32::System::LibraryLoader::{GetProcAddress, LoadLibraryA}; +use windows_sys::Win32::UI::HiDpi::DPI_AWARENESS_CONTEXT; +use windows_sys::Win32::UI::WindowsAndMessaging::{WINDOW_EX_STYLE, WINDOW_STYLE}; + +pub struct ExtendedUser32 { + _library: LibraryModule, + pub set_thread_dpi_awareness_context: Option, + pub adjust_window_rect_ex_for_dpi: Option, + pub get_dpi_for_window: Option, +} + +type SetThreadDpiAwarenessContext = + unsafe extern "system" fn(DPI_AWARENESS_CONTEXT) -> DPI_AWARENESS_CONTEXT; + +type GetDpiForWindow = unsafe extern "system" fn(HWND) -> u32; + +type AdjustWindowRectExForDpi = unsafe extern "system" fn( + lprect: *mut RECT, + dwstyle: WINDOW_STYLE, + bmenu: BOOL, + dwexstyle: WINDOW_EX_STYLE, + dpi: u32, +) -> BOOL; + +impl ExtendedUser32 { + pub fn load() -> Result { + let library = unsafe { LibraryModule::load(s!("user32.dll"))? }; + + unsafe { + Ok(Self { + set_thread_dpi_awareness_context: library + .get_proc_address(s!("SetThreadDpiAwarenessContext")) + .map(|p| transmute::<*const c_void, SetThreadDpiAwarenessContext>(p)), + adjust_window_rect_ex_for_dpi: library + .get_proc_address(s!("AdjustWindowRectExForDpi")) + .map(|p| transmute::<*const c_void, AdjustWindowRectExForDpi>(p)), + get_dpi_for_window: library + .get_proc_address(s!("GetDpiForWindow")) + .map(|p| transmute::<*const c_void, GetDpiForWindow>(p)), + _library: library, + }) + } + } +} + +struct LibraryModule(NonNull); + +impl LibraryModule { + pub unsafe fn load(module_name: PCSTR) -> Result { + let library = unsafe { LoadLibraryA(module_name.as_ptr()) }; + let Some(library) = NonNull::new(library) else { return Err(Error::from_win32()) }; + + Ok(Self(library)) + } + + pub unsafe fn get_proc_address(&self, name: PCSTR) -> Option<*const c_void> { + let addr = unsafe { GetProcAddress(self.0.as_ptr(), name.as_ptr()) }; + + addr.map(|f| f as _) + } +} + +impl Drop for LibraryModule { + fn drop(&mut self) { + unsafe { FreeLibrary(self.0.as_ptr()) }; + } +} diff --git a/src/wrappers/win32/window/handle.rs b/src/wrappers/win32/window/handle.rs index f356af5a..45de8a65 100644 --- a/src/wrappers/win32/window/handle.rs +++ b/src/wrappers/win32/window/handle.rs @@ -1,4 +1,5 @@ use crate::wrappers::win32::style::WindowStyle; +use crate::wrappers::win32::user32::ExtendedUser32; use crate::wrappers::win32::{Dpi, DpiAwarenessContext, Rect}; use crate::PhySize; use std::marker::PhantomData; @@ -8,7 +9,6 @@ use windows::Win32::System::Ole::IDropTarget; use windows_core::{Error, Interface, InterfaceRef, Result, HRESULT}; use windows_sys::Win32::Foundation::{SetLastError, HWND, S_OK}; use windows_sys::Win32::System::Ole::{RegisterDragDrop, RevokeDragDrop}; -use windows_sys::Win32::UI::HiDpi::GetDpiForWindow; use windows_sys::Win32::UI::Input::KeyboardAndMouse::{ GetFocus, ReleaseCapture, SetCapture, SetFocus, TrackMouseEvent, TME_LEAVE, TRACKMOUSEEVENT, }; @@ -86,9 +86,13 @@ impl HWnd<'_> { }) } - pub fn get_dpi(&self) -> Result { + pub fn get_dpi(&self, extended_user32: &ExtendedUser32) -> Result { + let Some(get_dpi_for_window) = extended_user32.get_dpi_for_window else { + return Ok(Dpi::default()); + }; + // SAFETY: This type guarantees the HWND is safe to use. - match unsafe { GetDpiForWindow(self.0) } { + match unsafe { get_dpi_for_window(self.0) } { 0 => Err(Error::from_win32()), dpi => Ok(Dpi(dpi)), } @@ -134,8 +138,10 @@ impl HWnd<'_> { Ok(()) } - pub fn resize_and_activate(&self, client_size: PhySize, window_dpi: Dpi) -> Result<()> { - let dpi_ctx = DpiAwarenessContext::new()?; + pub fn resize_and_activate( + &self, client_size: PhySize, window_dpi: Dpi, user32: &ExtendedUser32, + ) -> Result<()> { + let dpi_ctx = DpiAwarenessContext::new(user32)?; let style = self.get_style()?; let rect = Rect::from(client_size); From 4fb16d11d510f9bc70437dbcd5804de1215fbff4 Mon Sep 17 00:00:00 2001 From: Adrien Prokopowicz <6529475+prokopyl@users.noreply.github.com> Date: Mon, 15 Jun 2026 02:37:02 +0200 Subject: [PATCH 2/2] fix --- src/wrappers/win32/dpi.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/wrappers/win32/dpi.rs b/src/wrappers/win32/dpi.rs index b0a30d3d..77367bf9 100644 --- a/src/wrappers/win32/dpi.rs +++ b/src/wrappers/win32/dpi.rs @@ -28,7 +28,7 @@ pub struct DpiAwarenessContext<'a> { impl<'a> DpiAwarenessContext<'a> { pub fn new(user32: &'a ExtendedUser32) -> Result { let Some(set_thread_dpi_awareness_context) = user32.set_thread_dpi_awareness_context else { - return Ok(Self { previous: DPI_AWARENESS_CONTEXT::default(), user32 }); + return Ok(Self { previous: null_mut(), user32 }); }; let previous =