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