Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
google
GitHub Repository: google/crosvm
Path: blob/main/gpu_display/src/gpu_display_win/surface.rs
5394 views
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_display")]
8
use std::os::raw::c_int;
9
#[cfg(feature = "gfxstream_display")]
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_display")]
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::HostDisplayWrapper;
54
use super::HostWindowSpace;
55
use super::MouseMode;
56
use crate::EventDeviceKind;
57
58
#[cfg(feature = "gfxstream_display")]
59
#[link(name = "gfxstream_backend")]
60
extern "C" {
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.
73
fn update_virtual_display_projection(
74
#[allow(unused)] host_display: impl Deref<Target = HostDisplayWrapper>,
75
#[allow(unused)] window: &GuiWindow,
76
#[allow(unused)] projection_box: &Box2D<i32, HostWindowSpace>,
77
) {
78
#[cfg(feature = "vulkan_display")]
79
if let HostDisplayWrapper::Initialized(ref host_display) = *host_display {
80
if let Err(err) = host_display
81
.move_window(&projection_box.cast_unit())
82
.with_context(|| "move the subwindow")
83
{
84
error!("{:?}", err);
85
}
86
#[cfg(feature = "gfxstream_display")]
87
return;
88
}
89
90
// Safe because `Window` object won't outlive the HWND.
91
#[cfg(feature = "gfxstream_display")]
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 {
115
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
host_display: Arc<Mutex<HostDisplayWrapper>>,
136
}
137
138
impl Surface {
139
pub fn new(
140
surface_id: u32,
141
window: &GuiWindow,
142
_metrics: Option<Weak<Metrics>>,
143
display_params: &DisplayParameters,
144
resources: SurfaceResources,
145
host_display: Arc<Mutex<HostDisplayWrapper>>,
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
host_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
host_display,
193
})
194
}
195
196
pub fn surface_id(&self) -> u32 {
197
self.surface_id
198
}
199
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
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
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.host_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`.
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]
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]
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]
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]
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
370