diff options
Diffstat (limited to 'winit')
| -rw-r--r-- | winit/Cargo.toml | 7 | ||||
| -rw-r--r-- | winit/src/application.rs | 484 | ||||
| -rw-r--r-- | winit/src/conversion.rs | 91 | ||||
| -rw-r--r-- | winit/src/lib.rs | 8 | ||||
| -rw-r--r-- | winit/src/multi_window.rs | 546 | ||||
| -rw-r--r-- | winit/src/multi_window/window_manager.rs | 5 | ||||
| -rw-r--r-- | winit/src/proxy.rs | 101 | 
7 files changed, 801 insertions, 441 deletions
| diff --git a/winit/Cargo.toml b/winit/Cargo.toml index 9d65cc1b..6d3dddde 100644 --- a/winit/Cargo.toml +++ b/winit/Cargo.toml @@ -10,6 +10,9 @@ homepage.workspace = true  categories.workspace = true  keywords.workspace = true +[lints] +workspace = true +  [features]  default = ["x11", "wayland", "wayland-dlopen", "wayland-csd-adwaita"]  debug = ["iced_runtime/debug"] @@ -22,12 +25,15 @@ 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  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 @@ -40,3 +46,4 @@ winapi.workspace = true  [target.'cfg(target_arch = "wasm32")'.dependencies]  web-sys.workspace = true  web-sys.features = ["Document", "Window"] + diff --git a/winit/src/application.rs b/winit/src/application.rs index 13d9282d..f7508b4c 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; @@ -21,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; @@ -128,9 +131,9 @@ pub fn default(theme: &Theme) -> Appearance {  /// Runs an [`Application`] with an executor, compositor, and the provided  /// settings. -pub async fn run<A, E, C>( +pub fn run<A, E, C>(      settings: Settings<A::Flags>, -    compositor_settings: C::Settings, +    graphics_settings: graphics::Settings,  ) -> Result<(), Error>  where      A: Application + 'static, @@ -140,21 +143,22 @@ 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"); -    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) = { @@ -163,104 +167,282 @@ 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::<A, E, C>( +        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<Message: 'static, F, C> { +        instance: std::pin::Pin<Box<F>>, +        context: task::Context<'static>, +        boot: Option<BootConfig<C>>, +        sender: mpsc::UnboundedSender<winit::event::Event<Message>>, +        receiver: mpsc::UnboundedReceiver<winit::event_loop::ControlFlow>, +        error: Option<Error>, +        #[cfg(target_arch = "wasm32")] +        is_booted: std::rc::Rc<std::cell::RefCell<bool>>, +        #[cfg(target_arch = "wasm32")] +        queued_events: Vec<winit::event::Event<Message>>, +    } -    let window = Arc::new( -        builder -            .build(&event_loop) -            .map_err(Error::WindowCreationFailed)?, -    ); +    struct BootConfig<C> { +        sender: oneshot::Sender<Boot<C>>, +        id: Option<String>, +        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<Message, F, C> winit::application::ApplicationHandler<Message> +        for Runner<Message, F, C> +    where +        F: Future<Output = ()>, +        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(); + +                async move { +                    let compositor = +                        C::new(graphics_settings, window.clone()).await?; -        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())); +                    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; +                });              } -        }; -    } +        } -    let compositor = C::new(compositor_settings, window.clone()).await?; -    let mut renderer = compositor.create_renderer(); +        fn new_events( +            &mut self, +            event_loop: &winit::event_loop::ActiveEventLoop, +            cause: winit::event::StartCause, +        ) { +            if self.boot.is_some() { +                return; +            } -    for font in settings.fonts { -        use crate::core::text::Renderer; +            self.process_event( +                event_loop, +                winit::event::Event::NewEvents(cause), +            ); +        } -        renderer.load_font(font); -    } +        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 event_sender, event_receiver) = mpsc::unbounded(); -    let (control_sender, mut control_receiver) = mpsc::unbounded(); +        fn user_event( +            &mut self, +            event_loop: &winit::event_loop::ActiveEventLoop, +            message: Message, +        ) { +            self.process_event( +                event_loop, +                winit::event::Event::UserEvent(message), +            ); +        } -    let mut instance = Box::pin(run_instance::<A, E, C>( -        application, -        compositor, -        renderer, -        runtime, -        proxy, -        debug, -        event_receiver, -        control_sender, -        init_command, -        window, -        should_be_visible, -        exit_on_close_request, -    )); +        fn about_to_wait( +            &mut self, +            event_loop: &winit::event_loop::ActiveEventLoop, +        ) { +            self.process_event(event_loop, winit::event::Event::AboutToWait); +        } +    } -    let mut context = task::Context::from_waker(task::noop_waker_ref()); +    impl<Message, F, C> Runner<Message, F, C> +    where +        F: Future<Output = ()>, +    { +        fn process_event( +            &mut self, +            event_loop: &winit::event_loop::ActiveEventLoop, +            event: winit::event::Event<Message>, +        ) { +            // 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); + +                // 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 +450,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); -    // 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")] +        runner.error.map(Err).unwrap_or(Ok(())) +    } + +    #[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<C> { +    window: Arc<winit::window::Window>, +    compositor: C, +    should_be_visible: bool, +    exit_on_close_request: bool,  }  async fn run_instance<A, E, C>(      mut application: A, -    mut compositor: C, -    mut renderer: A::Renderer,      mut runtime: Runtime<E, Proxy<A::Message>, A::Message>, -    mut proxy: winit::event_loop::EventLoopProxy<A::Message>, +    mut proxy: Proxy<A::Message>,      mut debug: Debug, +    mut boot: oneshot::Receiver<Boot<C>>,      mut event_receiver: mpsc::UnboundedReceiver<          winit::event::Event<A::Message>,      >,      mut control_sender: mpsc::UnboundedSender<winit::event_loop::ControlFlow>,      init_command: Command<A::Message>, -    window: Arc<winit::window::Window>, -    should_be_visible: bool, -    exit_on_close_request: bool, +    fonts: Vec<Cow<'static, [u8]>>,  ) where      A: Application + 'static,      E: Executor + 'static, @@ -326,6 +499,19 @@ async fn run_instance<A, E, C>(      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(); @@ -371,6 +557,7 @@ async fn run_instance<A, E, C>(      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(); @@ -397,6 +584,7 @@ async fn run_instance<A, E, C>(              }              event::Event::UserEvent(message) => {                  messages.push(message); +                user_events += 1;              }              event::Event::WindowEvent {                  event: event::WindowEvent::RedrawRequested { .. }, @@ -478,7 +666,7 @@ async fn run_instance<A, E, C>(                  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,                      )); @@ -594,6 +782,11 @@ async fn run_instance<A, E, C>(                      if should_exit {                          break;                      } + +                    if user_events > 0 { +                        proxy.free_slots(user_events); +                        user_events = 0; +                    }                  }                  if !redraw_pending { @@ -668,7 +861,7 @@ pub fn update<A: Application, C, E: Executor>(      runtime: &mut Runtime<E, Proxy<A::Message>, A::Message>,      clipboard: &mut Clipboard,      should_exit: &mut bool, -    proxy: &mut winit::event_loop::EventLoopProxy<A::Message>, +    proxy: &mut Proxy<A::Message>,      debug: &mut Debug,      messages: &mut Vec<A::Message>,      window: &winit::window::Window, @@ -718,7 +911,7 @@ pub fn run_command<A, C, E>(      runtime: &mut Runtime<E, Proxy<A::Message>, A::Message>,      clipboard: &mut Clipboard,      should_exit: &mut bool, -    proxy: &mut winit::event_loop::EventLoopProxy<A::Message>, +    proxy: &mut Proxy<A::Message>,      debug: &mut Debug,      window: &winit::window::Window,  ) where @@ -743,9 +936,7 @@ pub fn run_command<A, C, E>(                  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); @@ -775,25 +966,16 @@ pub fn run_command<A, C, E>(                      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); @@ -809,9 +991,7 @@ pub fn run_command<A, C, E>(                          })                          .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 { @@ -836,9 +1016,7 @@ pub fn run_command<A, C, E>(                          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()); @@ -866,17 +1044,13 @@ pub fn run_command<A, C, E>(                      }                  }                  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));                      }                  } @@ -889,12 +1063,10 @@ pub fn run_command<A, C, E>(                          &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 { @@ -902,7 +1074,7 @@ pub fn run_command<A, C, E>(                      #[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 = @@ -910,9 +1082,7 @@ pub fn run_command<A, C, E>(                              let message = _tag(information); -                            proxy -                                .send_event(message) -                                .expect("Send message to event loop"); +                            proxy.send(message);                          });                      }                  } @@ -935,9 +1105,7 @@ pub fn run_command<A, C, E>(                      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); @@ -949,14 +1117,10 @@ pub fn run_command<A, C, E>(                  *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(()))) -                    .expect("Send message to event loop"); +                proxy.send(tagger(Ok(())));              }              command::Action::Custom(_) => {                  log::warn!("Unsupported custom action in `iced_winit` shell"); diff --git a/winit/src/conversion.rs b/winit/src/conversion.rs index fc3d1c08..ea33e610 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<winit::monitor::MonitorHandle>,      _id: Option<String>, -) -> 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. @@ -327,6 +323,35 @@ pub fn position(                  y: f64::from(position.y),              }))          } +        window::Position::SpecificWith(to_position) => { +            if let Some(monitor) = monitor { +                let start = monitor.position(); + +                let resolution: winit::dpi::LogicalSize<f32> = +                    monitor.size().to_logical(monitor.scale_factor()); + +                let position = to_position( +                    size, +                    Size::new(resolution.width, resolution.height), +                ); + +                let centered: winit::dpi::PhysicalPosition<i32> = +                    winit::dpi::LogicalPosition { +                        x: position.x, +                        y: position.y, +                    } +                    .to_physical(monitor.scale_factor()); + +                Some(winit::dpi::Position::Physical( +                    winit::dpi::PhysicalPosition { +                        x: start.x + centered.x, +                        y: start.y + centered.y, +                    }, +                )) +            } else { +                None +            } +        }          window::Position::Centered => {              if let Some(monitor) = monitor {                  let start = monitor.position(); @@ -396,7 +421,9 @@ pub fn mouse_interaction(      use mouse::Interaction;      match interaction { -        Interaction::Idle => winit::window::CursorIcon::Default, +        Interaction::None | Interaction::Idle => { +            winit::window::CursorIcon::Default +        }          Interaction::Pointer => winit::window::CursorIcon::Pointer,          Interaction::Working => winit::window::CursorIcon::Progress,          Interaction::Grab => winit::window::CursorIcon::Grab, diff --git a/winit/src/lib.rs b/winit/src/lib.rs index 64912b3f..3619cde8 100644 --- a/winit/src/lib.rs +++ b/winit/src/lib.rs @@ -17,14 +17,6 @@  #![doc(      html_logo_url = "https://raw.githubusercontent.com/iced-rs/iced/9ab6923e943f784985e9ef9ca28b10278297225d/docs/logo.svg"  )] -#![forbid(rust_2018_idioms)] -#![deny( -    missing_debug_implementations, -    missing_docs, -    unused_results, -    unsafe_code, -    rustdoc::broken_intra_doc_links -)]  #![cfg_attr(docsrs, feature(doc_auto_cfg))]  pub use iced_graphics as graphics;  pub use iced_runtime as runtime; diff --git a/winit/src/multi_window.rs b/winit/src/multi_window.rs index 18db1fb5..95d78b83 100644 --- a/winit/src/multi_window.rs +++ b/winit/src/multi_window.rs @@ -12,10 +12,12 @@ 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};  use crate::futures::{Executor, Runtime, Subscription}; +use crate::graphics;  use crate::graphics::{compositor, Compositor};  use crate::multi_window::window_manager::WindowManager;  use crate::runtime::command::{self, Command}; @@ -26,7 +28,7 @@ use crate::{Clipboard, Error, Proxy, Settings};  pub use crate::application::{default, Appearance, DefaultStyle}; -use std::collections::HashMap; +use rustc_hash::FxHashMap;  use std::mem::ManuallyDrop;  use std::sync::Arc;  use std::time::Instant; @@ -105,7 +107,7 @@ where  /// settings.  pub fn run<A, E, C>(      settings: Settings<A::Flags>, -    compositor_settings: C::Settings, +    graphics_settings: graphics::Settings,  ) -> Result<(), Error>  where      A: Application + 'static, @@ -113,22 +115,22 @@ where      C: Compositor<Renderer = A::Renderer> + '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"); -    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) = { @@ -137,187 +139,292 @@ 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::<A, E, C>( +        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()); + +    struct Runner<Message: 'static, F, C> { +        instance: std::pin::Pin<Box<F>>, +        context: task::Context<'static>, +        boot: Option<BootConfig<C>>, +        sender: mpsc::UnboundedSender<Event<Message>>, +        receiver: mpsc::UnboundedReceiver<Control>, +        error: Option<Error>, +    } + +    struct BootConfig<C> { +        sender: oneshot::Sender<Boot<C>>, +        id: Option<String>, +        title: String, +        window_settings: window::Settings, +        graphics_settings: graphics::Settings, +    } -    #[cfg(target_arch = "wasm32")] +    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, +    }; + +    impl<Message, F, C> winit::application::ApplicationHandler<Message> +        for Runner<Message, F, C> +    where +        F: Future<Output = ()>, +        C: Compositor,      { -        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 = main_window.canvas(); +            let should_be_visible = window_settings.visible; +            let exit_on_close_request = window_settings.exit_on_close_request; -        let window = web_sys::window().unwrap(); -        let document = window.document().unwrap(); -        let body = document.body().unwrap(); +            let window_attributes = conversion::window_attributes( +                window_settings, +                &title, +                event_loop.primary_monitor(), +                id, +            ) +            .with_visible(false); -        let target = target.and_then(|target| { -            body.query_selector(&format!("#{}", target)) -                .ok() -                .unwrap_or(None) -        }); +            log::debug!("Window attributes: {window_attributes:#?}"); -        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 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 compositor = -        executor::block_on(C::new(compositor_settings, main_window.clone()))?; +            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 window_manager = WindowManager::new(); -    let _ = window_manager.insert( -        window::Id::MAIN, -        main_window, -        &application, -        &mut compositor, -        exit_on_close_request, -    ); +            if let Err(error) = executor::block_on(finish_boot) { +                self.error = Some(Error::GraphicsCreationFailed(error)); +                event_loop.exit(); +            } +        } -    let (mut event_sender, event_receiver) = mpsc::unbounded(); -    let (control_sender, mut control_receiver) = mpsc::unbounded(); +        fn new_events( +            &mut self, +            event_loop: &winit::event_loop::ActiveEventLoop, +            cause: winit::event::StartCause, +        ) { +            if self.boot.is_some() { +                return; +            } -    let mut instance = Box::pin(run_instance::<A, E, C>( -        application, -        compositor, -        runtime, -        proxy, -        debug, -        event_receiver, -        control_sender, -        init_command, -        window_manager, -        should_main_be_visible, -    )); +            self.process_event( +                event_loop, +                Event::EventLoopAwakened(winit::event::Event::NewEvents(cause)), +            ); +        } + +        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, +                        ), +                    ); +                } +            } +        } -    let mut context = task::Context::from_waker(task::noop_waker_ref()); +        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, +                )), +            ); +        } -    let process_event = move |event, event_loop: &winit::event_loop::EventLoopWindowTarget<_>| { -        if event_loop.exiting() { -            return; +        fn about_to_wait( +            &mut self, +            event_loop: &winit::event_loop::ActiveEventLoop, +        ) { +            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); +    impl<Message, F, C> Runner<Message, F, C> +    where +        F: Future<Output = ()>, +        C: Compositor, +    { +        fn process_event( +            &mut self, +            event_loop: &winit::event_loop::ActiveEventLoop, +            event: Event<Message>, +        ) { +            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<C> { +    window: Arc<winit::window::Window>, +    compositor: C, +    should_be_visible: bool, +    exit_on_close_request: bool, +} +  enum Event<Message: 'static> {      WindowCreated {          id: window::Id, @@ -340,15 +447,13 @@ enum Control {  async fn run_instance<A, E, C>(      mut application: A, -    mut compositor: C,      mut runtime: Runtime<E, Proxy<A::Message>, A::Message>, -    mut proxy: winit::event_loop::EventLoopProxy<A::Message>, +    mut proxy: Proxy<A::Message>,      mut debug: Debug, +    mut boot: oneshot::Receiver<Boot<C>>,      mut event_receiver: mpsc::UnboundedReceiver<Event<A::Message>>,      mut control_sender: mpsc::UnboundedSender<Control>,      init_command: Command<A::Message>, -    mut window_manager: WindowManager<A, C>, -    should_main_window_be_visible: bool,  ) where      A: Application + 'static,      E: Executor + 'static, @@ -358,11 +463,28 @@ async fn run_instance<A, E, C>(      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, +        &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);      } @@ -380,12 +502,12 @@ async fn run_instance<A, E, C>(          )]      }; -    let mut ui_caches = HashMap::new(); +    let mut ui_caches = FxHashMap::default();      let mut user_interfaces = ManuallyDrop::new(build_user_interfaces(          &application,          &mut debug,          &mut window_manager, -        HashMap::from_iter([( +        FxHashMap::from_iter([(              window::Id::MAIN,              user_interface::Cache::default(),          )]), @@ -407,6 +529,7 @@ async fn run_instance<A, E, C>(      runtime.track(application.subscription().into_recipes());      let mut messages = Vec::new(); +    let mut user_events = 0;      debug.startup_finished(); @@ -481,6 +604,7 @@ async fn run_instance<A, E, C>(                      }                      event::Event::UserEvent(message) => {                          messages.push(message); +                        user_events += 1;                      }                      event::Event::WindowEvent {                          window_id: id, @@ -529,7 +653,7 @@ async fn run_instance<A, E, C>(                          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,                                  ), @@ -600,7 +724,7 @@ async fn run_instance<A, E, C>(                              if new_mouse_interaction != window.mouse_interaction                              { -                                window.raw.set_cursor_icon( +                                window.raw.set_cursor(                                      conversion::mouse_interaction(                                          new_mouse_interaction,                                      ), @@ -758,7 +882,7 @@ async fn run_instance<A, E, C>(                          // TODO mw application update returns which window IDs to update                          if !messages.is_empty() || uis_stale { -                            let mut cached_interfaces: HashMap< +                            let mut cached_interfaces: FxHashMap<                                  window::Id,                                  user_interface::Cache,                              > = ManuallyDrop::into_inner(user_interfaces) @@ -802,6 +926,11 @@ async fn run_instance<A, E, C>(                                      &mut window_manager,                                      cached_interfaces,                                  )); + +                            if user_events > 0 { +                                proxy.free_slots(user_events); +                                user_events = 0; +                            }                          }                      }                      _ => {} @@ -844,11 +973,11 @@ fn update<A: Application, C, E: Executor>(      runtime: &mut Runtime<E, Proxy<A::Message>, A::Message>,      clipboard: &mut Clipboard,      control_sender: &mut mpsc::UnboundedSender<Control>, -    proxy: &mut winit::event_loop::EventLoopProxy<A::Message>, +    proxy: &mut Proxy<A::Message>,      debug: &mut Debug,      messages: &mut Vec<A::Message>,      window_manager: &mut WindowManager<A, C>, -    ui_caches: &mut HashMap<window::Id, user_interface::Cache>, +    ui_caches: &mut FxHashMap<window::Id, user_interface::Cache>,  ) where      C: Compositor<Renderer = A::Renderer> + 'static,      A::Theme: DefaultStyle, @@ -886,10 +1015,10 @@ fn run_command<A, C, E>(      runtime: &mut Runtime<E, Proxy<A::Message>, A::Message>,      clipboard: &mut Clipboard,      control_sender: &mut mpsc::UnboundedSender<Control>, -    proxy: &mut winit::event_loop::EventLoopProxy<A::Message>, +    proxy: &mut Proxy<A::Message>,      debug: &mut Debug,      window_manager: &mut WindowManager<A, C>, -    ui_caches: &mut HashMap<window::Id, user_interface::Cache>, +    ui_caches: &mut FxHashMap<window::Id, user_interface::Cache>,  ) where      A: Application,      E: Executor, @@ -912,9 +1041,7 @@ fn run_command<A, C, E>(                  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); @@ -966,18 +1093,12 @@ fn run_command<A, C, E>(                              .to_logical(window.raw.scale_factor());                          proxy -                            .send_event(callback(Size::new( -                                size.width, -                                size.height, -                            ))) -                            .expect("Send message to event loop"); +                            .send(callback(Size::new(size.width, size.height)));                      }                  }                  window::Action::FetchMaximized(id, callback) => {                      if let Some(window) = window_manager.get_mut(id) { -                        proxy -                            .send_event(callback(window.raw.is_maximized())) -                            .expect("Send message to event loop"); +                        proxy.send(callback(window.raw.is_maximized()));                      }                  }                  window::Action::Maximize(id, maximized) => { @@ -987,9 +1108,7 @@ fn run_command<A, C, E>(                  }                  window::Action::FetchMinimized(id, callback) => {                      if let Some(window) = window_manager.get_mut(id) { -                        proxy -                            .send_event(callback(window.raw.is_minimized())) -                            .expect("Send message to event loop"); +                        proxy.send(callback(window.raw.is_minimized()));                      }                  }                  window::Action::Minimize(id, minimized) => { @@ -1011,9 +1130,7 @@ fn run_command<A, C, E>(                              })                              .ok(); -                        proxy -                            .send_event(callback(position)) -                            .expect("Send message to event loop"); +                        proxy.send(callback(position));                      }                  }                  window::Action::Move(id, position) => { @@ -1048,9 +1165,7 @@ fn run_command<A, C, E>(                              core::window::Mode::Hidden                          }; -                        proxy -                            .send_event(tag(mode)) -                            .expect("Send message to event loop"); +                        proxy.send(tag(mode));                      }                  }                  window::Action::ToggleMaximize(id) => { @@ -1098,9 +1213,7 @@ fn run_command<A, C, E>(                  }                  window::Action::FetchId(id, tag) => {                      if let Some(window) = window_manager.get_mut(id) { -                        proxy -                            .send_event(tag(window.raw.id().into())) -                            .expect("Send message to event loop"); +                        proxy.send(tag(window.raw.id().into()));                      }                  }                  window::Action::RunWithHandle(id, tag) => { @@ -1110,9 +1223,7 @@ fn run_command<A, C, E>(                          .get_mut(id)                          .and_then(|window| window.raw.window_handle().ok())                      { -                        proxy -                            .send_event(tag(&handle)) -                            .expect("Send message to event loop"); +                        proxy.send(tag(handle));                      }                  }                  window::Action::Screenshot(id, tag) => { @@ -1125,12 +1236,10 @@ fn run_command<A, C, E>(                              &debug.overlay(),                          ); -                        proxy -                            .send_event(tag(window::Screenshot::new( -                                bytes, -                                window.state.physical_size(), -                            ))) -                            .expect("Event loop doesn't exist."); +                        proxy.send(tag(window::Screenshot::new( +                            bytes, +                            window.state.physical_size(), +                        )));                      }                  }              }, @@ -1139,7 +1248,7 @@ fn run_command<A, C, E>(                      #[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 = @@ -1147,9 +1256,7 @@ fn run_command<A, C, E>(                              let message = _tag(information); -                            proxy -                                .send_event(message) -                                .expect("Event loop doesn't exist."); +                            proxy.send(message);                          });                      }                  } @@ -1174,9 +1281,7 @@ fn run_command<A, C, E>(                              match operation.finish() {                                  operation::Outcome::None => {}                                  operation::Outcome::Some(message) => { -                                    proxy -                                        .send_event(message) -                                        .expect("Event loop doesn't exist."); +                                    proxy.send(message);                                      // operation completed, don't need to try to operate on rest of UIs                                      break 'operate; @@ -1193,17 +1298,10 @@ fn run_command<A, C, E>(                      uis.drain().map(|(id, ui)| (id, ui.into_cache())).collect();              }              command::Action::LoadFont { bytes, tagger } => { -                use crate::core::text::Renderer; - -                // TODO change this once we change each renderer to having a single backend reference.. :pain:                  // TODO: Error handling (?) -                for (_, window) in window_manager.iter_mut() { -                    window.renderer.load_font(bytes.clone()); -                } +                compositor.load_font(bytes.clone()); -                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"); @@ -1213,12 +1311,12 @@ fn run_command<A, C, E>(  }  /// Build the user interface for every window. -pub fn build_user_interfaces<'a, A: Application, C: Compositor>( +pub fn build_user_interfaces<'a, A: Application, C>(      application: &'a A,      debug: &mut Debug,      window_manager: &mut WindowManager<A, C>, -    mut cached_user_interfaces: HashMap<window::Id, user_interface::Cache>, -) -> HashMap<window::Id, UserInterface<'a, A::Message, A::Theme, A::Renderer>> +    mut cached_user_interfaces: FxHashMap<window::Id, user_interface::Cache>, +) -> FxHashMap<window::Id, UserInterface<'a, A::Message, A::Theme, A::Renderer>>  where      C: Compositor<Renderer = A::Renderer>,      A::Theme: DefaultStyle, diff --git a/winit/src/multi_window/window_manager.rs b/winit/src/multi_window/window_manager.rs index 71c1688b..57a7dc7e 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<A: Application, C: Compositor> +pub struct WindowManager<A, C>  where +    A: Application,      C: Compositor<Renderer = A::Renderer>,      A::Theme: DefaultStyle,  { @@ -60,7 +61,7 @@ where                  exit_on_close_request,                  surface,                  renderer, -                mouse_interaction: mouse::Interaction::Idle, +                mouse_interaction: mouse::Interaction::None,              },          ); diff --git a/winit/src/proxy.rs b/winit/src/proxy.rs index 1d6c48bb..3edc30ad 100644 --- a/winit/src/proxy.rs +++ b/winit/src/proxy.rs @@ -1,28 +1,94 @@  use crate::futures::futures::{      channel::mpsc, +    select,      task::{Context, Poll}, -    Sink, +    Future, Sink, StreamExt,  };  use std::pin::Pin; -/// An event loop proxy that implements `Sink`. +/// An event loop proxy with backpressure that implements `Sink`.  #[derive(Debug)]  pub struct Proxy<Message: 'static> {      raw: winit::event_loop::EventLoopProxy<Message>, +    sender: mpsc::Sender<Message>, +    notifier: mpsc::Sender<usize>,  }  impl<Message: 'static> Clone for Proxy<Message> {      fn clone(&self) -> Self {          Self {              raw: self.raw.clone(), +            sender: self.sender.clone(), +            notifier: self.notifier.clone(),          }      }  }  impl<Message: 'static> Proxy<Message> { +    const MAX_SIZE: usize = 100; +      /// Creates a new [`Proxy`] from an `EventLoopProxy`. -    pub fn new(raw: winit::event_loop::EventLoopProxy<Message>) -> Self { -        Self { raw } +    pub fn new( +        raw: winit::event_loop::EventLoopProxy<Message>, +    ) -> (Self, impl Future<Output = ()>) { +        let (notifier, mut processed) = mpsc::channel(Self::MAX_SIZE); +        let (sender, mut receiver) = mpsc::channel(Self::MAX_SIZE); +        let proxy = raw.clone(); + +        let worker = async move { +            let mut count = 0; + +            loop { +                if count < Self::MAX_SIZE { +                    select! { +                        message = receiver.select_next_some() => { +                            let _ = proxy.send_event(message); +                            count += 1; + +                        } +                        amount = processed.select_next_some() => { +                            count = count.saturating_sub(amount); +                        } +                        complete => break, +                    } +                } else { +                    select! { +                        amount = processed.select_next_some() => { +                            count = count.saturating_sub(amount); +                        } +                        complete => break, +                    } +                } +            } +        }; + +        ( +            Self { +                raw, +                sender, +                notifier, +            }, +            worker, +        ) +    } + +    /// Sends a `Message` to the event loop. +    /// +    /// Note: This skips the backpressure mechanism with an unbounded +    /// channel. Use sparingly! +    pub fn send(&mut self, message: Message) +    where +        Message: std::fmt::Debug, +    { +        self.raw +            .send_event(message) +            .expect("Send message to event loop"); +    } + +    /// Frees an amount of slots for additional messages to be queued in +    /// this [`Proxy`]. +    pub fn free_slots(&mut self, amount: usize) { +        let _ = self.notifier.start_send(amount);      }  } @@ -30,32 +96,37 @@ impl<Message: 'static> Sink<Message> for Proxy<Message> {      type Error = mpsc::SendError;      fn poll_ready( -        self: Pin<&mut Self>, -        _cx: &mut Context<'_>, +        mut self: Pin<&mut Self>, +        cx: &mut Context<'_>,      ) -> Poll<Result<(), Self::Error>> { -        Poll::Ready(Ok(())) +        self.sender.poll_ready(cx)      }      fn start_send( -        self: Pin<&mut Self>, +        mut self: Pin<&mut Self>,          message: Message,      ) -> Result<(), Self::Error> { -        let _ = self.raw.send_event(message); - -        Ok(()) +        self.sender.start_send(message)      }      fn poll_flush( -        self: Pin<&mut Self>, -        _cx: &mut Context<'_>, +        mut self: Pin<&mut Self>, +        cx: &mut Context<'_>,      ) -> Poll<Result<(), Self::Error>> { -        Poll::Ready(Ok(())) +        match self.sender.poll_ready(cx) { +            Poll::Ready(Err(ref e)) if e.is_disconnected() => { +                // If the receiver disconnected, we consider the sink to be flushed. +                Poll::Ready(Ok(())) +            } +            x => x, +        }      }      fn poll_close( -        self: Pin<&mut Self>, +        mut self: Pin<&mut Self>,          _cx: &mut Context<'_>,      ) -> Poll<Result<(), Self::Error>> { +        self.sender.disconnect();          Poll::Ready(Ok(()))      }  } | 
