Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Add safer borrowed handles #110

Merged
merged 2 commits into from
Mar 17, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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