Description
This is a follow-up to the closed issue #4041. The same Windows mixed-DPI drag/resize feedback loop is still reproducible with winit 0.30.13 on Windows 11 24H2.
When a maximized window is dragged from a 125% scaled primary monitor to a 100% scaled monitor, the restored window can grow to an extremely large size and remain attached to the original monitor instead of landing on the target monitor. This also reproduces through eframe 0.29.1, because it uses winit underneath.
Monitor layout used for reproduction
DISPLAY5: 1920x1080, 100% scale, positioned left of primary: (-1920,0)-(0,1080)
DISPLAY1: 1920x1080, 125% scale, primary: (0,0)-(1920,1080)
DISPLAY6: 1920x1200, 125% scale, positioned right: (1920,-65)-(3456,895)
Minimal repro
Two minimal binaries were tested:
- Raw
winit 0.30.13:
use winit::{
application::ApplicationHandler,
dpi::LogicalSize,
event::WindowEvent,
event_loop::{ActiveEventLoop, EventLoop},
window::{Window, WindowAttributes, WindowId},
};
#[derive(Default)]
struct App { window: Option<Window> }
impl ApplicationHandler for App {
fn resumed(&mut self, event_loop: &ActiveEventLoop) {
if self.window.is_none() {
let attrs = WindowAttributes::default()
.with_title("repro_winit")
.with_inner_size(LogicalSize::new(1000.0, 650.0))
.with_maximized(true);
self.window = Some(event_loop.create_window(attrs).expect("create window"));
}
}
fn window_event(&mut self, event_loop: &ActiveEventLoop, _id: WindowId, event: WindowEvent) {
if matches!(event, WindowEvent::CloseRequested) {
event_loop.exit();
}
}
}
fn main() {
let event_loop = EventLoop::new().expect("event loop");
let mut app = App::default();
event_loop.run_app(&mut app).expect("run app");
}
eframe 0.29.1 with ViewportBuilder::with_maximized(true) also reproduces the same behavior.
Both binaries used a PerMonitorV2 manifest.
Observed measurement without patch
Raw winit 0.30.13, after dragging from DISPLAY1 to the left monitor:
Before:
ShowCmd: 3
Rect: (-9,-9)-(1929,1029)
Normal: (32,32)-(1300,892)
Monitor: \\.\DISPLAY1
DPI: 120
After drag:
ShowCmd: 1
Rect: (-1329,75)-(2545,2701)
Size: 3874x2626
Monitor: \\.\DISPLAY1
DPI: 120
eframe 0.29.1 showed the same class of failure:
After drag:
Rect: (-1164,75)-(1935,2176)
Size: 3099x2101
Monitor: \\.\DISPLAY1
DPI: 120
Local patch tested
The issue disappears when WM_DPICHANGED while WindowFlags::MARKER_IN_SIZE_MOVE uses the Windows-provided suggested_rect directly instead of applying the current conservative rect / monitor nudge logic.
Patch tested locally against winit 0.30.13:
let new_outer_rect: RECT;
if dragging_window {
new_outer_rect = suggested_rect;
} else {
// existing conservative_rect + monitor nudge logic
}
Observed measurement with local patch
Raw winit 0.30.13 patched:
Before:
ShowCmd: 3
Rect: (-9,-9)-(1929,1029)
Normal: (160,160)-(1428,1020)
Monitor: \\.\DISPLAY1
DPI: 120
After drag:
ShowCmd: 1
Rect: (-1417,79)-(-403,767)
Size: 1014x688
Monitor: \\.\DISPLAY5
DPI: 96
eframe 0.29.1 using the patched winit also behaves correctly:
After drag:
Rect: (-1404,79)-(-390,767)
Size: 1014x688
Monitor: \\.\DISPLAY5
DPI: 96
Notes
This matches the analysis already posted in #4041: during WM_DPICHANGED, the current monitor check / nudge logic can keep the window on the old monitor, trigger another DPI change, and repeatedly enlarge the window. Microsoft's WM_DPICHANGED guidance recommends applying the suggested rectangle from lParam with SetWindowPos.
Reference: https://learn.microsoft.com/en-us/windows/win32/hidpi/wm-dpichanged
Platform
- Windows 11 24H2
winit 0.30.13
- Reproduces with raw
winit and through eframe 0.29.1
- Process DPI awareness: Per Monitor Aware (
GetProcessDpiAwareness == 2)
Description
This is a follow-up to the closed issue #4041. The same Windows mixed-DPI drag/resize feedback loop is still reproducible with
winit 0.30.13on Windows 11 24H2.When a maximized window is dragged from a 125% scaled primary monitor to a 100% scaled monitor, the restored window can grow to an extremely large size and remain attached to the original monitor instead of landing on the target monitor. This also reproduces through
eframe 0.29.1, because it useswinitunderneath.Monitor layout used for reproduction
DISPLAY5: 1920x1080, 100% scale, positioned left of primary:(-1920,0)-(0,1080)DISPLAY1: 1920x1080, 125% scale, primary:(0,0)-(1920,1080)DISPLAY6: 1920x1200, 125% scale, positioned right:(1920,-65)-(3456,895)Minimal repro
Two minimal binaries were tested:
winit 0.30.13:eframe 0.29.1withViewportBuilder::with_maximized(true)also reproduces the same behavior.Both binaries used a PerMonitorV2 manifest.
Observed measurement without patch
Raw
winit 0.30.13, after dragging fromDISPLAY1to the left monitor:eframe 0.29.1showed the same class of failure:Local patch tested
The issue disappears when
WM_DPICHANGEDwhileWindowFlags::MARKER_IN_SIZE_MOVEuses the Windows-providedsuggested_rectdirectly instead of applying the current conservative rect / monitor nudge logic.Patch tested locally against
winit 0.30.13:Observed measurement with local patch
Raw
winit 0.30.13patched:eframe 0.29.1using the patchedwinitalso behaves correctly:Notes
This matches the analysis already posted in #4041: during
WM_DPICHANGED, the current monitor check / nudge logic can keep the window on the old monitor, trigger another DPI change, and repeatedly enlarge the window. Microsoft'sWM_DPICHANGEDguidance recommends applying the suggested rectangle fromlParamwithSetWindowPos.Reference: https://learn.microsoft.com/en-us/windows/win32/hidpi/wm-dpichanged
Platform
winit 0.30.13winitand througheframe 0.29.1GetProcessDpiAwareness == 2)