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