xref: /aosp_15_r20/external/crosvm/gpu_display/src/gpu_display_win/window_message_processor.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::rc::Rc;
6 use std::time::Duration;
7 
8 use anyhow::Context;
9 use anyhow::Result;
10 use base::error;
11 use base::info;
12 use base::Tube;
13 use cros_tracing::trace_event;
14 use euclid::point2;
15 use euclid::size2;
16 use euclid::Rect;
17 use linux_input_sys::virtio_input_event;
18 use winapi::shared::minwindef::LPARAM;
19 use winapi::shared::minwindef::LRESULT;
20 use winapi::shared::minwindef::UINT;
21 use winapi::shared::minwindef::WPARAM;
22 use winapi::um::winuser::HRAWINPUT;
23 use winapi::um::winuser::MK_XBUTTON1;
24 use winapi::um::winuser::SWP_HIDEWINDOW;
25 use winapi::um::winuser::SWP_SHOWWINDOW;
26 use winapi::um::winuser::WINDOWPOS;
27 use winapi::um::winuser::WM_ACTIVATE;
28 use winapi::um::winuser::WM_DISPLAYCHANGE;
29 use winapi::um::winuser::WM_ENTERSIZEMOVE;
30 use winapi::um::winuser::WM_EXITSIZEMOVE;
31 use winapi::um::winuser::WM_KEYDOWN;
32 use winapi::um::winuser::WM_KEYUP;
33 use winapi::um::winuser::WM_LBUTTONDOWN;
34 use winapi::um::winuser::WM_LBUTTONUP;
35 use winapi::um::winuser::WM_MBUTTONDOWN;
36 use winapi::um::winuser::WM_MBUTTONUP;
37 use winapi::um::winuser::WM_MOUSEACTIVATE;
38 use winapi::um::winuser::WM_MOUSEMOVE;
39 use winapi::um::winuser::WM_MOUSEWHEEL;
40 use winapi::um::winuser::WM_RBUTTONDOWN;
41 use winapi::um::winuser::WM_RBUTTONUP;
42 use winapi::um::winuser::WM_SETCURSOR;
43 use winapi::um::winuser::WM_SETFOCUS;
44 use winapi::um::winuser::WM_SIZE;
45 use winapi::um::winuser::WM_SIZING;
46 use winapi::um::winuser::WM_SYSKEYDOWN;
47 use winapi::um::winuser::WM_SYSKEYUP;
48 use winapi::um::winuser::WM_USER;
49 use winapi::um::winuser::WM_WINDOWPOSCHANGED;
50 use winapi::um::winuser::WM_WINDOWPOSCHANGING;
51 use winapi::um::winuser::WM_XBUTTONDOWN;
52 use winapi::um::winuser::WM_XBUTTONUP;
53 
54 use super::keyboard_input_manager::KeyboardInputManager;
55 use super::window::BasicWindow;
56 use super::window::GuiWindow;
57 use super::window::MessagePacket;
58 use super::window_message_dispatcher::DisplayEventDispatcher;
59 use super::HostWindowSpace;
60 use super::MouseMode;
61 use super::ObjectId;
62 use super::Surface;
63 use crate::EventDevice;
64 use crate::EventDeviceKind;
65 
66 // Once a window message is added to the message queue, if it is not retrieved within 5 seconds,
67 // Windows will mark the application as "Not Responding", so we'd better finish processing any
68 // message within this timeout and retrieve the next one.
69 // https://docs.microsoft.com/en-us/windows/win32/win7appqual/preventing-hangs-in-windows-applications
70 pub(crate) const HANDLE_WINDOW_MESSAGE_TIMEOUT: Duration = Duration::from_secs(5);
71 
72 /// Thread message for destroying all windows and releasing all resources during a
73 /// `WindowProcedureThread` drop. This may be triggered if crosvm has encountered errors and has to
74 /// shut down, or if the user/service initiates the shutdown.
75 pub(crate) const WM_USER_SHUTDOWN_WNDPROC_THREAD_INTERNAL: UINT = WM_USER;
76 
77 // Message for handling the change in host viewport. This is sent when the host window size changes
78 // and we need to render to a different part of the window. The new width and height are sent as the
79 // low/high word of lParam.
80 pub(crate) const WM_USER_HOST_VIEWPORT_CHANGE_INTERNAL: UINT = WM_USER + 1;
81 
82 /// Thread message for handling the message sent from the GPU worker thread. A pointer to enum
83 /// `DisplaySendToWndProc` is sent as the lParam. Note that the receiver is responsible for
84 /// destructing the message.
85 pub(crate) const WM_USER_HANDLE_DISPLAY_MESSAGE_INTERNAL: UINT = WM_USER + 2;
86 
87 /// Struct for resources used for Surface creation.
88 pub struct SurfaceResources {
89     pub display_event_dispatcher: DisplayEventDispatcher,
90     pub gpu_main_display_tube: Option<Rc<Tube>>,
91 }
92 
93 pub type CreateSurfaceFunction = Box<dyn FnOnce(&GuiWindow, SurfaceResources) -> Result<Surface>>;
94 
95 /// Called after the surface creation finishes. The argument indicates whether that was successful.
96 pub type CreateSurfaceCallback = Box<dyn FnOnce(bool)>;
97 
98 /// Messages sent from the GPU worker thread to the WndProc thread.
99 pub enum DisplaySendToWndProc {
100     CreateSurface {
101         scanout_id: u32,
102         function: CreateSurfaceFunction,
103         callback: CreateSurfaceCallback,
104     },
105     ReleaseSurface {
106         surface_id: u32,
107     },
108     ImportEventDevice {
109         event_device_id: ObjectId,
110         event_device: EventDevice,
111     },
112     /// Handle a guest -> host input_event.
113     HandleEventDevice(ObjectId),
114     SetMouseMode {
115         surface_id: u32,
116         mouse_mode: MouseMode,
117     },
118 }
119 
120 /// This struct wraps a `GuiWindow` that is currently not associated with any `Surface`.
121 pub(crate) struct WindowResources {
122     window: GuiWindow,
123 }
124 
125 impl WindowResources {
126     /// # Safety
127     /// The owner of `WindowResource` object is responsible for dropping it before we finish
128     /// processing `WM_NCDESTROY` for this window, because the window handle will become invalid
129     /// afterwards.
new(window: GuiWindow) -> Self130     pub unsafe fn new(window: GuiWindow) -> Self {
131         Self { window }
132     }
133 
window(&self) -> &GuiWindow134     pub fn window(&self) -> &GuiWindow {
135         &self.window
136     }
137 }
138 
139 /// This struct drives the underlying `Surface` object to process window messages retrieved from the
140 /// message pump.
141 pub(crate) struct WindowMessageProcessor {
142     window_resources: WindowResources,
143     surface: Surface,
144 }
145 
146 impl WindowMessageProcessor {
147     /// Creates a `Surface` and associates it with the window. To dissociate them, call
148     /// `release_surface_and_take_window_resources()` below.
149     /// # Safety
150     /// The owner of `WindowMessageProcessor` object is responsible for dropping it before we finish
151     /// processing `WM_NCDESTROY` for this window, because the window handle will become invalid
152     /// afterwards.
new( create_surface_func: CreateSurfaceFunction, surface_resources: SurfaceResources, window_resources: WindowResources, ) -> Result<Self>153     pub unsafe fn new(
154         create_surface_func: CreateSurfaceFunction,
155         surface_resources: SurfaceResources,
156         window_resources: WindowResources,
157     ) -> Result<Self> {
158         create_surface_func(&window_resources.window, surface_resources)
159             .map(|surface| Self {
160                 window_resources,
161                 surface,
162             })
163             .context("When creating Surface")
164     }
165 
surface_id(&self) -> u32166     pub fn surface_id(&self) -> u32 {
167         self.surface.surface_id()
168     }
169 
170     /// Drops the associated `Surface` and turns `self` back into `WindowResources`. This also hides
171     /// the window if it hasn't been hidden.
172     /// # Safety
173     /// The owner of `WindowResources` object is responsible for dropping it before we finish
174     /// processing `WM_NCDESTROY` for this window, because the window handle will become invalid
175     /// afterwards.
release_surface_and_take_window_resources(self) -> WindowResources176     pub unsafe fn release_surface_and_take_window_resources(self) -> WindowResources {
177         let surface_id = self.surface_id();
178         let resources = self.window_resources;
179         info!(
180             "Releasing surface {} associated with scanout {}",
181             surface_id,
182             resources.window().scanout_id(),
183         );
184         if let Err(e) = resources.window().hide_if_visible() {
185             error!("Failed to hide window before releasing surface: {:?}", e);
186         }
187         resources
188     }
189 
process_window_message( &mut self, packet: &MessagePacket, keyboard_input_manager: &KeyboardInputManager, ) -> LRESULT190     pub fn process_window_message(
191         &mut self,
192         packet: &MessagePacket,
193         keyboard_input_manager: &KeyboardInputManager,
194     ) -> LRESULT {
195         // Message handlers may read window states so we should update those states first.
196         let window = &mut self.window_resources.window;
197         window.update_states(packet.msg, packet.w_param);
198 
199         let _trace_event = Self::new_trace_event(packet.msg);
200 
201         let window_message: WindowMessage = packet.into();
202         keyboard_input_manager.handle_window_message(window, &window_message);
203         self.surface
204             .handle_window_message(window, window_message)
205             .unwrap_or_else(|| window.default_process_message(packet))
206     }
207 
process_general_message( &mut self, message: GeneralMessage, keyboard_input_manager: &KeyboardInputManager, )208     pub fn process_general_message(
209         &mut self,
210         message: GeneralMessage,
211         keyboard_input_manager: &KeyboardInputManager,
212     ) {
213         self.surface.handle_general_message(
214             &self.window_resources.window,
215             &message,
216             keyboard_input_manager,
217         );
218     }
219 
220     #[allow(clippy::if_same_then_else)]
new_trace_event(msg: UINT) -> impl std::any::Any221     fn new_trace_event(msg: UINT) -> impl std::any::Any {
222         if msg == WM_USER_HOST_VIEWPORT_CHANGE_INTERNAL {
223             trace_event!(gpu_display, "WM_USER_HOST_VIEWPORT_CHANGE_INTERNAL")
224         } else {
225             trace_event!(gpu_display, "WM_OTHER_GUI_WINDOW_MESSAGE")
226         }
227     }
228 }
229 
230 /// Indicates whether the window is getting shown or hidden when receiving `WM_WINDOWPOSCHANGED`.
231 #[derive(PartialEq, Debug)]
232 pub enum WindowVisibilityChange {
233     Unchanged,
234     Shown,
235     Hidden,
236 }
237 
238 impl From<UINT> for WindowVisibilityChange {
from(flags: UINT) -> Self239     fn from(flags: UINT) -> Self {
240         if flags & SWP_SHOWWINDOW != 0 {
241             Self::Shown
242         } else if flags & SWP_HIDEWINDOW != 0 {
243             Self::Hidden
244         } else {
245             Self::Unchanged
246         }
247     }
248 }
249 
250 /// General window messages that multiple modules may want to process, such as the window manager,
251 /// input manager, IME handler, etc.
252 #[derive(Debug)]
253 pub enum WindowMessage {
254     /// `WM_ACTIVATE`, "sent to both the window being activated and the window being deactivated."
255     WindowActivate { is_activated: bool },
256     /// Window location and size related messages.
257     WindowPos(WindowPosMessage),
258     /// Mouse related messages.
259     Mouse(MouseMessage),
260     /// `WM_SETFOCUS`, "sent to a window after it has gained the keyboard focus."
261     KeyboardFocus,
262     /// `WM_KEYDOWN`, `WM_KEYUP`, `WM_SYSKEYDOWN` or `WM_SYSKEYUP`, "posted to the window with the
263     /// keyboard focus when a nonsystem/system key is pressed/released."
264     Key {
265         is_sys_key: bool,
266         is_down: bool,
267         w_param: WPARAM,
268         l_param: LPARAM,
269     },
270     /// `WM_DISPLAYCHANGE`, "sent to all windows when the display resolution has changed."
271     DisplayChange,
272     /// `WM_USER_HOST_VIEWPORT_CHANGE_INTERNAL`.
273     HostViewportChange { l_param: LPARAM },
274     /// Not one of the general window messages we care about.
275     Other(MessagePacket),
276 }
277 
278 /// Window location and size related window messages.
279 #[derive(Debug)]
280 pub enum WindowPosMessage {
281     /// `WM_WINDOWPOSCHANGING`, "sent to a window whose size, position, or place in the Z order is
282     /// about to change."
283     WindowPosChanging { l_param: LPARAM },
284     /// `WM_WINDOWPOSCHANGED`, "sent to a window whose size, position, or place in the Z order has
285     /// changed."
286     WindowPosChanged {
287         visibility_change: WindowVisibilityChange,
288         window_rect: Rect<i32, HostWindowSpace>,
289     },
290     /// `WM_SIZING`, "sent to a window that the user is resizing."
291     WindowSizeChanging { w_param: WPARAM, l_param: LPARAM },
292     /// `WM_SIZE`, "sent to a window after its size has changed."
293     WindowSizeChanged { w_param: WPARAM, l_param: LPARAM },
294     /// `WM_ENTERSIZEMOVE`, "sent one time to a window after it enters the moving or sizing modal
295     /// loop."
296     EnterSizeMove,
297     /// `WM_EXITSIZEMOVE`, "sent one time to a window, after it has exited the moving or sizing
298     /// modal loop."
299     ExitSizeMove,
300 }
301 
302 impl std::fmt::Display for WindowPosMessage {
fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result303     fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
304         match self {
305             Self::WindowPosChanging { .. } => write!(f, "WindowPosChanging"),
306             Self::WindowPosChanged { .. } => write!(f, "WindowPosChanged"),
307             Self::WindowSizeChanging { .. } => write!(f, "WindowSizeChanging"),
308             Self::WindowSizeChanged { .. } => write!(f, "WindowSizeChanged"),
309             Self::EnterSizeMove => write!(f, "EnterSizeMove"),
310             Self::ExitSizeMove => write!(f, "ExitSizeMove"),
311         }
312     }
313 }
314 
315 /// Mouse related window messages.
316 #[derive(Debug)]
317 pub enum MouseMessage {
318     /// `WM_MOUSEACTIVATE`, "sent when the cursor is in an inactive window and the user presses a
319     /// mouse button."
320     MouseActivate { l_param: LPARAM },
321     /// `WM_MOUSEMOVE`, "posted to a window when the cursor moves."
322     MouseMove { w_param: WPARAM, l_param: LPARAM },
323     /// `WM_LBUTTONDOWN` or `WM_LBUTTONUP`, "posted when the user presses/releases the left mouse
324     /// button while the cursor is in the client area of a window."
325     LeftMouseButton { is_down: bool, l_param: LPARAM },
326     /// `WM_RBUTTONDOWN` or `WM_RBUTTONUP`, "posted when the user presses/releases the right mouse
327     /// button while the cursor is in the client area of a window."
328     RightMouseButton { is_down: bool },
329     /// `WM_MBUTTONDOWN` or `WM_MBUTTONUP`, "posted when the user presses/releases the middle mouse
330     /// button while the cursor is in the client area of a window."
331     MiddleMouseButton { is_down: bool },
332     /// `WM_XBUTTONDOWN` or `WM_XBUTTONUP`, "posted when the user presses/releases the forward
333     /// mouse button while the cursor is in the client area of a window."
334     ForwardMouseButton { is_down: bool },
335     /// `WM_XBUTTONDOWN` or `WM_XBUTTONUP`, "posted when the user presses/releases the back mouse
336     /// button while the cursor is in the client area of a window."
337     BackMouseButton { is_down: bool },
338     /// `WM_MOUSEWHEEL`, "sent to the focus window when the mouse wheel is rotated."
339     MouseWheel { w_param: WPARAM, l_param: LPARAM },
340     /// `WM_SETCURSOR`, "sent to a window if the mouse causes the cursor to move within a window
341     /// and mouse input is not captured."
342     SetCursor,
343 }
344 
345 impl From<&MessagePacket> for WindowMessage {
from(packet: &MessagePacket) -> Self346     fn from(packet: &MessagePacket) -> Self {
347         let MessagePacket {
348             msg,
349             w_param,
350             l_param,
351         } = *packet;
352 
353         match msg {
354             WM_ACTIVATE => Self::WindowActivate {
355                 is_activated: w_param != 0,
356             },
357             WM_WINDOWPOSCHANGING => {
358                 Self::WindowPos(WindowPosMessage::WindowPosChanging { l_param })
359             }
360             WM_WINDOWPOSCHANGED => {
361                 // SAFETY:
362                 // Safe because it will live at least until we finish handling
363                 // `WM_WINDOWPOSCHANGED`.
364                 let window_pos: WINDOWPOS = unsafe { *(l_param as *mut WINDOWPOS) };
365                 Self::WindowPos(WindowPosMessage::WindowPosChanged {
366                     visibility_change: window_pos.flags.into(),
367                     window_rect: Rect::new(
368                         point2(window_pos.x, window_pos.y),
369                         size2(window_pos.cx, window_pos.cy),
370                     ),
371                 })
372             }
373             WM_SIZING => Self::WindowPos(WindowPosMessage::WindowSizeChanging { w_param, l_param }),
374             WM_SIZE => Self::WindowPos(WindowPosMessage::WindowSizeChanged { w_param, l_param }),
375             WM_ENTERSIZEMOVE => Self::WindowPos(WindowPosMessage::EnterSizeMove),
376             WM_EXITSIZEMOVE => Self::WindowPos(WindowPosMessage::ExitSizeMove),
377             WM_MOUSEACTIVATE => Self::Mouse(MouseMessage::MouseActivate { l_param }),
378             WM_MOUSEMOVE => Self::Mouse(MouseMessage::MouseMove { w_param, l_param }),
379             WM_LBUTTONDOWN | WM_LBUTTONUP => Self::Mouse(MouseMessage::LeftMouseButton {
380                 is_down: msg == WM_LBUTTONDOWN,
381                 l_param,
382             }),
383             WM_RBUTTONDOWN | WM_RBUTTONUP => Self::Mouse(MouseMessage::RightMouseButton {
384                 is_down: msg == WM_RBUTTONDOWN,
385             }),
386             WM_MBUTTONDOWN | WM_MBUTTONUP => Self::Mouse(MouseMessage::MiddleMouseButton {
387                 is_down: msg == WM_MBUTTONDOWN,
388             }),
389             WM_XBUTTONDOWN | WM_XBUTTONUP => Self::Mouse(if w_param & MK_XBUTTON1 == MK_XBUTTON1 {
390                 MouseMessage::ForwardMouseButton {
391                     is_down: msg == WM_XBUTTONDOWN,
392                 }
393             } else {
394                 MouseMessage::BackMouseButton {
395                     is_down: msg == WM_XBUTTONDOWN,
396                 }
397             }),
398             WM_MOUSEWHEEL => Self::Mouse(MouseMessage::MouseWheel { w_param, l_param }),
399             WM_SETCURSOR => Self::Mouse(MouseMessage::SetCursor),
400             WM_SETFOCUS => Self::KeyboardFocus,
401             WM_KEYDOWN | WM_KEYUP | WM_SYSKEYDOWN | WM_SYSKEYUP => Self::Key {
402                 is_sys_key: msg == WM_SYSKEYDOWN || msg == WM_SYSKEYUP,
403                 is_down: msg == WM_KEYDOWN || msg == WM_SYSKEYDOWN,
404                 w_param,
405                 l_param,
406             },
407             WM_DISPLAYCHANGE => Self::DisplayChange,
408             WM_USER_HOST_VIEWPORT_CHANGE_INTERNAL => Self::HostViewportChange { l_param },
409             _ => Self::Other(*packet),
410         }
411     }
412 }
413 
414 /// Messages that are either rerouted from other windows (e.g. WM_INPUT), or not sent/posted by the
415 /// system.
416 pub enum GeneralMessage {
417     /// This should be sent once when it is safe to assume all future messages targeting the GUI
418     /// window will be dispatched to this `WindowMessageProcessor`.
419     MessageDispatcherAttached,
420     RawInputEvent(HRAWINPUT),
421     GuestEvent {
422         event_device_kind: EventDeviceKind,
423         event: virtio_input_event,
424     },
425     SetMouseMode(MouseMode),
426 }
427