From 2645524f88414393d8b3ca9c6fe801b32b5ebd33 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A9ctor=20Ram=C3=B3n=20Jim=C3=A9nez?= Date: Tue, 7 May 2024 15:50:18 +0200 Subject: [PATCH 1/2] Update `winit` to `0.30` --- .github/workflows/check.yml | 2 - Cargo.toml | 4 +- examples/integration/README.md | 19 +- examples/integration/index.html | 21 - examples/integration/src/main.rs | 577 ++++++++++++----------- src/application.rs | 19 +- wgpu/src/lib.rs | 8 +- winit/Cargo.toml | 2 + winit/src/application.rs | 383 ++++++++++----- winit/src/conversion.rs | 58 ++- winit/src/multi_window.rs | 428 ++++++++++------- winit/src/multi_window/window_manager.rs | 3 +- 12 files changed, 891 insertions(+), 633 deletions(-) delete mode 100644 examples/integration/index.html diff --git a/.github/workflows/check.yml b/.github/workflows/check.yml index ceac39e7a9..8f5e65d628 100644 --- a/.github/workflows/check.yml +++ b/.github/workflows/check.yml @@ -17,8 +17,6 @@ jobs: run: cargo build --package tour --target wasm32-unknown-unknown - name: Check compilation of `todos` example run: cargo build --package todos --target wasm32-unknown-unknown - - name: Check compilation of `integration` example - run: cargo build --package integration --target wasm32-unknown-unknown widget: runs-on: ubuntu-latest diff --git a/Cargo.toml b/Cargo.toml index 60773da01d..06f7d19858 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -172,11 +172,11 @@ unicode-segmentation = "1.0" wasm-bindgen-futures = "0.4" wasm-timer = "0.2" web-sys = "=0.3.67" -web-time = "0.2" +web-time = "1.1" wgpu = "0.19" winapi = "0.3" window_clipboard = "0.4.1" -winit = { git = "https://github.com/iced-rs/winit.git", rev = "592bd152f6d5786fae7d918532d7db752c0d164f" } +winit = { git = "https://github.com/iced-rs/winit.git", rev = "8affa522bc6dcc497d332a28c03491d22a22f5a7" } [workspace.lints.rust] rust_2018_idioms = "forbid" diff --git a/examples/integration/README.md b/examples/integration/README.md index aa3a6e94ad..bac0640cd5 100644 --- a/examples/integration/README.md +++ b/examples/integration/README.md @@ -10,25 +10,8 @@ The __[`main`]__ file contains all the code of the example. You can run it with `cargo run`: ``` -cargo run --package integration_wgpu +cargo run --package integration ``` -### How to run this example with WebGL backend -NOTE: Currently, WebGL backend is still experimental, so expect bugs. - -```sh -# 0. Install prerequisites -cargo install wasm-bindgen-cli https -# 1. cd to the current folder -# 2. Compile wasm module -cargo build -p integration_wgpu --target wasm32-unknown-unknown -# 3. Invoke wasm-bindgen -wasm-bindgen ../../target/wasm32-unknown-unknown/debug/integration_wgpu.wasm --out-dir . --target web --no-typescript -# 4. run http server -http -# 5. Open 127.0.0.1:8000 in browser -``` - - [`main`]: src/main.rs [`wgpu`]: https://github.com/gfx-rs/wgpu diff --git a/examples/integration/index.html b/examples/integration/index.html deleted file mode 100644 index 920bc4a063..0000000000 --- a/examples/integration/index.html +++ /dev/null @@ -1,21 +0,0 @@ - - - - - Iced - wgpu + WebGL integration - - -

integration_wgpu

