Skip to content

Commit

Permalink
Add wasm-bindgen v0.2 handles for web_sys::HtmlCanvasElement and web_…
Browse files Browse the repository at this point in the history
…sys::OffscreenCanvas (#134)

* Add wasm-bindgen v0.2 handles

* Replace wasm-bindgen handles with real wbg objects

In response to feedback from the community, this sets up the wasm-
bindgen handles to use direct wasm-bindgen objects rather than
indices.

As wasm-bindgen handles are not Copy, Eq or Hash, this commit also
removes those impls from all handles.

Closes #142 as well

Signed-off-by: John Nunley <dev@notgull.net>

* Fixup CI

Signed-off-by: John Nunley <dev@notgull.net>

* Whoops

Signed-off-by: John Nunley <dev@notgull.net>

* Rename feature to unstable_web_handles_wbg_02

Signed-off-by: John Nunley <dev@notgull.net>

* Use a raw pointer instead of the direct JsValue

Signed-off-by: John Nunley <dev@notgull.net>

* Fix CI issues

Signed-off-by: John Nunley <dev@notgull.net>

* Review comments

Signed-off-by: John Nunley <dev@notgull.net>

* Fix wasm feature name and structs names

* Re-add Copy

* Redo web canvas examples

* Documentation tweaks

* Add safety comment about ABI

* Add From impls for the new web handles

---------

Signed-off-by: John Nunley <dev@notgull.net>
Co-authored-by: Mads Marquart <mads@marquart.dk>
  • Loading branch information
notgull and madsmtm committed Sep 3, 2023
1 parent 69d9eac commit 809b130
Show file tree
Hide file tree
Showing 5 changed files with 191 additions and 15 deletions.
16 changes: 6 additions & 10 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -40,19 +40,15 @@ jobs:
with:
rust-version: ${{ matrix.rust_version }}

- run: rustup target add x86_64-linux-android
- run: rustup target add wasm32-unknown-unknown

- name: Check documentation
run: cargo doc --no-deps --document-private-items

- name: Run tests
run: cargo test --verbose

- name: Run tests with std
run: cargo test --verbose --features std
- uses: taiki-e/install-action@cargo-hack

- name: Check on Android
run: cargo check --verbose --target x86_64-linux-android
- name: Run tests
run: cargo hack test --feature-powerset

- name: Check on Android with std
run: cargo check --verbose --target x86_64-linux-android --features std
- name: Run tests for wasm32-unknown-unknown
run: cargo hack check --target wasm32-unknown-unknown --feature-powerset
9 changes: 9 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,15 @@ rust-version = "1.64"
alloc = []
std = ["alloc"]

# Allow conversion methods to/from WASM types using `wasm-bindgen` v0.2.
wasm-bindgen-0-2 = ["wasm-bindgen", "std"]

[target.'cfg(target_family = "wasm")'.dependencies.wasm-bindgen]
version = "0.2.87"
default-features = false
features = ["std"]
optional = true

[package.metadata.docs.rs]
all-features = true
rustdoc-args = ["--cfg", "docsrs"]
6 changes: 3 additions & 3 deletions src/borrowed.rs
Original file line number Diff line number Diff line change
Expand Up @@ -209,7 +209,7 @@ impl<H: HasWindowHandle + ?Sized> HasWindowHandle for alloc::sync::Arc<H> {
///
/// This handle is guaranteed to be safe and valid. Get the underlying raw window handle with the
/// [`HasRawWindowHandle`] trait.
#[derive(PartialEq, Eq, Hash, Clone)]
#[derive(PartialEq, Eq, Hash, Copy, Clone)]
pub struct WindowHandle<'a> {
raw: RawWindowHandle,
_marker: PhantomData<&'a *const ()>,
Expand Down Expand Up @@ -237,13 +237,13 @@ impl<'a> WindowHandle<'a> {

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

impl HasWindowHandle for WindowHandle<'_> {
fn window_handle(&self) -> Result<Self, HandleError> {
Ok(self.clone())
Ok(*self)
}
}

Expand Down
28 changes: 26 additions & 2 deletions src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
#![no_std]
#![cfg_attr(docsrs, feature(doc_cfg))]
#![allow(clippy::new_without_default)]
#![deny(unsafe_op_in_unsafe_fn)]

//! Interoperability library for Rust Windowing applications.
//!
Expand Down Expand Up @@ -51,7 +53,9 @@ pub use unix::{
DrmDisplayHandle, DrmWindowHandle, GbmDisplayHandle, GbmWindowHandle, WaylandDisplayHandle,
WaylandWindowHandle, XcbDisplayHandle, XcbWindowHandle, XlibDisplayHandle, XlibWindowHandle,
};
pub use web::{WebDisplayHandle, WebWindowHandle};
pub use web::{
WebCanvasWindowHandle, WebDisplayHandle, WebOffscreenCanvasWindowHandle, WebWindowHandle,
};
pub use windows::{Win32WindowHandle, WinRtWindowHandle, WindowsDisplayHandle};

use core::fmt;
Expand Down Expand Up @@ -120,7 +124,7 @@ unsafe impl<T: HasRawWindowHandle + ?Sized> HasRawWindowHandle for alloc::sync::
/// [`RawWindowHandle::Xlib`] on macOS, it would just be weird, and probably
/// requires something like XQuartz be used).
#[non_exhaustive]
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum RawWindowHandle {
/// A raw window handle for UIKit (Apple's non-macOS windowing library).
///
Expand Down Expand Up @@ -189,6 +193,20 @@ pub enum RawWindowHandle {
/// ## Availability Hints
/// This variant is used on Wasm or asm.js targets when targeting the Web/HTML5.
Web(WebWindowHandle),
/// A raw window handle for a Web canvas registered via [`wasm-bindgen`].
///
/// ## Availability Hints
/// This variant is used on Wasm or asm.js targets when targeting the Web/HTML5.
///
/// [`wasm-bindgen`]: https://crates.io/crates/wasm-bindgen
WebCanvas(WebCanvasWindowHandle),
/// A raw window handle for a Web offscreen canvas registered via [`wasm-bindgen`].
///
/// ## Availability Hints
/// This variant is used on Wasm or asm.js targets when targeting the Web/HTML5.
///
/// [`wasm-bindgen`]: https://crates.io/crates/wasm-bindgen
WebOffscreenCanvas(WebOffscreenCanvasWindowHandle),
/// A raw window handle for Android NDK.
///
/// ## Availability Hints
Expand Down Expand Up @@ -431,5 +449,11 @@ from_impl!(RawWindowHandle, Gbm, GbmWindowHandle);
from_impl!(RawWindowHandle, Win32, Win32WindowHandle);
from_impl!(RawWindowHandle, WinRt, WinRtWindowHandle);
from_impl!(RawWindowHandle, Web, WebWindowHandle);
from_impl!(RawWindowHandle, WebCanvas, WebCanvasWindowHandle);
from_impl!(
RawWindowHandle,
WebOffscreenCanvas,
WebOffscreenCanvasWindowHandle
);
from_impl!(RawWindowHandle, AndroidNdk, AndroidNdkWindowHandle);
from_impl!(RawWindowHandle, Haiku, HaikuWindowHandle);
147 changes: 147 additions & 0 deletions src/web.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
use core::ffi::c_void;
use core::ptr::NonNull;

/// Raw display handle for the Web.
#[non_exhaustive]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
Expand Down Expand Up @@ -48,3 +51,147 @@ impl WebWindowHandle {
Self { id }
}
}

/// Raw window handle for a Web canvas registered via [`wasm-bindgen`].
///
/// [`wasm-bindgen`]: https://crates.io/crates/wasm-bindgen
#[non_exhaustive]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct WebCanvasWindowHandle {
/// A pointer to the [`JsValue`] of an [`HtmlCanvasElement`].
///
/// Note: This uses [`c_void`] to avoid depending on `wasm-bindgen`
/// directly.
///
/// [`JsValue`]: https://docs.rs/wasm-bindgen/latest/wasm_bindgen/struct.JsValue.html
/// [`HtmlCanvasElement`]: https://docs.rs/web-sys/latest/web_sys/struct.HtmlCanvasElement.html
//
// SAFETY: Not using `JsValue` is sound because `wasm-bindgen` guarantees
// that there's only one version of itself in any given binary, and hence
// we can't have a type-confusion where e.g. one library used `JsValue`
// from `v0.2` of `wasm-bindgen`, and another used `JsValue` from `v1.0`;
// the binary will simply fail to compile!
//
// Reference: TODO
pub obj: NonNull<c_void>,
}

impl WebCanvasWindowHandle {
/// Create a new handle from a pointer to [`HtmlCanvasElement`].
///
/// [`HtmlCanvasElement`]: https://docs.rs/web-sys/latest/web_sys/struct.HtmlCanvasElement.html
///
/// # Example
///
/// ```
/// # use core::ffi::c_void;
/// # use core::ptr::NonNull;
/// # use raw_window_handle::WebCanvasWindowHandle;
/// # type HtmlCanvasElement = ();
/// # type JsValue = ();
/// let canvas: &HtmlCanvasElement;
/// # canvas = &();
/// let value: &JsValue = &canvas; // Deref to `JsValue`
/// let obj: NonNull<c_void> = NonNull::from(value).cast();
/// let mut handle = WebCanvasWindowHandle::new(obj);
/// ```
pub fn new(obj: NonNull<c_void>) -> Self {
Self { obj }
}
}

#[cfg(all(target_family = "wasm", feature = "wasm-bindgen-0-2"))]
#[cfg_attr(
docsrs,
doc(cfg(all(target_family = "wasm", feature = "wasm-bindgen-0-2")))
)]
/// These implementations are only available when `wasm-bindgen-0-2` is enabled.
impl WebCanvasWindowHandle {
/// Create a new `WebCanvasWindowHandle` from a [`wasm_bindgen::JsValue`].
///
/// The `JsValue` should refer to a `HtmlCanvasElement`, and the lifetime
/// of the value should be at least as long as the lifetime of this.
pub fn from_wasm_bindgen_0_2(js_value: &wasm_bindgen::JsValue) -> Self {
Self::new(NonNull::from(js_value).cast())
}

/// Convert to the underlying [`wasm_bindgen::JsValue`].
///
/// # Safety
///
/// The inner pointer must be valid. This is ensured if this handle was
/// borrowed from [`WindowHandle`][crate::WindowHandle].
pub unsafe fn as_wasm_bindgen_0_2(&self) -> &wasm_bindgen::JsValue {
unsafe { self.obj.cast().as_ref() }
}
}

