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