Skip to content

Commit ae2f28c

Browse files
Billy MessengerBilly Messenger
authored andcommitted
add window resizing and dpi scaling support in Windows
1 parent f7873f1 commit ae2f28c

1 file changed

Lines changed: 126 additions & 15 deletions

File tree

src/win/window.rs

Lines changed: 126 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -6,15 +6,16 @@ use winapi::um::winuser::{
66
AdjustWindowRectEx, CreateWindowExW, DefWindowProcW, DispatchMessageW,
77
GetMessageW, GetWindowLongPtrW, PostMessageW, RegisterClassW, SetTimer,
88
SetWindowLongPtrW, TranslateMessage, UnregisterClassW, LoadCursorW,
9-
DestroyWindow,
9+
DestroyWindow, SetProcessDpiAwarenessContext, SetWindowPos,
10+
GetDpiForWindow,
1011
CS_OWNDC, GWLP_USERDATA, IDC_ARROW,
1112
MSG, WM_CLOSE, WM_CREATE, WM_MOUSEMOVE, WM_SHOWWINDOW, WM_TIMER, WM_NCDESTROY,
1213
WNDCLASSW, WS_CAPTION, WS_CHILD, WS_CLIPSIBLINGS, WS_MAXIMIZEBOX, WS_MINIMIZEBOX,
1314
WS_POPUPWINDOW, WS_SIZEBOX, WS_VISIBLE, WM_DPICHANGED, WM_CHAR, WM_SYSCHAR, WM_KEYDOWN,
14-
WM_SYSKEYDOWN, WM_KEYUP, WM_SYSKEYUP, WM_INPUTLANGCHANGE,
15+
WM_SYSKEYDOWN, WM_KEYUP, WM_SYSKEYUP, WM_INPUTLANGCHANGE, WM_SIZE,
1516
GET_XBUTTON_WPARAM, WM_LBUTTONDOWN, WM_LBUTTONUP, WM_MBUTTONDOWN, WM_MBUTTONUP,
1617
WM_RBUTTONDOWN, WM_RBUTTONUP, WM_XBUTTONDOWN, WM_XBUTTONUP, XBUTTON1, XBUTTON2,
17-
SetCapture, GetCapture, ReleaseCapture, IsWindow,
18+
SetCapture, GetCapture, ReleaseCapture, IsWindow, SWP_NOZORDER, SWP_NOMOVE
1819
};
1920

