Skip to main content

incular_runtime/
application.rs

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
81/// Owns application services, one Tokio runtime, and multiple retained roots.
82/// A root's environment, input/focus, semantics, compositor, frame work, and
83/// cancellation scope are window-local; assets, Signals, and Tokio are shared.
84pub 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    /// Creates an application from a retained root builder.
133    ///
134    /// The builder runs during construction and may run again whenever state
135    /// or environment values read by the builder change. Keep the builder
136    /// free of external side effects such as network requests, file writes,
137    /// or analytics; put those operations in event callbacks or tasks.
138    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    /// Builds the initial retained root with portable native-window options.
152    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    /// Creates an application whose explicitly selected values can survive a
160    /// restart. Restoration loads synchronously before the initial root build;
161    /// a corrupt, incompatible, or unmigratable snapshot is reported through
162    /// diagnostics and simply exposes application defaults instead.
163    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    /// Creates a restoration-enabled application whose primary native window
172    /// also has a stable application identity. Use this instead of
173    /// [`Self::new_with_restoration`] when its logical size/fullscreen state
174    /// should be reconstructed across launches.
175    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    /// Adopts an existing standalone runtime without remounting its tree or
204    /// replacing its task scheduler. The action callback observes legacy action
205    /// IDs on the UI thread; native event handling uses the application host.
206    /// Existing reactive builders, focus and queued task completions are retained.
207    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(&registry),
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    /// Adds an independent retained root. Native creation is deferred until a
304    /// desktop adapter reaches an active Winit event-loop callback.
305    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    /// Adds an independent declarative root with its own window environment
314    /// and cancellation scope.
315    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    /// Opens an auxiliary window with a stable restoration ID. A normal user
324    /// close removes this descriptor and its window-scoped values from the
325    /// next session; application shutdown preserves currently active
326    /// restorable windows.
327    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    /// Registers an in-memory factory for one stable auxiliary window kind.
339    /// Builders remain ordinary closures and are never stored in a snapshot.
340    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    /// Restores all persisted auxiliary windows whose stable factories have
361    /// been registered. Call this after registration and before entering the
362    /// native runner; it therefore cannot flash a default auxiliary UI before
363    /// restore. Unknown kinds, unparseable ids, and duplicate descriptors are
364    /// skipped safely and counted in diagnostics; only a genuine window-open
365    /// failure aborts the remaining restores.
366    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    /// Returns a thread-safe command handle for one live Incular window.
422    #[must_use]
423    pub fn window_handle(&self, window_id: WindowId) -> Option<WindowHandle> {
424        self.manager.handle(window_id)
425    }
426
427    /// Effective retained cursor for the window's current hover position.
428    /// Native adapters use this after input and frame updates; applications
429    /// should normally express cursor intent through `MouseRegion` instead.
430    #[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    /// Returns the default application-menu bridges mounted in one retained
440    /// window root. Desktop adapters bind them to their native menu delegate.
441    #[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    /// Returns the latest backend capability snapshot for application-level
451    /// services. Before a native backend attaches, fields are `Unknown`.
452    #[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    /// Returns the application-scoped activation stream. Native open-file,
461    /// deep-link, reopen, secondary-launch, and global-shortcut events all use
462    /// this one ordered delivery path rather than a window input queue.
463    #[must_use]
464    pub fn activations(&self) -> ApplicationActivationService {
465        self.activations.clone()
466    }
467
468    /// Returns the application-scoped native global shortcut service.
469    #[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    /// Returns the application-owned native shell service. Tray/status items,
478    /// notifications, and taskbar/Dock state are independent of retained widget
479    /// lifetime and may therefore be created before or after windows.
480    #[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    /// Replaces the launch payload that the desktop runner publishes for this
501    /// process. This is useful for custom launchers which already classified
502    /// document or URL arguments and want to preserve those semantics.
503    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    /// Enables or disables optional single-instance forwarding. Incular never
514    /// enforces this policy unless the application explicitly supplies one.
515    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    /// Connects URL activations to an existing Router platform-route provider.
525    ///
526    /// The application supplies URL-to-route interpretation. This keeps deep
527    /// link parsing in the route layer and prevents desktop adapters from
528    /// acquiring application navigation policy.
529    #[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    /// Publishes a normalized activation from a native/application host. This
539    /// method is public only for platform embedders and deterministic tests;
540    /// normal application code observes [`Self::activations`] instead.
541    #[doc(hidden)]
542    pub fn handle_application_activation(&mut self, activation: ApplicationActivation) {
543        self.activations.publish(activation);
544    }
545
546    /// Replaces the backend's immutable connected-display snapshot.
547    ///
548    /// Display IDs are allocated/generationally managed by the native backend.
549    /// Replacing the snapshot immediately invalidates removed IDs at this API
550    /// boundary; a later display using the same slot must have a new generation.
551    /// Replaces the native backend's immutable display snapshot catalog.
552    ///
553    /// This is public for sibling platform adapters and headless/test hosts; UI
554    /// code should read displays through [`Application::displays`] or a
555    /// [`WindowHandle`](crate::WindowHandle), not manufacture native topology.
556    #[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    /// Publishes one backend/session capability snapshot and applies it to all
602    /// currently live windows. Custom embedders may call this before entering
603    /// their native event loop.
604    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    /// Returns the capability snapshot associated with one live window.
620    #[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    /// Overrides capabilities for one live window when native support depends
633    /// on that concrete surface/session rather than the application backend.
634    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    /// Publishes the native adapter's resolved presentation for the visible
651    /// semantic transients in one retained window. Custom embedders should
652    /// replace the complete snapshot after each host-negotiation pass.
653    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    /// Publishes the desktop bounds a confirmed native transient may use for
676    /// collision placement. Bounds are parent-view-local logical geometry.
677    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    /// Development-only, read-only view of application windows.  Keeping this
689    /// extraction here means platform runners never need to reach into the
690    /// retained-window registry.
691    #[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    /// Extracts one window's retained widget tree for DevTools.  The method
712    /// performs no layout, paint, or allocation proportional to the entire
713    /// application beyond the explicit bounded protocol snapshot.
714    #[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    /// Returns curated details for a live DevTools element id, rejecting stale
748    /// ids before touching the retained tree.
749    #[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    /// Applies a supported temporary property override to one live retained
794    /// node. Generational IDs make stale edits fail closed.
795    #[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    /// Reads the exact world-space bounds for the selected-widget overlay.
815    #[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    /// Read-only retained geometry for selected-node debug adornments.
841    #[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    /// Bounded exact retained layout rectangles for one target window. The
860    /// platform adapter consumes these in its compositor-only overlay pass;
861    /// no widget, layout or semantics state changes as a consequence.
862    #[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    /// Bounded existing per-node work counters for target-side phase flashes.
916    #[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    /// Enables one bounded per-node Deep trace for the next frame of this
930    /// window. Normal/Performance profiler modes never call this, so the hot
931    /// path has only compile-time-gated instrumentation checks.
932    #[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    /// Takes the completed per-node trace without cloning its event buffer.
941    #[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    /// Exact retained subtree bounds for the selected DevTools node, together
963    /// with its owning native window. Stale IDs return no result.
964    #[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    /// Hit-tests a retained root for Select Widget mode without dispatching
984    /// the pointer to application widgets.
985    #[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    /// Framework-owned memory/resource inventory.  This is deliberately a
1009    /// count of retained Incular objects, not a misleading attempt to inspect
1010    /// Rust's allocator or another crate's heap.
1011    #[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    /// Returns only signals which explicitly opted into DevTools visibility.
1031    /// Values are already reduced to bounded summaries at write time.
1032    #[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    /// Resolves actual live reactive dependencies for a DevTools-visible
1057    /// signal.  The registry stores only weak signal state and Element ids;
1058    /// this method resolves those ids against the matching live window tree,
1059    /// so unmounted elements are never retained or reported.
1060    #[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    /// Applies an explicitly opted-in typed DevTools edit on the UI thread.
1107    /// The conversion and the normal `Signal::set` invalidation both live in
1108    /// the signal registration; unsupported types are rejected.
1109    #[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    /// Visible transient surfaces belonging to `window_id` after its latest
1134    /// retained layout. Geometry is window-local logical space and therefore
1135    /// remains anchored when the native parent moves; the platform adapter is
1136    /// responsible only for mapping that geometry into desktop coordinates.
1137    #[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    /// Installs the application policy used for a native close request. The
1158    /// callback returns `true` to accept closing and `false` to keep the
1159    /// window alive (for example, an unsaved-work dialog flow).
1160    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    /// Returns a cloneable, in-process controller for the primary window.
1174    /// Simulation commands are serviced by the native event loop and never
1175    /// synthesize OS-level mouse or keyboard input.
1176    #[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        // `callback` may have opened a different window through BuildContext,
1189        // so borrow the registry only after it returns.
1190        // The root being serviced remains generationally reserved throughout.
1191        self.registry.borrow_mut().restore(window_id, record);
1192        Some(result)
1193    }
1194    /// Starts application-scoped Tokio work. It is not cancelled when any
1195    /// individual window closes.
1196    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    /// Queues a UI callback for the primary window. For work targeting another
1216    /// root, capture that root's `BuildContext` dispatcher instead.
1217    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    /// Drains at most one bounded Tokio UI turn, routing each completion to
1242    /// the window scope that created it. Closed/reused IDs discard stale work.
1243    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            // Application-scoped work (including restoration write completion)
1251            // is not owned by a particular root. If the original primary has
1252            // closed while another window remains, deliver it to that live UI
1253            // turn rather than discarding persistence bookkeeping.
1254            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    /// Routes external platform transfer data to one retained window and
1296    /// returns the operation requested by the currently hit-tested target.
1297    /// Native adapters with richer drag protocols can feed this response back
1298    /// to the source; adapters such as Winit file-drop may simply ignore it.
1299    #[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    /// Routes a normalized event to exactly one retained root.
1313    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    /// Applies one genuine application-level lifecycle transition to every
1395    /// retained window root. Window focus remains a separate per-window
1396    /// lifecycle channel and never synthesizes these application states.
1397    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    /// Records a backend failure for a user-driven operation that does not
1426    /// have an explicit result consumer (for example a `WindowDragRegion`).
1427    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    /// Installs a platform clipboard for one window's input/editing bridge.
1450    /// The clipboard service itself may be application-wide; the runtime keeps
1451    /// the mutable adapter at the window input boundary.
1452    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    /// Drains text-input commands for one native window. The caller applies
1459    /// them on that window's platform/UI thread.
1460    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    /// Records one UI-relevant runtime wake (async completion or message).
1491    /// Wakes that mutate no visible state must not produce redraws; the idle
1492    /// contract test relies on these counters staying independent.
1493    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    /// Projects this window's retained semantics through its window-local
1579    /// AccessKit bridge. Native adapters call this on the UI/event-loop thread
1580    /// only after AccessKit activation; a changed semantic revision produces a
1581    /// full or incremental tree update while ordinary paint/compositor frames
1582    /// produce no work.
1583    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    /// Receives an already validated native semantic request for exactly one
1604    /// window. It uses the ordinary retained runtime action dispatcher, so
1605    /// native callbacks never mutate widget/controller state directly.
1606    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    /// Turns one window's per-frame semantics pass on or off. Native hosts
1620    /// enable it when an assistive technology requests the tree; enabling
1621    /// schedules a frame so the first native update is complete.
1622    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    /// Updates one window's value-free bridge diagnostics after a lifecycle or
1635    /// action event that did not emit a tree update.
1636    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    /// Selects how much profiling state is retained. Normal keeps only
1647    /// counters and the latest frame; higher modes retain bounded history.
1648    pub fn set_profiler_mode(&mut self, mode: ProfilerMode) {
1649        self.profiler.set_mode(mode);
1650    }
1651
1652    /// Applies the DevTools animation speed to every live window. This is a
1653    /// retained animation-clock setting, not a scheduler or Tokio setting.
1654    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    /// Rebuilds every retained builder in every live window on the next
1663    /// frame. Element identity, focus, scroll offsets and signal values are
1664    /// kept; only widget descriptions and the callbacks they carry are
1665    /// replaced. Use this after application code was swapped underneath a
1666    /// running tree.
1667    #[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    /// Loads a hot patch and [reassembles](Self::reassemble) every window.
1675    /// A patch that fails to load leaves the running code untouched.
1676    ///
1677    /// # Safety
1678    ///
1679    /// The patch must have been built against the executable image of this
1680    /// exact process; see [`HotPatch::apply`](crate::HotPatch::apply).
1681    #[cfg(feature = "hot-reload")]
1682    pub unsafe fn apply_hot_patch(
1683        &mut self,
1684        patch: crate::HotPatch,
1685    ) -> Result<(), crate::HotPatchError> {
1686        // SAFETY: forwarded caller contract.
1687        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    /// Derives frame budgets from an actual refresh rate (Hz). `None` clears.
1698    pub fn set_refresh_rate_hz(&mut self, hz: Option<f32>) {
1699        self.profiler.set_refresh_rate_hz(hz);
1700    }
1701
1702    /// Merges renderer-reported metrics into one window's latest record and
1703    /// feeds the application profiler history with a complete frame sample.
1704    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                // Renderer prepare/encode/submit completes the CPU picture.
1723                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        // Throttled publish for observers (debug overlays). Production frames
1737        // with no observer and Normal mode skip the snapshot build entirely.
1738        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    /// Shared handle to the observable performance snapshot. Overlay builders
1747    /// read [`PerformanceHub::version`] so only they rebuild on publish.
1748    #[must_use]
1749    pub const fn performance_hub(&self) -> &PerformanceHub {
1750        &self.hub
1751    }
1752
1753    /// Installs the debug performance overlay into `window_id`.
1754    ///
1755    /// The application tree must contain a widget keyed with
1756    /// [`PERFORMANCE_OVERLAY_KEY`]; that placeholder element is replaced by a
1757    /// repaint-contained overlay whose builder re-runs **only** when the hub
1758    /// publishes, so measured widget work is unaffected. Requires a profiler
1759    /// mode of [`ProfilerMode::Diagnostic`] or higher to observe data.
1760    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                // Reading the version subscribes this element alone.
1772                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    /// Builds a complete read-only performance view. This allocates and is
1789    /// intended for diagnostics tooling, the debug overlay, and JSON export —
1790    /// never for the per-frame hot path.
1791    #[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 &registry.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        // Widget/text totals come from the primary window's retained tree;
1809        // accessibility counters merge across every live window adapter.
1810        let mut widgets = WidgetWorkSnapshot::default();
1811        let mut text = TextCacheSnapshot::default();
1812        let mut accessibility = AccessibilitySnapshot::default();
1813        for slot in &registry.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        // Explicit user close removes the auxiliary descriptor and its
1938        // window-scoped values; shutdown bypasses this method and therefore
1939        // preserves active descriptors. Not-yet-restored descriptors for other
1940        // windows are retained.
1941        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                // If the desktop host has not taken this request yet, cancellation
2246                // can prevent native presentation and the FIFO may advance.
2247                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                    // The native modal operation already owns the slot. Detach
2258                    // result delivery, but keep serialization until completion.
2259                    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    /// Takes native file-dialog work ready to start. At most one request per
2321    /// parent window is active; later requests for that window remain FIFO in
2322    /// the runtime until completion or cancellation.
2323    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    /// Completes one active file dialog and promotes the next request for the
2335    /// same parent. Backends must invoke this only on the application UI/event
2336    /// loop thread; native dialog objects never cross this boundary.
2337    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    /// Takes application-scoped global-shortcut work ready for the native UI
2427    /// thread. Custom desktop embedders may implement this boundary without
2428    /// exposing native hotkey handles to runtime/application code.
2429    #[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    /// Completes one native global-shortcut request. If the requesting future
2441    /// was abandoned after a registration succeeded, the caller is instructed
2442    /// to roll that native registration back immediately.
2443    #[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    /// Completes one result-bearing native operation. Backends call this only
2458    /// after applying the operation on the owning native/UI thread.
2459    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    /// Takes all UI-thread native operations. Adapters must call this only
2491    /// from their event-loop callback; creation is consequently valid for
2492    /// Winit 0.30's active-loop restriction.
2493    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    /// Returns value-free health and persistence-coalescing data when the
2573    /// application opted into restoration.
2574    #[must_use]
2575    pub fn restoration_diagnostics(&self) -> Option<RestorationDiagnostics> {
2576        self.restoration
2577            .as_ref()
2578            .map(restoration::RestorationManager::diagnostics)
2579    }
2580
2581    /// Returns the configured snapshot path for developer tooling. In-memory
2582    /// and custom stores intentionally have no filesystem location.
2583    #[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    /// Removes every opt-in persisted value and every restorable-window
2591    /// descriptor. The deletion is coalesced through the normal Tokio-backed
2592    /// persistence pipeline; call [`Self::flush_restoration`] before an
2593    /// immediate process handoff if required.
2594    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    /// Bypasses the normal 250ms debounce but still writes on Tokio's blocking
2603    /// pool. This is useful for an explicit user action or lifecycle flush.
2604    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    /// Flushes restoration and bounded-waits for the blocking save to settle,
2613    /// mirroring the shutdown handoff. Used when the host may freeze or kill
2614    /// the process after the lifecycle event (for example mobile suspend).
2615    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    /// Compatibility escape hatch for existing single-window embedders and
2681    /// tests. Extra windows are cancelled; desktop `run` retains all windows.
2682    /// Pending restoration state is flushed with the same bounded wait as
2683    /// shutdown so the extracted runtime does not silently drop it.
2684    #[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}
2706/// Widget key that marks the performance-overlay mount point.
2707pub const PERFORMANCE_OVERLAY_KEY: &str = incular_widgets::internal::PERFORMANCE_OVERLAY_KEY;
2708
2709/// Builds the repaint-contained overlay visual from one snapshot.
2710fn 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    // The overlay is diagnostic chrome, not an application control. Keep the
2724    // visual mounted in the tree while allowing the widgets underneath it to
2725    // receive pointer events, just like Flutter's non-interactive performance
2726    // overlay.
2727    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}