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