/// Raw window handle for a Web offscreen canvas registered via
/// [`wasm-bindgen`].
///
/// [`wasm-bindgen`]: https://crates.io/crates/wasm-bindgen
#[non_exhaustive]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct WebOffscreenCanvasWindowHandle {
/// A pointer to the [`JsValue`] of an [`OffscreenCanvas`].
///
/// Note: This uses [`c_void`] to avoid depending on `wasm-bindgen`
/// directly.
///
/// [`JsValue`]: https://docs.rs/wasm-bindgen/latest/wasm_bindgen/struct.JsValue.html
/// [`OffscreenCanvas`]: https://docs.rs/web-sys/latest/web_sys/struct.OffscreenCanvas.html
//
// SAFETY: See WebCanvasWindowHandle.
pub obj: NonNull<c_void>,
}

impl WebOffscreenCanvasWindowHandle {
/// Create a new handle from a pointer to an [`OffscreenCanvas`].
///
/// [`OffscreenCanvas`]: https://docs.rs/web-sys/latest/web_sys/struct.OffscreenCanvas.html
///
/// # Example
///
/// ```
/// # use core::ffi::c_void;
/// # use core::ptr::NonNull;
/// # use raw_window_handle::WebOffscreenCanvasWindowHandle;
/// # type OffscreenCanvas = ();
/// # type JsValue = ();
/// let canvas: &OffscreenCanvas;
/// # canvas = &();
/// let value: &JsValue = &canvas; // Deref to `JsValue`
/// let obj: NonNull<c_void> = NonNull::from(value).cast();
/// let mut handle = WebOffscreenCanvasWindowHandle::new(obj);
/// ```
pub fn new(obj: NonNull<c_void>) -> Self {
Self { obj }
}
}

#[cfg(all(target_family = "wasm", feature = "wasm-bindgen-0-2"))]
#[cfg_attr(
docsrs,
doc(cfg(all(target_family = "wasm", feature = "wasm-bindgen-0-2")))
)]
/// These implementations are only available when `wasm-bindgen-0-2` is enabled.
impl WebOffscreenCanvasWindowHandle {
/// Create a new `WebOffscreenCanvasWindowHandle` from a
/// [`wasm_bindgen::JsValue`].
///
/// The `JsValue` should refer to a `HtmlCanvasElement`, and the lifetime
/// of the value should be at least as long as the lifetime of this.
pub fn from_wasm_bindgen_0_2(js_value: &wasm_bindgen::JsValue) -> Self {
Self::new(NonNull::from(js_value).cast())
}

/// Convert to the underlying [`wasm_bindgen::JsValue`].
///
/// # Safety
///
/// The inner pointer must be valid. This is ensured if this handle was
/// borrowed from [`WindowHandle`][crate::WindowHandle].
pub unsafe fn as_wasm_bindgen_0_2(&self) -> &wasm_bindgen::JsValue {
unsafe { self.obj.cast().as_ref() }
}
}

0 comments on commit 809b130

Please sign in to comment.