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