Skip to main content

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/master/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_cfg))]
21pub use iced_debug as debug;
22pub use iced_program as program;
23pub use iced_runtime as runtime;
24pub use program::core;
25pub use program::graphics;
26pub use runtime::futures;
27pub use winit;
28
29pub mod clipboard;
30pub mod conversion;
31
32mod error;
33mod proxy;
34mod window;
35
36pub use clipboard::Clipboard;
37pub use error::Error;
38pub use proxy::Proxy;
39
40use crate::core::backend;
41use crate::core::mouse;
42use crate::core::renderer;
43use crate::core::shell;
44use crate::core::theme;
45use crate::core::time::Instant;
46use crate::core::widget::operation;
47use crate::core::{Point, Renderer, Size};
48use crate::futures::futures::channel::mpsc;
49use crate::futures::futures::channel::oneshot;
50use crate::futures::futures::task;
51use crate::futures::futures::{Future, StreamExt};
52use crate::futures::subscription;
53use crate::futures::{Executor, Runtime};
54use crate::graphics::{Compositor, Shell, compositor};
55use crate::runtime::font;
56use crate::runtime::image;
57use crate::runtime::system;
58use crate::runtime::user_interface::{self, UserInterface};
59use crate::runtime::{Action, Task};
60
61use program::Program;
62
63use rustc_hash::FxHashMap;
64use std::borrow::Cow;
65use std::mem::ManuallyDrop;
66use std::slice;
67use std::sync::Arc;
68
69/// Runs a [`Program`] with the provided settings.
70pub fn run<P>(program: P) -> Result<(), Error>
71where
72    P: Program + 'static,
73    P::Theme: theme::Base,
74{
75    use winit::event_loop::EventLoop;
76
77    let boot_span = debug::boot();
78    let settings = program.settings();
79    let window_settings = program.window();
80
81    let event_loop = EventLoop::with_user_event()
82        .build()
83        .expect("Create event loop");
84
85    let backend_settings = backend::Settings::from(&settings);
86    let renderer_settings = renderer::Settings::from(&settings);
87    let display_handle = event_loop.owned_display_handle();
88
89    let (proxy, worker) = Proxy::new(event_loop.create_proxy());
90
91    #[cfg(feature = "debug")]
92    {
93        let proxy = proxy.clone();
94
95        debug::on_hotpatch(move || {
96            proxy.send_action(Action::Reload);
97        });
98    }
99
100    let mut runtime = {
101        let executor = P::Executor::new().map_err(Error::ExecutorCreationFailed)?;
102        executor.spawn(worker);
103
104        Runtime::new(executor, proxy.clone())
105    };
106
107    let (program, task) = runtime.enter(|| program::Instance::new(program));
108    let is_daemon = window_settings.is_none();
109
110    let task = if let Some(window_settings) = window_settings {
111        let mut task = Some(task);
112
113        let (_id, open) = runtime::window::open(window_settings);
114
115        open.then(move |_| task.take().unwrap_or_else(Task::none))
116    } else {
117        task
118    };
119
120    if let Some(stream) = runtime::task::into_stream(task) {
121        runtime.run(stream);
122    }
123
124    runtime.track(subscription::into_recipes(
125        runtime.enter(|| program.subscription().map(Action::Output)),
126    ));
127
128    let (event_sender, event_receiver) = mpsc::unbounded();
129    let (control_sender, control_receiver) = mpsc::unbounded();
130    let (system_theme_sender, system_theme_receiver) = oneshot::channel();
131
132    let instance: std::pin::Pin<Box<dyn Future<Output = ()>>> = Box::pin(run_instance::<P>(
133        program,
134        runtime,
135        proxy.clone(),
136        event_receiver,
137        control_sender,
138        display_handle,
139        is_daemon,
140        backend_settings,
141        renderer_settings,
142        settings.fonts,
143        system_theme_receiver,
144    ));
145
146    let context = task::Context::from_waker(task::noop_waker_ref());
147
148    struct Runner<Message: 'static> {
149        instance: std::pin::Pin<Box<dyn Future<Output = ()>>>,
150        context: task::Context<'static>,
151        id: Option<String>,
152        sender: mpsc::UnboundedSender<Event<Action<Message>>>,
153        receiver: mpsc::UnboundedReceiver<Control>,
154        error: Option<Error>,
155        system_theme: Option<oneshot::Sender<theme::Mode>>,
156
157        #[cfg(target_arch = "wasm32")]
158        canvas: Option<web_sys::HtmlCanvasElement>,
159    }
160
161    let runner = Runner {
162        instance,
163        context,
164        id: settings.id,
165        sender: event_sender,
166        receiver: control_receiver,
167        error: None,
168        system_theme: Some(system_theme_sender),
169
170        #[cfg(target_arch = "wasm32")]
171        canvas: None,
172    };
173
174    boot_span.finish();
175
176    impl<Message> winit::application::ApplicationHandler<Action<Message>> for Runner<Message> {
177        fn resumed(&mut self, event_loop: &winit::event_loop::ActiveEventLoop) {
178            if let Some(sender) = self.system_theme.take() {
179                let _ = sender.send(
180                    event_loop
181                        .system_theme()
182                        .map(conversion::theme_mode)
183                        .unwrap_or_default(),
184                );
185            }
186        }
187
188        fn new_events(
189            &mut self,
190            event_loop: &winit::event_loop::ActiveEventLoop,
191            cause: winit::event::StartCause,
192        ) {
193            self.process_event(
194                event_loop,
195                Event::EventLoopAwakened(winit::event::Event::NewEvents(cause)),
196            );
197        }
198
199        fn window_event(
200            &mut self,
201            event_loop: &winit::event_loop::ActiveEventLoop,
202            window_id: winit::window::WindowId,
203            event: winit::event::WindowEvent,
204        ) {
205            #[cfg(target_os = "windows")]
206            let is_move_or_resize = matches!(
207                event,
208                winit::event::WindowEvent::Resized(_) | winit::event::WindowEvent::Moved(_)
209            );
210
211            self.process_event(
212                event_loop,
213                Event::EventLoopAwakened(winit::event::Event::WindowEvent { window_id, event }),
214            );
215
216            // TODO: Remove when unnecessary
217            // On Windows, we emulate an `AboutToWait` event after every `Resized` event
218            // since the event loop does not resume during resize interaction.
219            // More details: https://github.com/rust-windowing/winit/issues/3272
220            #[cfg(target_os = "windows")]
221            {
222                if is_move_or_resize {
223                    self.process_event(
224                        event_loop,
225                        Event::EventLoopAwakened(winit::event::Event::AboutToWait),
226                    );
227                }
228            }
229        }
230
231        fn user_event(
232            &mut self,
233            event_loop: &winit::event_loop::ActiveEventLoop,
234            action: Action<Message>,
235        ) {
236            self.process_event(
237                event_loop,
238                Event::EventLoopAwakened(winit::event::Event::UserEvent(action)),
239            );
240        }
241
242        fn received_url(&mut self, event_loop: &winit::event_loop::ActiveEventLoop, url: String) {
243            self.process_event(
244                event_loop,
245                Event::EventLoopAwakened(winit::event::Event::PlatformSpecific(
246                    winit::event::PlatformSpecific::MacOS(winit::event::MacOS::ReceivedUrl(url)),
247                )),
248            );
249        }
250
251        fn about_to_wait(&mut self, event_loop: &winit::event_loop::ActiveEventLoop) {
252            self.process_event(
253                event_loop,
254                Event::EventLoopAwakened(winit::event::Event::AboutToWait),
255            );
256        }
257    }
258
259    impl<Message> Runner<Message> {
260        fn process_event(
261            &mut self,
262            event_loop: &winit::event_loop::ActiveEventLoop,
263            event: Event<Action<Message>>,
264        ) {
265            if event_loop.exiting() {
266                return;
267            }
268
269            self.sender.start_send(event).expect("Send event");
270
271            loop {
272                let poll = self.instance.as_mut().poll(&mut self.context);
273
274                match poll {
275                    task::Poll::Pending => match self.receiver.try_recv() {
276                        Ok(control) => match control {
277                            Control::ChangeFlow(flow) => {
278                                use winit::event_loop::ControlFlow;
279
280                                let now = Instant::now();
281
282                                match (event_loop.control_flow(), flow) {
283                                    (
284                                        ControlFlow::WaitUntil(current),
285                                        ControlFlow::WaitUntil(new),
286                                    ) if current > now && current < new => {}
287                                    (ControlFlow::WaitUntil(current), ControlFlow::Wait)
288                                        if current > now => {}
289                                    _ => {
290                                        event_loop.set_control_flow(flow);
291                                    }
292                                }
293                            }
294                            Control::CreateWindow {
295                                id,
296                                settings,
297                                title,
298                                scale_factor,
299                                monitor,
300                                on_open,
301                            } => {
302                                let exit_on_close_request = settings.exit_on_close_request;
303
304                                let visible = settings.visible;
305
306                                #[cfg(target_arch = "wasm32")]
307                                let target = settings.platform_specific.target.clone();
308
309                                let window_attributes = conversion::window_attributes(
310                                    settings,
311                                    &title,
312                                    scale_factor,
313                                    monitor.or(event_loop.primary_monitor()),
314                                    self.id.clone(),
315                                )
316                                .with_visible(false);
317
318                                #[cfg(target_arch = "wasm32")]
319                                let window_attributes = {
320                                    use winit::platform::web::WindowAttributesExtWebSys;
321                                    window_attributes.with_canvas(self.canvas.take())
322                                };
323
324                                log::info!(
325                                    "Window attributes for id `{id:#?}`: {window_attributes:#?}"
326                                );
327
328                                // On macOS, the `position` in `WindowAttributes` represents the "inner"
329                                // position of the window; while on other platforms it's the "outer" position.
330                                // We fix the inconsistency on macOS by positioning the window after creation.
331                                #[cfg(target_os = "macos")]
332                                let mut window_attributes = window_attributes;
333
334                                #[cfg(target_os = "macos")]
335                                let position = window_attributes.position.take();
336
337                                let window = event_loop
338                                    .create_window(window_attributes)
339                                    .expect("Create window");
340
341                                #[cfg(target_os = "macos")]
342                                if let Some(position) = position {
343                                    window.set_outer_position(position);
344                                }
345
346                                #[cfg(target_arch = "wasm32")]
347                                {
348                                    use winit::platform::web::WindowExtWebSys;
349
350                                    let canvas = window.canvas().expect("Get window canvas");
351
352                                    let _ = canvas.set_attribute(
353                                        "style",
354                                        "display: block; width: 100%; height: 100%",
355                                    );
356
357                                    let window = web_sys::window().unwrap();
358                                    let document = window.document().unwrap();
359                                    let body = document.body().unwrap();
360
361                                    let target = target.and_then(|target| {
362                                        body.query_selector(&format!("#{target}"))
363                                            .ok()
364                                            .unwrap_or(None)
365                                    });
366
367                                    match target {
368                                        Some(node) => {
369                                            let _ = node.replace_with_with_node_1(&canvas).expect(
370                                                &format!("Could not replace #{}", node.id()),
371                                            );
372                                        }
373                                        None => {
374                                            let _ = body
375                                                .append_child(&canvas)
376                                                .expect("Append canvas to HTML body");
377                                        }
378                                    };
379                                }
380
381                                self.process_event(
382                                    event_loop,
383                                    Event::WindowCreated {
384                                        id,
385                                        window: Arc::new(window),
386                                        exit_on_close_request,
387                                        make_visible: visible,
388                                        on_open,
389                                    },
390                                );
391                            }
392                            Control::Exit => {
393                                self.process_event(event_loop, Event::Exit);
394                                event_loop.exit();
395                                break;
396                            }
397                            Control::Crash(error) => {
398                                self.error = Some(error);
399                                event_loop.exit();
400                            }
401                            Control::SetAutomaticWindowTabbing(_enabled) => {
402                                #[cfg(target_os = "macos")]
403                                {
404                                    use winit::platform::macos::ActiveEventLoopExtMacOS;
405                                    event_loop.set_allows_automatic_window_tabbing(_enabled);
406                                }
407                            }
408                        },
409                        _ => {
410                            break;
411                        }
412                    },
413                    task::Poll::Ready(_) => {
414                        event_loop.exit();
415                        break;
416                    }
417                };
418            }
419        }
420    }
421
422    #[cfg(not(target_arch = "wasm32"))]
423    {
424        let mut runner = runner;
425        let _ = event_loop.run_app(&mut runner);
426
427        runner.error.map(Err).unwrap_or(Ok(()))
428    }
429
430    #[cfg(target_arch = "wasm32")]
431    {
432        use winit::platform::web::EventLoopExtWebSys;
433        let _ = event_loop.spawn_app(runner);
434
435        Ok(())
436    }
437}
438
439#[derive(Debug)]
440enum Event<Message: 'static> {
441    WindowCreated {
442        id: window::Id,
443        window: Arc<winit::window::Window>,
444        exit_on_close_request: bool,
445        make_visible: bool,
446        on_open: oneshot::Sender<window::Id>,
447    },
448    EventLoopAwakened(winit::event::Event<Message>),
449    Exit,
450}
451
452#[derive(Debug)]
453enum Control {
454    ChangeFlow(winit::event_loop::ControlFlow),
455    Exit,
456    Crash(Error),
457    CreateWindow {
458        id: window::Id,
459        settings: window::Settings,
460        title: String,
461        monitor: Option<winit::monitor::MonitorHandle>,
462        on_open: oneshot::Sender<window::Id>,
463        scale_factor: f32,
464    },
465    SetAutomaticWindowTabbing(bool),
466}
467
468async fn run_instance<P>(
469    mut program: program::Instance<P>,
470    mut runtime: Runtime<P::Executor, Proxy<P::Message>, Action<P::Message>>,
471    mut proxy: Proxy<P::Message>,
472    mut event_receiver: mpsc::UnboundedReceiver<Event<Action<P::Message>>>,
473    mut control_sender: mpsc::UnboundedSender<Control>,
474    display_handle: winit::event_loop::OwnedDisplayHandle,
475    is_daemon: bool,
476    backend_settings: backend::Settings,
477    mut renderer_settings: renderer::Settings,
478    default_fonts: Vec<Cow<'static, [u8]>>,
479    mut _system_theme: oneshot::Receiver<theme::Mode>,
480) where
481    P: Program + 'static,
482    P::Theme: theme::Base,
483{
484    use winit::event;
485    use winit::event_loop::ControlFlow;
486
487    let mut window_manager = window::Manager::new();
488    let mut is_window_opening = !is_daemon;
489
490    let mut compositor = None;
491    let mut events = Vec::new();
492    let mut messages = shell::Bus::new();
493    let mut actions = 0;
494
495    let mut ui_caches = FxHashMap::default();
496    let mut user_interfaces = ManuallyDrop::new(FxHashMap::default());
497    let mut clipboard = Clipboard::new();
498
499    #[cfg(all(feature = "linux-theme-detection", target_os = "linux"))]
500    let mut system_theme = {
501        let to_mode = |color_scheme| match color_scheme {
502            mundy::ColorScheme::NoPreference => theme::Mode::None,
503            mundy::ColorScheme::Light => theme::Mode::Light,
504            mundy::ColorScheme::Dark => theme::Mode::Dark,
505        };
506
507        runtime.run(
508            mundy::Preferences::stream(mundy::Interest::ColorScheme)
509                .map(move |preferences| {
510                    Action::System(system::Action::NotifyTheme(to_mode(
511                        preferences.color_scheme,
512                    )))
513                })
514                .boxed(),
515        );
516
517        runtime
518            .enter(|| {
519                mundy::Preferences::once_blocking(
520                    mundy::Interest::ColorScheme,
521                    core::time::Duration::from_millis(200),
522                )
523            })
524            .map(|preferences| to_mode(preferences.color_scheme))
525            .unwrap_or_default()
526    };
527
528    #[cfg(not(all(feature = "linux-theme-detection", target_os = "linux")))]
529    let mut system_theme = _system_theme.try_recv().ok().flatten().unwrap_or_default();
530
531    log::info!("System theme: {system_theme:?}");
532
533    'next_event: loop {
534        // Empty the queue if possible
535        let event = if let Ok(event) = event_receiver.try_recv() {
536            Some(event)
537        } else {
538            event_receiver.next().await
539        };
540
541        let Some(event) = event else {
542            break;
543        };
544
545        match event {
546            Event::WindowCreated {
547                id,
548                window,
549                exit_on_close_request,
550                make_visible,
551                on_open,
552            } => {
553                if compositor.is_none() {
554                    let (compositor_sender, compositor_receiver) = oneshot::channel();
555
556                    let create_compositor = {
557                        let window = window.clone();
558                        let backend_settings = backend_settings.clone();
559                        let display_handle = display_handle.clone();
560                        let proxy = proxy.clone();
561                        let default_fonts = default_fonts.clone();
562
563                        async move {
564                            let shell = Shell::new(proxy.clone());
565
566                            let mut compositor =
567                                <P::Renderer as compositor::Default>::Compositor::new(
568                                    backend_settings,
569                                    display_handle,
570                                    window,
571                                    shell,
572                                )
573                                .await;
574
575                            if let Ok(compositor) = &mut compositor {
576                                for font in default_fonts {
577                                    compositor.load_font(font.clone());
578                                }
579                            }
580
581                            compositor_sender
582                                .send(compositor)
583                                .ok()
584                                .expect("Send compositor");
585
586                            // HACK! Send a proxy event on completion to trigger
587                            // a runtime re-poll
588                            // TODO: Send compositor through proxy (?)
589                            {
590                                let (sender, _receiver) = oneshot::channel();
591
592                                proxy.send_action(Action::Window(
593                                    runtime::window::Action::GetLatest(sender),
594                                ));
595                            }
596                        }
597                    };
598
599                    #[cfg(target_arch = "wasm32")]
600                    wasm_bindgen_futures::spawn_local(create_compositor);
601
602                    #[cfg(not(target_arch = "wasm32"))]
603                    runtime.block_on(create_compositor);
604
605                    match compositor_receiver.await.expect("Wait for compositor") {
606                        Ok(new_compositor) => {
607                            compositor = Some(new_compositor);
608                        }
609                        Err(error) => {
610                            let _ = control_sender.start_send(Control::Crash(error.into()));
611                            continue;
612                        }
613                    }
614                }
615
616                let window_theme = window
617                    .theme()
618                    .map(conversion::theme_mode)
619                    .unwrap_or_default();
620
621                if system_theme != window_theme {
622                    system_theme = window_theme;
623
624                    runtime.broadcast(subscription::Event::SystemThemeChanged(window_theme));
625                }
626
627                let is_first = window_manager.is_empty();
628                let window = window_manager.insert(
629                    id,
630                    window,
631                    &program,
632                    compositor.as_mut().expect("Compositor must be initialized"),
633                    proxy.clone(),
634                    renderer_settings,
635                    exit_on_close_request,
636                    system_theme,
637                );
638
639                window
640                    .raw
641                    .set_theme(conversion::window_theme(window.state.theme_mode()));
642
643                debug::theme_changed(|| {
644                    if is_first {
645                        theme::Base::seed(window.state.theme())
646                    } else {
647                        None
648                    }
649                });
650
651                let logical_size = window.state.logical_size();
652
653                window.renderer.hint(window.state.scale());
654
655                let _ = user_interfaces.insert(
656                    id,
657                    build_user_interface(
658                        &program,
659                        user_interface::Cache::default(),
660                        &mut window.renderer,
661                        logical_size,
662                        id,
663                    ),
664                );
665                let _ = ui_caches.insert(id, user_interface::Cache::default());
666
667                if make_visible {
668                    window.raw.set_visible(true);
669                }
670
671                events.push((
672                    id,
673                    core::Event::Window(window::Event::Opened {
674                        position: window.position(),
675                        size: window.state.logical_size(),
676                        scale_factor: window.raw.scale_factor() as f32,
677                    }),
678                ));
679
680                let _ = on_open.send(id);
681                is_window_opening = false;
682            }
683            Event::EventLoopAwakened(event) => {
684                match event {
685                    event::Event::NewEvents(event::StartCause::Init) => {
686                        for (_id, window) in window_manager.iter_mut() {
687                            window.raw.request_redraw();
688                        }
689                    }
690                    event::Event::NewEvents(event::StartCause::ResumeTimeReached { .. }) => {
691                        let now = Instant::now();
692
693                        for (_id, window) in window_manager.iter_mut() {
694                            if let Some(redraw_at) = window.redraw_at
695                                && redraw_at <= now
696                            {
697                                window.raw.request_redraw();
698                                window.redraw_at = None;
699                            }
700                        }
701
702                        if let Some(redraw_at) = window_manager.redraw_at() {
703                            let _ = control_sender
704                                .start_send(Control::ChangeFlow(ControlFlow::WaitUntil(redraw_at)));
705                        } else {
706                            let _ =
707                                control_sender.start_send(Control::ChangeFlow(ControlFlow::Wait));
708                        }
709                    }
710                    event::Event::PlatformSpecific(event::PlatformSpecific::MacOS(
711                        event::MacOS::ReceivedUrl(url),
712                    )) => {
713                        runtime.broadcast(subscription::Event::PlatformSpecific(
714                            subscription::PlatformSpecific::MacOS(
715                                subscription::MacOS::ReceivedUrl(url),
716                            ),
717                        ));
718                    }
719                    event::Event::UserEvent(action) => {
720                        run_action(
721                            action,
722                            &program,
723                            &proxy,
724                            &mut runtime,
725                            &mut compositor,
726                            &mut events,
727                            &mut messages,
728                            &mut clipboard,
729                            &mut control_sender,
730                            &mut user_interfaces,
731                            &mut window_manager,
732                            &mut ui_caches,
733                            &mut is_window_opening,
734                            &mut system_theme,
735                            &mut renderer_settings,
736                        );
737                        actions += 1;
738                    }
739                    event::Event::WindowEvent {
740                        window_id: id,
741                        event: event::WindowEvent::RedrawRequested,
742                        ..
743                    } => {
744                        let Some(mut current_compositor) = compositor.as_mut() else {
745                            continue;
746                        };
747
748                        let Some((id, mut window)) = window_manager.get_mut_alias(id) else {
749                            continue;
750                        };
751
752                        let physical_size = window.state.physical_size();
753                        let mut logical_size = window.state.logical_size();
754
755                        if physical_size.width == 0 || physical_size.height == 0 {
756                            continue;
757                        }
758
759                        // Window was resized between redraws
760                        if window.surface_version != window.state.surface_version() {
761                            window.renderer.hint(window.state.scale());
762
763                            let ui = user_interfaces.remove(&id).expect("Remove user interface");
764
765                            let layout_span = debug::layout(id);
766                            let _ = user_interfaces
767                                .insert(id, ui.relayout(logical_size, &mut window.renderer));
768                            layout_span.finish();
769
770                            current_compositor.configure_surface(
771                                &mut window.surface,
772                                physical_size.width,
773                                physical_size.height,
774                            );
775
776                            window.surface_version = window.state.surface_version();
777                        }
778
779                        let redraw_event =
780                            core::Event::Window(window::Event::RedrawRequested(Instant::now()));
781
782                        let cursor = window.state.cursor();
783
784                        let mut interface =
785                            user_interfaces.get_mut(&id).expect("Get user interface");
786
787                        let interact_span = debug::interact(id);
788                        let mut redraw_count = 0;
789
790                        let state = loop {
791                            let message_count = messages.len();
792                            let (state, _) = interface.update(
793                                &window.raw,
794                                &window.waker,
795                                slice::from_ref(&redraw_event),
796                                cursor,
797                                &mut window.renderer,
798                                &mut messages,
799                            );
800
801                            if message_count == messages.len() && !state.has_layout_changed() {
802                                break state;
803                            }
804
805                            if redraw_count >= 2 {
806                                log::warn!(
807                                    "More than 3 consecutive RedrawRequested events \
808                                    produced layout invalidation"
809                                );
810
811                                break state;
812                            }
813
814                            redraw_count += 1;
815
816                            if !messages.is_empty()
817                                || matches!(state, user_interface::State::Outdated)
818                            {
819                                let caches: FxHashMap<_, _> =
820                                    ManuallyDrop::into_inner(user_interfaces)
821                                        .into_iter()
822                                        .map(|(id, interface)| (id, interface.into_cache()))
823                                        .collect();
824
825                                let actions = update(&mut program, &mut runtime, &mut messages);
826
827                                user_interfaces = ManuallyDrop::new(build_user_interfaces(
828                                    &program,
829                                    &mut window_manager,
830                                    caches,
831                                    &mut proxy,
832                                ));
833
834                                for action in actions {
835                                    // Defer all window actions to avoid compositor
836                                    // race conditions while redrawing
837                                    if let Action::Window(_) = action {
838                                        proxy.send_action(action);
839                                        continue;
840                                    }
841
842                                    run_action(
843                                        action,
844                                        &program,
845                                        &proxy,
846                                        &mut runtime,
847                                        &mut compositor,
848                                        &mut events,
849                                        &mut messages,
850                                        &mut clipboard,
851                                        &mut control_sender,
852                                        &mut user_interfaces,
853                                        &mut window_manager,
854                                        &mut ui_caches,
855                                        &mut is_window_opening,
856                                        &mut system_theme,
857                                        &mut renderer_settings,
858                                    );
859                                }
860
861                                for (window_id, window) in window_manager.iter_mut() {
862                                    // We are already redrawing this window
863                                    if window_id == id {
864                                        continue;
865                                    }
866
867                                    window.raw.request_redraw();
868                                }
869
870                                let Some(next_compositor) = compositor.as_mut() else {
871                                    continue 'next_event;
872                                };
873
874                                current_compositor = next_compositor;
875                                window = window_manager.get_mut(id).unwrap();
876
877                                // Window scale factor changed during a redraw request
878                                if logical_size != window.state.logical_size() {
879                                    logical_size = window.state.logical_size();
880
881                                    log::debug!(
882                                        "Window scale factor changed during a redraw request"
883                                    );
884
885                                    let ui =
886                                        user_interfaces.remove(&id).expect("Remove user interface");
887
888                                    let layout_span = debug::layout(id);
889                                    let _ = user_interfaces.insert(
890                                        id,
891                                        ui.relayout(logical_size, &mut window.renderer),
892                                    );
893                                    layout_span.finish();
894                                }
895
896                                interface = user_interfaces.get_mut(&id).unwrap();
897                            }
898                        };
899                        interact_span.finish();
900
901                        let draw_span = debug::draw(id);
902                        interface.draw(
903                            &mut window.renderer,
904                            window.state.theme(),
905                            &renderer::Style {
906                                text_color: window.state.text_color(),
907                            },
908                            cursor,
909                        );
910                        draw_span.finish();
911
912                        if let user_interface::State::Updated {
913                            redraw_request,
914                            input_method,
915                            mouse_interaction,
916                            clipboard: clipboard_requests,
917                            ..
918                        } = state
919                        {
920                            window.request_redraw(redraw_request);
921                            window.request_input_method(input_method);
922                            window.update_mouse(mouse_interaction);
923
924                            run_clipboard(&mut proxy, &mut clipboard, clipboard_requests, id);
925                        }
926
927                        runtime.broadcast(subscription::Event::Interaction {
928                            window: id,
929                            event: redraw_event,
930                            status: core::event::Status::Ignored,
931                        });
932
933                        window.draw_preedit();
934
935                        let present_span = debug::present(id);
936                        match current_compositor.present(
937                            &mut window.renderer,
938                            &mut window.surface,
939                            window.state.viewport(),
940                            window.state.background_color(),
941                            || window.raw.pre_present_notify(),
942                        ) {
943                            Ok(()) => {
944                                present_span.finish();
945                            }
946                            Err(error) => match error {
947                                compositor::SurfaceError::OutOfMemory => {
948                                    // This is an unrecoverable error.
949                                    panic!("{error:?}");
950                                }
951                                compositor::SurfaceError::Outdated
952                                | compositor::SurfaceError::Lost => {
953                                    present_span.finish();
954
955                                    // Reconfigure surface and try redrawing
956                                    let physical_size = window.state.physical_size();
957
958                                    if error == compositor::SurfaceError::Lost {
959                                        window.surface = current_compositor.create_surface(
960                                            window.raw.clone(),
961                                            physical_size.width,
962                                            physical_size.height,
963                                        );
964                                    } else {
965                                        current_compositor.configure_surface(
966                                            &mut window.surface,
967                                            physical_size.width,
968                                            physical_size.height,
969                                        );
970                                    }
971
972                                    window.raw.request_redraw();
973                                }
974                                compositor::SurfaceError::Occluded => {
975                                    present_span.finish();
976
977                                    // Do nothing and wait for window to become visible again
978                                }
979                                _ => {
980                                    present_span.finish();
981
982                                    log::warn!("Error {error:?} when presenting surface.");
983
984                                    // Try rendering all windows again next frame.
985                                    for (_id, window) in window_manager.iter_mut() {
986                                        window.raw.request_redraw();
987                                    }
988                                }
989                            },
990                        }
991                    }
992                    event::Event::WindowEvent {
993                        event: window_event,
994                        window_id,
995                    } => {
996                        if !is_daemon
997                            && matches!(window_event, winit::event::WindowEvent::Destroyed)
998                            && !is_window_opening
999                            && window_manager.is_empty()
1000                        {
1001                            control_sender
1002                                .start_send(Control::Exit)
1003                                .expect("Send control action");
1004
1005                            continue;
1006                        }
1007
1008                        let Some((id, window)) = window_manager.get_mut_alias(window_id) else {
1009                            continue;
1010                        };
1011
1012                        match window_event {
1013                            winit::event::WindowEvent::Resized(_)
1014                            | winit::event::WindowEvent::Occluded(false) => {
1015                                window.raw.request_redraw();
1016                            }
1017                            winit::event::WindowEvent::ThemeChanged(theme) => {
1018                                let mode = conversion::theme_mode(theme);
1019
1020                                if mode != system_theme {
1021                                    system_theme = mode;
1022
1023                                    runtime
1024                                        .broadcast(subscription::Event::SystemThemeChanged(mode));
1025                                }
1026                            }
1027                            _ => {}
1028                        }
1029
1030                        if matches!(window_event, winit::event::WindowEvent::CloseRequested)
1031                            && window.exit_on_close_request
1032                        {
1033                            run_action(
1034                                Action::Window(runtime::window::Action::Close(id)),
1035                                &program,
1036                                &proxy,
1037                                &mut runtime,
1038                                &mut compositor,
1039                                &mut events,
1040                                &mut messages,
1041                                &mut clipboard,
1042                                &mut control_sender,
1043                                &mut user_interfaces,
1044                                &mut window_manager,
1045                                &mut ui_caches,
1046                                &mut is_window_opening,
1047                                &mut system_theme,
1048                                &mut renderer_settings,
1049                            );
1050                        } else {
1051                            window.state.update(&program, &window.raw, &window_event);
1052
1053                            if let Some(event) = conversion::window_event(
1054                                window_event,
1055                                window.state.scale_factor(),
1056                                window.state.modifiers(),
1057                            ) {
1058                                events.push((id, event));
1059                            }
1060                        }
1061                    }
1062                    event::Event::AboutToWait => {
1063                        if actions > 0 {
1064                            proxy.free_slots(actions);
1065                            actions = 0;
1066                        }
1067
1068                        if events.is_empty() && messages.is_empty() && window_manager.is_idle() {
1069                            continue;
1070                        }
1071
1072                        let mut uis_stale = false;
1073
1074                        for (id, window) in window_manager.iter_mut() {
1075                            let interact_span = debug::interact(id);
1076                            let mut window_events = vec![];
1077
1078                            events.retain(|(window_id, event)| {
1079                                if *window_id == id {
1080                                    window_events.push(event.clone());
1081                                    false
1082                                } else {
1083                                    true
1084                                }
1085                            });
1086
1087                            if window_events.is_empty() {
1088                                continue;
1089                            }
1090
1091                            let (ui_state, statuses) = user_interfaces
1092                                .get_mut(&id)
1093                                .expect("Get user interface")
1094                                .update(
1095                                    &window.raw,
1096                                    &window.waker,
1097                                    &window_events,
1098                                    window.state.cursor(),
1099                                    &mut window.renderer,
1100                                    &mut messages,
1101                                );
1102
1103                            #[cfg(feature = "unconditional-rendering")]
1104                            window.request_redraw(window::RedrawRequest::NextFrame);
1105
1106                            match ui_state {
1107                                user_interface::State::Updated {
1108                                    redraw_request: _redraw_request,
1109                                    mouse_interaction,
1110                                    clipboard: clipboard_requests,
1111                                    ..
1112                                } => {
1113                                    window.update_mouse(mouse_interaction);
1114
1115                                    #[cfg(not(feature = "unconditional-rendering"))]
1116                                    window.request_redraw(_redraw_request);
1117
1118                                    run_clipboard(
1119                                        &mut proxy,
1120                                        &mut clipboard,
1121                                        clipboard_requests,
1122                                        id,
1123                                    );
1124                                }
1125                                user_interface::State::Outdated => {
1126                                    uis_stale = true;
1127                                }
1128                            }
1129
1130                            for (event, status) in window_events.into_iter().zip(statuses) {
1131                                runtime.broadcast(subscription::Event::Interaction {
1132                                    window: id,
1133                                    event,
1134                                    status,
1135                                });
1136                            }
1137
1138                            interact_span.finish();
1139                        }
1140
1141                        for (id, event) in events.drain(..) {
1142                            runtime.broadcast(subscription::Event::Interaction {
1143                                window: id,
1144                                event,
1145                                status: core::event::Status::Ignored,
1146                            });
1147                        }
1148
1149                        if !messages.is_empty() || uis_stale {
1150                            let cached_interfaces: FxHashMap<_, _> =
1151                                ManuallyDrop::into_inner(user_interfaces)
1152                                    .into_iter()
1153                                    .map(|(id, ui)| (id, ui.into_cache()))
1154                                    .collect();
1155
1156                            let actions = update(&mut program, &mut runtime, &mut messages);
1157
1158                            user_interfaces = ManuallyDrop::new(build_user_interfaces(
1159                                &program,
1160                                &mut window_manager,
1161                                cached_interfaces,
1162                                &mut proxy,
1163                            ));
1164
1165                            for action in actions {
1166                                run_action(
1167                                    action,
1168                                    &program,
1169                                    &proxy,
1170                                    &mut runtime,
1171                                    &mut compositor,
1172                                    &mut events,
1173                                    &mut messages,
1174                                    &mut clipboard,
1175                                    &mut control_sender,
1176                                    &mut user_interfaces,
1177                                    &mut window_manager,
1178                                    &mut ui_caches,
1179                                    &mut is_window_opening,
1180                                    &mut system_theme,
1181                                    &mut renderer_settings,
1182                                );
1183                            }
1184
1185                            for (_id, window) in window_manager.iter_mut() {
1186                                window.raw.request_redraw();
1187                            }
1188                        }
1189
1190                        if let Some(redraw_at) = window_manager.redraw_at() {
1191                            let _ = control_sender
1192                                .start_send(Control::ChangeFlow(ControlFlow::WaitUntil(redraw_at)));
1193                        } else {
1194                            let _ =
1195                                control_sender.start_send(Control::ChangeFlow(ControlFlow::Wait));
1196                        }
1197                    }
1198                    _ => {}
1199                }
1200            }
1201            Event::Exit => break,
1202        }
1203    }
1204
1205    let _ = ManuallyDrop::into_inner(user_interfaces);
1206}
1207
1208/// Builds a window's [`UserInterface`] for the [`Program`].
1209fn build_user_interface<'a, P: Program>(
1210    program: &'a program::Instance<P>,
1211    cache: user_interface::Cache,
1212    renderer: &mut P::Renderer,
1213    size: Size,
1214    id: window::Id,
1215) -> UserInterface<'a, P::Message, P::Theme, P::Renderer>
1216where
1217    P::Theme: theme::Base,
1218{
1219    let view_span = debug::view(id);
1220    let view = program.view(id);
1221    view_span.finish();
1222
1223    let layout_span = debug::layout(id);
1224    let user_interface = UserInterface::build(view, size, cache, renderer);
1225    layout_span.finish();
1226
1227    user_interface
1228}
1229
1230fn update<P: Program, E: Executor>(
1231    program: &mut program::Instance<P>,
1232    runtime: &mut Runtime<E, Proxy<P::Message>, Action<P::Message>>,
1233    messages: &mut shell::Bus<P::Message>,
1234) -> Vec<Action<P::Message>>
1235where
1236    P::Theme: theme::Base,
1237{
1238    use futures::futures;
1239
1240    let mut actions = Vec::new();
1241    let mut outputs = Vec::new();
1242
1243    while !messages.is_empty() {
1244        for (message, _receipt) in messages.drain() {
1245            let task = runtime.enter(|| program.update(message));
1246
1247            if let Some(mut stream) = runtime::task::into_stream(task) {
1248                let waker = futures::task::noop_waker_ref();
1249                let mut context = futures::task::Context::from_waker(waker);
1250
1251                // Run immediately available actions synchronously (e.g. widget operations)
1252                loop {
1253                    match runtime.enter(|| stream.poll_next_unpin(&mut context)) {
1254                        futures::task::Poll::Ready(Some(Action::Output(output))) => {
1255                            outputs.push(output);
1256                        }
1257                        futures::task::Poll::Ready(Some(action)) => {
1258                            actions.push(action);
1259                        }
1260                        futures::task::Poll::Ready(None) => {
1261                            break;
1262                        }
1263                        futures::task::Poll::Pending => {
1264                            runtime.run(stream);
1265                            break;
1266                        }
1267                    }
1268                }
1269            }
1270        }
1271
1272        for output in outputs.drain(..) {
1273            let _ = messages.push(output);
1274        }
1275    }
1276
1277    let subscription = runtime.enter(|| program.subscription());
1278    let recipes = subscription::into_recipes(subscription.map(Action::Output));
1279
1280    runtime.track(recipes);
1281
1282    actions
1283}
1284
1285fn run_action<'a, P, C>(
1286    action: Action<P::Message>,
1287    program: &'a program::Instance<P>,
1288    _proxy: &Proxy<P::Message>,
1289    runtime: &mut Runtime<P::Executor, Proxy<P::Message>, Action<P::Message>>,
1290    compositor: &mut Option<C>,
1291    events: &mut Vec<(window::Id, core::Event)>,
1292    messages: &mut shell::Bus<P::Message>,
1293    clipboard: &mut Clipboard,
1294    control_sender: &mut mpsc::UnboundedSender<Control>,
1295    interfaces: &mut FxHashMap<window::Id, UserInterface<'a, P::Message, P::Theme, P::Renderer>>,
1296    window_manager: &mut window::Manager<P, C>,
1297    ui_caches: &mut FxHashMap<window::Id, user_interface::Cache>,
1298    is_window_opening: &mut bool,
1299    system_theme: &mut theme::Mode,
1300    renderer_settings: &mut renderer::Settings,
1301) where
1302    P: Program,
1303    C: Compositor<Renderer = P::Renderer> + 'static,
1304    P::Theme: theme::Base,
1305{
1306    use crate::core::Renderer as _;
1307    use crate::runtime::backend;
1308    use crate::runtime::clipboard;
1309    use crate::runtime::window;
1310
1311    match action {
1312        Action::Output(message) => {
1313            let _ = messages.push(message);
1314        }
1315        Action::Clipboard(action) => match action {
1316            clipboard::Action::Read { kind, channel } => {
1317                clipboard.read(kind, move |result| {
1318                    let _ = channel.send(result);
1319                });
1320            }
1321            clipboard::Action::Write { content, channel } => {
1322                clipboard.write(content, move |result| {
1323                    let _ = channel.send(result);
1324                });
1325            }
1326        },
1327        Action::Window(action) => match action {
1328            window::Action::Open(id, settings, channel) => {
1329                let monitor = window_manager.last_monitor();
1330
1331                control_sender
1332                    .start_send(Control::CreateWindow {
1333                        id,
1334                        settings,
1335                        title: program.title(id),
1336                        scale_factor: program.scale_factor(id),
1337                        monitor,
1338                        on_open: channel,
1339                    })
1340                    .expect("Send control action");
1341
1342                *is_window_opening = true;
1343            }
1344            window::Action::Close(id) => {
1345                let _ = ui_caches.remove(&id);
1346                let _ = interfaces.remove(&id);
1347
1348                if window_manager.remove(id).is_some() {
1349                    events.push((id, core::Event::Window(core::window::Event::Closed)));
1350                }
1351
1352                if window_manager.is_empty() {
1353                    *compositor = None;
1354                }
1355            }
1356            window::Action::GetOldest(channel) => {
1357                let id = window_manager.iter_mut().next().map(|(id, _window)| id);
1358
1359                let _ = channel.send(id);
1360            }
1361            window::Action::GetLatest(channel) => {
1362                let id = window_manager.iter_mut().last().map(|(id, _window)| id);
1363
1364                let _ = channel.send(id);
1365            }
1366            window::Action::Drag(id) => {
1367                if let Some(window) = window_manager.get_mut(id) {
1368                    let _ = window.raw.drag_window();
1369                }
1370            }
1371            window::Action::DragResize(id, direction) => {
1372                if let Some(window) = window_manager.get_mut(id) {
1373                    let _ = window
1374                        .raw
1375                        .drag_resize_window(conversion::resize_direction(direction));
1376                }
1377            }
1378            window::Action::Resize(id, size) => {
1379                if let Some(window) = window_manager.get_mut(id) {
1380                    let _ = window.raw.request_inner_size(
1381                        winit::dpi::LogicalSize {
1382                            width: size.width,
1383                            height: size.height,
1384                        }
1385                        .to_physical::<f32>(f64::from(window.state.scale_factor())),
1386                    );
1387                }
1388            }
1389            window::Action::SetMinSize(id, size) => {
1390                if let Some(window) = window_manager.get_mut(id) {
1391                    window.raw.set_min_inner_size(size.map(|size| {
1392                        winit::dpi::LogicalSize {
1393                            width: size.width,
1394                            height: size.height,
1395                        }
1396                        .to_physical::<f32>(f64::from(window.state.scale_factor()))
1397                    }));
1398                }
1399            }
1400            window::Action::SetMaxSize(id, size) => {
1401                if let Some(window) = window_manager.get_mut(id) {
1402                    window.raw.set_max_inner_size(size.map(|size| {
1403                        winit::dpi::LogicalSize {
1404                            width: size.width,
1405                            height: size.height,
1406                        }
1407                        .to_physical::<f32>(f64::from(window.state.scale_factor()))
1408                    }));
1409                }
1410            }
1411            window::Action::SetResizeIncrements(id, increments) => {
1412                if let Some(window) = window_manager.get_mut(id) {
1413                    window.raw.set_resize_increments(increments.map(|size| {
1414                        winit::dpi::LogicalSize {
1415                            width: size.width,
1416                            height: size.height,
1417                        }
1418                        .to_physical::<f32>(f64::from(window.state.scale_factor()))
1419                    }));
1420                }
1421            }
1422            window::Action::SetResizable(id, resizable) => {
1423                if let Some(window) = window_manager.get_mut(id) {
1424                    window.raw.set_resizable(resizable);
1425                }
1426            }
1427            window::Action::GetSize(id, channel) => {
1428                if let Some(window) = window_manager.get_mut(id) {
1429                    let size = window.state.logical_size();
1430                    let _ = channel.send(Size::new(size.width, size.height));
1431                }
1432            }
1433            window::Action::GetMaximized(id, channel) => {
1434                if let Some(window) = window_manager.get_mut(id) {
1435                    let _ = channel.send(window.raw.is_maximized());
1436                }
1437            }
1438            window::Action::Maximize(id, maximized) => {
1439                if let Some(window) = window_manager.get_mut(id) {
1440                    window.raw.set_maximized(maximized);
1441                }
1442            }
1443            window::Action::GetMinimized(id, channel) => {
1444                if let Some(window) = window_manager.get_mut(id) {
1445                    let _ = channel.send(window.raw.is_minimized());
1446                }
1447            }
1448            window::Action::Minimize(id, minimized) => {
1449                if let Some(window) = window_manager.get_mut(id) {
1450                    window.raw.set_minimized(minimized);
1451                }
1452            }
1453            window::Action::GetPosition(id, channel) => {
1454                if let Some(window) = window_manager.get(id) {
1455                    let position = window
1456                        .raw
1457                        .outer_position()
1458                        .map(|position| {
1459                            let position = position.to_logical::<f32>(window.raw.scale_factor());
1460
1461                            Point::new(position.x, position.y)
1462                        })
1463                        .ok();
1464
1465                    let _ = channel.send(position);
1466                }
1467            }
1468            window::Action::GetScaleFactor(id, channel) => {
1469                if let Some(window) = window_manager.get_mut(id) {
1470                    let scale_factor = window.raw.scale_factor();
1471
1472                    let _ = channel.send(scale_factor as f32);
1473                }
1474            }
1475            window::Action::Move(id, position) => {
1476                if let Some(window) = window_manager.get_mut(id) {
1477                    window.raw.set_outer_position(winit::dpi::LogicalPosition {
1478                        x: position.x,
1479                        y: position.y,
1480                    });
1481                }
1482            }
1483            window::Action::SetMode(id, mode) => {
1484                if let Some(window) = window_manager.get_mut(id) {
1485                    window.raw.set_visible(conversion::visible(mode));
1486                    window
1487                        .raw
1488                        .set_fullscreen(conversion::fullscreen(window.raw.current_monitor(), mode));
1489                }
1490            }
1491            window::Action::SetIcon(id, icon) => {
1492                if let Some(window) = window_manager.get_mut(id) {
1493                    window.raw.set_window_icon(conversion::icon(icon));
1494                }
1495            }
1496            window::Action::GetMode(id, channel) => {
1497                if let Some(window) = window_manager.get_mut(id) {
1498                    let mode = if window.raw.is_visible().unwrap_or(true) {
1499                        conversion::mode(window.raw.fullscreen())
1500                    } else {
1501                        core::window::Mode::Hidden
1502                    };
1503
1504                    let _ = channel.send(mode);
1505                }
1506            }
1507            window::Action::ToggleMaximize(id) => {
1508                if let Some(window) = window_manager.get_mut(id) {
1509                    window.raw.set_maximized(!window.raw.is_maximized());
1510                }
1511            }
1512            window::Action::ToggleDecorations(id) => {
1513                if let Some(window) = window_manager.get_mut(id) {
1514                    window.raw.set_decorations(!window.raw.is_decorated());
1515                }
1516            }
1517            window::Action::RequestUserAttention(id, attention_type) => {
1518                if let Some(window) = window_manager.get_mut(id) {
1519                    window
1520                        .raw
1521                        .request_user_attention(attention_type.map(conversion::user_attention));
1522                }
1523            }
1524            window::Action::GainFocus(id) => {
1525                if let Some(window) = window_manager.get_mut(id) {
1526                    window.raw.focus_window();
1527                }
1528            }
1529            window::Action::SetLevel(id, level) => {
1530                if let Some(window) = window_manager.get_mut(id) {
1531                    window.raw.set_window_level(conversion::window_level(level));
1532                }
1533            }
1534            window::Action::ShowSystemMenu(id) => {
1535                if let Some(window) = window_manager.get_mut(id)
1536                    && let mouse::Cursor::Available(point) = window.state.cursor()
1537                {
1538                    window.raw.show_window_menu(winit::dpi::LogicalPosition {
1539                        x: point.x,
1540                        y: point.y,
1541                    });
1542                }
1543            }
1544            window::Action::GetRawId(id, channel) => {
1545                if let Some(window) = window_manager.get_mut(id) {
1546                    let _ = channel.send(window.raw.id().into());
1547                }
1548            }
1549            window::Action::Run(id, f) => {
1550                if let Some(window) = window_manager.get_mut(id) {
1551                    f(&window.raw);
1552                }
1553            }
1554            window::Action::Screenshot(id, channel) => {
1555                if let Some(window) = window_manager.get_mut(id)
1556                    && let Some(compositor) = compositor
1557                {
1558                    let bytes = compositor.screenshot(
1559                        &mut window.renderer,
1560                        window.state.viewport(),
1561                        window.state.background_color(),
1562                    );
1563
1564                    let _ = channel.send(core::window::Screenshot::new(
1565                        bytes,
1566                        window.state.physical_size(),
1567                        window.state.scale_factor(),
1568                    ));
1569                }
1570            }
1571            window::Action::EnableMousePassthrough(id) => {
1572                if let Some(window) = window_manager.get_mut(id) {
1573                    let _ = window.raw.set_cursor_hittest(false);
1574                }
1575            }
1576            window::Action::DisableMousePassthrough(id) => {
1577                if let Some(window) = window_manager.get_mut(id) {
1578                    let _ = window.raw.set_cursor_hittest(true);
1579                }
1580            }
1581            window::Action::GetMonitorSize(id, channel) => {
1582                if let Some(window) = window_manager.get(id) {
1583                    let size = window.raw.current_monitor().map(|monitor| {
1584                        let scale = window.state.scale_factor();
1585                        let size = monitor.size().to_logical(f64::from(scale));
1586
1587                        Size::new(size.width, size.height)
1588                    });
1589
1590                    let _ = channel.send(size);
1591                }
1592            }
1593            window::Action::SetAllowAutomaticTabbing(enabled) => {
1594                control_sender
1595                    .start_send(Control::SetAutomaticWindowTabbing(enabled))
1596                    .expect("Send control action");
1597            }
1598            window::Action::RedrawAll => {
1599                for (_id, window) in window_manager.iter_mut() {
1600                    window.raw.request_redraw();
1601                }
1602            }
1603            window::Action::RelayoutAll => {
1604                for (id, window) in window_manager.iter_mut() {
1605                    if let Some(ui) = interfaces.remove(&id) {
1606                        let _ = interfaces.insert(
1607                            id,
1608                            ui.relayout(window.state.logical_size(), &mut window.renderer),
1609                        );
1610                    }
1611
1612                    window.raw.request_redraw();
1613                }
1614            }
1615        },
1616        Action::System(action) => match action {
1617            system::Action::GetInformation(_channel) => {
1618                #[cfg(feature = "sysinfo")]
1619                {
1620                    if let Some(compositor) = compositor {
1621                        let graphics_info = compositor.information();
1622
1623                        let _ = std::thread::spawn(move || {
1624                            let information = system_information(graphics_info);
1625
1626                            let _ = _channel.send(information);
1627                        });
1628                    }
1629                }
1630            }
1631            system::Action::GetTheme(channel) => {
1632                let _ = channel.send(*system_theme);
1633            }
1634            system::Action::NotifyTheme(mode) => {
1635                if mode != *system_theme {
1636                    *system_theme = mode;
1637
1638                    runtime.broadcast(subscription::Event::SystemThemeChanged(mode));
1639                }
1640
1641                let Some(theme) = conversion::window_theme(mode) else {
1642                    return;
1643                };
1644
1645                for (_id, window) in window_manager.iter_mut() {
1646                    window.state.update(
1647                        program,
1648                        &window.raw,
1649                        &winit::event::WindowEvent::ThemeChanged(theme),
1650                    );
1651                }
1652            }
1653        },
1654        Action::Font(action) => match action {
1655            font::Action::Load { bytes, channel } => {
1656                if let Some(compositor) = compositor {
1657                    let result = compositor.load_font(bytes.clone());
1658                    let _ = channel.send(result);
1659                }
1660            }
1661            font::Action::List { channel } => {
1662                if let Some(compositor) = compositor {
1663                    let fonts = compositor.list_fonts();
1664                    let _ = channel.send(fonts);
1665                }
1666            }
1667            font::Action::SetDefaults { font, text_size } => {
1668                renderer_settings.font = font;
1669                renderer_settings.text_size = text_size;
1670
1671                let Some(compositor) = compositor else {
1672                    return;
1673                };
1674
1675                // Recreate renderers and relayout all windows
1676                for (id, window) in window_manager.iter_mut() {
1677                    window.renderer = compositor.create_renderer(*renderer_settings);
1678
1679                    let Some(ui) = interfaces.remove(&id) else {
1680                        continue;
1681                    };
1682
1683                    let size = window.state.logical_size();
1684                    let ui = ui.relayout(size, &mut window.renderer);
1685                    let _ = interfaces.insert(id, ui);
1686
1687                    window.raw.request_redraw();
1688                }
1689            }
1690        },
1691        Action::Widget(operation) => {
1692            let mut current_operation = Some(operation);
1693
1694            while let Some(mut operation) = current_operation.take() {
1695                for (id, ui) in interfaces.iter_mut() {
1696                    if let Some(window) = window_manager.get_mut(*id) {
1697                        ui.operate(&window.renderer, operation.as_mut());
1698                    }
1699                }
1700
1701                match operation.finish() {
1702                    operation::Outcome::None => {}
1703                    operation::Outcome::Some(()) => {}
1704                    operation::Outcome::Chain(next) => {
1705                        current_operation = Some(next);
1706                    }
1707                }
1708            }
1709
1710            // Redraw all windows
1711            for (_, window) in window_manager.iter_mut() {
1712                window.raw.request_redraw();
1713            }
1714        }
1715        Action::Image(action) => match action {
1716            image::Action::Allocate(handle, sender) => {
1717                // TODO: Shared image cache in compositor
1718                if let Some((_id, window)) = window_manager.iter_mut().next() {
1719                    window.renderer.allocate_image(&handle, move |allocation| {
1720                        let _ = sender.send(allocation);
1721                    });
1722                }
1723            }
1724        },
1725        Action::Backend(action) => match action {
1726            #[cfg(not(target_arch = "wasm32"))]
1727            backend::Action::Configure(settings, sender) => {
1728                let shell = Shell::new(_proxy.clone());
1729
1730                let mut new_compositor = if let Some(window) = window_manager.first() {
1731                    match runtime.block_on(C::new(
1732                        settings,
1733                        window.raw.clone(),
1734                        window.raw.clone(),
1735                        shell,
1736                    )) {
1737                        Ok(compositor) => compositor,
1738                        Err(error) => {
1739                            let _ = sender.send(Err(error));
1740                            return;
1741                        }
1742                    }
1743                } else {
1744                    return;
1745                };
1746
1747                graphics::cache::invalidate_all();
1748
1749                window_manager.replace_with(|mut window| {
1750                    let size = window.state.physical_size();
1751
1752                    drop(window.renderer);
1753                    drop(window.surface);
1754
1755                    window.renderer = new_compositor.create_renderer(*renderer_settings);
1756                    window.surface =
1757                        new_compositor.create_surface(window.raw.clone(), size.width, size.height);
1758
1759                    window
1760                });
1761
1762                *compositor = Some(new_compositor);
1763
1764                let _ = sender.send(Ok(()));
1765            }
1766            #[cfg(target_arch = "wasm32")]
1767            backend::Action::Configure(_, _) => {}
1768        },
1769        Action::Event { window, event } => {
1770            events.push((window, event));
1771        }
1772        Action::Tick => {
1773            for (_id, window) in window_manager.iter_mut() {
1774                window.renderer.tick();
1775            }
1776        }
1777        Action::Reload => {
1778            for (id, window) in window_manager.iter_mut() {
1779                let Some(ui) = interfaces.remove(&id) else {
1780                    continue;
1781                };
1782
1783                let cache = ui.into_cache();
1784                let size = window.state.logical_size();
1785
1786                let _ = interfaces.insert(
1787                    id,
1788                    build_user_interface(program, cache, &mut window.renderer, size, id),
1789                );
1790
1791                window.raw.request_redraw();
1792            }
1793        }
1794        Action::Exit => {
1795            control_sender
1796                .start_send(Control::Exit)
1797                .expect("Send control action");
1798        }
1799    }
1800}
1801
1802/// Build the user interface for every window.
1803pub fn build_user_interfaces<'a, P: Program, C>(
1804    program: &'a program::Instance<P>,
1805    window_manager: &mut window::Manager<P, C>,
1806    mut cached_user_interfaces: FxHashMap<window::Id, user_interface::Cache>,
1807    proxy: &mut Proxy<P::Message>,
1808) -> FxHashMap<window::Id, UserInterface<'a, P::Message, P::Theme, P::Renderer>>
1809where
1810    C: Compositor<Renderer = P::Renderer>,
1811    P::Theme: theme::Base,
1812{
1813    for (id, window) in window_manager.iter_mut() {
1814        let old_size = window.state.logical_size();
1815
1816        window.state.synchronize(program, id, &window.raw);
1817
1818        let new_size = window.state.logical_size();
1819
1820        if old_size != new_size {
1821            proxy.send_action(Action::Event {
1822                window: id,
1823                event: core::Event::Window(window::Event::Resized(new_size)),
1824            });
1825        }
1826
1827        window.renderer.hint(window.state.scale());
1828    }
1829
1830    debug::theme_changed(|| {
1831        window_manager
1832            .first()
1833            .and_then(|window| theme::Base::seed(window.state.theme()))
1834    });
1835
1836    cached_user_interfaces
1837        .drain()
1838        .filter_map(|(id, cache)| {
1839            let window = window_manager.get_mut(id)?;
1840
1841            Some((
1842                id,
1843                build_user_interface(
1844                    program,
1845                    cache,
1846                    &mut window.renderer,
1847                    window.state.logical_size(),
1848                    id,
1849                ),
1850            ))
1851        })
1852        .collect()
1853}
1854
1855/// Returns true if the provided event should cause a [`Program`] to
1856/// exit.
1857pub fn user_force_quit(
1858    event: &winit::event::WindowEvent,
1859    _modifiers: winit::keyboard::ModifiersState,
1860) -> bool {
1861    match event {
1862        #[cfg(target_os = "macos")]
1863        winit::event::WindowEvent::KeyboardInput {
1864            event:
1865                winit::event::KeyEvent {
1866                    logical_key: winit::keyboard::Key::Character(c),
1867                    state: winit::event::ElementState::Pressed,
1868                    ..
1869                },
1870            ..
1871        } if c == "q" && _modifiers.super_key() => true,
1872        _ => false,
1873    }
1874}
1875
1876#[cfg(feature = "sysinfo")]
1877fn system_information(graphics: compositor::Information) -> system::Information {
1878    use sysinfo::{Process, System};
1879
1880    let mut system = System::new_all();
1881    system.refresh_all();
1882
1883    let cpu_brand = system
1884        .cpus()
1885        .first()
1886        .map(|cpu| cpu.brand().to_string())
1887        .unwrap_or_default();
1888
1889    let memory_used = sysinfo::get_current_pid()
1890        .and_then(|pid| system.process(pid).ok_or("Process not found"))
1891        .map(Process::memory)
1892        .ok();
1893
1894    system::Information {
1895        system_name: System::name(),
1896        system_kernel: System::kernel_version(),
1897        system_version: System::long_os_version(),
1898        system_short_version: System::os_version(),
1899        cpu_brand,
1900        cpu_cores: system.physical_core_count(),
1901        memory_total: system.total_memory(),
1902        memory_used,
1903        graphics_adapter: graphics.adapter,
1904        graphics_backend: graphics.backend,
1905    }
1906}
1907
1908fn run_clipboard<Message: Send>(
1909    proxy: &mut Proxy<Message>,
1910    clipboard: &mut Clipboard,
1911    requests: core::Clipboard,
1912    window: window::Id,
1913) {
1914    for kind in requests.reads {
1915        let proxy = proxy.clone();
1916
1917        clipboard.read(kind, move |result| {
1918            proxy.send_action(Action::Event {
1919                window,
1920                event: core::Event::Clipboard(core::clipboard::Event::Read(result.map(Arc::new))),
1921            });
1922        });
1923    }
1924
1925    if let Some(content) = requests.write {
1926        let proxy = proxy.clone();
1927
1928        clipboard.write(content, move |result| {
1929            proxy.send_action(Action::Event {
1930                window,
1931                event: core::Event::Clipboard(core::clipboard::Event::Written(result)),
1932            });
1933        });
1934    }
1935}