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