win32: disable DPI re-adjustments on Windows 11
For earlier Windows 10 builds (pre-22000), a workaround was necessary to fix dragging window onto a monitor with different DPI. This commit makes the old DPI workaround to only apply conditionally on affected Windows versions. Fixes #4041.
This commit is contained in:
parent
a4af50ec13
commit
488c036a05
4 changed files with 154 additions and 132 deletions
|
|
@ -5,9 +5,8 @@ use std::{ffi::c_void, ptr};
|
||||||
|
|
||||||
use windows_sys::core::{PCSTR, PCWSTR};
|
use windows_sys::core::{PCSTR, PCWSTR};
|
||||||
use windows_sys::w;
|
use windows_sys::w;
|
||||||
use windows_sys::Win32::Foundation::{BOOL, HWND, LPARAM, NTSTATUS, S_OK, WPARAM};
|
use windows_sys::Win32::Foundation::{BOOL, HWND, LPARAM, S_OK, WPARAM};
|
||||||
use windows_sys::Win32::System::LibraryLoader::{GetProcAddress, LoadLibraryA};
|
use windows_sys::Win32::System::LibraryLoader::{GetProcAddress, LoadLibraryA};
|
||||||
use windows_sys::Win32::System::SystemInformation::OSVERSIONINFOW;
|
|
||||||
use windows_sys::Win32::UI::Accessibility::{HCF_HIGHCONTRASTON, HIGHCONTRASTA};
|
use windows_sys::Win32::UI::Accessibility::{HCF_HIGHCONTRASTON, HIGHCONTRASTA};
|
||||||
use windows_sys::Win32::UI::Controls::SetWindowTheme;
|
use windows_sys::Win32::UI::Controls::SetWindowTheme;
|
||||||
use windows_sys::Win32::UI::Input::KeyboardAndMouse::GetActiveWindow;
|
use windows_sys::Win32::UI::Input::KeyboardAndMouse::GetActiveWindow;
|
||||||
|
|
@ -17,39 +16,10 @@ use windows_sys::Win32::UI::WindowsAndMessaging::{
|
||||||
use winit_core::window::Theme;
|
use winit_core::window::Theme;
|
||||||
|
|
||||||
use super::util;
|
use super::util;
|
||||||
|
|
||||||
static WIN10_BUILD_VERSION: LazyLock<Option<u32>> = LazyLock::new(|| {
|
|
||||||
type RtlGetVersion = unsafe extern "system" fn(*mut OSVERSIONINFOW) -> NTSTATUS;
|
|
||||||
let handle = get_function!("ntdll.dll", RtlGetVersion);
|
|
||||||
|
|
||||||
if let Some(rtl_get_version) = handle {
|
|
||||||
unsafe {
|
|
||||||
let mut vi = OSVERSIONINFOW {
|
|
||||||
dwOSVersionInfoSize: 0,
|
|
||||||
dwMajorVersion: 0,
|
|
||||||
dwMinorVersion: 0,
|
|
||||||
dwBuildNumber: 0,
|
|
||||||
dwPlatformId: 0,
|
|
||||||
szCSDVersion: [0; 128],
|
|
||||||
};
|
|
||||||
|
|
||||||
let status = (rtl_get_version)(&mut vi);
|
|
||||||
|
|
||||||
if status >= 0 && vi.dwMajorVersion == 10 && vi.dwMinorVersion == 0 {
|
|
||||||
Some(vi.dwBuildNumber)
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
static DARK_MODE_SUPPORTED: LazyLock<bool> = LazyLock::new(|| {
|
static DARK_MODE_SUPPORTED: LazyLock<bool> = LazyLock::new(|| {
|
||||||
// We won't try to do anything for windows versions < 17763
|
// We won't try to do anything for windows versions < 17763
|
||||||
// (Windows 10 October 2018 update)
|
// (Windows 10 October 2018 update)
|
||||||
match *WIN10_BUILD_VERSION {
|
match *util::WIN10_BUILD_VERSION {
|
||||||
Some(v) => v >= 17763,
|
Some(v) => v >= 17763,
|
||||||
None => false,
|
None => false,
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -88,7 +88,7 @@ use crate::ime::ImeContext;
|
||||||
use crate::keyboard::KeyEventBuilder;
|
use crate::keyboard::KeyEventBuilder;
|
||||||
use crate::keyboard_layout::LAYOUT_CACHE;
|
use crate::keyboard_layout::LAYOUT_CACHE;
|
||||||
use crate::monitor::{self, MonitorHandle};
|
use crate::monitor::{self, MonitorHandle};
|
||||||
use crate::util::wrap_device_id;
|
use crate::util::{wrap_device_id, WIN10_BUILD_VERSION};
|
||||||
use crate::window::{InitData, Window};
|
use crate::window::{InitData, Window};
|
||||||
use crate::window_state::{CursorFlags, ImeState, WindowFlags, WindowState};
|
use crate::window_state::{CursorFlags, ImeState, WindowFlags, WindowState};
|
||||||
use crate::{raw_input, util};
|
use crate::{raw_input, util};
|
||||||
|
|
@ -2346,105 +2346,22 @@ unsafe fn public_window_callback_inner(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let new_outer_rect: RECT;
|
let new_outer_rect: RECT = if WIN10_BUILD_VERSION.is_some_and(|version| version < 22000)
|
||||||
{
|
{
|
||||||
let suggested_ul =
|
// Apply Windows 10-specific DPI adjustment workaround
|
||||||
(suggested_rect.left + margin_left, suggested_rect.top + margin_top);
|
apply_win10_dpi_adjustment(
|
||||||
|
window,
|
||||||
let mut conservative_rect = RECT {
|
suggested_rect,
|
||||||
left: suggested_ul.0,
|
margin_left,
|
||||||
top: suggested_ul.1,
|
margin_top,
|
||||||
right: suggested_ul.0 + new_physical_surface_size.width as i32,
|
new_physical_surface_size,
|
||||||
bottom: suggested_ul.1 + new_physical_surface_size.height as i32,
|
window_flags,
|
||||||
};
|
dragging_window,
|
||||||
|
)
|
||||||
conservative_rect = window_flags
|
} else {
|
||||||
.adjust_rect(window, conservative_rect)
|
// The suggested position is fine w/o adjustment on Windows 11+
|
||||||
.unwrap_or(conservative_rect);
|
suggested_rect
|
||||||
|
};
|
||||||
// If we're dragging the window, offset the window so that the cursor's
|
|
||||||
// relative horizontal position in the title bar is preserved.
|
|
||||||
if dragging_window {
|
|
||||||
let bias = {
|
|
||||||
let cursor_pos = {
|
|
||||||
let mut pos = unsafe { mem::zeroed() };
|
|
||||||
unsafe { GetCursorPos(&mut pos) };
|
|
||||||
pos
|
|
||||||
};
|
|
||||||
let suggested_cursor_horizontal_ratio = (cursor_pos.x - suggested_rect.left)
|
|
||||||
as f64
|
|
||||||
/ (suggested_rect.right - suggested_rect.left) as f64;
|
|
||||||
|
|
||||||
(cursor_pos.x
|
|
||||||
- (suggested_cursor_horizontal_ratio
|
|
||||||
* (conservative_rect.right - conservative_rect.left) as f64)
|
|
||||||
as i32)
|
|
||||||
- conservative_rect.left
|
|
||||||
};
|
|
||||||
conservative_rect.left += bias;
|
|
||||||
conservative_rect.right += bias;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check to see if the new window rect is on the monitor with the new DPI factor.
|
|
||||||
// If it isn't, offset the window so that it is.
|
|
||||||
let new_dpi_monitor = unsafe { MonitorFromWindow(window, MONITOR_DEFAULTTONULL) };
|
|
||||||
let conservative_rect_monitor =
|
|
||||||
unsafe { MonitorFromRect(&conservative_rect, MONITOR_DEFAULTTONULL) };
|
|
||||||
new_outer_rect = if conservative_rect_monitor == new_dpi_monitor {
|
|
||||||
conservative_rect
|
|
||||||
} else {
|
|
||||||
let get_monitor_rect = |monitor| {
|
|
||||||
let mut monitor_info = MONITORINFO {
|
|
||||||
cbSize: mem::size_of::<MONITORINFO>() as _,
|
|
||||||
..unsafe { mem::zeroed() }
|
|
||||||
};
|
|
||||||
unsafe { GetMonitorInfoW(monitor, &mut monitor_info) };
|
|
||||||
monitor_info.rcMonitor
|
|
||||||
};
|
|
||||||
let wrong_monitor = conservative_rect_monitor;
|
|
||||||
let wrong_monitor_rect = get_monitor_rect(wrong_monitor);
|
|
||||||
let new_monitor_rect = get_monitor_rect(new_dpi_monitor);
|
|
||||||
|
|
||||||
// The direction to nudge the window in to get the window onto the monitor with
|
|
||||||
// the new DPI factor. We calculate this by seeing which monitor edges are
|
|
||||||
// shared and nudging away from the wrong monitor based on those.
|
|
||||||
#[allow(clippy::bool_to_int_with_if)]
|
|
||||||
let delta_nudge_to_dpi_monitor = (
|
|
||||||
if wrong_monitor_rect.left == new_monitor_rect.right {
|
|
||||||
-1
|
|
||||||
} else if wrong_monitor_rect.right == new_monitor_rect.left {
|
|
||||||
1
|
|
||||||
} else {
|
|
||||||
0
|
|
||||||
},
|
|
||||||
if wrong_monitor_rect.bottom == new_monitor_rect.top {
|
|
||||||
1
|
|
||||||
} else if wrong_monitor_rect.top == new_monitor_rect.bottom {
|
|
||||||
-1
|
|
||||||
} else {
|
|
||||||
0
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
let abort_after_iterations = new_monitor_rect.right - new_monitor_rect.left
|
|
||||||
+ new_monitor_rect.bottom
|
|
||||||
- new_monitor_rect.top;
|
|
||||||
for _ in 0..abort_after_iterations {
|
|
||||||
conservative_rect.left += delta_nudge_to_dpi_monitor.0;
|
|
||||||
conservative_rect.right += delta_nudge_to_dpi_monitor.0;
|
|
||||||
conservative_rect.top += delta_nudge_to_dpi_monitor.1;
|
|
||||||
conservative_rect.bottom += delta_nudge_to_dpi_monitor.1;
|
|
||||||
|
|
||||||
if unsafe { MonitorFromRect(&conservative_rect, MONITOR_DEFAULTTONULL) }
|
|
||||||
== new_dpi_monitor
|
|
||||||
{
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
conservative_rect
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
unsafe {
|
unsafe {
|
||||||
SetWindowPos(
|
SetWindowPos(
|
||||||
|
|
@ -2669,3 +2586,108 @@ fn get_pointer_move_kind(
|
||||||
PointerMoveKind::None
|
PointerMoveKind::None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Apply Windows 10-specific DPI adjustment workaround for window positioning.
|
||||||
|
/// This fixes DPI switching issues on older Windows 10 but should not be applied on Windows 11+
|
||||||
|
/// where it would break DPI switching.
|
||||||
|
fn apply_win10_dpi_adjustment(
|
||||||
|
window: HWND,
|
||||||
|
suggested_rect: RECT,
|
||||||
|
margin_left: i32,
|
||||||
|
margin_top: i32,
|
||||||
|
new_physical_surface_size: PhysicalSize<u32>,
|
||||||
|
window_flags: WindowFlags,
|
||||||
|
dragging_window: bool,
|
||||||
|
) -> RECT {
|
||||||
|
let suggested_ul = (suggested_rect.left + margin_left, suggested_rect.top + margin_top);
|
||||||
|
|
||||||
|
let mut conservative_rect = RECT {
|
||||||
|
left: suggested_ul.0,
|
||||||
|
top: suggested_ul.1,
|
||||||
|
right: suggested_ul.0 + new_physical_surface_size.width as i32,
|
||||||
|
bottom: suggested_ul.1 + new_physical_surface_size.height as i32,
|
||||||
|
};
|
||||||
|
|
||||||
|
conservative_rect =
|
||||||
|
window_flags.adjust_rect(window, conservative_rect).unwrap_or(conservative_rect);
|
||||||
|
|
||||||
|
// If we're dragging the window, offset the window so that the cursor's
|
||||||
|
// relative horizontal position in the title bar is preserved.
|
||||||
|
if dragging_window {
|
||||||
|
let bias = {
|
||||||
|
let cursor_pos = {
|
||||||
|
let mut pos = unsafe { mem::zeroed() };
|
||||||
|
unsafe { GetCursorPos(&mut pos) };
|
||||||
|
pos
|
||||||
|
};
|
||||||
|
let suggested_cursor_horizontal_ratio = (cursor_pos.x - suggested_rect.left) as f64
|
||||||
|
/ (suggested_rect.right - suggested_rect.left) as f64;
|
||||||
|
|
||||||
|
(cursor_pos.x
|
||||||
|
- (suggested_cursor_horizontal_ratio
|
||||||
|
* (conservative_rect.right - conservative_rect.left) as f64)
|
||||||
|
as i32)
|
||||||
|
- conservative_rect.left
|
||||||
|
};
|
||||||
|
conservative_rect.left += bias;
|
||||||
|
conservative_rect.right += bias;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check to see if the new window rect is on the monitor with the new DPI factor.
|
||||||
|
// If it isn't, offset the window so that it is.
|
||||||
|
let new_dpi_monitor = unsafe { MonitorFromWindow(window, MONITOR_DEFAULTTONULL) };
|
||||||
|
let conservative_rect_monitor =
|
||||||
|
unsafe { MonitorFromRect(&conservative_rect, MONITOR_DEFAULTTONULL) };
|
||||||
|
|
||||||
|
if conservative_rect_monitor == new_dpi_monitor {
|
||||||
|
return conservative_rect;
|
||||||
|
}
|
||||||
|
|
||||||
|
let get_monitor_rect = |monitor| {
|
||||||
|
let mut monitor_info =
|
||||||
|
MONITORINFO { cbSize: mem::size_of::<MONITORINFO>() as _, ..unsafe { mem::zeroed() } };
|
||||||
|
unsafe { GetMonitorInfoW(monitor, &mut monitor_info) };
|
||||||
|
monitor_info.rcMonitor
|
||||||
|
};
|
||||||
|
let wrong_monitor = conservative_rect_monitor;
|
||||||
|
let wrong_monitor_rect = get_monitor_rect(wrong_monitor);
|
||||||
|
let new_monitor_rect = get_monitor_rect(new_dpi_monitor);
|
||||||
|
|
||||||
|
// The direction to nudge the window in to get the window onto the monitor with
|
||||||
|
// the new DPI factor. We calculate this by seeing which monitor edges are
|
||||||
|
// shared and nudging away from the wrong monitor based on those.
|
||||||
|
#[allow(clippy::bool_to_int_with_if)]
|
||||||
|
let delta_nudge_to_dpi_monitor = (
|
||||||
|
if wrong_monitor_rect.left == new_monitor_rect.right {
|
||||||
|
-1
|
||||||
|
} else if wrong_monitor_rect.right == new_monitor_rect.left {
|
||||||
|
1
|
||||||
|
} else {
|
||||||
|
0
|
||||||
|
},
|
||||||
|
if wrong_monitor_rect.bottom == new_monitor_rect.top {
|
||||||
|
1
|
||||||
|
} else if wrong_monitor_rect.top == new_monitor_rect.bottom {
|
||||||
|
-1
|
||||||
|
} else {
|
||||||
|
0
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
let abort_after_iterations = new_monitor_rect.right - new_monitor_rect.left
|
||||||
|
+ new_monitor_rect.bottom
|
||||||
|
- new_monitor_rect.top;
|
||||||
|
for _ in 0..abort_after_iterations {
|
||||||
|
conservative_rect.left += delta_nudge_to_dpi_monitor.0;
|
||||||
|
conservative_rect.right += delta_nudge_to_dpi_monitor.0;
|
||||||
|
conservative_rect.top += delta_nudge_to_dpi_monitor.1;
|
||||||
|
conservative_rect.bottom += delta_nudge_to_dpi_monitor.1;
|
||||||
|
|
||||||
|
if unsafe { MonitorFromRect(&conservative_rect, MONITOR_DEFAULTTONULL) } == new_dpi_monitor
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
conservative_rect
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -7,9 +7,10 @@ use std::sync::LazyLock;
|
||||||
use std::{io, mem, ptr};
|
use std::{io, mem, ptr};
|
||||||
|
|
||||||
use windows_sys::core::{HRESULT, PCWSTR};
|
use windows_sys::core::{HRESULT, PCWSTR};
|
||||||
use windows_sys::Win32::Foundation::{BOOL, HANDLE, HMODULE, HWND, POINT, RECT};
|
use windows_sys::Win32::Foundation::{BOOL, HANDLE, HMODULE, HWND, NTSTATUS, POINT, RECT};
|
||||||
use windows_sys::Win32::Graphics::Gdi::{ClientToScreen, HMONITOR};
|
use windows_sys::Win32::Graphics::Gdi::{ClientToScreen, HMONITOR};
|
||||||
use windows_sys::Win32::System::LibraryLoader::{GetProcAddress, LoadLibraryA};
|
use windows_sys::Win32::System::LibraryLoader::{GetProcAddress, LoadLibraryA};
|
||||||
|
use windows_sys::Win32::System::SystemInformation::OSVERSIONINFOW;
|
||||||
use windows_sys::Win32::System::SystemServices::IMAGE_DOS_HEADER;
|
use windows_sys::Win32::System::SystemServices::IMAGE_DOS_HEADER;
|
||||||
use windows_sys::Win32::UI::HiDpi::{
|
use windows_sys::Win32::UI::HiDpi::{
|
||||||
DPI_AWARENESS_CONTEXT, MONITOR_DPI_TYPE, PROCESS_DPI_AWARENESS,
|
DPI_AWARENESS_CONTEXT, MONITOR_DPI_TYPE, PROCESS_DPI_AWARENESS,
|
||||||
|
|
@ -254,6 +255,34 @@ pub type GetPointerDeviceRects = unsafe extern "system" fn(
|
||||||
pub type GetPointerTouchInfo =
|
pub type GetPointerTouchInfo =
|
||||||
unsafe extern "system" fn(pointer_id: u32, touch_info: *mut POINTER_TOUCH_INFO) -> BOOL;
|
unsafe extern "system" fn(pointer_id: u32, touch_info: *mut POINTER_TOUCH_INFO) -> BOOL;
|
||||||
|
|
||||||
|
pub(crate) static WIN10_BUILD_VERSION: LazyLock<Option<u32>> = LazyLock::new(|| {
|
||||||
|
type RtlGetVersion = unsafe extern "system" fn(*mut OSVERSIONINFOW) -> NTSTATUS;
|
||||||
|
let handle = get_function!("ntdll.dll", RtlGetVersion);
|
||||||
|
|
||||||
|
if let Some(rtl_get_version) = handle {
|
||||||
|
unsafe {
|
||||||
|
let mut vi = OSVERSIONINFOW {
|
||||||
|
dwOSVersionInfoSize: 0,
|
||||||
|
dwMajorVersion: 0,
|
||||||
|
dwMinorVersion: 0,
|
||||||
|
dwBuildNumber: 0,
|
||||||
|
dwPlatformId: 0,
|
||||||
|
szCSDVersion: [0; 128],
|
||||||
|
};
|
||||||
|
|
||||||
|
let status = (rtl_get_version)(&mut vi);
|
||||||
|
|
||||||
|
if status >= 0 && vi.dwMajorVersion == 10 && vi.dwMinorVersion == 0 {
|
||||||
|
Some(vi.dwBuildNumber)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
pub(crate) static GET_DPI_FOR_WINDOW: LazyLock<Option<GetDpiForWindow>> =
|
pub(crate) static GET_DPI_FOR_WINDOW: LazyLock<Option<GetDpiForWindow>> =
|
||||||
LazyLock::new(|| get_function!("user32.dll", GetDpiForWindow));
|
LazyLock::new(|| get_function!("user32.dll", GetDpiForWindow));
|
||||||
pub(crate) static ADJUST_WINDOW_RECT_EX_FOR_DPI: LazyLock<Option<AdjustWindowRectExForDpi>> =
|
pub(crate) static ADJUST_WINDOW_RECT_EX_FOR_DPI: LazyLock<Option<AdjustWindowRectExForDpi>> =
|
||||||
|
|
|
||||||
|
|
@ -259,3 +259,4 @@ changelog entry.
|
||||||
- On Windows, account for mouse wheel lines per scroll setting for `WindowEvent::MouseWheel`.
|
- On Windows, account for mouse wheel lines per scroll setting for `WindowEvent::MouseWheel`.
|
||||||
- On Windows, `Window::theme` will return the correct theme after setting it through `Window::set_theme`.
|
- On Windows, `Window::theme` will return the correct theme after setting it through `Window::set_theme`.
|
||||||
- On Windows, `Window::set_theme` will change the title bar color immediately now.
|
- On Windows, `Window::set_theme` will change the title bar color immediately now.
|
||||||
|
- On Windows 11, prevent incorrect shifting when dragging window onto a monitor with different DPI.
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue