bevy_window/
raw_handle.rs

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