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

Add wasm-bindgen v0.2 handles for web_sys::HtmlCanvasElement and web_sys::OffscreenCanvas #134

Merged
merged 14 commits into from
Sep 3, 2023
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"]
notgull marked this conversation as resolved.
Show resolved Hide resolved
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
22 changes: 20 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
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!
Comment on lines +69 to +73
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@daxpedda: Do you have a reference for this? How guaranteed is it that there will never be two wasm-bindgen?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is not 100% guaranteed.

But generally speaking the way wasm-bindgen works prevents this, because it encodes "stuff" into the Wasm binary that needs to be interpreted by the CLI tool, so multiple versions will always be incompatible unless wasm-bindgen becomes a completely different beast.

That said, it was discussed in the past to move wasm-bindgen to the component model (which isn't stable yet) and drop the CLI completely at some point. This would probably allow multiple versions of wasm-bindgen to co-exist in the same binary, but old versions will still be unusable. But considering that wasm-bindgen is not actively developed anymore and there are now things like wit-bindgen, I doubt this will ever happen.

    // the binary will simply fail to compile!

I believe it should compile, but you won't be able to use it. Without the CLI tool it won't work on any runtime and the CLI tool will fail if you have multiple versions.

//
// 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`
madsmtm marked this conversation as resolved.
Show resolved Hide resolved
/// 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() }
}
}