-
Notifications
You must be signed in to change notification settings - Fork 1.1k
prevent incorrect shifting of window when dragging onto monitor with different DPI #4119
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -90,6 +90,7 @@ use crate::platform_impl::platform::window_state::{ | |
| CursorFlags, ImeState, WindowFlags, WindowState, | ||
| }; | ||
| use crate::platform_impl::platform::{raw_input, util, wrap_device_id}; | ||
| use crate::platform_impl::windows::util::WIN10_BUILD_VERSION; | ||
| use crate::platform_impl::Window; | ||
| use crate::utils::Lazy; | ||
| use crate::window::{ | ||
|
|
@@ -2307,103 +2308,110 @@ unsafe fn public_window_callback_inner( | |
| } | ||
|
|
||
| let new_outer_rect: 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, | ||
| }; | ||
| if WIN10_BUILD_VERSION.is_some_and(|version| version < 22000) { | ||
| // The window position needs adjustment on Windows 10. | ||
| { | ||
| 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 | ||
| 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 | ||
| }; | ||
| 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; | ||
| } | ||
| 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() } | ||
| // 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 | ||
| }; | ||
| 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 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; | ||
| 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 | ||
| }; | ||
| conservative_rect | ||
| }; | ||
|
Comment on lines
+2312
to
+2410
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. From what I can see this hasn't changed at all and all that got added is the |
||
| } | ||
| } else { | ||
| // The suggested position is fine w/o adjustment on Windows 11. | ||
| new_outer_rect = suggested_rect | ||
| } | ||
|
|
||
| unsafe { | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -6,9 +6,10 @@ use std::sync::atomic::{AtomicBool, Ordering}; | |
| use std::{io, mem, ptr}; | ||
|
|
||
| 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::System::LibraryLoader::{GetProcAddress, LoadLibraryA}; | ||
| use windows_sys::Win32::System::SystemInformation::OSVERSIONINFOW; | ||
| use windows_sys::Win32::System::SystemServices::IMAGE_DOS_HEADER; | ||
| use windows_sys::Win32::UI::HiDpi::{ | ||
| DPI_AWARENESS_CONTEXT, MONITOR_DPI_TYPE, PROCESS_DPI_AWARENESS, | ||
|
|
@@ -251,6 +252,34 @@ pub type GetPointerDeviceRects = unsafe extern "system" fn( | |
| pub type GetPointerTouchInfo = | ||
| unsafe extern "system" fn(pointer_id: u32, touch_info: *mut POINTER_TOUCH_INFO) -> BOOL; | ||
|
|
||
| pub(crate) static WIN10_BUILD_VERSION: Lazy<Option<u32>> = Lazy::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], | ||
| }; | ||
|
|
||
|
Comment on lines
+255
to
+269
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'd assume that got moved just for the sake of it not being in |
||
| 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: Lazy<Option<GetDpiForWindow>> = | ||
| Lazy::new(|| get_function!("user32.dll", GetDpiForWindow)); | ||
| pub(crate) static ADJUST_WINDOW_RECT_EX_FOR_DPI: Lazy<Option<AdjustWindowRectExForDpi>> = | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd assume that it's talking only about Windows 11?