iced_winit/
program.rs

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