2021
use std::cell::RefCell;
@@ -30,7 +31,7 @@ use raw_window_handle::{
3031

3132
use crate::{
3233
Event, MouseButton, MouseEvent, WindowEvent,
33-
WindowHandler, WindowInfo, WindowOpenOptions, WindowScalePolicy, PhyPoint,
34+
WindowHandler, WindowInfo, WindowOpenOptions, WindowScalePolicy, PhyPoint, PhySize
3435
};
3536

3637
use super::keyboard::KeyboardState;
@@ -152,9 +153,6 @@ unsafe extern "system" fn wnd_proc(
152153
// return 0;
153154
return DefWindowProcW(hwnd, msg, wparam, lparam);
154155
}
155-
WM_DPICHANGED => {
156-
// TODO: Notify app of DPI change
157-
},
158156
WM_CHAR | WM_SYSCHAR | WM_KEYDOWN | WM_SYSKEYDOWN | WM_KEYUP
159157
| WM_SYSKEYUP | WM_INPUTLANGCHANGE => {
160158
let opt_event = (&*window_state_ptr).borrow_mut()
@@ -171,6 +169,70 @@ unsafe extern "system" fn wnd_proc(
171169
return 0;
172170
}
173171
}
172+
WM_SIZE => {
173+
let width = (lparam & 0xFFFF) as u16 as u32;
174+
let height = ((lparam >> 16) & 0xFFFF) as u16 as u32;
175+
176+
let mut window_state = (&*window_state_ptr).borrow_mut();
177+
178+
window_state.window_info = WindowInfo::from_physical_size(
179+
PhySize { width, height },
180+
window_state.window_info.scale(),
181+
);
182+
183+
let window_info = window_state.window_info;
184+
185+
window_state.handler.on_event(
186+
&mut window,
187+
Event::Window(WindowEvent::Resized(window_info)),
188+
);
189+
}
190+
WM_DPICHANGED => {
191+
// To avoid weirdness with the realtime borrow checker.
192+
let new_rect = {
193+
let mut window_state = (&*window_state_ptr).borrow_mut();
194+
195+
if let WindowScalePolicy::SystemScaleFactor = window_state.scale_policy {
196+
let dpi = (wparam & 0xFFFF) as u16 as u32;
197+
let scale_factor = dpi as f64 / 96.0;
198+
199+
window_state.window_info = WindowInfo::from_logical_size(
200+
window_state.window_info.logical_size(),
201+
scale_factor,
202+
);
203+
204+
Some((
205+
RECT {
206+
left: 0,
207+
top: 0,
208+
// todo: check if usize fits into i32
209+
right: window_state.window_info.physical_size().width as i32,
210+
bottom: window_state.window_info.physical_size().height as i32,
211+
},
212+
window_state.dw_style,
213+
))
214+
} else {
215+
None
216+
}
217+
};
218+
if let Some((mut new_rect, dw_style)) = new_rect {
219+
// Convert this desired "client rectangle" size to the actual "window rectangle"
220+
// size (Because of course you have to do that).
221+
AdjustWindowRectEx(&mut new_rect, dw_style, 0, 0);
222+
223+
// Windows makes us resize the window manually. This will trigger another `WM_SIZE` event,
224+
// which we can then send the user the new scale factor.
225+
SetWindowPos(
226+
hwnd,
227+
hwnd,
228+
new_rect.left as i32,
229+
new_rect.top as i32,
230+
new_rect.right - new_rect.left,
231+
new_rect.bottom - new_rect.top,
232+
SWP_NOZORDER | SWP_NOMOVE,
233+
);
234+
}
235+
}
174236
WM_NCDESTROY => {
175237
let window_state = Box::from_raw(window_state_ptr);
176238
unregister_wnd_class(window_state.borrow().window_class);
@@ -219,6 +281,8 @@ struct WindowState {
219281
keyboard_state: KeyboardState,
220282
mouse_button_counter: usize,
221283
handler: Box<dyn WindowHandler>,
284+
scale_policy: WindowScalePolicy,
285+
dw_style: u32,
222286
}
223287

224288
pub struct Window {
@@ -299,7 +363,7 @@ impl Window {
299363
// todo: manage error ^
300364

301365
let scaling = match options.scale {
302-
WindowScalePolicy::SystemScaleFactor => get_scaling().unwrap_or(1.0),
366+
WindowScalePolicy::SystemScaleFactor => 1.0,
303367
WindowScalePolicy::ScaleFactor(scale) => scale
304368
};
305369

@@ -347,17 +411,69 @@ impl Window {
347411

348412
let handler = Box::new(build(&mut crate::Window::new(&mut Window { hwnd })));
349413

350-
let window_state = Box::new(RefCell::new(WindowState {
414+
let mut window_state = Box::new(RefCell::new(WindowState {
351415
window_class,
352416
window_info,
353417
keyboard_state: KeyboardState::new(),
354418
mouse_button_counter: 0,
355419
handler,
420+
scale_policy: options.scale,
421+
dw_style: flags,
356422
}));
357423

424+
// Only works on Windows 10 unfortunately.
425+
SetProcessDpiAwarenessContext(
426+
winapi::shared::windef::DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE,
427+
);
428+
429+
// Now we can get the actual dpi of the window.
430+
let new_rect = if let WindowScalePolicy::SystemScaleFactor = options.scale {
431+
// Only works on Windows 10 unfortunately.
432+
let dpi = GetDpiForWindow(hwnd);
433+
let scale_factor = dpi as f64 / 96.0;
434+
435+
let mut window_state = window_state.get_mut();
436+
if window_state.window_info.scale() != scale_factor {
437+
window_state.window_info = WindowInfo::from_logical_size(
438+
window_state.window_info.logical_size(),
439+
scale_factor,
440+
);
441+
442+
Some(RECT {
443+
left: 0,
444+
top: 0,
445+
// todo: check if usize fits into i32
446+
right: window_state.window_info.physical_size().width as i32,
447+
bottom: window_state.window_info.physical_size().height as i32,
448+
})
449+
} else {
450+
None
451+
}
452+
} else {
453+
None
454+
};
455+
358456
SetWindowLongPtrW(hwnd, GWLP_USERDATA, Box::into_raw(window_state) as *const _ as _);
359457
SetTimer(hwnd, WIN_FRAME_TIMER, 15, None);
360458

459+
if let Some(mut new_rect) = new_rect {
460+
// Convert this desired"client rectangle" size to the actual "window rectangle"
461+
// size (Because of course you have to do that).
462+
AdjustWindowRectEx(&mut new_rect, flags, 0, 0);
463+
464+
// Windows makes us resize the window manually. This will trigger another `WM_SIZE` event,
465+
// which we can then send the user the new scale factor.
466+
SetWindowPos(
467+
hwnd,
468+
hwnd,
469+
new_rect.left as i32,
470+
new_rect.top as i32,
471+
new_rect.right - new_rect.left,
472+
new_rect.bottom - new_rect.top,
473+
SWP_NOZORDER | SWP_NOMOVE,
474+
);
475+
}
476+
361477
hwnd
362478
}
363479
}
@@ -370,9 +486,4 @@ unsafe impl HasRawWindowHandle for Window {
370486
..WindowsHandle::empty()
371487
})
372488
}
373-
}
374-
375-
fn get_scaling() -> Option<f64> {
376-
// TODO: find system scaling
377-
None
378-
}
489+
}

0 commit comments

Comments
 (0)