diff --git a/CHANGELOG.md b/CHANGELOG.md index 55033c9c..43484cd6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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`. diff --git a/examples/multithreaded.rs b/examples/multithreaded.rs index 8ac3bbaa..955cdcc9 100644 --- a/examples/multithreaded.rs +++ b/examples/multithreaded.rs @@ -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}, diff --git a/src/event.rs b/src/event.rs index 85899d3e..a139f415 100644 --- a/src/event.rs +++ b/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, diff --git a/src/platform_impl/windows/dpi.rs b/src/platform_impl/windows/dpi.rs index d76c4344..f7ec4391 100644 --- a/src/platform_impl/windows/dpi.rs +++ b/src/platform_impl/windows/dpi.rs @@ -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 = - get_function!("user32.dll", GetDpiForWindow); - static ref GET_DPI_FOR_MONITOR: Option = - get_function!("shcore.dll", GetDpiForMonitor); - static ref ENABLE_NON_CLIENT_DPI_SCALING: Option = - 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(); } diff --git a/src/platform_impl/windows/event_loop.rs b/src/platform_impl/windows/event_loop.rs index 387b0fd4..32876a2c 100644 --- a/src/platform_impl/windows/event_loop.rs +++ b/src/platform_impl/windows/event_loop.rs @@ -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( 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( } 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( // 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( 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). diff --git a/src/platform_impl/windows/util.rs b/src/platform_impl/windows/util.rs index 996aaf8d..36fc2253 100644 --- a/src/platform_impl/windows/util.rs +++ b/src/platform_impl/windows/util.rs @@ -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(bitset: T, flag: T) -> bool where T: Copy + PartialEq + BitAnd, @@ -105,6 +76,18 @@ pub fn get_client_rect(hwnd: HWND) -> Result { } } +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 { 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 = + get_function!("user32.dll", GetDpiForWindow); + pub static ref ADJUST_WINDOW_RECT_EX_FOR_DPI: Option = + get_function!("user32.dll", AdjustWindowRectExForDpi); + pub static ref GET_DPI_FOR_MONITOR: Option = + get_function!("shcore.dll", GetDpiForMonitor); + pub static ref ENABLE_NON_CLIENT_DPI_SCALING: Option = + get_function!("user32.dll", EnableNonClientDpiScaling); + pub static ref SET_PROCESS_DPI_AWARENESS_CONTEXT: Option = + get_function!("user32.dll", SetProcessDpiAwarenessContext); + pub static ref SET_PROCESS_DPI_AWARENESS: Option = + get_function!("shcore.dll", SetProcessDpiAwareness); + pub static ref SET_PROCESS_DPI_AWARE: Option = + get_function!("user32.dll", SetProcessDPIAware); +} diff --git a/src/platform_impl/windows/window.rs b/src/platform_impl/windows/window.rs index 3faf0293..481ccdcc 100644 --- a/src/platform_impl/windows/window.rs +++ b/src/platform_impl/windows/window.rs @@ -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( mut attributes: WindowAttributes, pl_attribs: PlatformSpecificWindowBuilderAttributes,