blob: 11284b570d504b85db5c325421f2573ae0c4c0ad [file] [log] [blame]
// Copyright 2022 The ChromiumOS Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
use std::cell::RefCell;
use std::collections::BTreeMap;
use std::marker::PhantomPinned;
use std::os::raw::c_void;
use std::pin::Pin;
use std::ptr::null_mut;
use std::rc::Rc;
use anyhow::bail;
use anyhow::Context;
use anyhow::Result;
use base::debug;
use base::error;
use base::info;
use base::Tube;
use linux_input_sys::virtio_input_event;
#[cfg(feature = "kiwi")]
use vm_control::ServiceSendToGpu;
use winapi::shared::minwindef::LRESULT;
use winapi::shared::windef::HWND;
use winapi::um::winuser::*;
use super::window::MessagePacket;
use super::window::Window;
use super::window_message_processor::*;
use super::ObjectId;
use crate::EventDevice;
use crate::EventDeviceKind;
/// The pointer to dispatcher will be stored with HWND using `SetPropW()` with the following name.
pub(crate) const DISPATCHER_PROPERTY_NAME: &str = "PROP_WND_MSG_DISPATCHER";
/// This class is used to dispatch display events to the guest device.
#[derive(Clone)]
pub struct DisplayEventDispatcher {
event_devices: Rc<RefCell<BTreeMap<ObjectId, EventDevice>>>,
}
impl DisplayEventDispatcher {
pub fn new() -> Self {
Self {
event_devices: Default::default(),
}
}
pub fn dispatch(&self, events: &[virtio_input_event], device_type: EventDeviceKind) {
for event_device in self
.event_devices
.borrow_mut()
.values_mut()
.filter(|event_device| event_device.kind() == device_type)
{
if let Err(e) = event_device.send_report(events.iter().cloned()) {
error!("Failed to send events to event device: {}", e);
}
}
}
fn import_event_device(&mut self, event_device_id: ObjectId, event_device: EventDevice) {
info!("Importing {:?} (ID: {:?})", event_device, event_device_id);
self.event_devices
.borrow_mut()
.insert(event_device_id, event_device);
}
}
impl Default for DisplayEventDispatcher {
fn default() -> Self {
Self::new()
}
}
/// This class is used for dispatching thread and window messages. It should be created before any
/// other threads start to post messages to the WndProc thread, and before the WndProc thread enters
/// the window message loop. Once all windows tracked by it are destroyed, it will signal exiting
/// the message loop and then it can be dropped.
pub(crate) struct WindowMessageDispatcher<T: HandleWindowMessage> {
message_processor: Option<WindowMessageProcessor<T>>,
display_event_dispatcher: DisplayEventDispatcher,
gpu_main_display_tube: Option<Rc<Tube>>,
// The dispatcher is pinned so that its address in the memory won't change, and it is always
// safe to use the pointer to it stored in the window.
_pinned_marker: PhantomPinned,
}
impl<T: HandleWindowMessage> WindowMessageDispatcher<T> {
/// This function should only be called once from the WndProc thread. It will take the ownership
/// of the `Window` object, and drop it before the underlying window is completely gone.
/// TODO(b/238680252): This should be good enough for supporting multi-windowing, but we should
/// revisit it if we also want to manage some child windows of the crosvm window.
pub fn create(
window: Window,
gpu_main_display_tube: Option<Rc<Tube>>,
) -> Result<Pin<Box<Self>>> {
let mut dispatcher = Box::pin(Self {
message_processor: Default::default(),
display_event_dispatcher: DisplayEventDispatcher::new(),
gpu_main_display_tube,
_pinned_marker: PhantomPinned,
});
dispatcher
.as_mut()
.create_message_processor(window)
.context("When creating WindowMessageDispatcher")?;
Ok(dispatcher)
}
#[cfg(feature = "kiwi")]
pub fn process_service_message(self: Pin<&mut Self>, message: &ServiceSendToGpu) {
// Safe because we won't move the dispatcher out of the returned mutable reference.
match unsafe { self.get_unchecked_mut().message_processor.as_mut() } {
Some(processor) => processor.handle_service_message(message),
None => error!("Cannot handle service message because there is no message processor!"),
}
}
pub fn process_thread_message(self: Pin<&mut Self>, packet: &MessagePacket) {
// Safe because we won't move the dispatcher out of the returned mutable reference.
unsafe {
self.get_unchecked_mut()
.process_thread_message_internal(packet);
}
}
/// Returns `Some` if the message is processed by the targeted window.
pub fn dispatch_window_message(
&mut self,
hwnd: HWND,
packet: &MessagePacket,
) -> Option<LRESULT> {
if let Some(processor) = &mut self.message_processor {
if processor.window().is_same_window(hwnd) {
let ret = processor.process_message(packet);
// `WM_NCDESTROY` is sent at the end of the window destruction, and is the last
// message the window will receive. Drop the message processor so that associated
// resources can be cleaned up before the window is completely gone.
if packet.msg == WM_NCDESTROY {
if let Err(e) = Self::remove_pointer_from_window(processor.window()) {
error!("{:?}", e);
}
self.message_processor = None;
Self::request_exit_message_loop();
}
return Some(ret);
}
}
None
}
fn create_message_processor(self: Pin<&mut Self>, window: Window) -> Result<()> {
if !window.is_valid() {
bail!("Window handle is invalid!");
}
Self::store_pointer_in_window(&*self, &window)?;
// Safe because we won't move the dispatcher out of it, and the dispatcher is aware of the
// lifecycle of the window.
unsafe {
self.get_unchecked_mut()
.message_processor
.replace(WindowMessageProcessor::<T>::new(window));
}
Ok(())
}
fn process_thread_message_internal(&mut self, packet: &MessagePacket) {
let MessagePacket {
msg,
w_param,
l_param,
} = *packet;
match msg {
WM_USER_HANDLE_DISPLAY_MESSAGE_INTERNAL => {
// Safe because the sender gives up the ownership and expects the receiver to
// destruct the message.
let message = unsafe { Box::from_raw(l_param as *mut DisplaySendToWndProc<T>) };
self.handle_display_message(*message);
}
WM_USER_WNDPROC_THREAD_DROP_KILL_WINDOW_INTERNAL => match &self.message_processor {
Some(processor) => {
debug!("Destroying window on WndProc thread drop");
if let Err(e) = processor.window().destroy() {
error!(
"Failed to destroy window when dropping WndProc thread: {:?}",
e
);
}
}
None => debug!("No window to destroy on WndProc thread drop"),
},
// Safe because we are processing a message targeting this thread.
_ => unsafe {
DefWindowProcW(null_mut(), msg, w_param, l_param);
},
}
}
fn handle_display_message(&mut self, message: DisplaySendToWndProc<T>) {
match message {
DisplaySendToWndProc::CreateSurface { function, callback } => {
callback(self.create_message_handler(function));
}
DisplaySendToWndProc::ImportEventDevice {
event_device_id,
event_device,
} => {
self.display_event_dispatcher
.import_event_device(event_device_id, event_device);
}
}
}
/// Returns true if the window message handler is created successfully.
fn create_message_handler(
&mut self,
create_handler_func: CreateMessageHandlerFunction<T>,
) -> bool {
match &mut self.message_processor {
Some(processor) => {
let resources = MessageHandlerResources {
display_event_dispatcher: self.display_event_dispatcher.clone(),
gpu_main_display_tube: self.gpu_main_display_tube.clone(),
};
match processor.create_message_handler(create_handler_func, resources) {
Ok(_) => return true,
Err(e) => error!("Failed to create message handler: {:?}", e),
}
}
None => {
error!("Cannot create message handler because there is no message processor!")
}
}
false
}
fn store_pointer_in_window(pointer: *const Self, window: &Window) -> Result<()> {
window
.set_property(DISPATCHER_PROPERTY_NAME, pointer as *mut c_void)
.context("When storing message dispatcher pointer")
}
/// When the window is being destroyed, we must remove all entries added to the property list
/// before `WM_NCDESTROY` returns.
fn remove_pointer_from_window(window: &Window) -> Result<()> {
window
.remove_property(DISPATCHER_PROPERTY_NAME)
.context("When removing message dispatcher pointer")
}
fn request_exit_message_loop() {
info!("Posting WM_QUIT");
// Safe because it will always succeed.
unsafe {
PostQuitMessage(0);
}
}
}