• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2022 The ChromiumOS Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 use std::convert::From;
6 use std::fmt;
7 use std::mem;
8 #[cfg(feature = "gfxstream")]
9 use std::os::raw::c_int;
10 use std::os::raw::c_void;
11 use std::ptr::null_mut;
12 
13 use anyhow::bail;
14 use anyhow::Context;
15 use anyhow::Result;
16 use base::error;
17 use base::info;
18 use base::warn;
19 use euclid::point2;
20 use euclid::size2;
21 use euclid::Box2D;
22 use euclid::Size2D;
23 use vm_control::display::WindowVisibility;
24 use win_util::syscall_bail;
25 use win_util::win32_wide_string;
26 use winapi::shared::minwindef::DWORD;
27 use winapi::shared::minwindef::FALSE;
28 use winapi::shared::minwindef::HINSTANCE;
29 use winapi::shared::minwindef::HMODULE;
30 use winapi::shared::minwindef::LPARAM;
31 use winapi::shared::minwindef::LRESULT;
32 use winapi::shared::minwindef::TRUE;
33 use winapi::shared::minwindef::UINT;
34 use winapi::shared::minwindef::WORD;
35 use winapi::shared::minwindef::WPARAM;
36 use winapi::shared::windef::HBRUSH;
37 use winapi::shared::windef::HCURSOR;
38 use winapi::shared::windef::HICON;
39 use winapi::shared::windef::HMONITOR;
40 use winapi::shared::windef::HWND;
41 use winapi::shared::windef::RECT;
42 use winapi::shared::winerror::S_OK;
43 use winapi::um::dwmapi::DwmEnableBlurBehindWindow;
44 use winapi::um::dwmapi::DWM_BB_ENABLE;
45 use winapi::um::dwmapi::DWM_BLURBEHIND;
46 use winapi::um::errhandlingapi::GetLastError;
47 use winapi::um::errhandlingapi::SetLastError;
48 use winapi::um::libloaderapi::GetModuleHandleW;
49 use winapi::um::shellscalingapi::GetDpiForMonitor;
50 use winapi::um::shellscalingapi::MDT_DEFAULT;
51 use winapi::um::shellscalingapi::MDT_RAW_DPI;
52 use winapi::um::wingdi::GetStockObject;
53 use winapi::um::wingdi::BLACK_BRUSH;
54 use winapi::um::winnt::LPCWSTR;
55 use winapi::um::winuser::*;
56 
57 use super::math_util::*;
58 use super::HostWindowSpace;
59 
60 #[cfg(feature = "gfxstream")]
61 #[link(name = "gfxstream_backend")]
62 extern "C" {
gfxstream_backend_setup_window( hwnd: *const c_void, window_x: c_int, window_y: c_int, window_width: c_int, window_height: c_int, fb_width: c_int, fb_height: c_int, )63     fn gfxstream_backend_setup_window(
64         hwnd: *const c_void,
65         window_x: c_int,
66         window_y: c_int,
67         window_width: c_int,
68         window_height: c_int,
69         fb_width: c_int,
70         fb_height: c_int,
71     );
72 }
73 
74 // Windows desktop's default DPI at default scaling settings is 96.
75 // (https://docs.microsoft.com/en-us/previous-versions/windows/desktop/mpc/pixel-density-and-usability)
76 pub(crate) const DEFAULT_HOST_DPI: i32 = 96;
77 
78 /// Stores a message retrieved from the message pump. We don't include the HWND since it is only
79 /// used for determining the recipient.
80 #[derive(Copy, Clone, Debug)]
81 pub struct MessagePacket {
82     pub msg: UINT,
83     pub w_param: WPARAM,
84     pub l_param: LPARAM,
85 }
86 
87 impl MessagePacket {
new(msg: UINT, w_param: WPARAM, l_param: LPARAM) -> Self88     pub fn new(msg: UINT, w_param: WPARAM, l_param: LPARAM) -> Self {
89         Self {
90             msg,
91             w_param,
92             l_param,
93         }
94     }
95 }
96 
97 impl From<MSG> for MessagePacket {
from(message: MSG) -> Self98     fn from(message: MSG) -> Self {
99         Self::new(message.message, message.wParam, message.lParam)
100     }
101 }
102 
103 /// The state of window moving or sizing modal loop.
104 ///
105 /// We do receive `WM_ENTERSIZEMOVE` when the window is about to be resized or moved, but it doesn't
106 /// tell us whether resizing or moving should be expected. We won't know that until later we receive
107 /// `WM_SIZING` or `WM_MOVING`. Corner cases are:
108 /// (1) If the user long presses the title bar, window borders or corners, and then releases without
109 ///     moving the mouse, we would receive both `WM_ENTERSIZEMOVE` and `WM_EXITSIZEMOVE`, but
110 ///     without any `WM_SIZING` or `WM_MOVING` in between.
111 /// (2) When the window is maximized, if we drag the title bar of it, it will be restored to the
112 ///     normal size and then move along with the cursor. In this case, we would expect
113 ///     `WM_ENTERSIZEMOVE` to be followed by one `WM_SIZING`, and then multiple `WM_MOVING`.
114 ///
115 /// This enum tracks the modal loop state. Possible state transition:
116 /// (1) NotInLoop -> WillResizeOrMove -> IsResizing -> NotInLoop. This is for sizing modal loops.
117 /// (2) NotInLoop -> WillResizeOrMove -> IsMoving -> NotInLoop. This is for moving modal loops.
118 /// (3) NotInLoop -> WillResizeOrMove -> NotInLoop. This may occur if the user long presses the
119 ///     window title bar, window borders or corners, but doesn't actually resize or move the window.
120 #[derive(Copy, Clone, Debug, PartialEq, Eq)]
121 enum SizeMoveLoopState {
122     /// The window is not in the moving or sizing modal loop.
123     NotInLoop,
124     /// We have received `WM_ENTERSIZEMOVE` but haven't received either `WM_SIZING` or `WM_MOVING`,
125     /// so we don't know if the window is going to be resized or moved at this point.
126     WillResizeOrMove,
127     /// We have received `WM_SIZING` after `WM_ENTERSIZEMOVE`. `is_first` indicates whether this is
128     /// the first `WM_SIZING`.
129     IsResizing { is_first: bool },
130     /// We have received `WM_MOVING` after `WM_ENTERSIZEMOVE`. `is_first` indicates whether this is
131     /// the first `WM_MOVING`.
132     IsMoving { is_first: bool },
133 }
134 
135 impl SizeMoveLoopState {
new() -> Self136     pub fn new() -> Self {
137         Self::NotInLoop
138     }
139 
update(&mut self, msg: UINT, w_param: WPARAM)140     pub fn update(&mut self, msg: UINT, w_param: WPARAM) {
141         match msg {
142             WM_ENTERSIZEMOVE => self.on_entering_loop(),
143             WM_EXITSIZEMOVE => self.on_exiting_loop(),
144             WM_SIZING => self.on_resizing_window(w_param),
145             WM_MOVING => self.on_moving_window(),
146             _ => (),
147         };
148     }
149 
is_in_loop(&self) -> bool150     pub fn is_in_loop(&self) -> bool {
151         *self != Self::NotInLoop
152     }
153 
is_resizing_starting(&self) -> bool154     pub fn is_resizing_starting(&self) -> bool {
155         *self == Self::IsResizing { is_first: true }
156     }
157 
on_entering_loop(&mut self)158     fn on_entering_loop(&mut self) {
159         info!("Entering window sizing/moving modal loop");
160         *self = Self::WillResizeOrMove;
161     }
162 
on_exiting_loop(&mut self)163     fn on_exiting_loop(&mut self) {
164         info!("Exiting window sizing/moving modal loop");
165         *self = Self::NotInLoop;
166     }
167 
on_resizing_window(&mut self, w_param: WPARAM)168     fn on_resizing_window(&mut self, w_param: WPARAM) {
169         match *self {
170             Self::NotInLoop => (),
171             Self::WillResizeOrMove => match w_param as u32 {
172                 // In these cases, the user is dragging window borders or corners for resizing.
173                 WMSZ_LEFT | WMSZ_RIGHT | WMSZ_TOP | WMSZ_BOTTOM | WMSZ_TOPLEFT | WMSZ_TOPRIGHT
174                 | WMSZ_BOTTOMLEFT | WMSZ_BOTTOMRIGHT => {
175                     info!("Window is being resized");
176                     *self = Self::IsResizing { is_first: true };
177                 }
178                 // In this case, the user is dragging the title bar of the maximized window. The
179                 // window will be restored to the normal size and then move along with the cursor,
180                 // so we can expect `WM_MOVING` coming and entering the moving modal loop.
181                 _ => info!("Window is being restored"),
182             },
183             Self::IsResizing { .. } => *self = Self::IsResizing { is_first: false },
184             Self::IsMoving { .. } => warn!("WM_SIZING is unexpected in moving modal loops!"),
185         }
186     }
187 
on_moving_window(&mut self)188     fn on_moving_window(&mut self) {
189         match *self {
190             Self::NotInLoop => (),
191             Self::WillResizeOrMove => {
192                 info!("Window is being moved");
193                 *self = Self::IsMoving { is_first: true };
194             }
195             Self::IsMoving { .. } => *self = Self::IsMoving { is_first: false },
196             Self::IsResizing { .. } => warn!("WM_MOVING is unexpected in sizing modal loops!"),
197         }
198     }
199 }
200 
201 /// This class helps create and operate on a window using Windows APIs. The owner of `Window` object
202 /// is responsible for:
203 /// (1) Calling `update_states()` when a new window message arrives.
204 /// (2) Dropping the `Window` object before the underlying window is completely gone.
205 pub struct Window {
206     hwnd: HWND,
207     size_move_loop_state: SizeMoveLoopState,
208 }
209 
210 impl Window {
211     /// # Safety
212     /// The owner of `Window` object is responsible for dropping it before we finish processing
213     /// `WM_NCDESTROY`, because the window handle will become invalid afterwards.
new( wnd_proc: WNDPROC, class_name: &str, title: &str, icon_resource_id: WORD, dw_style: DWORD, initial_window_size: &Size2D<i32, HostWindowSpace>, ) -> Result<Self>214     pub unsafe fn new(
215         wnd_proc: WNDPROC,
216         class_name: &str,
217         title: &str,
218         icon_resource_id: WORD,
219         dw_style: DWORD,
220         initial_window_size: &Size2D<i32, HostWindowSpace>,
221     ) -> Result<Self> {
222         info!("Creating window");
223         static CONTEXT_MESSAGE: &str = "When creating Window";
224 
225         let hinstance = Self::get_current_module_handle();
226         // If we fail to load any UI element below, use NULL to let the system use the default UI
227         // rather than crash.
228         let hicon = Self::load_custom_icon(hinstance, icon_resource_id).unwrap_or(null_mut());
229         let hcursor = Self::load_system_cursor(IDC_ARROW).unwrap_or(null_mut());
230         let hbrush_background = Self::create_opaque_black_brush().unwrap_or(null_mut());
231 
232         Self::register_window_class(
233             wnd_proc,
234             hinstance,
235             class_name,
236             hicon,
237             hcursor,
238             hbrush_background,
239         )
240         .context(CONTEXT_MESSAGE)?;
241 
242         let hwnd =
243             Self::create_sys_window(hinstance, class_name, title, dw_style, initial_window_size)
244                 .context(CONTEXT_MESSAGE)?;
245 
246         Ok(Self {
247             hwnd,
248             size_move_loop_state: SizeMoveLoopState::new(),
249         })
250     }
251 
252     /// # Safety
253     /// The returned handle should be used carefully, since it may have become invalid if it
254     /// outlives the `Window` object.
handle(&self) -> HWND255     pub unsafe fn handle(&self) -> HWND {
256         self.hwnd
257     }
258 
is_same_window(&self, hwnd: HWND) -> bool259     pub fn is_same_window(&self, hwnd: HWND) -> bool {
260         hwnd == self.hwnd
261     }
262 
update_states(&mut self, msg: UINT, w_param: WPARAM)263     pub fn update_states(&mut self, msg: UINT, w_param: WPARAM) {
264         self.size_move_loop_state.update(msg, w_param);
265     }
266 
is_sizing_or_moving(&self) -> bool267     pub fn is_sizing_or_moving(&self) -> bool {
268         self.size_move_loop_state.is_in_loop()
269     }
270 
is_resizing_loop_starting(&self) -> bool271     pub fn is_resizing_loop_starting(&self) -> bool {
272         self.size_move_loop_state.is_resizing_starting()
273     }
274 
275     /// Calls `IsWindow()` internally. Returns true if the HWND identifies an existing window.
is_valid(&self) -> bool276     pub fn is_valid(&self) -> bool {
277         // Safe because it is called from the same thread the created the window.
278         unsafe { IsWindow(self.hwnd) != 0 }
279     }
280 
281     /// Calls `SetPropW()` internally.
set_property(&self, property: &str, data: *mut c_void) -> Result<()>282     pub fn set_property(&self, property: &str, data: *mut c_void) -> Result<()> {
283         // Safe because `Window` object won't outlive the HWND, and failures are handled below.
284         unsafe {
285             if SetPropW(self.hwnd, win32_wide_string(property).as_ptr(), data) == 0 {
286                 syscall_bail!("Failed to call SetPropW()");
287             }
288         }
289         Ok(())
290     }
291 
292     /// Calls `RemovePropW()` internally.
remove_property(&self, property: &str) -> Result<()>293     pub fn remove_property(&self, property: &str) -> Result<()> {
294         // Safe because `Window` object won't outlive the HWND, and failures are handled below.
295         unsafe {
296             SetLastError(0);
297             RemovePropW(self.hwnd, win32_wide_string(property).as_ptr());
298             if GetLastError() != 0 {
299                 syscall_bail!("Failed to call RemovePropW()");
300             }
301         }
302         Ok(())
303     }
304 
305     /// Updates the rectangle in the window's client area to which gfxstream renders.
update_virtual_display_projection( &self, #[allow(unused)] projection_box: &Box2D<i32, HostWindowSpace>, )306     pub fn update_virtual_display_projection(
307         &self,
308         #[allow(unused)] projection_box: &Box2D<i32, HostWindowSpace>,
309     ) {
310         // Safe because `Window` object won't outlive the HWND.
311         #[cfg(feature = "gfxstream")]
312         unsafe {
313             gfxstream_backend_setup_window(
314                 self.hwnd as *const c_void,
315                 projection_box.min.x,
316                 projection_box.min.y,
317                 projection_box.width(),
318                 projection_box.height(),
319                 projection_box.width(),
320                 projection_box.height(),
321             );
322         }
323     }
324 
325     /// Calls `GetWindowLongPtrW()` internally.
get_attribute(&self, index: i32) -> Result<isize>326     pub fn get_attribute(&self, index: i32) -> Result<isize> {
327         // Safe because `Window` object won't outlive the HWND, and failures are handled below.
328         unsafe {
329             // GetWindowLongPtrW() may return zero if we haven't set that attribute before, so we
330             // need to check if the error code is non-zero.
331             SetLastError(0);
332             let value = GetWindowLongPtrW(self.hwnd, index);
333             if value == 0 && GetLastError() != 0 {
334                 syscall_bail!("Failed to call GetWindowLongPtrW()");
335             }
336             Ok(value)
337         }
338     }
339 
340     /// Calls `SetWindowLongPtrW()` internally.
set_attribute(&self, index: i32, value: isize) -> Result<()>341     pub fn set_attribute(&self, index: i32, value: isize) -> Result<()> {
342         // Safe because `Window` object won't outlive the HWND, and failures are handled below.
343         unsafe {
344             // SetWindowLongPtrW() may return zero if the previous value of that attribute was zero,
345             // so we need to check if the error code is non-zero.
346             SetLastError(0);
347             let prev_value = SetWindowLongPtrW(self.hwnd, index, value);
348             if prev_value == 0 && GetLastError() != 0 {
349                 syscall_bail!("Failed to call SetWindowLongPtrW()");
350             }
351             Ok(())
352         }
353     }
354 
355     /// Calls `GetWindowRect()` internally.
get_window_rect(&self) -> Result<Rect>356     pub fn get_window_rect(&self) -> Result<Rect> {
357         let mut rect: RECT = Default::default();
358         // Safe because `Window` object won't outlive the HWND, we know `rect` is valid, and
359         // failures are handled below.
360         unsafe {
361             if GetWindowRect(self.hwnd, &mut rect) == 0 {
362                 syscall_bail!("Failed to call GetWindowRect()");
363             }
364         }
365         Ok(rect.to_rect())
366     }
367 
368     /// Calls `GetWindowRect()` internally.
get_window_origin(&self) -> Result<Point>369     pub fn get_window_origin(&self) -> Result<Point> {
370         Ok(self.get_window_rect()?.origin)
371     }
372 
373     /// Calls `GetClientRect()` internally.
get_client_rect(&self) -> Result<Rect>374     pub fn get_client_rect(&self) -> Result<Rect> {
375         let mut rect: RECT = Default::default();
376         // Safe because `Window` object won't outlive the HWND, we know `rect` is valid, and
377         // failures are handled below.
378         unsafe {
379             if GetClientRect(self.hwnd, &mut rect) == 0 {
380                 syscall_bail!("Failed to call GetClientRect()");
381             }
382         }
383         Ok(rect.to_rect())
384     }
385 
386     /// The system may add adornments around the client area of the window, such as the title bar
387     /// and borders. This function returns the size of all those paddings. It can be assumed that:
388     /// window_size = client_size + window_padding_size
get_window_padding_size(&self, dw_style: u32) -> Result<Size>389     pub fn get_window_padding_size(&self, dw_style: u32) -> Result<Size> {
390         static CONTEXT_MESSAGE: &str = "When calculating window padding";
391         // The padding is always the same in windowed mode, hence we can use an arbitrary rect.
392         let client_rect = Rect::new(point2(0, 0), size2(500, 500));
393         let dw_ex_style = self.get_attribute(GWL_EXSTYLE).context(CONTEXT_MESSAGE)?;
394         let window_rect: Rect = self
395             .get_adjusted_window_rect(&client_rect, dw_style, dw_ex_style as u32)
396             .context(CONTEXT_MESSAGE)?;
397         Ok(window_rect.size - client_rect.size)
398     }
399 
400     /// Calls `ClientToScreen()` internally. Converts the window client area coordinates of a
401     /// specified point to screen coordinates.
client_to_screen(&self, point: &Point) -> Result<Point>402     pub fn client_to_screen(&self, point: &Point) -> Result<Point> {
403         let mut point = point.to_sys_point();
404         // Safe because `Window` object won't outlive the HWND, we know `point` is valid, and
405         // failures are handled below.
406         unsafe {
407             if ClientToScreen(self.hwnd, &mut point) == 0 {
408                 syscall_bail!("Failed to call ClientToScreen()");
409             }
410         }
411         Ok(point.to_point())
412     }
413 
414     /// Calls `MonitorFromWindow()` internally. If the window is not on any active display monitor,
415     /// returns the handle to the closest one.
get_nearest_monitor_handle(&self) -> HMONITOR416     pub fn get_nearest_monitor_handle(&self) -> HMONITOR {
417         // Safe because `Window` object won't outlive the HWND.
418         unsafe { MonitorFromWindow(self.hwnd, MONITOR_DEFAULTTONEAREST) }
419     }
420 
421     /// Calls `MonitorFromWindow()` internally. If the window is not on any active display monitor,
422     /// returns the info of the closest one.
get_monitor_info(&self) -> Result<MonitorInfo>423     pub fn get_monitor_info(&self) -> Result<MonitorInfo> {
424         // Safe because `get_nearest_monitor_handle()` always returns a valid monitor handle.
425         unsafe { MonitorInfo::new(self.get_nearest_monitor_handle()) }
426     }
427 
428     /// Calls `MonitorFromWindow()` internally.
is_on_active_display(&self) -> bool429     pub fn is_on_active_display(&self) -> bool {
430         // Safe because `Window` object won't outlive the HWND.
431         unsafe { !MonitorFromWindow(self.hwnd, MONITOR_DEFAULTTONULL).is_null() }
432     }
433 
434     /// Calls `SetWindowPos()` internally.
set_pos(&self, window_rect: &Rect, flags: u32) -> Result<()>435     pub fn set_pos(&self, window_rect: &Rect, flags: u32) -> Result<()> {
436         // Safe because `Window` object won't outlive the HWND, and failures are handled below.
437         unsafe {
438             if SetWindowPos(
439                 self.hwnd,
440                 null_mut(),
441                 window_rect.origin.x,
442                 window_rect.origin.y,
443                 window_rect.size.width,
444                 window_rect.size.height,
445                 flags,
446             ) == 0
447             {
448                 syscall_bail!("Failed to call SetWindowPos()");
449             }
450             Ok(())
451         }
452     }
453 
454     /// Calls `SetWindowPos()` internally. If window size and position need to be changed as well,
455     /// prefer to call `set_pos()` with the `SWP_FRAMECHANGED` flag instead.
flush_window_style_change(&self) -> Result<()>456     pub fn flush_window_style_change(&self) -> Result<()> {
457         // Because of `SWP_NOMOVE` and `SWP_NOSIZE` flags, we can pass in arbitrary window size and
458         // position as they will be ignored.
459         self.set_pos(
460             &Rect::zero(),
461             SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_FRAMECHANGED,
462         )
463     }
464 
465     /// Calls `ShowWindow()` internally. Note that it is more preferable to call `set_pos()` with
466     /// `SWP_SHOWWINDOW` since that would set the error code on failure.
show(&self)467     pub fn show(&self) {
468         // Safe because `Window` object won't outlive the HWND.
469         unsafe {
470             ShowWindow(self.hwnd, SW_SHOW);
471         }
472     }
473 
474     /// Calls `IsZoomed()` internally. Note that the window may carry the WS_MAXIMIZE flag until it
475     /// is restored. For example, if we have switched from maximized to fullscreen, this function
476     /// would still return true.
was_maximized(&self) -> bool477     pub fn was_maximized(&self) -> bool {
478         // Safe because `Window` object won't outlive the HWND.
479         unsafe { IsZoomed(self.hwnd) != 0 }
480     }
481 
482     /// Calls `IsWindowVisible()` internally. We also require that the window size is nonzero to be
483     /// considered visible.
is_visible(&self) -> Result<bool>484     pub fn is_visible(&self) -> Result<bool> {
485         // Safe because `Window` object won't outlive the HWND.
486         if unsafe { IsWindowVisible(self.hwnd) } != 0 {
487             let window_rect = self
488                 .get_window_rect()
489                 .context("When querying window visibility")?;
490             if window_rect.size != Size::zero() {
491                 return Ok(true);
492             } else {
493                 info!("Window has WS_VISIBLE flag but its size is zero");
494             }
495         }
496         Ok(false)
497     }
498 
get_visibility(&self) -> Result<WindowVisibility>499     pub fn get_visibility(&self) -> Result<WindowVisibility> {
500         Ok(if !self.is_visible()? {
501             WindowVisibility::Hidden
502         } else if self.is_minimized() {
503             WindowVisibility::Minimized
504         } else {
505             WindowVisibility::Normal
506         })
507     }
508 
509     /// Calls `GetForegroundWindow()` internally. A foreground window is the window with which the
510     /// user is currently working. It might belong to a different thread/process than the calling
511     /// thread.
is_global_foreground_window(&self) -> bool512     pub fn is_global_foreground_window(&self) -> bool {
513         // Safe because there is no argument.
514         unsafe { GetForegroundWindow() == self.hwnd }
515     }
516 
517     /// Calls `GetActiveWindow()` internally. An active window is the window with which the user is
518     /// currently working and is attached to the calling thread's message queue. It is possible that
519     /// there is no active window if the foreground focus is on another thread/process.
is_thread_foreground_window(&self) -> bool520     pub fn is_thread_foreground_window(&self) -> bool {
521         // Safe because there is no argument.
522         unsafe { GetActiveWindow() == self.hwnd }
523     }
524 
525     /// Calls `IsIconic()` internally.
is_minimized(&self) -> bool526     pub fn is_minimized(&self) -> bool {
527         // Safe because `Window` object won't outlive the HWND.
528         unsafe { IsIconic(self.hwnd) != 0 }
529     }
530 
531     /// Calls `SetForegroundWindow()` internally. `SetForegroundWindow()` may fail, for example,
532     /// when the taskbar is in the foreground, hence this is a best-effort call.
bring_to_foreground(&self)533     pub fn bring_to_foreground(&self) {
534         // Safe because `Window` object won't outlive the HWND.
535         if unsafe { SetForegroundWindow(self.hwnd) } == 0 {
536             info!("Cannot bring the window to foreground.");
537         }
538     }
539 
540     /// Calls `DwmEnableBlurBehindWindow()` internally. This is only used for a top-level window.
541     /// Even though the name of Windows API suggests that it blurs the background, beginning with
542     /// Windows 8, it does not blur it, but only makes the window semi-transparent.
set_transparency(&self, is_transparent: bool) -> Result<()>543     pub fn set_transparency(&self, is_transparent: bool) -> Result<()> {
544         let blur_behind = DWM_BLURBEHIND {
545             dwFlags: DWM_BB_ENABLE,
546             fEnable: if is_transparent { TRUE } else { FALSE },
547             hRgnBlur: null_mut(),
548             fTransitionOnMaximized: FALSE,
549         };
550         // Safe because `Window` object won't outlive the HWND, we know `blur_behind` is valid,
551         // and failures are handled below.
552         let errno = unsafe { DwmEnableBlurBehindWindow(self.hwnd, &blur_behind) };
553         match errno {
554             0 => Ok(()),
555             _ => bail!(
556                 "Failed to call DwmEnableBlurBehindWindow() when setting \
557                 window transparency to {} (Error code {})",
558                 is_transparent,
559                 errno
560             ),
561         }
562     }
563 
564     /// Calls `AdjustWindowRectExForDpi()` internally.
get_adjusted_window_rect( &self, client_rect: &Rect, dw_style: u32, dw_ex_style: u32, ) -> Result<Rect>565     pub fn get_adjusted_window_rect(
566         &self,
567         client_rect: &Rect,
568         dw_style: u32,
569         dw_ex_style: u32,
570     ) -> Result<Rect> {
571         let mut window_rect: RECT = client_rect.to_sys_rect();
572         // Safe because `Window` object won't outlive the HWND, we know `window_rect` is valid,
573         // and failures are handled below.
574         unsafe {
575             if AdjustWindowRectExForDpi(
576                 &mut window_rect,
577                 dw_style,
578                 FALSE,
579                 dw_ex_style,
580                 GetDpiForSystem(),
581             ) == 0
582             {
583                 syscall_bail!("Failed to call AdjustWindowRectExForDpi()");
584             }
585         }
586         Ok(window_rect.to_rect())
587     }
588 
589     /// Calls `GetWindowPlacement()` and `SetWindowPlacement()` internally.
set_restored_pos(&self, window_rect: &Rect) -> Result<()>590     pub fn set_restored_pos(&self, window_rect: &Rect) -> Result<()> {
591         let mut window_placement = WINDOWPLACEMENT {
592             length: mem::size_of::<WINDOWPLACEMENT>().try_into().unwrap(),
593             ..Default::default()
594         };
595         // Safe because `Window` object won't outlive the HWND, we know `window_placement` is valid,
596         // and failures are handled below.
597         unsafe {
598             if GetWindowPlacement(self.hwnd, &mut window_placement) == 0 {
599                 syscall_bail!("Failed to call GetWindowPlacement()");
600             }
601             window_placement.rcNormalPosition = window_rect.to_sys_rect();
602             if SetWindowPlacement(self.hwnd, &window_placement) == 0 {
603                 syscall_bail!("Failed to call SetWindowPlacement()");
604             }
605         }
606         Ok(())
607     }
608 
609     /// Calls `PostMessageW()` internally.
post_message(&self, msg: UINT, w_param: WPARAM, l_param: LPARAM) -> Result<()>610     pub fn post_message(&self, msg: UINT, w_param: WPARAM, l_param: LPARAM) -> Result<()> {
611         // Safe because `Window` object won't outlive the HWND.
612         unsafe {
613             if PostMessageW(self.hwnd, msg, w_param, l_param) == 0 {
614                 syscall_bail!("Failed to call PostMessageW()");
615             }
616         }
617         Ok(())
618     }
619 
620     /// Calls `DestroyWindow()` internally.
destroy(&self) -> Result<()>621     pub fn destroy(&self) -> Result<()> {
622         // Safe because `Window` object won't outlive the HWND.
623         unsafe {
624             if DestroyWindow(self.hwnd) == 0 {
625                 syscall_bail!("Failed to call DestroyWindow()");
626             }
627         }
628         Ok(())
629     }
630 
631     /// Calls `DefWindowProcW()` internally.
default_process_message(&self, packet: &MessagePacket) -> LRESULT632     pub fn default_process_message(&self, packet: &MessagePacket) -> LRESULT {
633         // Safe because `Window` object won't outlive the HWND.
634         unsafe { DefWindowProcW(self.hwnd, packet.msg, packet.w_param, packet.l_param) }
635     }
636 
637     /// Calls `GetModuleHandleW()` internally.
get_current_module_handle() -> HMODULE638     fn get_current_module_handle() -> HMODULE {
639         // Safe because we handle failures below.
640         let hmodule = unsafe { GetModuleHandleW(null_mut()) };
641         if hmodule.is_null() {
642             // If it fails, we are in a very broken state and it doesn't make sense to keep running.
643             panic!(
644                 "Failed to call GetModuleHandleW() for the current module (Error code {})",
645                 unsafe { GetLastError() }
646             );
647         }
648         hmodule
649     }
650 
651     /// Calls `LoadIconW()` internally.
load_custom_icon(hinstance: HINSTANCE, resource_id: WORD) -> Result<HICON>652     fn load_custom_icon(hinstance: HINSTANCE, resource_id: WORD) -> Result<HICON> {
653         // Safe because we handle failures below.
654         unsafe {
655             let hicon = LoadIconW(hinstance, MAKEINTRESOURCEW(resource_id));
656             if hicon.is_null() {
657                 syscall_bail!("Failed to call LoadIconW()");
658             }
659             Ok(hicon)
660         }
661     }
662 
663     /// Calls `LoadCursorW()` internally.
load_system_cursor(cursor_id: LPCWSTR) -> Result<HCURSOR>664     fn load_system_cursor(cursor_id: LPCWSTR) -> Result<HCURSOR> {
665         // Safe because we handle failures below.
666         unsafe {
667             let hcursor = LoadCursorW(null_mut(), cursor_id);
668             if hcursor.is_null() {
669                 syscall_bail!("Failed to call LoadCursorW()");
670             }
671             Ok(hcursor)
672         }
673     }
674 
675     /// Calls `GetStockObject()` internally.
create_opaque_black_brush() -> Result<HBRUSH>676     fn create_opaque_black_brush() -> Result<HBRUSH> {
677         // Safe because we handle failures below.
678         unsafe {
679             let hobject = GetStockObject(BLACK_BRUSH as i32);
680             if hobject.is_null() {
681                 syscall_bail!("Failed to call GetStockObject()");
682             }
683             Ok(hobject as HBRUSH)
684         }
685     }
686 
register_window_class( wnd_proc: WNDPROC, hinstance: HINSTANCE, class_name: &str, hicon: HICON, hcursor: HCURSOR, hbrush_background: HBRUSH, ) -> Result<()>687     fn register_window_class(
688         wnd_proc: WNDPROC,
689         hinstance: HINSTANCE,
690         class_name: &str,
691         hicon: HICON,
692         hcursor: HCURSOR,
693         hbrush_background: HBRUSH,
694     ) -> Result<()> {
695         let class_name = win32_wide_string(class_name);
696         let window_class = WNDCLASSEXW {
697             cbSize: std::mem::size_of::<WNDCLASSEXW>() as u32,
698             style: CS_OWNDC | CS_HREDRAW | CS_VREDRAW,
699             lpfnWndProc: wnd_proc,
700             cbClsExtra: 0,
701             cbWndExtra: 0,
702             hInstance: hinstance,
703             hIcon: hicon,
704             hCursor: hcursor,
705             hbrBackground: hbrush_background,
706             lpszMenuName: null_mut(),
707             lpszClassName: class_name.as_ptr(),
708             hIconSm: hicon,
709         };
710 
711         // Safe because we know the lifetime of `window_class`, and we handle failures below.
712         unsafe {
713             if RegisterClassExW(&window_class) == 0 {
714                 syscall_bail!("Failed to call RegisterClassExW()");
715             }
716             Ok(())
717         }
718     }
719 
create_sys_window( hinstance: HINSTANCE, class_name: &str, title: &str, dw_style: DWORD, initial_window_size: &Size2D<i32, HostWindowSpace>, ) -> Result<HWND>720     fn create_sys_window(
721         hinstance: HINSTANCE,
722         class_name: &str,
723         title: &str,
724         dw_style: DWORD,
725         initial_window_size: &Size2D<i32, HostWindowSpace>,
726     ) -> Result<HWND> {
727         // Safe because we handle failures below.
728         unsafe {
729             let hwnd = CreateWindowExW(
730                 0,
731                 win32_wide_string(class_name).as_ptr(),
732                 win32_wide_string(title).as_ptr(),
733                 dw_style,
734                 0,
735                 0,
736                 initial_window_size.width,
737                 initial_window_size.height,
738                 null_mut(),
739                 null_mut(),
740                 hinstance,
741                 null_mut(),
742             );
743             if hwnd.is_null() {
744                 syscall_bail!("Failed to call CreateWindowExW()");
745             }
746             Ok(hwnd)
747         }
748     }
749 }
750 
751 impl Drop for Window {
drop(&mut self)752     fn drop(&mut self) {
753         // Safe because it is called from the same thread the created the window.
754         if unsafe { IsWindow(self.hwnd) } == 0 {
755             error!("The underlying HWND is invalid when Window is being dropped!")
756         }
757     }
758 }
759 
760 /// If the resolution/orientation of the monitor changes, or if the monitor is unplugged, this must
761 /// be recreated with a valid HMONITOR.
762 pub struct MonitorInfo {
763     pub hmonitor: HMONITOR,
764     pub display_rect: Rect,
765     pub work_rect: Rect,
766     pub dpi: i32,
767 }
768 
769 impl MonitorInfo {
770     /// # Safety
771     /// Caller is responsible for ensuring that `hmonitor` is a valid handle.
new(hmonitor: HMONITOR) -> Result<Self>772     pub unsafe fn new(hmonitor: HMONITOR) -> Result<Self> {
773         let monitor_info: MONITORINFO =
774             Self::get_monitor_info(hmonitor).context("When creating MonitorInfo")?;
775         Ok(Self {
776             hmonitor,
777             display_rect: monitor_info.rcMonitor.to_rect(),
778             work_rect: monitor_info.rcWork.to_rect(),
779             dpi: Self::get_dpi(hmonitor),
780         })
781     }
782 
783     /// Calls `GetMonitorInfoW()` internally.
784     /// # Safety
785     /// Caller is responsible for ensuring that `hmonitor` is a valid handle.
get_monitor_info(hmonitor: HMONITOR) -> Result<MONITORINFO>786     unsafe fn get_monitor_info(hmonitor: HMONITOR) -> Result<MONITORINFO> {
787         let mut monitor_info = MONITORINFO {
788             cbSize: mem::size_of::<MONITORINFO>().try_into().unwrap(),
789             ..Default::default()
790         };
791         if GetMonitorInfoW(hmonitor, &mut monitor_info) == 0 {
792             syscall_bail!("Failed to call GetMonitorInfoW()");
793         }
794         Ok(monitor_info)
795     }
796 
797     /// Calls `GetDpiForMonitor()` internally.
get_dpi(hmonitor: HMONITOR) -> i32798     fn get_dpi(hmonitor: HMONITOR) -> i32 {
799         let mut dpi_x = 0;
800         let mut dpi_y = 0;
801         // This is always safe since `GetDpiForMonitor` won't crash if HMONITOR is invalid, but
802         // return E_INVALIDARG.
803         unsafe {
804             if GetDpiForMonitor(hmonitor, MDT_RAW_DPI, &mut dpi_x, &mut dpi_y) == S_OK
805                 || GetDpiForMonitor(hmonitor, MDT_DEFAULT, &mut dpi_x, &mut dpi_y) == S_OK
806             {
807                 // We assume screen pixels are square and DPI in different directions are the same.
808                 dpi_x as i32
809             } else {
810                 error!("Failed to retrieve DPI with HMONITOR {:p}", hmonitor);
811                 DEFAULT_HOST_DPI
812             }
813         }
814     }
815 }
816 
817 impl fmt::Debug for MonitorInfo {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result818     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
819         write!(
820             f,
821             "{{hmonitor: {:p}, display_rect: {:?}, work_rect: {:?}, DPI: {}}}",
822             self.hmonitor, self.display_rect, self.work_rect, self.dpi,
823         )
824     }
825 }
826