- - - - - diff --git a/examples/integration/src/main.rs b/examples/integration/src/main.rs index c28332100d..e1c7d62fbc 100644 --- a/examples/integration/src/main.rs +++ b/examples/integration/src/main.rs @@ -18,305 +18,342 @@ use iced_winit::winit; use iced_winit::Clipboard; use winit::{ - event::{Event, WindowEvent}, + event::WindowEvent, event_loop::{ControlFlow, EventLoop}, keyboard::ModifiersState, }; use std::sync::Arc; -#[cfg(target_arch = "wasm32")] -use wasm_bindgen::JsCast; -#[cfg(target_arch = "wasm32")] -use web_sys::HtmlCanvasElement; -#[cfg(target_arch = "wasm32")] -use winit::platform::web::WindowBuilderExtWebSys; - -pub fn main() -> Result<(), Box> { - #[cfg(target_arch = "wasm32")] - let canvas_element = { - console_log::init().expect("Initialize logger"); - - std::panic::set_hook(Box::new(console_error_panic_hook::hook)); - - web_sys::window() - .and_then(|win| win.document()) - .and_then(|doc| doc.get_element_by_id("iced_canvas")) - .and_then(|element| element.dyn_into::().ok()) - .expect("Get canvas element") - }; - - #[cfg(not(target_arch = "wasm32"))] +pub fn main() -> Result<(), winit::error::EventLoopError> { tracing_subscriber::fmt::init(); // Initialize winit let event_loop = EventLoop::new()?; - #[cfg(target_arch = "wasm32")] - let window = winit::window::WindowBuilder::new() - .with_canvas(Some(canvas_element)) - .build(&event_loop)?; - - #[cfg(not(target_arch = "wasm32"))] - let window = winit::window::Window::new(&event_loop)?; - - let window = Arc::new(window); - - let physical_size = window.inner_size(); - let mut viewport = Viewport::with_physical_size( - Size::new(physical_size.width, physical_size.height), - window.scale_factor(), - ); - let mut cursor_position = None; - let mut modifiers = ModifiersState::default(); - let mut clipboard = Clipboard::connect(&window); - - // Initialize wgpu - #[cfg(target_arch = "wasm32")] - let default_backend = wgpu::Backends::GL; - #[cfg(not(target_arch = "wasm32"))] - let default_backend = wgpu::Backends::PRIMARY; - - let backend = - wgpu::util::backend_bits_from_env().unwrap_or(default_backend); - - let instance = wgpu::Instance::new(wgpu::InstanceDescriptor { - backends: backend, - ..Default::default() - }); - let surface = instance.create_surface(window.clone())?; - - let (format, adapter, device, queue) = - futures::futures::executor::block_on(async { - let adapter = wgpu::util::initialize_adapter_from_env_or_default( - &instance, - Some(&surface), - ) - .await - .expect("Create adapter"); - - let adapter_features = adapter.features(); - - #[cfg(target_arch = "wasm32")] - let needed_limits = wgpu::Limits::downlevel_webgl2_defaults() - .using_resolution(adapter.limits()); - - #[cfg(not(target_arch = "wasm32"))] - let needed_limits = wgpu::Limits::default(); - - let capabilities = surface.get_capabilities(&adapter); - - let (device, queue) = adapter - .request_device( - &wgpu::DeviceDescriptor { - label: None, - required_features: adapter_features - & wgpu::Features::default(), - required_limits: needed_limits, + #[allow(clippy::large_enum_variant)] + enum Runner { + Loading, + Ready { + window: Arc, + device: wgpu::Device, + queue: wgpu::Queue, + surface: wgpu::Surface<'static>, + format: wgpu::TextureFormat, + engine: Engine, + renderer: Renderer, + scene: Scene, + state: program::State, + cursor_position: Option>, + clipboard: Clipboard, + viewport: Viewport, + modifiers: ModifiersState, + resized: bool, + debug: Debug, + }, + } + + impl winit::application::ApplicationHandler for Runner { + fn resumed(&mut self, event_loop: &winit::event_loop::ActiveEventLoop) { + if let Self::Loading = self { + let window = Arc::new( + event_loop + .create_window( + winit::window::WindowAttributes::default(), + ) + .expect("Create window"), + ); + + let physical_size = window.inner_size(); + let viewport = Viewport::with_physical_size( + Size::new(physical_size.width, physical_size.height), + window.scale_factor(), + ); + let clipboard = Clipboard::connect(&window); + + let backend = + wgpu::util::backend_bits_from_env().unwrap_or_default(); + + let instance = wgpu::Instance::new(wgpu::InstanceDescriptor { + backends: backend, + ..Default::default() + }); + let surface = instance + .create_surface(window.clone()) + .expect("Create window surface"); + + let (format, adapter, device, queue) = + futures::futures::executor::block_on(async { + let adapter = + wgpu::util::initialize_adapter_from_env_or_default( + &instance, + Some(&surface), + ) + .await + .expect("Create adapter"); + + let adapter_features = adapter.features(); + + let capabilities = surface.get_capabilities(&adapter); + + let (device, queue) = adapter + .request_device( + &wgpu::DeviceDescriptor { + label: None, + required_features: adapter_features + & wgpu::Features::default(), + required_limits: wgpu::Limits::default(), + }, + None, + ) + .await + .expect("Request device"); + + ( + capabilities + .formats + .iter() + .copied() + .find(wgpu::TextureFormat::is_srgb) + .or_else(|| { + capabilities.formats.first().copied() + }) + .expect("Get preferred format"), + adapter, + device, + queue, + ) + }); + + surface.configure( + &device, + &wgpu::SurfaceConfiguration { + usage: wgpu::TextureUsages::RENDER_ATTACHMENT, + format, + width: physical_size.width, + height: physical_size.height, + present_mode: wgpu::PresentMode::AutoVsync, + alpha_mode: wgpu::CompositeAlphaMode::Auto, + view_formats: vec![], + desired_maximum_frame_latency: 2, }, - None, - ) - .await - .expect("Request device"); - - ( - capabilities - .formats - .iter() - .copied() - .find(wgpu::TextureFormat::is_srgb) - .or_else(|| capabilities.formats.first().copied()) - .expect("Get preferred format"), - adapter, + ); + + // Initialize scene and GUI controls + let scene = Scene::new(&device, format); + let controls = Controls::new(); + + // Initialize iced + let mut debug = Debug::new(); + let engine = + Engine::new(&adapter, &device, &queue, format, None); + let mut renderer = Renderer::new( + &device, + &engine, + Font::default(), + Pixels::from(16), + ); + + let state = program::State::new( + controls, + viewport.logical_size(), + &mut renderer, + &mut debug, + ); + + // You should change this if you want to render continuously + event_loop.set_control_flow(ControlFlow::Wait); + + *self = Self::Ready { + window, + device, + queue, + surface, + format, + engine, + renderer, + scene, + state, + cursor_position: None, + modifiers: ModifiersState::default(), + clipboard, + viewport, + resized: false, + debug, + }; + } + } + + fn window_event( + &mut self, + event_loop: &winit::event_loop::ActiveEventLoop, + _window_id: winit::window::WindowId, + event: WindowEvent, + ) { + let Self::Ready { + window, device, queue, - ) - }); - - surface.configure( - &device, - &wgpu::SurfaceConfiguration { - usage: wgpu::TextureUsages::RENDER_ATTACHMENT, - format, - width: physical_size.width, - height: physical_size.height, - present_mode: wgpu::PresentMode::AutoVsync, - alpha_mode: wgpu::CompositeAlphaMode::Auto, - view_formats: vec![], - desired_maximum_frame_latency: 2, - }, - ); - - let mut resized = false; - - // Initialize scene and GUI controls - let scene = Scene::new(&device, format); - let controls = Controls::new(); - - // Initialize iced - let mut debug = Debug::new(); - let mut engine = Engine::new(&adapter, &device, &queue, format, None); - let mut renderer = - Renderer::new(&device, &engine, Font::default(), Pixels::from(16)); - - let mut state = program::State::new( - controls, - viewport.logical_size(), - &mut renderer, - &mut debug, - ); - - // Run event loop - event_loop.run(move |event, window_target| { - // You should change this if you want to render continuously - window_target.set_control_flow(ControlFlow::Wait); - - match event { - Event::WindowEvent { - event: WindowEvent::RedrawRequested, - .. - } => { - if resized { - let size = window.inner_size(); - - viewport = Viewport::with_physical_size( - Size::new(size.width, size.height), - window.scale_factor(), - ); - - surface.configure( - &device, - &wgpu::SurfaceConfiguration { - format, - usage: wgpu::TextureUsages::RENDER_ATTACHMENT, - width: size.width, - height: size.height, - present_mode: wgpu::PresentMode::AutoVsync, - alpha_mode: wgpu::CompositeAlphaMode::Auto, - view_formats: vec![], - desired_maximum_frame_latency: 2, - }, - ); - - resized = false; - } - - match surface.get_current_texture() { - Ok(frame) => { - let mut encoder = device.create_command_encoder( - &wgpu::CommandEncoderDescriptor { label: None }, + surface, + format, + engine, + renderer, + scene, + state, + viewport, + cursor_position, + modifiers, + clipboard, + resized, + debug, + } = self + else { + return; + }; + + match event { + WindowEvent::RedrawRequested => { + if *resized { + let size = window.inner_size(); + + *viewport = Viewport::with_physical_size( + Size::new(size.width, size.height), + window.scale_factor(), ); - let program = state.program(); - - let view = frame.texture.create_view( - &wgpu::TextureViewDescriptor::default(), + surface.configure( + device, + &wgpu::SurfaceConfiguration { + format: *format, + usage: wgpu::TextureUsages::RENDER_ATTACHMENT, + width: size.width, + height: size.height, + present_mode: wgpu::PresentMode::AutoVsync, + alpha_mode: wgpu::CompositeAlphaMode::Auto, + view_formats: vec![], + desired_maximum_frame_latency: 2, + }, ); - { - // We clear the frame - let mut render_pass = Scene::clear( - &view, - &mut encoder, - program.background_color(), + *resized = false; + } + + match surface.get_current_texture() { + Ok(frame) => { + let mut encoder = device.create_command_encoder( + &wgpu::CommandEncoderDescriptor { label: None }, ); - // Draw the scene - scene.draw(&mut render_pass); - } + let program = state.program(); - // And then iced on top - renderer.present( - &mut engine, - &device, - &queue, - &mut encoder, - None, - frame.texture.format(), - &view, - &viewport, - &debug.overlay(), - ); + let view = frame.texture.create_view( + &wgpu::TextureViewDescriptor::default(), + ); + + { + // We clear the frame + let mut render_pass = Scene::clear( + &view, + &mut encoder, + program.background_color(), + ); + + // Draw the scene + scene.draw(&mut render_pass); + } + + // And then iced on top + renderer.present( + engine, + device, + queue, + &mut encoder, + None, + frame.texture.format(), + &view, + viewport, + &debug.overlay(), + ); - // Then we submit the work - queue.submit(Some(encoder.finish())); - frame.present(); + // Then we submit the work + engine.submit(queue, encoder); + frame.present(); - // Update the mouse cursor - window.set_cursor_icon( - iced_winit::conversion::mouse_interaction( - state.mouse_interaction(), - ), - ); - } - Err(error) => match error { - wgpu::SurfaceError::OutOfMemory => { - panic!( - "Swapchain error: {error}. \ - Rendering cannot continue." - ) - } - _ => { - // Try rendering again next frame. - window.request_redraw(); + // Update the mouse cursor + window.set_cursor( + iced_winit::conversion::mouse_interaction( + state.mouse_interaction(), + ), + ); } - }, - } - } - Event::WindowEvent { event, .. } => { - match event { - WindowEvent::CursorMoved { position, .. } => { - cursor_position = Some(position); - } - WindowEvent::ModifiersChanged(new_modifiers) => { - modifiers = new_modifiers.state(); - } - WindowEvent::Resized(_) => { - resized = true; - } - WindowEvent::CloseRequested => { - window_target.exit(); + Err(error) => match error { + wgpu::SurfaceError::OutOfMemory => { + panic!( + "Swapchain error: {error}. \ + Rendering cannot continue." + ) + } + _ => { + // Try rendering again next frame. + window.request_redraw(); + } + }, } - _ => {} } - - // Map window event to iced event - if let Some(event) = iced_winit::conversion::window_event( - window::Id::MAIN, - event, - window.scale_factor(), - modifiers, - ) { - state.queue_event(event); + WindowEvent::CursorMoved { position, .. } => { + *cursor_position = Some(position); + } + WindowEvent::ModifiersChanged(new_modifiers) => { + *modifiers = new_modifiers.state(); } + WindowEvent::Resized(_) => { + *resized = true; + } + WindowEvent::CloseRequested => { + event_loop.exit(); + } + _ => {} } - _ => {} - } - // If there are events pending - if !state.is_queue_empty() { - // We update iced - let _ = state.update( - viewport.logical_size(), - cursor_position - .map(|p| { - conversion::cursor_position(p, viewport.scale_factor()) - }) - .map(mouse::Cursor::Available) - .unwrap_or(mouse::Cursor::Unavailable), - &mut renderer, - &Theme::Dark, - &renderer::Style { - text_color: Color::WHITE, - }, - &mut clipboard, - &mut debug, - ); - - // and request a redraw - window.request_redraw(); + // Map window event to iced event + if let Some(event) = iced_winit::conversion::window_event( + window::Id::MAIN, + event, + window.scale_factor(), + *modifiers, + ) { + state.queue_event(event); + } + + // If there are events pending + if !state.is_queue_empty() { + // We update iced + let _ = state.update( + viewport.logical_size(), + cursor_position + .map(|p| { + conversion::cursor_position( + p, + viewport.scale_factor(), + ) + }) + .map(mouse::Cursor::Available) + .unwrap_or(mouse::Cursor::Unavailable), + renderer, + &Theme::Dark, + &renderer::Style { + text_color: Color::WHITE, + }, + clipboard, + debug, + ); + + // and request a redraw + window.request_redraw(); + } } - })?; + } - Ok(()) + let mut runner = Runner::Loading; + event_loop.run_app(&mut runner) } diff --git a/src/application.rs b/src/application.rs index 9197834b23..d12ba73dcc 100644 --- a/src/application.rs +++ b/src/application.rs @@ -212,26 +212,11 @@ where ..crate::graphics::Settings::default() }; - let run = crate::shell::application::run::< + Ok(crate::shell::application::run::< Instance, Self::Executor, ::Compositor, - >(settings.into(), renderer_settings); - - #[cfg(target_arch = "wasm32")] - { - use crate::futures::FutureExt; - use iced_futures::backend::wasm::wasm_bindgen::Executor; - - Executor::new() - .expect("Create Wasm executor") - .spawn(run.map(|_| ())); - - Ok(()) - } - - #[cfg(not(target_arch = "wasm32"))] - Ok(crate::futures::executor::block_on(run)?) + >(settings.into(), renderer_settings)?) } } diff --git a/wgpu/src/lib.rs b/wgpu/src/lib.rs index ad8ac59195..1fbdbe9a3d 100644 --- a/wgpu/src/lib.rs +++ b/wgpu/src/lib.rs @@ -67,8 +67,6 @@ use crate::core::{ use crate::graphics::text::{Editor, Paragraph}; use crate::graphics::Viewport; -use std::cell::RefCell; - /// A [`wgpu`] graphics renderer for [`iced`]. /// /// [`wgpu`]: https://github.com/gfx-rs/wgpu-rs @@ -84,7 +82,7 @@ pub struct Renderer { // TODO: Centralize all the image feature handling #[cfg(any(feature = "svg", feature = "image"))] - image_cache: RefCell, + image_cache: std::cell::RefCell, } impl Renderer { @@ -103,7 +101,9 @@ impl Renderer { text_storage: text::Storage::new(), #[cfg(any(feature = "svg", feature = "image"))] - image_cache: RefCell::new(_engine.create_image_cache(_device)), + image_cache: std::cell::RefCell::new( + _engine.create_image_cache(_device), + ), } } diff --git a/winit/Cargo.toml b/winit/Cargo.toml index dccb7c07f0..6d3dddde8e 100644 --- a/winit/Cargo.toml +++ b/winit/Cargo.toml @@ -25,6 +25,7 @@ wayland-csd-adwaita = ["winit/wayland-csd-adwaita"] multi-window = ["iced_runtime/multi-window"] [dependencies] +iced_futures.workspace = true iced_graphics.workspace = true iced_runtime.workspace = true @@ -32,6 +33,7 @@ log.workspace = true rustc-hash.workspace = true thiserror.workspace = true tracing.workspace = true +wasm-bindgen-futures.workspace = true window_clipboard.workspace = true winit.workspace = true diff --git a/winit/src/application.rs b/winit/src/application.rs index a447c9da05..e056f4c554 100644 --- a/winit/src/application.rs +++ b/winit/src/application.rs @@ -22,7 +22,9 @@ use crate::runtime::{Command, Debug}; use crate::{Clipboard, Error, Proxy, Settings}; use futures::channel::mpsc; +use futures::channel::oneshot; +use std::borrow::Cow; use std::mem::ManuallyDrop; use std::sync::Arc; @@ -129,7 +131,7 @@ pub fn default(theme: &Theme) -> Appearance { /// Runs an [`Application`] with an executor, compositor, and the provided /// settings. -pub async fn run( +pub fn run( settings: Settings, graphics_settings: graphics::Settings, ) -> Result<(), Error> @@ -141,12 +143,12 @@ where { use futures::task; use futures::Future; - use winit::event_loop::EventLoopBuilder; + use winit::event_loop::EventLoop; let mut debug = Debug::new(); debug.startup_started(); - let event_loop = EventLoopBuilder::with_user_event() + let event_loop = EventLoop::with_user_event() .build() .expect("Create event loop"); @@ -165,102 +167,267 @@ where runtime.enter(|| A::new(flags)) }; - #[cfg(target_arch = "wasm32")] - let target = settings.window.platform_specific.target.clone(); + let id = settings.id; + let title = application.title(); - let should_be_visible = settings.window.visible; - let exit_on_close_request = settings.window.exit_on_close_request; + let (boot_sender, boot_receiver) = oneshot::channel(); + let (event_sender, event_receiver) = mpsc::unbounded(); + let (control_sender, control_receiver) = mpsc::unbounded(); - let builder = conversion::window_settings( - settings.window, - &application.title(), - event_loop.primary_monitor(), - settings.id, - ) - .with_visible(false); + let instance = Box::pin(run_instance::( + application, + runtime, + proxy, + debug, + boot_receiver, + event_receiver, + control_sender, + init_command, + settings.fonts, + )); - log::debug!("Window builder: {builder:#?}"); + let context = task::Context::from_waker(task::noop_waker_ref()); + + struct Runner { + instance: std::pin::Pin>, + context: task::Context<'static>, + boot: Option>, + sender: mpsc::UnboundedSender>, + receiver: mpsc::UnboundedReceiver, + error: Option, + #[cfg(target_arch = "wasm32")] + is_booted: std::rc::Rc>, + #[cfg(target_arch = "wasm32")] + queued_events: Vec>, + } - let window = Arc::new( - builder - .build(&event_loop) - .map_err(Error::WindowCreationFailed)?, - ); + struct BootConfig { + sender: oneshot::Sender>, + id: Option, + title: String, + window_settings: window::Settings, + graphics_settings: graphics::Settings, + } - #[cfg(target_arch = "wasm32")] + let runner = Runner { + instance, + context, + boot: Some(BootConfig { + sender: boot_sender, + id, + title, + window_settings: settings.window, + graphics_settings, + }), + sender: event_sender, + receiver: control_receiver, + error: None, + #[cfg(target_arch = "wasm32")] + is_booted: std::rc::Rc::new(std::cell::RefCell::new(false)), + #[cfg(target_arch = "wasm32")] + queued_events: Vec::new(), + }; + + impl winit::application::ApplicationHandler + for Runner + where + F: Future, + C: Compositor + 'static, { - use winit::platform::web::WindowExtWebSys; + fn resumed(&mut self, event_loop: &winit::event_loop::ActiveEventLoop) { + let Some(BootConfig { + sender, + id, + title, + window_settings, + graphics_settings, + }) = self.boot.take() + else { + return; + }; - let canvas = window.canvas().expect("Get window canvas"); - let _ = canvas.set_attribute( - "style", - "display: block; width: 100%; height: 100%", - ); + let should_be_visible = window_settings.visible; + let exit_on_close_request = window_settings.exit_on_close_request; + + #[cfg(target_arch = "wasm32")] + let target = window_settings.platform_specific.target.clone(); + + let window_attributes = conversion::window_attributes( + window_settings, + &title, + event_loop.primary_monitor(), + id, + ) + .with_visible(false); + + log::debug!("Window attributes: {window_attributes:#?}"); + + let window = match event_loop.create_window(window_attributes) { + Ok(window) => Arc::new(window), + Err(error) => { + self.error = Some(Error::WindowCreationFailed(error)); + event_loop.exit(); + return; + } + }; + + let finish_boot = { + let window = window.clone(); - let window = web_sys::window().unwrap(); - let document = window.document().unwrap(); - let body = document.body().unwrap(); - - let target = target.and_then(|target| { - body.query_selector(&format!("#{target}")) - .ok() - .unwrap_or(None) - }); - - match target { - Some(node) => { - let _ = node - .replace_with_with_node_1(&canvas) - .expect(&format!("Could not replace #{}", node.id())); + async move { + let compositor = + C::new(graphics_settings, window.clone()).await?; + + sender + .send(Boot { + window, + compositor, + should_be_visible, + exit_on_close_request, + }) + .ok() + .expect("Send boot event"); + + Ok::<_, graphics::Error>(()) + } + }; + + #[cfg(not(target_arch = "wasm32"))] + if let Err(error) = futures::executor::block_on(finish_boot) { + self.error = Some(Error::GraphicsCreationFailed(error)); + event_loop.exit(); } - None => { - let _ = body - .append_child(&canvas) - .expect("Append canvas to HTML body"); + + #[cfg(target_arch = "wasm32")] + { + use winit::platform::web::WindowExtWebSys; + + let canvas = window.canvas().expect("Get window canvas"); + let _ = canvas.set_attribute( + "style", + "display: block; width: 100%; height: 100%", + ); + + let window = web_sys::window().unwrap(); + let document = window.document().unwrap(); + let body = document.body().unwrap(); + + let target = target.and_then(|target| { + body.query_selector(&format!("#{target}")) + .ok() + .unwrap_or(None) + }); + + match target { + Some(node) => { + let _ = node.replace_with_with_node_1(&canvas).expect( + &format!("Could not replace #{}", node.id()), + ); + } + None => { + let _ = body + .append_child(&canvas) + .expect("Append canvas to HTML body"); + } + }; + + let is_booted = self.is_booted.clone(); + + wasm_bindgen_futures::spawn_local(async move { + finish_boot.await.expect("Finish boot!"); + + *is_booted.borrow_mut() = true; + }); } - }; - } + } + + fn window_event( + &mut self, + event_loop: &winit::event_loop::ActiveEventLoop, + window_id: winit::window::WindowId, + event: winit::event::WindowEvent, + ) { + #[cfg(target_os = "windows")] + let is_move_or_resize = matches!( + event, + winit::event::WindowEvent::Resized(_) + | winit::event::WindowEvent::Moved(_) + ); + + self.process_event( + event_loop, + winit::event::Event::WindowEvent { window_id, event }, + ); + + // TODO: Remove when unnecessary + // On Windows, we emulate an `AboutToWait` event after every `Resized` event + // since the event loop does not resume during resize interaction. + // More details: https://github.com/rust-windowing/winit/issues/3272 + #[cfg(target_os = "windows")] + { + if is_move_or_resize { + self.process_event( + event_loop, + winit::event::Event::AboutToWait, + ); + } + } + } - let mut compositor = C::new(graphics_settings, window.clone()).await?; - let renderer = compositor.create_renderer(); + fn user_event( + &mut self, + event_loop: &winit::event_loop::ActiveEventLoop, + message: Message, + ) { + self.process_event( + event_loop, + winit::event::Event::UserEvent(message), + ); + } - for font in settings.fonts { - compositor.load_font(font); + fn about_to_wait( + &mut self, + event_loop: &winit::event_loop::ActiveEventLoop, + ) { + self.process_event(event_loop, winit::event::Event::AboutToWait); + } } - let (mut event_sender, event_receiver) = mpsc::unbounded(); - let (control_sender, mut control_receiver) = mpsc::unbounded(); - - let mut instance = Box::pin(run_instance::( - application, - compositor, - renderer, - runtime, - proxy, - debug, - event_receiver, - control_sender, - init_command, - window, - should_be_visible, - exit_on_close_request, - )); + impl Runner + where + F: Future, + { + fn process_event( + &mut self, + event_loop: &winit::event_loop::ActiveEventLoop, + event: winit::event::Event, + ) { + // On Wasm, events may start being processed before the compositor + // boots up. We simply queue them and process them once ready. + #[cfg(target_arch = "wasm32")] + if !*self.is_booted.borrow() { + self.queued_events.push(event); + return; + } else if !self.queued_events.is_empty() { + let queued_events = std::mem::take(&mut self.queued_events); - let mut context = task::Context::from_waker(task::noop_waker_ref()); + // This won't infinitely recurse, since we `mem::take` + for event in queued_events { + self.process_event(event_loop, event); + } + } - let process_event = - move |event, event_loop: &winit::event_loop::EventLoopWindowTarget<_>| { if event_loop.exiting() { return; } - event_sender.start_send(event).expect("Send event"); + self.sender.start_send(event).expect("Send event"); - let poll = instance.as_mut().poll(&mut context); + let poll = self.instance.as_mut().poll(&mut self.context); match poll { task::Poll::Pending => { - if let Ok(Some(flow)) = control_receiver.try_next() { + if let Ok(Some(flow)) = self.receiver.try_next() { event_loop.set_control_flow(flow); } } @@ -268,54 +435,45 @@ where event_loop.exit(); } } - }; + } + } - #[cfg(not(target_os = "windows"))] - let _ = event_loop.run(process_event); + #[cfg(not(target_arch = "wasm32"))] + { + let mut runner = runner; + let _ = event_loop.run_app(&mut runner); + + runner.error.map(Err).unwrap_or(Ok(())) + } - // TODO: Remove when unnecessary - // On Windows, we emulate an `AboutToWait` event after every `Resized` event - // since the event loop does not resume during resize interaction. - // More details: https://github.com/rust-windowing/winit/issues/3272 - #[cfg(target_os = "windows")] + #[cfg(target_arch = "wasm32")] { - let mut process_event = process_event; + use winit::platform::web::EventLoopExtWebSys; + let _ = event_loop.spawn_app(runner); - let _ = event_loop.run(move |event, event_loop| { - if matches!( - event, - winit::event::Event::WindowEvent { - event: winit::event::WindowEvent::Resized(_) - | winit::event::WindowEvent::Moved(_), - .. - } - ) { - process_event(event, event_loop); - process_event(winit::event::Event::AboutToWait, event_loop); - } else { - process_event(event, event_loop); - } - }); + Ok(()) } +} - Ok(()) +struct Boot { + window: Arc, + compositor: C, + should_be_visible: bool, + exit_on_close_request: bool, } async fn run_instance( mut application: A, - mut compositor: C, - mut renderer: A::Renderer, mut runtime: Runtime, A::Message>, mut proxy: Proxy, mut debug: Debug, + mut boot: oneshot::Receiver>, mut event_receiver: mpsc::UnboundedReceiver< winit::event::Event, >, mut control_sender: mpsc::UnboundedSender, init_command: Command, - window: Arc, - should_be_visible: bool, - exit_on_close_request: bool, + fonts: Vec>, ) where A: Application + 'static, E: Executor + 'static, @@ -326,6 +484,19 @@ async fn run_instance( use winit::event; use winit::event_loop::ControlFlow; + let Boot { + window, + mut compositor, + should_be_visible, + exit_on_close_request, + } = boot.try_recv().ok().flatten().expect("Receive boot"); + + let mut renderer = compositor.create_renderer(); + + for font in fonts { + compositor.load_font(font); + } + let mut state = State::new(&application, &window); let mut viewport_version = state.viewport_version(); let physical_size = state.physical_size(); @@ -480,7 +651,7 @@ async fn run_instance( debug.draw_finished(); if new_mouse_interaction != mouse_interaction { - window.set_cursor_icon(conversion::mouse_interaction( + window.set_cursor(conversion::mouse_interaction( new_mouse_interaction, )); diff --git a/winit/src/conversion.rs b/winit/src/conversion.rs index 0f83dac3cb..d04fc2f1e0 100644 --- a/winit/src/conversion.rs +++ b/winit/src/conversion.rs @@ -8,16 +8,16 @@ use crate::core::touch; use crate::core::window; use crate::core::{Event, Point, Size}; -/// Converts some [`window::Settings`] into a `WindowBuilder` from `winit`. -pub fn window_settings( +/// Converts some [`window::Settings`] into some `WindowAttributes` from `winit`. +pub fn window_attributes( settings: window::Settings, title: &str, primary_monitor: Option, _id: Option, -) -> winit::window::WindowBuilder { - let mut window_builder = winit::window::WindowBuilder::new(); +) -> winit::window::WindowAttributes { + let mut attributes = winit::window::WindowAttributes::default(); - window_builder = window_builder + attributes = attributes .with_title(title) .with_inner_size(winit::dpi::LogicalSize { width: settings.size.width, @@ -39,23 +39,21 @@ pub fn window_settings( if let Some(position) = position(primary_monitor.as_ref(), settings.size, settings.position) { - window_builder = window_builder.with_position(position); + attributes = attributes.with_position(position); } if let Some(min_size) = settings.min_size { - window_builder = - window_builder.with_min_inner_size(winit::dpi::LogicalSize { - width: min_size.width, - height: min_size.height, - }); + attributes = attributes.with_min_inner_size(winit::dpi::LogicalSize { + width: min_size.width, + height: min_size.height, + }); } if let Some(max_size) = settings.max_size { - window_builder = - window_builder.with_max_inner_size(winit::dpi::LogicalSize { - width: max_size.width, - height: max_size.height, - }); + attributes = attributes.with_max_inner_size(winit::dpi::LogicalSize { + width: max_size.width, + height: max_size.height, + }); } #[cfg(any( @@ -65,35 +63,33 @@ pub fn window_settings( target_os = "openbsd" ))] { - // `with_name` is available on both `WindowBuilderExtWayland` and `WindowBuilderExtX11` and they do - // exactly the same thing. We arbitrarily choose `WindowBuilderExtWayland` here. - use ::winit::platform::wayland::WindowBuilderExtWayland; + use ::winit::platform::wayland::WindowAttributesExtWayland; if let Some(id) = _id { - window_builder = window_builder.with_name(id.clone(), id); + attributes = attributes.with_name(id.clone(), id); } } #[cfg(target_os = "windows")] { - use winit::platform::windows::WindowBuilderExtWindows; + use winit::platform::windows::WindowAttributesExtWindows; #[allow(unsafe_code)] unsafe { - window_builder = window_builder + attributes = attributes .with_parent_window(settings.platform_specific.parent); } - window_builder = window_builder + attributes = attributes .with_drag_and_drop(settings.platform_specific.drag_and_drop); - window_builder = window_builder + attributes = attributes .with_skip_taskbar(settings.platform_specific.skip_taskbar); } #[cfg(target_os = "macos")] { - use winit::platform::macos::WindowBuilderExtMacOS; + use winit::platform::macos::WindowAttributesExtMacOS; - window_builder = window_builder + attributes = attributes .with_title_hidden(settings.platform_specific.title_hidden) .with_titlebar_transparent( settings.platform_specific.titlebar_transparent, @@ -107,25 +103,25 @@ pub fn window_settings( { #[cfg(feature = "x11")] { - use winit::platform::x11::WindowBuilderExtX11; + use winit::platform::x11::WindowAttributesExtX11; - window_builder = window_builder.with_name( + attributes = attributes.with_name( &settings.platform_specific.application_id, &settings.platform_specific.application_id, ); } #[cfg(feature = "wayland")] { - use winit::platform::wayland::WindowBuilderExtWayland; + use winit::platform::wayland::WindowAttributesExtWayland; - window_builder = window_builder.with_name( + attributes = attributes.with_name( &settings.platform_specific.application_id, &settings.platform_specific.application_id, ); } } - window_builder + attributes } /// Converts a winit window event into an iced event. diff --git a/winit/src/multi_window.rs b/winit/src/multi_window.rs index 2c148031c1..145b15694f 100644 --- a/winit/src/multi_window.rs +++ b/winit/src/multi_window.rs @@ -12,6 +12,7 @@ use crate::core::widget::operation; use crate::core::window; use crate::core::{Point, Size}; use crate::futures::futures::channel::mpsc; +use crate::futures::futures::channel::oneshot; use crate::futures::futures::executor; use crate::futures::futures::task; use crate::futures::futures::{Future, StreamExt}; @@ -114,12 +115,12 @@ where C: Compositor + 'static, A::Theme: DefaultStyle, { - use winit::event_loop::EventLoopBuilder; + use winit::event_loop::EventLoop; let mut debug = Debug::new(); debug.startup_started(); - let event_loop = EventLoopBuilder::with_user_event() + let event_loop = EventLoop::with_user_event() .build() .expect("Create event loop"); @@ -138,187 +139,277 @@ where runtime.enter(|| A::new(flags)) }; - let should_main_be_visible = settings.window.visible; - let exit_on_close_request = settings.window.exit_on_close_request; + let id = settings.id; + let title = application.title(window::Id::MAIN); - let builder = conversion::window_settings( - settings.window, - &application.title(window::Id::MAIN), - event_loop.primary_monitor(), - settings.id, - ) - .with_visible(false); + let (boot_sender, boot_receiver) = oneshot::channel(); + let (event_sender, event_receiver) = mpsc::unbounded(); + let (control_sender, control_receiver) = mpsc::unbounded(); - log::info!("Window builder: {:#?}", builder); + let instance = Box::pin(run_instance::( + application, + runtime, + proxy, + debug, + boot_receiver, + event_receiver, + control_sender, + init_command, + )); - let main_window = Arc::new( - builder - .build(&event_loop) - .map_err(Error::WindowCreationFailed)?, - ); + let context = task::Context::from_waker(task::noop_waker_ref()); - #[cfg(target_arch = "wasm32")] - { - use winit::platform::web::WindowExtWebSys; + struct Runner { + instance: std::pin::Pin>, + context: task::Context<'static>, + boot: Option>, + sender: mpsc::UnboundedSender>, + receiver: mpsc::UnboundedReceiver, + error: Option, + } - let canvas = main_window.canvas(); + struct BootConfig { + sender: oneshot::Sender>, + id: Option, + title: String, + window_settings: window::Settings, + graphics_settings: graphics::Settings, + } - let window = web_sys::window().unwrap(); - let document = window.document().unwrap(); - let body = document.body().unwrap(); + let mut runner = Runner { + instance, + context, + boot: Some(BootConfig { + sender: boot_sender, + id, + title, + window_settings: settings.window, + graphics_settings, + }), + sender: event_sender, + receiver: control_receiver, + error: None, + }; - let target = target.and_then(|target| { - body.query_selector(&format!("#{}", target)) - .ok() - .unwrap_or(None) - }); + impl winit::application::ApplicationHandler + for Runner + where + F: Future, + C: Compositor, + { + fn resumed(&mut self, event_loop: &winit::event_loop::ActiveEventLoop) { + let Some(BootConfig { + sender, + id, + title, + window_settings, + graphics_settings, + }) = self.boot.take() + else { + return; + }; - match target { - Some(node) => { - let _ = node - .replace_with_with_node_1(&canvas) - .expect(&format!("Could not replace #{}", node.id())); - } - None => { - let _ = body - .append_child(&canvas) - .expect("Append canvas to HTML body"); - } - }; - } + let should_be_visible = window_settings.visible; + let exit_on_close_request = window_settings.exit_on_close_request; - let mut compositor = - executor::block_on(C::new(graphics_settings, main_window.clone()))?; + let window_attributes = conversion::window_attributes( + window_settings, + &title, + event_loop.primary_monitor(), + id, + ) + .with_visible(false); - let mut window_manager = WindowManager::new(); - let _ = window_manager.insert( - window::Id::MAIN, - main_window, - &application, - &mut compositor, - exit_on_close_request, - ); + log::debug!("Window attributes: {window_attributes:#?}"); - let (mut event_sender, event_receiver) = mpsc::unbounded(); - let (control_sender, mut control_receiver) = mpsc::unbounded(); + let window = match event_loop.create_window(window_attributes) { + Ok(window) => Arc::new(window), + Err(error) => { + self.error = Some(Error::WindowCreationFailed(error)); + event_loop.exit(); + return; + } + }; - let mut instance = Box::pin(run_instance::( - application, - compositor, - runtime, - proxy, - debug, - event_receiver, - control_sender, - init_command, - window_manager, - should_main_be_visible, - )); + let finish_boot = async move { + let compositor = + C::new(graphics_settings, window.clone()).await?; + + sender + .send(Boot { + window, + compositor, + should_be_visible, + exit_on_close_request, + }) + .ok() + .expect("Send boot event"); + + Ok::<_, graphics::Error>(()) + }; - let mut context = task::Context::from_waker(task::noop_waker_ref()); + if let Err(error) = executor::block_on(finish_boot) { + self.error = Some(Error::GraphicsCreationFailed(error)); + event_loop.exit(); + } + } - let process_event = move |event, event_loop: &winit::event_loop::EventLoopWindowTarget<_>| { - if event_loop.exiting() { - return; + fn window_event( + &mut self, + event_loop: &winit::event_loop::ActiveEventLoop, + window_id: winit::window::WindowId, + event: winit::event::WindowEvent, + ) { + #[cfg(target_os = "windows")] + let is_move_or_resize = matches!( + event, + winit::event::WindowEvent::Resized(_) + | winit::event::WindowEvent::Moved(_) + ); + + self.process_event( + event_loop, + Event::EventLoopAwakened(winit::event::Event::WindowEvent { + window_id, + event, + }), + ); + + // TODO: Remove when unnecessary + // On Windows, we emulate an `AboutToWait` event after every `Resized` event + // since the event loop does not resume during resize interaction. + // More details: https://github.com/rust-windowing/winit/issues/3272 + #[cfg(target_os = "windows")] + { + if is_move_or_resize { + self.process_event( + event_loop, + Event::EventLoopAwakened( + winit::event::Event::AboutToWait, + ), + ); + } + } } - event_sender - .start_send(Event::EventLoopAwakened(event)) - .expect("Send event"); - - loop { - let poll = instance.as_mut().poll(&mut context); - - match poll { - task::Poll::Pending => match control_receiver.try_next() { - Ok(Some(control)) => match control { - Control::ChangeFlow(flow) => { - use winit::event_loop::ControlFlow; - - match (event_loop.control_flow(), flow) { - ( - ControlFlow::WaitUntil(current), - ControlFlow::WaitUntil(new), - ) if new < current => {} - ( - ControlFlow::WaitUntil(target), - ControlFlow::Wait, - ) if target > Instant::now() => {} - _ => { - event_loop.set_control_flow(flow); + fn user_event( + &mut self, + event_loop: &winit::event_loop::ActiveEventLoop, + message: Message, + ) { + self.process_event( + event_loop, + Event::EventLoopAwakened(winit::event::Event::UserEvent( + message, + )), + ); + } + + fn about_to_wait( + &mut self, + event_loop: &winit::event_loop::ActiveEventLoop, + ) { + self.process_event( + event_loop, + Event::EventLoopAwakened(winit::event::Event::AboutToWait), + ); + } + } + + impl Runner + where + F: Future, + C: Compositor, + { + fn process_event( + &mut self, + event_loop: &winit::event_loop::ActiveEventLoop, + event: Event, + ) { + if event_loop.exiting() { + return; + } + + self.sender.start_send(event).expect("Send event"); + + loop { + let poll = self.instance.as_mut().poll(&mut self.context); + + match poll { + task::Poll::Pending => match self.receiver.try_next() { + Ok(Some(control)) => match control { + Control::ChangeFlow(flow) => { + use winit::event_loop::ControlFlow; + + match (event_loop.control_flow(), flow) { + ( + ControlFlow::WaitUntil(current), + ControlFlow::WaitUntil(new), + ) if new < current => {} + ( + ControlFlow::WaitUntil(target), + ControlFlow::Wait, + ) if target > Instant::now() => {} + _ => { + event_loop.set_control_flow(flow); + } } } - } - Control::CreateWindow { - id, - settings, - title, - monitor, - } => { - let exit_on_close_request = - settings.exit_on_close_request; - - let window = conversion::window_settings( - settings, &title, monitor, None, - ) - .build(event_loop) - .expect("Failed to build window"); - - event_sender - .start_send(Event::WindowCreated { - id, - window, - exit_on_close_request, - }) - .expect("Send event"); - } - Control::Exit => { - event_loop.exit(); + Control::CreateWindow { + id, + settings, + title, + monitor, + } => { + let exit_on_close_request = + settings.exit_on_close_request; + + let window = event_loop + .create_window( + conversion::window_attributes( + settings, &title, monitor, None, + ), + ) + .expect("Create window"); + + self.process_event( + event_loop, + Event::WindowCreated { + id, + window, + exit_on_close_request, + }, + ); + } + Control::Exit => { + event_loop.exit(); + } + }, + _ => { + break; } }, - _ => { + task::Poll::Ready(_) => { + event_loop.exit(); break; } - }, - task::Poll::Ready(_) => { - event_loop.exit(); - break; - } - }; - } - }; - - #[cfg(not(target_os = "windows"))] - let _ = event_loop.run(process_event); - - // TODO: Remove when unnecessary - // On Windows, we emulate an `AboutToWait` event after every `Resized` event - // since the event loop does not resume during resize interaction. - // More details: https://github.com/rust-windowing/winit/issues/3272 - #[cfg(target_os = "windows")] - { - let mut process_event = process_event; - - let _ = event_loop.run(move |event, event_loop| { - if matches!( - event, - winit::event::Event::WindowEvent { - event: winit::event::WindowEvent::Resized(_) - | winit::event::WindowEvent::Moved(_), - .. - } - ) { - process_event(event, event_loop); - process_event(winit::event::Event::AboutToWait, event_loop); - } else { - process_event(event, event_loop); + }; } - }); + } } + let _ = event_loop.run_app(&mut runner); + Ok(()) } +struct Boot { + window: Arc, + compositor: C, + should_be_visible: bool, + exit_on_close_request: bool, +} + enum Event { WindowCreated { id: window::Id, @@ -341,15 +432,13 @@ enum Control { async fn run_instance( mut application: A, - mut compositor: C, mut runtime: Runtime, A::Message>, mut proxy: Proxy, mut debug: Debug, + mut boot: oneshot::Receiver>, mut event_receiver: mpsc::UnboundedReceiver>, mut control_sender: mpsc::UnboundedSender, init_command: Command, - mut window_manager: WindowManager, - should_main_window_be_visible: bool, ) where A: Application + 'static, E: Executor + 'static, @@ -359,11 +448,28 @@ async fn run_instance( use winit::event; use winit::event_loop::ControlFlow; + let Boot { + window: main_window, + mut compositor, + should_be_visible, + exit_on_close_request, + } = boot.try_recv().ok().flatten().expect("Receive boot"); + + let mut window_manager = WindowManager::new(); + + let _ = window_manager.insert( + window::Id::MAIN, + main_window.clone(), + &application, + &mut compositor, + exit_on_close_request, + ); + let main_window = window_manager .get_mut(window::Id::MAIN) .expect("Get main window"); - if should_main_window_be_visible { + if should_be_visible { main_window.raw.set_visible(true); } @@ -532,7 +638,7 @@ async fn run_instance( debug.draw_finished(); if new_mouse_interaction != window.mouse_interaction { - window.raw.set_cursor_icon( + window.raw.set_cursor( conversion::mouse_interaction( new_mouse_interaction, ), @@ -603,7 +709,7 @@ async fn run_instance( if new_mouse_interaction != window.mouse_interaction { - window.raw.set_cursor_icon( + window.raw.set_cursor( conversion::mouse_interaction( new_mouse_interaction, ), diff --git a/winit/src/multi_window/window_manager.rs b/winit/src/multi_window/window_manager.rs index 3a0c8556a9..57a7dc7e38 100644 --- a/winit/src/multi_window/window_manager.rs +++ b/winit/src/multi_window/window_manager.rs @@ -9,8 +9,9 @@ use std::sync::Arc; use winit::monitor::MonitorHandle; #[allow(missing_debug_implementations)] -pub struct WindowManager +pub struct WindowManager where + A: Application, C: Compositor, A::Theme: DefaultStyle, { From 7e7285d60f08e9d32a796e9adf7e37535a67fc43 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A9ctor=20Ram=C3=B3n=20Jim=C3=A9nez?= Date: Tue, 7 May 2024 17:00:55 +0200 Subject: [PATCH 2/2] Plug `new_events` handler to event loop --- winit/src/application.rs | 15 +++++++++++++++ winit/src/multi_window.rs | 15 +++++++++++++++ 2 files changed, 30 insertions(+) diff --git a/winit/src/application.rs b/winit/src/application.rs index e056f4c554..3bc292552c 100644 --- a/winit/src/application.rs +++ b/winit/src/application.rs @@ -341,6 +341,21 @@ where } } + fn new_events( + &mut self, + event_loop: &winit::event_loop::ActiveEventLoop, + cause: winit::event::StartCause, + ) { + if self.boot.is_some() { + return; + } + + self.process_event( + event_loop, + winit::event::Event::NewEvents(cause), + ); + } + fn window_event( &mut self, event_loop: &winit::event_loop::ActiveEventLoop, diff --git a/winit/src/multi_window.rs b/winit/src/multi_window.rs index 145b15694f..673a6f3067 100644 --- a/winit/src/multi_window.rs +++ b/winit/src/multi_window.rs @@ -254,6 +254,21 @@ where } } + fn new_events( + &mut self, + event_loop: &winit::event_loop::ActiveEventLoop, + cause: winit::event::StartCause, + ) { + if self.boot.is_some() { + return; + } + + self.process_event( + event_loop, + Event::EventLoopAwakened(winit::event::Event::NewEvents(cause)), + ); + } + fn window_event( &mut self, event_loop: &winit::event_loop::ActiveEventLoop,