diff --git a/crates/bevy_window/src/window.rs b/crates/bevy_window/src/window.rs index 43d2ec7fb3701..8c3c33b001224 100644 --- a/crates/bevy_window/src/window.rs +++ b/crates/bevy_window/src/window.rs @@ -169,7 +169,7 @@ pub enum AbstractWindowHandle { /// [`ExtractedWindow`](https://docs.rs/bevy/*/bevy/render/view/struct.ExtractedWindow.html). Virtual, #[cfg(target_arch = "wasm32")] - HtmlCanvas(web_sys::HtmlCanvasElement), + HtmlCanvas(HtmlCanvasElement), #[cfg(target_arch = "wasm32")] OffscreenCanvas(web_sys::OffscreenCanvas), } @@ -290,6 +290,7 @@ pub struct Window { window_handle: AbstractWindowHandle, focused: bool, mode: WindowMode, + #[cfg(target_arch = "wasm32")] fit_canvas_to_parent: bool, command_queue: Vec, } @@ -420,6 +421,7 @@ impl Window { )), focused: true, mode: window_descriptor.mode, + #[cfg(target_arch = "wasm32")] fit_canvas_to_parent: window_descriptor.fit_canvas_to_parent, command_queue: Vec::new(), } @@ -457,6 +459,7 @@ impl Window { window_handle: AbstractWindowHandle::Virtual, focused: true, mode: window_descriptor.mode, + #[cfg(target_arch = "wasm32")] fit_canvas_to_parent: window_descriptor.fit_canvas_to_parent, command_queue: Vec::new(), } @@ -948,6 +951,7 @@ impl Window { /// feature, ensure the parent's size is not affected by its children. /// /// This value has no effect on non-web platforms. + #[cfg(target_arch = "wasm32")] #[inline] pub fn fit_canvas_to_parent(&self) -> bool { self.fit_canvas_to_parent @@ -986,6 +990,15 @@ pub enum MonitorSelection { Index(usize), } +#[cfg(target_arch = "wasm32")] +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct SendSyncCanvas(pub HtmlCanvasElement); + +#[cfg(target_arch = "wasm32")] +unsafe impl Send for SendSyncCanvas {} +#[cfg(target_arch = "wasm32")] +unsafe impl Sync for SendSyncCanvas {} + /// Describes the information needed for creating a window. /// /// This should be set up before adding the [`WindowPlugin`](crate::WindowPlugin). @@ -1056,6 +1069,8 @@ pub struct WindowDescriptor { /// macOS X transparent works with winit out of the box, so this issue might be related to: /// Windows 11 is related to pub transparent: bool, + #[cfg(target_arch = "wasm32")] + pub canvas: Option, /// Whether or not to fit the canvas element's size to its parent element's size. /// /// **Warning**: this will not behave as expected for parents that set their size according to the size of their @@ -1063,6 +1078,7 @@ pub struct WindowDescriptor { /// feature, ensure the parent's size is not affected by its children. /// /// This value has no effect on non-web platforms. + #[cfg(target_arch = "wasm32")] pub fit_canvas_to_parent: bool, } @@ -1083,7 +1099,33 @@ impl Default for WindowDescriptor { cursor_visible: true, mode: WindowMode::Windowed, transparent: false, + #[cfg(target_arch = "wasm32")] + canvas: None, + #[cfg(target_arch = "wasm32")] fit_canvas_to_parent: false, } } } + +#[cfg(target_arch = "wasm32")] +impl WindowDescriptor { + pub fn set_canvas_from_selector( + &mut self, + selector: &str, + ) -> Result { + Ok(web_sys::window() + .unwrap() + .document() + .unwrap() + .query_selector(selector)? + .and_then(|element| element.dyn_into().ok()) + .map(|canvas| { + self.canvas = Some(SendSyncCanvas(canvas)); + true + }) + .unwrap_or(false)) + } + pub fn set_canvas(&mut self, canvas: HtmlCanvasElement) { + self.canvas = Some(SendSyncCanvas(canvas)); + } +} diff --git a/crates/bevy_winit/src/lib.rs b/crates/bevy_winit/src/lib.rs index 95e7f69e70953..a25a8a4ac1b15 100644 --- a/crates/bevy_winit/src/lib.rs +++ b/crates/bevy_winit/src/lib.rs @@ -23,10 +23,12 @@ use bevy_utils::{ tracing::{error, info, trace, warn}, Instant, }; +#[cfg(target_arch = "wasm32")] +use bevy_window::SendSyncCanvas; use bevy_window::{ - AbstractWindowHandle, CreateWindow, CursorEntered, CursorLeft, CursorMoved, FileDragAndDrop, - ModifiesWindows, ReceivedCharacter, RequestRedraw, WindowBackendScaleFactorChanged, - WindowCloseRequested, WindowClosed, WindowCreated, WindowFocused, WindowMoved, WindowResized, + CreateWindow, CursorEntered, CursorLeft, CursorMoved, FileDragAndDrop, ModifiesWindows, + ReceivedCharacter, RequestRedraw, WindowBackendScaleFactorChanged, WindowCloseRequested, + WindowClosed, WindowCreated, WindowFocused, WindowMoved, WindowResized, WindowScaleFactorChanged, Windows, }; @@ -686,6 +688,22 @@ fn handle_create_window_events( #[cfg(not(any(target_os = "windows", target_feature = "x11")))] let mut window_resized_events = world.resource_mut::>(); for create_window_event in create_window_event_reader.iter(&create_window_events) { + #[cfg(target_arch = "wasm32")] + let window = if let Some(SendSyncCanvas(canvas)) = &create_window_event.descriptor.canvas { + winit_windows.create_window_with_canvas( + event_loop, + create_window_event.id, + &create_window_event.descriptor, + canvas.clone(), + ) + } else { + winit_windows.create_window( + event_loop, + create_window_event.id, + &create_window_event.descriptor, + ) + }; + #[cfg(not(target_arch = "wasm32"))] let window = winit_windows.create_window( event_loop, create_window_event.id, @@ -709,7 +727,7 @@ fn handle_create_window_events( { let channel = world.resource_mut::(); if create_window_event.descriptor.fit_canvas_to_parent { - if let AbstractWindowHandle::HtmlCanvas(canvas) = window.window_handle { + if let Some(SendSyncCanvas(canvas)) = &create_window_event.descriptor.canvas { // PROBLEM: this path is unreachable, because we're always creating the window // based on the raw window handle above. channel.listen_to_element(create_window_event.id, canvas.clone()); diff --git a/crates/bevy_winit/src/web_resize.rs b/crates/bevy_winit/src/web_resize.rs index c666491378adc..baf3ca739221e 100644 --- a/crates/bevy_winit/src/web_resize.rs +++ b/crates/bevy_winit/src/web_resize.rs @@ -58,7 +58,7 @@ impl Default for CanvasParentResizeEventChannel { } } -fn get_size_element(element: &HtmlCanvasElement) -> Option> { +fn get_size_element(element: &web_sys::HtmlCanvasElement) -> Option> { let parent_element = element.parent_element()?; let rect = parent_element.get_bounding_client_rect(); return Some(winit::dpi::LogicalSize::new( @@ -91,7 +91,11 @@ impl CanvasParentResizeEventChannel { closure.forget(); } - pub(crate) fn listen_to_element(&self, window_id: WindowId, element: HtmlCanvasElement) { + pub(crate) fn listen_to_element( + &self, + window_id: WindowId, + element: web_sys::HtmlCanvasElement, + ) { let sender = self.sender.clone(); let resize = move || { if let Some(size) = get_size_element(&element) { diff --git a/crates/bevy_winit/src/winit_windows.rs b/crates/bevy_winit/src/winit_windows.rs index a447b828019c7..9c7b0d0c265f2 100644 --- a/crates/bevy_winit/src/winit_windows.rs +++ b/crates/bevy_winit/src/winit_windows.rs @@ -25,9 +25,16 @@ impl WinitWindows { window_id: WindowId, window_descriptor: &WindowDescriptor, ) -> Window { - Self::create_window_internal(event_loop, window_id, window_descriptor, None) + #[cfg(target_arch = "wasm32")] + { + self.create_window_internal(event_loop, window_id, window_descriptor, None) + } + #[cfg(not(target_arch = "wasm32"))] + { + self.create_window_internal(event_loop, window_id, window_descriptor) + } } - #[cfg(target_wasm = "wasm32")] + #[cfg(target_arch = "wasm32")] pub fn create_window_with_canvas( &mut self, event_loop: &winit::event_loop::EventLoopWindowTarget<()>, @@ -35,9 +42,9 @@ impl WinitWindows { window_descriptor: &WindowDescriptor, canvas: web_sys::HtmlCanvasElement, ) -> Window { - Self::create_window_internal(event_loop, window_id, window_descriptor, Some(canvas)) + self.create_window_internal(event_loop, window_id, window_descriptor, Some(canvas)) } - #[cfg(target_wasm = "wasm32")] + #[cfg(target_arch = "wasm32")] pub fn create_window_with_selector( &mut self, event_loop: &winit::event_loop::EventLoopWindowTarget<()>, @@ -52,7 +59,7 @@ impl WinitWindows { let canvas = document.query_selector(selector)?; Ok(canvas.map(|canvas| { let canvas = canvas.dyn_into::().ok(); - Self::create_window_internal(event_loop, window_id, window_descriptor, canvas) + self.create_window_internal(event_loop, window_id, window_descriptor, canvas) })) } fn create_window_internal( @@ -60,7 +67,7 @@ impl WinitWindows { event_loop: &winit::event_loop::EventLoopWindowTarget<()>, window_id: WindowId, window_descriptor: &WindowDescriptor, - #[cfg(target_wasm = "wasm32")] canvas: Option, + #[cfg(target_arch = "wasm32")] canvas: Option, ) -> Window { let mut winit_window_builder = winit::window::WindowBuilder::new(); @@ -130,10 +137,10 @@ impl WinitWindows { #[allow(unused_mut)] let mut winit_window_builder = winit_window_builder.with_title(&window_descriptor.title); - #[cfg(target_wasm = "wasm32")] + #[cfg(target_arch = "wasm32")] if let Some(canvas) = canvas { use winit::platform::web::WindowBuilderExtWebSys; - winit_window_builder = winit_window_builder.with_canvas(canvas); + winit_window_builder = winit_window_builder.with_canvas(Some(canvas)); } let winit_window = winit_window_builder.build(event_loop).unwrap();