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::backend;
41use crate::core::mouse;
42use crate::core::renderer;
43use crate::core::theme;
44use crate::core::time::Instant;
45use crate::core::widget::operation;
46use crate::core::{Point, Renderer, Size};
47use crate::futures::futures::channel::mpsc;
48use crate::futures::futures::channel::oneshot;
49use crate::futures::futures::task;
50use crate::futures::futures::{Future, StreamExt};
51use crate::futures::subscription;
52use crate::futures::{Executor, Runtime};
53use crate::graphics::{Compositor, Shell, compositor};
54use crate::runtime::font;
55use crate::runtime::image;
56use crate::runtime::system;
57use crate::runtime::user_interface::{self, UserInterface};
58use crate::runtime::{Action, Task};
59
60use program::Program;
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 backend_settings = backend::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 backend_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 backend_settings: backend::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 = window::Manager::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 backend_settings = backend_settings.clone();
562 let display_handle = display_handle.clone();
563 let proxy = proxy.clone();
564 let default_fonts = default_fonts.clone();
565
566 async move {
567 let shell = Shell::new(proxy.clone());
568
569 let mut compositor =
570 <P::Renderer as compositor::Default>::Compositor::new(
571 backend_settings,
572 display_handle,
573 window,
574 shell,
575 )
576 .await;
577
578 if let Ok(compositor) = &mut compositor {
579 for font in default_fonts {
580 compositor.load_font(font.clone());
581 }
582 }
583
584 compositor_sender
585 .send(compositor)
586 .ok()
587 .expect("Send compositor");
588
589 {
593 let (sender, _receiver) = oneshot::channel();
594
595 proxy.send_action(Action::Window(
596 runtime::window::Action::GetLatest(sender),
597 ));
598 }
599 }
600 };
601
602 #[cfg(target_arch = "wasm32")]
603 wasm_bindgen_futures::spawn_local(create_compositor);
604
605 #[cfg(not(target_arch = "wasm32"))]
606 runtime.block_on(create_compositor);
607
608 match compositor_receiver.await.expect("Wait for compositor") {
609 Ok(new_compositor) => {
610 compositor = Some(new_compositor);
611 }
612 Err(error) => {
613 let _ = control_sender.start_send(Control::Crash(error.into()));
614 continue;
615 }
616 }
617 }
618
619 let window_theme = window
620 .theme()
621 .map(conversion::theme_mode)
622 .unwrap_or_default();
623
624 if system_theme != window_theme {
625 system_theme = window_theme;
626
627 runtime.broadcast(subscription::Event::SystemThemeChanged(window_theme));
628 }
629
630 let is_first = window_manager.is_empty();
631 let window = window_manager.insert(
632 id,
633 window,
634 &program,
635 compositor.as_mut().expect("Compositor must be initialized"),
636 proxy.clone(),
637 renderer_settings,
638 exit_on_close_request,
639 system_theme,
640 );
641
642 window
643 .raw
644 .set_theme(conversion::window_theme(window.state.theme_mode()));
645
646 debug::theme_changed(|| {
647 if is_first {
648 theme::Base::seed(window.state.theme())
649 } else {
650 None
651 }
652 });
653
654 let logical_size = window.state.logical_size();
655
656 #[cfg(feature = "hinting")]
657 window.renderer.hint(window.state.scale_factor());
658
659 let _ = user_interfaces.insert(
660 id,
661 build_user_interface(
662 &program,
663 user_interface::Cache::default(),
664 &mut window.renderer,
665 logical_size,
666 id,
667 ),
668 );
669 let _ = ui_caches.insert(id, user_interface::Cache::default());
670
671 if make_visible {
672 window.raw.set_visible(true);
673 }
674
675 events.push((
676 id,
677 core::Event::Window(window::Event::Opened {
678 position: window.position(),
679 size: window.state.logical_size(),
680 scale_factor: window.raw.scale_factor() as f32,
681 }),
682 ));
683
684 let _ = on_open.send(id);
685 is_window_opening = false;
686 }
687 Event::EventLoopAwakened(event) => {
688 match event {
689 event::Event::NewEvents(event::StartCause::Init) => {
690 for (_id, window) in window_manager.iter_mut() {
691 window.raw.request_redraw();
692 }
693 }
694 event::Event::NewEvents(event::StartCause::ResumeTimeReached { .. }) => {
695 let now = Instant::now();
696
697 for (_id, window) in window_manager.iter_mut() {
698 if let Some(redraw_at) = window.redraw_at
699 && redraw_at <= now
700 {
701 window.raw.request_redraw();
702 window.redraw_at = None;
703 }
704 }
705
706 if let Some(redraw_at) = window_manager.redraw_at() {
707 let _ = control_sender
708 .start_send(Control::ChangeFlow(ControlFlow::WaitUntil(redraw_at)));
709 } else {
710 let _ =
711 control_sender.start_send(Control::ChangeFlow(ControlFlow::Wait));
712 }
713 }
714 event::Event::PlatformSpecific(event::PlatformSpecific::MacOS(
715 event::MacOS::ReceivedUrl(url),
716 )) => {
717 runtime.broadcast(subscription::Event::PlatformSpecific(
718 subscription::PlatformSpecific::MacOS(
719 subscription::MacOS::ReceivedUrl(url),
720 ),
721 ));
722 }
723 event::Event::UserEvent(action) => {
724 run_action(
725 action,
726 &program,
727 &proxy,
728 &mut runtime,
729 &mut compositor,
730 &mut events,
731 &mut messages,
732 &mut clipboard,
733 &mut control_sender,
734 &mut user_interfaces,
735 &mut window_manager,
736 &mut ui_caches,
737 &mut is_window_opening,
738 &mut system_theme,
739 &mut renderer_settings,
740 );
741 actions += 1;
742 }
743 event::Event::WindowEvent {
744 window_id: id,
745 event: event::WindowEvent::RedrawRequested,
746 ..
747 } => {
748 let Some(mut current_compositor) = compositor.as_mut() else {
749 continue;
750 };
751
752 let Some((id, mut window)) = window_manager.get_mut_alias(id) else {
753 continue;
754 };
755
756 let physical_size = window.state.physical_size();
757 let mut logical_size = window.state.logical_size();
758
759 if physical_size.width == 0 || physical_size.height == 0 {
760 continue;
761 }
762
763 if window.surface_version != window.state.surface_version() {
765 #[cfg(feature = "hinting")]
766 window.renderer.hint(window.state.scale_factor());
767
768 let ui = user_interfaces.remove(&id).expect("Remove user interface");
769
770 let layout_span = debug::layout(id);
771 let _ = user_interfaces
772 .insert(id, ui.relayout(logical_size, &mut window.renderer));
773 layout_span.finish();
774
775 current_compositor.configure_surface(
776 &mut window.surface,
777 physical_size.width,
778 physical_size.height,
779 );
780
781 window.surface_version = window.state.surface_version();
782 }
783
784 let redraw_event =
785 core::Event::Window(window::Event::RedrawRequested(Instant::now()));
786
787 let cursor = window.state.cursor();
788
789 let mut interface =
790 user_interfaces.get_mut(&id).expect("Get user interface");
791
792 let interact_span = debug::interact(id);
793 let mut redraw_count = 0;
794
795 let state = loop {
796 let message_count = messages.len();
797 let (state, _) = interface.update(
798 &window.raw,
799 &window.waker,
800 slice::from_ref(&redraw_event),
801 cursor,
802 &mut window.renderer,
803 &mut messages,
804 );
805
806 if message_count == messages.len() && !state.has_layout_changed() {
807 break state;
808 }
809
810 if redraw_count >= 2 {
811 log::warn!(
812 "More than 3 consecutive RedrawRequested events \
813 produced layout invalidation"
814 );
815
816 break state;
817 }
818
819 redraw_count += 1;
820
821 if !messages.is_empty() {
822 let caches: FxHashMap<_, _> =
823 ManuallyDrop::into_inner(user_interfaces)
824 .into_iter()
825 .map(|(id, interface)| (id, interface.into_cache()))
826 .collect();
827
828 let actions = update(&mut program, &mut runtime, &mut messages);
829
830 user_interfaces = ManuallyDrop::new(build_user_interfaces(
831 &program,
832 &mut window_manager,
833 caches,
834 &mut proxy,
835 ));
836
837 for action in actions {
838 if let Action::Window(_) = action {
841 proxy.send_action(action);
842 continue;
843 }
844
845 run_action(
846 action,
847 &program,
848 &proxy,
849 &mut runtime,
850 &mut compositor,
851 &mut events,
852 &mut messages,
853 &mut clipboard,
854 &mut control_sender,
855 &mut user_interfaces,
856 &mut window_manager,
857 &mut ui_caches,
858 &mut is_window_opening,
859 &mut system_theme,
860 &mut renderer_settings,
861 );
862 }
863
864 for (window_id, window) in window_manager.iter_mut() {
865 if window_id == id {
867 continue;
868 }
869
870 window.raw.request_redraw();
871 }
872
873 let Some(next_compositor) = compositor.as_mut() else {
874 continue 'next_event;
875 };
876
877 current_compositor = next_compositor;
878 window = window_manager.get_mut(id).unwrap();
879
880 if logical_size != window.state.logical_size() {
882 logical_size = window.state.logical_size();
883
884 log::debug!(
885 "Window scale factor changed during a redraw request"
886 );
887
888 let ui =
889 user_interfaces.remove(&id).expect("Remove user interface");
890
891 let layout_span = debug::layout(id);
892 let _ = user_interfaces.insert(
893 id,
894 ui.relayout(logical_size, &mut window.renderer),
895 );
896 layout_span.finish();
897 }
898
899 interface = user_interfaces.get_mut(&id).unwrap();
900 }
901 };
902 interact_span.finish();
903
904 let draw_span = debug::draw(id);
905 interface.draw(
906 &mut window.renderer,
907 window.state.theme(),
908 &renderer::Style {
909 text_color: window.state.text_color(),
910 },
911 cursor,
912 );
913 draw_span.finish();
914
915 if let user_interface::State::Updated {
916 redraw_request,
917 input_method,
918 mouse_interaction,
919 clipboard: clipboard_requests,
920 ..
921 } = state
922 {
923 window.request_redraw(redraw_request);
924 window.request_input_method(input_method);
925 window.update_mouse(mouse_interaction);
926
927 run_clipboard(&mut proxy, &mut clipboard, clipboard_requests, id);
928 }
929
930 runtime.broadcast(subscription::Event::Interaction {
931 window: id,
932 event: redraw_event,
933 status: core::event::Status::Ignored,
934 });
935
936 window.draw_preedit();
937
938 let present_span = debug::present(id);
939 match current_compositor.present(
940 &mut window.renderer,
941 &mut window.surface,
942 window.state.viewport(),
943 window.state.background_color(),
944 || window.raw.pre_present_notify(),
945 ) {
946 Ok(()) => {
947 present_span.finish();
948 }
949 Err(error) => match error {
950 compositor::SurfaceError::OutOfMemory => {
951 panic!("{error:?}");
953 }
954 compositor::SurfaceError::Outdated
955 | compositor::SurfaceError::Lost => {
956 present_span.finish();
957
958 let physical_size = window.state.physical_size();
960
961 if error == compositor::SurfaceError::Lost {
962 window.surface = current_compositor.create_surface(
963 window.raw.clone(),
964 physical_size.width,
965 physical_size.height,
966 );
967 } else {
968 current_compositor.configure_surface(
969 &mut window.surface,
970 physical_size.width,
971 physical_size.height,
972 );
973 }
974
975 window.raw.request_redraw();
976 }
977 compositor::SurfaceError::Occluded => {
978 present_span.finish();
979
980 }
982 _ => {
983 present_span.finish();
984
985 log::warn!("Error {error:?} when presenting surface.");
986
987 for (_id, window) in window_manager.iter_mut() {
989 window.raw.request_redraw();
990 }
991 }
992 },
993 }
994 }
995 event::Event::WindowEvent {
996 event: window_event,
997 window_id,
998 } => {
999 if !is_daemon
1000 && matches!(window_event, winit::event::WindowEvent::Destroyed)
1001 && !is_window_opening
1002 && window_manager.is_empty()
1003 {
1004 control_sender
1005 .start_send(Control::Exit)
1006 .expect("Send control action");
1007
1008 continue;
1009 }
1010
1011 let Some((id, window)) = window_manager.get_mut_alias(window_id) else {
1012 continue;
1013 };
1014
1015 match window_event {
1016 winit::event::WindowEvent::Resized(_)
1017 | winit::event::WindowEvent::Occluded(false) => {
1018 window.raw.request_redraw();
1019 }
1020 winit::event::WindowEvent::ThemeChanged(theme) => {
1021 let mode = conversion::theme_mode(theme);
1022
1023 if mode != system_theme {
1024 system_theme = mode;
1025
1026 runtime
1027 .broadcast(subscription::Event::SystemThemeChanged(mode));
1028 }
1029 }
1030 _ => {}
1031 }
1032
1033 if matches!(window_event, winit::event::WindowEvent::CloseRequested)
1034 && window.exit_on_close_request
1035 {
1036 run_action(
1037 Action::Window(runtime::window::Action::Close(id)),
1038 &program,
1039 &proxy,
1040 &mut runtime,
1041 &mut compositor,
1042 &mut events,
1043 &mut messages,
1044 &mut clipboard,
1045 &mut control_sender,
1046 &mut user_interfaces,
1047 &mut window_manager,
1048 &mut ui_caches,
1049 &mut is_window_opening,
1050 &mut system_theme,
1051 &mut renderer_settings,
1052 );
1053 } else {
1054 window.state.update(&program, &window.raw, &window_event);
1055
1056 if let Some(event) = conversion::window_event(
1057 window_event,
1058 window.state.scale_factor(),
1059 window.state.modifiers(),
1060 ) {
1061 events.push((id, event));
1062 }
1063 }
1064 }
1065 event::Event::AboutToWait => {
1066 if actions > 0 {
1067 proxy.free_slots(actions);
1068 actions = 0;
1069 }
1070
1071 if events.is_empty() && messages.is_empty() && window_manager.is_idle() {
1072 continue;
1073 }
1074
1075 let mut uis_stale = false;
1076
1077 for (id, window) in window_manager.iter_mut() {
1078 let interact_span = debug::interact(id);
1079 let mut window_events = vec![];
1080
1081 events.retain(|(window_id, event)| {
1082 if *window_id == id {
1083 window_events.push(event.clone());
1084 false
1085 } else {
1086 true
1087 }
1088 });
1089
1090 if window_events.is_empty() {
1091 continue;
1092 }
1093
1094 let (ui_state, statuses) = user_interfaces
1095 .get_mut(&id)
1096 .expect("Get user interface")
1097 .update(
1098 &window.raw,
1099 &window.waker,
1100 &window_events,
1101 window.state.cursor(),
1102 &mut window.renderer,
1103 &mut messages,
1104 );
1105
1106 #[cfg(feature = "unconditional-rendering")]
1107 window.request_redraw(window::RedrawRequest::NextFrame);
1108
1109 match ui_state {
1110 user_interface::State::Updated {
1111 redraw_request: _redraw_request,
1112 mouse_interaction,
1113 clipboard: clipboard_requests,
1114 ..
1115 } => {
1116 window.update_mouse(mouse_interaction);
1117
1118 #[cfg(not(feature = "unconditional-rendering"))]
1119 window.request_redraw(_redraw_request);
1120
1121 run_clipboard(
1122 &mut proxy,
1123 &mut clipboard,
1124 clipboard_requests,
1125 id,
1126 );
1127 }
1128 user_interface::State::Outdated => {
1129 uis_stale = true;
1130 }
1131 }
1132
1133 for (event, status) in window_events.into_iter().zip(statuses) {
1134 runtime.broadcast(subscription::Event::Interaction {
1135 window: id,
1136 event,
1137 status,
1138 });
1139 }
1140
1141 interact_span.finish();
1142 }
1143
1144 for (id, event) in events.drain(..) {
1145 runtime.broadcast(subscription::Event::Interaction {
1146 window: id,
1147 event,
1148 status: core::event::Status::Ignored,
1149 });
1150 }
1151
1152 if !messages.is_empty() || uis_stale {
1153 let cached_interfaces: FxHashMap<_, _> =
1154 ManuallyDrop::into_inner(user_interfaces)
1155 .into_iter()
1156 .map(|(id, ui)| (id, ui.into_cache()))
1157 .collect();
1158
1159 let actions = update(&mut program, &mut runtime, &mut messages);
1160
1161 user_interfaces = ManuallyDrop::new(build_user_interfaces(
1162 &program,
1163 &mut window_manager,
1164 cached_interfaces,
1165 &mut proxy,
1166 ));
1167
1168 for action in actions {
1169 run_action(
1170 action,
1171 &program,
1172 &proxy,
1173 &mut runtime,
1174 &mut compositor,
1175 &mut events,
1176 &mut messages,
1177 &mut clipboard,
1178 &mut control_sender,
1179 &mut user_interfaces,
1180 &mut window_manager,
1181 &mut ui_caches,
1182 &mut is_window_opening,
1183 &mut system_theme,
1184 &mut renderer_settings,
1185 );
1186 }
1187
1188 for (_id, window) in window_manager.iter_mut() {
1189 window.raw.request_redraw();
1190 }
1191 }
1192
1193 if let Some(redraw_at) = window_manager.redraw_at() {
1194 let _ = control_sender
1195 .start_send(Control::ChangeFlow(ControlFlow::WaitUntil(redraw_at)));
1196 } else {
1197 let _ =
1198 control_sender.start_send(Control::ChangeFlow(ControlFlow::Wait));
1199 }
1200 }
1201 _ => {}
1202 }
1203 }
1204 Event::Exit => break,
1205 }
1206 }
1207
1208 let _ = ManuallyDrop::into_inner(user_interfaces);
1209}
1210
1211fn build_user_interface<'a, P: Program>(
1213 program: &'a program::Instance<P>,
1214 cache: user_interface::Cache,
1215 renderer: &mut P::Renderer,
1216 size: Size,
1217 id: window::Id,
1218) -> UserInterface<'a, P::Message, P::Theme, P::Renderer>
1219where
1220 P::Theme: theme::Base,
1221{
1222 let view_span = debug::view(id);
1223 let view = program.view(id);
1224 view_span.finish();
1225
1226 let layout_span = debug::layout(id);
1227 let user_interface = UserInterface::build(view, size, cache, renderer);
1228 layout_span.finish();
1229
1230 user_interface
1231}
1232
1233fn update<P: Program, E: Executor>(
1234 program: &mut program::Instance<P>,
1235 runtime: &mut Runtime<E, Proxy<P::Message>, Action<P::Message>>,
1236 messages: &mut Vec<P::Message>,
1237) -> Vec<Action<P::Message>>
1238where
1239 P::Theme: theme::Base,
1240{
1241 use futures::futures;
1242
1243 let mut actions = Vec::new();
1244 let mut outputs = Vec::new();
1245
1246 while !messages.is_empty() {
1247 for message in messages.drain(..) {
1248 let task = runtime.enter(|| program.update(message));
1249
1250 if let Some(mut stream) = runtime::task::into_stream(task) {
1251 let waker = futures::task::noop_waker_ref();
1252 let mut context = futures::task::Context::from_waker(waker);
1253
1254 loop {
1256 match runtime.enter(|| stream.poll_next_unpin(&mut context)) {
1257 futures::task::Poll::Ready(Some(Action::Output(output))) => {
1258 outputs.push(output);
1259 }
1260 futures::task::Poll::Ready(Some(action)) => {
1261 actions.push(action);
1262 }
1263 futures::task::Poll::Ready(None) => {
1264 break;
1265 }
1266 futures::task::Poll::Pending => {
1267 runtime.run(stream);
1268 break;
1269 }
1270 }
1271 }
1272 }
1273 }
1274
1275 messages.append(&mut outputs);
1276 }
1277
1278 let subscription = runtime.enter(|| program.subscription());
1279 let recipes = subscription::into_recipes(subscription.map(Action::Output));
1280
1281 runtime.track(recipes);
1282
1283 actions
1284}
1285
1286fn run_action<'a, P, C>(
1287 action: Action<P::Message>,
1288 program: &'a program::Instance<P>,
1289 _proxy: &Proxy<P::Message>,
1290 runtime: &mut Runtime<P::Executor, Proxy<P::Message>, Action<P::Message>>,
1291 compositor: &mut Option<C>,
1292 events: &mut Vec<(window::Id, core::Event)>,
1293 messages: &mut Vec<P::Message>,
1294 clipboard: &mut Clipboard,
1295 control_sender: &mut mpsc::UnboundedSender<Control>,
1296 interfaces: &mut FxHashMap<window::Id, UserInterface<'a, P::Message, P::Theme, P::Renderer>>,
1297 window_manager: &mut window::Manager<P, C>,
1298 ui_caches: &mut FxHashMap<window::Id, user_interface::Cache>,
1299 is_window_opening: &mut bool,
1300 system_theme: &mut theme::Mode,
1301 renderer_settings: &mut renderer::Settings,
1302) where
1303 P: Program,
1304 C: Compositor<Renderer = P::Renderer> + 'static,
1305 P::Theme: theme::Base,
1306{
1307 use crate::core::Renderer as _;
1308 use crate::runtime::backend;
1309 use crate::runtime::clipboard;
1310 use crate::runtime::window;
1311
1312 match action {
1313 Action::Output(message) => {
1314 messages.push(message);
1315 }
1316 Action::Clipboard(action) => match action {
1317 clipboard::Action::Read { kind, channel } => {
1318 clipboard.read(kind, move |result| {
1319 let _ = channel.send(result);
1320 });
1321 }
1322 clipboard::Action::Write { content, channel } => {
1323 clipboard.write(content, move |result| {
1324 let _ = channel.send(result);
1325 });
1326 }
1327 },
1328 Action::Window(action) => match action {
1329 window::Action::Open(id, settings, channel) => {
1330 let monitor = window_manager.last_monitor();
1331
1332 control_sender
1333 .start_send(Control::CreateWindow {
1334 id,
1335 settings,
1336 title: program.title(id),
1337 scale_factor: program.scale_factor(id),
1338 monitor,
1339 on_open: channel,
1340 })
1341 .expect("Send control action");
1342
1343 *is_window_opening = true;
1344 }
1345 window::Action::Close(id) => {
1346 let _ = ui_caches.remove(&id);
1347 let _ = interfaces.remove(&id);
1348
1349 if window_manager.remove(id).is_some() {
1350 events.push((id, core::Event::Window(core::window::Event::Closed)));
1351 }
1352
1353 if window_manager.is_empty() {
1354 *compositor = None;
1355 }
1356 }
1357 window::Action::GetOldest(channel) => {
1358 let id = window_manager.iter_mut().next().map(|(id, _window)| id);
1359
1360 let _ = channel.send(id);
1361 }
1362 window::Action::GetLatest(channel) => {
1363 let id = window_manager.iter_mut().last().map(|(id, _window)| id);
1364
1365 let _ = channel.send(id);
1366 }
1367 window::Action::Drag(id) => {
1368 if let Some(window) = window_manager.get_mut(id) {
1369 let _ = window.raw.drag_window();
1370 }
1371 }
1372 window::Action::DragResize(id, direction) => {
1373 if let Some(window) = window_manager.get_mut(id) {
1374 let _ = window
1375 .raw
1376 .drag_resize_window(conversion::resize_direction(direction));
1377 }
1378 }
1379 window::Action::Resize(id, size) => {
1380 if let Some(window) = window_manager.get_mut(id) {
1381 let _ = window.raw.request_inner_size(
1382 winit::dpi::LogicalSize {
1383 width: size.width,
1384 height: size.height,
1385 }
1386 .to_physical::<f32>(f64::from(window.state.scale_factor())),
1387 );
1388 }
1389 }
1390 window::Action::SetMinSize(id, size) => {
1391 if let Some(window) = window_manager.get_mut(id) {
1392 window.raw.set_min_inner_size(size.map(|size| {
1393 winit::dpi::LogicalSize {
1394 width: size.width,
1395 height: size.height,
1396 }
1397 .to_physical::<f32>(f64::from(window.state.scale_factor()))
1398 }));
1399 }
1400 }
1401 window::Action::SetMaxSize(id, size) => {
1402 if let Some(window) = window_manager.get_mut(id) {
1403 window.raw.set_max_inner_size(size.map(|size| {
1404 winit::dpi::LogicalSize {
1405 width: size.width,
1406 height: size.height,
1407 }
1408 .to_physical::<f32>(f64::from(window.state.scale_factor()))
1409 }));
1410 }
1411 }
1412 window::Action::SetResizeIncrements(id, increments) => {
1413 if let Some(window) = window_manager.get_mut(id) {
1414 window.raw.set_resize_increments(increments.map(|size| {
1415 winit::dpi::LogicalSize {
1416 width: size.width,
1417 height: size.height,
1418 }
1419 .to_physical::<f32>(f64::from(window.state.scale_factor()))
1420 }));
1421 }
1422 }
1423 window::Action::SetResizable(id, resizable) => {
1424 if let Some(window) = window_manager.get_mut(id) {
1425 window.raw.set_resizable(resizable);
1426 }
1427 }
1428 window::Action::GetSize(id, channel) => {
1429 if let Some(window) = window_manager.get_mut(id) {
1430 let size = window.state.logical_size();
1431 let _ = channel.send(Size::new(size.width, size.height));
1432 }
1433 }
1434 window::Action::GetMaximized(id, channel) => {
1435 if let Some(window) = window_manager.get_mut(id) {
1436 let _ = channel.send(window.raw.is_maximized());
1437 }
1438 }
1439 window::Action::Maximize(id, maximized) => {
1440 if let Some(window) = window_manager.get_mut(id) {
1441 window.raw.set_maximized(maximized);
1442 }
1443 }
1444 window::Action::GetMinimized(id, channel) => {
1445 if let Some(window) = window_manager.get_mut(id) {
1446 let _ = channel.send(window.raw.is_minimized());
1447 }
1448 }
1449 window::Action::Minimize(id, minimized) => {
1450 if let Some(window) = window_manager.get_mut(id) {
1451 window.raw.set_minimized(minimized);
1452 }
1453 }
1454 window::Action::GetPosition(id, channel) => {
1455 if let Some(window) = window_manager.get(id) {
1456 let position = window
1457 .raw
1458 .outer_position()
1459 .map(|position| {
1460 let position = position.to_logical::<f32>(window.raw.scale_factor());
1461
1462 Point::new(position.x, position.y)
1463 })
1464 .ok();
1465
1466 let _ = channel.send(position);
1467 }
1468 }
1469 window::Action::GetScaleFactor(id, channel) => {
1470 if let Some(window) = window_manager.get_mut(id) {
1471 let scale_factor = window.raw.scale_factor();
1472
1473 let _ = channel.send(scale_factor as f32);
1474 }
1475 }
1476 window::Action::Move(id, position) => {
1477 if let Some(window) = window_manager.get_mut(id) {
1478 window.raw.set_outer_position(winit::dpi::LogicalPosition {
1479 x: position.x,
1480 y: position.y,
1481 });
1482 }
1483 }
1484 window::Action::SetMode(id, mode) => {
1485 if let Some(window) = window_manager.get_mut(id) {
1486 window.raw.set_visible(conversion::visible(mode));
1487 window
1488 .raw
1489 .set_fullscreen(conversion::fullscreen(window.raw.current_monitor(), mode));
1490 }
1491 }
1492 window::Action::SetIcon(id, icon) => {
1493 if let Some(window) = window_manager.get_mut(id) {
1494 window.raw.set_window_icon(conversion::icon(icon));
1495 }
1496 }
1497 window::Action::GetMode(id, channel) => {
1498 if let Some(window) = window_manager.get_mut(id) {
1499 let mode = if window.raw.is_visible().unwrap_or(true) {
1500 conversion::mode(window.raw.fullscreen())
1501 } else {
1502 core::window::Mode::Hidden
1503 };
1504
1505 let _ = channel.send(mode);
1506 }
1507 }
1508 window::Action::ToggleMaximize(id) => {
1509 if let Some(window) = window_manager.get_mut(id) {
1510 window.raw.set_maximized(!window.raw.is_maximized());
1511 }
1512 }
1513 window::Action::ToggleDecorations(id) => {
1514 if let Some(window) = window_manager.get_mut(id) {
1515 window.raw.set_decorations(!window.raw.is_decorated());
1516 }
1517 }
1518 window::Action::RequestUserAttention(id, attention_type) => {
1519 if let Some(window) = window_manager.get_mut(id) {
1520 window
1521 .raw
1522 .request_user_attention(attention_type.map(conversion::user_attention));
1523 }
1524 }
1525 window::Action::GainFocus(id) => {
1526 if let Some(window) = window_manager.get_mut(id) {
1527 window.raw.focus_window();
1528 }
1529 }
1530 window::Action::SetLevel(id, level) => {
1531 if let Some(window) = window_manager.get_mut(id) {
1532 window.raw.set_window_level(conversion::window_level(level));
1533 }
1534 }
1535 window::Action::ShowSystemMenu(id) => {
1536 if let Some(window) = window_manager.get_mut(id)
1537 && let mouse::Cursor::Available(point) = window.state.cursor()
1538 {
1539 window.raw.show_window_menu(winit::dpi::LogicalPosition {
1540 x: point.x,
1541 y: point.y,
1542 });
1543 }
1544 }
1545 window::Action::GetRawId(id, channel) => {
1546 if let Some(window) = window_manager.get_mut(id) {
1547 let _ = channel.send(window.raw.id().into());
1548 }
1549 }
1550 window::Action::Run(id, f) => {
1551 if let Some(window) = window_manager.get_mut(id) {
1552 f(&window.raw);
1553 }
1554 }
1555 window::Action::Screenshot(id, channel) => {
1556 if let Some(window) = window_manager.get_mut(id)
1557 && let Some(compositor) = compositor
1558 {
1559 let bytes = compositor.screenshot(
1560 &mut window.renderer,
1561 window.state.viewport(),
1562 window.state.background_color(),
1563 );
1564
1565 let _ = channel.send(core::window::Screenshot::new(
1566 bytes,
1567 window.state.physical_size(),
1568 window.state.scale_factor(),
1569 ));
1570 }
1571 }
1572 window::Action::EnableMousePassthrough(id) => {
1573 if let Some(window) = window_manager.get_mut(id) {
1574 let _ = window.raw.set_cursor_hittest(false);
1575 }
1576 }
1577 window::Action::DisableMousePassthrough(id) => {
1578 if let Some(window) = window_manager.get_mut(id) {
1579 let _ = window.raw.set_cursor_hittest(true);
1580 }
1581 }
1582 window::Action::GetMonitorSize(id, channel) => {
1583 if let Some(window) = window_manager.get(id) {
1584 let size = window.raw.current_monitor().map(|monitor| {
1585 let scale = window.state.scale_factor();
1586 let size = monitor.size().to_logical(f64::from(scale));
1587
1588 Size::new(size.width, size.height)
1589 });
1590
1591 let _ = channel.send(size);
1592 }
1593 }
1594 window::Action::SetAllowAutomaticTabbing(enabled) => {
1595 control_sender
1596 .start_send(Control::SetAutomaticWindowTabbing(enabled))
1597 .expect("Send control action");
1598 }
1599 window::Action::RedrawAll => {
1600 for (_id, window) in window_manager.iter_mut() {
1601 window.raw.request_redraw();
1602 }
1603 }
1604 window::Action::RelayoutAll => {
1605 for (id, window) in window_manager.iter_mut() {
1606 if let Some(ui) = interfaces.remove(&id) {
1607 let _ = interfaces.insert(
1608 id,
1609 ui.relayout(window.state.logical_size(), &mut window.renderer),
1610 );
1611 }
1612
1613 window.raw.request_redraw();
1614 }
1615 }
1616 },
1617 Action::System(action) => match action {
1618 system::Action::GetInformation(_channel) => {
1619 #[cfg(feature = "sysinfo")]
1620 {
1621 if let Some(compositor) = compositor {
1622 let graphics_info = compositor.information();
1623
1624 let _ = std::thread::spawn(move || {
1625 let information = system_information(graphics_info);
1626
1627 let _ = _channel.send(information);
1628 });
1629 }
1630 }
1631 }
1632 system::Action::GetTheme(channel) => {
1633 let _ = channel.send(*system_theme);
1634 }
1635 system::Action::NotifyTheme(mode) => {
1636 if mode != *system_theme {
1637 *system_theme = mode;
1638
1639 runtime.broadcast(subscription::Event::SystemThemeChanged(mode));
1640 }
1641
1642 let Some(theme) = conversion::window_theme(mode) else {
1643 return;
1644 };
1645
1646 for (_id, window) in window_manager.iter_mut() {
1647 window.state.update(
1648 program,
1649 &window.raw,
1650 &winit::event::WindowEvent::ThemeChanged(theme),
1651 );
1652 }
1653 }
1654 },
1655 Action::Font(action) => match action {
1656 font::Action::Load { bytes, channel } => {
1657 if let Some(compositor) = compositor {
1658 let result = compositor.load_font(bytes.clone());
1659 let _ = channel.send(result);
1660 }
1661 }
1662 font::Action::List { channel } => {
1663 if let Some(compositor) = compositor {
1664 let fonts = compositor.list_fonts();
1665 let _ = channel.send(fonts);
1666 }
1667 }
1668 font::Action::SetDefaults { font, text_size } => {
1669 renderer_settings.default_font = font;
1670 renderer_settings.default_text_size = text_size;
1671
1672 let Some(compositor) = compositor else {
1673 return;
1674 };
1675
1676 for (id, window) in window_manager.iter_mut() {
1678 window.renderer = compositor.create_renderer(*renderer_settings);
1679
1680 let Some(ui) = interfaces.remove(&id) else {
1681 continue;
1682 };
1683
1684 let size = window.state.logical_size();
1685 let ui = ui.relayout(size, &mut window.renderer);
1686 let _ = interfaces.insert(id, ui);
1687
1688 window.raw.request_redraw();
1689 }
1690 }
1691 },
1692 Action::Widget(operation) => {
1693 let mut current_operation = Some(operation);
1694
1695 while let Some(mut operation) = current_operation.take() {
1696 for (id, ui) in interfaces.iter_mut() {
1697 if let Some(window) = window_manager.get_mut(*id) {
1698 ui.operate(&window.renderer, operation.as_mut());
1699 }
1700 }
1701
1702 match operation.finish() {
1703 operation::Outcome::None => {}
1704 operation::Outcome::Some(()) => {}
1705 operation::Outcome::Chain(next) => {
1706 current_operation = Some(next);
1707 }
1708 }
1709 }
1710
1711 for (_, window) in window_manager.iter_mut() {
1713 window.raw.request_redraw();
1714 }
1715 }
1716 Action::Image(action) => match action {
1717 image::Action::Allocate(handle, sender) => {
1718 if let Some((_id, window)) = window_manager.iter_mut().next() {
1720 window.renderer.allocate_image(&handle, move |allocation| {
1721 let _ = sender.send(allocation);
1722 });
1723 }
1724 }
1725 },
1726 Action::Backend(action) => match action {
1727 #[cfg(not(target_arch = "wasm32"))]
1728 backend::Action::Configure(settings, sender) => {
1729 let shell = Shell::new(_proxy.clone());
1730
1731 let mut new_compositor = if let Some(window) = window_manager.first() {
1732 match runtime.block_on(C::new(
1733 settings,
1734 window.raw.clone(),
1735 window.raw.clone(),
1736 shell,
1737 )) {
1738 Ok(compositor) => compositor,
1739 Err(error) => {
1740 let _ = sender.send(Err(error));
1741 return;
1742 }
1743 }
1744 } else {
1745 return;
1746 };
1747
1748 graphics::cache::invalidate_all();
1749
1750 window_manager.replace_with(|mut window| {
1751 let size = window.state.physical_size();
1752
1753 drop(window.renderer);
1754 drop(window.surface);
1755
1756 window.renderer = new_compositor.create_renderer(*renderer_settings);
1757 window.surface =
1758 new_compositor.create_surface(window.raw.clone(), size.width, size.height);
1759
1760 window
1761 });
1762
1763 *compositor = Some(new_compositor);
1764
1765 let _ = sender.send(Ok(()));
1766 }
1767 #[cfg(target_arch = "wasm32")]
1768 backend::Action::Configure(_, _) => {}
1769 },
1770 Action::Event { window, event } => {
1771 events.push((window, event));
1772 }
1773 Action::Tick => {
1774 for (_id, window) in window_manager.iter_mut() {
1775 window.renderer.tick();
1776 }
1777 }
1778 Action::Reload => {
1779 for (id, window) in window_manager.iter_mut() {
1780 let Some(ui) = interfaces.remove(&id) else {
1781 continue;
1782 };
1783
1784 let cache = ui.into_cache();
1785 let size = window.state.logical_size();
1786
1787 let _ = interfaces.insert(
1788 id,
1789 build_user_interface(program, cache, &mut window.renderer, size, id),
1790 );
1791
1792 window.raw.request_redraw();
1793 }
1794 }
1795 Action::Exit => {
1796 control_sender
1797 .start_send(Control::Exit)
1798 .expect("Send control action");
1799 }
1800 }
1801}
1802
1803pub fn build_user_interfaces<'a, P: Program, C>(
1805 program: &'a program::Instance<P>,
1806 window_manager: &mut window::Manager<P, C>,
1807 mut cached_user_interfaces: FxHashMap<window::Id, user_interface::Cache>,
1808 proxy: &mut Proxy<P::Message>,
1809) -> FxHashMap<window::Id, UserInterface<'a, P::Message, P::Theme, P::Renderer>>
1810where
1811 C: Compositor<Renderer = P::Renderer>,
1812 P::Theme: theme::Base,
1813{
1814 for (id, window) in window_manager.iter_mut() {
1815 let old_size = window.state.logical_size();
1816
1817 window.state.synchronize(program, id, &window.raw);
1818
1819 let new_size = window.state.logical_size();
1820
1821 if old_size != new_size {
1822 proxy.send_action(Action::Event {
1823 window: id,
1824 event: core::Event::Window(window::Event::Resized(new_size)),
1825 });
1826 }
1827
1828 #[cfg(feature = "hinting")]
1829 window.renderer.hint(window.state.scale_factor());
1830 }
1831
1832 debug::theme_changed(|| {
1833 window_manager
1834 .first()
1835 .and_then(|window| theme::Base::seed(window.state.theme()))
1836 });
1837
1838 cached_user_interfaces
1839 .drain()
1840 .filter_map(|(id, cache)| {
1841 let window = window_manager.get_mut(id)?;
1842
1843 Some((
1844 id,
1845 build_user_interface(
1846 program,
1847 cache,
1848 &mut window.renderer,
1849 window.state.logical_size(),
1850 id,
1851 ),
1852 ))
1853 })
1854 .collect()
1855}
1856
1857pub fn user_force_quit(
1860 event: &winit::event::WindowEvent,
1861 _modifiers: winit::keyboard::ModifiersState,
1862) -> bool {
1863 match event {
1864 #[cfg(target_os = "macos")]
1865 winit::event::WindowEvent::KeyboardInput {
1866 event:
1867 winit::event::KeyEvent {
1868 logical_key: winit::keyboard::Key::Character(c),
1869 state: winit::event::ElementState::Pressed,
1870 ..
1871 },
1872 ..
1873 } if c == "q" && _modifiers.super_key() => true,
1874 _ => false,
1875 }
1876}
1877
1878#[cfg(feature = "sysinfo")]
1879fn system_information(graphics: compositor::Information) -> system::Information {
1880 use sysinfo::{Process, System};
1881
1882 let mut system = System::new_all();
1883 system.refresh_all();
1884
1885 let cpu_brand = system
1886 .cpus()
1887 .first()
1888 .map(|cpu| cpu.brand().to_string())
1889 .unwrap_or_default();
1890
1891 let memory_used = sysinfo::get_current_pid()
1892 .and_then(|pid| system.process(pid).ok_or("Process not found"))
1893 .map(Process::memory)
1894 .ok();
1895
1896 system::Information {
1897 system_name: System::name(),
1898 system_kernel: System::kernel_version(),
1899 system_version: System::long_os_version(),
1900 system_short_version: System::os_version(),
1901 cpu_brand,
1902 cpu_cores: system.physical_core_count(),
1903 memory_total: system.total_memory(),
1904 memory_used,
1905 graphics_adapter: graphics.adapter,
1906 graphics_backend: graphics.backend,
1907 }
1908}
1909
1910fn run_clipboard<Message: Send>(
1911 proxy: &mut Proxy<Message>,
1912 clipboard: &mut Clipboard,
1913 requests: core::Clipboard,
1914 window: window::Id,
1915) {
1916 for kind in requests.reads {
1917 let proxy = proxy.clone();
1918
1919 clipboard.read(kind, move |result| {
1920 proxy.send_action(Action::Event {
1921 window,
1922 event: core::Event::Clipboard(core::clipboard::Event::Read(result.map(Arc::new))),
1923 });
1924 });
1925 }
1926
1927 if let Some(content) = requests.write {
1928 let proxy = proxy.clone();
1929
1930 clipboard.write(content, move |result| {
1931 proxy.send_action(Action::Event {
1932 window,
1933 event: core::Event::Clipboard(core::clipboard::Event::Written(result)),
1934 });
1935 });
1936 }
1937}