xref: /aosp_15_r20/external/crosvm/gpu_display/src/gpu_display_win/window.rs (revision bb4ee6a4ae7042d18b07a98463b9c8b875e44b39)
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 use std::os::raw::c_void;
9 use std::ptr::null_mut;
10 
11 use anyhow::bail;
12 use anyhow::Context;
13 use anyhow::Result;
14 use base::error;
15 use base::info;
16 use base::warn;
17 use euclid::point2;
18 use euclid::size2;
19 use euclid::Point2D;
20 use euclid::Size2D;
21 use win_util::syscall_bail;
22 use win_util::win32_wide_string;
23 use winapi::shared::minwindef::DWORD;
24 use winapi::shared::minwindef::FALSE;
25 use winapi::shared::minwindef::HINSTANCE;
26 use winapi::shared::minwindef::HMODULE;
27 use winapi::shared::minwindef::LPARAM;
28 use winapi::shared::minwindef::LRESULT;
29 use winapi::shared::minwindef::TRUE;
30 use winapi::shared::minwindef::UINT;
31 use winapi::shared::minwindef::WORD;
32 use winapi::shared::minwindef::WPARAM;
33 use winapi::shared::windef::HBRUSH;
34 use winapi::shared::windef::HCURSOR;
35 use winapi::shared::windef::HICON;
36 use winapi::shared::windef::HMONITOR;
37 use winapi::shared::windef::HWND;
38 use winapi::shared::windef::RECT;
39 use winapi::shared::winerror::S_OK;
40 use winapi::um::dwmapi::DwmEnableBlurBehindWindow;
41 use winapi::um::dwmapi::DWM_BB_ENABLE;
42 use winapi::um::dwmapi::DWM_BLURBEHIND;
43 use winapi::um::errhandlingapi::GetLastError;
44 use winapi::um::errhandlingapi::SetLastError;
45 use winapi::um::libloaderapi::GetModuleHandleW;
46 use winapi::um::shellscalingapi::GetDpiForMonitor;
47 use winapi::um::shellscalingapi::MDT_DEFAULT;
48 use winapi::um::shellscalingapi::MDT_RAW_DPI;
49 use winapi::um::wingdi::GetStockObject;
50 use winapi::um::wingdi::BLACK_BRUSH;
51 use winapi::um::winnt::LPCWSTR;
52 use winapi::um::winuser::AdjustWindowRectExForDpi;
53 use winapi::um::winuser::ClientToScreen;
54 use winapi::um::winuser::CreateWindowExW;
55 use winapi::um::winuser::DefWindowProcW;
56 use winapi::um::winuser::DestroyWindow;
57 use winapi::um::winuser::GetActiveWindow;
58 use winapi::um::winuser::GetClientRect;
59 use winapi::um::winuser::GetDpiForSystem;
60 use winapi::um::winuser::GetForegroundWindow;
61 use winapi::um::winuser::GetMonitorInfoW;
62 use winapi::um::winuser::GetSystemMetrics;
63 use winapi::um::winuser::GetWindowLongPtrW;
64 use winapi::um::winuser::GetWindowPlacement;
65 use winapi::um::winuser::GetWindowRect;
66 use winapi::um::winuser::IsIconic;
67 use winapi::um::winuser::IsWindow;
68 use winapi::um::winuser::IsWindowVisible;
69 use winapi::um::winuser::IsZoomed;
70 use winapi::um::winuser::LoadCursorW;
71 use winapi::um::winuser::LoadIconW;
72 use winapi::um::winuser::MonitorFromWindow;
73 use winapi::um::winuser::PostMessageW;
74 use winapi::um::winuser::RegisterRawInputDevices;
75 use winapi::um::winuser::RegisterTouchWindow;
76 use winapi::um::winuser::RemovePropW;
77 use winapi::um::winuser::ScreenToClient;
78 use winapi::um::winuser::SetForegroundWindow;
79 use winapi::um::winuser::SetPropW;
80 use winapi::um::winuser::SetWindowLongPtrW;
81 use winapi::um::winuser::SetWindowPlacement;
82 use winapi::um::winuser::SetWindowPos;
83 use winapi::um::winuser::ShowWindow;
84 use winapi::um::winuser::GWL_EXSTYLE;
85 use winapi::um::winuser::HWND_MESSAGE;
86 use winapi::um::winuser::MAKEINTRESOURCEW;
87 use winapi::um::winuser::MONITORINFO;
88 use winapi::um::winuser::MONITOR_DEFAULTTONEAREST;
89 use winapi::um::winuser::MONITOR_DEFAULTTONULL;
90 use winapi::um::winuser::MSG;
91 use winapi::um::winuser::PCRAWINPUTDEVICE;
92 use winapi::um::winuser::RAWINPUTDEVICE;
93 use winapi::um::winuser::SM_REMOTESESSION;
94 use winapi::um::winuser::SWP_FRAMECHANGED;
95 use winapi::um::winuser::SWP_HIDEWINDOW;
96 use winapi::um::winuser::SWP_NOACTIVATE;
97 use winapi::um::winuser::SWP_NOMOVE;
98 use winapi::um::winuser::SWP_NOSIZE;
99 use winapi::um::winuser::SWP_NOZORDER;
100 use winapi::um::winuser::SW_RESTORE;
101 use winapi::um::winuser::SW_SHOW;
102 use winapi::um::winuser::WINDOWPLACEMENT;
103 use winapi::um::winuser::WMSZ_BOTTOM;
104 use winapi::um::winuser::WMSZ_BOTTOMLEFT;
105 use winapi::um::winuser::WMSZ_BOTTOMRIGHT;
106 use winapi::um::winuser::WMSZ_LEFT;
107 use winapi::um::winuser::WMSZ_RIGHT;
108 use winapi::um::winuser::WMSZ_TOP;
109 use winapi::um::winuser::WMSZ_TOPLEFT;
110 use winapi::um::winuser::WMSZ_TOPRIGHT;
111 use winapi::um::winuser::WM_ENTERSIZEMOVE;
112 use winapi::um::winuser::WM_EXITSIZEMOVE;
113 use winapi::um::winuser::WM_MOVING;
114 use winapi::um::winuser::WM_SIZING;
115 
116 use super::math_util::*;
117 use super::HostWindowSpace;
118 
119 // Windows desktop's default DPI at default scaling settings is 96.
120 // (https://docs.microsoft.com/en-us/previous-versions/windows/desktop/mpc/pixel-density-and-usability)
121 pub(crate) const DEFAULT_HOST_DPI: i32 = 96;
122 
123 /// Stores a message retrieved from the message pump. We don't include the HWND since it is only
124 /// used for determining the recipient.
125 #[derive(Copy, Clone, Debug)]
126 pub struct MessagePacket {
127     pub msg: UINT,
128     pub w_param: WPARAM,
129     pub l_param: LPARAM,
130 }
131 
132 impl MessagePacket {
new(msg: UINT, w_param: WPARAM, l_param: LPARAM) -> Self133     pub fn new(msg: UINT, w_param: WPARAM, l_param: LPARAM) -> Self {
134         Self {
135             msg,
136             w_param,
137             l_param,
138         }
139     }
140 }
141 
142 impl From<MSG> for MessagePacket {
from(message: MSG) -> Self143     fn from(message: MSG) -> Self {
144         Self::new(message.message, message.wParam, message.lParam)
145     }
146 }
147 
148 /// The state of window moving or sizing modal loop.
149 ///
150 /// We do receive `WM_ENTERSIZEMOVE` when the window is about to be resized or moved, but it doesn't
151 /// tell us whether resizing or moving should be expected. We won't know that until later we receive
152 /// `WM_SIZING` or `WM_MOVING`. Corner cases are:
153 /// (1) If the user long presses the title bar, window borders or corners, and then releases without
154 ///     moving the mouse, we would receive both `WM_ENTERSIZEMOVE` and `WM_EXITSIZEMOVE`, but
155 ///     without any `WM_SIZING` or `WM_MOVING` in between.
156 /// (2) When the window is maximized, if we drag the title bar of it, it will be restored to the
157 ///     normal size and then move along with the cursor. In this case, we would expect
158 ///     `WM_ENTERSIZEMOVE` to be followed by one `WM_SIZING`, and then multiple `WM_MOVING`.
159 ///
160 /// This enum tracks the modal loop state. Possible state transition:
161 /// (1) NotInLoop -> WillResizeOrMove -> IsResizing -> NotInLoop. This is for sizing modal loops.
162 /// (2) NotInLoop -> WillResizeOrMove -> IsMoving -> NotInLoop. This is for moving modal loops.
163 /// (3) NotInLoop -> WillResizeOrMove -> NotInLoop. This may occur if the user long presses the
164 ///     window title bar, window borders or corners, but doesn't actually resize or move the window.
165 #[derive(Copy, Clone, Debug, PartialEq, Eq)]
166 enum SizeMoveLoopState {
167     /// The window is not in the moving or sizing modal loop.
168     NotInLoop,
169     /// We have received `WM_ENTERSIZEMOVE` but haven't received either `WM_SIZING` or `WM_MOVING`,
170     /// so we don't know if the window is going to be resized or moved at this point.
171     WillResizeOrMove,
172     /// We have received `WM_SIZING` after `WM_ENTERSIZEMOVE`. `is_first` indicates whether this is
173     /// the first `WM_SIZING`.
174     IsResizing { is_first: bool },
175     /// We have received `WM_MOVING` after `WM_ENTERSIZEMOVE`. `is_first` indicates whether this is
176     /// the first `WM_MOVING`.
177     IsMoving { is_first: bool },
178 }
179 
180 impl SizeMoveLoopState {
new() -> Self181     pub fn new() -> Self {
182         Self::NotInLoop
183     }
184 
update(&mut self, msg: UINT, w_param: WPARAM)185     pub fn update(&mut self, msg: UINT, w_param: WPARAM) {
186         match msg {
187             WM_ENTERSIZEMOVE => self.on_entering_loop(),
188             WM_EXITSIZEMOVE => self.on_exiting_loop(),
189             WM_SIZING => self.on_resizing_window(w_param),
190             WM_MOVING => self.on_moving_window(),
191             _ => (),
192         };
193     }
194 
is_in_loop(&self) -> bool195     pub fn is_in_loop(&self) -> bool {
196         *self != Self::NotInLoop
197     }
198 
is_resizing_starting(&self) -> bool199     pub fn is_resizing_starting(&self) -> bool {
200         *self == Self::IsResizing { is_first: true }
201     }
202 
on_entering_loop(&mut self)203     fn on_entering_loop(&mut self) {
204         info!("Entering window sizing/moving modal loop");
205         *self = Self::WillResizeOrMove;
206     }
207 
on_exiting_loop(&mut self)208     fn on_exiting_loop(&mut self) {
209         info!("Exiting window sizing/moving modal loop");
210         *self = Self::NotInLoop;
211     }
212 
on_resizing_window(&mut self, w_param: WPARAM)213     fn on_resizing_window(&mut self, w_param: WPARAM) {
214         match *self {
215             Self::NotInLoop => (),
216             Self::WillResizeOrMove => match w_param as u32 {
217                 // In these cases, the user is dragging window borders or corners for resizing.
218                 WMSZ_LEFT | WMSZ_RIGHT | WMSZ_TOP | WMSZ_BOTTOM | WMSZ_TOPLEFT | WMSZ_TOPRIGHT
219                 | WMSZ_BOTTOMLEFT | WMSZ_BOTTOMRIGHT => {
220                     info!("Window is being resized");
221                     *self = Self::IsResizing { is_first: true };
222                 }
223                 // In this case, the user is dragging the title bar of the maximized window. The
224                 // window will be restored to the normal size and then move along with the cursor,
225                 // so we can expect `WM_MOVING` coming and entering the moving modal loop.
226                 _ => info!("Window is being restored"),
227             },
228             Self::IsResizing { .. } => *self = Self::IsResizing { is_first: false },
229             Self::IsMoving { .. } => warn!("WM_SIZING is unexpected in moving modal loops!"),
230         }
231     }
232 
on_moving_window(&mut self)233     fn on_moving_window(&mut self) {
234         match *self {
235             Self::NotInLoop => (),
236             Self::WillResizeOrMove => {
237                 info!("Window is being moved");
238                 *self = Self::IsMoving { is_first: true };
239             }
240             Self::IsMoving { .. } => *self = Self::IsMoving { is_first: false },
241             Self::IsResizing { .. } => warn!("WM_MOVING is unexpected in sizing modal loops!"),
242         }
243     }
244 }
245 
246 /// A trait for basic functionalities that are common to both message-only windows and GUI windows.
247 /// Implementers must guarantee that when these functions are called, the underlying window object
248 /// is still alive.
249 pub(crate) trait BasicWindow {
250     /// # Safety
251     /// The returned handle should be used carefully, since it may have become invalid if it
252     /// outlives the window object.
handle(&self) -> HWND253     unsafe fn handle(&self) -> HWND;
254 
is_same_window(&self, hwnd: HWND) -> bool255     fn is_same_window(&self, hwnd: HWND) -> bool {
256         // SAFETY:
257         // Safe because we are just comparing handle values.
258         hwnd == unsafe { self.handle() }
259     }
260 
261     /// Calls `DefWindowProcW()` internally.
default_process_message(&self, packet: &MessagePacket) -> LRESULT262     fn default_process_message(&self, packet: &MessagePacket) -> LRESULT {
263         // SAFETY:
264         // Safe because the window object won't outlive the HWND.
265         unsafe { DefWindowProcW(self.handle(), packet.msg, packet.w_param, packet.l_param) }
266     }
267 
268     /// Calls `SetPropW()` internally.
269     /// # Safety
270     /// The caller is responsible for keeping the data pointer valid until `remove_property()` is
271     /// called.
set_property(&self, property: &str, data: *mut c_void) -> Result<()>272     unsafe fn set_property(&self, property: &str, data: *mut c_void) -> Result<()> {
273         // Partially safe because the window object won't outlive the HWND, and failures are handled
274         // below. The caller is responsible for the rest of safety.
275         if SetPropW(self.handle(), win32_wide_string(property).as_ptr(), data) == 0 {
276             syscall_bail!("Failed to call SetPropW()");
277         }
278         Ok(())
279     }
280 
281     /// Calls `RemovePropW()` internally.
282     #[allow(dead_code)]
remove_property(&self, property: &str) -> Result<()>283     fn remove_property(&self, property: &str) -> Result<()> {
284         // SAFETY:
285         // Safe because the window object won't outlive the HWND, and failures are handled below.
286         unsafe {
287             SetLastError(0);
288             RemovePropW(self.handle(), win32_wide_string(property).as_ptr());
289             if GetLastError() != 0 {
290                 syscall_bail!("Failed to call RemovePropW()");
291             }
292         }
293         Ok(())
294     }
295 
296     /// Calls `DestroyWindow()` internally.
destroy(&self) -> Result<()>297     fn destroy(&self) -> Result<()> {
298         // SAFETY:
299         // Safe because the window object won't outlive the HWND.
300         if unsafe { DestroyWindow(self.handle()) } == 0 {
301             syscall_bail!("Failed to call DestroyWindow()");
302         }
303         Ok(())
304     }
305 }
306 
307 /// This class helps create and operate on a GUI window using Windows APIs. The owner of `GuiWindow`
308 /// object is responsible for:
309 /// (1) Calling `update_states()` when a new window message arrives.
310 /// (2) Dropping the `GuiWindow` object before the underlying window is completely gone.
311 pub struct GuiWindow {
312     hwnd: HWND,
313     scanout_id: u32,
314     size_move_loop_state: SizeMoveLoopState,
315 }
316 
317 impl GuiWindow {
318     /// # Safety
319     /// The owner of `GuiWindow` object is responsible for dropping it before we finish processing
320     /// `WM_NCDESTROY`, because the window handle will become invalid afterwards.
new( scanout_id: u32, class_name: &str, title: &str, dw_style: DWORD, initial_window_size: &Size2D<i32, HostWindowSpace>, ) -> Result<Self>321     pub unsafe fn new(
322         scanout_id: u32,
323         class_name: &str,
324         title: &str,
325         dw_style: DWORD,
326         initial_window_size: &Size2D<i32, HostWindowSpace>,
327     ) -> Result<Self> {
328         info!("Creating GUI window for scanout {}", scanout_id);
329 
330         let hwnd = create_sys_window(
331             get_current_module_handle(),
332             class_name,
333             title,
334             dw_style,
335             /* hwnd_parent */ null_mut(),
336             initial_window_size,
337         )
338         .context("When creating GuiWindow")?;
339         let window = Self {
340             hwnd,
341             scanout_id,
342             size_move_loop_state: SizeMoveLoopState::new(),
343         };
344         window.register_touch();
345         Ok(window)
346     }
347 
scanout_id(&self) -> u32348     pub fn scanout_id(&self) -> u32 {
349         self.scanout_id
350     }
351 
update_states(&mut self, msg: UINT, w_param: WPARAM)352     pub fn update_states(&mut self, msg: UINT, w_param: WPARAM) {
353         self.size_move_loop_state.update(msg, w_param);
354     }
355 
is_sizing_or_moving(&self) -> bool356     pub fn is_sizing_or_moving(&self) -> bool {
357         self.size_move_loop_state.is_in_loop()
358     }
359 
is_resizing_loop_starting(&self) -> bool360     pub fn is_resizing_loop_starting(&self) -> bool {
361         self.size_move_loop_state.is_resizing_starting()
362     }
363 
364     /// Calls `IsWindow()` internally. Returns true if the HWND identifies an existing window.
is_valid(&self) -> bool365     pub fn is_valid(&self) -> bool {
366         // SAFETY:
367         // Safe because it is called from the same thread the created the window.
368         unsafe { IsWindow(self.hwnd) != 0 }
369     }
370 
371     /// Calls `GetWindowLongPtrW()` internally.
get_attribute(&self, index: i32) -> Result<isize>372     pub fn get_attribute(&self, index: i32) -> Result<isize> {
373         // SAFETY:
374         // Safe because `GuiWindow` object won't outlive the HWND, and failures are handled below.
375         unsafe {
376             // GetWindowLongPtrW() may return zero if we haven't set that attribute before, so we
377             // need to check if the error code is non-zero.
378             SetLastError(0);
379             let value = GetWindowLongPtrW(self.hwnd, index);
380             if value == 0 && GetLastError() != 0 {
381                 syscall_bail!("Failed to call GetWindowLongPtrW()");
382             }
383             Ok(value)
384         }
385     }
386 
387     /// Calls `SetWindowLongPtrW()` internally.
set_attribute(&self, index: i32, value: isize) -> Result<()>388     pub fn set_attribute(&self, index: i32, value: isize) -> Result<()> {
389         // SAFETY:
390         // Safe because `GuiWindow` object won't outlive the HWND, and failures are handled below.
391         unsafe {
392             // SetWindowLongPtrW() may return zero if the previous value of that attribute was zero,
393             // so we need to check if the error code is non-zero.
394             SetLastError(0);
395             let prev_value = SetWindowLongPtrW(self.hwnd, index, value);
396             if prev_value == 0 && GetLastError() != 0 {
397                 syscall_bail!("Failed to call SetWindowLongPtrW()");
398             }
399             Ok(())
400         }
401     }
402 
403     /// Calls `GetWindowRect()` internally.
get_window_rect(&self) -> Result<Rect>404     pub fn get_window_rect(&self) -> Result<Rect> {
405         let mut rect: RECT = Default::default();
406         // SAFETY:
407         // Safe because `GuiWindow` object won't outlive the HWND, we know `rect` is valid, and
408         // failures are handled below.
409         unsafe {
410             if GetWindowRect(self.hwnd, &mut rect) == 0 {
411                 syscall_bail!("Failed to call GetWindowRect()");
412             }
413         }
414         Ok(rect.to_rect())
415     }
416 
417     /// Calls `GetWindowRect()` internally.
get_window_origin(&self) -> Result<Point>418     pub fn get_window_origin(&self) -> Result<Point> {
419         Ok(self.get_window_rect()?.origin)
420     }
421 
422     /// Calls `GetClientRect()` internally.
get_client_rect(&self) -> Result<Rect>423     pub fn get_client_rect(&self) -> Result<Rect> {
424         let mut rect: RECT = Default::default();
425         // SAFETY:
426         // Safe because `GuiWindow` object won't outlive the HWND, we know `rect` is valid, and
427         // failures are handled below.
428         unsafe {
429             if GetClientRect(self.hwnd, &mut rect) == 0 {
430                 syscall_bail!("Failed to call GetClientRect()");
431             }
432         }
433         Ok(rect.to_rect())
434     }
435 
436     /// The system may add adornments around the client area of the window, such as the title bar
437     /// and borders. This function returns the size of all those paddings. It can be assumed that:
438     /// window_size = client_size + window_padding_size
get_window_padding_size(&self, dw_style: u32) -> Result<Size>439     pub fn get_window_padding_size(&self, dw_style: u32) -> Result<Size> {
440         static CONTEXT_MESSAGE: &str = "When calculating window padding";
441         // The padding is always the same in windowed mode, hence we can use an arbitrary rect.
442         let client_rect = Rect::new(point2(0, 0), size2(500, 500));
443         let dw_ex_style = self.get_attribute(GWL_EXSTYLE).context(CONTEXT_MESSAGE)?;
444         let window_rect: Rect = self
445             .get_adjusted_window_rect(&client_rect, dw_style, dw_ex_style as u32)
446             .context(CONTEXT_MESSAGE)?;
447         Ok(window_rect.size - client_rect.size)
448     }
449 
450     /// Calls `ClientToScreen()` internally. Converts the window client area coordinates of a
451     /// specified point to screen coordinates.
client_to_screen(&self, point: &Point) -> Result<Point>452     pub fn client_to_screen(&self, point: &Point) -> Result<Point> {
453         let mut point = point.to_sys_point();
454         // SAFETY:
455         // Safe because `GuiWindow` object won't outlive the HWND, we know `point` is valid, and
456         // failures are handled below.
457         unsafe {
458             if ClientToScreen(self.hwnd, &mut point) == 0 {
459                 syscall_bail!("Failed to call ClientToScreen()");
460             }
461         }
462         Ok(point.to_point())
463     }
464 
465     /// Calls `ScreenToClient()` internally. Converts the screen coordinates to window client area
466     /// coordinates.
screen_to_client(&self, point: Point) -> Result<Point>467     pub fn screen_to_client(&self, point: Point) -> Result<Point> {
468         let mut point = point.to_sys_point();
469 
470         // SAFETY:
471         // Safe because:
472         // 1. point is stack allocated & lives as long as the function call.
473         // 2. the window handle is guaranteed valid by self.
474         // 3. we check the error before using the output data.
475         unsafe {
476             let res = ScreenToClient(self.hwnd, point.as_mut_ptr());
477             if res == 0 {
478                 syscall_bail!("failed to convert cursor position to client coordinates");
479             }
480         }
481         Ok(Point2D::new(point.x, point.y))
482     }
483 
484     /// Calls `MonitorFromWindow()` internally. If the window is not on any active display monitor,
485     /// returns the handle to the closest one.
get_nearest_monitor_handle(&self) -> HMONITOR486     pub fn get_nearest_monitor_handle(&self) -> HMONITOR {
487         // SAFETY:
488         // Safe because `GuiWindow` object won't outlive the HWND.
489         unsafe { MonitorFromWindow(self.hwnd, MONITOR_DEFAULTTONEAREST) }
490     }
491 
492     /// Calls `MonitorFromWindow()` internally. If the window is not on any active display monitor,
493     /// returns the info of the closest one.
get_monitor_info(&self) -> Result<MonitorInfo>494     pub fn get_monitor_info(&self) -> Result<MonitorInfo> {
495         // SAFETY:
496         // Safe because `get_nearest_monitor_handle()` always returns a valid monitor handle.
497         unsafe { MonitorInfo::new(self.get_nearest_monitor_handle()) }
498     }
499 
500     /// Calls `MonitorFromWindow()` internally.
is_on_active_display(&self) -> bool501     pub fn is_on_active_display(&self) -> bool {
502         // SAFETY:
503         // Safe because `GuiWindow` object won't outlive the HWND.
504         unsafe { !MonitorFromWindow(self.hwnd, MONITOR_DEFAULTTONULL).is_null() }
505     }
506 
507     /// Calls `SetWindowPos()` internally.
set_pos(&self, window_rect: &Rect, flags: u32) -> Result<()>508     pub fn set_pos(&self, window_rect: &Rect, flags: u32) -> Result<()> {
509         // SAFETY:
510         // Safe because `GuiWindow` object won't outlive the HWND, and failures are handled below.
511         unsafe {
512             if SetWindowPos(
513                 self.hwnd,
514                 null_mut(),
515                 window_rect.origin.x,
516                 window_rect.origin.y,
517                 window_rect.size.width,
518                 window_rect.size.height,
519                 flags,
520             ) == 0
521             {
522                 syscall_bail!("Failed to call SetWindowPos()");
523             }
524             Ok(())
525         }
526     }
527 
528     /// Calls `SetWindowPos()` internally. If window size and position need to be changed as well,
529     /// prefer to call `set_pos()` with the `SWP_FRAMECHANGED` flag instead.
flush_window_style_change(&self) -> Result<()>530     pub fn flush_window_style_change(&self) -> Result<()> {
531         // Because of `SWP_NOMOVE` and `SWP_NOSIZE` flags, we can pass in arbitrary window size and
532         // position as they will be ignored.
533         self.set_pos(
534             &Rect::zero(),
535             SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_FRAMECHANGED,
536         )
537     }
538 
539     /// Calls `ShowWindow()` internally. Note that it is more preferable to call `set_pos()` with
540     /// `SWP_SHOWWINDOW` since that would set the error code on failure.
show(&self)541     pub fn show(&self) {
542         // SAFETY:
543         // Safe because `GuiWindow` object won't outlive the HWND.
544         unsafe {
545             ShowWindow(self.hwnd, SW_SHOW);
546         }
547     }
548 
549     /// Calls `SetWindowPos()` internally. Returns false if the window is already hidden and thus
550     /// this operation is skipped.
hide_if_visible(&self) -> Result<bool>551     pub fn hide_if_visible(&self) -> Result<bool> {
552         Ok(if self.is_visible()? {
553             self.set_pos(
554                 &Rect::zero(),
555                 SWP_HIDEWINDOW | SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER,
556             )?;
557             true
558         } else {
559             false
560         })
561     }
562 
563     /// Calls `ShowWindow()` internally to restore a minimized window.
restore(&self)564     pub fn restore(&self) {
565         // SAFETY:
566         // Safe because `GuiWindow` object won't outlive the HWND.
567         unsafe {
568             ShowWindow(self.hwnd, SW_RESTORE);
569         }
570     }
571 
572     /// Calls `IsZoomed()` internally. Note that the window may carry the WS_MAXIMIZE flag until it
573     /// is restored. For example, if we have switched from maximized to fullscreen, this function
574     /// would still return true.
was_maximized(&self) -> bool575     pub fn was_maximized(&self) -> bool {
576         // SAFETY:
577         // Safe because `GuiWindow` object won't outlive the HWND.
578         unsafe { IsZoomed(self.hwnd) != 0 }
579     }
580 
581     /// Calls `IsWindowVisible()` internally. We also require that the window size is nonzero to be
582     /// considered visible.
is_visible(&self) -> Result<bool>583     pub fn is_visible(&self) -> Result<bool> {
584         // SAFETY:
585         // Safe because `GuiWindow` object won't outlive the HWND.
586         if unsafe { IsWindowVisible(self.hwnd) } != 0 {
587             let window_rect = self
588                 .get_window_rect()
589                 .context("When querying window visibility")?;
590             if window_rect.size != Size::zero() {
591                 return Ok(true);
592             } else {
593                 info!("Window has WS_VISIBLE flag but its size is zero");
594             }
595         }
596         Ok(false)
597     }
598 
599     /// Calls `GetForegroundWindow()` internally. A foreground window is the window with which the
600     /// user is currently working. It might belong to a different thread/process than the calling
601     /// thread.
is_global_foreground_window(&self) -> bool602     pub fn is_global_foreground_window(&self) -> bool {
603         // SAFETY:
604         // Safe because there is no argument.
605         unsafe { GetForegroundWindow() == self.hwnd }
606     }
607 
608     /// Calls `GetActiveWindow()` internally. An active window is the window with which the user is
609     /// currently working and is attached to the calling thread's message queue. It is possible that
610     /// there is no active window if the foreground focus is on another thread/process.
is_thread_foreground_window(&self) -> bool611     pub fn is_thread_foreground_window(&self) -> bool {
612         // SAFETY:
613         // Safe because there is no argument.
614         unsafe { GetActiveWindow() == self.hwnd }
615     }
616 
617     /// Calls `IsIconic()` internally.
is_minimized(&self) -> bool618     pub fn is_minimized(&self) -> bool {
619         // SAFETY:
620         // Safe because `GuiWindow` object won't outlive the HWND.
621         unsafe { IsIconic(self.hwnd) != 0 }
622     }
623 
624     /// Calls `SetForegroundWindow()` internally. `SetForegroundWindow()` may fail, for example,
625     /// when the taskbar is in the foreground, hence this is a best-effort call.
bring_to_foreground(&self)626     pub fn bring_to_foreground(&self) {
627         // SAFETY:
628         // Safe because `GuiWindow` object won't outlive the HWND.
629         if unsafe { SetForegroundWindow(self.hwnd) } == 0 {
630             info!("Cannot bring the window to foreground.");
631         }
632     }
633 
634     /// Calls `DwmEnableBlurBehindWindow()` internally. This is only used for a top-level window.
635     /// Even though the name of Windows API suggests that it blurs the background, beginning with
636     /// Windows 8, it does not blur it, but only makes the window semi-transparent.
set_backgound_transparency(&self, semi_transparent: bool) -> Result<()>637     pub fn set_backgound_transparency(&self, semi_transparent: bool) -> Result<()> {
638         let blur_behind = DWM_BLURBEHIND {
639             dwFlags: DWM_BB_ENABLE,
640             fEnable: if semi_transparent { TRUE } else { FALSE },
641             hRgnBlur: null_mut(),
642             fTransitionOnMaximized: FALSE,
643         };
644         // SAFETY:
645         // Safe because `GuiWindow` object won't outlive the HWND, we know `blur_behind` is valid,
646         // and failures are handled below.
647         let errno = unsafe { DwmEnableBlurBehindWindow(self.hwnd, &blur_behind) };
648         match errno {
649             0 => Ok(()),
650             _ => bail!(
651                 "Failed to call DwmEnableBlurBehindWindow() when setting \
652                 window background transparency to {} (Error code {})",
653                 semi_transparent,
654                 errno
655             ),
656         }
657     }
658 
659     /// Calls `AdjustWindowRectExForDpi()` internally.
get_adjusted_window_rect( &self, client_rect: &Rect, dw_style: u32, dw_ex_style: u32, ) -> Result<Rect>660     pub fn get_adjusted_window_rect(
661         &self,
662         client_rect: &Rect,
663         dw_style: u32,
664         dw_ex_style: u32,
665     ) -> Result<Rect> {
666         let mut window_rect: RECT = client_rect.to_sys_rect();
667         // SAFETY:
668         // Safe because `GuiWindow` object won't outlive the HWND, we know `window_rect` is valid,
669         // and failures are handled below.
670         unsafe {
671             if AdjustWindowRectExForDpi(
672                 &mut window_rect,
673                 dw_style,
674                 FALSE,
675                 dw_ex_style,
676                 GetDpiForSystem(),
677             ) == 0
678             {
679                 syscall_bail!("Failed to call AdjustWindowRectExForDpi()");
680             }
681         }
682         Ok(window_rect.to_rect())
683     }
684 
685     /// Calls `GetWindowPlacement()` and `SetWindowPlacement()` internally.
set_restored_pos(&self, window_rect: &Rect) -> Result<()>686     pub fn set_restored_pos(&self, window_rect: &Rect) -> Result<()> {
687         let mut window_placement = WINDOWPLACEMENT {
688             length: mem::size_of::<WINDOWPLACEMENT>().try_into().unwrap(),
689             ..Default::default()
690         };
691         // SAFETY:
692         // Safe because `GuiWindow` object won't outlive the HWND, we know `window_placement` is
693         // valid, and failures are handled below.
694         unsafe {
695             if GetWindowPlacement(self.hwnd, &mut window_placement) == 0 {
696                 syscall_bail!("Failed to call GetWindowPlacement()");
697             }
698             window_placement.rcNormalPosition = window_rect.to_sys_rect();
699             if SetWindowPlacement(self.hwnd, &window_placement) == 0 {
700                 syscall_bail!("Failed to call SetWindowPlacement()");
701             }
702         }
703         Ok(())
704     }
705 
706     /// Calls `PostMessageW()` internally.
post_message(&self, msg: UINT, w_param: WPARAM, l_param: LPARAM) -> Result<()>707     pub fn post_message(&self, msg: UINT, w_param: WPARAM, l_param: LPARAM) -> Result<()> {
708         // SAFETY:
709         // Safe because `GuiWindow` object won't outlive the HWND.
710         unsafe {
711             if PostMessageW(self.hwnd, msg, w_param, l_param) == 0 {
712                 syscall_bail!("Failed to call PostMessageW()");
713             }
714         }
715         Ok(())
716     }
717 
718     /// Calls `LoadIconW()` internally.
load_custom_icon(hinstance: HINSTANCE, resource_id: WORD) -> Result<HICON>719     pub(crate) fn load_custom_icon(hinstance: HINSTANCE, resource_id: WORD) -> Result<HICON> {
720         // SAFETY:
721         // Safe because we handle failures below.
722         unsafe {
723             let hicon = LoadIconW(hinstance, MAKEINTRESOURCEW(resource_id));
724             if hicon.is_null() {
725                 syscall_bail!("Failed to call LoadIconW()");
726             }
727             Ok(hicon)
728         }
729     }
730 
731     /// Calls `LoadCursorW()` internally.
load_system_cursor(cursor_id: LPCWSTR) -> Result<HCURSOR>732     pub(crate) fn load_system_cursor(cursor_id: LPCWSTR) -> Result<HCURSOR> {
733         // SAFETY:
734         // Safe because we handle failures below.
735         unsafe {
736             let hcursor = LoadCursorW(null_mut(), cursor_id);
737             if hcursor.is_null() {
738                 syscall_bail!("Failed to call LoadCursorW()");
739             }
740             Ok(hcursor)
741         }
742     }
743 
744     /// Calls `GetStockObject()` internally.
create_opaque_black_brush() -> Result<HBRUSH>745     pub(crate) fn create_opaque_black_brush() -> Result<HBRUSH> {
746         // SAFETY:
747         // Safe because we handle failures below.
748         unsafe {
749             let hobject = GetStockObject(BLACK_BRUSH as i32);
750             if hobject.is_null() {
751                 syscall_bail!("Failed to call GetStockObject()");
752             }
753             Ok(hobject as HBRUSH)
754         }
755     }
756 
757     /// Calls `RegisterTouchWindow()` internally.
register_touch(&self)758     fn register_touch(&self) {
759         // SAFETY: Safe because `GuiWindow` object won't outlive the HWND.
760         if unsafe { RegisterTouchWindow(self.handle(), 0) } == 0 {
761             // For now, we register touch only to get stats. It is ok if the registration fails.
762             // SAFETY: trivially-safe
763             warn!("failed to register touch: {}", unsafe { GetLastError() });
764         }
765     }
766 }
767 
768 impl BasicWindow for GuiWindow {
769     /// # Safety
770     /// The returned handle should be used carefully, since it may have become invalid if it
771     /// outlives the `GuiWindow` object.
handle(&self) -> HWND772     unsafe fn handle(&self) -> HWND {
773         self.hwnd
774     }
775 }
776 
777 /// A message-only window is always invisible, and is only responsible for sending and receiving
778 /// messages. The owner of `MessageOnlyWindow` object is responsible for dropping it before the
779 /// underlying window is completely gone.
780 pub(crate) struct MessageOnlyWindow {
781     hwnd: HWND,
782 }
783 
784 impl MessageOnlyWindow {
785     /// # Safety
786     /// The owner of `MessageOnlyWindow` object is responsible for dropping it before we finish
787     /// processing `WM_NCDESTROY`, because the window handle will become invalid afterwards.
new(class_name: &str, title: &str) -> Result<Self>788     pub unsafe fn new(class_name: &str, title: &str) -> Result<Self> {
789         info!("Creating message-only window");
790         static CONTEXT_MESSAGE: &str = "When creating MessageOnlyWindow";
791 
792         let window = Self {
793             hwnd: create_sys_window(
794                 get_current_module_handle(),
795                 class_name,
796                 title,
797                 /* dw_style */ 0,
798                 HWND_MESSAGE,
799                 /* initial_window_size */ &size2(0, 0),
800             )
801             .context(CONTEXT_MESSAGE)?,
802         };
803         window.register_raw_input_mouse().context(CONTEXT_MESSAGE)?;
804         Ok(window)
805     }
806 
807     /// Registers this window as the receiver of raw mouse input events.
808     ///
809     /// On Windows, an application can only have one window that receives raw input events, so we
810     /// make `MessageOnlyWindow` take on this role and reroute events to the foreground `GuiWindow`.
register_raw_input_mouse(&self) -> Result<()>811     fn register_raw_input_mouse(&self) -> Result<()> {
812         let mouse_device = RAWINPUTDEVICE {
813             usUsagePage: 1, // Generic
814             usUsage: 2,     // Mouse
815             dwFlags: 0,
816             // SAFETY: Safe because `self` won't outlive the HWND.
817             hwndTarget: unsafe { self.handle() },
818         };
819         // SAFETY: Safe because `mouse_device` lives longer than this function call.
820         if unsafe {
821             RegisterRawInputDevices(
822                 &mouse_device as PCRAWINPUTDEVICE,
823                 1,
824                 mem::size_of::<RAWINPUTDEVICE>() as u32,
825             )
826         } == 0
827         {
828             syscall_bail!("Relative mouse is broken. Failed to call RegisterRawInputDevices()");
829         }
830         Ok(())
831     }
832 }
833 
834 impl BasicWindow for MessageOnlyWindow {
835     /// # Safety
836     /// The returned handle should be used carefully, since it may have become invalid if it
837     /// outlives the `MessageOnlyWindow` object.
handle(&self) -> HWND838     unsafe fn handle(&self) -> HWND {
839         self.hwnd
840     }
841 }
842 
843 /// Calls `CreateWindowExW()` internally.
create_sys_window( hinstance: HINSTANCE, class_name: &str, title: &str, dw_style: DWORD, hwnd_parent: HWND, initial_window_size: &Size2D<i32, HostWindowSpace>, ) -> Result<HWND>844 fn create_sys_window(
845     hinstance: HINSTANCE,
846     class_name: &str,
847     title: &str,
848     dw_style: DWORD,
849     hwnd_parent: HWND,
850     initial_window_size: &Size2D<i32, HostWindowSpace>,
851 ) -> Result<HWND> {
852     // SAFETY:
853     // Safe because we handle failures below.
854     let hwnd = unsafe {
855         CreateWindowExW(
856             /* dwExStyle */ 0,
857             win32_wide_string(class_name).as_ptr(),
858             win32_wide_string(title).as_ptr(),
859             dw_style,
860             /* x */ 0,
861             /* y */ 0,
862             initial_window_size.width,
863             initial_window_size.height,
864             hwnd_parent,
865             /* hMenu */ null_mut(),
866             hinstance,
867             /* lpParam */ null_mut(),
868         )
869     };
870     if hwnd.is_null() {
871         syscall_bail!("Failed to call CreateWindowExW()");
872     }
873     info!("Created window {:p}", hwnd);
874     Ok(hwnd)
875 }
876 
877 /// Calls `GetModuleHandleW()` internally.
get_current_module_handle() -> HMODULE878 pub(crate) fn get_current_module_handle() -> HMODULE {
879     // SAFETY:
880     // Safe because we handle failures below.
881     let hmodule = unsafe { GetModuleHandleW(null_mut()) };
882     if hmodule.is_null() {
883         // If it fails, we are in a very broken state and it doesn't make sense to keep running.
884         panic!(
885             "Failed to call GetModuleHandleW() for the current module (Error code {})",
886             // SAFETY: trivially safe
887             unsafe { GetLastError() }
888         );
889     }
890     hmodule
891 }
892 
893 /// If the resolution/orientation of the monitor changes, or if the monitor is unplugged, this must
894 /// be recreated with a valid HMONITOR.
895 pub struct MonitorInfo {
896     pub hmonitor: HMONITOR,
897     pub display_rect: Rect,
898     pub work_rect: Rect,
899     raw_dpi: i32,
900     // Whether we are running in a Remote Desktop Protocol (RDP) session. The monitor DPI returned
901     // by `GetDpiForMonitor()` may not make sense in that case. For example, the DPI is always 25
902     // under Chrome Remote Desktop, which is way lower than the standard DPI 96. This might be a
903     // flaw in RDP itself. We have to override the DPI in that case, otherwise the guest DPI
904     // calculated based on it would be too low as well.
905     // https://learn.microsoft.com/en-us/troubleshoot/windows-server/shell-experience/dpi-adjustment-unavailable-in-rdp
906     is_rdp_session: bool,
907 }
908 
909 impl MonitorInfo {
910     /// # Safety
911     /// Caller is responsible for ensuring that `hmonitor` is a valid handle.
new(hmonitor: HMONITOR) -> Result<Self>912     pub unsafe fn new(hmonitor: HMONITOR) -> Result<Self> {
913         let monitor_info: MONITORINFO =
914             Self::get_monitor_info(hmonitor).context("When creating MonitorInfo")?;
915         // Docs state that apart from `GetSystemMetrics(SM_REMOTESESSION)`, we also need to check
916         // registry entries to see if we are running in a remote session that uses RemoteFX vGPU:
917         // https://learn.microsoft.com/en-us/windows/win32/termserv/detecting-the-terminal-services-environment
918         // However, RemoteFX vGPU was then removed because of security vulnerabilities:
919         // https://support.microsoft.com/en-us/topic/kb4570006-update-to-disable-and-remove-the-remotefx-vgpu-component-in-windows-bbdf1531-7188-2bf4-0de6-641de79f09d2
920         // So, we are only calling `GetSystemMetrics(SM_REMOTESESSION)` here until this changes in
921         // the future.
922         // SAFETY:
923         // Safe because no memory management is needed for arguments.
924         let is_rdp_session = unsafe { GetSystemMetrics(SM_REMOTESESSION) != 0 };
925         Ok(Self {
926             hmonitor,
927             display_rect: monitor_info.rcMonitor.to_rect(),
928             work_rect: monitor_info.rcWork.to_rect(),
929             raw_dpi: Self::get_monitor_dpi(hmonitor),
930             is_rdp_session,
931         })
932     }
933 
get_dpi(&self) -> i32934     pub fn get_dpi(&self) -> i32 {
935         if self.is_rdp_session {
936             // Override the DPI since the system may not tell us the correct value in RDP sessions.
937             DEFAULT_HOST_DPI
938         } else {
939             self.raw_dpi
940         }
941     }
942 
943     /// Calls `GetMonitorInfoW()` internally.
944     /// # Safety
945     /// Caller is responsible for ensuring that `hmonitor` is a valid handle.
get_monitor_info(hmonitor: HMONITOR) -> Result<MONITORINFO>946     unsafe fn get_monitor_info(hmonitor: HMONITOR) -> Result<MONITORINFO> {
947         let mut monitor_info = MONITORINFO {
948             cbSize: mem::size_of::<MONITORINFO>().try_into().unwrap(),
949             ..Default::default()
950         };
951         if GetMonitorInfoW(hmonitor, &mut monitor_info) == 0 {
952             syscall_bail!("Failed to call GetMonitorInfoW()");
953         }
954         Ok(monitor_info)
955     }
956 
957     /// Calls `GetDpiForMonitor()` internally.
get_monitor_dpi(hmonitor: HMONITOR) -> i32958     fn get_monitor_dpi(hmonitor: HMONITOR) -> i32 {
959         let mut dpi_x = 0;
960         let mut dpi_y = 0;
961         // SAFETY:
962         // This is always safe since `GetDpiForMonitor` won't crash if HMONITOR is invalid, but
963         // return E_INVALIDARG.
964         unsafe {
965             if GetDpiForMonitor(hmonitor, MDT_RAW_DPI, &mut dpi_x, &mut dpi_y) == S_OK
966                 || GetDpiForMonitor(hmonitor, MDT_DEFAULT, &mut dpi_x, &mut dpi_y) == S_OK
967             {
968                 // We assume screen pixels are square and DPI in different directions are the same.
969                 dpi_x as i32
970             } else {
971                 error!("Failed to retrieve DPI with HMONITOR {:p}", hmonitor);
972                 DEFAULT_HOST_DPI
973             }
974         }
975     }
976 }
977 
978 impl fmt::Debug for MonitorInfo {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result979     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
980         write!(
981             f,
982             "{{hmonitor: {:p}, display_rect: {:?}, work_rect: {:?}, DPI: {}{}}}",
983             self.hmonitor,
984             self.display_rect,
985             self.work_rect,
986             self.get_dpi(),
987             if self.is_rdp_session {
988                 format!(" (raw value: {}, overriden due to RDP)", self.raw_dpi)
989             } else {
990                 String::new()
991             }
992         )
993     }
994 }
995