• 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 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.
remove_property(&self, property: &str) -> Result<()>282     fn remove_property(&self, property: &str) -> Result<()> {
283         // SAFETY:
284         // Safe because the window object won't outlive the HWND, and failures are handled below.
285         unsafe {
286             SetLastError(0);
287             RemovePropW(self.handle(), win32_wide_string(property).as_ptr());
288             if GetLastError() != 0 {
289                 syscall_bail!("Failed to call RemovePropW()");
290             }
291         }
292         Ok(())
293     }
294 
295     /// Calls `DestroyWindow()` internally.
destroy(&self) -> Result<()>296     fn destroy(&self) -> Result<()> {
297         // SAFETY:
298         // Safe because the window object won't outlive the HWND.
299         if unsafe { DestroyWindow(self.handle()) } == 0 {
300             syscall_bail!("Failed to call DestroyWindow()");
301         }
302         Ok(())
303     }
304 }
305 
306 /// This class helps create and operate on a GUI window using Windows APIs. The owner of `GuiWindow`
307 /// object is responsible for:
308 /// (1) Calling `update_states()` when a new window message arrives.
309 /// (2) Dropping the `GuiWindow` object before the underlying window is completely gone.
310 pub struct GuiWindow {
311     hwnd: HWND,
312     scanout_id: u32,
313     size_move_loop_state: SizeMoveLoopState,
314 }
315 
316 impl GuiWindow {
317     /// # Safety
318     /// The owner of `GuiWindow` object is responsible for dropping it before we finish processing
319     /// `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>320     pub unsafe fn new(
321         scanout_id: u32,
322         class_name: &str,
323         title: &str,
324         dw_style: DWORD,
325         initial_window_size: &Size2D<i32, HostWindowSpace>,
326     ) -> Result<Self> {
327         info!("Creating GUI window for scanout {}", scanout_id);
328 
329         let hwnd = create_sys_window(
330             get_current_module_handle(),
331             class_name,
332             title,
333             dw_style,
334             /* hwnd_parent */ null_mut(),
335             initial_window_size,
336         )
337         .context("When creating GuiWindow")?;
338         let window = Self {
339             hwnd,
340             scanout_id,
341             size_move_loop_state: SizeMoveLoopState::new(),
342         };
343         window.register_touch();
344         Ok(window)
345     }
346 
scanout_id(&self) -> u32347     pub fn scanout_id(&self) -> u32 {
348         self.scanout_id
349     }
350 
update_states(&mut self, msg: UINT, w_param: WPARAM)351     pub fn update_states(&mut self, msg: UINT, w_param: WPARAM) {
352         self.size_move_loop_state.update(msg, w_param);
353     }
354 
is_sizing_or_moving(&self) -> bool355     pub fn is_sizing_or_moving(&self) -> bool {
356         self.size_move_loop_state.is_in_loop()
357     }
358 
is_resizing_loop_starting(&self) -> bool359     pub fn is_resizing_loop_starting(&self) -> bool {
360         self.size_move_loop_state.is_resizing_starting()
361     }
362 
363     /// Calls `IsWindow()` internally. Returns true if the HWND identifies an existing window.
is_valid(&self) -> bool364     pub fn is_valid(&self) -> bool {
365         // SAFETY:
366         // Safe because it is called from the same thread the created the window.
367         unsafe { IsWindow(self.hwnd) != 0 }
368     }
369 
370     /// Calls `GetWindowLongPtrW()` internally.
get_attribute(&self, index: i32) -> Result<isize>371     pub fn get_attribute(&self, index: i32) -> Result<isize> {
372         // SAFETY:
373         // Safe because `GuiWindow` object won't outlive the HWND, and failures are handled below.
374         unsafe {
375             // GetWindowLongPtrW() may return zero if we haven't set that attribute before, so we
376             // need to check if the error code is non-zero.
377             SetLastError(0);
378             let value = GetWindowLongPtrW(self.hwnd, index);
379             if value == 0 && GetLastError() != 0 {
380                 syscall_bail!("Failed to call GetWindowLongPtrW()");
381             }
382             Ok(value)
383         }
384     }
385 
386     /// Calls `SetWindowLongPtrW()` internally.
set_attribute(&self, index: i32, value: isize) -> Result<()>387     pub fn set_attribute(&self, index: i32, value: isize) -> Result<()> {
388         // SAFETY:
389         // Safe because `GuiWindow` object won't outlive the HWND, and failures are handled below.
390         unsafe {
391             // SetWindowLongPtrW() may return zero if the previous value of that attribute was zero,
392             // so we need to check if the error code is non-zero.
393             SetLastError(0);
394             let prev_value = SetWindowLongPtrW(self.hwnd, index, value);
395             if prev_value == 0 && GetLastError() != 0 {
396                 syscall_bail!("Failed to call SetWindowLongPtrW()");
397             }
398             Ok(())
399         }
400     }
401 
402     /// Calls `GetWindowRect()` internally.
get_window_rect(&self) -> Result<Rect>403     pub fn get_window_rect(&self) -> Result<Rect> {
404         let mut rect: RECT = Default::default();
405         // SAFETY:
406         // Safe because `GuiWindow` object won't outlive the HWND, we know `rect` is valid, and
407         // failures are handled below.
408         unsafe {
409             if GetWindowRect(self.hwnd, &mut rect) == 0 {
410                 syscall_bail!("Failed to call GetWindowRect()");
411             }
412         }
413         Ok(rect.to_rect())
414     }
415 
416     /// Calls `GetWindowRect()` internally.
get_window_origin(&self) -> Result<Point>417     pub fn get_window_origin(&self) -> Result<Point> {
418         Ok(self.get_window_rect()?.origin)
419     }
420 
421     /// Calls `GetClientRect()` internally.
get_client_rect(&self) -> Result<Rect>422     pub fn get_client_rect(&self) -> Result<Rect> {
423         let mut rect: RECT = Default::default();
424         // SAFETY:
425         // Safe because `GuiWindow` object won't outlive the HWND, we know `rect` is valid, and
426         // failures are handled below.
427         unsafe {
428             if GetClientRect(self.hwnd, &mut rect) == 0 {
429                 syscall_bail!("Failed to call GetClientRect()");
430             }
431         }
432         Ok(rect.to_rect())
433     }
434 
435     /// The system may add adornments around the client area of the window, such as the title bar
436     /// and borders. This function returns the size of all those paddings. It can be assumed that:
437     /// window_size = client_size + window_padding_size
get_window_padding_size(&self, dw_style: u32) -> Result<Size>438     pub fn get_window_padding_size(&self, dw_style: u32) -> Result<Size> {
439         static CONTEXT_MESSAGE: &str = "When calculating window padding";
440         // The padding is always the same in windowed mode, hence we can use an arbitrary rect.
441         let client_rect = Rect::new(point2(0, 0), size2(500, 500));
442         let dw_ex_style = self.get_attribute(GWL_EXSTYLE).context(CONTEXT_MESSAGE)?;
443         let window_rect: Rect = self
444             .get_adjusted_window_rect(&client_rect, dw_style, dw_ex_style as u32)
445             .context(CONTEXT_MESSAGE)?;
446         Ok(window_rect.size - client_rect.size)
447     }
448 
449     /// Calls `ClientToScreen()` internally. Converts the window client area coordinates of a
450     /// specified point to screen coordinates.
client_to_screen(&self, point: &Point) -> Result<Point>451     pub fn client_to_screen(&self, point: &Point) -> Result<Point> {
452         let mut point = point.to_sys_point();
453         // SAFETY:
454         // Safe because `GuiWindow` object won't outlive the HWND, we know `point` is valid, and
455         // failures are handled below.
456         unsafe {
457             if ClientToScreen(self.hwnd, &mut point) == 0 {
458                 syscall_bail!("Failed to call ClientToScreen()");
459             }
460         }
461         Ok(point.to_point())
462     }
463 
464     /// Calls `ScreenToClient()` internally. Converts the screen coordinates to window client area
465     /// coordinates.
screen_to_client(&self, point: Point) -> Result<Point>466     pub fn screen_to_client(&self, point: Point) -> Result<Point> {
467         let mut point = point.to_sys_point();
468 
469         // SAFETY:
470         // Safe because:
471         // 1. point is stack allocated & lives as long as the function call.
472         // 2. the window handle is guaranteed valid by self.
473         // 3. we check the error before using the output data.
474         unsafe {
475             let res = ScreenToClient(self.hwnd, point.as_mut_ptr());
476             if res == 0 {
477                 syscall_bail!("failed to convert cursor position to client coordinates");
478             }
479         }
480         Ok(Point2D::new(point.x, point.y))
481     }
482 
483     /// Calls `MonitorFromWindow()` internally. If the window is not on any active display monitor,
484     /// returns the handle to the closest one.
get_nearest_monitor_handle(&self) -> HMONITOR485     pub fn get_nearest_monitor_handle(&self) -> HMONITOR {
486         // SAFETY:
487         // Safe because `GuiWindow` object won't outlive the HWND.
488         unsafe { MonitorFromWindow(self.hwnd, MONITOR_DEFAULTTONEAREST) }
489     }
490 
491     /// Calls `MonitorFromWindow()` internally. If the window is not on any active display monitor,
492     /// returns the info of the closest one.
get_monitor_info(&self) -> Result<MonitorInfo>493     pub fn get_monitor_info(&self) -> Result<MonitorInfo> {
494         // SAFETY:
495         // Safe because `get_nearest_monitor_handle()` always returns a valid monitor handle.
496         unsafe { MonitorInfo::new(self.get_nearest_monitor_handle()) }
497     }
498 
499     /// Calls `MonitorFromWindow()` internally.
is_on_active_display(&self) -> bool500     pub fn is_on_active_display(&self) -> bool {
501         // SAFETY:
502         // Safe because `GuiWindow` object won't outlive the HWND.
503         unsafe { !MonitorFromWindow(self.hwnd, MONITOR_DEFAULTTONULL).is_null() }
504     }
505 
506     /// Calls `SetWindowPos()` internally.
set_pos(&self, window_rect: &Rect, flags: u32) -> Result<()>507     pub fn set_pos(&self, window_rect: &Rect, flags: u32) -> Result<()> {
508         // SAFETY:
509         // Safe because `GuiWindow` object won't outlive the HWND, and failures are handled below.
510         unsafe {
511             if SetWindowPos(
512                 self.hwnd,
513                 null_mut(),
514                 window_rect.origin.x,
515                 window_rect.origin.y,
516                 window_rect.size.width,
517                 window_rect.size.height,
518                 flags,
519             ) == 0
520             {
521                 syscall_bail!("Failed to call SetWindowPos()");
522             }
523             Ok(())
524         }
525     }
526 
527     /// Calls `SetWindowPos()` internally. If window size and position need to be changed as well,
528     /// prefer to call `set_pos()` with the `SWP_FRAMECHANGED` flag instead.
flush_window_style_change(&self) -> Result<()>529     pub fn flush_window_style_change(&self) -> Result<()> {
530         // Because of `SWP_NOMOVE` and `SWP_NOSIZE` flags, we can pass in arbitrary window size and
531         // position as they will be ignored.
532         self.set_pos(
533             &Rect::zero(),
534             SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_FRAMECHANGED,
535         )
536     }
537 
538     /// Calls `ShowWindow()` internally. Note that it is more preferable to call `set_pos()` with
539     /// `SWP_SHOWWINDOW` since that would set the error code on failure.
show(&self)540     pub fn show(&self) {
541         // SAFETY:
542         // Safe because `GuiWindow` object won't outlive the HWND.
543         unsafe {
544             ShowWindow(self.hwnd, SW_SHOW);
545         }
546     }
547 
548     /// Calls `SetWindowPos()` internally. Returns false if the window is already hidden and thus
549     /// this operation is skipped.
hide_if_visible(&self) -> Result<bool>550     pub fn hide_if_visible(&self) -> Result<bool> {
551         Ok(if self.is_visible()? {
552             self.set_pos(
553                 &Rect::zero(),
554                 SWP_HIDEWINDOW | SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER,
555             )?;
556             true
557         } else {
558             false
559         })
560     }
561 
562     /// Calls `ShowWindow()` internally to restore a minimized window.
restore(&self)563     pub fn restore(&self) {
564         // SAFETY:
565         // Safe because `GuiWindow` object won't outlive the HWND.
566         unsafe {
567             ShowWindow(self.hwnd, SW_RESTORE);
568         }
569     }
570 
571     /// Calls `IsZoomed()` internally. Note that the window may carry the WS_MAXIMIZE flag until it
572     /// is restored. For example, if we have switched from maximized to fullscreen, this function
573     /// would still return true.
was_maximized(&self) -> bool574     pub fn was_maximized(&self) -> bool {
575         // SAFETY:
576         // Safe because `GuiWindow` object won't outlive the HWND.
577         unsafe { IsZoomed(self.hwnd) != 0 }
578     }
579 
580     /// Calls `IsWindowVisible()` internally. We also require that the window size is nonzero to be
581     /// considered visible.
is_visible(&self) -> Result<bool>582     pub fn is_visible(&self) -> Result<bool> {
583         // SAFETY:
584         // Safe because `GuiWindow` object won't outlive the HWND.
585         if unsafe { IsWindowVisible(self.hwnd) } != 0 {
586             let window_rect = self
587                 .get_window_rect()
588                 .context("When querying window visibility")?;
589             if window_rect.size != Size::zero() {
590                 return Ok(true);
591             } else {
592                 info!("Window has WS_VISIBLE flag but its size is zero");
593             }
594         }
595         Ok(false)
596     }
597 
598     /// Calls `GetForegroundWindow()` internally. A foreground window is the window with which the
599     /// user is currently working. It might belong to a different thread/process than the calling
600     /// thread.
is_global_foreground_window(&self) -> bool601     pub fn is_global_foreground_window(&self) -> bool {
602         // SAFETY:
603         // Safe because there is no argument.
604         unsafe { GetForegroundWindow() == self.hwnd }
605     }
606 
607     /// Calls `GetActiveWindow()` internally. An active window is the window with which the user is
608     /// currently working and is attached to the calling thread's message queue. It is possible that
609     /// there is no active window if the foreground focus is on another thread/process.
is_thread_foreground_window(&self) -> bool610     pub fn is_thread_foreground_window(&self) -> bool {
611         // SAFETY:
612         // Safe because there is no argument.
613         unsafe { GetActiveWindow() == self.hwnd }
614     }
615 
616     /// Calls `IsIconic()` internally.
is_minimized(&self) -> bool617     pub fn is_minimized(&self) -> bool {
618         // SAFETY:
619         // Safe because `GuiWindow` object won't outlive the HWND.
620         unsafe { IsIconic(self.hwnd) != 0 }
621     }
622 
623     /// Calls `SetForegroundWindow()` internally. `SetForegroundWindow()` may fail, for example,
624     /// when the taskbar is in the foreground, hence this is a best-effort call.
bring_to_foreground(&self)625     pub fn bring_to_foreground(&self) {
626         // SAFETY:
627         // Safe because `GuiWindow` object won't outlive the HWND.
628         if unsafe { SetForegroundWindow(self.hwnd) } == 0 {
629             info!("Cannot bring the window to foreground.");
630         }
631     }
632 
633     /// Calls `DwmEnableBlurBehindWindow()` internally. This is only used for a top-level window.
634     /// Even though the name of Windows API suggests that it blurs the background, beginning with
635     /// Windows 8, it does not blur it, but only makes the window semi-transparent.
set_backgound_transparency(&self, semi_transparent: bool) -> Result<()>636     pub fn set_backgound_transparency(&self, semi_transparent: bool) -> Result<()> {
637         let blur_behind = DWM_BLURBEHIND {
638             dwFlags: DWM_BB_ENABLE,
639             fEnable: if semi_transparent { TRUE } else { FALSE },
640             hRgnBlur: null_mut(),
641             fTransitionOnMaximized: FALSE,
642         };
643         // SAFETY:
644         // Safe because `GuiWindow` object won't outlive the HWND, we know `blur_behind` is valid,
645         // and failures are handled below.
646         let errno = unsafe { DwmEnableBlurBehindWindow(self.hwnd, &blur_behind) };
647         match errno {
648             0 => Ok(()),
649             _ => bail!(
650                 "Failed to call DwmEnableBlurBehindWindow() when setting \
651                 window background transparency to {} (Error code {})",
652                 semi_transparent,
653                 errno
654             ),
655         }
656     }
657 
658     /// Calls `AdjustWindowRectExForDpi()` internally.
get_adjusted_window_rect( &self, client_rect: &Rect, dw_style: u32, dw_ex_style: u32, ) -> Result<Rect>659     pub fn get_adjusted_window_rect(
660         &self,
661         client_rect: &Rect,
662         dw_style: u32,
663         dw_ex_style: u32,
664     ) -> Result<Rect> {
665         let mut window_rect: RECT = client_rect.to_sys_rect();
666         // SAFETY:
667         // Safe because `GuiWindow` object won't outlive the HWND, we know `window_rect` is valid,
668         // and failures are handled below.
669         unsafe {
670             if AdjustWindowRectExForDpi(
671                 &mut window_rect,
672                 dw_style,
673                 FALSE,
674                 dw_ex_style,
675                 GetDpiForSystem(),
676             ) == 0
677             {
678                 syscall_bail!("Failed to call AdjustWindowRectExForDpi()");
679             }
680         }
681         Ok(window_rect.to_rect())
682     }
683 
684     /// Calls `GetWindowPlacement()` and `SetWindowPlacement()` internally.
set_restored_pos(&self, window_rect: &Rect) -> Result<()>685     pub fn set_restored_pos(&self, window_rect: &Rect) -> Result<()> {
686         let mut window_placement = WINDOWPLACEMENT {
687             length: mem::size_of::<WINDOWPLACEMENT>().try_into().unwrap(),
688             ..Default::default()
689         };
690         // SAFETY:
691         // Safe because `GuiWindow` object won't outlive the HWND, we know `window_placement` is
692         // valid, and failures are handled below.
693         unsafe {
694             if GetWindowPlacement(self.hwnd, &mut window_placement) == 0 {
695                 syscall_bail!("Failed to call GetWindowPlacement()");
696             }
697             window_placement.rcNormalPosition = window_rect.to_sys_rect();
698             if SetWindowPlacement(self.hwnd, &window_placement) == 0 {
699                 syscall_bail!("Failed to call SetWindowPlacement()");
700             }
701         }
702         Ok(())
703     }
704 
705     /// Calls `PostMessageW()` internally.
post_message(&self, msg: UINT, w_param: WPARAM, l_param: LPARAM) -> Result<()>706     pub fn post_message(&self, msg: UINT, w_param: WPARAM, l_param: LPARAM) -> Result<()> {
707         // SAFETY:
708         // Safe because `GuiWindow` object won't outlive the HWND.
709         unsafe {
710             if PostMessageW(self.hwnd, msg, w_param, l_param) == 0 {
711                 syscall_bail!("Failed to call PostMessageW()");
712             }
713         }
714         Ok(())
715     }
716 
717     /// Calls `LoadIconW()` internally.
load_custom_icon(hinstance: HINSTANCE, resource_id: WORD) -> Result<HICON>718     pub(crate) fn load_custom_icon(hinstance: HINSTANCE, resource_id: WORD) -> Result<HICON> {
719         // SAFETY:
720         // Safe because we handle failures below.
721         unsafe {
722             let hicon = LoadIconW(hinstance, MAKEINTRESOURCEW(resource_id));
723             if hicon.is_null() {
724                 syscall_bail!("Failed to call LoadIconW()");
725             }
726             Ok(hicon)
727         }
728     }
729 
730     /// Calls `LoadCursorW()` internally.
load_system_cursor(cursor_id: LPCWSTR) -> Result<HCURSOR>731     pub(crate) fn load_system_cursor(cursor_id: LPCWSTR) -> Result<HCURSOR> {
732         // SAFETY:
733         // Safe because we handle failures below.
734         unsafe {
735             let hcursor = LoadCursorW(null_mut(), cursor_id);
736             if hcursor.is_null() {
737                 syscall_bail!("Failed to call LoadCursorW()");
738             }
739             Ok(hcursor)
740         }
741     }
742 
743     /// Calls `GetStockObject()` internally.
create_opaque_black_brush() -> Result<HBRUSH>744     pub(crate) fn create_opaque_black_brush() -> Result<HBRUSH> {
745         // SAFETY:
746         // Safe because we handle failures below.
747         unsafe {
748             let hobject = GetStockObject(BLACK_BRUSH as i32);
749             if hobject.is_null() {
750                 syscall_bail!("Failed to call GetStockObject()");
751             }
752             Ok(hobject as HBRUSH)
753         }
754     }
755 
756     /// Calls `RegisterTouchWindow()` internally.
register_touch(&self)757     fn register_touch(&self) {
758         // SAFETY: Safe because `GuiWindow` object won't outlive the HWND.
759         if unsafe { RegisterTouchWindow(self.handle(), 0) } == 0 {
760             // For now, we register touch only to get stats. It is ok if the registration fails.
761             // SAFETY: trivially-safe
762             warn!("failed to register touch: {}", unsafe { GetLastError() });
763         }
764     }
765 }
766 
767 impl BasicWindow for GuiWindow {
768     /// # Safety
769     /// The returned handle should be used carefully, since it may have become invalid if it
770     /// outlives the `GuiWindow` object.
handle(&self) -> HWND771     unsafe fn handle(&self) -> HWND {
772         self.hwnd
773     }
774 }
775 
776 /// A message-only window is always invisible, and is only responsible for sending and receiving
777 /// messages. The owner of `MessageOnlyWindow` object is responsible for dropping it before the
778 /// underlying window is completely gone.
779 pub(crate) struct MessageOnlyWindow {
780     hwnd: HWND,
781 }
782 
783 impl MessageOnlyWindow {
784     /// # Safety
785     /// The owner of `MessageOnlyWindow` object is responsible for dropping it before we finish
786     /// processing `WM_NCDESTROY`, because the window handle will become invalid afterwards.
new(class_name: &str, title: &str) -> Result<Self>787     pub unsafe fn new(class_name: &str, title: &str) -> Result<Self> {
788         info!("Creating message-only window");
789         static CONTEXT_MESSAGE: &str = "When creating MessageOnlyWindow";
790 
791         let window = Self {
792             hwnd: create_sys_window(
793                 get_current_module_handle(),
794                 class_name,
795                 title,
796                 /* dw_style */ 0,
797                 HWND_MESSAGE,
798                 /* initial_window_size */ &size2(0, 0),
799             )
800             .context(CONTEXT_MESSAGE)?,
801         };
802         window.register_raw_input_mouse().context(CONTEXT_MESSAGE)?;
803         Ok(window)
804     }
805 
806     /// Registers this window as the receiver of raw mouse input events.
807     ///
808     /// On Windows, an application can only have one window that receives raw input events, so we
809     /// make `MessageOnlyWindow` take on this role and reroute events to the foreground `GuiWindow`.
register_raw_input_mouse(&self) -> Result<()>810     fn register_raw_input_mouse(&self) -> Result<()> {
811         let mouse_device = RAWINPUTDEVICE {
812             usUsagePage: 1, // Generic
813             usUsage: 2,     // Mouse
814             dwFlags: 0,
815             // SAFETY: Safe because `self` won't outlive the HWND.
816             hwndTarget: unsafe { self.handle() },
817         };
818         // SAFETY: Safe because `mouse_device` lives longer than this function call.
819         if unsafe {
820             RegisterRawInputDevices(
821                 &mouse_device as PCRAWINPUTDEVICE,
822                 1,
823                 mem::size_of::<RAWINPUTDEVICE>() as u32,
824             )
825         } == 0
826         {
827             syscall_bail!("Relative mouse is broken. Failed to call RegisterRawInputDevices()");
828         }
829         Ok(())
830     }
831 }
832 
833 impl BasicWindow for MessageOnlyWindow {
834     /// # Safety
835     /// The returned handle should be used carefully, since it may have become invalid if it
836     /// outlives the `MessageOnlyWindow` object.
handle(&self) -> HWND837     unsafe fn handle(&self) -> HWND {
838         self.hwnd
839     }
840 }
841 
842 /// 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>843 fn create_sys_window(
844     hinstance: HINSTANCE,
845     class_name: &str,
846     title: &str,
847     dw_style: DWORD,
848     hwnd_parent: HWND,
849     initial_window_size: &Size2D<i32, HostWindowSpace>,
850 ) -> Result<HWND> {
851     // SAFETY:
852     // Safe because we handle failures below.
853     let hwnd = unsafe {
854         CreateWindowExW(
855             /* dwExStyle */ 0,
856             win32_wide_string(class_name).as_ptr(),
857             win32_wide_string(title).as_ptr(),
858             dw_style,
859             /* x */ 0,
860             /* y */ 0,
861             initial_window_size.width,
862             initial_window_size.height,
863             hwnd_parent,
864             /* hMenu */ null_mut(),
865             hinstance,
866             /* lpParam */ null_mut(),
867         )
868     };
869     if hwnd.is_null() {
870         syscall_bail!("Failed to call CreateWindowExW()");
871     }
872     info!("Created window {:p}", hwnd);
873     Ok(hwnd)
874 }
875 
876 /// Calls `GetModuleHandleW()` internally.
get_current_module_handle() -> HMODULE877 pub(crate) fn get_current_module_handle() -> HMODULE {
878     // SAFETY:
879     // Safe because we handle failures below.
880     let hmodule = unsafe { GetModuleHandleW(null_mut()) };
881     if hmodule.is_null() {
882         // If it fails, we are in a very broken state and it doesn't make sense to keep running.
883         panic!(
884             "Failed to call GetModuleHandleW() for the current module (Error code {})",
885             // SAFETY: trivially safe
886             unsafe { GetLastError() }
887         );
888     }
889     hmodule
890 }
891 
892 /// If the resolution/orientation of the monitor changes, or if the monitor is unplugged, this must
893 /// be recreated with a valid HMONITOR.
894 pub struct MonitorInfo {
895     pub hmonitor: HMONITOR,
896     pub display_rect: Rect,
897     pub work_rect: Rect,
898     raw_dpi: i32,
899     // Whether we are running in a Remote Desktop Protocol (RDP) session. The monitor DPI returned
900     // by `GetDpiForMonitor()` may not make sense in that case. For example, the DPI is always 25
901     // under Chrome Remote Desktop, which is way lower than the standard DPI 96. This might be a
902     // flaw in RDP itself. We have to override the DPI in that case, otherwise the guest DPI
903     // calculated based on it would be too low as well.
904     // https://learn.microsoft.com/en-us/troubleshoot/windows-server/shell-experience/dpi-adjustment-unavailable-in-rdp
905     is_rdp_session: bool,
906 }
907 
908 impl MonitorInfo {
909     /// # Safety
910     /// Caller is responsible for ensuring that `hmonitor` is a valid handle.
new(hmonitor: HMONITOR) -> Result<Self>911     pub unsafe fn new(hmonitor: HMONITOR) -> Result<Self> {
912         let monitor_info: MONITORINFO =
913             Self::get_monitor_info(hmonitor).context("When creating MonitorInfo")?;
914         // Docs state that apart from `GetSystemMetrics(SM_REMOTESESSION)`, we also need to check
915         // registry entries to see if we are running in a remote session that uses RemoteFX vGPU:
916         // https://learn.microsoft.com/en-us/windows/win32/termserv/detecting-the-terminal-services-environment
917         // However, RemoteFX vGPU was then removed because of security vulnerabilities:
918         // https://support.microsoft.com/en-us/topic/kb4570006-update-to-disable-and-remove-the-remotefx-vgpu-component-in-windows-bbdf1531-7188-2bf4-0de6-641de79f09d2
919         // So, we are only calling `GetSystemMetrics(SM_REMOTESESSION)` here until this changes in
920         // the future.
921         // SAFETY:
922         // Safe because no memory management is needed for arguments.
923         let is_rdp_session = unsafe { GetSystemMetrics(SM_REMOTESESSION) != 0 };
924         Ok(Self {
925             hmonitor,
926             display_rect: monitor_info.rcMonitor.to_rect(),
927             work_rect: monitor_info.rcWork.to_rect(),
928             raw_dpi: Self::get_monitor_dpi(hmonitor),
929             is_rdp_session,
930         })
931     }
932 
get_dpi(&self) -> i32933     pub fn get_dpi(&self) -> i32 {
934         if self.is_rdp_session {
935             // Override the DPI since the system may not tell us the correct value in RDP sessions.
936             DEFAULT_HOST_DPI
937         } else {
938             self.raw_dpi
939         }
940     }
941 
942     /// Calls `GetMonitorInfoW()` internally.
943     /// # Safety
944     /// Caller is responsible for ensuring that `hmonitor` is a valid handle.
get_monitor_info(hmonitor: HMONITOR) -> Result<MONITORINFO>945     unsafe fn get_monitor_info(hmonitor: HMONITOR) -> Result<MONITORINFO> {
946         let mut monitor_info = MONITORINFO {
947             cbSize: mem::size_of::<MONITORINFO>().try_into().unwrap(),
948             ..Default::default()
949         };
950         if GetMonitorInfoW(hmonitor, &mut monitor_info) == 0 {
951             syscall_bail!("Failed to call GetMonitorInfoW()");
952         }
953         Ok(monitor_info)
954     }
955 
956     /// Calls `GetDpiForMonitor()` internally.
get_monitor_dpi(hmonitor: HMONITOR) -> i32957     fn get_monitor_dpi(hmonitor: HMONITOR) -> i32 {
958         let mut dpi_x = 0;
959         let mut dpi_y = 0;
960         // SAFETY:
961         // This is always safe since `GetDpiForMonitor` won't crash if HMONITOR is invalid, but
962         // return E_INVALIDARG.
963         unsafe {
964             if GetDpiForMonitor(hmonitor, MDT_RAW_DPI, &mut dpi_x, &mut dpi_y) == S_OK
965                 || GetDpiForMonitor(hmonitor, MDT_DEFAULT, &mut dpi_x, &mut dpi_y) == S_OK
966             {
967                 // We assume screen pixels are square and DPI in different directions are the same.
968                 dpi_x as i32
969             } else {
970                 error!("Failed to retrieve DPI with HMONITOR {:p}", hmonitor);
971                 DEFAULT_HOST_DPI
972             }
973         }
974     }
975 }
976 
977 impl fmt::Debug for MonitorInfo {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result978     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
979         write!(
980             f,
981             "{{hmonitor: {:p}, display_rect: {:?}, work_rect: {:?}, DPI: {}{}}}",
982             self.hmonitor,
983             self.display_rect,
984             self.work_rect,
985             self.get_dpi(),
986             if self.is_rdp_session {
987                 format!(" (raw value: {}, overriden due to RDP)", self.raw_dpi)
988             } else {
989                 String::new()
990             }
991         )
992     }
993 }
994