Skip to content

Commit

Permalink
feat: Add safer borrowed handles (#110)
Browse files Browse the repository at this point in the history
Adds safer versions of the raw handles, similar to the `AsFd/BorrowedFd` being added to the standard library.
  • Loading branch information
notgull authored Mar 17, 2023
1 parent c74b241 commit d0eb070
Show file tree
Hide file tree
Showing 2 changed files with 276 additions and 0 deletions.
274 changes: 274 additions & 0 deletions src/borrowed.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,274 @@
//! Borrowable window handles based on the ones in this crate.
//!
//! These should be 100% safe to pass around and use, no possibility of dangling or
//! invalidity.
use core::fmt;
use core::marker::PhantomData;

use crate::{HasRawDisplayHandle, HasRawWindowHandle, RawDisplayHandle, RawWindowHandle};

/// The application is currently active.
///
/// This structure is a token that indicates that the application is
/// not presently suspended. It is used to ensure that the window handle
/// is only used when the application is active.
///
/// It is safe to create this token on platforms where the application
/// is guaranteed to be active, such as on desktop platforms. On Android
/// platforms, the application may be suspended, so this token must be
/// either created with `unsafe` or derived from a `HasDisplayHandle`
/// type.
pub struct Active<'a> {
_marker: PhantomData<&'a *const ()>,
}

impl fmt::Debug for Active<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str("Active { .. }")
}
}

impl<'a> Active<'a> {
/// Create a new active token.
///
/// # Safety
///
/// The application must not be `Suspend`ed.
///
/// # Examples
///
/// ```
/// use raw_window_handle::Active;
///
/// // SAFETY: The application is active.
/// let active = unsafe { Active::new_unchecked() };
/// ```
pub unsafe fn new_unchecked() -> Self {
Self {
_marker: PhantomData,
}
}

/// Create a new active token on a system where the application is
/// guaranteed to be active.
///
/// On most platforms, there is no event where the application is
/// suspended, so there are no cases where this function is unsafe.
///
/// ```
/// use raw_window_handle::Active;
///
/// let with_active = |active: Active<'_>| {
/// /* ... */
/// };
///
/// // Only use this code-path on non-android platforms.
/// #[cfg(not(target_os = "android"))]
/// {
/// let active = Active::new();
/// with_active(active);
/// }
///
/// // Only use this code-path on android platforms.
/// #[cfg(target_os = "android")]
/// {
/// if application_is_active() {
/// let active = unsafe { Active::new_unchecked() };
/// with_active(active);
/// }
/// }
/// # fn application_is_active() -> bool { false }
/// ```
#[cfg(not(target_os = "android"))]
#[cfg_attr(docsrs, doc(cfg(not(target_os = "android"))))]
pub fn new() -> Self {
// SAFETY: The application is guaranteed to be active.
unsafe { Self::new_unchecked() }
}
}

/// A display that acts as a wrapper around a display handle.
///
/// # Safety
///
/// The safety requirements of [`HasRawDisplayHandle`] apply here as
/// well. To clarify, the [`DisplayHandle`] must contain a valid window
/// handle for its lifetime. In addition, the handle must be consistent
/// between multiple calls barring platform-specific events.
///
/// In addition, the `active` function must only return an `Active`
/// token if the application is active.
///
/// Note that these requirements are not enforced on `HasDisplayHandle`,
/// rather, they are enforced on the constructors of [`Active`] and
/// [`DisplayHandle`]. This is because the `HasDisplayHandle` trait is
/// safe to implement.
///
/// [`HasRawDisplayHandle`]: crate::HasRawDisplayHandle
pub trait HasDisplayHandle {
/// Get a token indicating whether the application is active.
fn active(&self) -> Option<Active<'_>>;

/// Get a handle to the display controller of the windowing system.
fn display_handle<'this, 'active>(
&'this self,
active: &'active Active<'_>,
) -> DisplayHandle<'this>
where
'active: 'this;
}

/// The handle to the display controller of the windowing system.
///
/// Get the underlying raw display handle with the `HasRawDisplayHandle` trait.
#[repr(transparent)]
#[derive(PartialEq, Eq, Hash)]
pub struct DisplayHandle<'a> {
raw: RawDisplayHandle,
_marker: PhantomData<&'a *const ()>,
}

impl fmt::Debug for DisplayHandle<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_tuple("DisplayHandle").field(&self.raw).finish()
}
}

impl<'a> Copy for DisplayHandle<'a> {}
impl<'a> Clone for DisplayHandle<'a> {
fn clone(&self) -> Self {
*self
}
}

