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