Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
google
GitHub Repository: google/crosvm
Path: blob/main/gpu_display/src/gpu_display_win/window_procedure_thread.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::any::type_name;
6
use std::any::TypeId;
7
use std::collections::btree_map::Entry;
8
use std::collections::BTreeMap;
9
use std::collections::HashMap;
10
use std::mem;
11
use std::os::windows::io::RawHandle;
12
use std::pin::Pin;
13
use std::ptr::null_mut;
14
use std::rc::Rc;
15
use std::sync::atomic::AtomicI32;
16
use std::sync::atomic::Ordering;
17
use std::sync::mpsc::channel;
18
use std::sync::mpsc::Sender;
19
use std::sync::Arc;
20
use std::thread::Builder as ThreadBuilder;
21
use std::thread::JoinHandle;
22
23
use anyhow::anyhow;
24
use anyhow::bail;
25
use anyhow::Context;
26
use anyhow::Result;
27
use base::error;
28
use base::info;
29
use base::warn;
30
use base::AsRawDescriptor;
31
use base::Event;
32
use base::ReadNotifier;
33
use base::Tube;
34
use euclid::size2;
35
use serde::Deserialize;
36
use serde::Serialize;
37
use sync::Mutex;
38
#[cfg(feature = "kiwi")]
39
use vm_control::ServiceSendToGpu;
40
use win_util::syscall_bail;
41
use win_util::win32_wide_string;
42
use winapi::shared::minwindef::DWORD;
43
use winapi::shared::minwindef::FALSE;
44
use winapi::shared::minwindef::LPARAM;
45
use winapi::shared::minwindef::LRESULT;
46
use winapi::shared::minwindef::UINT;
47
use winapi::shared::minwindef::WPARAM;
48
use winapi::shared::windef::HWND;
49
use winapi::um::winbase::INFINITE;
50
use winapi::um::winbase::WAIT_OBJECT_0;
51
use winapi::um::winnt::MAXIMUM_WAIT_OBJECTS;
52
use winapi::um::winuser::*;
53
54
use super::window::get_current_module_handle;
55
use super::window::GuiWindow;
56
use super::window::MessageOnlyWindow;
57
use super::window::MessagePacket;
58
use super::window_message_dispatcher::WindowMessageDispatcher;
59
use super::window_message_dispatcher::DISPATCHER_PROPERTY_NAME;
60
use super::window_message_processor::*;
61
62
// The default app icon id, which is defined in crosvm-manifest.rc.
63
const APP_ICON_ID: u16 = 1;
64
65
#[derive(Debug)]
66
enum MessageLoopState {
67
/// The initial state.
68
NotStarted = 0,
69
/// The loop is running normally.
70
Running,
71
/// The loop has ended normally.
72
ExitedNormally,
73
/// The loop never started because errors occurred.
74
EarlyTerminatedWithError,
75
/// The loop has ended because errors occurred.
76
ExitedWithError,
77
}
78
79
#[derive(Copy, Clone, PartialEq)]
80
enum Token {
81
MessagePump,
82
ServiceMessage,
83
}
84
85
/// A context that can wait on both the thread-specific message queue and the given handles.
86
struct MsgWaitContext {
87
triggers: HashMap<RawHandle, Token>,
88
raw_handles: Vec<RawHandle>,
89
}
90
91
impl MsgWaitContext {
92
pub fn new() -> Self {
93
Self {
94
triggers: HashMap::new(),
95
raw_handles: Vec::new(),
96
}
97
}
98
99
/// Note that since there is no handle associated with `Token::MessagePump`, this token will be
100
/// used internally by `MsgWaitContext` and the caller should never use it.
101
pub fn add(&mut self, handle: &dyn AsRawDescriptor, token: Token) -> Result<()> {
102
if token == Token::MessagePump {
103
bail!("Token::MessagePump is reserved!");
104
}
105
if self.raw_handles.len() == MAXIMUM_WAIT_OBJECTS as usize {
106
bail!("Number of raw handles exceeding MAXIMUM_WAIT_OBJECTS!");
107
}
108
109
let raw_descriptor = handle.as_raw_descriptor();
110
if self.triggers.contains_key(&raw_descriptor) {
111
bail!("The handle has already been registered in MsgWaitContext!")
112
}
113
114
self.triggers.insert(raw_descriptor, token);
115
self.raw_handles.push(raw_descriptor);
116
Ok(())
117
}
118
119
/// Blocks the thread until there is any new message available on the message queue, or if any
120
/// of the given handles is signaled, and returns the associated token.
121
///
122
/// If multiple handles are signaled, this will return the token associated with the one that
123
/// was first added to this context.
124
///
125
/// # Safety
126
///
127
/// Caller is responsible for ensuring that the handles are still valid.
128
pub unsafe fn wait(&self) -> Result<Token> {
129
let num_handles = self.raw_handles.len();
130
// Safe because the caller is required to guarantee that the handles are valid, and failures
131
// are handled below.
132
let result = MsgWaitForMultipleObjects(
133
num_handles as DWORD,
134
self.raw_handles.as_ptr(),
135
/* fWaitAll= */ FALSE,
136
INFINITE,
137
QS_ALLINPUT,
138
);
139
match (result - WAIT_OBJECT_0) as usize {
140
// At least one of the handles has been signaled.
141
index if index < num_handles => Ok(self.triggers[&self.raw_handles[index]]),
142
// At least one message is available at the message queue.
143
index if index == num_handles => Ok(Token::MessagePump),
144
// Invalid cases. This is most likely a `WAIT_FAILED`, but anything not matched by the
145
// above is an error case.
146
_ => syscall_bail!(format!(
147
"MsgWaitForMultipleObjects() unexpectedly returned {}",
148
result
149
)),
150
}
151
}
152
}
153
154
trait RegisterWindowClass: 'static {
155
// Only for debug purpose. Not required to be unique across different implementors.
156
const CLASS_NAME_PREFIX: &'static str = "";
157
fn register_window_class(class_name: &str, wnd_proc: WNDPROC) -> Result<()>;
158
}
159
160
impl RegisterWindowClass for GuiWindow {
161
const CLASS_NAME_PREFIX: &'static str = "CROSVM";
162
163
fn register_window_class(class_name: &str, wnd_proc: WNDPROC) -> Result<()> {
164
let hinstance = get_current_module_handle();
165
// If we fail to load any UI element below, use NULL to let the system use the default UI
166
// rather than crash.
167
let hicon = Self::load_custom_icon(hinstance, APP_ICON_ID).unwrap_or(null_mut());
168
let hcursor = Self::load_system_cursor(IDC_ARROW).unwrap_or(null_mut());
169
let hbrush_background = Self::create_opaque_black_brush().unwrap_or(null_mut());
170
let class_name = win32_wide_string(class_name);
171
let window_class = WNDCLASSEXW {
172
cbSize: std::mem::size_of::<WNDCLASSEXW>() as u32,
173
style: CS_OWNDC | CS_HREDRAW | CS_VREDRAW,
174
lpfnWndProc: wnd_proc,
175
cbClsExtra: 0,
176
cbWndExtra: 0,
177
hInstance: hinstance,
178
hIcon: hicon,
179
hCursor: hcursor,
180
hbrBackground: hbrush_background,
181
lpszMenuName: null_mut(),
182
lpszClassName: class_name.as_ptr(),
183
hIconSm: hicon,
184
};
185
186
// SAFETY:
187
// Safe because we know the lifetime of `window_class`, and we handle failures below.
188
if unsafe { RegisterClassExW(&window_class) } == 0 {
189
syscall_bail!("Failed to call RegisterClassExW()");
190
}
191
Ok(())
192
}
193
}
194
195
impl RegisterWindowClass for MessageOnlyWindow {
196
const CLASS_NAME_PREFIX: &'static str = "THREAD_MESSAGE_ROUTER";
197
198
fn register_window_class(class_name: &str, wnd_proc: WNDPROC) -> Result<()> {
199
let hinstance = get_current_module_handle();
200
let class_name = win32_wide_string(class_name);
201
let window_class = WNDCLASSEXW {
202
cbSize: std::mem::size_of::<WNDCLASSEXW>() as u32,
203
style: 0,
204
lpfnWndProc: wnd_proc,
205
cbClsExtra: 0,
206
cbWndExtra: 0,
207
hInstance: hinstance,
208
hIcon: null_mut(),
209
hCursor: null_mut(),
210
hbrBackground: null_mut(),
211
lpszMenuName: null_mut(),
212
lpszClassName: class_name.as_ptr(),
213
hIconSm: null_mut(),
214
};
215
216
// SAFETY:
217
// Safe because we know the lifetime of `window_class`, and we handle failures below.
218
if unsafe { RegisterClassExW(&window_class) } == 0 {
219
syscall_bail!("Failed to call RegisterClassExW()");
220
}
221
Ok(())
222
}
223
}
224
225
/// This class runs the WndProc thread, and provides helper functions for other threads to
226
/// communicate with it.
227
pub struct WindowProcedureThread {
228
thread: Option<JoinHandle<()>>,
229
message_router_handle: HWND,
230
message_loop_state: Option<Arc<AtomicI32>>,
231
close_requested_event: Event,
232
}
233
234
impl WindowProcedureThread {
235
pub fn builder() -> WindowProcedureThreadBuilder {
236
// We don't implement Default for WindowProcedureThreadBuilder so that the builder function
237
// is the only way to create WindowProcedureThreadBuilder.
238
WindowProcedureThreadBuilder {
239
max_num_windows: 1,
240
display_tube: None,
241
#[cfg(feature = "kiwi")]
242
ime_tube: None,
243
}
244
}
245
246
fn start_thread(max_num_windows: u32, gpu_main_display_tube: Option<Tube>) -> Result<Self> {
247
let (message_router_handle_sender, message_router_handle_receiver) = channel();
248
let message_loop_state = Arc::new(AtomicI32::new(MessageLoopState::NotStarted as i32));
249
let close_requested_event = Event::new().unwrap();
250
251
let message_loop_state_clone = Arc::clone(&message_loop_state);
252
let close_requested_event_clone = close_requested_event
253
.try_clone()
254
.map_err(|e| anyhow!("Failed to clone close_requested_event: {}", e))?;
255
256
let thread = ThreadBuilder::new()
257
.name("gpu_display_wndproc".into())
258
.spawn(move || {
259
match close_requested_event_clone.try_clone() {
260
Ok(close_requested_event) => Self::run_message_loop(
261
max_num_windows,
262
message_router_handle_sender,
263
message_loop_state_clone,
264
gpu_main_display_tube,
265
close_requested_event,
266
),
267
Err(e) => error!("Failed to clone close_requested_event: {}", e),
268
}
269
// The close requested event should have been signaled at this point, unless we hit
270
// some edge cases, e.g. the WndProc thread terminates unexpectedly during startup.
271
// We want to make sure it is signaled in all cases.
272
if let Err(e) = close_requested_event_clone.signal() {
273
error!("Failed to signal close requested event: {}", e);
274
}
275
})
276
.context("Failed to spawn WndProc thread")?;
277
278
match message_router_handle_receiver.recv() {
279
Ok(message_router_handle_res) => match message_router_handle_res {
280
Ok(message_router_handle) => Ok(Self {
281
thread: Some(thread),
282
message_router_handle: message_router_handle as HWND,
283
message_loop_state: Some(message_loop_state),
284
close_requested_event,
285
}),
286
Err(e) => bail!("WndProc internal failure: {:?}", e),
287
},
288
Err(e) => bail!("Failed to receive WndProc thread ID: {}", e),
289
}
290
}
291
292
pub fn try_clone_close_requested_event(&self) -> Result<Event> {
293
self.close_requested_event
294
.try_clone()
295
.map_err(|e| anyhow!("Failed to clone close_requested_event: {}", e))
296
}
297
298
pub fn post_display_command(&self, message: DisplaySendToWndProc) -> Result<()> {
299
self.post_message_to_thread_carrying_object(
300
WM_USER_HANDLE_DISPLAY_MESSAGE_INTERNAL,
301
message,
302
)
303
.context("When posting DisplaySendToWndProc message")
304
}
305
306
/// Calls `PostMessageW()` internally.
307
fn post_message_to_thread(&self, msg: UINT, w_param: WPARAM, l_param: LPARAM) -> Result<()> {
308
if !self.is_message_loop_running() {
309
bail!("Cannot post message to WndProc thread because message loop is not running!");
310
}
311
// SAFETY:
312
// Safe because the message loop is still running.
313
if unsafe { PostMessageW(self.message_router_handle, msg, w_param, l_param) } == 0 {
314
syscall_bail!("Failed to call PostMessageW()");
315
}
316
Ok(())
317
}
318
319
/// Calls `PostMessageW()` internally. This is a common pattern, where we send a pointer to
320
/// the given object as the lParam. The receiver is responsible for destructing the object.
321
fn post_message_to_thread_carrying_object<U>(&self, msg: UINT, object: U) -> Result<()> {
322
let mut boxed_object = Box::new(object);
323
self.post_message_to_thread(
324
msg,
325
/* w_param= */ 0,
326
&mut *boxed_object as *mut U as LPARAM,
327
)
328
.map(|_| {
329
// If successful, the receiver will be responsible for destructing the object.
330
std::mem::forget(boxed_object);
331
})
332
}
333
334
fn run_message_loop(
335
max_num_windows: u32,
336
message_router_handle_sender: Sender<Result<u32>>,
337
message_loop_state: Arc<AtomicI32>,
338
gpu_main_display_tube: Option<Tube>,
339
close_requested_event: Event,
340
) {
341
let gpu_main_display_tube = gpu_main_display_tube.map(Rc::new);
342
// SAFETY:
343
// Safe because the dispatcher will take care of the lifetime of the `MessageOnlyWindow` and
344
// `GuiWindow` objects.
345
match unsafe { Self::create_windows(max_num_windows) }.and_then(
346
|(message_router_window, gui_windows)| {
347
WindowMessageDispatcher::new(
348
message_router_window,
349
gui_windows,
350
gpu_main_display_tube.clone(),
351
close_requested_event,
352
)
353
},
354
) {
355
Ok(dispatcher) => {
356
info!("WndProc thread entering message loop");
357
message_loop_state.store(MessageLoopState::Running as i32, Ordering::SeqCst);
358
359
let message_router_handle =
360
// SAFETY:
361
// Safe because we won't use the handle unless the message loop is still running.
362
unsafe { dispatcher.message_router_handle().unwrap_or(null_mut()) };
363
// HWND cannot be sent cross threads, so we cast it to u32 first.
364
if let Err(e) = message_router_handle_sender.send(Ok(message_router_handle as u32))
365
{
366
error!("Failed to send message router handle: {}", e);
367
}
368
369
let exit_state = Self::run_message_loop_body(dispatcher, gpu_main_display_tube);
370
message_loop_state.store(exit_state as i32, Ordering::SeqCst);
371
}
372
Err(e) => {
373
error!("WndProc thread didn't enter message loop: {:?}", e);
374
message_loop_state.store(
375
MessageLoopState::EarlyTerminatedWithError as i32,
376
Ordering::SeqCst,
377
);
378
if let Err(e) = message_router_handle_sender.send(Err(e)) {
379
error!("Failed to report message loop early termination: {}", e)
380
}
381
}
382
}
383
}
384
385
fn run_message_loop_body(
386
#[cfg_attr(not(feature = "kiwi"), allow(unused_variables, unused_mut))]
387
mut message_dispatcher: Pin<Box<WindowMessageDispatcher>>,
388
gpu_main_display_tube: Option<Rc<Tube>>,
389
) -> MessageLoopState {
390
let mut msg_wait_ctx = MsgWaitContext::new();
391
if let Some(tube) = &gpu_main_display_tube {
392
if let Err(e) = msg_wait_ctx.add(tube.get_read_notifier(), Token::ServiceMessage) {
393
error!(
394
"Failed to add service message read notifier to MsgWaitContext: {:?}",
395
e
396
);
397
return MessageLoopState::EarlyTerminatedWithError;
398
}
399
}
400
401
loop {
402
// SAFETY:
403
// Safe because the lifetime of handles are at least as long as the function call.
404
match unsafe { msg_wait_ctx.wait() } {
405
Ok(token) => match token {
406
Token::MessagePump => {
407
if !Self::retrieve_and_dispatch_messages() {
408
info!("WndProc thread exiting message loop normally");
409
return MessageLoopState::ExitedNormally;
410
}
411
}
412
Token::ServiceMessage => Self::read_and_dispatch_service_message(
413
&mut message_dispatcher,
414
// We never use this token if `gpu_main_display_tube` is None, so
415
// `expect()` should always succeed.
416
gpu_main_display_tube
417
.as_ref()
418
.expect("Service message tube is None"),
419
),
420
},
421
Err(e) => {
422
error!(
423
"WndProc thread exiting message loop because of error: {:?}",
424
e
425
);
426
return MessageLoopState::ExitedWithError;
427
}
428
}
429
}
430
}
431
432
/// Retrieves and dispatches all messages in the queue, and returns whether the message loop
433
/// should continue running.
434
fn retrieve_and_dispatch_messages() -> bool {
435
// Since `MsgWaitForMultipleObjects()` returns only when there is a new event in the queue,
436
// if we call `MsgWaitForMultipleObjects()` again without draining the queue, it will ignore
437
// existing events and will not return immediately. Hence, we need to keep calling
438
// `PeekMessageW()` with `PM_REMOVE` until the queue is drained before returning.
439
// https://devblogs.microsoft.com/oldnewthing/20050217-00/?p=36423
440
// Alternatively we could use `MsgWaitForMultipleObjectsEx()` with the `MWMO_INPUTAVAILABLE`
441
// flag, which will always return if there is any message in the queue, no matter that is a
442
// new message or not. However, we found that it may also return when there is no message at
443
// all, so we slightly prefer `MsgWaitForMultipleObjects()`.
444
loop {
445
// Safe because if `message` is initialized, we will call `assume_init()` to extract the
446
// value, which will get dropped eventually.
447
let mut message = mem::MaybeUninit::uninit();
448
// SAFETY:
449
// Safe because `message` lives at least as long as the function call.
450
if unsafe {
451
PeekMessageW(
452
message.as_mut_ptr(),
453
/* hWnd= */ null_mut(),
454
/* wMsgFilterMin= */ 0,
455
/* wMsgFilterMax= */ 0,
456
PM_REMOVE,
457
) == 0
458
} {
459
// No more message in the queue.
460
return true;
461
}
462
463
// SAFETY:
464
// Safe because `PeekMessageW()` has populated `message`.
465
unsafe {
466
let new_message = message.assume_init();
467
if new_message.message == WM_QUIT {
468
return false;
469
}
470
TranslateMessage(&new_message);
471
DispatchMessageW(&new_message);
472
}
473
}
474
}
475
476
#[cfg(feature = "kiwi")]
477
fn read_and_dispatch_service_message(
478
message_dispatcher: &mut Pin<Box<WindowMessageDispatcher>>,
479
gpu_main_display_tube: &Tube,
480
) {
481
match gpu_main_display_tube.recv::<ServiceSendToGpu>() {
482
Ok(message) => message_dispatcher.as_mut().process_service_message(message),
483
Err(e) => {
484
error!("Failed to receive service message through the tube: {}", e)
485
}
486
}
487
}
488
489
#[cfg(not(feature = "kiwi"))]
490
fn read_and_dispatch_service_message(
491
_: &mut Pin<Box<WindowMessageDispatcher>>,
492
_gpu_main_display_tube: &Tube,
493
) {
494
}
495
496
fn is_message_loop_running(&self) -> bool {
497
self.message_loop_state
498
.as_ref()
499
.is_some_and(|state| state.load(Ordering::SeqCst) == MessageLoopState::Running as i32)
500
}
501
502
/// Normally the WndProc thread should still be running when the `WindowProcedureThread` struct
503
/// is dropped, and this function will post a message to notify that thread to destroy all
504
/// windows and release all resources. If the WndProc thread has encountered fatal errors and
505
/// has already terminated, we don't need to post this message anymore.
506
fn signal_exit_message_loop_if_needed(&self) {
507
if !self.is_message_loop_running() {
508
return;
509
}
510
511
info!("WndProc thread is still in message loop before dropping. Signaling shutting down");
512
if let Err(e) = self.post_message_to_thread(
513
WM_USER_SHUTDOWN_WNDPROC_THREAD_INTERNAL,
514
/* w_param */ 0,
515
/* l_param */ 0,
516
) {
517
error!("Failed to signal WndProc thread to shut down: {:?}", e);
518
}
519
}
520
521
/// Checks if the message loop has exited normally. This should be called after joining with the
522
/// WndProc thread.
523
fn check_message_loop_final_state(&mut self) {
524
match Arc::try_unwrap(self.message_loop_state.take().unwrap()) {
525
Ok(state) => {
526
let state = state.into_inner();
527
if state == MessageLoopState::ExitedNormally as i32 {
528
info!("WndProc thread exited gracefully");
529
} else {
530
warn!("WndProc thread exited with message loop state: {:?}", state);
531
}
532
}
533
Err(e) => error!(
534
"WndProc thread exited but message loop state retrieval failed: {:?}",
535
e
536
),
537
}
538
}
539
540
/// # Safety
541
/// The owner of the returned window objects is responsible for dropping them before we finish
542
/// processing `WM_NCDESTROY`, because the window handle will become invalid afterwards.
543
unsafe fn create_windows(max_num_windows: u32) -> Result<(MessageOnlyWindow, Vec<GuiWindow>)> {
544
let message_router_window = MessageOnlyWindow::new(
545
/* class_name */
546
Self::get_window_class_name::<MessageOnlyWindow>()
547
.with_context(|| {
548
format!(
549
"retrieve the window class name for MessageOnlyWindow of {}.",
550
type_name::<Self>()
551
)
552
})?
553
.as_str(),
554
/* title */ "ThreadMessageRouter",
555
)?;
556
// Gfxstream window is a child window of crosvm window. Without WS_CLIPCHILDREN, the parent
557
// window may use the background brush to clear the gfxstream window client area when
558
// drawing occurs. This caused the screen flickering issue during resizing.
559
// See b/197786842 for details.
560
let mut gui_windows = Vec::with_capacity(max_num_windows as usize);
561
for scanout_id in 0..max_num_windows {
562
gui_windows.push(GuiWindow::new(
563
scanout_id,
564
/* class_name */
565
Self::get_window_class_name::<GuiWindow>()
566
.with_context(|| {
567
format!(
568
"retrieve the window class name for GuiWindow of {}",
569
type_name::<Self>()
570
)
571
})?
572
.as_str(),
573
/* title */ Self::get_window_title().as_str(),
574
/* dw_style */ WS_POPUP | WS_CLIPCHILDREN,
575
// The window size and style can be adjusted later when `Surface` is created.
576
&size2(1, 1),
577
)?);
578
}
579
Ok((message_router_window, gui_windows))
580
}
581
582
unsafe extern "system" fn wnd_proc(
583
hwnd: HWND,
584
msg: UINT,
585
w_param: WPARAM,
586
l_param: LPARAM,
587
) -> LRESULT {
588
let dispatcher_ptr = GetPropW(hwnd, win32_wide_string(DISPATCHER_PROPERTY_NAME).as_ptr())
589
as *mut WindowMessageDispatcher;
590
if let Some(dispatcher) = dispatcher_ptr.as_mut() {
591
if let Some(ret) =
592
dispatcher.dispatch_window_message(hwnd, &MessagePacket::new(msg, w_param, l_param))
593
{
594
return ret;
595
}
596
}
597
DefWindowProcW(hwnd, msg, w_param, l_param)
598
}
599
600
/// U + T decides one window class. For the same combination of U + T, the same window class
601
/// name will be returned. This function also registers the Window class if it is not registered
602
/// through this function yet.
603
fn get_window_class_name<T: RegisterWindowClass>() -> Result<String> {
604
static WINDOW_CLASS_NAMES: Mutex<BTreeMap<TypeId, String>> = Mutex::new(BTreeMap::new());
605
let mut window_class_names = WINDOW_CLASS_NAMES.lock();
606
let id = window_class_names.len();
607
let entry = window_class_names.entry(TypeId::of::<T>());
608
let entry = match entry {
609
Entry::Occupied(entry) => return Ok(entry.get().clone()),
610
Entry::Vacant(entry) => entry,
611
};
612
// We are generating a different class name everytime we reach this line, so the name
613
// shouldn't collide with any window classes registered through this function. The
614
// underscore here is important. If we just use `"{}{}"`, we may collide for prefix = "" and
615
// prefix = "1".
616
let window_class_name = format!("{}_{}", T::CLASS_NAME_PREFIX, id);
617
T::register_window_class(&window_class_name, Some(Self::wnd_proc)).with_context(|| {
618
format!(
619
"Failed to register the window class for ({}, {}), with name {}.",
620
type_name::<Self>(),
621
type_name::<T>(),
622
window_class_name
623
)
624
})?;
625
entry.insert(window_class_name.clone());
626
Ok(window_class_name)
627
}
628
629
fn get_window_title() -> String {
630
"crosvm".to_string()
631
}
632
}
633
634
impl Drop for WindowProcedureThread {
635
fn drop(&mut self) {
636
self.signal_exit_message_loop_if_needed();
637
match self.thread.take().unwrap().join() {
638
Ok(_) => self.check_message_loop_final_state(),
639
Err(e) => error!("Failed to join with WndProc thread: {:?}", e),
640
}
641
}
642
}
643
644
// SAFETY:
645
// Since `WindowProcedureThread` does not hold anything that cannot be transferred between threads,
646
// we can implement `Send` for it.
647
unsafe impl Send for WindowProcedureThread {}
648
649
#[derive(Deserialize, Serialize)]
650
pub struct WindowProcedureThreadBuilder {
651
max_num_windows: u32,
652
display_tube: Option<Tube>,
653
#[cfg(feature = "kiwi")]
654
ime_tube: Option<Tube>,
655
}
656
657
impl WindowProcedureThreadBuilder {
658
pub fn set_max_num_windows(&mut self, max_num_windows: u32) -> &mut Self {
659
self.max_num_windows = max_num_windows;
660
self
661
}
662
663
pub fn set_display_tube(&mut self, display_tube: Option<Tube>) -> &mut Self {
664
self.display_tube = display_tube;
665
self
666
}
667
668
#[cfg(feature = "kiwi")]
669
pub fn set_ime_tube(&mut self, ime_tube: Option<Tube>) -> &mut Self {
670
self.ime_tube = ime_tube;
671
self
672
}
673
674
/// This function creates the window procedure thread and windows.
675
///
676
/// We have seen third-party DLLs hooking into window creation. They may have deep call stack,
677
/// and they may not be well tested against late window creation, which may lead to stack
678
/// overflow. Hence, this should be called as early as possible when the VM is booting.
679
pub fn start_thread(self) -> Result<WindowProcedureThread> {
680
cfg_if::cfg_if! {
681
if #[cfg(feature = "kiwi")] {
682
let ime_tube = self
683
.ime_tube
684
.ok_or_else(|| anyhow!("The ime tube is not set."))?;
685
WindowProcedureThread::start_thread(
686
self.max_num_windows,
687
self.display_tube,
688
ime_tube,
689
)
690
} else {
691
WindowProcedureThread::start_thread(self.max_num_windows, None)
692
}
693
}
694
}
695
}
696
697
#[cfg(test)]
698
mod tests {
699
use super::*;
700
701
#[test]
702
fn error_on_adding_reserved_token_to_context() {
703
let mut ctx = MsgWaitContext::new();
704
let event = Event::new().unwrap();
705
assert!(ctx.add(&event, Token::MessagePump).is_err());
706
}
707
708
#[test]
709
fn error_on_adding_duplicated_handle_to_context() {
710
let mut ctx = MsgWaitContext::new();
711
let event = Event::new().unwrap();
712
assert!(ctx.add(&event, Token::ServiceMessage).is_ok());
713
assert!(ctx.add(&event, Token::ServiceMessage).is_err());
714
}
715
716
#[test]
717
fn window_procedure_window_class_name_should_include_class_name_prefix() {
718
const PREFIX: &str = "test-window-class-prefix";
719
struct TestWindow;
720
impl RegisterWindowClass for TestWindow {
721
const CLASS_NAME_PREFIX: &'static str = PREFIX;
722
fn register_window_class(_class_name: &str, _wnd_proc: WNDPROC) -> Result<()> {
723
Ok(())
724
}
725
}
726
727
let name = WindowProcedureThread::get_window_class_name::<TestWindow>().unwrap();
728
assert!(
729
name.starts_with(PREFIX),
730
"The class name {name} should start with {PREFIX}."
731
);
732
}
733
734
#[test]
735
fn window_procedure_with_same_types_should_return_same_name() {
736
struct TestWindow;
737
impl RegisterWindowClass for TestWindow {
738
fn register_window_class(_class_name: &str, _wnd_proc: WNDPROC) -> Result<()> {
739
Ok(())
740
}
741
}
742
743
let name1 = WindowProcedureThread::get_window_class_name::<TestWindow>().unwrap();
744
let name2 = WindowProcedureThread::get_window_class_name::<TestWindow>().unwrap();
745
assert_eq!(name1, name2);
746
}
747
748
#[test]
749
fn window_procedure_with_different_types_should_return_different_names() {
750
struct TestWindow1;
751
impl RegisterWindowClass for TestWindow1 {
752
fn register_window_class(_class_name: &str, _wnd_proc: WNDPROC) -> Result<()> {
753
Ok(())
754
}
755
}
756
struct TestWindow2;
757
impl RegisterWindowClass for TestWindow2 {
758
fn register_window_class(_class_name: &str, _wnd_proc: WNDPROC) -> Result<()> {
759
Ok(())
760
}
761
}
762
763
let name1 = WindowProcedureThread::get_window_class_name::<TestWindow1>().unwrap();
764
let name2 = WindowProcedureThread::get_window_class_name::<TestWindow2>().unwrap();
765
assert_ne!(name1, name2);
766
}
767
}
768
769