impl<'a> DisplayHandle<'a> {
/// Borrow a `DisplayHandle` from a `RawDisplayHandle`.
///
/// # Safety
///
/// The `RawDisplayHandle` must be valid for the lifetime and the
/// application must be `Active`. See the requirements on the
/// [`HasDisplayHandle`] trait for more information.
pub unsafe fn borrow_raw(raw: RawDisplayHandle, active: &Active<'a>) -> Self {
let _ = active;
Self {
raw,
_marker: PhantomData,
}
}
}

unsafe impl HasRawDisplayHandle for DisplayHandle<'_> {
fn raw_display_handle(&self) -> RawDisplayHandle {
self.raw
}
}

impl<'a> HasDisplayHandle for DisplayHandle<'a> {
fn active(&self) -> Option<Active<'_>> {
// SAFETY: The fact that this handle was created means that the
// application is active.
Some(unsafe { Active::new_unchecked() })
}

fn display_handle<'this, 'active>(
&'this self,
_active: &'active Active<'_>,
) -> DisplayHandle<'this>
where
'active: 'this,
{
*self
}
}

/// A handle to a window.
///
/// # Safety
///
/// The safety requirements of [`HasRawWindowHandle`] apply here as
/// well. To clarify, the [`WindowHandle`] must contain a valid window
/// handle for its lifetime. In addition, the handle must be consistent
/// between multiple calls barring platform-specific events.
///
/// Note that these requirements are not enforced on `HasWindowHandle`,
/// rather, they are enforced on the constructors of
/// [`WindowHandle`]. This is because the `HasWindowHandle` trait is
/// safe to implement.
pub trait HasWindowHandle {
/// Get a handle to the window.
fn window_handle<'this, 'active>(
&'this self,
active: &'active Active<'_>,
) -> WindowHandle<'this>
where
'active: 'this;
}

/// The handle to a window.
///
/// This handle is guaranteed to be safe and valid. Get the underlying
/// raw window handle with the `HasRawWindowHandle` trait.
#[repr(transparent)]
#[derive(PartialEq, Eq, Hash)]
pub struct WindowHandle<'a> {
raw: RawWindowHandle,
_marker: PhantomData<&'a *const ()>,
}

impl fmt::Debug for WindowHandle<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_tuple("WindowHandle").field(&self.raw).finish()
}
}

impl<'a> Copy for WindowHandle<'a> {}
impl<'a> Clone for WindowHandle<'a> {
fn clone(&self) -> Self {
*self
}
}

impl<'a> WindowHandle<'a> {
/// Borrow a `WindowHandle` from a `RawWindowHandle`.
///
/// # Safety
///
/// The `RawWindowHandle` must be valid for the lifetime and the
/// application must be `Active`.
pub unsafe fn borrow_raw(raw: RawWindowHandle, active: &Active<'a>) -> Self {
let _ = active;
Self {
raw,
_marker: PhantomData,
}
}
}

unsafe impl HasRawWindowHandle for WindowHandle<'_> {
fn raw_window_handle(&self) -> RawWindowHandle {
self.raw
}
}

impl<'a> HasWindowHandle for WindowHandle<'a> {
fn window_handle<'this, 'active>(
&'this self,
_active: &'active Active<'_>,
) -> WindowHandle<'this>
where
'active: 'this,
{
*self
}
}

/// ```compile_fail
/// use raw_window_handle::{Active, DisplayHandle, WindowHandle};
/// fn _assert<T: Send + Sync>() {}
/// _assert::<Active<'static>>();
/// _assert::<DisplayHandle<'static>>();
/// _assert::<WindowHandle<'static>>();
/// ```
fn _not_send_or_sync() {}
2 changes: 2 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ extern crate alloc;

mod android;
mod appkit;
mod borrowed;
mod haiku;
mod redox;
mod uikit;
Expand All @@ -39,6 +40,7 @@ mod windows;

pub use android::{AndroidDisplayHandle, AndroidNdkWindowHandle};
pub use appkit::{AppKitDisplayHandle, AppKitWindowHandle};
pub use borrowed::{Active, DisplayHandle, HasDisplayHandle, HasWindowHandle, WindowHandle};
pub use haiku::{HaikuDisplayHandle, HaikuWindowHandle};
pub use redox::{OrbitalDisplayHandle, OrbitalWindowHandle};
pub use uikit::{UiKitDisplayHandle, UiKitWindowHandle};
Expand Down

0 comments on commit d0eb070

Please sign in to comment.