Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 22 additions & 8 deletions src/win/window.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)]
Expand Down Expand Up @@ -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() {
Expand All @@ -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)?;
}
}

Expand Down Expand Up @@ -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();
Expand Down Expand Up @@ -473,6 +474,8 @@ pub(super) struct WindowState {
handler: RefCell<Option<Box<dyn WindowHandler>>>,
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
Expand All @@ -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(),
Expand All @@ -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)),

Expand Down Expand Up @@ -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(),
}
Expand Down Expand Up @@ -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 {
Expand All @@ -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,
Expand Down
2 changes: 2 additions & 0 deletions src/wrappers/win32.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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};
Expand Down
47 changes: 31 additions & 16 deletions src/wrappers/win32/dpi.rs
Original file line number Diff line number Diff line change
@@ -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);
Expand All @@ -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<Self> {
let mut previous =
unsafe { SetThreadDpiAwarenessContext(DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2) };
impl<'a> DpiAwarenessContext<'a> {
pub fn new(user32: &'a ExtendedUser32) -> Result<Self> {
let Some(set_thread_dpi_awareness_context) = user32.set_thread_dpi_awareness_context else {
return Ok(Self { previous: null_mut(), 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<Rect> {
// 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());
Expand All @@ -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) };
}
}
}
73 changes: 73 additions & 0 deletions src/wrappers/win32/user32.rs
Original file line number Diff line number Diff line change
@@ -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<SetThreadDpiAwarenessContext>,
pub adjust_window_rect_ex_for_dpi: Option<AdjustWindowRectExForDpi>,
pub get_dpi_for_window: Option<GetDpiForWindow>,
}

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<Self, Error> {
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<c_void>);

impl LibraryModule {
pub unsafe fn load(module_name: PCSTR) -> Result<Self, Error> {
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()) };
}
}
16 changes: 11 additions & 5 deletions src/wrappers/win32/window/handle.rs
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -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,
};
Expand Down Expand Up @@ -86,9 +86,13 @@ impl HWnd<'_> {
})
}

pub fn get_dpi(&self) -> Result<Dpi> {
pub fn get_dpi(&self, extended_user32: &ExtendedUser32) -> Result<Dpi> {
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)),
}
Expand Down Expand Up @@ -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);
Expand Down