iced_winit/
lib.rs

1//! A windowing shell for Iced, on top of [`winit`].
2//!
3//! ![The native path of the Iced ecosystem](https://github.com/iced-rs/iced/blob/0525d76ff94e828b7b21634fa94a747022001c83/docs/graphs/native.png?raw=true)
4//!
5//! `iced_winit` offers some convenient abstractions on top of [`iced_runtime`]
6//! to quickstart development when using [`winit`].
7//!
8//! It exposes a renderer-agnostic [`Program`] trait that can be implemented
9//! and then run with a simple call. The use of this trait is optional.
10//!
11//! Additionally, a [`conversion`] module is available for users that decide to
12//! implement a custom event loop.
13//!
14//! [`iced_runtime`]: https://github.com/iced-rs/iced/tree/0.13/runtime
15//! [`winit`]: https://github.com/rust-windowing/winit
16//! [`conversion`]: crate::conversion
17#![doc(
18    html_logo_url = "https://raw.githubusercontent.com/iced-rs/iced/9ab6923e943f784985e9ef9ca28b10278297225d/docs/logo.svg"
19)]
20#![cfg_attr(docsrs, feature(doc_auto_cfg))]
21pub use iced_program as program;
22pub use program::core;
23pub use program::graphics;
24pub use program::runtime;
25pub use runtime::debug;
26pub use runtime::futures;
27pub use winit;
28
29pub mod clipboard;
30pub mod conversion;
31
32#[cfg(feature = "system")]
33pub mod system;
34
35mod error;
36mod proxy;
37mod window;
38
39pub use clipboard::Clipboard;
40pub use error::Error;
41pub use proxy::Proxy;
42
43use crate::core::mouse;
44use crate::core::renderer;
45use crate::core::theme;
46use crate::core::time::Instant;
47use crate::core::widget::operation;
48use crate::core::{Point, Settings, Size};
49use crate::futures::futures::channel::mpsc;
50use crate::futures::futures::channel::oneshot;
51use crate::futures::futures::task;
52use crate::futures::futures::{Future, StreamExt};
53use crate::futures::subscription;
54use crate::futures::{Executor, Runtime};
55use crate::graphics::{Compositor, compositor};
56use crate::runtime::user_interface::{self, UserInterface};
57use crate::runtime::{Action, Task};
58
59use program::Program;
60use window::WindowManager;
61
62use rustc_hash::FxHashMap;
63use std::borrow::Cow;
64use std::mem::ManuallyDrop;
65use std::sync::Arc;
66
67/// Runs a [`Program`] with the provided settings.
68pub fn run<P>(
69    program: P,
70    settings: Settings,
71    window_settings: Option<window::Settings>,
72) -> Result<(), Error>
73where
74    P: Program + 'static,
75    P::Theme: theme::Base,
76{
77    use winit::event_loop::EventLoop;
78
79    debug::init(P::name());
80
81    let boot_span = debug::boot();
82
83    let graphics_settings = settings.clone().into();
84    let event_loop = EventLoop::with_user_event()
85        .build()
86        .expect("Create event loop");
87
88    let (proxy, worker) = Proxy::new(event_loop.create_proxy());
89
90    let mut runtime = {
91        let executor =
92            P::Executor::new().map_err(Error::ExecutorCreationFailed)?;
93        executor.spawn(worker);
94
95        Runtime::new(executor, proxy.clone())
96    };
97
98    let (program, task) = runtime.enter(|| program::Instance::new(program));
99    let is_daemon = window_settings.is_none();
100
101    let task = if let Some(window_settings) = window_settings {
102        let mut task = Some(task);
103
104        let (_id, open) = runtime::window::open(window_settings);
105
106        open.then(move |_| task.take().unwrap_or(Task::none()))
107    } else {
108        task
109    };
110
111    if let Some(stream) = runtime::task::into_stream(task) {
112        runtime.run(stream);
113    }
114
115    runtime.track(subscription::into_recipes(
116        runtime.enter(|| program.subscription().map(Action::Output)),
117    ));
118
119    let (event_sender, event_receiver) = mpsc::unbounded();
120    let (control_sender, control_receiver) = mpsc::unbounded();
121
122    let instance = Box::pin(run_instance::<P>(
123        program,
124        runtime,
125        proxy.clone(),
126        event_receiver,
127        control_sender,
128        is_daemon,
129        graphics_settings,
130        settings.fonts,
131    ));
132
133    let context = task::Context::from_waker(task::noop_waker_ref());
134
135    struct Runner<Message: 'static, F> {
136        instance: std::pin::Pin<Box<F>>,
137        context: task::Context<'static>,
138        id: Option<String>,
139        sender: mpsc::UnboundedSender<Event<Action<Message>>>,
140        receiver: mpsc::UnboundedReceiver<Control>,
141        error: Option<Error>,
142
143        #[cfg(target_arch = "wasm32")]
144        canvas: Option<web_sys::HtmlCanvasElement>,
145    }
146
147    let runner = Runner {
148        instance,
149        context,
150        id: settings.id,
151        sender: event_sender,
152        receiver: control_receiver,
153        error: None,
154
155        #[cfg(target_arch = "wasm32")]
156        canvas: None,
157    };
158
159    boot_span.finish();
160
161    impl<Message, F> winit::application::ApplicationHandler<Action<Message>>
162        for Runner<Message, F>
163    where
164        Message: std::fmt::Debug,
165        F: Future<Output = ()>,
166    {
167        fn resumed(
168            &mut self,
169            _event_loop: &winit::event_loop::ActiveEventLoop,
170        ) {
171        }
172
173        fn new_events(
174            &mut self,
175            event_loop: &winit::event_loop::ActiveEventLoop,
176            cause: winit::event::StartCause,
177        ) {
178            self.process_event(
179                event_loop,
180                Event::EventLoopAwakened(winit::event::Event::NewEvents(cause)),
181            );
182        }
183
184        fn window_event(
185            &mut self,
186            event_loop: &winit::event_loop::ActiveEventLoop,
187            window_id: winit::window::WindowId,
188            event: winit::event::WindowEvent,
189        ) {
190            #[cfg(target_os = "windows")]
191            let is_move_or_resize = matches!(
192                event,
193                winit::event::WindowEvent::Resized(_)
194                    | winit::event::WindowEvent::Moved(_)
195            );
196
197            self.process_event(
198                event_loop,
199                Event::EventLoopAwakened(winit::event::Event::WindowEvent {
200                    window_id,
201                    event,
202                }),
203            );
204
205            // TODO: Remove when unnecessary
206            // On Windows, we emulate an `AboutToWait` event after every `Resized` event
207            // since the event loop does not resume during resize interaction.
208            // More details: https://github.com/rust-windowing/winit/issues/3272
209            #[cfg(target_os = "windows")]
210            {
211                if is_move_or_resize {
212                    self.process_event(
213                        event_loop,
214                        Event::EventLoopAwakened(
215                            winit::event::Event::AboutToWait,
216                        ),
217                    );
218                }
219            }
220        }
221
222        fn user_event(
223            &mut self,
224            event_loop: &winit::event_loop::ActiveEventLoop,
225            action: Action<Message>,
226        ) {
227            self.process_event(
228                event_loop,
229                Event::EventLoopAwakened(winit::event::Event::UserEvent(
230                    action,
231                )),
232            );
233        }
234
235        fn received_url(
236            &mut self,
237            event_loop: &winit::event_loop::ActiveEventLoop,
238            url: String,
239        ) {
240            self.process_event(
241                event_loop,
242                Event::EventLoopAwakened(
243                    winit::event::Event::PlatformSpecific(
244                        winit::event::PlatformSpecific::MacOS(
245                            winit::event::MacOS::ReceivedUrl(url),
246                        ),
247                    ),
248                ),
249            );
250        }
251
252        fn about_to_wait(
253            &mut self,
254            event_loop: &winit::event_loop::ActiveEventLoop,
255        ) {
256            self.process_event(
257                event_loop,
258                Event::EventLoopAwakened(winit::event::Event::AboutToWait),
259            );
260        }
261    }
262
263    impl<Message, F> Runner<Message, F>
264    where
265        F: Future<Output = ()>,
266    {
267        fn process_event(
268            &mut self,
269            event_loop: &winit::event_loop::ActiveEventLoop,
270            event: Event<Action<Message>>,
271        ) {
272            if event_loop.exiting() {
273                return;
274            }
275
276            self.sender.start_send(event).expect("Send event");
277
278            loop {
279                let poll = self.instance.as_mut().poll(&mut self.context);
280
281                match poll {
282                    task::Poll::Pending => match self.receiver.try_next() {
283                        Ok(Some(control)) => match control {
284                            Control::ChangeFlow(flow) => {
285                                use winit::event_loop::ControlFlow;
286
287                                match (event_loop.control_flow(), flow) {
288                                    (
289                                        ControlFlow::WaitUntil(current),
290                                        ControlFlow::WaitUntil(new),
291                                    ) if current < new => {}
292                                    (
293                                        ControlFlow::WaitUntil(target),
294                                        ControlFlow::Wait,
295                                    ) if target > Instant::now() => {}
296                                    _ => {
297                                        event_loop.set_control_flow(flow);
298                                    }
299                                }
300                            }
301                            Control::CreateWindow {
302                                id,
303                                settings,
304                                title,
305                                monitor,
306                                on_open,
307                            } => {
308                                let exit_on_close_request =
309                                    settings.exit_on_close_request;
310
311                                let visible = settings.visible;
312
313                                #[cfg(target_arch = "wasm32")]
314                                let target =
315                                    settings.platform_specific.target.clone();
316
317                                let window_attributes =
318                                    conversion::window_attributes(
319                                        settings,
320                                        &title,
321                                        monitor
322                                            .or(event_loop.primary_monitor()),
323                                        self.id.clone(),
324                                    )
325                                    .with_visible(false);
326
327                                #[cfg(target_arch = "wasm32")]
328                                let window_attributes = {
329                                    use winit::platform::web::WindowAttributesExtWebSys;
330                                    window_attributes
331                                        .with_canvas(self.canvas.take())
332                                };
333
334                                log::info!(
335                                    "Window attributes for id `{id:#?}`: {window_attributes:#?}"
336                                );
337
338                                // On macOS, the `position` in `WindowAttributes` represents the "inner"
339                                // position of the window; while on other platforms it's the "outer" position.
340                                // We fix the inconsistency on macOS by positioning the window after creation.
341                                #[cfg(target_os = "macos")]
342                                let mut window_attributes = window_attributes;
343
344                                #[cfg(target_os = "macos")]
345                                let position =
346                                    window_attributes.position.take();
347
348                                let window = event_loop
349                                    .create_window(window_attributes)
350                                    .expect("Create window");
351
352                                #[cfg(target_os = "macos")]
353                                if let Some(position) = position {
354                                    window.set_outer_position(position);
355                                }
356
357                                #[cfg(target_arch = "wasm32")]
358                                {
359                                    use winit::platform::web::WindowExtWebSys;
360
361                                    let canvas = window
362                                        .canvas()
363                                        .expect("Get window canvas");
364
365                                    let _ = canvas.set_attribute(
366                                        "style",
367                                        "display: block; width: 100%; height: 100%",
368                                    );
369
370                                    let window = web_sys::window().unwrap();
371                                    let document = window.document().unwrap();
372                                    let body = document.body().unwrap();
373
374                                    let target = target.and_then(|target| {
375                                        body.query_selector(&format!(
376                                            "#{target}"
377                                        ))
378                                        .ok()
379                                        .unwrap_or(None)
380                                    });
381
382                                    match target {
383                                        Some(node) => {
384                                            let _ = node
385                                                .replace_with_with_node_1(
386                                                    &canvas,
387                                                )
388                                                .expect(&format!(
389                                                    "Could not replace #{}",
390                                                    node.id()
391                                                ));
392                                        }
393                                        None => {
394                                            let _ = body
395                                                .append_child(&canvas)
396                                                .expect(
397                                                "Append canvas to HTML body",
398                                            );
399                                        }
400                                    };
401                                }
402
403                                self.process_event(
404                                    event_loop,
405                                    Event::WindowCreated {
406                                        id,
407                                        window: Arc::new(window),
408                                        exit_on_close_request,
409                                        make_visible: visible,
410                                        on_open,
411                                    },
412                                );
413                            }
414                            Control::Exit => {
415                                event_loop.exit();
416                            }
417                            Control::Crash(error) => {
418                                self.error = Some(error);
419                                event_loop.exit();
420                            }
421                        },
422                        _ => {
423                            break;
424                        }
425                    },
426                    task::Poll::Ready(_) => {
427                        event_loop.exit();
428                        break;
429                    }
430                };
431            }
432        }
433    }
434
435    #[cfg(not(target_arch = "wasm32"))]
436    {
437        let mut runner = runner;
438        let _ = event_loop.run_app(&mut runner);
439
440        runner.error.map(Err).unwrap_or(Ok(()))
441    }
442
443    #[cfg(target_arch = "wasm32")]
444    {
445        use winit::platform::web::EventLoopExtWebSys;
446        let _ = event_loop.spawn_app(runner);
447
448        Ok(())
449    }
450}
451
452#[derive(Debug)]
453enum Event<Message: 'static> {
454    WindowCreated {
455        id: window::Id,
456        window: Arc<winit::window::Window>,
457        exit_on_close_request: bool,
458        make_visible: bool,
459        on_open: oneshot::Sender<window::Id>,
460    },
461    EventLoopAwakened(winit::event::Event<Message>),
462}
463
464#[derive(Debug)]
465enum Control {
466    ChangeFlow(winit::event_loop::ControlFlow),
467    Exit,
468    Crash(Error),
469    CreateWindow {
470        id: window::Id,
471        settings: window::Settings,
472        title: String,
473        monitor: Option<winit::monitor::MonitorHandle>,
474        on_open: oneshot::Sender<window::Id>,
475    },
476}
477
478async fn run_instance<P>(
479    mut program: program::Instance<P>,
480    mut runtime: Runtime<P::Executor, Proxy<P::Message>, Action<P::Message>>,
481    mut proxy: Proxy<P::Message>,
482    mut event_receiver: mpsc::UnboundedReceiver<Event<Action<P::Message>>>,
483    mut control_sender: mpsc::UnboundedSender<Control>,
484    is_daemon: bool,
485    graphics_settings: graphics::Settings,
486    default_fonts: Vec<Cow<'static, [u8]>>,
487) where
488    P: Program + 'static,
489    P::Theme: theme::Base,
490{
491    use winit::event;
492    use winit::event_loop::ControlFlow;
493
494    let mut window_manager = WindowManager::new();
495    let mut is_window_opening = !is_daemon;
496
497    let mut compositor = None;
498    let mut events = Vec::new();
499    let mut messages = Vec::new();
500    let mut actions = 0;
501
502    let mut ui_caches = FxHashMap::default();
503    let mut user_interfaces = ManuallyDrop::new(FxHashMap::default());
504    let mut clipboard = Clipboard::unconnected();
505
506    loop {
507        // Empty the queue if possible
508        let event = if let Ok(event) = event_receiver.try_next() {
509            event
510        } else {
511            event_receiver.next().await
512        };
513
514        let Some(event) = event else {
515            break;
516        };
517
518        match event {
519            Event::WindowCreated {
520                id,
521                window,
522                exit_on_close_request,
523                make_visible,
524                on_open,
525            } => {
526                if compositor.is_none() {
527                    let (compositor_sender, compositor_receiver) =
528                        oneshot::channel();
529
530                    let create_compositor = {
531                        let window = window.clone();
532                        let mut proxy = proxy.clone();
533                        let default_fonts = default_fonts.clone();
534
535                        async move {
536                            let mut compositor =
537                                <P::Renderer as compositor::Default>::Compositor::new(graphics_settings, window).await;
538
539                            if let Ok(compositor) = &mut compositor {
540                                for font in default_fonts {
541                                    compositor.load_font(font.clone());
542                                }
543                            }
544
545                            compositor_sender
546                                .send(compositor)
547                                .ok()
548                                .expect("Send compositor");
549
550                            // HACK! Send a proxy event on completion to trigger
551                            // a runtime re-poll
552                            // TODO: Send compositor through proxy (?)
553                            {
554                                let (sender, _receiver) = oneshot::channel();
555
556                                proxy.send_action(Action::Window(
557                                    runtime::window::Action::GetLatest(sender),
558                                ));
559                            }
560                        }
561                    };
562
563                    #[cfg(target_arch = "wasm32")]
564                    wasm_bindgen_futures::spawn_local(create_compositor);
565
566                    #[cfg(not(target_arch = "wasm32"))]
567                    runtime.block_on(create_compositor);
568
569                    match compositor_receiver
570                        .await
571                        .expect("Wait for compositor")
572                    {
573                        Ok(new_compositor) => {
574                            compositor = Some(new_compositor);
575                        }
576                        Err(error) => {
577                            let _ = control_sender
578                                .start_send(Control::Crash(error.into()));
579                            continue;
580                        }
581                    }
582                }
583
584                debug::theme_changed(|| {
585                    if window_manager.is_empty() {
586                        theme::Base::palette(&program.theme(id))
587                    } else {
588                        None
589                    }
590                });
591
592                let window = window_manager.insert(
593                    id,
594                    window,
595                    &program,
596                    compositor
597                        .as_mut()
598                        .expect("Compositor must be initialized"),
599                    exit_on_close_request,
600                );
601
602                let logical_size = window.state.logical_size();
603
604                let _ = user_interfaces.insert(
605                    id,
606                    build_user_interface(
607                        &program,
608                        user_interface::Cache::default(),
609                        &mut window.renderer,
610                        logical_size,
611                        id,
612                    ),
613                );
614                let _ = ui_caches.insert(id, user_interface::Cache::default());
615
616                if make_visible {
617                    window.raw.set_visible(true);
618                }
619
620                events.push((
621                    id,
622                    core::Event::Window(window::Event::Opened {
623                        position: window.position(),
624                        size: window.size(),
625                    }),
626                ));
627
628                if clipboard.window_id().is_none() {
629                    clipboard = Clipboard::connect(window.raw.clone());
630                }
631
632                let _ = on_open.send(id);
633                is_window_opening = false;
634            }
635            Event::EventLoopAwakened(event) => {
636                match event {
637                    event::Event::NewEvents(event::StartCause::Init) => {
638                        for (_id, window) in window_manager.iter_mut() {
639                            window.raw.request_redraw();
640                        }
641                    }
642                    event::Event::NewEvents(
643                        event::StartCause::ResumeTimeReached { .. },
644                    ) => {
645                        let now = Instant::now();
646
647                        for (_id, window) in window_manager.iter_mut() {
648                            if let Some(redraw_at) = window.redraw_at {
649                                if redraw_at <= now {
650                                    window.raw.request_redraw();
651                                    window.redraw_at = None;
652                                }
653                            }
654                        }
655
656                        if let Some(redraw_at) = window_manager.redraw_at() {
657                            let _ =
658                                control_sender.start_send(Control::ChangeFlow(
659                                    ControlFlow::WaitUntil(redraw_at),
660                                ));
661                        } else {
662                            let _ = control_sender.start_send(
663                                Control::ChangeFlow(ControlFlow::Wait),
664                            );
665                        }
666                    }
667                    event::Event::PlatformSpecific(
668                        event::PlatformSpecific::MacOS(
669                            event::MacOS::ReceivedUrl(url),
670                        ),
671                    ) => {
672                        runtime.broadcast(
673                            subscription::Event::PlatformSpecific(
674                                subscription::PlatformSpecific::MacOS(
675                                    subscription::MacOS::ReceivedUrl(url),
676                                ),
677                            ),
678                        );
679                    }
680                    event::Event::UserEvent(action) => {
681                        run_action(
682                            action,
683                            &program,
684                            &mut compositor,
685                            &mut events,
686                            &mut messages,
687                            &mut clipboard,
688                            &mut control_sender,
689                            &mut user_interfaces,
690                            &mut window_manager,
691                            &mut ui_caches,
692                            &mut is_window_opening,
693                        );
694                        actions += 1;
695                    }
696                    event::Event::WindowEvent {
697                        window_id: id,
698                        event: event::WindowEvent::RedrawRequested,
699                        ..
700                    } => {
701                        let Some(compositor) = &mut compositor else {
702                            continue;
703                        };
704
705                        let Some((id, window)) =
706                            window_manager.get_mut_alias(id)
707                        else {
708                            continue;
709                        };
710
711                        let physical_size = window.state.physical_size();
712
713                        if physical_size.width == 0 || physical_size.height == 0
714                        {
715                            continue;
716                        }
717
718                        if window.viewport_version
719                            != window.state.viewport_version()
720                        {
721                            let logical_size = window.state.logical_size();
722
723                            let layout_span = debug::layout(id);
724                            let ui = user_interfaces
725                                .remove(&id)
726                                .expect("Remove user interface");
727
728                            let _ = user_interfaces.insert(
729                                id,
730                                ui.relayout(logical_size, &mut window.renderer),
731                            );
732                            layout_span.finish();
733
734                            compositor.configure_surface(
735                                &mut window.surface,
736                                physical_size.width,
737                                physical_size.height,
738                            );
739
740                            window.viewport_version =
741                                window.state.viewport_version();
742                        }
743
744                        let redraw_event = core::Event::Window(
745                            window::Event::RedrawRequested(Instant::now()),
746                        );
747
748                        let cursor = window.state.cursor();
749
750                        let ui = user_interfaces
751                            .get_mut(&id)
752                            .expect("Get user interface");
753
754                        let draw_span = debug::draw(id);
755                        let (ui_state, _) = ui.update(
756                            &[redraw_event.clone()],
757                            cursor,
758                            &mut window.renderer,
759                            &mut clipboard,
760                            &mut messages,
761                        );
762
763                        let new_mouse_interaction = ui.draw(
764                            &mut window.renderer,
765                            window.state.theme(),
766                            &renderer::Style {
767                                text_color: window.state.text_color(),
768                            },
769                            cursor,
770                        );
771                        draw_span.finish();
772
773                        if new_mouse_interaction != window.mouse_interaction {
774                            window.raw.set_cursor(
775                                conversion::mouse_interaction(
776                                    new_mouse_interaction,
777                                ),
778                            );
779
780                            window.mouse_interaction = new_mouse_interaction;
781                        }
782
783                        runtime.broadcast(subscription::Event::Interaction {
784                            window: id,
785                            event: redraw_event,
786                            status: core::event::Status::Ignored,
787                        });
788
789                        if let user_interface::State::Updated {
790                            redraw_request,
791                            input_method,
792                        } = ui_state
793                        {
794                            window.request_redraw(redraw_request);
795                            window.request_input_method(input_method);
796                        }
797
798                        window.draw_preedit();
799
800                        let present_span = debug::present(id);
801                        match compositor.present(
802                            &mut window.renderer,
803                            &mut window.surface,
804                            window.state.viewport(),
805                            window.state.background_color(),
806                            || window.raw.pre_present_notify(),
807                        ) {
808                            Ok(()) => {
809                                present_span.finish();
810                            }
811                            Err(error) => match error {
812                                // This is an unrecoverable error.
813                                compositor::SurfaceError::OutOfMemory => {
814                                    panic!("{:?}", error);
815                                }
816                                _ => {
817                                    present_span.finish();
818
819                                    log::error!(
820                                        "Error {error:?} when \
821                                        presenting surface."
822                                    );
823
824                                    // Try rendering all windows again next frame.
825                                    for (_id, window) in
826                                        window_manager.iter_mut()
827                                    {
828                                        window.raw.request_redraw();
829                                    }
830                                }
831                            },
832                        }
833                    }
834                    event::Event::WindowEvent {
835                        event: window_event,
836                        window_id,
837                    } => {
838                        if !is_daemon
839                            && matches!(
840                                window_event,
841                                winit::event::WindowEvent::Destroyed
842                            )
843                            && !is_window_opening
844                            && window_manager.is_empty()
845                        {
846                            control_sender
847                                .start_send(Control::Exit)
848                                .expect("Send control action");
849
850                            continue;
851                        }
852
853                        let Some((id, window)) =
854                            window_manager.get_mut_alias(window_id)
855                        else {
856                            continue;
857                        };
858
859                        if matches!(
860                            window_event,
861                            winit::event::WindowEvent::Resized(_)
862                        ) {
863                            window.raw.request_redraw();
864                        }
865
866                        if matches!(
867                            window_event,
868                            winit::event::WindowEvent::CloseRequested
869                        ) && window.exit_on_close_request
870                        {
871                            run_action(
872                                Action::Window(runtime::window::Action::Close(
873                                    id,
874                                )),
875                                &program,
876                                &mut compositor,
877                                &mut events,
878                                &mut messages,
879                                &mut clipboard,
880                                &mut control_sender,
881                                &mut user_interfaces,
882                                &mut window_manager,
883                                &mut ui_caches,
884                                &mut is_window_opening,
885                            );
886                        } else {
887                            window.state.update(&window.raw, &window_event);
888
889                            if let Some(event) = conversion::window_event(
890                                window_event,
891                                window.state.scale_factor(),
892                                window.state.modifiers(),
893                            ) {
894                                events.push((id, event));
895                            }
896                        }
897                    }
898                    event::Event::AboutToWait => {
899                        if actions > 0 {
900                            proxy.free_slots(actions);
901                            actions = 0;
902                        }
903
904                        if events.is_empty()
905                            && messages.is_empty()
906                            && window_manager.is_idle()
907                        {
908                            continue;
909                        }
910
911                        let mut uis_stale = false;
912
913                        for (id, window) in window_manager.iter_mut() {
914                            let interact_span = debug::interact(id);
915                            let mut window_events = vec![];
916
917                            events.retain(|(window_id, event)| {
918                                if *window_id == id {
919                                    window_events.push(event.clone());
920                                    false
921                                } else {
922                                    true
923                                }
924                            });
925
926                            if window_events.is_empty() && messages.is_empty() {
927                                continue;
928                            }
929
930                            let (ui_state, statuses) = user_interfaces
931                                .get_mut(&id)
932                                .expect("Get user interface")
933                                .update(
934                                    &window_events,
935                                    window.state.cursor(),
936                                    &mut window.renderer,
937                                    &mut clipboard,
938                                    &mut messages,
939                                );
940
941                            #[cfg(feature = "unconditional-rendering")]
942                            window.request_redraw(
943                                window::RedrawRequest::NextFrame,
944                            );
945
946                            match ui_state {
947                                user_interface::State::Updated {
948                                    redraw_request: _redraw_request,
949                                    ..
950                                } => {
951                                    #[cfg(not(
952                                        feature = "unconditional-rendering"
953                                    ))]
954                                    window.request_redraw(_redraw_request);
955                                }
956                                user_interface::State::Outdated => {
957                                    uis_stale = true;
958                                }
959                            }
960
961                            for (event, status) in window_events
962                                .into_iter()
963                                .zip(statuses.into_iter())
964                            {
965                                runtime.broadcast(
966                                    subscription::Event::Interaction {
967                                        window: id,
968                                        event,
969                                        status,
970                                    },
971                                );
972                            }
973
974                            interact_span.finish();
975                        }
976
977                        for (id, event) in events.drain(..) {
978                            runtime.broadcast(
979                                subscription::Event::Interaction {
980                                    window: id,
981                                    event,
982                                    status: core::event::Status::Ignored,
983                                },
984                            );
985                        }
986
987                        if !messages.is_empty() || uis_stale {
988                            let cached_interfaces: FxHashMap<
989                                window::Id,
990                                user_interface::Cache,
991                            > = ManuallyDrop::into_inner(user_interfaces)
992                                .drain()
993                                .map(|(id, ui)| (id, ui.into_cache()))
994                                .collect();
995
996                            update(&mut program, &mut runtime, &mut messages);
997
998                            for (id, window) in window_manager.iter_mut() {
999                                window.state.synchronize(
1000                                    &program,
1001                                    id,
1002                                    &window.raw,
1003                                );
1004
1005                                window.raw.request_redraw();
1006                            }
1007
1008                            debug::theme_changed(|| {
1009                                window_manager.first().and_then(|window| {
1010                                    theme::Base::palette(window.state.theme())
1011                                })
1012                            });
1013
1014                            user_interfaces =
1015                                ManuallyDrop::new(build_user_interfaces(
1016                                    &program,
1017                                    &mut window_manager,
1018                                    cached_interfaces,
1019                                ));
1020                        }
1021
1022                        if let Some(redraw_at) = window_manager.redraw_at() {
1023                            let _ =
1024                                control_sender.start_send(Control::ChangeFlow(
1025                                    ControlFlow::WaitUntil(redraw_at),
1026                                ));
1027                        } else {
1028                            let _ = control_sender.start_send(
1029                                Control::ChangeFlow(ControlFlow::Wait),
1030                            );
1031                        }
1032                    }
1033                    _ => {}
1034                }
1035            }
1036        }
1037    }
1038
1039    let _ = ManuallyDrop::into_inner(user_interfaces);
1040}
1041
1042/// Builds a window's [`UserInterface`] for the [`Program`].
1043fn build_user_interface<'a, P: Program>(
1044    program: &'a program::Instance<P>,
1045    cache: user_interface::Cache,
1046    renderer: &mut P::Renderer,
1047    size: Size,
1048    id: window::Id,
1049) -> UserInterface<'a, P::Message, P::Theme, P::Renderer>
1050where
1051    P::Theme: theme::Base,
1052{
1053    let view_span = debug::view(id);
1054    let view = program.view(id);
1055    view_span.finish();
1056
1057    let layout_span = debug::layout(id);
1058    let user_interface = UserInterface::build(view, size, cache, renderer);
1059    layout_span.finish();
1060
1061    user_interface
1062}
1063
1064fn update<P: Program, E: Executor>(
1065    program: &mut program::Instance<P>,
1066    runtime: &mut Runtime<E, Proxy<P::Message>, Action<P::Message>>,
1067    messages: &mut Vec<P::Message>,
1068) where
1069    P::Theme: theme::Base,
1070{
1071    for message in messages.drain(..) {
1072        let update_span = debug::update(&message);
1073        let task = runtime.enter(|| program.update(message));
1074        debug::tasks_spawned(task.units());
1075        update_span.finish();
1076
1077        if let Some(stream) = runtime::task::into_stream(task) {
1078            runtime.run(stream);
1079        }
1080    }
1081
1082    let subscription = runtime.enter(|| program.subscription());
1083    let recipes = subscription::into_recipes(subscription.map(Action::Output));
1084
1085    debug::subscriptions_tracked(recipes.len());
1086    runtime.track(recipes);
1087}
1088
1089fn run_action<P, C>(
1090    action: Action<P::Message>,
1091    program: &program::Instance<P>,
1092    compositor: &mut Option<C>,
1093    events: &mut Vec<(window::Id, core::Event)>,
1094    messages: &mut Vec<P::Message>,
1095    clipboard: &mut Clipboard,
1096    control_sender: &mut mpsc::UnboundedSender<Control>,
1097    interfaces: &mut FxHashMap<
1098        window::Id,
1099        UserInterface<'_, P::Message, P::Theme, P::Renderer>,
1100    >,
1101    window_manager: &mut WindowManager<P, C>,
1102    ui_caches: &mut FxHashMap<window::Id, user_interface::Cache>,
1103    is_window_opening: &mut bool,
1104) where
1105    P: Program,
1106    C: Compositor<Renderer = P::Renderer> + 'static,
1107    P::Theme: theme::Base,
1108{
1109    use crate::runtime::clipboard;
1110    use crate::runtime::system;
1111    use crate::runtime::window;
1112
1113    match action {
1114        Action::Output(message) => {
1115            messages.push(message);
1116        }
1117        Action::Clipboard(action) => match action {
1118            clipboard::Action::Read { target, channel } => {
1119                let _ = channel.send(clipboard.read(target));
1120            }
1121            clipboard::Action::Write { target, contents } => {
1122                clipboard.write(target, contents);
1123            }
1124        },
1125        Action::Window(action) => match action {
1126            window::Action::Open(id, settings, channel) => {
1127                let monitor = window_manager.last_monitor();
1128
1129                control_sender
1130                    .start_send(Control::CreateWindow {
1131                        id,
1132                        settings,
1133                        title: program.title(id),
1134                        monitor,
1135                        on_open: channel,
1136                    })
1137                    .expect("Send control action");
1138
1139                *is_window_opening = true;
1140            }
1141            window::Action::Close(id) => {
1142                let _ = ui_caches.remove(&id);
1143                let _ = interfaces.remove(&id);
1144
1145                if let Some(window) = window_manager.remove(id) {
1146                    if clipboard.window_id() == Some(window.raw.id()) {
1147                        *clipboard = window_manager
1148                            .first()
1149                            .map(|window| window.raw.clone())
1150                            .map(Clipboard::connect)
1151                            .unwrap_or_else(Clipboard::unconnected);
1152                    }
1153
1154                    events.push((
1155                        id,
1156                        core::Event::Window(core::window::Event::Closed),
1157                    ));
1158                }
1159
1160                if window_manager.is_empty() {
1161                    *compositor = None;
1162                }
1163            }
1164            window::Action::GetOldest(channel) => {
1165                let id =
1166                    window_manager.iter_mut().next().map(|(id, _window)| id);
1167
1168                let _ = channel.send(id);
1169            }
1170            window::Action::GetLatest(channel) => {
1171                let id =
1172                    window_manager.iter_mut().last().map(|(id, _window)| id);
1173
1174                let _ = channel.send(id);
1175            }
1176            window::Action::Drag(id) => {
1177                if let Some(window) = window_manager.get_mut(id) {
1178                    let _ = window.raw.drag_window();
1179                }
1180            }
1181            window::Action::DragResize(id, direction) => {
1182                if let Some(window) = window_manager.get_mut(id) {
1183                    let _ = window.raw.drag_resize_window(
1184                        conversion::resize_direction(direction),
1185                    );
1186                }
1187            }
1188            window::Action::Resize(id, size) => {
1189                if let Some(window) = window_manager.get_mut(id) {
1190                    let _ = window.raw.request_inner_size(
1191                        winit::dpi::LogicalSize {
1192                            width: size.width,
1193                            height: size.height,
1194                        },
1195                    );
1196                }
1197            }
1198            window::Action::SetMinSize(id, size) => {
1199                if let Some(window) = window_manager.get_mut(id) {
1200                    window.raw.set_min_inner_size(size.map(|size| {
1201                        winit::dpi::LogicalSize {
1202                            width: size.width,
1203                            height: size.height,
1204                        }
1205                    }));
1206                }
1207            }
1208            window::Action::SetMaxSize(id, size) => {
1209                if let Some(window) = window_manager.get_mut(id) {
1210                    window.raw.set_max_inner_size(size.map(|size| {
1211                        winit::dpi::LogicalSize {
1212                            width: size.width,
1213                            height: size.height,
1214                        }
1215                    }));
1216                }
1217            }
1218            window::Action::SetResizeIncrements(id, increments) => {
1219                if let Some(window) = window_manager.get_mut(id) {
1220                    window.raw.set_resize_increments(increments.map(|size| {
1221                        winit::dpi::LogicalSize {
1222                            width: size.width,
1223                            height: size.height,
1224                        }
1225                    }));
1226                }
1227            }
1228            window::Action::SetResizable(id, resizable) => {
1229                if let Some(window) = window_manager.get_mut(id) {
1230                    window.raw.set_resizable(resizable);
1231                }
1232            }
1233            window::Action::GetSize(id, channel) => {
1234                if let Some(window) = window_manager.get_mut(id) {
1235                    let size = window
1236                        .raw
1237                        .inner_size()
1238                        .to_logical(window.raw.scale_factor());
1239
1240                    let _ = channel.send(Size::new(size.width, size.height));
1241                }
1242            }
1243            window::Action::GetMaximized(id, channel) => {
1244                if let Some(window) = window_manager.get_mut(id) {
1245                    let _ = channel.send(window.raw.is_maximized());
1246                }
1247            }
1248            window::Action::Maximize(id, maximized) => {
1249                if let Some(window) = window_manager.get_mut(id) {
1250                    window.raw.set_maximized(maximized);
1251                }
1252            }
1253            window::Action::GetMinimized(id, channel) => {
1254                if let Some(window) = window_manager.get_mut(id) {
1255                    let _ = channel.send(window.raw.is_minimized());
1256                }
1257            }
1258            window::Action::Minimize(id, minimized) => {
1259                if let Some(window) = window_manager.get_mut(id) {
1260                    window.raw.set_minimized(minimized);
1261                }
1262            }
1263            window::Action::GetPosition(id, channel) => {
1264                if let Some(window) = window_manager.get(id) {
1265                    let position = window
1266                        .raw
1267                        .outer_position()
1268                        .map(|position| {
1269                            let position = position
1270                                .to_logical::<f32>(window.raw.scale_factor());
1271
1272                            Point::new(position.x, position.y)
1273                        })
1274                        .ok();
1275
1276                    let _ = channel.send(position);
1277                }
1278            }
1279            window::Action::GetScaleFactor(id, channel) => {
1280                if let Some(window) = window_manager.get_mut(id) {
1281                    let scale_factor = window.raw.scale_factor();
1282
1283                    let _ = channel.send(scale_factor as f32);
1284                }
1285            }
1286            window::Action::Move(id, position) => {
1287                if let Some(window) = window_manager.get_mut(id) {
1288                    window.raw.set_outer_position(
1289                        winit::dpi::LogicalPosition {
1290                            x: position.x,
1291                            y: position.y,
1292                        },
1293                    );
1294                }
1295            }
1296            window::Action::SetMode(id, mode) => {
1297                if let Some(window) = window_manager.get_mut(id) {
1298                    window.raw.set_visible(conversion::visible(mode));
1299                    window.raw.set_fullscreen(conversion::fullscreen(
1300                        window.raw.current_monitor(),
1301                        mode,
1302                    ));
1303                }
1304            }
1305            window::Action::SetIcon(id, icon) => {
1306                if let Some(window) = window_manager.get_mut(id) {
1307                    window.raw.set_window_icon(conversion::icon(icon));
1308                }
1309            }
1310            window::Action::GetMode(id, channel) => {
1311                if let Some(window) = window_manager.get_mut(id) {
1312                    let mode = if window.raw.is_visible().unwrap_or(true) {
1313                        conversion::mode(window.raw.fullscreen())
1314                    } else {
1315                        core::window::Mode::Hidden
1316                    };
1317
1318                    let _ = channel.send(mode);
1319                }
1320            }
1321            window::Action::ToggleMaximize(id) => {
1322                if let Some(window) = window_manager.get_mut(id) {
1323                    window.raw.set_maximized(!window.raw.is_maximized());
1324                }
1325            }
1326            window::Action::ToggleDecorations(id) => {
1327                if let Some(window) = window_manager.get_mut(id) {
1328                    window.raw.set_decorations(!window.raw.is_decorated());
1329                }
1330            }
1331            window::Action::RequestUserAttention(id, attention_type) => {
1332                if let Some(window) = window_manager.get_mut(id) {
1333                    window.raw.request_user_attention(
1334                        attention_type.map(conversion::user_attention),
1335                    );
1336                }
1337            }
1338            window::Action::GainFocus(id) => {
1339                if let Some(window) = window_manager.get_mut(id) {
1340                    window.raw.focus_window();
1341                }
1342            }
1343            window::Action::SetLevel(id, level) => {
1344                if let Some(window) = window_manager.get_mut(id) {
1345                    window
1346                        .raw
1347                        .set_window_level(conversion::window_level(level));
1348                }
1349            }
1350            window::Action::ShowSystemMenu(id) => {
1351                if let Some(window) = window_manager.get_mut(id) {
1352                    if let mouse::Cursor::Available(point) =
1353                        window.state.cursor()
1354                    {
1355                        window.raw.show_window_menu(
1356                            winit::dpi::LogicalPosition {
1357                                x: point.x,
1358                                y: point.y,
1359                            },
1360                        );
1361                    }
1362                }
1363            }
1364            window::Action::GetRawId(id, channel) => {
1365                if let Some(window) = window_manager.get_mut(id) {
1366                    let _ = channel.send(window.raw.id().into());
1367                }
1368            }
1369            window::Action::RunWithHandle(id, f) => {
1370                use window::raw_window_handle::HasWindowHandle;
1371
1372                if let Some(handle) = window_manager
1373                    .get_mut(id)
1374                    .and_then(|window| window.raw.window_handle().ok())
1375                {
1376                    f(handle);
1377                }
1378            }
1379            window::Action::Screenshot(id, channel) => {
1380                if let Some(window) = window_manager.get_mut(id) {
1381                    if let Some(compositor) = compositor {
1382                        let bytes = compositor.screenshot(
1383                            &mut window.renderer,
1384                            window.state.viewport(),
1385                            window.state.background_color(),
1386                        );
1387
1388                        let _ = channel.send(core::window::Screenshot::new(
1389                            bytes,
1390                            window.state.physical_size(),
1391                            window.state.viewport().scale_factor(),
1392                        ));
1393                    }
1394                }
1395            }
1396            window::Action::EnableMousePassthrough(id) => {
1397                if let Some(window) = window_manager.get_mut(id) {
1398                    let _ = window.raw.set_cursor_hittest(false);
1399                }
1400            }
1401            window::Action::DisableMousePassthrough(id) => {
1402                if let Some(window) = window_manager.get_mut(id) {
1403                    let _ = window.raw.set_cursor_hittest(true);
1404                }
1405            }
1406        },
1407        Action::System(action) => match action {
1408            system::Action::QueryInformation(_channel) => {
1409                #[cfg(feature = "system")]
1410                {
1411                    if let Some(compositor) = compositor {
1412                        let graphics_info = compositor.fetch_information();
1413
1414                        let _ = std::thread::spawn(move || {
1415                            let information =
1416                                crate::system::information(graphics_info);
1417
1418                            let _ = _channel.send(information);
1419                        });
1420                    }
1421                }
1422            }
1423        },
1424        Action::Widget(operation) => {
1425            let mut current_operation = Some(operation);
1426
1427            while let Some(mut operation) = current_operation.take() {
1428                for (id, ui) in interfaces.iter_mut() {
1429                    if let Some(window) = window_manager.get_mut(*id) {
1430                        ui.operate(&window.renderer, operation.as_mut());
1431                    }
1432                }
1433
1434                match operation.finish() {
1435                    operation::Outcome::None => {}
1436                    operation::Outcome::Some(()) => {}
1437                    operation::Outcome::Chain(next) => {
1438                        current_operation = Some(next);
1439                    }
1440                }
1441            }
1442        }
1443        Action::LoadFont { bytes, channel } => {
1444            if let Some(compositor) = compositor {
1445                // TODO: Error handling (?)
1446                compositor.load_font(bytes.clone());
1447
1448                let _ = channel.send(Ok(()));
1449            }
1450        }
1451        Action::Exit => {
1452            control_sender
1453                .start_send(Control::Exit)
1454                .expect("Send control action");
1455        }
1456    }
1457}
1458
1459/// Build the user interface for every window.
1460pub fn build_user_interfaces<'a, P: Program, C>(
1461    program: &'a program::Instance<P>,
1462    window_manager: &mut WindowManager<P, C>,
1463    mut cached_user_interfaces: FxHashMap<window::Id, user_interface::Cache>,
1464) -> FxHashMap<window::Id, UserInterface<'a, P::Message, P::Theme, P::Renderer>>
1465where
1466    C: Compositor<Renderer = P::Renderer>,
1467    P::Theme: theme::Base,
1468{
1469    cached_user_interfaces
1470        .drain()
1471        .filter_map(|(id, cache)| {
1472            let window = window_manager.get_mut(id)?;
1473
1474            Some((
1475                id,
1476                build_user_interface(
1477                    program,
1478                    cache,
1479                    &mut window.renderer,
1480                    window.state.logical_size(),
1481                    id,
1482                ),
1483            ))
1484        })
1485        .collect()
1486}
1487
1488/// Returns true if the provided event should cause a [`Program`] to
1489/// exit.
1490pub fn user_force_quit(
1491    event: &winit::event::WindowEvent,
1492    _modifiers: winit::keyboard::ModifiersState,
1493) -> bool {
1494    match event {
1495        #[cfg(target_os = "macos")]
1496        winit::event::WindowEvent::KeyboardInput {
1497            event:
1498                winit::event::KeyEvent {
1499                    logical_key: winit::keyboard::Key::Character(c),
1500                    state: winit::event::ElementState::Pressed,
1501                    ..
1502                },
1503            ..
1504        } if c == "q" && _modifiers.super_key() => true,
1505        _ => false,
1506    }
1507}