1use crate::application_activations::{ActivationRouteBridge, ApplicationActivationService};
2use crate::application_shell::{
3 ApplicationShellService, NativeApplicationShellCompletion, NativeApplicationShellEvent,
4 NativeApplicationShellRequest,
5};
6use crate::application_types::{
7 ApplicationSchedulerCounters, LastWindowPolicy, RestorableWindowFactory,
8 RestorableWindowMetadata, WindowError, WindowRestorationId,
9};
10use crate::context::BuildContext;
11use crate::file_dialogs::{
12 FileDialogBridge, FileDialogCompletionStatus, NativeFileDialogCompletion,
13 NativeFileDialogRequest, QueuedFileDialogRequest, explicitly_unsupported_request,
14 result_matches_request,
15};
16use crate::frame::{FrameStats, Runtime};
17use crate::global_shortcuts::{
18 GlobalShortcutBridge, GlobalShortcutCompletionStatus, GlobalShortcutService,
19 NativeGlobalShortcutCompletion, NativeGlobalShortcutOperation, NativeGlobalShortcutRequest,
20 PendingGlobalShortcutRequest, QueuedGlobalShortcutRequest, drain_queued,
21};
22use crate::profiling::{
23 AccessibilitySnapshot, BudgetStatistics, FrameHistory, FrameRecord, FrameWork, GpuSample,
24 PerformanceHub, PerformanceProfiler, PerformanceSnapshot, ProfilerMode, RenderFrameMetrics,
25 TextCacheSnapshot, WidgetWorkSnapshot, WindowPerformance,
26};
27use crate::request_registry::{CompletionRejection, RequestPhase, RequestRegistry};
28use crate::restoration::{self, RestorationConfig, RestorationDiagnostics};
29use crate::scheduler_counters;
30use crate::simulation::{self, Simulation};
31use crate::tasks::{self, RuntimeWake, Task, TaskFailure, TaskHandle, TokioHandle};
32use crate::transient_presentation::{
33 ResolvedTransientPresentation, TransientPresentationResolution,
34};
35use crate::window_commands::{
36 DisplayCatalog, NativeOperationCompletionStatus, NativeWindowCommand, QueuedNativeRequest,
37 QueuedWindowCommand, WindowCommandBridge, WindowHandle,
38};
39use crate::window_state::{
40 ApplicationDiagnostics, ContentSizeCoordinator, WindowDiagnostics, WindowManager, WindowRecord,
41 WindowRegistry,
42};
43use incular_accessibility::{
44 AccessKitProjection, AccessibilityDiagnostics, NativeAccessibilityUpdate, SemanticActionRequest,
45};
46use incular_config::{Constraints, RuntimeEnvironment};
47use incular_core::RestorationKey;
48use incular_platform::{
49 ApplicationActivation, Clipboard, DisplayId, DisplaySnapshot, ExternalDragEvent,
50 ExternalDragResponse, FileDialogError, FileDialogOutcome, FileDialogRequest,
51 FileDialogRequestId, LaunchActivation, NativeOperationCompletion, NativeRequestId,
52 PlatformCapabilities, PlatformEvent, PlatformLifecycle, PlatformOperationError,
53 PlatformOperationErrorKind, PlatformOperationResult, SingleInstancePolicy, TextInputCommand,
54 WindowCommand, WindowEvent, WindowEventKind, WindowId, WindowLifecycle, WindowOperation,
55 WindowOptions,
56};
57use incular_rendering::DisplayList;
58use incular_widgets::internal::{Key, MouseCursor, PlatformMenuBinding, TreeError};
59use incular_widgets::{RouteInformation, RouteInformationProvider, Widget};
60use std::{
61 cell::RefCell,
62 collections::{HashMap, HashSet, VecDeque},
63 rc::Rc,
64 sync::{Arc, RwLock, atomic::Ordering, mpsc},
65 time::Instant,
66};
67use tokio::sync::oneshot;
68
69struct PendingNativeRequest {
70 window_id: WindowId,
71 sender: oneshot::Sender<PlatformOperationResult>,
72}
73
74struct PendingFileDialogRequest {
75 window_id: WindowId,
76 request: FileDialogRequest,
77 sender: Option<oneshot::Sender<Result<FileDialogOutcome, FileDialogError>>>,
78 cancelled: Arc<std::sync::atomic::AtomicBool>,
79}
80
81pub struct Application {
85 pub(crate) scheduler: Rc<RefCell<tasks::TaskScheduler>>,
86 pub(crate) registry: Rc<RefCell<WindowRegistry>>,
87 pub(crate) manager: WindowManager,
88 pub(crate) command_receiver: mpsc::Receiver<QueuedWindowCommand>,
89 pub(crate) request_cancellation_receiver: mpsc::Receiver<NativeRequestId>,
90 pending_native_requests: RequestRegistry<NativeRequestId, PendingNativeRequest>,
91 file_dialog_receiver: mpsc::Receiver<QueuedFileDialogRequest>,
92 file_dialog_cancellation_receiver: mpsc::Receiver<FileDialogRequestId>,
93 file_dialog_bridge: Arc<FileDialogBridge>,
94 pending_file_dialog_requests: RequestRegistry<FileDialogRequestId, PendingFileDialogRequest>,
95 active_file_dialog_requests: HashMap<WindowId, FileDialogRequestId>,
96 queued_file_dialog_requests: HashMap<WindowId, VecDeque<FileDialogRequestId>>,
97 native_file_dialog_requests: VecDeque<NativeFileDialogRequest>,
98 global_shortcut_receiver: mpsc::Receiver<QueuedGlobalShortcutRequest>,
99 global_shortcut_bridge: Arc<GlobalShortcutBridge>,
100 pending_global_shortcut_requests: RequestRegistry<
101 crate::global_shortcuts::GlobalShortcutRequestId,
102 PendingGlobalShortcutRequest,
103 >,
104 native_global_shortcut_requests: VecDeque<NativeGlobalShortcutRequest>,
105 platform_capabilities: Arc<RwLock<PlatformCapabilities>>,
106 application_shell: ApplicationShellService,
107 activations: ApplicationActivationService,
108 launch_activation: LaunchActivation,
109 single_instance_policy: Option<SingleInstancePolicy>,
110 display_catalog: Arc<RwLock<DisplayCatalog>>,
111 pub(crate) simulation_receiver: mpsc::Receiver<simulation::SimulationRequest>,
112 pub(crate) simulation_bridge: Arc<simulation::SimulationBridge>,
113 pub(crate) simulation_waiters: simulation::SimulationWaiters,
114 pub(crate) native_commands: Rc<RefCell<VecDeque<NativeWindowCommand>>>,
115 pub(crate) primary_window: WindowId,
116 pub(crate) last_window_policy: LastWindowPolicy,
117 pub(crate) should_exit: bool,
118 pub(crate) close_request: Option<Box<dyn FnMut(WindowId) -> bool>>,
119 pub(crate) restoration: Option<restoration::RestorationManager>,
120 pub(crate) restoration_window_factories:
121 HashMap<String, (WindowOptions, RestorableWindowFactory)>,
122 pub(crate) profiler: PerformanceProfiler,
123 pub(crate) hub: PerformanceHub,
124 pub(crate) scheduler_counters: ApplicationSchedulerCounters,
125}
126impl Drop for Application {
127 fn drop(&mut self) {
128 self.application_shell.stop();
129 }
130}
131impl Application {
132 pub fn new(
139 build: impl FnMut(&mut BuildContext) -> Widget + 'static,
140 ) -> Result<Self, TreeError> {
141 Self::new_with_options(WindowOptions::default(), build).map_err(|error| match error {
142 WindowError::Tree(error) => error,
143 WindowError::Options(_)
144 | WindowError::Restoration(_)
145 | WindowError::ApplicationStopped => {
146 unreachable!("default application options are valid while constructing")
147 }
148 })
149 }
150
151 pub fn new_with_options(
153 options: WindowOptions,
154 build: impl FnMut(&mut BuildContext) -> Widget + 'static,
155 ) -> Result<Self, WindowError> {
156 Self::new_inner(options, None, None, build)
157 }
158
159 pub fn new_with_restoration(
164 options: WindowOptions,
165 restoration: RestorationConfig,
166 build: impl FnMut(&mut BuildContext) -> Widget + 'static,
167 ) -> Result<Self, WindowError> {
168 Self::new_inner(options, Some(restoration), None, build)
169 }
170
171 pub fn new_restorable(
176 restoration_id: WindowRestorationId,
177 kind: impl Into<String>,
178 options: WindowOptions,
179 restoration: RestorationConfig,
180 build: impl FnMut(&mut BuildContext) -> Widget + 'static,
181 ) -> Result<Self, WindowError> {
182 Self::new_inner(
183 options,
184 Some(restoration),
185 Some(RestorableWindowMetadata::new(restoration_id, kind)?),
186 build,
187 )
188 }
189
190 fn new_inner(
191 options: WindowOptions,
192 restoration_config: Option<RestorationConfig>,
193 primary_restoration: Option<RestorableWindowMetadata>,
194 build: impl FnMut(&mut BuildContext) -> Widget + 'static,
195 ) -> Result<Self, WindowError> {
196 Self::with_primary(tasks::TaskScheduler::new(), restoration_config, |manager| {
197 manager
198 .open_window_with_inner(options, build, primary_restoration)
199 .map(|handle| handle.id())
200 })
201 }
202
203 pub fn from_runtime(
208 mut runtime: Runtime,
209 on_action: impl FnMut(incular_widgets::internal::ActionId) + 'static,
210 ) -> Self {
211 runtime.legacy_action_handler = Some(Box::new(on_action));
212 Self::with_primary(runtime.scheduler.clone(), None, |manager| {
213 Ok(manager.adopt_runtime(runtime))
214 })
215 .expect("adopting a mounted runtime with default window options is infallible")
216 }
217
218 fn with_primary(
219 scheduler: Rc<RefCell<tasks::TaskScheduler>>,
220 restoration_config: Option<RestorationConfig>,
221 create: impl FnOnce(&WindowManager) -> Result<WindowId, WindowError>,
222 ) -> Result<Self, WindowError> {
223 let restoration = restoration_config.map(restoration::RestorationManager::load);
224 if let Some(restoration) = &restoration {
225 restoration.attach_scheduler(&scheduler);
226 }
227 let registry = Rc::new(RefCell::new(WindowRegistry::default()));
228 let native_commands = Rc::new(RefCell::new(VecDeque::new()));
229 let (sender, command_receiver) = mpsc::channel();
230 let (cancellation_sender, request_cancellation_receiver) = mpsc::channel();
231 let (file_dialog_sender, file_dialog_receiver) = mpsc::channel();
232 let (file_dialog_cancellation_sender, file_dialog_cancellation_receiver) = mpsc::channel();
233 let file_dialog_bridge = Arc::new(FileDialogBridge::new(
234 file_dialog_sender,
235 file_dialog_cancellation_sender,
236 ));
237 let (global_shortcut_sender, global_shortcut_receiver) = mpsc::channel();
238 let global_shortcut_bridge = Arc::new(GlobalShortcutBridge::new(global_shortcut_sender));
239 let (simulation_sender, simulation_receiver) = mpsc::channel();
240 let simulation_bridge = Arc::new(simulation::SimulationBridge::new(simulation_sender));
241 let platform_capabilities = Arc::new(RwLock::new(PlatformCapabilities::default()));
242 let application_shell = ApplicationShellService::new(platform_capabilities.clone());
243 let activations = ApplicationActivationService::default();
244 let display_catalog = Arc::new(RwLock::new(DisplayCatalog::default()));
245 let application_lifecycle = Rc::new(std::cell::Cell::new(
246 crate::application_types::ApplicationLifecycle::Starting,
247 ));
248 let bridge = Arc::new(WindowCommandBridge::new(sender, cancellation_sender));
249 let manager = WindowManager {
250 registry: Rc::downgrade(®istry),
251 scheduler: scheduler.clone(),
252 bridge,
253 file_dialog_bridge: file_dialog_bridge.clone(),
254 global_shortcut_bridge: global_shortcut_bridge.clone(),
255 application_shell: application_shell.clone(),
256 native_commands: native_commands.clone(),
257 restoration: restoration.clone(),
258 application_capabilities: platform_capabilities.clone(),
259 displays: display_catalog.clone(),
260 application_lifecycle,
261 };
262 let primary_window = create(&manager)?;
263 Ok(Self {
264 scheduler,
265 registry,
266 manager,
267 command_receiver,
268 request_cancellation_receiver,
269 pending_native_requests: RequestRegistry::new(),
270 file_dialog_receiver,
271 file_dialog_cancellation_receiver,
272 file_dialog_bridge,
273 pending_file_dialog_requests: RequestRegistry::new(),
274 active_file_dialog_requests: HashMap::new(),
275 queued_file_dialog_requests: HashMap::new(),
276 native_file_dialog_requests: VecDeque::new(),
277 global_shortcut_receiver,
278 global_shortcut_bridge,
279 pending_global_shortcut_requests: RequestRegistry::new(),
280 native_global_shortcut_requests: VecDeque::new(),
281 platform_capabilities,
282 application_shell,
283 activations,
284 launch_activation: LaunchActivation::current_process(),
285 single_instance_policy: None,
286 display_catalog,
287 simulation_receiver,
288 simulation_bridge,
289 simulation_waiters: simulation::SimulationWaiters::default(),
290 native_commands,
291 primary_window,
292 last_window_policy: LastWindowPolicy::ExitOnLastWindow,
293 should_exit: false,
294 close_request: None,
295 restoration,
296 restoration_window_factories: HashMap::new(),
297 profiler: PerformanceProfiler::new(ProfilerMode::Normal),
298 hub: PerformanceHub::new(),
299 scheduler_counters: ApplicationSchedulerCounters::default(),
300 })
301 }
302
303 pub fn open_window(
306 &mut self,
307 options: WindowOptions,
308 root: Widget,
309 ) -> Result<WindowHandle, WindowError> {
310 self.manager.open_window(options, root)
311 }
312
313 pub fn open_window_with(
316 &mut self,
317 options: WindowOptions,
318 build: impl FnMut(&mut BuildContext) -> Widget + 'static,
319 ) -> Result<WindowHandle, WindowError> {
320 self.manager.open_window_with(options, build)
321 }
322
323 pub fn open_restorable_window_with(
328 &mut self,
329 restoration_id: WindowRestorationId,
330 kind: impl Into<String>,
331 options: WindowOptions,
332 build: impl FnMut(&mut BuildContext) -> Widget + 'static,
333 ) -> Result<WindowHandle, WindowError> {
334 self.manager
335 .open_restorable_window_with(restoration_id, kind, options, build)
336 }
337
338 pub fn register_restorable_window_factory(
341 &mut self,
342 kind: impl Into<String>,
343 options: WindowOptions,
344 factory: impl Fn(&mut BuildContext) -> Widget + 'static,
345 ) -> Result<(), WindowError> {
346 let kind = kind.into();
347 if kind.trim().is_empty() || kind.chars().any(char::is_control) {
348 return Err(WindowError::Restoration(
349 "window factory kind must be a non-empty printable stable identifier".to_owned(),
350 ));
351 }
352 let factory: RestorableWindowFactory = Rc::new(move |context: &mut BuildContext| {
353 incular_core::hot_reload::call_with(&mut &factory, context)
354 });
355 self.restoration_window_factories
356 .insert(kind, (options, factory));
357 Ok(())
358 }
359
360 pub fn restore_restorable_windows(&mut self) -> Result<usize, WindowError> {
367 let Some(restoration) = self.restoration.clone() else {
368 return Ok(0);
369 };
370 let mut active = self
371 .registry
372 .borrow()
373 .slots
374 .iter()
375 .filter_map(|slot| slot.record.as_ref())
376 .filter_map(|record| record.restoration.as_ref())
377 .map(|metadata| metadata.id.as_key().as_str().to_owned())
378 .collect::<HashSet<_>>();
379 let descriptors = restoration.windows();
380 let mut restored = 0;
381 for descriptor in descriptors {
382 if !active.insert(descriptor.restoration_id.clone()) {
383 continue;
384 }
385 let restoration_id = match WindowRestorationId::new(descriptor.restoration_id) {
386 Ok(id) => id,
387 Err(_) => {
388 restoration.note_skipped_window();
389 continue;
390 }
391 };
392 let Some((options, factory)) = self
393 .restoration_window_factories
394 .get(&descriptor.kind)
395 .cloned()
396 else {
397 restoration.note_skipped_window();
398 continue;
399 };
400 let kind = descriptor.kind;
401 let factory = factory.clone();
402 self.manager
403 .open_restorable_window_with(restoration_id, kind, options, move |cx| {
404 factory(cx)
405 })?;
406 restored += 1;
407 }
408 Ok(restored)
409 }
410
411 #[must_use]
412 pub const fn primary_window(&self) -> WindowId {
413 self.primary_window
414 }
415
416 #[must_use]
417 pub fn active_window_ids(&self) -> Vec<WindowId> {
418 self.registry.borrow().ids()
419 }
420
421 #[must_use]
423 pub fn window_handle(&self, window_id: WindowId) -> Option<WindowHandle> {
424 self.manager.handle(window_id)
425 }
426
427 #[doc(hidden)]
431 #[must_use]
432 pub fn window_mouse_cursor(&self, window_id: WindowId) -> Option<MouseCursor> {
433 self.registry
434 .borrow()
435 .get(window_id)
436 .map(|record| record.runtime.effective_mouse_cursor())
437 }
438
439 #[doc(hidden)]
442 #[must_use]
443 pub fn platform_menu_bindings(&self, window_id: WindowId) -> Vec<PlatformMenuBinding> {
444 self.registry
445 .borrow()
446 .get(window_id)
447 .map_or_else(Vec::new, |record| record.runtime.platform_menu_bindings())
448 }
449
450 #[must_use]
453 pub fn platform_capabilities(&self) -> PlatformCapabilities {
454 *self
455 .platform_capabilities
456 .read()
457 .expect("application capability snapshot lock")
458 }
459
460 #[must_use]
464 pub fn activations(&self) -> ApplicationActivationService {
465 self.activations.clone()
466 }
467
468 #[must_use]
470 pub fn global_shortcuts(&self) -> GlobalShortcutService {
471 GlobalShortcutService::new(
472 self.global_shortcut_bridge.clone(),
473 self.platform_capabilities.clone(),
474 )
475 }
476
477 #[must_use]
481 pub fn application_shell(&self) -> ApplicationShellService {
482 self.application_shell.clone()
483 }
484
485 #[doc(hidden)]
486 pub fn take_native_application_shell_requests(&self) -> Vec<NativeApplicationShellRequest> {
487 self.application_shell.take_native_requests()
488 }
489
490 #[doc(hidden)]
491 pub fn complete_application_shell_request(&self, completion: NativeApplicationShellCompletion) {
492 self.application_shell.complete(completion);
493 }
494
495 #[doc(hidden)]
496 pub fn handle_application_shell_event(&self, event: NativeApplicationShellEvent) -> bool {
497 self.application_shell.handle_native_event(event)
498 }
499
500 pub fn set_launch_activation(&mut self, activation: LaunchActivation) {
504 self.launch_activation = activation;
505 }
506
507 #[doc(hidden)]
508 #[must_use]
509 pub fn launch_activation(&self) -> LaunchActivation {
510 self.launch_activation.clone()
511 }
512
513 pub fn set_single_instance_policy(&mut self, policy: Option<SingleInstancePolicy>) {
516 self.single_instance_policy = policy;
517 }
518
519 #[must_use]
520 pub fn single_instance_policy(&self) -> Option<&SingleInstancePolicy> {
521 self.single_instance_policy.as_ref()
522 }
523
524 #[must_use]
530 pub fn bridge_activation_routes(
531 &self,
532 provider: Rc<dyn RouteInformationProvider>,
533 map: impl Fn(&str) -> Option<RouteInformation> + 'static,
534 ) -> ActivationRouteBridge {
535 ActivationRouteBridge::new(&self.activations, provider, map)
536 }
537
538 #[doc(hidden)]
542 pub fn handle_application_activation(&mut self, activation: ApplicationActivation) {
543 self.activations.publish(activation);
544 }
545
546 #[doc(hidden)]
557 pub fn publish_displays(
558 &mut self,
559 displays: impl IntoIterator<Item = DisplaySnapshot>,
560 primary: Option<DisplayId>,
561 ) {
562 self.display_catalog
563 .write()
564 .expect("display catalog lock")
565 .replace(displays, primary);
566 }
567
568 #[must_use]
569 pub fn displays(&self) -> Vec<DisplaySnapshot> {
570 self.display_catalog
571 .read()
572 .expect("display catalog lock")
573 .all()
574 }
575
576 #[must_use]
577 pub fn display(&self, id: DisplayId) -> Option<DisplaySnapshot> {
578 self.display_catalog
579 .read()
580 .expect("display catalog lock")
581 .get(id)
582 }
583
584 #[must_use]
585 pub fn primary_display(&self) -> Option<DisplaySnapshot> {
586 self.display_catalog
587 .read()
588 .expect("display catalog lock")
589 .primary()
590 }
591
592 #[must_use]
593 pub fn current_display(&self, window_id: WindowId) -> Option<DisplaySnapshot> {
594 let display_id = self
595 .window_handle(window_id)?
596 .observed_state()
597 .current_display?;
598 self.display(display_id)
599 }
600
601 pub fn set_platform_capabilities(&mut self, capabilities: PlatformCapabilities) {
605 *self
606 .platform_capabilities
607 .write()
608 .expect("application capability snapshot lock") = capabilities;
609 for slot in &self.registry.borrow().slots {
610 if let Some(record) = slot.record.as_ref() {
611 *record
612 .capabilities
613 .write()
614 .expect("window capability snapshot lock") = capabilities;
615 }
616 }
617 }
618
619 #[must_use]
621 pub fn window_capabilities(&self, window_id: WindowId) -> Option<PlatformCapabilities> {
622 let registry = self.registry.borrow();
623 let record = registry.get(window_id)?;
624 Some(
625 *record
626 .capabilities
627 .read()
628 .expect("window capability snapshot lock"),
629 )
630 }
631
632 pub fn set_window_capabilities(
635 &mut self,
636 window_id: WindowId,
637 capabilities: PlatformCapabilities,
638 ) -> bool {
639 let registry = self.registry.borrow();
640 let Some(record) = registry.get(window_id) else {
641 return false;
642 };
643 *record
644 .capabilities
645 .write()
646 .expect("window capability snapshot lock") = capabilities;
647 true
648 }
649
650 pub fn set_transient_presentations(
654 &mut self,
655 window_id: WindowId,
656 presentations: Vec<TransientPresentationResolution>,
657 ) -> bool {
658 self.with_window_mut(window_id, |record| {
659 let native = presentations
660 .iter()
661 .filter_map(|presentation| {
662 (presentation.resolved == ResolvedTransientPresentation::Native)
663 .then_some(presentation.id)
664 })
665 .collect::<Vec<_>>();
666 record.runtime.set_native_transient_presentations(native);
667 *record
668 .transient_presentations
669 .write()
670 .expect("transient presentation snapshot lock") = presentations;
671 })
672 .is_some()
673 }
674
675 pub fn set_native_transient_bounds(
678 &mut self,
679 window_id: WindowId,
680 bounds: Option<incular_core::Rect>,
681 ) -> bool {
682 self.with_window_mut(window_id, |record| {
683 record.runtime.set_native_transient_bounds(bounds)
684 })
685 .unwrap_or(false)
686 }
687
688 #[cfg(feature = "devtools")]
692 #[must_use]
693 pub fn devtools_windows(&self) -> Vec<incular_devtools_protocol::WindowSummary> {
694 self.active_window_ids()
695 .into_iter()
696 .filter_map(|id| {
697 self.window_diagnostics(id)
698 .map(|window| incular_devtools_protocol::WindowSummary {
699 id: incular_devtools_protocol::DevWindowId::new(
700 u64::from(id.index()) + 1,
701 u64::from(id.generation()),
702 ),
703 title: window.title,
704 logical_size: [window.logical_size.width, window.logical_size.height],
705 scale_factor: window.scale_factor,
706 })
707 })
708 .collect()
709 }
710
711 #[cfg(feature = "devtools")]
715 pub fn devtools_widget_tree(
716 &self,
717 window: incular_devtools_protocol::DevWindowId,
718 ) -> Result<Vec<incular_devtools_protocol::TreeDelta>, incular_devtools_protocol::ErrorCode>
719 {
720 let id = WindowId::from_parts(
721 window.index().saturating_sub(1) as u32,
722 window.generation() as u32,
723 );
724 let registry = self.registry.borrow();
725 let record = registry
726 .get(id)
727 .ok_or(incular_devtools_protocol::ErrorCode::StaleId)?;
728 let root = record
729 .runtime
730 .tree()
731 .root()
732 .ok_or(incular_devtools_protocol::ErrorCode::UnknownId)?;
733 let (mut nodes, truncated) = record.runtime.tree().devtools_snapshot(root, false);
734 let root = nodes
735 .first()
736 .cloned()
737 .ok_or(incular_devtools_protocol::ErrorCode::UnknownId)?;
738 nodes.remove(0);
739 Ok(vec![incular_devtools_protocol::TreeDelta::Snapshot {
740 window,
741 root: Box::new(root),
742 nodes,
743 truncated,
744 }])
745 }
746
747 #[cfg(feature = "devtools")]
750 pub fn devtools_node_details(
751 &self,
752 id: incular_devtools_protocol::DevWidgetId,
753 ) -> Result<incular_devtools_protocol::NodeDetails, incular_devtools_protocol::ErrorCode> {
754 for window in self.active_window_ids() {
755 let registry = self.registry.borrow();
756 let Some(record) = registry.get(window) else {
757 continue;
758 };
759 let tree = record.runtime.tree();
760 if let Some(element) = tree.devtools_resolve_id(id) {
761 let mut details = tree
762 .devtools_node_details(
763 element,
764 id,
765 incular_devtools_protocol::DevWindowId::new(
766 u64::from(window.index()) + 1,
767 u64::from(window.generation()),
768 ),
769 )
770 .ok_or(incular_devtools_protocol::ErrorCode::StaleId)?;
771 let root = record.runtime.devtools_reactive_root();
772 details.consumed_signals = crate::devtools_registry::with_all(|signals| {
773 signals
774 .iter()
775 .filter(|(_, registration)| {
776 (registration.subscribers)().into_iter().any(
777 |(subscriber_root, subscriber)| {
778 subscriber_root == root && subscriber == element
779 },
780 )
781 })
782 .map(|(signal_id, _)| {
783 incular_devtools_protocol::DevSignalId::new(*signal_id, 1)
784 })
785 .collect()
786 });
787 return Ok(details);
788 }
789 }
790 Err(incular_devtools_protocol::ErrorCode::StaleId)
791 }
792
793 #[cfg(feature = "devtools")]
796 pub fn devtools_edit_property(
797 &mut self,
798 id: incular_devtools_protocol::DevWidgetId,
799 name: &str,
800 value: &incular_devtools_protocol::DebugValue,
801 ) -> bool {
802 let mut registry = self.registry.borrow_mut();
803 for slot in &mut registry.slots {
804 let Some(record) = slot.record.as_mut() else {
805 continue;
806 };
807 if record.runtime.devtools_edit_property(id, name, value) {
808 return true;
809 }
810 }
811 false
812 }
813
814 #[cfg(feature = "devtools")]
816 pub fn devtools_node_bounds(
817 &self,
818 id: incular_devtools_protocol::DevWidgetId,
819 ) -> Option<[f32; 4]> {
820 for window in self.active_window_ids() {
821 let registry = self.registry.borrow();
822 let Some(record) = registry.get(window) else {
823 continue;
824 };
825 let tree = record.runtime.tree();
826 if let Some(element) = tree.devtools_resolve_id(id) {
827 return tree.element_bounds(element).map(|bounds| {
828 [
829 bounds.origin.x,
830 bounds.origin.y,
831 bounds.size.width,
832 bounds.size.height,
833 ]
834 });
835 }
836 }
837 None
838 }
839
840 #[cfg(feature = "devtools")]
842 pub fn devtools_node_overlay_geometry(
843 &self,
844 id: incular_devtools_protocol::DevWidgetId,
845 ) -> Option<incular_widgets::devtools::DevOverlayGeometry> {
846 for window in self.active_window_ids() {
847 let registry = self.registry.borrow();
848 let Some(record) = registry.get(window) else {
849 continue;
850 };
851 let tree = record.runtime.tree();
852 if let Some(element) = tree.devtools_resolve_id(id) {
853 return tree.devtools_overlay_geometry(element);
854 }
855 }
856 None
857 }
858
859 #[cfg(feature = "devtools")]
863 pub fn devtools_window_layout_bounds(&self, window: WindowId, limit: usize) -> Vec<[f32; 4]> {
864 self.registry
865 .borrow()
866 .get(window)
867 .map(|record| record.runtime.tree().devtools_layout_bounds(limit))
868 .unwrap_or_default()
869 }
870
871 #[cfg(feature = "devtools")]
872 pub fn devtools_window_semantics_bounds(
873 &self,
874 window: WindowId,
875 limit: usize,
876 ) -> Vec<[f32; 4]> {
877 self.registry
878 .borrow()
879 .get(window)
880 .map(|record| record.runtime.tree().devtools_semantics_bounds(limit))
881 .unwrap_or_default()
882 }
883
884 #[cfg(feature = "devtools")]
885 pub fn devtools_window_hit_regions(&self, window: WindowId, limit: usize) -> Vec<[f32; 4]> {
886 self.registry
887 .borrow()
888 .get(window)
889 .map(|record| record.runtime.tree().devtools_hit_regions(limit))
890 .unwrap_or_default()
891 }
892
893 #[cfg(feature = "devtools")]
894 pub fn devtools_window_scroll_viewports(
895 &self,
896 window: WindowId,
897 limit: usize,
898 ) -> Vec<[f32; 4]> {
899 self.registry
900 .borrow()
901 .get(window)
902 .map(|record| record.runtime.tree().devtools_scroll_viewports(limit))
903 .unwrap_or_default()
904 }
905
906 #[cfg(feature = "devtools")]
907 pub fn devtools_window_layer_bounds(&self, window: WindowId, limit: usize) -> Vec<[f32; 4]> {
908 self.registry
909 .borrow()
910 .get(window)
911 .map(|record| record.runtime.tree().devtools_layer_bounds(limit))
912 .unwrap_or_default()
913 }
914
915 #[cfg(feature = "devtools")]
917 pub fn devtools_window_phase_nodes(
918 &self,
919 window: WindowId,
920 limit: usize,
921 ) -> Vec<incular_widgets::devtools::DevPhaseNode> {
922 self.registry
923 .borrow()
924 .get(window)
925 .map(|record| record.runtime.tree().devtools_phase_nodes(limit))
926 .unwrap_or_default()
927 }
928
929 #[cfg(feature = "devtools")]
933 pub fn devtools_begin_deep_trace(&mut self, window: WindowId, max_events: usize) -> bool {
934 self.with_window_mut(window, |record| {
935 record.runtime.tree_mut().begin_deep_trace(max_events);
936 })
937 .is_some()
938 }
939
940 #[cfg(feature = "devtools")]
942 pub fn devtools_take_deep_trace(
943 &mut self,
944 window: WindowId,
945 frame: u64,
946 ) -> Option<incular_devtools_protocol::DeepFrameTrace> {
947 let (events, dropped_events) = self
948 .with_window_mut(window, |record| record.runtime.tree_mut().take_deep_trace())
949 .flatten()?;
950 Some(incular_devtools_protocol::DeepFrameTrace {
951 window: incular_devtools_protocol::DevWindowId::new(
952 u64::from(window.index()) + 1,
953 u64::from(window.generation()),
954 ),
955 frame,
956 truncated: dropped_events > 0,
957 dropped_events,
958 events,
959 })
960 }
961
962 #[cfg(feature = "devtools")]
965 pub fn devtools_subtree_layout_bounds(
966 &self,
967 id: incular_devtools_protocol::DevWidgetId,
968 limit: usize,
969 ) -> Option<(WindowId, Vec<[f32; 4]>)> {
970 for window in self.active_window_ids() {
971 let registry = self.registry.borrow();
972 let Some(record) = registry.get(window) else {
973 continue;
974 };
975 let tree = record.runtime.tree();
976 if let Some(element) = tree.devtools_resolve_id(id) {
977 return Some((window, tree.devtools_subtree_layout_bounds(element, limit)));
978 }
979 }
980 None
981 }
982
983 #[cfg(feature = "devtools")]
986 pub fn devtools_hit_test(
987 &self,
988 window: WindowId,
989 point: incular_core::Offset,
990 ) -> Option<(incular_devtools_protocol::DevWidgetId, [f32; 4])> {
991 let registry = self.registry.borrow();
992 let record = registry.get(window)?;
993 let tree = record.runtime.tree();
994 let element = tree.devtools_deepest_at(point)?;
995 let id = tree.devtools_id_for_element(element)?;
996 let bounds = tree.element_bounds(element)?;
997 Some((
998 id,
999 [
1000 bounds.origin.x,
1001 bounds.origin.y,
1002 bounds.size.width,
1003 bounds.size.height,
1004 ],
1005 ))
1006 }
1007
1008 #[cfg(feature = "devtools")]
1012 #[must_use]
1013 pub fn devtools_resource_counts(&self) -> incular_devtools_protocol::ResourceCounts {
1014 let mut counts = incular_devtools_protocol::ResourceCounts::default();
1015 for window in self.active_window_ids() {
1016 let registry = self.registry.borrow();
1017 let Some(record) = registry.get(window) else {
1018 continue;
1019 };
1020 let tree = record.runtime.tree();
1021 counts.elements += tree.element_count();
1022 counts.render_objects += tree.render_object_count();
1023 counts.semantics_nodes += tree.semantics().len();
1024 counts.tasks_active += record.runtime.runtime_diagnostics().active_tracked_tasks;
1025 }
1026 crate::devtools_registry::with_all(|signals| counts.signals = signals.len());
1027 counts
1028 }
1029
1030 #[cfg(feature = "devtools")]
1033 #[must_use]
1034 pub fn devtools_signals(&self) -> Vec<incular_devtools_protocol::SignalSummary> {
1035 crate::devtools_registry::with_all(|signals| {
1036 signals
1037 .iter()
1038 .map(|(id, registration)| {
1039 let (_, last_write) = (registration.last_write)();
1040 let writes = (registration.write_count)();
1041 incular_devtools_protocol::SignalSummary {
1042 id: incular_devtools_protocol::DevSignalId::new(*id, 1),
1043 name: registration.name.clone(),
1044 type_name: registration.type_name.to_owned(),
1045 generation: writes,
1046 write_count: writes,
1047 subscriber_count: (registration.subscriber_count)(),
1048 last_write_summary: last_write,
1049 editable: registration.editable_kind.is_some(),
1050 }
1051 })
1052 .collect()
1053 })
1054 }
1055
1056 #[cfg(feature = "devtools")]
1061 #[must_use]
1062 pub fn devtools_signal_subscribers(
1063 &self,
1064 signal: incular_devtools_protocol::DevSignalId,
1065 ) -> Vec<incular_devtools_protocol::SignalSubscriber> {
1066 let entries = crate::devtools_registry::with_all(|signals| {
1067 signals
1068 .iter()
1069 .find(|(id, _)| *id == signal.index())
1070 .and_then(|(_, registration)| {
1071 (signal.generation() == 1).then(|| (registration.subscribers)())
1072 })
1073 .unwrap_or_default()
1074 });
1075 if entries.is_empty() {
1076 return Vec::new();
1077 }
1078
1079 let windows = self.active_window_ids();
1080 let registry = self.registry.borrow();
1081 windows
1082 .into_iter()
1083 .flat_map(|window| {
1084 let Some(record) = registry.get(window) else {
1085 return Vec::new();
1086 };
1087 let root = record.runtime.devtools_reactive_root();
1088 let tree = record.runtime.tree();
1089 entries
1090 .iter()
1091 .filter(|(entry_root, _)| *entry_root == root)
1092 .filter_map(|(_, element)| {
1093 tree.devtools_id_for_element(*element).map(|dev_id| {
1094 incular_devtools_protocol::SignalSubscriber {
1095 signal,
1096 element: dev_id,
1097 path: tree.devtools_element_path(*element),
1098 }
1099 })
1100 })
1101 .collect::<Vec<_>>()
1102 })
1103 .collect()
1104 }
1105
1106 #[cfg(feature = "devtools")]
1110 pub fn devtools_edit_signal(
1111 &self,
1112 signal: incular_devtools_protocol::DevSignalId,
1113 value: &incular_devtools_protocol::EditableValue,
1114 ) -> bool {
1115 if signal.generation() != 1 {
1116 return false;
1117 }
1118 crate::devtools_registry::with_all(|signals| {
1119 signals
1120 .iter()
1121 .find(|(id, registration)| {
1122 *id == signal.index() && registration.editable_kind.is_some()
1123 })
1124 .is_some_and(|(_, registration)| (registration.apply_edit)(value))
1125 })
1126 }
1127
1128 #[must_use]
1129 pub fn contains_window(&self, window_id: WindowId) -> bool {
1130 self.registry.borrow().contains(window_id)
1131 }
1132
1133 #[must_use]
1138 pub fn transient_surfaces(
1139 &self,
1140 window_id: WindowId,
1141 ) -> Vec<incular_widgets::TransientSurfaceSnapshot> {
1142 self.registry
1143 .borrow()
1144 .get(window_id)
1145 .map_or_else(Vec::new, |record| record.runtime.transient_surfaces())
1146 }
1147
1148 pub fn set_last_window_policy(&mut self, policy: LastWindowPolicy) {
1149 self.last_window_policy = policy;
1150 }
1151
1152 #[must_use]
1153 pub const fn last_window_policy(&self) -> LastWindowPolicy {
1154 self.last_window_policy
1155 }
1156
1157 pub fn on_close_request(&mut self, callback: impl FnMut(WindowId) -> bool + 'static) {
1161 self.close_request = Some(Box::new(callback));
1162 }
1163
1164 pub fn set_wake_handler(&mut self, wake: Arc<dyn RuntimeWake>) {
1165 self.scheduler.borrow_mut().set_wake(wake.clone());
1166 self.manager.bridge.set_wake(wake.clone());
1167 self.file_dialog_bridge.set_wake(wake.clone());
1168 self.global_shortcut_bridge.set_wake(wake.clone());
1169 self.application_shell.set_wake(wake.clone());
1170 self.simulation_bridge.set_wake(wake);
1171 }
1172
1173 #[must_use]
1177 pub fn simulation(&self) -> Simulation {
1178 Simulation::new(self.simulation_bridge.clone(), self.primary_window)
1179 }
1180
1181 pub(crate) fn with_window_mut<R>(
1182 &mut self,
1183 window_id: WindowId,
1184 callback: impl FnOnce(&mut WindowRecord) -> R,
1185 ) -> Option<R> {
1186 let mut record = self.registry.borrow_mut().take(window_id)?;
1187 let result = callback(&mut record);
1188 self.registry.borrow_mut().restore(window_id, record);
1192 Some(result)
1193 }
1194 pub fn spawn<F, T>(&self, future: F) -> Task<T>
1197 where
1198 F: std::future::Future<Output = T> + Send + 'static,
1199 T: Send + 'static,
1200 {
1201 tasks::TaskScheduler::spawner(&self.scheduler).spawn(future)
1202 }
1203
1204 pub fn spawn_blocking<T>(
1205 &self,
1206 work: impl FnOnce() -> T + Send + 'static,
1207 complete: impl FnOnce(Result<T, TaskFailure>, &mut Runtime) + 'static,
1208 ) -> TaskHandle
1209 where
1210 T: Send + 'static,
1211 {
1212 self.scheduler.borrow_mut().spawn_blocking(work, complete)
1213 }
1214
1215 pub fn dispatch(&self, callback: impl FnOnce(&mut Runtime) + Send + 'static) {
1218 self.scheduler
1219 .borrow()
1220 .dispatcher_for(Some(self.primary_window))
1221 .dispatch(callback);
1222 }
1223
1224 pub fn spawn_into<F, T>(
1225 &self,
1226 future: F,
1227 complete: impl FnOnce(Result<T, TaskFailure>, &mut Runtime) + 'static,
1228 ) -> TaskHandle
1229 where
1230 F: std::future::Future<Output = T> + Send + 'static,
1231 T: Send + 'static,
1232 {
1233 tasks::TaskScheduler::spawner(&self.scheduler).spawn_into(future, complete)
1234 }
1235
1236 #[must_use]
1237 pub fn tokio_handle(&self) -> TokioHandle {
1238 self.scheduler.borrow().tokio_handle()
1239 }
1240
1241 pub fn process_runtime_work_at(&mut self, _now: Instant) {
1244 let work = self.scheduler.borrow_mut().take_turn();
1245 for item in work {
1246 let requested_window = match &item {
1247 tasks::UiWork::Dispatch { window_id, .. } => *window_id,
1248 tasks::UiWork::Task(pending) => pending.control.window_id(),
1249 };
1250 let window_id = requested_window.or_else(|| {
1255 if self.contains_window(self.primary_window) {
1256 Some(self.primary_window)
1257 } else {
1258 self.active_window_ids().into_iter().next()
1259 }
1260 });
1261 let Some(window_id) = window_id else {
1262 self.discard_stale_ui_work(item);
1263 continue;
1264 };
1265 let record = self.registry.borrow_mut().take(window_id);
1266 if let Some(mut record) = record {
1267 record.runtime.process_ui_work(&self.scheduler, item);
1268 self.registry.borrow_mut().restore(window_id, record);
1269 } else {
1270 self.discard_stale_ui_work(item);
1271 }
1272 }
1273 self.drain_window_commands();
1274 self.drain_native_request_cancellations();
1275 self.drain_file_dialog_requests();
1276 self.drain_file_dialog_cancellations();
1277 self.drain_global_shortcut_requests();
1278 self.process_simulation_requests();
1279 }
1280
1281 pub fn process_runtime_work(&mut self) {
1282 self.process_runtime_work_at(Instant::now());
1283 }
1284
1285 fn discard_stale_ui_work(&mut self, work: tasks::UiWork) {
1286 if let tasks::UiWork::Task(pending) = work {
1287 self.scheduler.borrow().detach_scope(&pending.control);
1288 (pending.discard)();
1289 self.scheduler
1290 .borrow_mut()
1291 .record_discard(pending.blocking, true);
1292 }
1293 }
1294
1295 #[must_use]
1300 pub fn handle_external_drag_event(
1301 &mut self,
1302 window_id: WindowId,
1303 event: ExternalDragEvent,
1304 ) -> ExternalDragResponse {
1305 self.with_window_mut(window_id, |record| {
1306 record.input_events = record.input_events.wrapping_add(1);
1307 record.runtime.handle_external_drag(event)
1308 })
1309 .unwrap_or_default()
1310 }
1311
1312 pub fn handle_window_event(&mut self, event: WindowEvent) {
1314 let window_id = event.window_id;
1315 match event.kind {
1316 WindowEventKind::Platform(PlatformEvent::CloseRequested) => {
1317 self.request_close(window_id);
1318 }
1319 WindowEventKind::Platform(PlatformEvent::Metrics(metrics)) => {
1320 let _ = self.with_window_mut(window_id, |record| {
1321 if record.metrics != metrics {
1322 record.metrics = metrics;
1323 record.surface_generation = record
1324 .surface_generation
1325 .checked_add(1)
1326 .expect("runtime surface generation exhausted");
1327 record.runtime.update_window_metrics(metrics);
1328 }
1329 });
1330 self.manager.sync_restorable_windows(true);
1331 }
1332 WindowEventKind::Platform(PlatformEvent::Environment(environment)) => {
1333 let _ = self.with_window_mut(window_id, |record| {
1334 record.runtime.set_environment(environment);
1335 });
1336 }
1337 WindowEventKind::Platform(PlatformEvent::Input(input)) => {
1338 let _ = self.with_window_mut(window_id, |record| {
1339 record.input_events = record.input_events.wrapping_add(1);
1340 let _ = record.runtime.handle_input(input);
1341 });
1342 }
1343 WindowEventKind::Platform(PlatformEvent::TextInputAction(action)) => {
1344 let _ = self.with_window_mut(window_id, |record| {
1345 record.input_events = record.input_events.wrapping_add(1);
1346 let _ = record.runtime.handle_text_input_action(action);
1347 });
1348 }
1349 WindowEventKind::Platform(PlatformEvent::Lifecycle(lifecycle)) => {
1350 self.handle_application_lifecycle(lifecycle);
1351 }
1352 WindowEventKind::Platform(PlatformEvent::ExternalDrag(event)) => {
1353 let _ = self.handle_external_drag_event(window_id, event);
1354 }
1355 WindowEventKind::Lifecycle(lifecycle) => {
1356 let _ = self.with_window_mut(window_id, |record| {
1357 record.lifecycle = lifecycle;
1358 match lifecycle {
1359 WindowLifecycle::Focused => {
1360 record.native_focused = true;
1361 let mut environment = record.runtime.environment();
1362 environment.window_focused = true;
1363 record.runtime.set_environment(environment);
1364 }
1365 WindowLifecycle::Unfocused => {
1366 record.native_focused = false;
1367 record.runtime.dismiss_transients(
1368 incular_widgets::TransientDismissReason::ParentDeactivated,
1369 );
1370 let mut environment = record.runtime.environment();
1371 environment.window_focused = false;
1372 record.runtime.set_environment(environment);
1373 }
1374 WindowLifecycle::Visible
1375 | WindowLifecycle::Hidden
1376 | WindowLifecycle::Creating
1377 | WindowLifecycle::Closing
1378 | WindowLifecycle::Closed => {}
1379 }
1380 });
1381 }
1382 WindowEventKind::RedrawRequested => {}
1383 WindowEventKind::StateChanged(state) => {
1384 let _ = self.with_window_mut(window_id, |record| {
1385 *record
1386 .observed_state
1387 .write()
1388 .expect("window observed-state snapshot lock") = state;
1389 });
1390 }
1391 }
1392 }
1393
1394 pub fn handle_application_lifecycle(&mut self, lifecycle: PlatformLifecycle) {
1398 self.manager.application_lifecycle.set(match lifecycle {
1399 PlatformLifecycle::Active | PlatformLifecycle::Resumed => {
1400 crate::application_types::ApplicationLifecycle::Active
1401 }
1402 PlatformLifecycle::Inactive => crate::application_types::ApplicationLifecycle::Inactive,
1403 PlatformLifecycle::Suspended => {
1404 crate::application_types::ApplicationLifecycle::Suspended
1405 }
1406 PlatformLifecycle::Stopping => crate::application_types::ApplicationLifecycle::Stopping,
1407 });
1408 if matches!(lifecycle, PlatformLifecycle::Stopping) {
1409 self.shutdown();
1410 return;
1411 }
1412 let ids = self.active_window_ids();
1413 for id in ids {
1414 let _ = self.with_window_mut(id, |record| {
1415 let _ = record
1416 .runtime
1417 .handle_platform_event(PlatformEvent::Lifecycle(lifecycle));
1418 });
1419 }
1420 if matches!(lifecycle, PlatformLifecycle::Suspended) {
1421 self.flush_restoration_and_wait();
1422 }
1423 }
1424
1425 pub fn record_platform_operation_error(
1428 &mut self,
1429 window_id: WindowId,
1430 error: PlatformOperationError,
1431 ) -> bool {
1432 self.with_window_mut(window_id, |record| {
1433 record.last_platform_error = Some(error);
1434 })
1435 .is_some()
1436 }
1437
1438 pub fn set_window_environment(
1439 &mut self,
1440 window_id: WindowId,
1441 environment: RuntimeEnvironment,
1442 ) -> bool {
1443 self.with_window_mut(window_id, |record| {
1444 record.runtime.set_environment(environment)
1445 })
1446 .unwrap_or(false)
1447 }
1448
1449 pub fn set_window_clipboard(&mut self, window_id: WindowId, clipboard: Box<dyn Clipboard>) {
1453 let _ = self.with_window_mut(window_id, |record| {
1454 record.runtime.set_clipboard(clipboard);
1455 });
1456 }
1457
1458 pub fn take_window_text_input_commands(
1461 &mut self,
1462 window_id: WindowId,
1463 ) -> Vec<TextInputCommand> {
1464 self.with_window_mut(window_id, |record| {
1465 record.runtime.take_text_input_commands()
1466 })
1467 .unwrap_or_default()
1468 }
1469
1470 #[must_use]
1471 pub fn frame_requested(&self, window_id: WindowId) -> bool {
1472 self.registry
1473 .borrow()
1474 .slots
1475 .get(window_id.index() as usize)
1476 .filter(|slot| slot.generation == window_id.generation())
1477 .and_then(|slot| slot.record.as_ref())
1478 .is_some_and(|record| record.runtime.frame_requested())
1479 }
1480
1481 pub fn note_frame_requested(&mut self, window_id: WindowId) {
1482 scheduler_counters::REDRAW_REQUESTS.fetch_add(1, Ordering::Relaxed);
1483 self.scheduler_counters.redraw_requests =
1484 self.scheduler_counters.redraw_requests.wrapping_add(1);
1485 let _ = self.with_window_mut(window_id, |record| {
1486 record.requested_frames = record.requested_frames.wrapping_add(1);
1487 });
1488 }
1489
1490 pub fn note_runtime_wake(&mut self) {
1494 scheduler_counters::RUNTIME_WAKES.fetch_add(1, Ordering::Relaxed);
1495 self.scheduler_counters.runtime_wakes =
1496 self.scheduler_counters.runtime_wakes.wrapping_add(1);
1497 }
1498
1499 pub fn run_window_frame_at(
1500 &mut self,
1501 window_id: WindowId,
1502 constraints: Constraints,
1503 now: Instant,
1504 ) -> Result<Option<(DisplayList, FrameStats)>, TreeError> {
1505 let mut sensitivity_update = None;
1506 let mut content_size_request = None;
1507 let outcome = self
1508 .with_window_mut(window_id, |record| {
1509 if record.metrics.physical_size.is_zero() {
1510 record.skipped_frames = record.skipped_frames.wrapping_add(1);
1511 scheduler_counters::FRAMES_SKIPPED.fetch_add(1, Ordering::Relaxed);
1512 Ok(None)
1513 } else {
1514 let frame_constraints =
1515 ContentSizeCoordinator::layout_constraints(&record.options, constraints);
1516 record
1517 .runtime
1518 .run_frame_at(frame_constraints, now)
1519 .map(|(list, stats)| {
1520 let sensitivity = record.runtime.content_sensitivity();
1521 if record.last_content_sensitivity != Some(sensitivity) {
1522 record.last_content_sensitivity = Some(sensitivity);
1523 sensitivity_update = Some(sensitivity);
1524 }
1525 content_size_request = record.content_sizing.reconcile(
1526 &record.options,
1527 record.metrics,
1528 record.runtime.tree().root_layout_size(),
1529 );
1530 record.last_frame = FrameRecord {
1531 frame: 0,
1532 timings: stats.timings,
1533 work: FrameWork {
1534 updated_elements: stats.updated_elements as u64,
1535 rebuilt_elements: stats.rebuilt_elements,
1536 laid_out_render_objects: stats.laid_out_render_objects,
1537 repainted_render_objects: stats.repainted_render_objects,
1538 composited_layers: stats.composited,
1539 active_animations: stats.active_animations,
1540 display_list_commands: stats.display_list_commands,
1541 requested_another_frame: stats.requested_another_frame,
1542 },
1543 over_budget: false,
1544 };
1545 Some((list, stats))
1546 })
1547 }
1548 })
1549 .unwrap_or(Ok(None));
1550 if let Some(size) = content_size_request {
1551 self.native_commands
1552 .borrow_mut()
1553 .push_back(NativeWindowCommand::Operate(WindowCommand::new(
1554 window_id,
1555 WindowOperation::SetLogicalSize(size),
1556 )));
1557 }
1558 if let Some(sensitivity) = sensitivity_update {
1559 self.native_commands
1560 .borrow_mut()
1561 .push_back(NativeWindowCommand::Operate(WindowCommand::new(
1562 window_id,
1563 WindowOperation::SetContentSensitivity(sensitivity),
1564 )));
1565 }
1566 if matches!(outcome, Ok(None)) && self.contains_window(window_id) {
1567 self.scheduler_counters.frames_skipped =
1568 self.scheduler_counters.frames_skipped.wrapping_add(1);
1569 }
1570 if matches!(outcome, Ok(Some(_))) {
1571 scheduler_counters::FRAMES_STARTED.fetch_add(1, Ordering::Relaxed);
1572 self.scheduler_counters.frames_started =
1573 self.scheduler_counters.frames_started.wrapping_add(1);
1574 }
1575 outcome
1576 }
1577
1578 pub fn sync_accessibility(
1584 &mut self,
1585 window_id: WindowId,
1586 projection: &mut AccessKitProjection,
1587 ) -> Option<NativeAccessibilityUpdate> {
1588 let update = self
1589 .with_window_mut(window_id, |record| {
1590 projection.sync(
1591 record.runtime.tree().semantics(),
1592 record.metrics.scale_factor,
1593 )
1594 })
1595 .flatten();
1596 let diagnostics = projection.diagnostics();
1597 let _ = self.with_window_mut(window_id, |record| {
1598 record.accessibility = diagnostics;
1599 });
1600 update
1601 }
1602
1603 pub fn dispatch_accessibility_action(
1607 &mut self,
1608 window_id: WindowId,
1609 request: SemanticActionRequest,
1610 ) -> bool {
1611 self.with_window_mut(window_id, |record| {
1612 record
1613 .runtime
1614 .dispatch_semantic_action(request.node, request.action)
1615 })
1616 .unwrap_or(false)
1617 }
1618
1619 pub fn set_semantics_enabled(&mut self, window_id: WindowId, enabled: bool) {
1623 let _ = self.with_window_mut(window_id, |record| {
1624 let runtime = &mut record.runtime;
1625 if runtime.tree().semantics_enabled() != enabled {
1626 runtime.tree_mut().set_semantics_enabled(enabled);
1627 if enabled {
1628 runtime.request_frame();
1629 }
1630 }
1631 });
1632 }
1633
1634 pub fn set_accessibility_diagnostics(
1637 &mut self,
1638 window_id: WindowId,
1639 diagnostics: AccessibilityDiagnostics,
1640 ) {
1641 let _ = self.with_window_mut(window_id, |record| {
1642 record.accessibility = diagnostics;
1643 });
1644 }
1645
1646 pub fn set_profiler_mode(&mut self, mode: ProfilerMode) {
1649 self.profiler.set_mode(mode);
1650 }
1651
1652 pub fn set_animation_time_scale(&mut self, scale: f32) {
1655 for window in self.active_window_ids() {
1656 let _ = self.with_window_mut(window, |record| {
1657 record.runtime.set_animation_time_scale(scale);
1658 });
1659 }
1660 }
1661
1662 #[cfg(feature = "hot-reload")]
1668 pub fn reassemble(&mut self) {
1669 for window in self.active_window_ids() {
1670 let _ = self.with_window_mut(window, |record| record.runtime.reassemble());
1671 }
1672 }
1673
1674 #[cfg(feature = "hot-reload")]
1682 pub unsafe fn apply_hot_patch(
1683 &mut self,
1684 patch: crate::HotPatch,
1685 ) -> Result<(), crate::HotPatchError> {
1686 unsafe { patch.apply() }?;
1688 self.reassemble();
1689 Ok(())
1690 }
1691
1692 #[must_use]
1693 pub const fn profiler_mode(&self) -> ProfilerMode {
1694 self.profiler.mode()
1695 }
1696
1697 pub fn set_refresh_rate_hz(&mut self, hz: Option<f32>) {
1699 self.profiler.set_refresh_rate_hz(hz);
1700 }
1701
1702 pub fn note_render_metrics(
1705 &mut self,
1706 window_id: WindowId,
1707 mut render: RenderFrameMetrics,
1708 gpu: Option<GpuSample>,
1709 ) {
1710 let frame_id = self.profiler.next_frame_id();
1711 render.frame = frame_id;
1712 let mut completed = None;
1713 {
1714 let mut registry = self.registry.borrow_mut();
1715 if let Some(slot) = registry.slots.get_mut(window_id.index() as usize)
1716 && slot.generation == window_id.generation()
1717 && let Some(record) = slot.record.as_mut()
1718 {
1719 record.render_metrics = render;
1720 record.gpu_sample = gpu;
1721 record.last_frame.frame = frame_id;
1722 let mut timings = record.last_frame.timings;
1724 timings.cpu_total = timings
1725 .cpu_total
1726 .saturating_add(render.prepare_us)
1727 .saturating_add(render.encode_us)
1728 .saturating_add(render.submit_us);
1729 record.last_frame.timings = timings;
1730 completed = Some(record.last_frame);
1731 }
1732 }
1733 if let Some(record) = completed {
1734 self.profiler.record(record);
1735 }
1736 if self.hub.observed()
1739 && self.profiler.mode() >= ProfilerMode::Diagnostic
1740 && self.hub.publish_due(std::time::Duration::from_millis(200))
1741 {
1742 self.hub.publish(self.performance_snapshot());
1743 }
1744 }
1745
1746 #[must_use]
1749 pub const fn performance_hub(&self) -> &PerformanceHub {
1750 &self.hub
1751 }
1752
1753 pub fn install_performance_overlay(&mut self, window_id: WindowId) -> Result<(), TreeError> {
1761 let hub = self.hub.clone();
1762 let installed = self.with_window_mut(window_id, |record| {
1763 let tree = record.runtime.tree();
1764 let Some(target) = tree.element_with_key(&Key::from(PERFORMANCE_OVERLAY_KEY)) else {
1765 return Err(TreeError::MissingElement(
1766 tree.root().expect("mounted window root"),
1767 ));
1768 };
1769 record.runtime.register_builder(target, move || {
1770 hub.set_observed(true);
1771 let _version = hub.version();
1773 overlay_widget(&hub.snapshot())
1774 })?;
1775 Ok(())
1776 });
1777 match installed {
1778 Some(result) => result,
1779 None => Err(TreeError::WindowUnknown),
1780 }
1781 }
1782
1783 #[must_use]
1784 pub fn profiler_history(&self) -> &FrameHistory {
1785 self.profiler.history()
1786 }
1787
1788 #[must_use]
1792 pub fn performance_snapshot(&self) -> PerformanceSnapshot {
1793 let mut windows = Vec::new();
1794 let registry = self.registry.borrow();
1795 for slot in ®istry.slots {
1796 let Some(record) = slot.record.as_ref() else {
1797 continue;
1798 };
1799 windows.push(WindowPerformance {
1800 requested_frames: record.requested_frames,
1801 presented_frames: record.presented_frames,
1802 skipped_frames: record.skipped_frames,
1803 latest: Some(record.last_frame),
1804 render: record.render_metrics,
1805 gpu: record.gpu_sample,
1806 });
1807 }
1808 let mut widgets = WidgetWorkSnapshot::default();
1811 let mut text = TextCacheSnapshot::default();
1812 let mut accessibility = AccessibilitySnapshot::default();
1813 for slot in ®istry.slots {
1814 let Some(record) = slot.record.as_ref() else {
1815 continue;
1816 };
1817 let tree = record.runtime.tree();
1818 if widgets.elements_total == 0 {
1819 let diagnostics = tree.diagnostics();
1820 widgets = WidgetWorkSnapshot {
1821 mounts: diagnostics.mounts,
1822 unmounts: diagnostics.unmounts,
1823 rebuilds: diagnostics.rebuilds,
1824 layouts: diagnostics.layouts,
1825 paints: diagnostics.paints,
1826 composites: diagnostics.composites,
1827 animation_ticks: diagnostics.animation_ticks,
1828 scroll_offset_updates: diagnostics.scroll_offset_updates,
1829 reconciliation_fast_paths: diagnostics.reconciliation_fast_paths,
1830 layout_cache_hits: diagnostics.layout_cache_hits,
1831 display_lists_reused: diagnostics.display_lists_reused,
1832 compositor_only_updates: diagnostics.compositor_only_updates,
1833 lazy_layouts: diagnostics.lazy_layouts,
1834 items_built: diagnostics.items_built,
1835 items_reused: diagnostics.items_reused,
1836 child_list_scans: diagnostics.child_list_scans,
1837 identical_child_bailouts: diagnostics.identical_child_bailouts,
1838 elements_created: diagnostics.elements_created,
1839 elements_removed: diagnostics.elements_removed,
1840 elements_moved: diagnostics.elements_moved,
1841 dirty_requests: diagnostics.dirty_requests,
1842 dirty_queue_deduplicated: diagnostics.dirty_queue_deduplicated,
1843 elements_total: tree.element_count(),
1844 render_objects_total: tree.render_object_count(),
1845 layers_total: usize::try_from(tree.compositor_diagnostics().layers)
1846 .unwrap_or(0),
1847 };
1848 let text_diagnostics = tree.text_diagnostics();
1849 text = TextCacheSnapshot {
1850 layouts_requested: text_diagnostics.layouts_requested,
1851 cache_hits: text_diagnostics.cache_hits,
1852 cache_misses: text_diagnostics.cache_misses,
1853 paragraphs_reshaped: text_diagnostics.paragraphs_reshaped,
1854 parley_layouts_reused: text_diagnostics.parley_layouts_reused,
1855 documents_composed: text_diagnostics.documents_composed,
1856 };
1857 }
1858 accessibility.nodes_published += record.accessibility.nodes_published;
1859 accessibility.updates_skipped_unchanged +=
1860 record.accessibility.semantic_updates_skipped_unchanged;
1861 }
1862 let mut scheduler = scheduler_counters();
1863 self.scheduler_counters.apply_to(&mut scheduler);
1864 PerformanceSnapshot {
1865 scheduler,
1866 budget: *self.profiler.budget(),
1867 fps: self.profiler.frames_per_second(),
1868 windows,
1869 frame_statistics: self.profiler.history().statistics(),
1870 widgets,
1871 text,
1872 accessibility,
1873 }
1874 }
1875
1876 #[must_use]
1877 pub const fn profiler_budget(&self) -> &BudgetStatistics {
1878 self.profiler.budget()
1879 }
1880
1881 pub fn note_presented(&mut self, window_id: WindowId, presented: bool) {
1882 if presented {
1883 scheduler_counters::FRAMES_PRESENTED.fetch_add(1, Ordering::Relaxed);
1884 self.scheduler_counters.frames_presented =
1885 self.scheduler_counters.frames_presented.wrapping_add(1);
1886 } else {
1887 scheduler_counters::FRAMES_SKIPPED.fetch_add(1, Ordering::Relaxed);
1888 self.scheduler_counters.frames_skipped =
1889 self.scheduler_counters.frames_skipped.wrapping_add(1);
1890 }
1891 self.profiler.note_present(presented);
1892 let _ = self.with_window_mut(window_id, |record| {
1893 if presented {
1894 record.presented_frames = record.presented_frames.wrapping_add(1);
1895 } else {
1896 record.skipped_frames = record.skipped_frames.wrapping_add(1);
1897 }
1898 });
1899 }
1900
1901 pub fn request_close(&mut self, window_id: WindowId) -> bool {
1902 if !self.contains_window(window_id) {
1903 return false;
1904 }
1905 if self
1906 .close_request
1907 .as_mut()
1908 .is_some_and(|callback| !callback(window_id))
1909 {
1910 return false;
1911 }
1912 self.close_window(window_id)
1913 }
1914
1915 pub fn close_window(&mut self, window_id: WindowId) -> bool {
1916 self.fail_simulation_window(window_id);
1917 self.fail_native_requests_for_window(window_id, PlatformOperationError::stale_resource());
1918 self.fail_file_dialog_requests_for_window(window_id, FileDialogError::ParentClosed);
1919 let record = self.registry.borrow_mut().close(window_id);
1920 let Some(mut record) = record else {
1921 self.registry.borrow_mut().stale_window_commands += 1;
1922 return false;
1923 };
1924 record
1925 .transient_presentations
1926 .write()
1927 .expect("transient presentation snapshot lock")
1928 .clear();
1929 record.scope.cancel();
1930 record.runtime.dispose_window();
1931 self.native_commands
1932 .borrow_mut()
1933 .push_back(NativeWindowCommand::Operate(WindowCommand::new(
1934 window_id,
1935 WindowOperation::Close,
1936 )));
1937 let closed_restoration = record.restoration.clone();
1942 if let (Some(metadata), Some(restoration)) = (&closed_restoration, &self.restoration) {
1943 restoration.remove_window_descriptor(metadata.id.as_key().as_str());
1944 restoration.remove_scope(&[
1945 RestorationKey::new("window").expect("static key"),
1946 metadata.id.as_key().clone(),
1947 ]);
1948 }
1949 self.manager.sync_restorable_windows(true);
1950 if self.registry.borrow().visible_count() == 0
1951 && self.last_window_policy == LastWindowPolicy::ExitOnLastWindow
1952 {
1953 self.should_exit = true;
1954 }
1955 true
1956 }
1957
1958 fn drain_window_commands(&mut self) {
1959 while let Ok(queued) = self.command_receiver.try_recv() {
1960 let QueuedWindowCommand { command, request } = queued;
1961 let window_id = command.window_id;
1962 match command.operation.clone() {
1963 WindowOperation::Close => {
1964 if let Some(request) = request {
1965 Self::resolve_queued_request(
1966 request,
1967 Err(PlatformOperationError::with_context(
1968 PlatformOperationErrorKind::InvalidState,
1969 "window close does not expose a native completion request",
1970 )),
1971 );
1972 }
1973 let _ = self.close_window(window_id);
1974 }
1975 operation => {
1976 let applied = self.with_window_mut(window_id, |record| {
1977 match &operation {
1978 WindowOperation::SetTitle(title) => {
1979 record.options.title = title.clone()
1980 }
1981 WindowOperation::SetVisible(visible) => {
1982 record.options.visible = *visible;
1983 record.requested_state.visible = *visible;
1984 record.lifecycle = if *visible {
1985 WindowLifecycle::Visible
1986 } else {
1987 WindowLifecycle::Hidden
1988 };
1989 }
1990 WindowOperation::SetLogicalSize(size) => {
1991 if size.width.is_finite()
1992 && size.height.is_finite()
1993 && size.width > 0.0
1994 && size.height > 0.0
1995 {
1996 record.content_sizing.reset();
1997 record.options.initial_logical_size = *size;
1998 } else {
1999 return false;
2000 }
2001 }
2002 WindowOperation::SetOuterPosition(_) => {}
2003 WindowOperation::BeginMoveDrag
2004 | WindowOperation::BeginResizeDrag(_) => {}
2005 WindowOperation::SetMinimized(minimized) => {
2006 record.requested_state.minimized = *minimized;
2007 }
2008 WindowOperation::SetMaximized(maximized) => {
2009 record.options.maximized = *maximized;
2010 record.requested_state.maximized = *maximized;
2011 }
2012 WindowOperation::SetFullscreen(fullscreen) => {
2013 record.options.fullscreen = *fullscreen;
2014 record.requested_state.fullscreen = *fullscreen;
2015 }
2016 WindowOperation::SetResizable(resizable) => {
2017 record.options.resizable = *resizable;
2018 record.requested_state.resizable = *resizable;
2019 }
2020 WindowOperation::SetDecorations(decorations) => {
2021 record.options.decorations = *decorations;
2022 record.requested_state.decorations = *decorations;
2023 }
2024 WindowOperation::SetLogicalSizeLimits(limits) => {
2025 record.options.minimum_logical_size = limits.minimum();
2026 record.options.maximum_logical_size = limits.maximum();
2027 record.requested_state.size_limits = *limits;
2028 record.content_sizing.reset();
2029 }
2030 WindowOperation::SetWindowLevel(level) => {
2031 record.options.window_level = *level;
2032 record.requested_state.level = *level;
2033 }
2034 WindowOperation::SetWindowIcon(icon) => {
2035 record.options.window_icon = icon.clone();
2036 }
2037 WindowOperation::RequestUserAttention(_) => {}
2038 WindowOperation::SetCursorGrab(_)
2039 | WindowOperation::SetCursorVisible(_)
2040 | WindowOperation::SetCursorPosition(_) => {}
2041 WindowOperation::SetContentSensitivity(sensitivity) => {
2042 record.last_content_sensitivity = Some(*sensitivity);
2043 }
2044 WindowOperation::RequestFocus => {}
2045 WindowOperation::RequestRedraw => record.runtime.request_frame(),
2046 WindowOperation::Close => unreachable!(),
2047 }
2048 true
2049 });
2050 if applied == Some(true) {
2051 if let Some(request) = request {
2052 let request_id = command
2053 .request_id
2054 .expect("result-bearing command has request id");
2055 if !request.cancelled.load(Ordering::Acquire) {
2056 self.pending_native_requests.insert(
2057 request_id,
2058 PendingNativeRequest {
2059 window_id,
2060 sender: request.sender,
2061 },
2062 );
2063 }
2064 } else {
2065 debug_assert!(command.request_id.is_none());
2066 }
2067 self.native_commands
2068 .borrow_mut()
2069 .push_back(NativeWindowCommand::Operate(command));
2070 if matches!(operation, WindowOperation::SetVisible(false))
2071 && self.registry.borrow().visible_count() == 0
2072 && self.last_window_policy == LastWindowPolicy::ExitOnLastWindow
2073 {
2074 self.should_exit = true;
2075 }
2076 self.manager.sync_restorable_windows(true);
2077 } else if applied == Some(false) {
2078 if let Some(request) = request {
2079 Self::resolve_queued_request(
2080 request,
2081 Err(PlatformOperationError::with_context(
2082 PlatformOperationErrorKind::InvalidState,
2083 "window operation arguments were rejected by the runtime",
2084 )),
2085 );
2086 }
2087 } else if applied.is_none() {
2088 if let Some(request) = request {
2089 Self::resolve_queued_request(
2090 request,
2091 Err(PlatformOperationError::stale_resource()),
2092 );
2093 }
2094 self.registry.borrow_mut().stale_window_commands += 1;
2095 }
2096 }
2097 }
2098 }
2099 }
2100
2101 fn resolve_queued_request(request: QueuedNativeRequest, result: PlatformOperationResult) {
2102 if !request.cancelled.load(Ordering::Acquire) {
2103 let _ = request.sender.send(result);
2104 }
2105 }
2106
2107 fn drain_native_request_cancellations(&mut self) {
2108 while let Ok(request_id) = self.request_cancellation_receiver.try_recv() {
2109 self.pending_native_requests.remove(&request_id);
2110 }
2111 }
2112
2113 fn fail_native_requests_for_window(
2114 &mut self,
2115 window_id: WindowId,
2116 error: PlatformOperationError,
2117 ) {
2118 let request_ids = self
2119 .pending_native_requests
2120 .iter()
2121 .filter_map(|(request_id, request)| {
2122 (request.window_id == window_id).then_some(*request_id)
2123 })
2124 .collect::<Vec<_>>();
2125 for request_id in request_ids {
2126 if let Some(request) = self.pending_native_requests.remove(&request_id) {
2127 let _ = request.sender.send(Err(error.clone()));
2128 }
2129 }
2130 }
2131
2132 fn fail_all_native_requests(&mut self, error: PlatformOperationError) {
2133 while self.request_cancellation_receiver.try_recv().is_ok() {}
2134 for (_, request) in self.pending_native_requests.drain() {
2135 let _ = request.sender.send(Err(error.clone()));
2136 }
2137 while let Ok(queued) = self.command_receiver.try_recv() {
2138 if let Some(request) = queued.request {
2139 Self::resolve_queued_request(request, Err(error.clone()));
2140 }
2141 }
2142 }
2143
2144 fn drain_file_dialog_requests(&mut self) {
2145 while let Ok(queued) = self.file_dialog_receiver.try_recv() {
2146 if queued.cancelled.load(std::sync::atomic::Ordering::Acquire) {
2147 continue;
2148 }
2149 if !self.contains_window(queued.window_id) {
2150 let _ = queued.sender.send(Err(FileDialogError::ParentClosed));
2151 continue;
2152 }
2153 let capabilities = self
2154 .window_capabilities(queued.window_id)
2155 .expect("live file-dialog parent has capabilities")
2156 .application_services
2157 .file_dialogs;
2158 if let Some(error) = explicitly_unsupported_request(capabilities, &queued.request) {
2159 let _ = queued.sender.send(Err(error));
2160 continue;
2161 }
2162 let request_id = queued.request_id;
2163 let window_id = queued.window_id;
2164 self.pending_file_dialog_requests.insert(
2165 request_id,
2166 PendingFileDialogRequest {
2167 window_id,
2168 request: queued.request,
2169 sender: Some(queued.sender),
2170 cancelled: queued.cancelled,
2171 },
2172 );
2173 if self.active_file_dialog_requests.contains_key(&window_id) {
2174 self.queued_file_dialog_requests
2175 .entry(window_id)
2176 .or_default()
2177 .push_back(request_id);
2178 } else {
2179 self.activate_file_dialog(request_id);
2180 }
2181 }
2182 }
2183
2184 fn activate_file_dialog(&mut self, request_id: FileDialogRequestId) {
2185 let Some(pending) = self.pending_file_dialog_requests.get(&request_id) else {
2186 return;
2187 };
2188 if pending.cancelled.load(std::sync::atomic::Ordering::Acquire) {
2189 let window_id = pending.window_id;
2190 self.pending_file_dialog_requests.remove(&request_id);
2191 self.promote_next_file_dialog(window_id);
2192 return;
2193 }
2194 let window_id = pending.window_id;
2195 debug_assert!(!self.active_file_dialog_requests.contains_key(&window_id));
2196 self.active_file_dialog_requests
2197 .insert(window_id, request_id);
2198 self.native_file_dialog_requests
2199 .push_back(NativeFileDialogRequest {
2200 request_id,
2201 window_id,
2202 request: pending.request.clone(),
2203 });
2204 }
2205
2206 fn promote_next_file_dialog(&mut self, window_id: WindowId) {
2207 if self.active_file_dialog_requests.contains_key(&window_id)
2208 || !self.contains_window(window_id)
2209 {
2210 return;
2211 }
2212 loop {
2213 let next = self
2214 .queued_file_dialog_requests
2215 .get_mut(&window_id)
2216 .and_then(VecDeque::pop_front);
2217 let Some(request_id) = next else {
2218 self.queued_file_dialog_requests.remove(&window_id);
2219 return;
2220 };
2221 let cancelled = self
2222 .pending_file_dialog_requests
2223 .get(&request_id)
2224 .is_none_or(|pending| pending.cancelled.load(std::sync::atomic::Ordering::Acquire));
2225 if cancelled {
2226 self.pending_file_dialog_requests.remove(&request_id);
2227 continue;
2228 }
2229 self.activate_file_dialog(request_id);
2230 return;
2231 }
2232 }
2233
2234 fn drain_file_dialog_cancellations(&mut self) {
2235 while let Ok(request_id) = self.file_dialog_cancellation_receiver.try_recv() {
2236 let Some(window_id) = self
2237 .pending_file_dialog_requests
2238 .get(&request_id)
2239 .map(|pending| pending.window_id)
2240 else {
2241 continue;
2242 };
2243 let is_active = self.active_file_dialog_requests.get(&window_id) == Some(&request_id);
2244 if is_active {
2245 let not_started = self.pending_file_dialog_requests.phase(&request_id)
2248 == Some(RequestPhase::Queued);
2249 self.native_file_dialog_requests
2250 .retain(|request| request.request_id != request_id);
2251 if not_started {
2252 self.active_file_dialog_requests.remove(&window_id);
2253 self.pending_file_dialog_requests.remove(&request_id);
2254 self.promote_next_file_dialog(window_id);
2255 } else if let Some(pending) = self.pending_file_dialog_requests.get_mut(&request_id)
2256 {
2257 pending.sender = None;
2260 self.pending_file_dialog_requests.abandon(&request_id);
2261 }
2262 } else {
2263 if let Some(queue) = self.queued_file_dialog_requests.get_mut(&window_id) {
2264 queue.retain(|queued| *queued != request_id);
2265 if queue.is_empty() {
2266 self.queued_file_dialog_requests.remove(&window_id);
2267 }
2268 }
2269 self.pending_file_dialog_requests.remove(&request_id);
2270 }
2271 }
2272 }
2273
2274 fn fail_file_dialog_requests_for_window(
2275 &mut self,
2276 window_id: WindowId,
2277 error: FileDialogError,
2278 ) {
2279 self.native_file_dialog_requests
2280 .retain(|request| request.window_id != window_id);
2281 self.active_file_dialog_requests.remove(&window_id);
2282 self.queued_file_dialog_requests.remove(&window_id);
2283 let request_ids = self
2284 .pending_file_dialog_requests
2285 .iter()
2286 .filter_map(|(request_id, pending)| {
2287 (pending.window_id == window_id).then_some(*request_id)
2288 })
2289 .collect::<Vec<_>>();
2290 for request_id in request_ids {
2291 if let Some(mut pending) = self.pending_file_dialog_requests.remove(&request_id)
2292 && !pending.cancelled.load(std::sync::atomic::Ordering::Acquire)
2293 && let Some(sender) = pending.sender.take()
2294 {
2295 let _ = sender.send(Err(error.clone()));
2296 }
2297 }
2298 }
2299
2300 fn fail_all_file_dialog_requests(&mut self, error: FileDialogError) {
2301 self.file_dialog_bridge.stop();
2302 while self.file_dialog_cancellation_receiver.try_recv().is_ok() {}
2303 while let Ok(queued) = self.file_dialog_receiver.try_recv() {
2304 if !queued.cancelled.load(std::sync::atomic::Ordering::Acquire) {
2305 let _ = queued.sender.send(Err(error.clone()));
2306 }
2307 }
2308 for (_, mut pending) in self.pending_file_dialog_requests.drain() {
2309 if !pending.cancelled.load(std::sync::atomic::Ordering::Acquire)
2310 && let Some(sender) = pending.sender.take()
2311 {
2312 let _ = sender.send(Err(error.clone()));
2313 }
2314 }
2315 self.active_file_dialog_requests.clear();
2316 self.queued_file_dialog_requests.clear();
2317 self.native_file_dialog_requests.clear();
2318 }
2319
2320 pub fn take_native_file_dialog_requests(&mut self) -> Vec<NativeFileDialogRequest> {
2324 self.drain_file_dialog_requests();
2325 self.drain_file_dialog_cancellations();
2326 let requests: Vec<_> = self.native_file_dialog_requests.drain(..).collect();
2327 for request in &requests {
2328 self.pending_file_dialog_requests
2329 .dispatch(&request.request_id);
2330 }
2331 requests
2332 }
2333
2334 pub fn complete_file_dialog(
2338 &mut self,
2339 completion: NativeFileDialogCompletion,
2340 ) -> FileDialogCompletionStatus {
2341 let Some(pending) = self
2342 .pending_file_dialog_requests
2343 .get(&completion.request_id)
2344 else {
2345 return FileDialogCompletionStatus::UnknownRequest;
2346 };
2347 if pending.window_id != completion.window_id {
2348 return FileDialogCompletionStatus::TargetMismatch;
2349 }
2350 if self.active_file_dialog_requests.get(&completion.window_id)
2351 != Some(&completion.request_id)
2352 {
2353 return FileDialogCompletionStatus::UnknownRequest;
2354 }
2355
2356 let Ok(mut pending) = self
2357 .pending_file_dialog_requests
2358 .complete(&completion.request_id, |_| true)
2359 else {
2360 return FileDialogCompletionStatus::UnknownRequest;
2361 };
2362 self.active_file_dialog_requests
2363 .remove(&completion.window_id);
2364
2365 let (result, status) = match completion.result {
2366 Ok(outcome) if result_matches_request(&pending.request, &outcome) => {
2367 (Ok(outcome), FileDialogCompletionStatus::Completed)
2368 }
2369 Ok(_) => (
2370 Err(FileDialogError::InvalidResult),
2371 FileDialogCompletionStatus::InvalidResult,
2372 ),
2373 Err(error) => (Err(error), FileDialogCompletionStatus::Completed),
2374 };
2375 if !pending.cancelled.load(std::sync::atomic::Ordering::Acquire)
2376 && let Some(sender) = pending.sender.take()
2377 {
2378 let _ = sender.send(result);
2379 }
2380 self.promote_next_file_dialog(completion.window_id);
2381 status
2382 }
2383
2384 fn drain_global_shortcut_requests(&mut self) {
2385 drain_queued(
2386 &self.global_shortcut_receiver,
2387 &mut self.pending_global_shortcut_requests,
2388 &mut self.native_global_shortcut_requests,
2389 );
2390 let abandoned: Vec<_> = self
2391 .pending_global_shortcut_requests
2392 .iter()
2393 .filter(|(_, pending)| pending.reply.is_abandoned())
2394 .map(|(id, pending)| (*id, pending.operation))
2395 .collect();
2396 for (id, operation) in abandoned {
2397 if self.pending_global_shortcut_requests.phase(&id) == Some(RequestPhase::Queued) {
2398 if matches!(operation, NativeGlobalShortcutOperation::Register { .. }) {
2399 self.pending_global_shortcut_requests.remove(&id);
2400 self.native_global_shortcut_requests
2401 .retain(|request| request.request_id != id);
2402 }
2403 } else {
2404 self.pending_global_shortcut_requests.abandon(&id);
2405 }
2406 }
2407 }
2408
2409 fn fail_all_global_shortcut_requests(&mut self) {
2410 self.global_shortcut_bridge.stop();
2411 while let Ok(queued) = self.global_shortcut_receiver.try_recv() {
2412 queued.reply.finish(
2413 queued.operation,
2414 Err(incular_platform::GlobalShortcutError::ApplicationStopped),
2415 );
2416 }
2417 for (_, pending) in self.pending_global_shortcut_requests.drain() {
2418 pending.reply.finish(
2419 pending.operation,
2420 Err(incular_platform::GlobalShortcutError::ApplicationStopped),
2421 );
2422 }
2423 self.native_global_shortcut_requests.clear();
2424 }
2425
2426 #[doc(hidden)]
2430 pub fn take_native_global_shortcut_requests(&mut self) -> Vec<NativeGlobalShortcutRequest> {
2431 self.drain_global_shortcut_requests();
2432 let requests: Vec<_> = self.native_global_shortcut_requests.drain(..).collect();
2433 for request in &requests {
2434 self.pending_global_shortcut_requests
2435 .dispatch(&request.request_id);
2436 }
2437 requests
2438 }
2439
2440 #[doc(hidden)]
2444 pub fn complete_global_shortcut_request(
2445 &mut self,
2446 completion: NativeGlobalShortcutCompletion,
2447 ) -> GlobalShortcutCompletionStatus {
2448 let Ok(pending) = self
2449 .pending_global_shortcut_requests
2450 .complete(&completion.request_id, |_| true)
2451 else {
2452 return GlobalShortcutCompletionStatus::UnknownRequest;
2453 };
2454 pending.reply.finish(pending.operation, completion.result)
2455 }
2456
2457 pub fn complete_native_operation(
2460 &mut self,
2461 completion: NativeOperationCompletion,
2462 ) -> NativeOperationCompletionStatus {
2463 let pending = match self
2464 .pending_native_requests
2465 .complete(&completion.request_id, |pending| {
2466 pending.window_id == completion.window_id
2467 }) {
2468 Ok(pending) => pending,
2469 Err(CompletionRejection::TargetMismatch) => {
2470 return NativeOperationCompletionStatus::TargetMismatch;
2471 }
2472 Err(_) => return NativeOperationCompletionStatus::UnknownRequest,
2473 };
2474 if !self.contains_window(completion.window_id) {
2475 let _ = pending
2476 .sender
2477 .send(Err(PlatformOperationError::stale_resource()));
2478 return NativeOperationCompletionStatus::StaleTarget;
2479 }
2480 if let Err(error) = &completion.result {
2481 let error = error.clone();
2482 let _ = self.with_window_mut(completion.window_id, |record| {
2483 record.last_platform_error = Some(error);
2484 });
2485 }
2486 let _ = pending.sender.send(completion.result);
2487 NativeOperationCompletionStatus::Completed
2488 }
2489
2490 pub fn take_native_window_commands(&mut self) -> Vec<NativeWindowCommand> {
2494 self.drain_window_commands();
2495 self.drain_native_request_cancellations();
2496 let commands: Vec<_> = self.native_commands.borrow_mut().drain(..).collect();
2497 for command in &commands {
2498 if let NativeWindowCommand::Operate(command) = command
2499 && let Some(id) = command.request_id
2500 {
2501 self.pending_native_requests.dispatch(&id);
2502 }
2503 }
2504 commands
2505 }
2506
2507 #[must_use]
2508 pub const fn should_exit(&self) -> bool {
2509 self.should_exit
2510 }
2511
2512 #[must_use]
2513 pub fn window_diagnostics(&self, window_id: WindowId) -> Option<WindowDiagnostics> {
2514 let registry = self.registry.borrow();
2515 let slot = registry.slots.get(window_id.index() as usize)?;
2516 if slot.generation != window_id.generation() {
2517 return None;
2518 }
2519 let record = slot.record.as_ref()?;
2520 Some(WindowDiagnostics {
2521 id: window_id,
2522 title: record.options.title.clone(),
2523 logical_size: record.metrics.logical_size(),
2524 physical_size: record.metrics.physical_size,
2525 scale_factor: record.metrics.scale_factor,
2526 visible: record.options.visible,
2527 native_focused: record.native_focused,
2528 lifecycle: record.lifecycle,
2529 application_lifecycle: record.runtime.lifecycle(),
2530 frame_requested: record.runtime.frame_requested(),
2531 requested_frames: record.requested_frames,
2532 presented_frames: record.presented_frames,
2533 skipped_frames: record.skipped_frames,
2534 input_events: record.input_events,
2535 surface_generation: record.surface_generation,
2536 content_sensitivity: record.runtime.content_sensitivity(),
2537 elements: record.runtime.tree().element_count(),
2538 render_objects: record.runtime.tree().render_object_count(),
2539 semantics: record.runtime.tree().semantics().len(),
2540 accessibility: record.accessibility,
2541 capabilities: *record
2542 .capabilities
2543 .read()
2544 .expect("window capability snapshot lock"),
2545 last_platform_error: record.last_platform_error.clone(),
2546 requested_state: record.requested_state,
2547 observed_state: *record
2548 .observed_state
2549 .read()
2550 .expect("window observed-state snapshot lock"),
2551 transient_presentations: record
2552 .transient_presentations
2553 .read()
2554 .expect("transient presentation snapshot lock")
2555 .clone(),
2556 })
2557 }
2558
2559 #[must_use]
2560 pub fn diagnostics(&self) -> ApplicationDiagnostics {
2561 let registry = self.registry.borrow();
2562 ApplicationDiagnostics {
2563 windows_created: registry.windows_created,
2564 windows_closed: registry.windows_closed,
2565 active_windows: registry.ids().len(),
2566 stale_window_commands: registry.stale_window_commands,
2567 pending_native_requests: self.pending_native_requests.len(),
2568 pending_file_dialog_requests: self.pending_file_dialog_requests.len(),
2569 }
2570 }
2571
2572 #[must_use]
2575 pub fn restoration_diagnostics(&self) -> Option<RestorationDiagnostics> {
2576 self.restoration
2577 .as_ref()
2578 .map(restoration::RestorationManager::diagnostics)
2579 }
2580
2581 #[must_use]
2584 pub fn restoration_path(&self) -> Option<std::path::PathBuf> {
2585 self.restoration
2586 .as_ref()
2587 .and_then(restoration::RestorationManager::store_location)
2588 }
2589
2590 pub fn reset_restoration(&mut self) -> bool {
2595 let Some(restoration) = &self.restoration else {
2596 return false;
2597 };
2598 restoration.reset();
2599 true
2600 }
2601
2602 pub fn flush_restoration(&mut self) -> bool {
2605 let Some(restoration) = &self.restoration else {
2606 return false;
2607 };
2608 restoration.flush();
2609 true
2610 }
2611
2612 fn flush_restoration_and_wait(&mut self) {
2616 let Some(restoration) = self.restoration.clone() else {
2617 return;
2618 };
2619 restoration.flush();
2620 let deadline = Instant::now() + std::time::Duration::from_millis(100);
2621 while !restoration.is_clean() && Instant::now() < deadline {
2622 self.process_runtime_work();
2623 std::thread::sleep(std::time::Duration::from_millis(1));
2624 }
2625 }
2626
2627 #[must_use]
2628 pub fn debug_dump(&self) -> String {
2629 let mut lines = vec!["Application".to_owned()];
2630 if let Some(restoration) = &self.restoration {
2631 lines.push(restoration.debug_dump());
2632 }
2633 for id in self.active_window_ids() {
2634 if let Some(window) = self.window_diagnostics(id) {
2635 lines.push(format!(
2636 " {id} title={:?} {:.0}x{:.0} @{} focused={}\\n root elements={} render={} semantics={} dirty={}",
2637 window.title,
2638 window.logical_size.width,
2639 window.logical_size.height,
2640 window.scale_factor,
2641 window.native_focused,
2642 window.elements,
2643 window.render_objects,
2644 window.semantics,
2645 window.frame_requested,
2646 ));
2647 }
2648 }
2649 lines.join("\\n")
2650 }
2651
2652 pub fn shutdown(&mut self) {
2653 self.flush_restoration_before_shutdown();
2654 self.application_shell.stop();
2655 self.manager.bridge.stop();
2656 self.fail_all_file_dialog_requests(FileDialogError::ApplicationStopped);
2657 self.fail_all_global_shortcut_requests();
2658 self.fail_all_native_requests(PlatformOperationError::with_context(
2659 PlatformOperationErrorKind::Unavailable,
2660 "application runtime stopped before native completion",
2661 ));
2662 let ids = self.active_window_ids();
2663 for id in ids {
2664 self.fail_simulation_window(id);
2665 if let Some(mut record) = self.registry.borrow_mut().close(id) {
2666 record.scope.cancel();
2667 record.runtime.dispose_window();
2668 }
2669 }
2670 self.native_commands.borrow_mut().clear();
2671 self.scheduler.borrow_mut().shutdown();
2672 self.stop_simulation();
2673 self.should_exit = true;
2674 }
2675
2676 fn flush_restoration_before_shutdown(&mut self) {
2677 self.flush_restoration_and_wait();
2678 }
2679
2680 #[must_use]
2685 pub fn into_runtime(mut self) -> Runtime {
2686 self.flush_restoration_and_wait();
2687 self.manager.bridge.stop();
2688 self.fail_all_file_dialog_requests(FileDialogError::ApplicationStopped);
2689 self.fail_all_global_shortcut_requests();
2690 let primary = self.primary_window;
2691 let mut primary_record = self
2692 .registry
2693 .borrow_mut()
2694 .close(primary)
2695 .expect("application primary window exists");
2696 for id in self.active_window_ids() {
2697 if let Some(record) = self.registry.borrow_mut().close(id) {
2698 record.scope.cancel();
2699 }
2700 }
2701 primary_record.runtime.window_manager = None;
2702 primary_record.runtime.window_id = None;
2703 primary_record.runtime
2704 }
2705}
2706pub const PERFORMANCE_OVERLAY_KEY: &str = incular_widgets::internal::PERFORMANCE_OVERLAY_KEY;
2708
2709fn overlay_widget(snapshot: &PerformanceSnapshot) -> incular_widgets::Widget {
2711 use incular_widgets::{DecoratedBox, Padding, Text, Widget};
2712 let lines = snapshot.overlay_lines();
2713 let monospace = || {
2714 incular_widgets::TextStyle::default()
2715 .family(incular_text::FontFamily::Monospace)
2716 .font_size(11.)
2717 .color(incular_core::Color::rgba(190, 220, 255, 255))
2718 };
2719 let rows: Vec<Widget> = lines
2720 .into_iter()
2721 .map(|line| Widget::from(Text::new(line).style(monospace())))
2722 .collect();
2723 Widget::from(
2728 incular_widgets::IgnorePointer::new(Widget::from(incular_widgets::RepaintBoundary::new(
2729 Widget::from(
2730 DecoratedBox::new(Padding::all(
2731 6.,
2732 Widget::from(incular_widgets::Column::new(rows)),
2733 ))
2734 .background(incular_core::Color::rgba(12, 14, 18, 216))
2735 .radius(4.)
2736 .border(incular_rendering::Border {
2737 color: incular_core::Color::rgba(120, 170, 245, 90),
2738 width: 1.,
2739 }),
2740 ),
2741 )))
2742 .ignoring(true),
2743 )
2744}