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