diff --git a/Cargo.toml b/Cargo.toml index f5527e53..5df2f12b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -22,7 +22,7 @@ all-features = true maintenance = { status = "actively-developed" } [features] -default = ["wgpu", "tiny-skia", "auto-detect-theme", "futures-executor"] +default = ["wgpu", "tiny-skia", "auto-detect-theme", "thread-pool"] # Enables the `wgpu` GPU-accelerated renderer backend wgpu = ["iced_renderer/wgpu", "iced_widget/wgpu"] # Enables the `tiny-skia` software renderer backend @@ -43,8 +43,8 @@ markdown = ["iced_widget/markdown"] lazy = ["iced_widget/lazy"] # Enables a debug view in native platforms (press F12) debug = ["iced_winit/debug"] -# Enables `futures-executor` as the `executor::Default` on native platforms -futures-executor = ["iced_futures/thread-pool"] +# Enables the `thread-pool` futures executor as the `executor::Default` on native platforms +thread-pool = ["iced_futures/thread-pool"] # Enables `tokio` as the `executor::Default` on native platforms tokio = ["iced_futures/tokio"] # Enables `async-std` as the `executor::Default` on native platforms @@ -152,7 +152,7 @@ bytemuck = { version = "1.0", features = ["derive"] } bytes = "1.6" cosmic-text = "0.13" dark-light = "2.0" -futures = { version = "0.3", default-features = false, features = ["std", "async-await"] } +futures = { version = "0.3", default-features = false } glam = "0.25" cryoglyph = { git = "https://github.com/iced-rs/cryoglyph.git", rev = "be2defe4a13fd7c97c6f4c81e8e085463eb578dc" } guillotiere = "0.6" diff --git a/futures/Cargo.toml b/futures/Cargo.toml index 3984ce83..6a49e9e8 100644 --- a/futures/Cargo.toml +++ b/futures/Cargo.toml @@ -24,6 +24,8 @@ thread-pool = ["futures/thread-pool"] iced_core.workspace = true futures.workspace = true +futures.features = ["std"] + log.workspace = true rustc-hash.workspace = true diff --git a/futures/src/backend/native/async_std.rs b/futures/src/backend/native/async_std.rs index 9acafd9c..be258b26 100644 --- a/futures/src/backend/native/async_std.rs +++ b/futures/src/backend/native/async_std.rs @@ -13,10 +13,6 @@ impl crate::Executor for Executor { fn spawn(&self, future: impl Future + Send + 'static) { let _ = async_std::task::spawn(future); } - - fn block_on(future: impl Future + 'static) { - async_std::task::block_on(future); - } } pub mod time { diff --git a/futures/src/backend/native/smol.rs b/futures/src/backend/native/smol.rs index 27998e8f..9ac6a27d 100644 --- a/futures/src/backend/native/smol.rs +++ b/futures/src/backend/native/smol.rs @@ -12,10 +12,6 @@ impl crate::Executor for Executor { fn spawn(&self, future: impl Future + Send + 'static) { smol::spawn(future).detach(); } - - fn block_on(future: impl Future + 'static) { - smol::block_on(future); - } } pub mod time { diff --git a/futures/src/backend/native/thread_pool.rs b/futures/src/backend/native/thread_pool.rs index 92004bc7..a90cc53a 100644 --- a/futures/src/backend/native/thread_pool.rs +++ b/futures/src/backend/native/thread_pool.rs @@ -11,10 +11,6 @@ impl crate::Executor for Executor { fn spawn(&self, future: impl Future + Send + 'static) { self.spawn_ok(future); } - - fn block_on(future: impl Future + 'static) { - futures::executor::block_on(future); - } } pub mod time { diff --git a/futures/src/backend/native/tokio.rs b/futures/src/backend/native/tokio.rs index 0670c5ee..911d788c 100644 --- a/futures/src/backend/native/tokio.rs +++ b/futures/src/backend/native/tokio.rs @@ -17,13 +17,6 @@ impl crate::Executor for Executor { let _guard = tokio::runtime::Runtime::enter(self); f() } - - fn block_on(future: impl Future + 'static) { - tokio::runtime::Builder::new_current_thread() - .build() - .unwrap() - .block_on(future); - } } pub mod time { diff --git a/futures/src/backend/null.rs b/futures/src/backend/null.rs index 151d5587..59b740e3 100644 --- a/futures/src/backend/null.rs +++ b/futures/src/backend/null.rs @@ -1,4 +1,5 @@ //! A backend that does nothing! +use crate::MaybeSend; /// An executor that drops all the futures, instead of spawning them. #[derive(Debug)] @@ -9,17 +10,7 @@ impl crate::Executor for Executor { Ok(Self) } - #[cfg(not(target_arch = "wasm32"))] - fn spawn(&self, _future: impl Future + Send + 'static) {} - - #[cfg(target_arch = "wasm32")] - fn spawn(&self, _future: impl Future + 'static) {} - - #[cfg(not(target_arch = "wasm32"))] - fn block_on(_future: impl Future + 'static) {} - - #[cfg(target_arch = "wasm32")] - fn block_on(_future: impl Future + 'static) {} + fn spawn(&self, _future: impl Future + MaybeSend + 'static) {} } pub mod time { diff --git a/futures/src/backend/wasm/wasm_bindgen.rs b/futures/src/backend/wasm/wasm_bindgen.rs index 667bf2fa..4811e7f4 100644 --- a/futures/src/backend/wasm/wasm_bindgen.rs +++ b/futures/src/backend/wasm/wasm_bindgen.rs @@ -12,10 +12,6 @@ impl crate::Executor for Executor { fn spawn(&self, future: impl futures::Future + 'static) { wasm_bindgen_futures::spawn_local(future); } - - fn block_on(future: impl futures::Future + 'static) { - wasm_bindgen_futures::spawn_local(future); - } } pub mod time { diff --git a/futures/src/executor.rs b/futures/src/executor.rs index 22ed4212..9c14a2c9 100644 --- a/futures/src/executor.rs +++ b/futures/src/executor.rs @@ -20,7 +20,4 @@ pub trait Executor: Sized { fn enter(&self, f: impl FnOnce() -> R) -> R { f() } - - /// Runs the future on the current thread, blocking it until it is completed. - fn block_on(future: impl Future + 'static); } diff --git a/futures/src/runtime.rs b/futures/src/runtime.rs index 0f30b469..72252458 100644 --- a/futures/src/runtime.rs +++ b/futures/src/runtime.rs @@ -1,6 +1,6 @@ //! Run commands and keep track of subscriptions. use crate::subscription; -use crate::{BoxFuture, BoxStream, Executor, MaybeSend}; +use crate::{BoxStream, Executor, MaybeSend}; use futures::{Sink, channel::mpsc}; use std::marker::PhantomData; @@ -51,20 +51,10 @@ where } /// Spawns a [`Future`] in the [`Runtime`]. - /// - /// The resulting `Message` will be forwarded to the `Sender` of the - /// [`Runtime`]. - /// - /// [`Future`]: BoxFuture - pub fn spawn(&mut self, future: BoxFuture) { - use futures::{FutureExt, SinkExt}; - - let mut sender = self.sender.clone(); - - let future = future.then(|message| async move { - let _ = sender.send(message).await; - }); - + pub fn spawn( + &mut self, + future: impl Future + MaybeSend + 'static, + ) { self.executor.spawn(future); } diff --git a/graphics/src/compositor.rs b/graphics/src/compositor.rs index e9063678..f0f67607 100644 --- a/graphics/src/compositor.rs +++ b/graphics/src/compositor.rs @@ -10,7 +10,7 @@ use thiserror::Error; use std::borrow::Cow; /// A graphics compositor that can draw to windows. -pub trait Compositor: Sized { +pub trait Compositor: Sized + MaybeSend { /// The iced renderer of the backend. type Renderer; @@ -21,7 +21,7 @@ pub trait Compositor: Sized { fn new( settings: Settings, compatible_window: W, - ) -> impl Future> { + ) -> impl Future> + MaybeSend { Self::with_backend(settings, compatible_window, None) } @@ -33,7 +33,7 @@ pub trait Compositor: Sized { _settings: Settings, _compatible_window: W, _backend: Option<&str>, - ) -> impl Future>; + ) -> impl Future> + MaybeSend; /// Creates a [`Self::Renderer`] for the [`Compositor`]. fn create_renderer(&self) -> Self::Renderer; diff --git a/src/lib.rs b/src/lib.rs index 95820ed7..9b3f3788 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -480,6 +480,13 @@ use iced_winit::runtime; pub use iced_futures::futures; pub use iced_futures::stream; +#[cfg(not(any(feature = "thread-pool", feature = "tokio", feature = "smol")))] +compile_error!( + "No futures executor has been enabled! You must enable an + executor feature.\n + Available options: thread-pool, tokio, smol, or async-std." +); + #[cfg(feature = "highlighter")] pub use iced_highlighter as highlighter; diff --git a/winit/src/program.rs b/winit/src/program.rs index ee921adb..77b4f9d7 100644 --- a/winit/src/program.rs +++ b/winit/src/program.rs @@ -18,7 +18,7 @@ use crate::futures::futures::channel::oneshot; use crate::futures::futures::task; use crate::futures::futures::{Future, StreamExt}; use crate::futures::subscription::{self, Subscription}; -use crate::futures::{Executor, Runtime}; +use crate::futures::{Executor, MaybeSend, Runtime}; use crate::graphics; use crate::graphics::{Compositor, compositor}; use crate::runtime::Debug; @@ -149,7 +149,7 @@ pub fn run( ) -> Result<(), Error> where P: Program + 'static, - C: Compositor + 'static, + C: Compositor + MaybeSend + 'static, P::Theme: theme::Base, { use winit::event_loop::EventLoop; @@ -494,9 +494,7 @@ where event_loop.exit(); } }, - _ => { - break; - } + _ => break, }, task::Poll::Ready(_) => { event_loop.exit(); @@ -562,7 +560,7 @@ async fn run_instance( default_fonts: Vec>, ) where P: Program + 'static, - C: Compositor + 'static, + C: Compositor + MaybeSend + 'static, P::Theme: theme::Base, { use winit::event; @@ -579,35 +577,11 @@ async fn run_instance( let mut ui_caches = FxHashMap::default(); let mut user_interfaces = ManuallyDrop::new(FxHashMap::default()); let mut clipboard = Clipboard::unconnected(); - let mut compositor_receiver: Option> = 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 - } else if let Ok(event) = event_receiver.try_next() { + let event = if let Ok(event) = event_receiver.try_next() { event } else { event_receiver.next().await @@ -626,17 +600,17 @@ async fn run_instance( on_open, } => { if compositor.is_none() { - let (compositor_sender, new_compositor_receiver) = + let (compositor_sender, compositor_receiver) = oneshot::channel(); - compositor_receiver = Some(new_compositor_receiver); - let create_compositor = { + let window = window.clone(); + let mut proxy = proxy.clone(); let default_fonts = default_fonts.clone(); async move { let mut compositor = - C::new(graphics_settings, window.clone()).await; + C::new(graphics_settings, window).await; if let Ok(compositor) = &mut compositor { for font in default_fonts { @@ -645,26 +619,38 @@ async fn run_instance( } compositor_sender - .send(compositor.map(|compositor| { - ( - compositor, - Event::WindowCreated { - id, - window, - exit_on_close_request, - make_visible, - on_open, - }, - ) - })) + .send(compositor) .ok() .expect("Send compositor"); + + // HACK! Send a proxy event on completion to trigger + // a runtime re-poll + // TODO: Send compositor through proxy (?) + { + let (sender, _receiver) = oneshot::channel(); + + proxy.send_action(Action::Window( + runtime::window::Action::GetLatest(sender), + )); + } } }; - P::Executor::block_on(create_compositor); + runtime.spawn(create_compositor); - continue; + match compositor_receiver + .await + .expect("Wait for compositor") + { + Ok(new_compositor) => { + compositor = Some(new_compositor); + } + Err(error) => { + let _ = control_sender + .start_send(Control::Crash(error.into())); + break; + } + } } let window = window_manager.insert(