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)]
new(hwnd: HWND, hmodule: HMODULE) -> Self113 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 {
get_inner_size(&self) -> Result<Size2D<u32, euclid::UnknownUnit>>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
create_vulkan_surface(self: Arc<Self>, instance: Arc<Instance>) -> Result<Arc<Surface>>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 {
drop(&mut self)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.
handle_window_message<AppState: ApplicationState>( window_state: &RefCell<Option<WindowState<AppState>>>, hwnd: HWND, message: MessagePacket, ) -> Result<LRESULT>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)]
wnd_proc<AppState: ApplicationState>( hwnd: HWND, msg: UINT, w_param: WPARAM, l_param: LPARAM, ) -> LRESULT292 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 {}).",
316 error
317 );
318 }
319 create_struct.lpCreateParams
320 } else {
321 // SAFETY: trivially safe
322 unsafe { SetLastError(ERROR_SUCCESS) };
323 // SAFETY: Safe because we handle the error cases.
324 let userdata_ptr = unsafe { GetWindowLongPtrW(hwnd, GWLP_USERDATA) };
325 if userdata_ptr == 0 {
326 // SAFETY: trivially safe
327 let error = unsafe { GetLastError() };
328 assert_eq!(
329 error, ERROR_SUCCESS,
330 "Failed to get GWLP_USERDATA when handling the message {} (Error code {}).",
331 msg, error
332 );
333 }
334 userdata_ptr as *mut _
335 };
336
337 let window_state =
338 // SAFETY: Safe because if the pointer is not null, it always points to a valid
339 // RefCell<Option<WindowState>>. This is guaranteed by the safety notes of create_window.
340 unsafe { (userdata_ptr as *const RefCell<Option<WindowState<AppState>>>).as_ref() };
341 let window_state = if let Some(window_state) = window_state {
342 window_state
343 } else {
344 // SAFETY: Safe because we are processing a message targeting this thread.
345 return unsafe { DefWindowProcW(hwnd, msg, w_param, l_param) };
346 };
347
348 let message = MessagePacket {
349 msg,
350 w_param,
351 l_param,
352 };
353 // SAFETY: Safe because we are processing a message targeting this thread.
354 let result = unsafe { handle_window_message(window_state, hwnd, message) }
355 .with_context(|| format_err!("handle the window message: {:?}", message));
356
357 match result {
358 Ok(result) => result,
359 Err(err) => {
360 error!("{:?}", err);
361 // SAFETY: Safe because we are processing a message targeting this thread.
362 unsafe { DefWindowProcW(hwnd, msg, w_param, l_param) }
363 }
364 }
365 }
366
367 static WND_CLASS_REGISTRATION_SUCCESS: Mutex<bool> = Mutex::new(false);
368
369 /// # Safety
370 /// - The passed in `worker` must not be destroyed before the created window is destroyed if the
371 /// window creation succeeds.
372 /// - The WNDPROC must be called within the same thread that calls create_window.
373 /// # Arguments
374 /// * `worker` - we use the runtime borrow checker to make sure there is no unwanted borrowing to
375 /// the underlying worker.
376 #[deny(unsafe_op_in_unsafe_fn)]
create_window<AppState, AppStateBuilder>( parent: HWND, initial_window_size: &Size2D<i32, UnknownUnit>, app_state_builder: AppStateBuilder, window_state: &RefCell<Option<WindowState<AppState>>>, user_event_rx: Receiver<AppState::UserEvent>, ) -> Result<HWND> where AppState: ApplicationState, AppStateBuilder: ApplicationStateBuilder<Target = AppState>,377 unsafe fn create_window<AppState, AppStateBuilder>(
378 parent: HWND,
379 initial_window_size: &Size2D<i32, UnknownUnit>,
380 app_state_builder: AppStateBuilder,
381 window_state: &RefCell<Option<WindowState<AppState>>>,
382 user_event_rx: Receiver<AppState::UserEvent>,
383 ) -> Result<HWND>
384 where
385 AppState: ApplicationState,
386 AppStateBuilder: ApplicationStateBuilder<Target = AppState>,
387 {
388 // SAFETY: Safe because we pass a null pointer for the module which tells this function to
389 // return the current executable name.
390 let hmodule = unsafe { GetModuleHandleW(null_mut()) };
391 if hmodule.is_null() {
392 syscall_bail!("Failed to call GetModuleHandleW() for the current module.");
393 }
394
395 let class_name = "vulkan-subWin";
396 let class_name_win32_str = win32_wide_string(class_name);
397 let window_class = WNDCLASSEXW {
398 cbSize: std::mem::size_of::<WNDCLASSEXW>() as u32,
399 style: CS_OWNDC | CS_HREDRAW | CS_VREDRAW,
400 lpfnWndProc: Some(wnd_proc::<AppState>),
401 cbClsExtra: 0,
402 cbWndExtra: 0,
403 hInstance: hmodule,
404 hIcon: null_mut(),
405 hCursor: null_mut(),
406 hbrBackground: null_mut(),
407 lpszMenuName: null_mut(),
408 lpszClassName: class_name_win32_str.as_ptr(),
409 hIconSm: null_mut(),
410 };
411
412 {
413 let mut wnd_class_registration_success = WND_CLASS_REGISTRATION_SUCCESS.lock();
414 if !*wnd_class_registration_success {
415 // Check if a window class with the same name already exists. This is unexpected.
416 {
417 let mut window_class_info = MaybeUninit::uninit();
418 // SAFETY: Safe because we pass valid pointers.
419 if unsafe {
420 GetClassInfoExW(
421 window_class.hInstance,
422 window_class.lpszClassName,
423 window_class_info.as_mut_ptr(),
424 )
425 } != 0
426 {
427 bail!(
428 "A window class with the same name {} has already been registered.",
429 class_name
430 );
431 } else {
432 // SAFETY: trivially safe
433 let sys_error = unsafe { GetLastError() };
434 if sys_error != ERROR_CLASS_DOES_NOT_EXIST {
435 bail!("Failed to call GetClassInfoExW: {}", sys_error);
436 }
437 }
438 }
439 // SAFETY: Safe because we know the lifetime of `window_class`, and we handle failures
440 // below.
441 if unsafe { RegisterClassExW(&window_class) } == 0 {
442 syscall_bail!("Failed to call RegisterClassExW()");
443 }
444 *wnd_class_registration_success = true;
445 }
446 }
447
448 let window_name = win32_wide_string("sub");
449 let mut style = WS_DISABLED | WS_VISIBLE;
450 // In normal practice parent will not be NULL, but it is convenient for this function to also
451 // support a NULL parent for unit tests.
452 if !parent.is_null() {
453 style |= WS_CHILD;
454 }
455 // SAFETY: Safe because we handle failures below.
456 let hwnd = unsafe {
457 CreateWindowExW(
458 WS_EX_NOPARENTNOTIFY,
459 class_name_win32_str.as_ptr(),
460 window_name.as_ptr(),
461 style,
462 0,
463 0,
464 initial_window_size.width,
465 initial_window_size.height,
466 parent,
467 null_mut(),
468 hmodule,
469 window_state as *const _ as LPVOID,
470 )
471 };
472 if hwnd.is_null() {
473 syscall_bail!("Failed to call CreateWindowExW()");
474 }
475 info!("Child window created. HWND = {:#x}", hwnd as usize);
476
477 // SAFETY: Safe because hwnd is a valid HWND handle, and we won't destroy the window outside
478 // this struct.
479 let window = Arc::new(unsafe { Window::new(hwnd, hmodule) });
480 let app_state = app_state_builder
481 .build(Arc::clone(&window))
482 .context("create the application state")?;
483 *window_state.borrow_mut() = Some(WindowState {
484 app_state,
485 user_event_rx,
486 window,
487 });
488
489 Ok(hwnd)
490 }
491
492 pub struct WindowsWindowEventLoop<AppState: ApplicationState> {
493 hwnd: HWND,
494 user_event_tx: SyncSender<AppState::UserEvent>,
495 event_loop_thread: Option<thread::JoinHandle<()>>,
496 }
497
498 // SAFETY: Safe because HWND handle can be used from multiple threads.
499 unsafe impl<T: ApplicationState> Send for WindowsWindowEventLoop<T> {}
500
501 impl<AppState: ApplicationState> WindowEventLoop<AppState> for WindowsWindowEventLoop<AppState> {
502 type WindowType = Window;
503
504 /// # Safety
505 /// The parent window must outlive the lifetime of this object.
506 #[deny(unsafe_op_in_unsafe_fn)]
create<Builder>( parent: NativeWindowType, initial_window_size: &Size2D<i32, UnknownUnit>, application_state_builder: Builder, ) -> Result<Self> where Builder: ApplicationStateBuilder<Target = AppState>,507 unsafe fn create<Builder>(
508 parent: NativeWindowType,
509 initial_window_size: &Size2D<i32, UnknownUnit>,
510 application_state_builder: Builder,
511 ) -> Result<Self>
512 where
513 Builder: ApplicationStateBuilder<Target = AppState>,
514 {
515 let parent = parent as isize;
516 let initial_window_size = *initial_window_size;
517 let (tx, rx) = channel();
518 // The only user events we have are Post and GetVulkanDevice. There's no point in queueing
519 // more Post commands than there are swapchain images (usually 3), and GetVulkanDevice is
520 // only used at initialization. Thus, a reasonable channel size is 5, and if any more
521 // events are queued then it will block the caller.
522 let (user_event_tx, user_event_rx) = sync_channel(5);
523 let event_loop_thread = thread::Builder::new()
524 .name("subwin event loop thread".to_owned())
525 .spawn(move || {
526 let window_state = RefCell::new(None);
527 // SAFETY: Safe because worker will be destroyed at the end of the thread, and the
528 // message pump would have already stopped then. And we are
529 // processing the message in the same thread.
530 let create_window_result = unsafe {
531 create_window(
532 parent as HWND,
533 &initial_window_size,
534 application_state_builder,
535 &window_state,
536 user_event_rx,
537 )
538 }
539 .context("Failed to create the window.");
540 let hwnd = match create_window_result {
541 Ok(hwnd) => hwnd,
542 Err(err) => {
543 tx.send(Err(err)).expect(
544 "Failed to send the create window message back to the caller thread",
545 );
546 return;
547 }
548 };
549
550 tx.send(Ok(hwnd as isize))
551 .expect("send the HWND back to the caller thread");
552 loop {
553 let mut message = MaybeUninit::uninit();
554 // SAFETY: Safe because we handle the error case.
555 match unsafe { GetMessageW(message.as_mut_ptr(), null_mut(), 0, 0) } {
556 // Receive WM_QUIT
557 0 => break,
558 -1 => {
559 // SAFETY: trivially safe
560 error!("GetMessage fails with error code {}", unsafe {
561 GetLastError()
562 });
563 }
564 _ => {
565 // SAFETY: Safe because GetMessage returns with success, and GetMessage
566 // should fill message on success.
567 let message = unsafe { message.assume_init() };
568 // SAFETY: Safe because we are calling this function on the thread where
569 // the window is created.
570 unsafe { DispatchMessageW(&message) };
571 }
572 }
573 }
574 })
575 .context("create the worker thread")?;
576 let hwnd = rx
577 .recv()
578 .context("receive the HWND from the worker thread")??;
579 Ok(Self {
580 hwnd: hwnd as HWND,
581 user_event_tx,
582 event_loop_thread: Some(event_loop_thread),
583 })
584 }
585
move_window(&self, pos: &Box2D<i32, UnknownUnit>) -> Result<()>586 fn move_window(&self, pos: &Box2D<i32, UnknownUnit>) -> Result<()> {
587 // SAFETY: Safe because we handle the error.
588 if unsafe {
589 MoveWindow(
590 self.hwnd,
591 pos.min.x,
592 pos.min.y,
593 pos.width(),
594 pos.height(),
595 TRUE,
596 )
597 } == 0
598 {
599 syscall_bail!("Failed to call MoveWindow");
600 }
601 Ok(())
602 }
603
send_event(&self, event: AppState::UserEvent) -> Result<()>604 fn send_event(&self, event: AppState::UserEvent) -> Result<()> {
605 self.user_event_tx.send(event).map_err(|e| {
606 format_err!("Failed to send post command to the worker thread: {:?}", e)
607 })?;
608 // SAFETY: Safe because arguments outlive function call and contain no pointers.
609 if unsafe { PostMessageW(self.hwnd, WM_USER_USER_EVENT_AVAILABLE, 0, 0) } == 0 {
610 syscall_bail!(
611 "Failed to call PostMessage to send the notification to the worker thread"
612 );
613 }
614 Ok(())
615 }
616 }
617
618 impl<AppState: ApplicationState> Drop for WindowsWindowEventLoop<AppState> {
drop(&mut self)619 fn drop(&mut self) {
620 // SAFETY: Safe because we handle the error.
621 if unsafe { PostMessageW(self.hwnd, WM_CLOSE, 0, 0) } == 0 {
622 // SAFETY: trivially safe
623 let error = unsafe { GetLastError() };
624 if error != ERROR_INVALID_WINDOW_HANDLE {
625 error!(
626 "Failed to post the WM_CLOSE message the window. (Error code {})",
627 error
628 );
629 } else {
630 info!(concat!(
631 "Failed to post the WM_CLOSE message the window with ",
632 "ERROR_INVALID_WINDOW_HANDLE. This is benign if the window is already closed ",
633 "through other mechanisms."
634 ));
635 }
636 }
637 if let Some(worker_thread) = self.event_loop_thread.take() {
638 // It's Ok to panic on join failure, because it only happens if the worker thread
639 // panics, on which case the whole process should have abort because crosvm sets abort
640 // on panic.
641 worker_thread
642 .join()
643 .expect("The worker thread panic unexpectedly");
644 }
645 }
646 }
647
create_post_image_external_memory_handle_types() -> ExternalMemoryHandleTypes648 pub(crate) fn create_post_image_external_memory_handle_types() -> ExternalMemoryHandleTypes {
649 ExternalMemoryHandleTypes {
650 opaque_win32: true,
651 ..ExternalMemoryHandleTypes::empty()
652 }
653 }
654
655 // The ownership of the descriptor is transferred to the returned MemoryImportInfo.
create_post_image_memory_import_info( memory_descriptor: &dyn AsRawDescriptor, ) -> MemoryImportInfo656 pub(crate) fn create_post_image_memory_import_info(
657 memory_descriptor: &dyn AsRawDescriptor,
658 ) -> MemoryImportInfo {
659 MemoryImportInfo::Win32 {
660 handle_type: ExternalMemoryHandleType::OpaqueWin32,
661 handle: memory_descriptor.as_raw_descriptor(),
662 }
663 }
664
import_semaphore_from_descriptor( device: &Arc<Device>, semaphore: vk::Semaphore, descriptor: &dyn AsRawDescriptor, ) -> vk::Result665 pub(crate) fn import_semaphore_from_descriptor(
666 device: &Arc<Device>,
667 semaphore: vk::Semaphore,
668 descriptor: &dyn AsRawDescriptor,
669 ) -> vk::Result {
670 let import_handle_info = vk::ImportSemaphoreWin32HandleInfoKHR::builder()
671 .semaphore(semaphore)
672 .flags(vk::SemaphoreImportFlags::empty())
673 .handle_type(vk::ExternalSemaphoreHandleTypeFlags::OPAQUE_WIN32)
674 .handle(descriptor.as_raw_descriptor())
675 .name(null())
676 .build();
677 // SAFETY: Safe because `import_handle_info` outlives call to import_semaphore_win32_handle_khr
678 // and because we know `import_semaphore_win32_handle_khr` will be non-null on windows.
679 unsafe {
680 (device
681 .fns()
682 .khr_external_semaphore_win32
683 .import_semaphore_win32_handle_khr)(
684 device.internal_object(), &import_handle_info
685 )
686 }
687 }
688
689 #[cfg(test)]
690 mod tests {
691 use std::any::Any;
692 use std::io;
693 use std::sync::atomic::AtomicBool;
694
695 use winapi::um::winuser::SetWindowTextW;
696
697 use super::*;
698
699 #[test]
user_event_handler_can_call_into_wndproc()700 fn user_event_handler_can_call_into_wndproc() {
701 static PROCESS_EVENT_CALLED: AtomicBool = AtomicBool::new(false);
702
703 struct UserEvent;
704 struct State {
705 hwnd: HWND,
706 }
707 impl ApplicationState for State {
708 type UserEvent = UserEvent;
709
710 fn process_event(&self, _: WindowEvent<Self::UserEvent>) {
711 // SAFETY: Safe because "test" string literal is static.
712 let res = unsafe { SetWindowTextW(self.hwnd, win32_wide_string("test").as_ptr()) };
713 assert!(
714 res != 0,
715 "SetWindowTextW failed: {:?}",
716 io::Error::last_os_error()
717 );
718 PROCESS_EVENT_CALLED.store(true, std::sync::atomic::Ordering::SeqCst);
719 }
720 }
721 struct StateBuilder;
722 impl ApplicationStateBuilder for StateBuilder {
723 type Target = State;
724
725 fn build<T: WindowT>(self, window: Arc<T>) -> Result<Self::Target> {
726 let window =
727 Arc::downcast::<Window>(window as Arc<dyn Any + Sync + Send + 'static>)
728 .expect("Failed to downcast the window type");
729 Ok(State {
730 hwnd: window.hwnd as HWND,
731 })
732 }
733 }
734 let event_loop =
735 // SAFETY: safe because 0 for parent hwnd means this is a toplevel window so there is
736 // no parent window that needs to outlive this object.
737 unsafe { WindowsWindowEventLoop::create(0 as HWND, &size2(640, 480), StateBuilder) }
738 .unwrap_or_else(|e| panic!("Failed to create the window event loop: {:?}", e));
739 event_loop
740 .send_event(UserEvent)
741 .unwrap_or_else(|e| panic!("Failed to send the user event: {:?}", e));
742
743 let max_timeout = Duration::from_secs(5);
744 let poll_interval = Duration::from_millis(100);
745 let loop_start = std::time::Instant::now();
746
747 while !PROCESS_EVENT_CALLED.load(std::sync::atomic::Ordering::SeqCst) {
748 if loop_start.elapsed() > max_timeout {
749 panic!("Timeout reached waiting for process_event_to be called");
750 }
751 std::thread::sleep(poll_interval);
752 }
753 }
754 }
755