mirror of
https://github.com/italicsjenga/winit-sonoma-fix.git
synced 2024-12-23 13:51:30 +11:00
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
|
@ -144,6 +144,9 @@ and `WindowEvent::HoveredFile`.
|
|||
- On macOS, change `WindowExtMacOS::request_user_attention()` to take an `enum` instead of a `bool`.
|
||||
|
||||
# 0.20.0 Alpha 1 (2019-06-21)
|
||||
- On Windows, fix window rectangle not getting set correctly on high-DPI systems.
|
||||
|
||||
# 0.20.0 Alpha 1
|
||||
|
||||
- Changes below are considered **breaking**.
|
||||
- Change all occurrences of `EventsLoop` to `EventLoop`.
|
||||
|
|
|
@ -5,7 +5,7 @@ fn main() {
|
|||
use std::{collections::HashMap, sync::mpsc, thread, time::Duration};
|
||||
|
||||
use winit::{
|
||||
dpi::{PhysicalPosition, PhysicalSize},
|
||||
dpi::{PhysicalPosition, PhysicalSize},
|
||||
event::{ElementState, Event, KeyboardInput, VirtualKeyCode, WindowEvent},
|
||||
event_loop::{ControlFlow, EventLoop},
|
||||
window::{CursorIcon, Fullscreen, WindowBuilder},
|
||||
|
|
10
src/event.rs
10
src/event.rs
|
@ -333,7 +333,15 @@ impl<'a> WindowEvent<'a> {
|
|||
HoveredFileCancelled => Some(HoveredFileCancelled),
|
||||
ReceivedCharacter(c) => Some(ReceivedCharacter(c)),
|
||||
Focused(focused) => Some(Focused(focused)),
|
||||
KeyboardInput { device_id, input, is_synthetic } => Some(KeyboardInput { device_id, input, is_synthetic }),
|
||||
KeyboardInput {
|
||||
device_id,
|
||||
input,
|
||||
is_synthetic,
|
||||
} => Some(KeyboardInput {
|
||||
device_id,
|
||||
input,
|
||||
is_synthetic,
|
||||
}),
|
||||
CursorMoved {
|
||||
device_id,
|
||||
position,
|
||||
|
|
|
@ -2,54 +2,30 @@
|
|||
|
||||
use std::sync::Once;
|
||||
|
||||
use crate::platform_impl::platform::util::{
|
||||
ENABLE_NON_CLIENT_DPI_SCALING, GET_DPI_FOR_MONITOR, GET_DPI_FOR_WINDOW, SET_PROCESS_DPI_AWARE,
|
||||
SET_PROCESS_DPI_AWARENESS, SET_PROCESS_DPI_AWARENESS_CONTEXT,
|
||||
};
|
||||
use winapi::{
|
||||
shared::{
|
||||
minwindef::{BOOL, FALSE, UINT},
|
||||
minwindef::FALSE,
|
||||
windef::{DPI_AWARENESS_CONTEXT, DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE, HMONITOR, HWND},
|
||||
winerror::S_OK,
|
||||
},
|
||||
um::{
|
||||
shellscalingapi::{
|
||||
MDT_EFFECTIVE_DPI, MONITOR_DPI_TYPE, PROCESS_DPI_AWARENESS,
|
||||
PROCESS_PER_MONITOR_DPI_AWARE,
|
||||
},
|
||||
shellscalingapi::{MDT_EFFECTIVE_DPI, PROCESS_PER_MONITOR_DPI_AWARE},
|
||||
wingdi::{GetDeviceCaps, LOGPIXELSX},
|
||||
winnt::HRESULT,
|
||||
winuser::{self, MONITOR_DEFAULTTONEAREST},
|
||||
},
|
||||
};
|
||||
|
||||
const DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2: DPI_AWARENESS_CONTEXT = -4isize as _;
|
||||
|
||||
type SetProcessDPIAware = unsafe extern "system" fn() -> BOOL;
|
||||
type SetProcessDpiAwareness = unsafe extern "system" fn(value: PROCESS_DPI_AWARENESS) -> HRESULT;
|
||||
type SetProcessDpiAwarenessContext =
|
||||
unsafe extern "system" fn(value: DPI_AWARENESS_CONTEXT) -> BOOL;
|
||||
type GetDpiForWindow = unsafe extern "system" fn(hwnd: HWND) -> UINT;
|
||||
type GetDpiForMonitor = unsafe extern "system" fn(
|
||||
hmonitor: HMONITOR,
|
||||
dpi_type: MONITOR_DPI_TYPE,
|
||||
dpi_x: *mut UINT,
|
||||
dpi_y: *mut UINT,
|
||||
) -> HRESULT;
|
||||
type EnableNonClientDpiScaling = unsafe extern "system" fn(hwnd: HWND) -> BOOL;
|
||||
|
||||
lazy_static! {
|
||||
static ref GET_DPI_FOR_WINDOW: Option<GetDpiForWindow> =
|
||||
get_function!("user32.dll", GetDpiForWindow);
|
||||
static ref GET_DPI_FOR_MONITOR: Option<GetDpiForMonitor> =
|
||||
get_function!("shcore.dll", GetDpiForMonitor);
|
||||
static ref ENABLE_NON_CLIENT_DPI_SCALING: Option<EnableNonClientDpiScaling> =
|
||||
get_function!("user32.dll", EnableNonClientDpiScaling);
|
||||
}
|
||||
|
||||
pub fn become_dpi_aware() {
|
||||
static ENABLE_DPI_AWARENESS: Once = Once::new();
|
||||
ENABLE_DPI_AWARENESS.call_once(|| {
|
||||
unsafe {
|
||||
if let Some(SetProcessDpiAwarenessContext) =
|
||||
get_function!("user32.dll", SetProcessDpiAwarenessContext)
|
||||
{
|
||||
if let Some(SetProcessDpiAwarenessContext) = *SET_PROCESS_DPI_AWARENESS_CONTEXT {
|
||||
// We are on Windows 10 Anniversary Update (1607) or later.
|
||||
if SetProcessDpiAwarenessContext(DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2)
|
||||
== FALSE
|
||||
|
@ -58,13 +34,10 @@ pub fn become_dpi_aware() {
|
|||
// V1 if we can't set V2.
|
||||
SetProcessDpiAwarenessContext(DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE);
|
||||
}
|
||||
} else if let Some(SetProcessDpiAwareness) =
|
||||
get_function!("shcore.dll", SetProcessDpiAwareness)
|
||||
{
|
||||
} else if let Some(SetProcessDpiAwareness) = *SET_PROCESS_DPI_AWARENESS {
|
||||
// We are on Windows 8.1 or later.
|
||||
SetProcessDpiAwareness(PROCESS_PER_MONITOR_DPI_AWARE);
|
||||
} else if let Some(SetProcessDPIAware) = get_function!("user32.dll", SetProcessDPIAware)
|
||||
{
|
||||
} else if let Some(SetProcessDPIAware) = *SET_PROCESS_DPI_AWARE {
|
||||
// We are on Vista or later.
|
||||
SetProcessDPIAware();
|
||||
}
|
||||
|
|
|
@ -36,7 +36,7 @@ use winapi::{
|
|||
},
|
||||
um::{
|
||||
commctrl, libloaderapi, ole2, processthreadsapi, winbase,
|
||||
winnt::{HANDLE, LPCSTR, SHORT},
|
||||
winnt::{HANDLE, LPCSTR, SHORT},
|
||||
winuser,
|
||||
},
|
||||
};
|
||||
|
@ -48,15 +48,12 @@ use crate::{
|
|||
event_loop::{ControlFlow, EventLoopClosed, EventLoopWindowTarget as RootELW},
|
||||
platform_impl::platform::{
|
||||
dark_mode::try_dark_mode,
|
||||
dpi::{
|
||||
become_dpi_aware, dpi_to_scale_factor, enable_non_client_dpi_scaling,
|
||||
},
|
||||
dpi::{become_dpi_aware, dpi_to_scale_factor, enable_non_client_dpi_scaling},
|
||||
drop_handler::FileDropHandler,
|
||||
event::{
|
||||
self, handle_extended_keys, process_key_params, vkey_to_winit_vkey, ModifiersStateSide,
|
||||
},
|
||||
monitor, raw_input, util,
|
||||
window::adjust_size,
|
||||
window_state::{CursorFlags, WindowFlags, WindowState},
|
||||
wrap_device_id, WindowId, DEVICE_ID,
|
||||
},
|
||||
|
@ -1394,11 +1391,9 @@ unsafe extern "system" fn public_window_callback<T: 'static>(
|
|||
let window_state = subclass_input.window_state.lock();
|
||||
|
||||
if window_state.min_size.is_some() || window_state.max_size.is_some() {
|
||||
let style = winuser::GetWindowLongA(window, winuser::GWL_STYLE) as DWORD;
|
||||
let ex_style = winuser::GetWindowLongA(window, winuser::GWL_EXSTYLE) as DWORD;
|
||||
if let Some(min_size) = window_state.min_size {
|
||||
let min_size = min_size.to_physical(window_state.dpi_factor);
|
||||
let (width, height) = adjust_size(min_size, style, ex_style);
|
||||
let (width, height): (u32, u32) = util::adjust_size(window, min_size).into();
|
||||
(*mmi).ptMinTrackSize = POINT {
|
||||
x: width as i32,
|
||||
y: height as i32,
|
||||
|
@ -1406,7 +1401,7 @@ unsafe extern "system" fn public_window_callback<T: 'static>(
|
|||
}
|
||||
if let Some(max_size) = window_state.max_size {
|
||||
let max_size = max_size.to_physical(window_state.dpi_factor);
|
||||
let (width, height) = adjust_size(max_size, style, ex_style);
|
||||
let (width, height): (u32, u32) = util::adjust_size(window, max_size).into();
|
||||
(*mmi).ptMaxTrackSize = POINT {
|
||||
x: width as i32,
|
||||
y: height as i32,
|
||||
|
@ -1428,10 +1423,11 @@ unsafe extern "system" fn public_window_callback<T: 'static>(
|
|||
// https://msdn.microsoft.com/en-us/library/windows/desktop/dn312083(v=vs.85).aspx
|
||||
let new_dpi_x = u32::from(LOWORD(wparam as DWORD));
|
||||
let new_dpi_factor = dpi_to_scale_factor(new_dpi_x);
|
||||
let old_dpi_factor: f64;
|
||||
|
||||
let allow_resize = {
|
||||
let mut window_state = subclass_input.window_state.lock();
|
||||
let old_dpi_factor = window_state.dpi_factor;
|
||||
old_dpi_factor = window_state.dpi_factor;
|
||||
window_state.dpi_factor = new_dpi_factor;
|
||||
|
||||
new_dpi_factor != old_dpi_factor && window_state.fullscreen.is_none()
|
||||
|
@ -1468,10 +1464,26 @@ unsafe extern "system" fn public_window_callback<T: 'static>(
|
|||
margins_vertical = (margin_bottom + margin_top) as u32;
|
||||
}
|
||||
|
||||
let physical_inner_rect = PhysicalSize::new(
|
||||
(rect.right - rect.left) as u32 - margins_horizontal,
|
||||
(rect.bottom - rect.top) as u32 - margins_vertical,
|
||||
);
|
||||
// Use the rect suggested by Windows
|
||||
// let physical_inner_rect = PhysicalSize::new(
|
||||
// (rect.right - rect.left) as u32 - margins_horizontal,
|
||||
// (rect.bottom - rect.top) as u32 - margins_vertical,
|
||||
// );
|
||||
|
||||
// We calculate our own rect because the default suggested rect doesn't do a great job
|
||||
// of preserving the window's logical size.
|
||||
let physical_inner_rect = {
|
||||
let mut current_rect = mem::zeroed();
|
||||
winuser::GetClientRect(window, &mut current_rect);
|
||||
|
||||
let client_rect = PhysicalSize::new(
|
||||
(current_rect.right - current_rect.left) as u32,
|
||||
(current_rect.bottom - current_rect.top) as u32,
|
||||
);
|
||||
client_rect
|
||||
.to_logical(old_dpi_factor)
|
||||
.to_physical(new_dpi_factor)
|
||||
};
|
||||
|
||||
// `allow_resize` prevents us from re-applying DPI adjustment to the restored size after
|
||||
// exiting fullscreen (the restored size is already DPI adjusted).
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -14,7 +14,7 @@ use std::{
|
|||
use winapi::{
|
||||
ctypes::c_int,
|
||||
shared::{
|
||||
minwindef::{DWORD, HINSTANCE, UINT},
|
||||
minwindef::{HINSTANCE, UINT},
|
||||
windef::{HWND, POINT, RECT},
|
||||
},
|
||||
um::{
|
||||
|
@ -37,9 +37,7 @@ use crate::{
|
|||
dark_mode::try_dark_mode,
|
||||
dpi::{dpi_to_scale_factor, hwnd_dpi},
|
||||
drop_handler::FileDropHandler,
|
||||
event_loop::{
|
||||
self, EventLoopWindowTarget, DESTROY_MSG_ID,
|
||||
},
|
||||
event_loop::{self, EventLoopWindowTarget, DESTROY_MSG_ID},
|
||||
icon::{self, IconType, WinIcon},
|
||||
monitor, util,
|
||||
window_state::{CursorFlags, SavedWindow, WindowFlags, WindowState},
|
||||
|
@ -669,22 +667,6 @@ pub struct WindowWrapper(HWND);
|
|||
unsafe impl Sync for WindowWrapper {}
|
||||
unsafe impl Send for WindowWrapper {}
|
||||
|
||||
pub unsafe fn adjust_size(
|
||||
physical_size: PhysicalSize,
|
||||
style: DWORD,
|
||||
ex_style: DWORD,
|
||||
) -> (LONG, LONG) {
|
||||
let (width, height): (u32, u32) = physical_size.into();
|
||||
let mut rect = RECT {
|
||||
left: 0,
|
||||
right: width as LONG,
|
||||
top: 0,
|
||||
bottom: height as LONG,
|
||||
};
|
||||
winuser::AdjustWindowRectEx(&mut rect, style, 0, ex_style);
|
||||
(rect.right - rect.left, rect.bottom - rect.top)
|
||||
}
|
||||
|
||||
unsafe fn init<T: 'static>(
|
||||
mut attributes: WindowAttributes,
|
||||
pl_attribs: PlatformSpecificWindowBuilderAttributes,
|
||||
|
|
Loading…
Reference in a new issue