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