Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
google
GitHub Repository: google/crosvm
Path: blob/main/gpu_display/src/vulkan/sys/windows.rs
5394 views
1
// Copyright 2023 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::cell::RefCell;
6
use std::mem::MaybeUninit;
7
use std::ptr::null;
8
use std::ptr::null_mut;
9
use std::sync::mpsc::channel;
10
use std::sync::mpsc::sync_channel;
11
use std::sync::mpsc::Receiver;
12
use std::sync::mpsc::RecvTimeoutError;
13
use std::sync::mpsc::SyncSender;
14
use std::sync::mpsc::TryRecvError;
15
use std::sync::Arc;
16
use std::thread::ThreadId;
17
use std::thread::{self};
18
use std::time::Duration;
19
20
use anyhow::bail;
21
use anyhow::format_err;
22
use anyhow::Context;
23
use anyhow::Result;
24
use ash::vk;
25
use base::error;
26
use base::info;
27
use base::warn;
28
use base::AsRawDescriptor;
29
use euclid::size2;
30
use euclid::Box2D;
31
use euclid::Size2D;
32
use euclid::UnknownUnit;
33
use sync::Mutex;
34
use vulkano::device::Device;
35
use vulkano::instance::Instance;
36
use vulkano::memory::ExternalMemoryHandleType;
37
use vulkano::memory::ExternalMemoryHandleTypes;
38
use vulkano::memory::MemoryImportInfo;
39
use vulkano::VulkanObject;
40
use win_util::syscall_bail;
41
use win_util::win32_wide_string;
42
use winapi::shared::minwindef::HMODULE;
43
use winapi::shared::minwindef::LPARAM;
44
use winapi::shared::minwindef::LPVOID;
45
use winapi::shared::minwindef::LRESULT;
46
use winapi::shared::minwindef::TRUE;
47
use winapi::shared::minwindef::UINT;
48
use winapi::shared::minwindef::WPARAM;
49
use winapi::shared::windef::HWND;
50
use winapi::shared::windef::RECT;
51
use winapi::shared::winerror::ERROR_CLASS_DOES_NOT_EXIST;
52
use winapi::shared::winerror::ERROR_INVALID_WINDOW_HANDLE;
53
use winapi::shared::winerror::ERROR_SUCCESS;
54
use winapi::um::errhandlingapi::GetLastError;
55
use winapi::um::errhandlingapi::SetLastError;
56
use winapi::um::libloaderapi::GetModuleHandleW;
57
use winapi::um::winuser::CreateWindowExW;
58
use winapi::um::winuser::DefWindowProcW;
59
use winapi::um::winuser::DestroyWindow;
60
use winapi::um::winuser::DispatchMessageW;
61
use winapi::um::winuser::GetClassInfoExW;
62
use winapi::um::winuser::GetClientRect;
63
use winapi::um::winuser::GetMessageW;
64
use winapi::um::winuser::GetWindowLongPtrW;
65
use winapi::um::winuser::MoveWindow;
66
use winapi::um::winuser::PostMessageW;
67
use winapi::um::winuser::PostQuitMessage;
68
use winapi::um::winuser::RegisterClassExW;
69
use winapi::um::winuser::SetWindowLongPtrW;
70
use winapi::um::winuser::CREATESTRUCTW;
71
use winapi::um::winuser::CS_HREDRAW;
72
use winapi::um::winuser::CS_OWNDC;
73
use winapi::um::winuser::CS_VREDRAW;
74
use winapi::um::winuser::GWLP_USERDATA;
75
use winapi::um::winuser::WM_CLOSE;
76
use winapi::um::winuser::WM_DESTROY;
77
use winapi::um::winuser::WM_NCCREATE;
78
use winapi::um::winuser::WM_SIZE;
79
use winapi::um::winuser::WM_USER;
80
use winapi::um::winuser::WNDCLASSEXW;
81
use winapi::um::winuser::WS_CHILD;
82
use winapi::um::winuser::WS_DISABLED;
83
use winapi::um::winuser::WS_EX_NOPARENTNOTIFY;
84
use winapi::um::winuser::WS_VISIBLE;
85
86
use super::ApplicationState;
87
use super::ApplicationStateBuilder;
88
use super::Surface;
89
use super::Window as WindowT;
90
use super::WindowEvent;
91
use super::WindowEventLoop;
92
93
pub type NativeWindowType = HWND;
94
95
#[derive(Copy, Clone, Debug)]
96
struct MessagePacket {
97
msg: UINT,
98
w_param: WPARAM,
99
l_param: LPARAM,
100
}
101
102
pub struct Window {
103
hwnd: isize,
104
hmodule: isize,
105
owner_thread_id: ThreadId,
106
}
107
108
impl Window {
109
/// # Safety
110
/// `hwnd` must be a valid `HWND` handle. The ownership of the hwnd is transferred to this
111
/// struct, so that the hwnd shouldn't be destroyed outside this struct.
112
#[deny(unsafe_op_in_unsafe_fn)]
113
unsafe fn new(hwnd: HWND, hmodule: HMODULE) -> Self {
114
Self {
115
hwnd: hwnd as isize,
116
hmodule: hmodule as isize,
117
owner_thread_id: thread::current().id(),
118
}
119
}
120
}
121
122
impl WindowT for Window {
123
fn get_inner_size(&self) -> Result<Size2D<u32, euclid::UnknownUnit>> {
124
let mut rect: RECT = Default::default();
125
// SAFETY: Safe because self.hwnd and rect outlive this function call.
126
if unsafe { GetClientRect(self.hwnd as HWND, &mut rect) } == 0 {
127
syscall_bail!("Failed when calling GetClientRect.");
128
}
129
Ok(size2(rect.right - rect.left, rect.bottom - rect.top)
130
.try_cast()
131
.unwrap_or_else(|| {
132
panic!(
133
"Size out of range for the client rect \
134
{{ left: {} right: {} bottom: {} top: {} }}",
135
rect.left, rect.right, rect.bottom, rect.top
136
)
137
}))
138
}
139
140
fn create_vulkan_surface(self: Arc<Self>, instance: Arc<Instance>) -> Result<Arc<Surface>> {
141
// SAFETY: Safe because we checked hmodule when we got it, and we created hwnd and checked
142
// it was valid when it was created and we know that self will oulive this Surface
143
// because we pass a clone of the Arc<Self> as the win parameter, which is kept as a
144
// field of the surface.
145
unsafe {
146
Surface::from_win32(
147
instance,
148
self.hmodule as HMODULE,
149
self.hwnd as HWND,
150
Arc::clone(&self) as _,
151
)
152
}
153
.map_err(|e| e.into())
154
}
155
}
156
157
impl Drop for Window {
158
fn drop(&mut self) {
159
// A thread cannot use DestroyWindow to destroy a window created by a different thread.
160
assert!(thread::current().id() == self.owner_thread_id);
161
// SAFETY: Safe because the safety requirement of new function guarantees that hwnd is a
162
// valid window handle, and we handle the error here.
163
if unsafe { DestroyWindow(self.hwnd as HWND) } == 0 {
164
error!(
165
"Failed to call DestroyWindow with {}. Error code: {}",
166
self.hwnd as usize,
167
// SAFETY: trivially safe
168
unsafe { GetLastError() }
169
);
170
}
171
}
172
}
173
174
// Used to notify the event loop thread that the user event queue has at least one user event
175
// available.
176
const WM_USER_USER_EVENT_AVAILABLE: UINT = WM_USER;
177
178
struct WindowState<AppState: ApplicationState> {
179
app_state: AppState,
180
user_event_rx: Receiver<AppState::UserEvent>,
181
window: Arc<Window>,
182
}
183
184
#[deny(unsafe_op_in_unsafe_fn)]
185
/// # Safety
186
/// Must only be called inside an associated WNDPROC callback.
187
unsafe fn handle_window_message<AppState: ApplicationState>(
188
window_state: &RefCell<Option<WindowState<AppState>>>,
189
hwnd: HWND,
190
message: MessagePacket,
191
) -> Result<LRESULT> {
192
if let Some(window_state) = window_state.borrow().as_ref() {
193
assert_eq!(
194
window_state.window.owner_thread_id,
195
std::thread::current().id()
196
);
197
}
198
match message.msg {
199
WM_DESTROY => {
200
info!("Window {:#x} is being destroyed.", hwnd as usize);
201
let window = window_state
202
.borrow_mut()
203
.take()
204
.map(|WindowState { window, .. }| window);
205
if let Some(window) = window {
206
// This could happen if the window is destroyed without receiving WM_CLOSE.
207
match Arc::try_unwrap(window) {
208
// The window is being destroyed. No need to call DestroyWindow again.
209
Ok(window) => std::mem::forget(window),
210
Err(window) => {
211
error!(concat!(
212
"Not the sole reference to the window. There is a possible resource ",
213
"leak."
214
));
215
// Prevent other reference from calling DestroyWindow on the window.
216
std::mem::forget(window);
217
}
218
}
219
}
220
// SAFETY: Safe because it will always succeed.
221
unsafe { PostQuitMessage(0) };
222
return Ok(0);
223
}
224
WM_CLOSE => {
225
info!("Window {:#x} is about to be destroyed.", hwnd as usize);
226
let window_state = window_state.borrow_mut().take();
227
drop(window_state);
228
return Ok(0);
229
}
230
WM_SIZE => {
231
let window_state = window_state.borrow();
232
if let Some(window_state) = window_state.as_ref() {
233
window_state.app_state.process_event(WindowEvent::Resized);
234
} else {
235
warn!(
236
concat!(
237
"The window state is not initialized or has already been destroyed when ",
238
"handling WM_SIZE. lParam = {:#x}, wParam = {:#x}, HWND = {:#x}."
239
),
240
message.l_param, message.w_param, hwnd as usize
241
);
242
}
243
return Ok(0);
244
}
245
WM_USER_USER_EVENT_AVAILABLE => {
246
let window_state = window_state.borrow();
247
let window_state = window_state.as_ref().ok_or_else(|| {
248
format_err!("The window state is not initialized or has already been destroyed.")
249
})?;
250
let user_event = {
251
// It is unlikely that the message arrives early than the channel receiver is ready.
252
// However, the message can arrives after the user event is read from the receiver.
253
// We may even end up with many notification messages in the queue after all the
254
// user events are handled. In which case, even a short timeout can hurt the
255
// performance badly. Hence a very small timeout is used.
256
const TIMEOUT: Duration = Duration::from_nanos(500);
257
match window_state.user_event_rx.recv_timeout(TIMEOUT) {
258
Ok(event) => event,
259
Err(RecvTimeoutError::Timeout) => {
260
bail!(
261
"Didn't receive any user events for {:?} after recieved the user \
262
event available notification. Skip.",
263
TIMEOUT
264
);
265
}
266
Err(e) => bail!("Failed to receive user event from the channel: {:?}", e),
267
}
268
};
269
let mut user_event = Some(user_event);
270
loop {
271
match user_event.take() {
272
Some(user_event) => window_state
273
.app_state
274
.process_event(WindowEvent::User(user_event)),
275
None => break,
276
}
277
match window_state.user_event_rx.try_recv() {
278
Ok(next_user_event) => user_event = Some(next_user_event),
279
Err(TryRecvError::Empty) => break,
280
Err(e) => bail!("Fail to receive more post commands: {:?}.", e),
281
}
282
}
283
}
284
_ => {}
285
}
286
// SAFETY: Safe because we are processing a message targeting this thread, which is guaranteed
287
// by the safety requirement of this function.
288
Ok(unsafe { DefWindowProcW(hwnd, message.msg, message.w_param, message.l_param) })
289
}
290
291
#[deny(unsafe_op_in_unsafe_fn)]
292
unsafe extern "system" fn wnd_proc<AppState: ApplicationState>(
293
hwnd: HWND,
294
msg: UINT,
295
w_param: WPARAM,
296
l_param: LPARAM,
297
) -> LRESULT {
298
let userdata_ptr = if msg == WM_NCCREATE {
299
// SAFETY: Safe because the lparam for this message is a CREATESTRUCTW.
300
let create_struct = unsafe { (l_param as *const CREATESTRUCTW).as_ref() }
301
.expect("Unexpected null lParam for the WM_NCCREATE message");
302
// SAFETY: Safe because we handle the error cases.
303
unsafe { SetLastError(ERROR_SUCCESS) };
304
// SAFETY: Safe because the GWLP_USERDATA pointer is only used by us, and we check if it's
305
// null each time before we use it. We also know that if the pointer is not null it always
306
// points to a valid RefCell<Option<WindowState>>. This is guaranteed by the safety notes of
307
// create_window.
308
if unsafe { SetWindowLongPtrW(hwnd, GWLP_USERDATA, create_struct.lpCreateParams as isize) }
309
== 0
310
{
311
// SAFETY: trivially safe
312
let error = unsafe { GetLastError() };
313
assert_eq!(
314
error, ERROR_SUCCESS,
315
"Failed to set GWLP_USERDATA when initializing the window (Error code {error})."
316
);
317
}
318
create_struct.lpCreateParams
319
} else {
320
// SAFETY: trivially safe
321
unsafe { SetLastError(ERROR_SUCCESS) };
322
// SAFETY: Safe because we handle the error cases.
323
let userdata_ptr = unsafe { GetWindowLongPtrW(hwnd, GWLP_USERDATA) };
324
if userdata_ptr == 0 {
325
// SAFETY: trivially safe
326
let error = unsafe { GetLastError() };
327
assert_eq!(
328
error, ERROR_SUCCESS,
329
"Failed to get GWLP_USERDATA when handling the message {msg} (Error code {error})."
330
);
331
}
332
userdata_ptr as *mut _
333
};
334
335
let window_state =
336
// SAFETY: Safe because if the pointer is not null, it always points to a valid
337
// RefCell<Option<WindowState>>. This is guaranteed by the safety notes of create_window.
338
unsafe { (userdata_ptr as *const RefCell<Option<WindowState<AppState>>>).as_ref() };
339
let window_state = if let Some(window_state) = window_state {
340
window_state
341
} else {
342
// SAFETY: Safe because we are processing a message targeting this thread.
343
return unsafe { DefWindowProcW(hwnd, msg, w_param, l_param) };
344
};
345
346
let message = MessagePacket {
347
msg,
348
w_param,
349
l_param,
350
};
351
// SAFETY: Safe because we are processing a message targeting this thread.
352
let result = unsafe { handle_window_message(window_state, hwnd, message) }
353
.with_context(|| format_err!("handle the window message: {:?}", message));
354
355
match result {
356
Ok(result) => result,
357
Err(err) => {
358
error!("{:?}", err);
359
// SAFETY: Safe because we are processing a message targeting this thread.
360
unsafe { DefWindowProcW(hwnd, msg, w_param, l_param) }
361
}
362
}
363
}
364
365
static WND_CLASS_REGISTRATION_SUCCESS: Mutex<bool> = Mutex::new(false);
366
367
/// # Safety
368
/// - The passed in `worker` must not be destroyed before the created window is destroyed if the
369
/// window creation succeeds.
370
/// - The WNDPROC must be called within the same thread that calls create_window.
371
/// # Arguments
372
/// * `worker` - we use the runtime borrow checker to make sure there is no unwanted borrowing to
373
/// the underlying worker.
374
#[deny(unsafe_op_in_unsafe_fn)]
375
unsafe fn create_window<AppState, AppStateBuilder>(
376
parent: HWND,
377
initial_window_size: &Size2D<i32, UnknownUnit>,
378
app_state_builder: AppStateBuilder,
379
window_state: &RefCell<Option<WindowState<AppState>>>,
380
user_event_rx: Receiver<AppState::UserEvent>,
381
) -> Result<HWND>
382
where
383
AppState: ApplicationState,
384
AppStateBuilder: ApplicationStateBuilder<Target = AppState>,
385
{
386
// SAFETY: Safe because we pass a null pointer for the module which tells this function to
387
// return the current executable name.
388
let hmodule = unsafe { GetModuleHandleW(null_mut()) };
389
if hmodule.is_null() {
390
syscall_bail!("Failed to call GetModuleHandleW() for the current module.");
391
}
392
393
let class_name = "vulkan-subWin";
394
let class_name_win32_str = win32_wide_string(class_name);
395
let window_class = WNDCLASSEXW {
396
cbSize: std::mem::size_of::<WNDCLASSEXW>() as u32,
397
style: CS_OWNDC | CS_HREDRAW | CS_VREDRAW,
398
lpfnWndProc: Some(wnd_proc::<AppState>),
399
cbClsExtra: 0,
400
cbWndExtra: 0,
401
hInstance: hmodule,
402
hIcon: null_mut(),
403
hCursor: null_mut(),
404
hbrBackground: null_mut(),
405
lpszMenuName: null_mut(),
406
lpszClassName: class_name_win32_str.as_ptr(),
407
hIconSm: null_mut(),
408
};
409
410
{
411
let mut wnd_class_registration_success = WND_CLASS_REGISTRATION_SUCCESS.lock();
412
if !*wnd_class_registration_success {
413
// Check if a window class with the same name already exists. This is unexpected.
414
{
415
let mut window_class_info = MaybeUninit::uninit();
416
// SAFETY: Safe because we pass valid pointers.
417
if unsafe {
418
GetClassInfoExW(
419
window_class.hInstance,
420
window_class.lpszClassName,
421
window_class_info.as_mut_ptr(),
422
)
423
} != 0
424
{
425
bail!(
426
"A window class with the same name {} has already been registered.",
427
class_name
428
);
429
} else {
430
// SAFETY: trivially safe
431
let sys_error = unsafe { GetLastError() };
432
if sys_error != ERROR_CLASS_DOES_NOT_EXIST {
433
bail!("Failed to call GetClassInfoExW: {}", sys_error);
434
}
435
}
436
}
437
// SAFETY: Safe because we know the lifetime of `window_class`, and we handle failures
438
// below.
439
if unsafe { RegisterClassExW(&window_class) } == 0 {
440
syscall_bail!("Failed to call RegisterClassExW()");
441
}
442
*wnd_class_registration_success = true;
443
}
444
}
445
446
let window_name = win32_wide_string("sub");
447
let mut style = WS_DISABLED | WS_VISIBLE;
448
// In normal practice parent will not be NULL, but it is convenient for this function to also
449
// support a NULL parent for unit tests.
450
if !parent.is_null() {
451
style |= WS_CHILD;
452
}
453
// SAFETY: Safe because we handle failures below.
454
let hwnd = unsafe {
455
CreateWindowExW(
456
WS_EX_NOPARENTNOTIFY,
457
class_name_win32_str.as_ptr(),
458
window_name.as_ptr(),
459
style,
460
0,
461
0,
462
initial_window_size.width,
463
initial_window_size.height,
464
parent,
465
null_mut(),
466
hmodule,
467
window_state as *const _ as LPVOID,
468
)
469
};
470
if hwnd.is_null() {
471
syscall_bail!("Failed to call CreateWindowExW()");
472
}
473
info!("Child window created. HWND = {:#x}", hwnd as usize);
474
475
// SAFETY: Safe because hwnd is a valid HWND handle, and we won't destroy the window outside
476
// this struct.
477
let window = Arc::new(unsafe { Window::new(hwnd, hmodule) });
478
let app_state = app_state_builder
479
.build(Arc::clone(&window))
480
.context("create the application state")?;
481
*window_state.borrow_mut() = Some(WindowState {
482
app_state,
483
user_event_rx,
484
window,
485
});
486
487
Ok(hwnd)
488
}
489
490
pub struct WindowsWindowEventLoop<AppState: ApplicationState> {
491
hwnd: HWND,
492
user_event_tx: SyncSender<AppState::UserEvent>,
493
event_loop_thread: Option<thread::JoinHandle<()>>,
494
}
495
496
// SAFETY: Safe because HWND handle can be used from multiple threads.
497
unsafe impl<T: ApplicationState> Send for WindowsWindowEventLoop<T> {}
498
499
impl<AppState: ApplicationState> WindowEventLoop<AppState> for WindowsWindowEventLoop<AppState> {
500
type WindowType = Window;
501
502
/// # Safety
503
/// The parent window must outlive the lifetime of this object.
504
#[deny(unsafe_op_in_unsafe_fn)]
505
unsafe fn create<Builder>(
506
parent: NativeWindowType,
507
initial_window_size: &Size2D<i32, UnknownUnit>,
508
application_state_builder: Builder,
509
) -> Result<Self>
510
where
511
Builder: ApplicationStateBuilder<Target = AppState>,
512
{
513
let parent = parent as isize;
514
let initial_window_size = *initial_window_size;
515
let (tx, rx) = channel();
516
// The only user events we have are Post and GetVulkanDevice. There's no point in queueing
517
// more Post commands than there are swapchain images (usually 3), and GetVulkanDevice is
518
// only used at initialization. Thus, a reasonable channel size is 5, and if any more
519
// events are queued then it will block the caller.
520
let (user_event_tx, user_event_rx) = sync_channel(5);
521
let event_loop_thread = thread::Builder::new()
522
.name("subwin event loop thread".to_owned())
523
.spawn(move || {
524
let window_state = RefCell::new(None);
525
// SAFETY: Safe because worker will be destroyed at the end of the thread, and the
526
// message pump would have already stopped then. And we are
527
// processing the message in the same thread.
528
let create_window_result = unsafe {
529
create_window(
530
parent as HWND,
531
&initial_window_size,
532
application_state_builder,
533
&window_state,
534
user_event_rx,
535
)
536
}
537
.context("Failed to create the window.");
538
let hwnd = match create_window_result {
539
Ok(hwnd) => hwnd,
540
Err(err) => {
541
tx.send(Err(err)).expect(
542
"Failed to send the create window message back to the caller thread",
543
);
544
return;
545
}
546
};
547
548
tx.send(Ok(hwnd as isize))
549
.expect("send the HWND back to the caller thread");
550
loop {
551
let mut message = MaybeUninit::uninit();
552
// SAFETY: Safe because we handle the error case.
553
match unsafe { GetMessageW(message.as_mut_ptr(), null_mut(), 0, 0) } {
554
// Receive WM_QUIT
555
0 => break,
556
-1 => {
557
// SAFETY: trivially safe
558
error!("GetMessage fails with error code {}", unsafe {
559
GetLastError()
560
});
561
}
562
_ => {
563
// SAFETY: Safe because GetMessage returns with success, and GetMessage
564
// should fill message on success.
565
let message = unsafe { message.assume_init() };
566
// SAFETY: Safe because we are calling this function on the thread where
567
// the window is created.
568
unsafe { DispatchMessageW(&message) };
569
}
570
}
571
}
572
})
573
.context("create the worker thread")?;
574
let hwnd = rx
575
.recv()
576
.context("receive the HWND from the worker thread")??;
577
Ok(Self {
578
hwnd: hwnd as HWND,
579
user_event_tx,
580
event_loop_thread: Some(event_loop_thread),
581
})
582
}
583
584
fn move_window(&self, pos: &Box2D<i32, UnknownUnit>) -> Result<()> {
585
// SAFETY: Safe because we handle the error.
586
if unsafe {
587
MoveWindow(
588
self.hwnd,
589
pos.min.x,
590
pos.min.y,
591
pos.width(),
592
pos.height(),
593
TRUE,
594
)
595
} == 0
596
{
597
syscall_bail!("Failed to call MoveWindow");
598
}
599
Ok(())
600
}
601
602
fn send_event(&self, event: AppState::UserEvent) -> Result<()> {
603
self.user_event_tx.send(event).map_err(|e| {
604
format_err!("Failed to send post command to the worker thread: {:?}", e)
605
})?;
606
// SAFETY: Safe because arguments outlive function call and contain no pointers.
607
if unsafe { PostMessageW(self.hwnd, WM_USER_USER_EVENT_AVAILABLE, 0, 0) } == 0 {
608
syscall_bail!(
609
"Failed to call PostMessage to send the notification to the worker thread"
610
);
611
}
612
Ok(())
613
}
614
}
615
616
impl<AppState: ApplicationState> Drop for WindowsWindowEventLoop<AppState> {
617
fn drop(&mut self) {
618
// SAFETY: Safe because we handle the error.
619
if unsafe { PostMessageW(self.hwnd, WM_CLOSE, 0, 0) } == 0 {
620
// SAFETY: trivially safe
621
let error = unsafe { GetLastError() };
622
if error != ERROR_INVALID_WINDOW_HANDLE {
623
error!(
624
"Failed to post the WM_CLOSE message the window. (Error code {})",
625
error
626
);
627
} else {
628
info!(concat!(
629
"Failed to post the WM_CLOSE message the window with ",
630
"ERROR_INVALID_WINDOW_HANDLE. This is benign if the window is already closed ",
631
"through other mechanisms."
632
));
633
}
634
}
635
if let Some(worker_thread) = self.event_loop_thread.take() {
636
// It's Ok to panic on join failure, because it only happens if the worker thread
637
// panics, on which case the whole process should have abort because crosvm sets abort
638
// on panic.
639
worker_thread
640
.join()
641
.expect("The worker thread panic unexpectedly");
642
}
643
}
644
}
645
646
pub(crate) fn create_post_image_external_memory_handle_types() -> ExternalMemoryHandleTypes {
647
ExternalMemoryHandleTypes {
648
opaque_win32: true,
649
..ExternalMemoryHandleTypes::empty()
650
}
651
}
652
653
// The ownership of the descriptor is transferred to the returned MemoryImportInfo.
654
pub(crate) fn create_post_image_memory_import_info(
655
memory_descriptor: &dyn AsRawDescriptor,
656
) -> MemoryImportInfo {
657
MemoryImportInfo::Win32 {
658
handle_type: ExternalMemoryHandleType::OpaqueWin32,
659
handle: memory_descriptor.as_raw_descriptor(),
660
}
661
}
662
663
pub(crate) fn import_semaphore_from_descriptor(
664
device: &Arc<Device>,
665
semaphore: vk::Semaphore,
666
descriptor: &dyn AsRawDescriptor,
667
) -> vk::Result {
668
let import_handle_info = vk::ImportSemaphoreWin32HandleInfoKHR::builder()
669
.semaphore(semaphore)
670
.flags(vk::SemaphoreImportFlags::empty())
671
.handle_type(vk::ExternalSemaphoreHandleTypeFlags::OPAQUE_WIN32)
672
.handle(descriptor.as_raw_descriptor())
673
.name(null())
674
.build();
675
// SAFETY: Safe because `import_handle_info` outlives call to import_semaphore_win32_handle_khr
676
// and because we know `import_semaphore_win32_handle_khr` will be non-null on windows.
677
unsafe {
678
(device
679
.fns()
680
.khr_external_semaphore_win32
681
.import_semaphore_win32_handle_khr)(
682
device.internal_object(), &import_handle_info
683
)
684
}
685
}
686
687
#[cfg(test)]
688
mod tests {
689
use std::any::Any;
690
use std::io;
691
use std::sync::atomic::AtomicBool;
692
693
use winapi::um::winuser::SetWindowTextW;
694
695
use super::*;
696
697
#[test]
698
fn user_event_handler_can_call_into_wndproc() {
699
static PROCESS_EVENT_CALLED: AtomicBool = AtomicBool::new(false);
700
701
struct UserEvent;
702
struct State {
703
hwnd: HWND,
704
}
705
impl ApplicationState for State {
706
type UserEvent = UserEvent;
707
708
fn process_event(&self, _: WindowEvent<Self::UserEvent>) {
709
// SAFETY: Safe because "test" string literal is static.
710
let res = unsafe { SetWindowTextW(self.hwnd, win32_wide_string("test").as_ptr()) };
711
assert!(
712
res != 0,
713
"SetWindowTextW failed: {:?}",
714
io::Error::last_os_error()
715
);
716
PROCESS_EVENT_CALLED.store(true, std::sync::atomic::Ordering::SeqCst);
717
}
718
}
719
struct StateBuilder;
720
impl ApplicationStateBuilder for StateBuilder {
721
type Target = State;
722
723
fn build<T: WindowT>(self, window: Arc<T>) -> Result<Self::Target> {
724
let window =
725
Arc::downcast::<Window>(window as Arc<dyn Any + Sync + Send + 'static>)
726
.expect("Failed to downcast the window type");
727
Ok(State {
728
hwnd: window.hwnd as HWND,
729
})
730
}
731
}
732
let event_loop =
733
// SAFETY: safe because 0 for parent hwnd means this is a toplevel window so there is
734
// no parent window that needs to outlive this object.
735
unsafe { WindowsWindowEventLoop::create(0 as HWND, &size2(640, 480), StateBuilder) }
736
.unwrap_or_else(|e| panic!("Failed to create the window event loop: {e:?}"));
737
event_loop
738
.send_event(UserEvent)
739
.unwrap_or_else(|e| panic!("Failed to send the user event: {e:?}"));
740
741
let max_timeout = Duration::from_secs(5);
742
let poll_interval = Duration::from_millis(100);
743
let loop_start = std::time::Instant::now();
744
745
while !PROCESS_EVENT_CALLED.load(std::sync::atomic::Ordering::SeqCst) {
746
if loop_start.elapsed() > max_timeout {
747
panic!("Timeout reached waiting for process_event_to be called");
748
}
749
std::thread::sleep(poll_interval);
750
}
751
}
752
}
753
754