Path: blob/main/gpu_display/src/gpu_display_win/surface.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::ops::ControlFlow;5use std::ops::Deref;6#[cfg(feature = "gfxstream_display")]7use std::os::raw::c_int;8#[cfg(feature = "gfxstream_display")]9use std::os::raw::c_void;10use std::rc::Rc;11use std::sync::Arc;12use std::sync::Weak;13use std::time::Instant;1415use anyhow::Context;16use anyhow::Result;17use base::error;18use base::info;19use base::warn;20use base::Tube;21use euclid::size2;22use euclid::Box2D;23use euclid::Size2D;24use metrics::sys::windows::Metrics;25use sync::Mutex;26use vm_control::gpu::DisplayMode;27use vm_control::gpu::DisplayParameters;28use win_util::keys_down;29use winapi::shared::minwindef::HIWORD;30use winapi::shared::minwindef::LOWORD;31use winapi::shared::minwindef::LPARAM;32use winapi::shared::minwindef::LRESULT;33use winapi::shared::minwindef::TRUE;34use winapi::shared::minwindef::WPARAM;35use winapi::um::winuser::VK_F4;36use winapi::um::winuser::VK_MENU;37use winapi::um::winuser::WM_CLOSE;3839use super::keyboard_input_manager::KeyboardInputManager;40use super::math_util::Size2DCheckedCast;41use super::mouse_input_manager::MouseInputManager;42use super::virtual_display_manager::NoopVirtualDisplayManager as VirtualDisplayManager;43#[cfg(feature = "gfxstream_display")]44use super::window::BasicWindow;45use super::window::GuiWindow;46use super::window_manager::NoopWindowManager as WindowManager;47use super::window_message_processor::GeneralMessage;48use super::window_message_processor::SurfaceResources;49use super::window_message_processor::WindowMessage;50use super::window_message_processor::WindowPosMessage;51use super::window_message_processor::HANDLE_WINDOW_MESSAGE_TIMEOUT;52use super::HostDisplayWrapper;53use super::HostWindowSpace;54use super::MouseMode;55use crate::EventDeviceKind;5657#[cfg(feature = "gfxstream_display")]58#[link(name = "gfxstream_backend")]59extern "C" {60fn gfxstream_backend_setup_window(61hwnd: *const c_void,62window_x: c_int,63window_y: c_int,64window_width: c_int,65window_height: c_int,66fb_width: c_int,67fb_height: c_int,68);69}7071// Updates the rectangle in the window's client area to which gfxstream renders.72fn update_virtual_display_projection(73#[allow(unused)] host_display: impl Deref<Target = HostDisplayWrapper>,74#[allow(unused)] window: &GuiWindow,75#[allow(unused)] projection_box: &Box2D<i32, HostWindowSpace>,76) {77#[cfg(feature = "vulkan_display")]78if let HostDisplayWrapper::Initialized(ref host_display) = *host_display {79if let Err(err) = host_display80.move_window(&projection_box.cast_unit())81.with_context(|| "move the subwindow")82{83error!("{:?}", err);84}85#[cfg(feature = "gfxstream_display")]86return;87}8889// Safe because `Window` object won't outlive the HWND.90#[cfg(feature = "gfxstream_display")]91unsafe {92gfxstream_backend_setup_window(93window.handle() as *const c_void,94projection_box.min.x,95projection_box.min.y,96projection_box.width(),97projection_box.height(),98projection_box.width(),99projection_box.height(),100);101}102}103104#[allow(dead_code)]105#[derive(Clone)]106pub(crate) struct DisplayProperties {107pub start_hidden: bool,108pub is_fullscreen: bool,109pub window_width: u32,110pub window_height: u32,111}112113impl From<&DisplayParameters> for DisplayProperties {114fn from(params: &DisplayParameters) -> Self {115let is_fullscreen = matches!(params.mode, DisplayMode::BorderlessFullScreen(_));116let (window_width, window_height) = params.get_window_size();117118Self {119start_hidden: params.hidden,120is_fullscreen,121window_width,122window_height,123}124}125}126127pub struct Surface {128surface_id: u32,129mouse_input: MouseInputManager,130window_manager: WindowManager,131virtual_display_manager: VirtualDisplayManager,132#[allow(dead_code)]133gpu_main_display_tube: Option<Rc<Tube>>,134host_display: Arc<Mutex<HostDisplayWrapper>>,135}136137impl Surface {138pub fn new(139surface_id: u32,140window: &GuiWindow,141_metrics: Option<Weak<Metrics>>,142display_params: &DisplayParameters,143resources: SurfaceResources,144host_display: Arc<Mutex<HostDisplayWrapper>>,145) -> Result<Self> {146static CONTEXT_MESSAGE: &str = "When creating Surface";147info!(148"Creating surface {} to associate with scanout {}",149surface_id,150window.scanout_id()151);152153let initial_host_viewport_size = window.get_client_rect().context(CONTEXT_MESSAGE)?.size;154let virtual_display_size = {155let (width, height) = display_params.get_virtual_display_size();156size2(width, height).checked_cast()157};158let virtual_display_manager =159VirtualDisplayManager::new(&initial_host_viewport_size, &virtual_display_size);160// This will make gfxstream initialize the child window to which it will render.161update_virtual_display_projection(162host_display.lock(),163window,164&virtual_display_manager.get_virtual_display_projection_box(),165);166167let SurfaceResources {168display_event_dispatcher,169gpu_main_display_tube,170} = resources;171172let mouse_input = MouseInputManager::new(173window,174*virtual_display_manager.get_host_to_guest_transform(),175virtual_display_size.checked_cast(),176display_event_dispatcher,177);178179Ok(Surface {180surface_id,181mouse_input,182window_manager: WindowManager::new(183window,184&display_params.into(),185initial_host_viewport_size,186gpu_main_display_tube.clone(),187)188.context(CONTEXT_MESSAGE)?,189virtual_display_manager,190gpu_main_display_tube,191host_display,192})193}194195pub fn surface_id(&self) -> u32 {196self.surface_id197}198199fn handle_key_event(200&mut self,201window: &GuiWindow,202_key_down: bool,203w_param: WPARAM,204_l_param: LPARAM,205) {206// Since we handle WM_SYSKEYDOWN we have to handle Alt-F4 ourselves.207if (w_param == VK_MENU as usize || w_param == VK_F4 as usize)208&& keys_down(&[VK_MENU, VK_F4])209{210info!("Got alt-F4 w_param={}, posting WM_CLOSE", w_param);211if let Err(e) =212window.post_message(WM_CLOSE, /* w_param */ 0, /* l_param */ 0)213{214error!("Failed to post WM_CLOSE: {:?}", e);215}216}217}218219fn set_mouse_mode(&mut self, window: &GuiWindow, mouse_mode: MouseMode) {220self.mouse_input221.handle_change_mouse_mode_request(window, mouse_mode);222}223224fn update_host_viewport_size(225&mut self,226window: &GuiWindow,227host_viewport_size: &Size2D<i32, HostWindowSpace>,228) {229info!("Updating host viewport size to {:?}", host_viewport_size);230let start = Instant::now();231232self.virtual_display_manager233.update_host_guest_transforms(host_viewport_size);234let virtual_display_projection_box = self235.virtual_display_manager236.get_virtual_display_projection_box();237update_virtual_display_projection(238self.host_display.lock(),239window,240&virtual_display_projection_box,241);242self.mouse_input.update_host_to_guest_transform(243*self.virtual_display_manager.get_host_to_guest_transform(),244);245246let elapsed = start.elapsed();247let elapsed_millis = elapsed.as_millis();248if elapsed < HANDLE_WINDOW_MESSAGE_TIMEOUT {249info!(250"Finished updating host viewport size in {}ms",251elapsed_millis252);253} else {254warn!(255"Window might have been hung since updating host viewport size took \256too long ({}ms)!",257elapsed_millis258);259}260}261262/// Called once when it is safe to assume all future messages targeting `window` will be263/// dispatched to this `Surface`.264fn on_message_dispatcher_attached(&mut self, window: &GuiWindow) {265// `WindowManager` relies on window messages to properly set initial window pos.266// We might see a suboptimal UI if any error occurs here, such as having black bars. Instead267// of crashing the emulator, we would just log the error and still allow the user to268// experience the app.269if let Err(e) = self.window_manager.set_initial_window_pos(window) {270error!("Failed to set initial window pos: {:#?}", e);271}272}273274/// Called whenever any window message is retrieved. Returns None if `DefWindowProcW()` should275/// be called after our processing.276#[inline]277pub fn handle_window_message(278&mut self,279window: &GuiWindow,280message: WindowMessage,281) -> Option<LRESULT> {282if let ControlFlow::Break(ret) = self.mouse_input.handle_window_message(window, &message) {283return ret;284}285286// Just return 0 for most of the messages we processed.287let mut ret: Option<LRESULT> = Some(0);288match message {289WindowMessage::Key {290is_sys_key: _,291is_down,292w_param,293l_param,294} => self.handle_key_event(window, is_down, w_param, l_param),295WindowMessage::WindowPos(window_pos_msg) => {296ret = self.handle_window_pos_message(window, window_pos_msg)297}298WindowMessage::DisplayChange => self.window_manager.handle_display_change(window),299WindowMessage::HostViewportChange { l_param } => {300self.on_host_viewport_change(window, l_param)301}302// The following messages are handled by other modules.303WindowMessage::WindowActivate { .. }304| WindowMessage::Mouse(_)305| WindowMessage::KeyboardFocus => (),306WindowMessage::Other(..) => {307// Request default processing for messages that we don't explicitly handle.308ret = None;309}310}311ret312}313314#[inline]315pub fn handle_general_message(316&mut self,317window: &GuiWindow,318message: &GeneralMessage,319keyboard_input_manager: &KeyboardInputManager,320) {321match message {322GeneralMessage::MessageDispatcherAttached => {323self.on_message_dispatcher_attached(window)324}325GeneralMessage::RawInputEvent(raw_input) => {326self.mouse_input.handle_raw_input_event(window, *raw_input)327}328GeneralMessage::GuestEvent {329event_device_kind,330event,331} => {332if let EventDeviceKind::Keyboard = event_device_kind {333keyboard_input_manager.handle_guest_event(window, *event);334}335}336GeneralMessage::SetMouseMode(mode) => self.set_mouse_mode(window, *mode),337}338}339340/// Returns None if `DefWindowProcW()` should be called after our processing.341#[inline]342fn handle_window_pos_message(343&mut self,344window: &GuiWindow,345message: WindowPosMessage,346) -> Option<LRESULT> {347self.window_manager348.handle_window_pos_message(window, &message);349match message {350WindowPosMessage::WindowPosChanged { .. } => {351// Request default processing, otherwise `WM_SIZE` and `WM_MOVE` won't be sent.352// https://learn.microsoft.com/en-us/windows/win32/winmsg/wm-windowposchanged#remarks353return None;354}355// "An application should return TRUE if it processes this message."356WindowPosMessage::WindowSizeChanging { .. } => return Some(TRUE as LRESULT),357_ => (),358}359Some(0)360}361362#[inline]363fn on_host_viewport_change(&mut self, window: &GuiWindow, l_param: LPARAM) {364let new_size = size2(LOWORD(l_param as u32) as i32, HIWORD(l_param as u32) as i32);365self.update_host_viewport_size(window, &new_size);366}367}368369370