Path: blob/main/gpu_display/src/gpu_display_win/window_message_dispatcher.rs
5394 views
// Copyright 2022 The ChromiumOS Authors1// Use of this source code is governed by a BSD-style license that can be2// found in the LICENSE file.34use std::cell::RefCell;5use std::collections::HashMap;6use std::io::ErrorKind;7use std::marker::PhantomPinned;8use std::os::raw::c_void;9use std::pin::Pin;10use std::rc::Rc;1112use anyhow::bail;13use anyhow::Context;14use anyhow::Result;15use base::error;16use base::info;17use base::Event;18use base::Tube;19use cros_tracing::trace_event;20use linux_input_sys::virtio_input_event;21#[cfg(feature = "kiwi")]22use vm_control::ServiceSendToGpu;23use win_util::win32_wide_string;24use winapi::shared::minwindef::LRESULT;25use winapi::shared::windef::HWND;26use winapi::um::winuser::DefWindowProcW;27use winapi::um::winuser::GetForegroundWindow;28use winapi::um::winuser::PostQuitMessage;29use winapi::um::winuser::RemovePropW;30use winapi::um::winuser::HRAWINPUT;31use winapi::um::winuser::WM_CLOSE;32use winapi::um::winuser::WM_INPUT;33use winapi::um::winuser::WM_NCDESTROY;3435use super::keyboard_input_manager::KeyboardInputManager;36use super::window::BasicWindow;37use super::window::GuiWindow;38use super::window::MessageOnlyWindow;39use super::window::MessagePacket;40use super::window_message_processor::*;41use super::MouseMode;42use super::ObjectId;43use crate::EventDevice;44use crate::EventDeviceKind;4546/// The pointer to dispatcher will be stored with HWND using `SetPropW()` with the following name.47pub(crate) const DISPATCHER_PROPERTY_NAME: &str = "PROP_WND_MSG_DISPATCHER";4849/// Tracks the ids of all event devices in one kind. For each kind, we either have one event device50/// shared by all scanouts (guest displays), or one per scanout.51enum EventDeviceIds {52GlobalDevice(ObjectId),53PerScanoutDevices(Vec<ObjectId>),54}5556/// This class is used to dispatch input events from the display to the guest input device. It is57/// also used to receive events from the input device (e.g. guest) on behalf of the window so they58/// can be routed back to the window for processing.59#[derive(Clone)]60pub struct DisplayEventDispatcher {61event_devices: Rc<RefCell<HashMap<ObjectId, EventDevice>>>,62event_device_ids: Rc<RefCell<HashMap<EventDeviceKind, EventDeviceIds>>>,63}6465impl DisplayEventDispatcher {66pub fn new() -> Self {67Self {68event_devices: Default::default(),69event_device_ids: Default::default(),70}71}7273pub fn dispatch(74&self,75window: &GuiWindow,76events: &[virtio_input_event],77device_kind: EventDeviceKind,78) {79if let Some(event_device_id) = self.find_event_device_id(device_kind, window.scanout_id()) {80if let Some(event_device) = self.event_devices.borrow_mut().get_mut(&event_device_id) {81if let Err(e) = event_device.send_report(events.iter().cloned()) {82error!("Failed to send events to event device: {}", e);83}84}85}86}8788pub fn read_from_device(89&self,90event_device_id: ObjectId,91) -> Option<(EventDeviceKind, virtio_input_event)> {92if let Some(device) = self.event_devices.borrow_mut().get(&event_device_id) {93match device.recv_event_encoded() {94Ok(event) => return Some((device.kind(), event)),95Err(e) if e.kind() == ErrorKind::WouldBlock => return None,96Err(e) => error!(97"failed to read from event device {:?} (index: {}): {:?}",98device.kind(),99event_device_id,100e101),102}103} else {104error!(105"notified to read from event device {:?} but do not have a device with that ID",106event_device_id107);108}109None110}111112fn import_event_device(&mut self, event_device_id: ObjectId, event_device: EventDevice) {113info!("Importing {:?} (ID: {:?})", event_device, event_device_id);114let device_kind = event_device.kind();115let same_kind_device_ids = match device_kind {116EventDeviceKind::Touchscreen => {117// Temporarily removing from `self.event_device_ids`. Will be reinserted after118// adding `event_device_id`.119let mut per_scanout_device_ids = self120.event_device_ids121.borrow_mut()122.remove(&device_kind)123.and_then(|imported_device_ids| match imported_device_ids {124EventDeviceIds::PerScanoutDevices(ids) => Some(ids),125_ => unreachable!(),126})127.unwrap_or_default();128per_scanout_device_ids.push(event_device_id);129EventDeviceIds::PerScanoutDevices(per_scanout_device_ids)130}131_ => EventDeviceIds::GlobalDevice(event_device_id),132};133self.event_device_ids134.borrow_mut()135.insert(device_kind, same_kind_device_ids);136self.event_devices137.borrow_mut()138.insert(event_device_id, event_device);139}140141fn find_event_device_id(142&self,143device_kind: EventDeviceKind,144scanout_id: u32,145) -> Option<ObjectId> {146self.event_device_ids147.borrow()148.get(&device_kind)149.and_then(|same_kind_device_ids| match same_kind_device_ids {150EventDeviceIds::GlobalDevice(event_device_id) => Some(*event_device_id),151EventDeviceIds::PerScanoutDevices(event_device_ids) => {152event_device_ids.get(scanout_id as usize).cloned()153}154})155}156}157158impl Default for DisplayEventDispatcher {159fn default() -> Self {160Self::new()161}162}163164/// This struct is used for dispatching window messages. Note that messages targeting the WndProc165/// thread itself shouldn't be posted using `PostThreadMessageW()`, but posted as window messages to166/// `message_router_window`, so that they won't get lost when the modal loop is running.167///168/// This struct should be created before the WndProc thread enters the message loop. Once all169/// windows tracked by it are destroyed, it will signal exiting the message loop, and then it can be170/// dropped.171pub(crate) struct WindowMessageDispatcher {172message_router_window: Option<MessageOnlyWindow>,173vacant_gui_windows: HashMap<HWND, WindowResources>,174in_use_gui_windows: HashMap<HWND, WindowMessageProcessor>,175// We have a one-to-one mapping between virtio-gpu scanouts and GUI window surfaces. The index176// of the GUI window in this Vec will be the same as the associated scanout's id.177// These handles are only used for hashmap queries. Do not directly call Windows APIs on them.178gui_window_handles: Vec<HWND>,179keyboard_input_manager: KeyboardInputManager,180display_event_dispatcher: DisplayEventDispatcher,181gpu_main_display_tube: Option<Rc<Tube>>,182close_requested_event: Event,183// The dispatcher is pinned so that its address in the memory won't change, and it is always184// safe to use the pointer to it stored in the window.185_pinned_marker: PhantomPinned,186}187188impl WindowMessageDispatcher {189/// This function should only be called once from the WndProc thread. It will take the ownership190/// of the `GuiWindow` objects, and drop them before the underlying windows are completely gone.191/// TODO(b/238680252): This should be good enough for supporting multi-windowing, but we should192/// revisit it if we also want to manage some child windows of the crosvm window.193pub fn new(194message_router_window: MessageOnlyWindow,195gui_windows: Vec<GuiWindow>,196gpu_main_display_tube: Option<Rc<Tube>>,197close_requested_event: Event,198) -> Result<Pin<Box<Self>>> {199static CONTEXT_MESSAGE: &str = "When creating WindowMessageDispatcher";200let display_event_dispatcher = DisplayEventDispatcher::new();201let gui_window_handles = gui_windows202.iter()203.map(|window| {204// SAFETY:205// Safe because we will only use these handles to query hashmaps.206unsafe { window.handle() }207})208.collect();209let mut dispatcher = Box::pin(Self {210message_router_window: Some(message_router_window),211vacant_gui_windows: Default::default(), // To be updated.212in_use_gui_windows: Default::default(),213gui_window_handles,214keyboard_input_manager: KeyboardInputManager::new(display_event_dispatcher.clone()),215display_event_dispatcher,216gpu_main_display_tube,217close_requested_event,218_pinned_marker: PhantomPinned,219});220dispatcher221.as_mut()222.attach_thread_message_router()223.context(CONTEXT_MESSAGE)?;224dispatcher225.as_mut()226.create_window_resources(gui_windows)227.context(CONTEXT_MESSAGE)?;228Ok(dispatcher)229}230231/// # Safety232/// The caller must not use the handle after the message loop terminates.233pub unsafe fn message_router_handle(&self) -> Option<HWND> {234self.message_router_window235.as_ref()236.map(|router| router.handle())237}238239#[cfg(feature = "kiwi")]240pub fn process_service_message(self: Pin<&mut Self>, message: &ServiceSendToGpu) {241if matches!(message, ServiceSendToGpu::Shutdown) {242info!("Processing ShutdownRequest from service");243// Safe because we won't move the dispatcher out of the returned mutable reference.244unsafe { self.get_unchecked_mut().request_shutdown_gpu_display() };245return;246}247248// TODO(b/306024335): `ServiceSendToGpu` should specify the targeted display id.249// Safe because we won't move the dispatcher out of the returned mutable reference.250let primary_window_handle = self.primary_window_handle();251match unsafe {252self.get_unchecked_mut()253.in_use_gui_windows254.get_mut(&primary_window_handle)255} {256Some(processor) => processor.handle_service_message(message),257None => error!("Cannot handle service message because primary window is not in-use!"),258}259}260261/// Returns `Some` if the message is processed by the targeted window.262pub fn dispatch_window_message(263&mut self,264hwnd: HWND,265packet: &MessagePacket,266) -> Option<LRESULT> {267// First, check if the message is targeting the wndproc thread itself.268if let Some(router) = &self.message_router_window {269if router.is_same_window(hwnd) {270return if packet.msg == WM_INPUT {271// The message router window is the sink for all WM_INPUT messages targeting272// this application. We would reroute WM_INPUT to the current foreground window.273// SAFETY: trivially safe274let foreground_hwnd = unsafe { GetForegroundWindow() };275if let Some(processor) = self.in_use_gui_windows.get_mut(&foreground_hwnd) {276processor.process_general_message(277GeneralMessage::RawInputEvent(packet.l_param as HRAWINPUT),278&self.keyboard_input_manager,279);280}281// Always do default processing "so the system can perform cleanup".282// https://learn.microsoft.com/en-us/windows/win32/inputdev/wm-input#parameters283router.default_process_message(packet);284Some(0)285} else {286Some(self.process_simulated_thread_message(hwnd, packet))287};288}289}290291// Second, check if this message indicates any lifetime events of GUI windows.292if let Some(ret) = self.handle_gui_window_lifetime_message(hwnd, packet) {293return Some(ret);294}295296// Third, check if the message is targeting an in-use GUI window.297self.in_use_gui_windows298.get_mut(&hwnd)299.map(|processor| processor.process_window_message(packet, &self.keyboard_input_manager))300}301302// TODO(b/306407787): We won't need this once we have full support for multi-window.303fn primary_window_handle(&self) -> HWND {304self.gui_window_handles[0]305}306307fn attach_thread_message_router(self: Pin<&mut Self>) -> Result<()> {308let dispatcher_ptr = &*self as *const Self;309// SAFETY:310// Safe because we won't move the dispatcher out of it.311match unsafe { &self.get_unchecked_mut().message_router_window } {312// SAFETY:313// Safe because we guarantee the dispatcher outlives the thread message router.314Some(router) => unsafe { Self::store_pointer_in_window(dispatcher_ptr, router) },315None => bail!("Thread message router not found, cannot associate with dispatcher!"),316}317}318319fn create_window_resources(self: Pin<&mut Self>, windows: Vec<GuiWindow>) -> Result<()> {320// SAFETY:321// because we won't move the dispatcher out of it.322let pinned_dispatcher = unsafe { self.get_unchecked_mut() };323for window in windows.into_iter() {324if !window.is_valid() {325// SAFETY:326// Safe because we are just logging the handle value.327bail!("Window {:p} is invalid!", unsafe { window.handle() });328}329330// SAFETY:331// because we guarantee the dispatcher outlives our GUI windows.332unsafe { Self::store_pointer_in_window(&*pinned_dispatcher, &window)? };333334pinned_dispatcher.vacant_gui_windows.insert(335// SAFETY:336// Safe because this handle is only used as the hashmap kay.337unsafe { window.handle() },338// SAFETY:339// Safe the dispatcher will take care of the lifetime of the window.340unsafe { WindowResources::new(window) },341);342}343Ok(())344}345346/// Processes messages targeting the WndProc thread itself. Note that these messages are not347/// posted using `PostThreadMessageW()`, but posted to `message_router_window` as window348/// messages (hence "simulated"), so they won't get lost if the modal loop is running.349fn process_simulated_thread_message(350&mut self,351message_router_hwnd: HWND,352packet: &MessagePacket,353) -> LRESULT {354let MessagePacket {355msg,356w_param,357l_param,358} = *packet;359match msg {360WM_USER_HANDLE_DISPLAY_MESSAGE_INTERNAL => {361let _trace_event =362trace_event!(gpu_display, "WM_USER_HANDLE_DISPLAY_MESSAGE_INTERNAL");363// SAFETY:364// Safe because the sender gives up the ownership and expects the receiver to365// destruct the message.366let message = unsafe { Box::from_raw(l_param as *mut DisplaySendToWndProc) };367self.handle_display_message(*message);368}369WM_USER_SHUTDOWN_WNDPROC_THREAD_INTERNAL => {370let _trace_event =371trace_event!(gpu_display, "WM_USER_SHUTDOWN_WNDPROC_THREAD_INTERNAL");372self.shutdown();373}374_ => {375let _trace_event =376trace_event!(gpu_display, "WM_OTHER_MESSAGE_ROUTER_WINDOW_MESSAGE");377// SAFETY:378// Safe because we are processing a message targeting the message router window.379return unsafe { DefWindowProcW(message_router_hwnd, msg, w_param, l_param) };380}381}3820383}384385fn handle_display_message(&mut self, message: DisplaySendToWndProc) {386match message {387DisplaySendToWndProc::CreateSurface {388scanout_id,389function,390callback,391} => {392callback(self.create_surface(scanout_id, function));393}394DisplaySendToWndProc::ReleaseSurface { surface_id } => self.release_surface(surface_id),395DisplaySendToWndProc::ImportEventDevice {396event_device_id,397event_device,398} => {399self.display_event_dispatcher400.import_event_device(event_device_id, event_device);401}402DisplaySendToWndProc::HandleEventDevice(event_device_id) => {403self.handle_event_device(event_device_id)404}405DisplaySendToWndProc::SetMouseMode {406surface_id,407mouse_mode,408} => self.set_mouse_mode(surface_id, mouse_mode),409}410}411412fn handle_event_device(&mut self, event_device_id: ObjectId) {413// TODO(b/306407787): Events should be routed to the correct window.414match self415.in_use_gui_windows416.get_mut(&self.primary_window_handle())417{418Some(processor) => {419if let Some((event_device_kind, event)) = self420.display_event_dispatcher421.read_from_device(event_device_id)422{423processor.process_general_message(424GeneralMessage::GuestEvent {425event_device_kind,426event,427},428&self.keyboard_input_manager,429);430}431}432None => {433error!("Cannot handle event device because primary window is not in-use!")434}435}436}437438fn set_mouse_mode(&mut self, surface_id: u32, mouse_mode: MouseMode) {439match self440.in_use_gui_windows441.iter_mut()442.find(|(_, processor)| processor.surface_id() == surface_id)443{444Some(iter) => iter.1.process_general_message(445GeneralMessage::SetMouseMode(mouse_mode),446&self.keyboard_input_manager,447),448None => error!(449"Can't set mouse mode for surface {} because it is not in-use!",450surface_id451),452}453}454455/// Returns true if the surface is created successfully.456fn create_surface(457&mut self,458scanout_id: u32,459create_surface_func: CreateSurfaceFunction,460) -> bool {461// virtio-gpu prefers to use the lowest available scanout id when creating a new surface, so462// here we implictly prefer the primary window (associated with scanout 0).463let hwnd = match self.gui_window_handles.get(scanout_id as usize) {464Some(hwnd) => *hwnd,465None => {466error!("Invalid scanout id {}!", scanout_id);467return false;468}469};470let window_resources = match self.vacant_gui_windows.remove(&hwnd) {471Some(resources) => resources,472None => {473error!(474"GUI window associated with scanout {} is not vacant!",475scanout_id476);477return false;478}479};480let surface_resources = SurfaceResources {481display_event_dispatcher: self.display_event_dispatcher.clone(),482gpu_main_display_tube: self.gpu_main_display_tube.clone(),483};484// SAFETY:485// Safe because the dispatcher will take care of the lifetime of the window.486match unsafe {487WindowMessageProcessor::new(create_surface_func, surface_resources, window_resources)488} {489Ok(processor) => {490self.in_use_gui_windows.insert(hwnd, processor);491self.in_use_gui_windows492.get_mut(&hwnd)493.expect("It is just inserted")494.process_general_message(495GeneralMessage::MessageDispatcherAttached,496&self.keyboard_input_manager,497);498true499}500Err(e) => {501error!("Failed to create surface: {:?}", e);502false503}504}505}506507fn release_surface(&mut self, surface_id: u32) {508match self509.in_use_gui_windows510.iter()511.find(|(_, processor)| processor.surface_id() == surface_id)512{513Some(iter) => {514self.try_release_surface_and_dissociate_gui_window(*iter.0);515}516None => error!(517"Can't release surface {} because there is no window associated with it!",518surface_id519),520}521}522523/// Returns true if `hwnd` points to an in-use GUI window.524fn try_release_surface_and_dissociate_gui_window(&mut self, hwnd: HWND) -> bool {525if let Some(processor) = self.in_use_gui_windows.remove(&hwnd) {526// SAFETY:527// Safe because the dispatcher will take care of the lifetime of the window.528self.vacant_gui_windows.insert(hwnd, unsafe {529processor.release_surface_and_take_window_resources()530});531return true;532}533false534}535536/// # Safety537/// The caller is responsible for keeping the pointer valid until it is removed from the window.538unsafe fn store_pointer_in_window(539pointer: *const Self,540window: &dyn BasicWindow,541) -> Result<()> {542window543.set_property(DISPATCHER_PROPERTY_NAME, pointer as *mut c_void)544.context("When storing message dispatcher pointer")545}546547/// Returns Some if this is a GUI window lifetime related message and if we have handled it.548fn handle_gui_window_lifetime_message(549&mut self,550hwnd: HWND,551packet: &MessagePacket,552) -> Option<LRESULT> {553// Windows calls WndProc as a subroutine when we call `DestroyWindow()`. So, when handling554// WM_DESTROY/WM_NCDESTROY for one window, we would avoid calling `DestroyWindow()` on555// another window to avoid recursively mutably borrowing self. Instead, we do556// `DestroyWindow()` and clean up all associated resources on WM_CLOSE. The long-term fix is557// tracked by b/314379499.558match packet.msg {559WM_CLOSE => {560// If the window is in-use, return it to the vacant window pool.561// TODO(b/314309389): This only frees the `Surface` in WndProc thread, while the562// corresponding guest display isn't unplugged. We might need to figure out a way to563// inform `gpu_control_tube` to remove that display.564if self.try_release_surface_and_dissociate_gui_window(hwnd) {565// If the service isn't connnected (e.g. when debugging the emulator alone), we566// would request shutdown if no window is in-use anymore.567if self.gpu_main_display_tube.is_none() && self.in_use_gui_windows.is_empty() {568self.request_shutdown_gpu_display();569}570return Some(0);571}572}573// Don't use any reference to `self` when handling WM_DESTROY/WM_NCDESTROY, since it is574// likely already mutably borrowed when handling WM_CLOSE on the same stack.575WM_NCDESTROY => {576info!("Window {:p} destroyed", hwnd);577// We don't care if removing the dispatcher pointer succeeds, since this window will578// be completely gone right after this function returns.579let property = win32_wide_string(DISPATCHER_PROPERTY_NAME);580// SAFETY:581// Safe because `hwnd` is valid, and `property` lives longer than the function call.582unsafe { RemovePropW(hwnd, property.as_ptr()) };583return Some(0);584}585_ => (),586}587None588}589590/// Signals GpuDisplay to close. This is not going to release any resources right away, but the591/// closure of GpuDisplay will trigger shutting down the entire VM, and all resources will be592/// released by then.593fn request_shutdown_gpu_display(&self) {594if let Err(e) = self.close_requested_event.signal() {595error!("Failed to signal close requested event: {}", e);596}597}598599/// Destroys all GUI windows and the message router window, and requests exiting message loop.600fn shutdown(&mut self) {601info!("Shutting down all windows and message loop");602603// Destroy all GUI windows.604// Note that Windows calls WndProc as a subroutine when we call `DestroyWindow()`, we have605// to store window handles in a Vec and query the hashmaps every time rather than simply606// iterating through the hashmaps.607let in_use_handles: Vec<HWND> = self.in_use_gui_windows.keys().cloned().collect();608for hwnd in in_use_handles.iter() {609if let Some(processor) = self.in_use_gui_windows.remove(hwnd) {610// SAFETY:611// Safe because we are dropping the `WindowResources` before the window is gone.612let resources = unsafe { processor.release_surface_and_take_window_resources() };613if let Err(e) = resources.window().destroy() {614error!("Failed to destroy in-use GUI window: {:?}", e);615}616}617}618619let vacant_handles: Vec<HWND> = self.vacant_gui_windows.keys().cloned().collect();620for hwnd in vacant_handles.iter() {621if let Some(resources) = self.vacant_gui_windows.remove(hwnd) {622if let Err(e) = resources.window().destroy() {623error!("Failed to destroy vacant GUI window: {:?}", e);624}625}626}627628// Destroy the message router window.629if let Some(window) = self.message_router_window.take() {630if let Err(e) = window.destroy() {631error!("Failed to destroy thread message router: {:?}", e);632}633}634635// Exit the message loop.636//637// SAFETY:638// Safe because this function takes in no memory managed by us, and it always succeeds.639unsafe {640PostQuitMessage(0);641}642}643}644645646