From 3645d34d6a1ba1247238e830e9eefd52d9e5b986 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Thu, 21 Mar 2024 22:27:17 +0100 Subject: Implement composable, type-safe renderer fallback --- winit/src/application.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'winit/src/application.rs') diff --git a/winit/src/application.rs b/winit/src/application.rs index 13d9282d..29786b65 100644 --- a/winit/src/application.rs +++ b/winit/src/application.rs @@ -130,7 +130,7 @@ pub fn default(theme: &Theme) -> Appearance { /// settings. pub async fn run( settings: Settings, - compositor_settings: C::Settings, + compositor_settings: impl Into, ) -> Result<(), Error> where A: Application + 'static, @@ -219,7 +219,7 @@ where }; } - let compositor = C::new(compositor_settings, window.clone()).await?; + let compositor = C::new(compositor_settings.into(), window.clone()).await?; let mut renderer = compositor.create_renderer(); for font in settings.fonts { -- cgit From 5137d655e6bbd29581fc1469d0385515113f2999 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Fri, 22 Mar 2024 07:09:51 +0100 Subject: Allow custom renderers in `Program` and `Application` --- winit/src/application.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'winit/src/application.rs') diff --git a/winit/src/application.rs b/winit/src/application.rs index 29786b65..d68523fa 100644 --- a/winit/src/application.rs +++ b/winit/src/application.rs @@ -13,6 +13,7 @@ use crate::core::window; use crate::core::{Color, Event, Point, Size, Theme}; use crate::futures::futures; use crate::futures::{Executor, Runtime, Subscription}; +use crate::graphics; use crate::graphics::compositor::{self, Compositor}; use crate::runtime::clipboard; use crate::runtime::program::Program; @@ -130,7 +131,7 @@ pub fn default(theme: &Theme) -> Appearance { /// settings. pub async fn run( settings: Settings, - compositor_settings: impl Into, + graphics_settings: graphics::Settings, ) -> Result<(), Error> where A: Application + 'static, @@ -219,7 +220,7 @@ where }; } - let compositor = C::new(compositor_settings.into(), window.clone()).await?; + let compositor = C::new(graphics_settings, window.clone()).await?; let mut renderer = compositor.create_renderer(); for font in settings.fonts { -- cgit From 6ad5bb3597f640ac329801adf735d633bf0a512f Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Tue, 9 Apr 2024 22:25:16 +0200 Subject: Port `iced_tiny_skia` to new layering architecture --- winit/src/application.rs | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) (limited to 'winit/src/application.rs') diff --git a/winit/src/application.rs b/winit/src/application.rs index d68523fa..1ca80609 100644 --- a/winit/src/application.rs +++ b/winit/src/application.rs @@ -220,13 +220,11 @@ where }; } - let compositor = C::new(graphics_settings, window.clone()).await?; - let mut renderer = compositor.create_renderer(); + let mut compositor = C::new(graphics_settings, window.clone()).await?; + let renderer = compositor.create_renderer(); for font in settings.fonts { - use crate::core::text::Renderer; - - renderer.load_font(font); + compositor.load_font(font); } let (mut event_sender, event_receiver) = mpsc::unbounded(); @@ -950,10 +948,8 @@ pub fn run_command( *cache = current_cache; } command::Action::LoadFont { bytes, tagger } => { - use crate::core::text::Renderer; - // TODO: Error handling (?) - renderer.load_font(bytes); + compositor.load_font(bytes); proxy .send_event(tagger(Ok(()))) -- cgit From b6b51375cfd96e330d6ee22096dacf831a992aa7 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Tue, 16 Apr 2024 21:50:28 +0200 Subject: Implement backpressure mechanism in `iced_winit::Proxy` --- winit/src/application.rs | 79 ++++++++++++++++++------------------------------ 1 file changed, 30 insertions(+), 49 deletions(-) (limited to 'winit/src/application.rs') diff --git a/winit/src/application.rs b/winit/src/application.rs index 1ca80609..a447c9da 100644 --- a/winit/src/application.rs +++ b/winit/src/application.rs @@ -149,13 +149,14 @@ where let event_loop = EventLoopBuilder::with_user_event() .build() .expect("Create event loop"); - let proxy = event_loop.create_proxy(); + + let (proxy, worker) = Proxy::new(event_loop.create_proxy()); let runtime = { - let proxy = Proxy::new(event_loop.create_proxy()); let executor = E::new().map_err(Error::ExecutorCreationFailed)?; + executor.spawn(worker); - Runtime::new(executor, proxy) + Runtime::new(executor, proxy.clone()) }; let (application, init_command) = { @@ -305,7 +306,7 @@ async fn run_instance( mut compositor: C, mut renderer: A::Renderer, mut runtime: Runtime, A::Message>, - mut proxy: winit::event_loop::EventLoopProxy, + mut proxy: Proxy, mut debug: Debug, mut event_receiver: mpsc::UnboundedReceiver< winit::event::Event, @@ -370,6 +371,7 @@ async fn run_instance( let mut mouse_interaction = mouse::Interaction::default(); let mut events = Vec::new(); let mut messages = Vec::new(); + let mut user_events = 0; let mut redraw_pending = false; debug.startup_finished(); @@ -396,6 +398,7 @@ async fn run_instance( } event::Event::UserEvent(message) => { messages.push(message); + user_events += 1; } event::Event::WindowEvent { event: event::WindowEvent::RedrawRequested { .. }, @@ -593,6 +596,11 @@ async fn run_instance( if should_exit { break; } + + if user_events > 0 { + proxy.free_slots(user_events); + user_events = 0; + } } if !redraw_pending { @@ -667,7 +675,7 @@ pub fn update( runtime: &mut Runtime, A::Message>, clipboard: &mut Clipboard, should_exit: &mut bool, - proxy: &mut winit::event_loop::EventLoopProxy, + proxy: &mut Proxy, debug: &mut Debug, messages: &mut Vec, window: &winit::window::Window, @@ -717,7 +725,7 @@ pub fn run_command( runtime: &mut Runtime, A::Message>, clipboard: &mut Clipboard, should_exit: &mut bool, - proxy: &mut winit::event_loop::EventLoopProxy, + proxy: &mut Proxy, debug: &mut Debug, window: &winit::window::Window, ) where @@ -742,9 +750,7 @@ pub fn run_command( clipboard::Action::Read(tag, kind) => { let message = tag(clipboard.read(kind)); - proxy - .send_event(message) - .expect("Send message to event loop"); + proxy.send(message); } clipboard::Action::Write(contents, kind) => { clipboard.write(kind, contents); @@ -774,25 +780,16 @@ pub fn run_command( let size = window.inner_size().to_logical(window.scale_factor()); - proxy - .send_event(callback(Size::new( - size.width, - size.height, - ))) - .expect("Send message to event loop"); + proxy.send(callback(Size::new(size.width, size.height))); } window::Action::FetchMaximized(_id, callback) => { - proxy - .send_event(callback(window.is_maximized())) - .expect("Send message to event loop"); + proxy.send(callback(window.is_maximized())); } window::Action::Maximize(_id, maximized) => { window.set_maximized(maximized); } window::Action::FetchMinimized(_id, callback) => { - proxy - .send_event(callback(window.is_minimized())) - .expect("Send message to event loop"); + proxy.send(callback(window.is_minimized())); } window::Action::Minimize(_id, minimized) => { window.set_minimized(minimized); @@ -808,9 +805,7 @@ pub fn run_command( }) .ok(); - proxy - .send_event(callback(position)) - .expect("Send message to event loop"); + proxy.send(callback(position)); } window::Action::Move(_id, position) => { window.set_outer_position(winit::dpi::LogicalPosition { @@ -835,9 +830,7 @@ pub fn run_command( core::window::Mode::Hidden }; - proxy - .send_event(tag(mode)) - .expect("Send message to event loop"); + proxy.send(tag(mode)); } window::Action::ToggleMaximize(_id) => { window.set_maximized(!window.is_maximized()); @@ -865,17 +858,13 @@ pub fn run_command( } } window::Action::FetchId(_id, tag) => { - proxy - .send_event(tag(window.id().into())) - .expect("Send message to event loop"); + proxy.send(tag(window.id().into())); } window::Action::RunWithHandle(_id, tag) => { use window::raw_window_handle::HasWindowHandle; if let Ok(handle) = window.window_handle() { - proxy - .send_event(tag(&handle)) - .expect("Send message to event loop"); + proxy.send(tag(&handle)); } } @@ -888,12 +877,10 @@ pub fn run_command( &debug.overlay(), ); - proxy - .send_event(tag(window::Screenshot::new( - bytes, - state.physical_size(), - ))) - .expect("Send message to event loop."); + proxy.send(tag(window::Screenshot::new( + bytes, + state.physical_size(), + ))); } }, command::Action::System(action) => match action { @@ -901,7 +888,7 @@ pub fn run_command( #[cfg(feature = "system")] { let graphics_info = compositor.fetch_information(); - let proxy = proxy.clone(); + let mut proxy = proxy.clone(); let _ = std::thread::spawn(move || { let information = @@ -909,9 +896,7 @@ pub fn run_command( let message = _tag(information); - proxy - .send_event(message) - .expect("Send message to event loop"); + proxy.send(message); }); } } @@ -934,9 +919,7 @@ pub fn run_command( match operation.finish() { operation::Outcome::None => {} operation::Outcome::Some(message) => { - proxy - .send_event(message) - .expect("Send message to event loop"); + proxy.send(message); } operation::Outcome::Chain(next) => { current_operation = Some(next); @@ -951,9 +934,7 @@ pub fn run_command( // TODO: Error handling (?) compositor.load_font(bytes); - proxy - .send_event(tagger(Ok(()))) - .expect("Send message to event loop"); + proxy.send(tagger(Ok(()))); } command::Action::Custom(_) => { log::warn!("Unsupported custom action in `iced_winit` shell"); -- cgit From 2645524f88414393d8b3ca9c6fe801b32b5ebd33 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Tue, 7 May 2024 15:50:18 +0200 Subject: Update `winit` to `0.30` --- winit/src/application.rs | 383 ++++++++++++++++++++++++++++++++++------------- 1 file changed, 277 insertions(+), 106 deletions(-) (limited to 'winit/src/application.rs') diff --git a/winit/src/application.rs b/winit/src/application.rs index a447c9da..e056f4c5 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, )); -- cgit From 7e7285d60f08e9d32a796e9adf7e37535a67fc43 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Tue, 7 May 2024 17:00:55 +0200 Subject: Plug `new_events` handler to event loop --- winit/src/application.rs | 15 +++++++++++++++ 1 file changed, 15 insertions(+) (limited to 'winit/src/application.rs') diff --git a/winit/src/application.rs b/winit/src/application.rs index e056f4c5..3bc29255 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, -- cgit From 718fe5b7de6c705b1797c49d382182182acdbe80 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Wed, 8 May 2024 23:14:37 +0200 Subject: Pass `WindowHandle` by value to `window::run_with_handle` --- winit/src/application.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'winit/src/application.rs') diff --git a/winit/src/application.rs b/winit/src/application.rs index 3bc29255..f7508b4c 100644 --- a/winit/src/application.rs +++ b/winit/src/application.rs @@ -1050,7 +1050,7 @@ pub fn run_command( use window::raw_window_handle::HasWindowHandle; if let Ok(handle) = window.window_handle() { - proxy.send(tag(&handle)); + proxy.send(tag(handle)); } } -- cgit