On Windows, make AdjustRect calls DPI-aware when possible (#1015)
* Use AdjustWidowRectExForDPI when available * Prioritize presevering logical size when handling WM_DPICHANGED * Format * Add changelog entry
This commit is contained in:
parent
f379d069b9
commit
6ffd78767f
7 changed files with 143 additions and 107 deletions
|
|
@ -6,51 +6,22 @@ use std::{
|
|||
sync::atomic::{AtomicBool, Ordering},
|
||||
};
|
||||
|
||||
use crate::window::CursorIcon;
|
||||
use crate::{dpi::PhysicalSize, window::CursorIcon};
|
||||
use winapi::{
|
||||
ctypes::wchar_t,
|
||||
shared::{
|
||||
minwindef::{BOOL, DWORD},
|
||||
windef::{HWND, RECT},
|
||||
minwindef::{BOOL, DWORD, UINT},
|
||||
windef::{DPI_AWARENESS_CONTEXT, HMONITOR, HWND, LPRECT, RECT},
|
||||
},
|
||||
um::{
|
||||
libloaderapi::{GetProcAddress, LoadLibraryA},
|
||||
shellscalingapi::{MONITOR_DPI_TYPE, PROCESS_DPI_AWARENESS},
|
||||
winbase::lstrlenW,
|
||||
winnt::LPCSTR,
|
||||
winnt::{HRESULT, LONG, LPCSTR},
|
||||
winuser,
|
||||
},
|
||||
};
|
||||
|
||||
// Helper function to dynamically load function pointer.
|
||||
// `library` and `function` must be zero-terminated.
|
||||
pub(super) fn get_function_impl(library: &str, function: &str) -> Option<*const c_void> {
|
||||
assert_eq!(library.chars().last(), Some('\0'));
|
||||
assert_eq!(function.chars().last(), Some('\0'));
|
||||
|
||||
// Library names we will use are ASCII so we can use the A version to avoid string conversion.
|
||||
let module = unsafe { LoadLibraryA(library.as_ptr() as LPCSTR) };
|
||||
if module.is_null() {
|
||||
return None;
|
||||
}
|
||||
|
||||
let function_ptr = unsafe { GetProcAddress(module, function.as_ptr() as LPCSTR) };
|
||||
if function_ptr.is_null() {
|
||||
return None;
|
||||
}
|
||||
|
||||
Some(function_ptr as _)
|
||||
}
|
||||
|
||||
macro_rules! get_function {
|
||||
($lib:expr, $func:ident) => {
|
||||
crate::platform_impl::platform::util::get_function_impl(
|
||||
concat!($lib, '\0'),
|
||||
concat!(stringify!($func), '\0'),
|
||||
)
|
||||
.map(|f| unsafe { std::mem::transmute::<*const _, $func>(f) })
|
||||
};
|
||||
}
|
||||
|
||||
pub fn has_flag<T>(bitset: T, flag: T) -> bool
|
||||
where
|
||||
T: Copy + PartialEq + BitAnd<T, Output = T>,
|
||||
|
|
@ -105,6 +76,18 @@ pub fn get_client_rect(hwnd: HWND) -> Result<RECT, io::Error> {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn adjust_size(hwnd: HWND, size: PhysicalSize) -> PhysicalSize {
|
||||
let (width, height): (u32, u32) = size.into();
|
||||
let rect = RECT {
|
||||
left: 0,
|
||||
right: width as LONG,
|
||||
top: 0,
|
||||
bottom: height as LONG,
|
||||
};
|
||||
let rect = adjust_window_rect(hwnd, rect).unwrap_or(rect);
|
||||
PhysicalSize::new((rect.right - rect.left) as _, (rect.bottom - rect.top) as _)
|
||||
}
|
||||
|
||||
pub fn adjust_window_rect(hwnd: HWND, rect: RECT) -> Option<RECT> {
|
||||
unsafe {
|
||||
let style = winuser::GetWindowLongW(hwnd, winuser::GWL_STYLE);
|
||||
|
|
@ -124,7 +107,14 @@ pub fn adjust_window_rect_with_styles(
|
|||
*r = rect;
|
||||
|
||||
let b_menu = !winuser::GetMenu(hwnd).is_null() as BOOL;
|
||||
winuser::AdjustWindowRectEx(r, style as _, b_menu, style_ex as _)
|
||||
if let (Some(get_dpi_for_window), Some(adjust_window_rect_ex_for_dpi)) =
|
||||
(*GET_DPI_FOR_WINDOW, *ADJUST_WINDOW_RECT_EX_FOR_DPI)
|
||||
{
|
||||
let dpi = get_dpi_for_window(hwnd);
|
||||
adjust_window_rect_ex_for_dpi(r, style as _, b_menu, style_ex as _, dpi)
|
||||
} else {
|
||||
winuser::AdjustWindowRectEx(r, style as _, b_menu, style_ex as _)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
@ -206,3 +196,71 @@ impl CursorIcon {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Helper function to dynamically load function pointer.
|
||||
// `library` and `function` must be zero-terminated.
|
||||
pub(super) fn get_function_impl(library: &str, function: &str) -> Option<*const c_void> {
|
||||
assert_eq!(library.chars().last(), Some('\0'));
|
||||
assert_eq!(function.chars().last(), Some('\0'));
|
||||
|
||||
// Library names we will use are ASCII so we can use the A version to avoid string conversion.
|
||||
let module = unsafe { LoadLibraryA(library.as_ptr() as LPCSTR) };
|
||||
if module.is_null() {
|
||||
return None;
|
||||
}
|
||||
|
||||
let function_ptr = unsafe { GetProcAddress(module, function.as_ptr() as LPCSTR) };
|
||||
if function_ptr.is_null() {
|
||||
return None;
|
||||
}
|
||||
|
||||
Some(function_ptr as _)
|
||||
}
|
||||
|
||||
macro_rules! get_function {
|
||||
($lib:expr, $func:ident) => {
|
||||
crate::platform_impl::platform::util::get_function_impl(
|
||||
concat!($lib, '\0'),
|
||||
concat!(stringify!($func), '\0'),
|
||||
)
|
||||
.map(|f| unsafe { std::mem::transmute::<*const _, $func>(f) })
|
||||
};
|
||||
}
|
||||
|
||||
pub type SetProcessDPIAware = unsafe extern "system" fn() -> BOOL;
|
||||
pub type SetProcessDpiAwareness =
|
||||
unsafe extern "system" fn(value: PROCESS_DPI_AWARENESS) -> HRESULT;
|
||||
pub type SetProcessDpiAwarenessContext =
|
||||
unsafe extern "system" fn(value: DPI_AWARENESS_CONTEXT) -> BOOL;
|
||||
pub type GetDpiForWindow = unsafe extern "system" fn(hwnd: HWND) -> UINT;
|
||||
pub type GetDpiForMonitor = unsafe extern "system" fn(
|
||||
hmonitor: HMONITOR,
|
||||
dpi_type: MONITOR_DPI_TYPE,
|
||||
dpi_x: *mut UINT,
|
||||
dpi_y: *mut UINT,
|
||||
) -> HRESULT;
|
||||
pub type EnableNonClientDpiScaling = unsafe extern "system" fn(hwnd: HWND) -> BOOL;
|
||||
pub type AdjustWindowRectExForDpi = unsafe extern "system" fn(
|
||||
rect: LPRECT,
|
||||
dwStyle: DWORD,
|
||||
bMenu: BOOL,
|
||||
dwExStyle: DWORD,
|
||||
dpi: UINT,
|
||||
) -> BOOL;
|
||||
|
||||
lazy_static! {
|
||||
pub static ref GET_DPI_FOR_WINDOW: Option<GetDpiForWindow> =
|
||||
get_function!("user32.dll", GetDpiForWindow);
|
||||
pub static ref ADJUST_WINDOW_RECT_EX_FOR_DPI: Option<AdjustWindowRectExForDpi> =
|
||||
get_function!("user32.dll", AdjustWindowRectExForDpi);
|
||||
pub static ref GET_DPI_FOR_MONITOR: Option<GetDpiForMonitor> =
|
||||
get_function!("shcore.dll", GetDpiForMonitor);
|
||||
pub static ref ENABLE_NON_CLIENT_DPI_SCALING: Option<EnableNonClientDpiScaling> =
|
||||
get_function!("user32.dll", EnableNonClientDpiScaling);
|
||||
pub static ref SET_PROCESS_DPI_AWARENESS_CONTEXT: Option<SetProcessDpiAwarenessContext> =
|
||||
get_function!("user32.dll", SetProcessDpiAwarenessContext);
|
||||
pub static ref SET_PROCESS_DPI_AWARENESS: Option<SetProcessDpiAwareness> =
|
||||
get_function!("shcore.dll", SetProcessDpiAwareness);
|
||||
pub static ref SET_PROCESS_DPI_AWARE: Option<SetProcessDPIAware> =
|
||||
get_function!("user32.dll", SetProcessDPIAware);
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue