Skip to content

Commit df52c1a

Browse files
committed
prevent incorrect shifting of window when dragging onto monitor with different DPI
1 parent 587ade8 commit df52c1a

File tree

4 files changed

+131
-122
lines changed

4 files changed

+131
-122
lines changed

src/changelog/unreleased.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -195,6 +195,7 @@ changelog entry.
195195
- Renamed "super" key to "meta", to match the naming in the W3C specification.
196196
`NamedKey::Super` still exists, but it's non-functional and deprecated, `NamedKey::Meta` should be used instead.
197197
- Move `IconExtWindows` into `WinIcon`.
198+
- On Windows, prevent incorrect shifting when dragging window onto a monitor with different DPI.
198199

199200
### Removed
200201

src/platform_impl/windows/dark_mode.rs

Lines changed: 2 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,8 @@
33
use std::{ffi::c_void, ptr};
44

55
use windows_sys::core::PCSTR;
6-
use windows_sys::Win32::Foundation::{BOOL, HWND, NTSTATUS, S_OK};
6+
use windows_sys::Win32::Foundation::{BOOL, HWND, S_OK};
77
use windows_sys::Win32::System::LibraryLoader::{GetProcAddress, LoadLibraryA};
8-
use windows_sys::Win32::System::SystemInformation::OSVERSIONINFOW;
98
use windows_sys::Win32::UI::Accessibility::{HCF_HIGHCONTRASTON, HIGHCONTRASTA};
109
use windows_sys::Win32::UI::Controls::SetWindowTheme;
1110
use windows_sys::Win32::UI::WindowsAndMessaging::{SystemParametersInfoA, SPI_GETHIGHCONTRAST};
@@ -14,38 +13,10 @@ use super::util;
1413
use crate::utils::Lazy;
1514
use crate::window::Theme;
1615

