bevy_window/raw_handle.rs
1#![allow(unsafe_code)]
2
3use alloc::sync::Arc;
4use bevy_ecs::prelude::Component;
5use core::{any::Any, marker::PhantomData, ops::Deref};
6use raw_window_handle::{
7 DisplayHandle, HandleError, HasDisplayHandle, HasWindowHandle, RawDisplayHandle,
8 RawWindowHandle, WindowHandle,
9};
10use std::sync::Mutex;
11
12/// A wrapper over a window.
13///
14/// This allows us to extend the lifetime of the window, so it doesn't get eagerly dropped while a
15/// pipelined renderer still has frames in flight that need to draw to it.
16///
17/// This is achieved by storing a shared reference to the window in the [`RawHandleWrapper`],
18/// which gets picked up by the renderer during extraction.
19#[derive(Debug)]
20pub struct WindowWrapper<W> {
21 reference: Arc<dyn Any + Send + Sync>,
22 ty: PhantomData<W>,
23}
24
25impl<W: Send + Sync + 'static> WindowWrapper<W> {
26 /// Creates a `WindowWrapper` from a window.
27 pub fn new(window: W) -> WindowWrapper<W> {
28 WindowWrapper {
29 reference: Arc::new(window),
30 ty: PhantomData,
31 }
32 }
33}
34
35impl<W: 'static> Deref for WindowWrapper<W> {
36 type Target = W;
37
38 fn deref(&self) -> &Self::Target {
39 self.reference.downcast_ref::<W>().unwrap()
40 }
41}
42
43/// A wrapper over [`RawWindowHandle`] and [`RawDisplayHandle`] that allows us to safely pass it across threads.
44///
45/// Depending on the platform, the underlying pointer-containing handle cannot be used on all threads,
46/// and so we cannot simply make it (or any type that has a safe operation to get a [`RawWindowHandle`] or [`RawDisplayHandle`])
47/// thread-safe.
48#[derive(Debug, Clone, Component)]
49pub struct RawHandleWrapper {
50 _window: Arc<dyn Any + Send + Sync>,
51 /// Raw handle to a window.
52 pub window_handle: RawWindowHandle,
53 /// Raw handle to the display server.
54 pub display_handle: RawDisplayHandle,
55}
56
57impl RawHandleWrapper {
58 /// Creates a `RawHandleWrapper` from a `WindowWrapper`.
59 pub fn new<W: HasWindowHandle + HasDisplayHandle + 'static>(
60 window: &WindowWrapper<W>,
61 ) -> Result<RawHandleWrapper, HandleError> {
62 Ok(RawHandleWrapper {
63 _window: window.reference.clone(),
64 window_handle: window.window_handle()?.as_raw(),
65 display_handle: window.display_handle()?.as_raw(),
66 })
67 }
68
69 /// Returns a [`HasWindowHandle`] + [`HasDisplayHandle`] impl, which exposes [`WindowHandle`] and [`DisplayHandle`].
70 ///
71 /// # Safety
72 ///
73 /// Some platforms have constraints on where/how this handle can be used. For example, some platforms don't support doing window
74 /// operations off of the main thread. The caller must ensure the [`RawHandleWrapper`] is only used in valid contexts.
75 pub unsafe fn get_handle(&self) -> ThreadLockedRawWindowHandleWrapper {
76 ThreadLockedRawWindowHandleWrapper(self.clone())
77 }
78}
79
80// SAFETY: [`RawHandleWrapper`] is just a normal "raw pointer", which doesn't impl Send/Sync. However the pointer is only
81// exposed via an unsafe method that forces the user to make a call for a given platform. (ex: some platforms don't
82// support doing window operations off of the main thread).
83// A recommendation for this pattern (and more context) is available here:
84// https://github.com/rust-windowing/raw-window-handle/issues/59
85unsafe impl Send for RawHandleWrapper {}
86// SAFETY: This is safe for the same reasons as the Send impl above.
87unsafe impl Sync for RawHandleWrapper {}
88
89/// A [`RawHandleWrapper`] that cannot be sent across threads.
90///
91/// This safely exposes [`RawWindowHandle`] and [`RawDisplayHandle`], but care must be taken to ensure that the construction itself is correct.
92///
93/// This can only be constructed via the [`RawHandleWrapper::get_handle()`] method;
94/// be sure to read the safety docs there about platform-specific limitations.
95/// In many cases, this should only be constructed on the main thread.
96pub struct ThreadLockedRawWindowHandleWrapper(RawHandleWrapper);
97
98impl HasWindowHandle for ThreadLockedRawWindowHandleWrapper {
99 fn window_handle(&self) -> Result<WindowHandle, HandleError> {
100 // SAFETY: the caller has validated that this is a valid context to get [`RawHandleWrapper`]
101 // as otherwise an instance of this type could not have been constructed
102 // NOTE: we cannot simply impl HasRawWindowHandle for RawHandleWrapper,
103 // as the `raw_window_handle` method is safe. We cannot guarantee that all calls
104 // of this method are correct (as it may be off the main thread on an incompatible platform),
105 // and so exposing a safe method to get a [`RawWindowHandle`] directly would be UB.
106 Ok(unsafe { WindowHandle::borrow_raw(self.0.window_handle) })
107 }
108}
109
110impl HasDisplayHandle for ThreadLockedRawWindowHandleWrapper {
111 fn display_handle(&self) -> Result<DisplayHandle, HandleError> {
112 // SAFETY: the caller has validated that this is a valid context to get [`RawDisplayHandle`]
113 // as otherwise an instance of this type could not have been constructed
114 // NOTE: we cannot simply impl HasRawDisplayHandle for RawHandleWrapper,
115 // as the `raw_display_handle` method is safe. We cannot guarantee that all calls
116 // of this method are correct (as it may be off the main thread on an incompatible platform),
117 // and so exposing a safe method to get a [`RawDisplayHandle`] directly would be UB.
118 Ok(unsafe { DisplayHandle::borrow_raw(self.0.display_handle) })
119 }
120}
121
122/// Holder of the [`RawHandleWrapper`] with wrappers, to allow use in asynchronous context
123#[derive(Debug, Clone, Component)]
124pub struct RawHandleWrapperHolder(pub Arc<Mutex<Option<RawHandleWrapper>>>);