use std::{cell::RefCell, sync::OnceLock}; use anyhow::anyhow; use bevy::window::Window; use parking_lot::ReentrantMutex; use web_sys::{CustomEvent, CustomEventInit, Element}; mod conv; pub use conv::*; pub(crate) type WasmMessageBuffer = OnceLock>>>; pub(crate) fn dispatch_to_js( window: &Window, event_type: &str, // NOTE: We use dynamic dispatch here to keep the wasm binary small(-ish) value: &dyn AsJsValue, ) -> anyhow::Result<()> { let canvas = try_get_canvas(window)?; let detail = value.as_js()?; let event = CustomEvent::new_with_event_init_dict( event_type, CustomEventInit::new() .bubbles(true) .cancelable(true) .detail(&detail), ) .map_err(|err| anyhow!("{err:?}"))?; canvas .dispatch_event(&event) .map_err(|err| anyhow!("{err:?}"))?; Ok(()) } fn try_get_canvas(window: &Window) -> anyhow::Result { let canvas_selector = window .canvas .as_ref() .ok_or(anyhow!("Canvas selector not set on Bevy Window"))?; let window = web_sys::window().ok_or(anyhow!("web_sys::window() returned None"))?; let document = window .document() .ok_or(anyhow!("window.document() returned None"))?; match document.query_selector(canvas_selector) { Ok(Some(canvas)) => Ok(canvas), Ok(None) => Err(anyhow!( "Failed to find element with selector '{canvas_selector}'" )), Err(err) => Err(anyhow!("JavaScript threw an Error: {err:?}")), } } macro_rules! get_buffer { ($buffer:ident) => { $buffer.get_or_init(|| { ::parking_lot::ReentrantMutex::new(::std::cell::RefCell::new( ::core::option::Option::None, )) }) }; } macro_rules! write_message { ($buffer:ident, $expr:expr) => {{ let buffer = crate::wasm::get_buffer!($buffer); if let Some(lock) = buffer.try_lock() { *lock.borrow_mut() = Some($expr); } }}; } macro_rules! read_buffer { ($buffer:ident, |$msg:ident| $stmt:stmt) => {{ let buffer = crate::wasm::get_buffer!($buffer); if let Some(lock) = buffer.try_lock() { if let Some($msg) = lock.borrow_mut().take() { $stmt } } }}; ($buffer:ident, |_| $stmt:stmt) => {{ let buffer = crate::wasm::get_buffer!($buffer); if let Some(lock) = buffer.try_lock() { if lock.borrow_mut().take().is_some() { $stmt } } }}; ($buffer:ident) => {{ let buffer = crate::wasm::get_buffer!($buffer); buffer.try_lock().and_then(|lock| lock.borrow_mut().take()) }}; } pub(crate) use get_buffer; pub(crate) use read_buffer; pub(crate) use write_message;