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