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