17-
static WIN10_BUILD_VERSION: Lazy<Option<u32>> = Lazy::new(|| {
18-
type RtlGetVersion = unsafe extern "system" fn(*mut OSVERSIONINFOW) -> NTSTATUS;
19-
let handle = get_function!("ntdll.dll", RtlGetVersion);
20-
21-
if let Some(rtl_get_version) = handle {
22-
unsafe {
23-
let mut vi = OSVERSIONINFOW {
24-
dwOSVersionInfoSize: 0,
25-
dwMajorVersion: 0,
26-
dwMinorVersion: 0,
27-
dwBuildNumber: 0,
28-
dwPlatformId: 0,
29-
szCSDVersion: [0; 128],
30-
};
31-
32-
let status = (rtl_get_version)(&mut vi);
33-
34-
if status >= 0 && vi.dwMajorVersion == 10 && vi.dwMinorVersion == 0 {
35-
Some(vi.dwBuildNumber)
36-
} else {
37-
None
38-
}
39-
}
40-
} else {
41-
None
42-
}
43-
});
44-
4516
static DARK_MODE_SUPPORTED: Lazy<bool> = Lazy::new(|| {
4617
// We won't try to do anything for windows versions < 17763
4718
// (Windows 10 October 2018 update)
48-
match *WIN10_BUILD_VERSION {
19+
match *util::WIN10_BUILD_VERSION {
4920
Some(v) => v >= 17763,
5021
None => false,
5122
}

src/platform_impl/windows/event_loop.rs

Lines changed: 98 additions & 90 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,7 @@ use crate::platform_impl::platform::window_state::{
9090
CursorFlags, ImeState, WindowFlags, WindowState,
9191
};
9292
use crate::platform_impl::platform::{raw_input, util, wrap_device_id};
93+
use crate::platform_impl::windows::util::WIN10_BUILD_VERSION;
9394
use crate::platform_impl::Window;
9495
use crate::utils::Lazy;
9596
use crate::window::{
@@ -2307,103 +2308,110 @@ unsafe fn public_window_callback_inner(
23072308
}
23082309

23092310
let new_outer_rect: RECT;
2310-
{
2311-
let suggested_ul =
2312-
(suggested_rect.left + margin_left, suggested_rect.top + margin_top);
2313-
2314-
let mut conservative_rect = RECT {
2315-
left: suggested_ul.0,
2316-
top: suggested_ul.1,
2317-
right: suggested_ul.0 + new_physical_surface_size.width as i32,
2318-
bottom: suggested_ul.1 + new_physical_surface_size.height as i32,
2319-
};
2311+
if WIN10_BUILD_VERSION.is_some_and(|version| version < 22000) {
2312+
// The window position needs adjustment on Windows 10.
2313+
{
2314+
let suggested_ul =
2315+
(suggested_rect.left + margin_left, suggested_rect.top + margin_top);
2316+
2317+
let mut conservative_rect = RECT {
2318+
left: suggested_ul.0,
2319+
top: suggested_ul.1,
2320+
right: suggested_ul.0 + new_physical_surface_size.width as i32,
2321+
bottom: suggested_ul.1 + new_physical_surface_size.height as i32,
2322+
};
23202323

2321-
conservative_rect = window_flags
2322-
.adjust_rect(window, conservative_rect)
2323-
.unwrap_or(conservative_rect);
2324-
2325-
// If we're dragging the window, offset the window so that the cursor's
2326-
// relative horizontal position in the title bar is preserved.
2327-
if dragging_window {
2328-
let bias = {
2329-
let cursor_pos = {
2330-
let mut pos = unsafe { mem::zeroed() };
2331-
unsafe { GetCursorPos(&mut pos) };
2332-
pos
2324+
conservative_rect = window_flags
2325+
.adjust_rect(window, conservative_rect)
2326+
.unwrap_or(conservative_rect);
2327+
2328+
// If we're dragging the window, offset the window so that the cursor's
2329+
// relative horizontal position in the title bar is preserved.
2330+
if dragging_window {
2331+
let bias = {
2332+
let cursor_pos = {
2333+
let mut pos = unsafe { mem::zeroed() };
2334+
unsafe { GetCursorPos(&mut pos) };
2335+
pos
2336+
};
2337+
let suggested_cursor_horizontal_ratio =
2338+
(cursor_pos.x - suggested_rect.left) as f64
2339+
/ (suggested_rect.right - suggested_rect.left) as f64;
2340+
2341+
(cursor_pos.x
2342+
- (suggested_cursor_horizontal_ratio
2343+
* (conservative_rect.right - conservative_rect.left) as f64)
2344+
as i32)
2345+
- conservative_rect.left
23332346
};
2334-
let suggested_cursor_horizontal_ratio = (cursor_pos.x - suggested_rect.left)
2335-
as f64
2336-
/ (suggested_rect.right - suggested_rect.left) as f64;
2337-
2338-
(cursor_pos.x
2339-
- (suggested_cursor_horizontal_ratio
2340-
* (conservative_rect.right - conservative_rect.left) as f64)
2341-
as i32)
2342-
- conservative_rect.left
2343-
};
2344-
conservative_rect.left += bias;
2345-
conservative_rect.right += bias;
2346-
}
2347+
conservative_rect.left += bias;
2348+
conservative_rect.right += bias;
2349+
}
23472350

2348-
// Check to see if the new window rect is on the monitor with the new DPI factor.
2349-
// If it isn't, offset the window so that it is.
2350-
let new_dpi_monitor = unsafe { MonitorFromWindow(window, MONITOR_DEFAULTTONULL) };
2351-
let conservative_rect_monitor =
2352-
unsafe { MonitorFromRect(&conservative_rect, MONITOR_DEFAULTTONULL) };
2353-
new_outer_rect = if conservative_rect_monitor == new_dpi_monitor {
2354-
conservative_rect
2355-
} else {
2356-
let get_monitor_rect = |monitor| {
2357-
let mut monitor_info = MONITORINFO {
2358-
cbSize: mem::size_of::<MONITORINFO>() as _,
2359-
..unsafe { mem::zeroed() }
2351+
// Check to see if the new window rect is on the monitor with the new DPI
2352+
// factor. If it isn't, offset the window so that it is.
2353+
let new_dpi_monitor =
2354+
unsafe { MonitorFromWindow(window, MONITOR_DEFAULTTONULL) };
2355+
let conservative_rect_monitor =
2356+
unsafe { MonitorFromRect(&conservative_rect, MONITOR_DEFAULTTONULL) };
2357+
new_outer_rect = if conservative_rect_monitor == new_dpi_monitor {
2358+
conservative_rect
2359+
} else {
2360+
let get_monitor_rect = |monitor| {
2361+
let mut monitor_info = MONITORINFO {
2362+
cbSize: mem::size_of::<MONITORINFO>() as _,
2363+
..unsafe { mem::zeroed() }
2364+
};
2365+
unsafe { GetMonitorInfoW(monitor, &mut monitor_info) };
2366+
monitor_info.rcMonitor
23602367
};
2361-
unsafe { GetMonitorInfoW(monitor, &mut monitor_info) };
2362-
monitor_info.rcMonitor
2363-
};
2364-
let wrong_monitor = conservative_rect_monitor;
2365-
let wrong_monitor_rect = get_monitor_rect(wrong_monitor);
2366-
let new_monitor_rect = get_monitor_rect(new_dpi_monitor);
2367-
2368-
// The direction to nudge the window in to get the window onto the monitor with
2369-
// the new DPI factor. We calculate this by seeing which monitor edges are
2370-
// shared and nudging away from the wrong monitor based on those.
2371-
#[allow(clippy::bool_to_int_with_if)]
2372-
let delta_nudge_to_dpi_monitor = (
2373-
if wrong_monitor_rect.left == new_monitor_rect.right {
2374-
-1
2375-
} else if wrong_monitor_rect.right == new_monitor_rect.left {
2376-
1
2377-
} else {
2378-
0
2379-
},
2380-
if wrong_monitor_rect.bottom == new_monitor_rect.top {
2381-
1
2382-
} else if wrong_monitor_rect.top == new_monitor_rect.bottom {
2383-
-1
2384-
} else {
2385-
0
2386-
},
2387-
);
2368+
let wrong_monitor = conservative_rect_monitor;
2369+
let wrong_monitor_rect = get_monitor_rect(wrong_monitor);
2370+
let new_monitor_rect = get_monitor_rect(new_dpi_monitor);
2371+
2372+
// The direction to nudge the window in to get the window onto the monitor
2373+
// with the new DPI factor. We calculate this by seeing which monitor edges
2374+
// are shared and nudging away from the wrong monitor based on those.
2375+
#[allow(clippy::bool_to_int_with_if)]
2376+
let delta_nudge_to_dpi_monitor = (
2377+
if wrong_monitor_rect.left == new_monitor_rect.right {
2378+
-1
2379+
} else if wrong_monitor_rect.right == new_monitor_rect.left {
2380+
1
2381+
} else {
2382+
0
2383+
},
2384+
if wrong_monitor_rect.bottom == new_monitor_rect.top {
2385+
1
2386+
} else if wrong_monitor_rect.top == new_monitor_rect.bottom {
2387+
-1
2388+
} else {
2389+
0
2390+
},
2391+
);
23882392

2389-
let abort_after_iterations = new_monitor_rect.right - new_monitor_rect.left
2390-
+ new_monitor_rect.bottom
2391-
- new_monitor_rect.top;
2392-
for _ in 0..abort_after_iterations {
2393-
conservative_rect.left += delta_nudge_to_dpi_monitor.0;
2394-
conservative_rect.right += delta_nudge_to_dpi_monitor.0;
2395-
conservative_rect.top += delta_nudge_to_dpi_monitor.1;
2396-
conservative_rect.bottom += delta_nudge_to_dpi_monitor.1;
2397-
2398-
if unsafe { MonitorFromRect(&conservative_rect, MONITOR_DEFAULTTONULL) }
2399-
== new_dpi_monitor
2400-
{
2401-
break;
2393+
let abort_after_iterations = new_monitor_rect.right - new_monitor_rect.left
2394+
+ new_monitor_rect.bottom
2395+
- new_monitor_rect.top;
2396+
for _ in 0..abort_after_iterations {
2397+
conservative_rect.left += delta_nudge_to_dpi_monitor.0;
2398+
conservative_rect.right += delta_nudge_to_dpi_monitor.0;
2399+
conservative_rect.top += delta_nudge_to_dpi_monitor.1;
2400+
conservative_rect.bottom += delta_nudge_to_dpi_monitor.1;
2401+
2402+
if unsafe { MonitorFromRect(&conservative_rect, MONITOR_DEFAULTTONULL) }
2403+
== new_dpi_monitor
2404+
{
2405+
break;
2406+
}
24022407
}
2403-
}
24042408

2405-
conservative_rect
2406-
};
2409+
conservative_rect
2410+
};
2411+
}
2412+
} else {
2413+
// The suggested position is fine w/o adjustment on Windows 11.
2414+
new_outer_rect = suggested_rect
24072415
}
24082416

24092417
unsafe {

src/platform_impl/windows/util.rs

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,10 @@ use std::sync::atomic::{AtomicBool, Ordering};
66
use std::{io, mem, ptr};
77

88
use windows_sys::core::{HRESULT, PCWSTR};
9-
use windows_sys::Win32::Foundation::{BOOL, HANDLE, HMODULE, HWND, POINT, RECT};
9+
use windows_sys::Win32::Foundation::{BOOL, HANDLE, HMODULE, HWND, NTSTATUS, POINT, RECT};
1010
use windows_sys::Win32::Graphics::Gdi::{ClientToScreen, HMONITOR};
1111
use windows_sys::Win32::System::LibraryLoader::{GetProcAddress, LoadLibraryA};
12+
use windows_sys::Win32::System::SystemInformation::OSVERSIONINFOW;
1213
use windows_sys::Win32::System::SystemServices::IMAGE_DOS_HEADER;
1314
use windows_sys::Win32::UI::HiDpi::{
1415
DPI_AWARENESS_CONTEXT, MONITOR_DPI_TYPE, PROCESS_DPI_AWARENESS,
@@ -251,6 +252,34 @@ pub type GetPointerDeviceRects = unsafe extern "system" fn(
251252
pub type GetPointerTouchInfo =
252253
unsafe extern "system" fn(pointer_id: u32, touch_info: *mut POINTER_TOUCH_INFO) -> BOOL;
253254

255+
pub(crate) static WIN10_BUILD_VERSION: Lazy<Option<u32>> = Lazy::new(|| {
256+
type RtlGetVersion = unsafe extern "system" fn(*mut OSVERSIONINFOW) -> NTSTATUS;
257+
let handle = get_function!("ntdll.dll", RtlGetVersion);
258+
259+
if let Some(rtl_get_version) = handle {
260+
unsafe {
261+
let mut vi = OSVERSIONINFOW {
262+
dwOSVersionInfoSize: 0,
263+
dwMajorVersion: 0,
264+
dwMinorVersion: 0,
265+
dwBuildNumber: 0,
266+
dwPlatformId: 0,
267+
szCSDVersion: [0; 128],
268+
};
269+
270+
let status = (rtl_get_version)(&mut vi);
271+
272+
if status >= 0 && vi.dwMajorVersion == 10 && vi.dwMinorVersion == 0 {
273+
Some(vi.dwBuildNumber)
274+
} else {
275+
None
276+
}
277+
}
278+
} else {
279+
None
280+
}
281+
});
282+
254283
pub(crate) static GET_DPI_FOR_WINDOW: Lazy<Option<GetDpiForWindow>> =
255284
Lazy::new(|| get_function!("user32.dll", GetDpiForWindow));
256285
pub(crate) static ADJUST_WINDOW_RECT_EX_FOR_DPI: Lazy<Option<AdjustWindowRectExForDpi>> =

0 commit comments

Comments
 (0)