summaryrefslogtreecommitdiffstats
path: root/winit
diff options
context:
space:
mode:
Diffstat (limited to 'winit')
-rw-r--r--winit/Cargo.toml7
-rw-r--r--winit/src/application.rs484
-rw-r--r--winit/src/conversion.rs91
-rw-r--r--winit/src/lib.rs8
-rw-r--r--winit/src/multi_window.rs546
-rw-r--r--winit/src/multi_window/window_manager.rs5
-rw-r--r--winit/src/proxy.rs101
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(()))
}
}