diff options
author | 2025-01-14 11:32:38 +0000 | |
---|---|---|
committer | 2025-01-14 11:32:38 +0000 | |
commit | bca293db2a4d4d5a9aa2b85a941b6f31d48072c0 (patch) | |
tree | 000dc0bec01f627cde0fc78438260259b8659047 /winit | |
parent | f4eebf0f61aafb5924ee7a02dfdc8816dcdce9c5 (diff) | |
parent | 8b3b554de2b9853a922b285082a0d1ca6f864ddb (diff) | |
download | iced-bca293db2a4d4d5a9aa2b85a941b6f31d48072c0.tar.gz iced-bca293db2a4d4d5a9aa2b85a941b6f31d48072c0.tar.bz2 iced-bca293db2a4d4d5a9aa2b85a941b6f31d48072c0.zip |
Merge pull request #2722 from iced-rs/fix/lazy-compositor-initialization
Initialize `Compositor` lazily in `winit` shell
Diffstat (limited to 'winit')
-rw-r--r-- | winit/src/program.rs | 276 |
1 files changed, 133 insertions, 143 deletions
diff --git a/winit/src/program.rs b/winit/src/program.rs index 499c6252..dddaf33d 100644 --- a/winit/src/program.rs +++ b/winit/src/program.rs @@ -192,7 +192,6 @@ where runtime.enter(|| program.subscription().map(Action::Output)), )); - let (boot_sender, boot_receiver) = oneshot::channel(); let (event_sender, event_receiver) = mpsc::unbounded(); let (control_sender, control_receiver) = mpsc::unbounded(); @@ -201,133 +200,49 @@ where runtime, proxy.clone(), debug, - boot_receiver, event_receiver, control_sender, is_daemon, + graphics_settings, + settings.fonts, )); let context = task::Context::from_waker(task::noop_waker_ref()); - struct Runner<Message: 'static, F, C> { + struct Runner<Message: 'static, F> { instance: std::pin::Pin<Box<F>>, context: task::Context<'static>, id: Option<String>, - boot: Option<BootConfig<C>>, sender: mpsc::UnboundedSender<Event<Action<Message>>>, receiver: mpsc::UnboundedReceiver<Control>, error: Option<Error>, #[cfg(target_arch = "wasm32")] - is_booted: std::rc::Rc<std::cell::RefCell<bool>>, - #[cfg(target_arch = "wasm32")] canvas: Option<web_sys::HtmlCanvasElement>, } - struct BootConfig<C> { - sender: oneshot::Sender<Boot<C>>, - fonts: Vec<Cow<'static, [u8]>>, - graphics_settings: graphics::Settings, - } - let runner = Runner { instance, context, id: settings.id, - boot: Some(BootConfig { - sender: boot_sender, - fonts: settings.fonts, - 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")] canvas: None, }; - impl<Message, F, C> winit::application::ApplicationHandler<Action<Message>> - for Runner<Message, F, C> + impl<Message, F> winit::application::ApplicationHandler<Action<Message>> + for Runner<Message, F> where Message: std::fmt::Debug, F: Future<Output = ()>, - C: Compositor + 'static, { - fn resumed(&mut self, event_loop: &winit::event_loop::ActiveEventLoop) { - let Some(BootConfig { - sender, - fonts, - graphics_settings, - }) = self.boot.take() - else { - return; - }; - - let window = { - let attributes = winit::window::WindowAttributes::default(); - - #[cfg(target_os = "windows")] - let attributes = { - use winit::platform::windows::WindowAttributesExtWindows; - attributes.with_drag_and_drop(false) - }; - - match event_loop.create_window(attributes.with_visible(false)) { - Ok(window) => Arc::new(window), - Err(error) => { - self.error = Some(Error::WindowCreationFailed(error)); - event_loop.exit(); - return; - } - } - }; - - #[cfg(target_arch = "wasm32")] - { - use winit::platform::web::WindowExtWebSys; - self.canvas = window.canvas(); - } - - let finish_boot = async move { - let mut compositor = - C::new(graphics_settings, window.clone()).await?; - - for font in fonts { - compositor.load_font(font); - } - - sender - .send(Boot { compositor }) - .ok() - .expect("Send boot event"); - - Ok::<_, graphics::Error>(()) - }; - - #[cfg(not(target_arch = "wasm32"))] - if let Err(error) = - crate::futures::futures::executor::block_on(finish_boot) - { - self.error = Some(Error::GraphicsCreationFailed(error)); - event_loop.exit(); - } - - #[cfg(target_arch = "wasm32")] - { - 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; - }); - - event_loop - .set_control_flow(winit::event_loop::ControlFlow::Poll); - } + fn resumed( + &mut self, + _event_loop: &winit::event_loop::ActiveEventLoop, + ) { } fn new_events( @@ -335,15 +250,6 @@ where event_loop: &winit::event_loop::ActiveEventLoop, cause: winit::event::StartCause, ) { - if self.boot.is_some() { - return; - } - - #[cfg(target_arch = "wasm32")] - if !*self.is_booted.borrow() { - return; - } - self.process_event( event_loop, Event::EventLoopAwakened(winit::event::Event::NewEvents(cause)), @@ -422,11 +328,6 @@ where &mut self, event_loop: &winit::event_loop::ActiveEventLoop, ) { - #[cfg(target_arch = "wasm32")] - if !*self.is_booted.borrow() { - return; - } - self.process_event( event_loop, Event::EventLoopAwakened(winit::event::Event::AboutToWait), @@ -434,10 +335,9 @@ where } } - impl<Message, F, C> Runner<Message, F, C> + impl<Message, F> Runner<Message, F> where F: Future<Output = ()>, - C: Compositor, { fn process_event( &mut self, @@ -577,7 +477,7 @@ where event_loop, Event::WindowCreated { id, - window, + window: Arc::new(window), exit_on_close_request, make_visible: visible, on_open, @@ -587,6 +487,10 @@ where Control::Exit => { event_loop.exit(); } + Control::Crash(error) => { + self.error = Some(error); + event_loop.exit(); + } }, _ => { break; @@ -618,15 +522,11 @@ where } } -struct Boot<C> { - compositor: C, -} - #[derive(Debug)] enum Event<Message: 'static> { WindowCreated { id: window::Id, - window: winit::window::Window, + window: Arc<winit::window::Window>, exit_on_close_request: bool, make_visible: bool, on_open: oneshot::Sender<window::Id>, @@ -638,6 +538,7 @@ enum Event<Message: 'static> { enum Control { ChangeFlow(winit::event_loop::ControlFlow), Exit, + Crash(Error), CreateWindow { id: window::Id, settings: window::Settings, @@ -652,10 +553,11 @@ async fn run_instance<P, C>( mut runtime: Runtime<P::Executor, Proxy<P::Message>, Action<P::Message>>, mut proxy: Proxy<P::Message>, mut debug: Debug, - boot: oneshot::Receiver<Boot<C>>, mut event_receiver: mpsc::UnboundedReceiver<Event<Action<P::Message>>>, mut control_sender: mpsc::UnboundedSender<Control>, is_daemon: bool, + graphics_settings: graphics::Settings, + default_fonts: Vec<Cow<'static, [u8]>>, ) where P: Program + 'static, C: Compositor<Renderer = P::Renderer> + 'static, @@ -664,11 +566,10 @@ async fn run_instance<P, C>( use winit::event; use winit::event_loop::ControlFlow; - let Boot { mut compositor } = boot.await.expect("Receive boot"); - let mut window_manager = WindowManager::new(); let mut is_window_opening = !is_daemon; + let mut compositor = None; let mut events = Vec::new(); let mut messages = Vec::new(); let mut actions = 0; @@ -676,12 +577,35 @@ async fn run_instance<P, C>( let mut ui_caches = FxHashMap::default(); let mut user_interfaces = ManuallyDrop::new(FxHashMap::default()); let mut clipboard = Clipboard::unconnected(); + let mut compositor_receiver: Option<oneshot::Receiver<_>> = None; debug.startup_finished(); loop { + let event = if compositor_receiver.is_some() { + let compositor_receiver = + compositor_receiver.take().expect("Waiting for compositor"); + + match compositor_receiver.await { + Ok(Ok((new_compositor, event))) => { + compositor = Some(new_compositor); + + Some(event) + } + Ok(Err(error)) => { + control_sender + .start_send(Control::Crash( + Error::GraphicsCreationFailed(error), + )) + .expect("Send control action"); + break; + } + Err(error) => { + panic!("Compositor initialization failed: {error}") + } + } // Empty the queue if possible - let event = if let Ok(event) = event_receiver.try_next() { + } else if let Ok(event) = event_receiver.try_next() { event } else { event_receiver.next().await @@ -699,11 +623,63 @@ async fn run_instance<P, C>( make_visible, on_open, } => { + if compositor.is_none() { + let (compositor_sender, new_compositor_receiver) = + oneshot::channel(); + + compositor_receiver = Some(new_compositor_receiver); + + let create_compositor = { + let default_fonts = default_fonts.clone(); + + async move { + let mut compositor = + C::new(graphics_settings, window.clone()).await; + + if let Ok(compositor) = &mut compositor { + for font in default_fonts { + compositor.load_font(font.clone()); + } + } + + compositor_sender + .send(compositor.map(|compositor| { + ( + compositor, + Event::WindowCreated { + id, + window, + exit_on_close_request, + make_visible, + on_open, + }, + ) + })) + .ok() + .expect("Send compositor"); + } + }; + + #[cfg(not(target_arch = "wasm32"))] + crate::futures::futures::executor::block_on( + create_compositor, + ); + + #[cfg(target_arch = "wasm32")] + { + wasm_bindgen_futures::spawn_local(create_compositor); + } + + continue; + } + let window = window_manager.insert( id, - Arc::new(window), + window, &program, - &mut compositor, + compositor + .as_mut() + .expect("Compositor must be initialized"), exit_on_close_request, ); @@ -797,6 +773,10 @@ async fn run_instance<P, C>( event: event::WindowEvent::RedrawRequested, .. } => { + let Some(compositor) = &mut compositor else { + continue; + }; + let Some((id, window)) = window_manager.get_mut_alias(id) else { @@ -1195,7 +1175,7 @@ fn update<P: Program, E: Executor>( fn run_action<P, C>( action: Action<P::Message>, program: &P, - compositor: &mut C, + compositor: &mut Option<C>, events: &mut Vec<(window::Id, core::Event)>, messages: &mut Vec<P::Message>, clipboard: &mut Clipboard, @@ -1263,6 +1243,10 @@ fn run_action<P, C>( core::Event::Window(core::window::Event::Closed), )); } + + if window_manager.is_empty() { + *compositor = None; + } } window::Action::GetOldest(channel) => { let id = @@ -1474,18 +1458,20 @@ fn run_action<P, C>( } window::Action::Screenshot(id, channel) => { if let Some(window) = window_manager.get_mut(id) { - let bytes = compositor.screenshot( - &mut window.renderer, - window.state.viewport(), - window.state.background_color(), - &debug.overlay(), - ); + if let Some(compositor) = compositor { + let bytes = compositor.screenshot( + &mut window.renderer, + window.state.viewport(), + window.state.background_color(), + &debug.overlay(), + ); - let _ = channel.send(core::window::Screenshot::new( - bytes, - window.state.physical_size(), - window.state.viewport().scale_factor(), - )); + let _ = channel.send(core::window::Screenshot::new( + bytes, + window.state.physical_size(), + window.state.viewport().scale_factor(), + )); + } } } window::Action::EnableMousePassthrough(id) => { @@ -1503,14 +1489,16 @@ fn run_action<P, C>( system::Action::QueryInformation(_channel) => { #[cfg(feature = "system")] { - let graphics_info = compositor.fetch_information(); + if let Some(compositor) = compositor { + let graphics_info = compositor.fetch_information(); - let _ = std::thread::spawn(move || { - let information = - crate::system::information(graphics_info); + let _ = std::thread::spawn(move || { + let information = + crate::system::information(graphics_info); - let _ = _channel.send(information); - }); + let _ = _channel.send(information); + }); + } } } }, @@ -1534,10 +1522,12 @@ fn run_action<P, C>( } } Action::LoadFont { bytes, channel } => { - // TODO: Error handling (?) - compositor.load_font(bytes.clone()); + if let Some(compositor) = compositor { + // TODO: Error handling (?) + compositor.load_font(bytes.clone()); - let _ = channel.send(Ok(())); + let _ = channel.send(Ok(())); + } } Action::Exit => { control_sender |