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};
31#[cfg(not(target_arch = "wasm32"))]
32use crate::tasks::TokioHandle;
33use crate::tasks::{self, RuntimeWake, Task, TaskFailure, TaskHandle};
34use crate::transient_presentation::{
35    ResolvedTransientPresentation, TransientPresentationResolution,
36};
37use crate::window_commands::{
38    DisplayCatalog, NativeOperationCompletionStatus, NativeWindowCommand, QueuedNativeRequest,
39    QueuedWindowCommand, WindowCommandBridge, WindowHandle,
40};
41use crate::window_state::{
42    ApplicationDiagnostics, ContentSizeCoordinator, WindowDiagnostics, WindowManager, WindowRecord,
43    WindowRegistry,
44};
45use incular_accessibility::{
46    AccessKitProjection, AccessibilityDiagnostics, MobileAccessibilityProjection,
47    MobileAccessibilityUpdate, NativeAccessibilityUpdate, SemanticActionRequest,
48};
49use incular_config::{Constraints, RuntimeEnvironment};
50use incular_core::RestorationKey;
51use incular_core::time::Instant;
52use incular_platform::{
53    ApplicationActivation, Clipboard, DisplayId, DisplaySnapshot, ExternalDragEvent,
54    ExternalDragResponse, FileDialogError, FileDialogOutcome, FileDialogRequest,
55    FileDialogRequestId, LaunchActivation, NativeOperationCompletion, NativeRequestId,
56    PlatformCapabilities, PlatformEvent, PlatformLifecycle, PlatformOperationError,
57    PlatformOperationErrorKind, PlatformOperationResult, SingleInstancePolicy, TextInputCommand,
58    WindowCommand, WindowEvent, WindowEventKind, WindowId, WindowLifecycle, WindowOperation,
59    WindowOptions,
60};
61use incular_rendering::DisplayList;
62use incular_widgets::internal::{Key, MouseCursor, PlatformMenuBinding, TreeError};
63use incular_widgets::{RouteInformation, RouteInformationProvider, Widget};
64use std::{
65    cell::RefCell,
66    collections::{HashMap, HashSet, VecDeque},
67    rc::Rc,
68    sync::{Arc, RwLock, atomic::Ordering, mpsc},
69};
70use tokio::sync::oneshot;
71
72struct PendingNativeRequest {
73    window_id: WindowId,
74    sender: oneshot::Sender<PlatformOperationResult>,
75}
76
77struct PendingFileDialogRequest {
78    window_id: WindowId,
79    request: FileDialogRequest,
80    sender: Option<oneshot::Sender<Result<FileDialogOutcome, FileDialogError>>>,
81    cancelled: Arc<std::sync::atomic::AtomicBool>,
82}
83
84/// Owns application services, one Tokio runtime, and multiple retained roots.
85/// A root's environment, input/focus, semantics, compositor, frame work, and
86/// cancellation scope are window-local; assets, Signals, and Tokio are shared.
87pub struct Application {
88    pub(crate) scheduler: Rc<RefCell<tasks::TaskScheduler>>,
89    pub(crate) registry: Rc<RefCell<WindowRegistry>>,
90    pub(crate) manager: WindowManager,
91    pub(crate) command_receiver: mpsc::Receiver<QueuedWindowCommand>,
92    pub(crate) request_cancellation_receiver: mpsc::Receiver<NativeRequestId>,
93    pending_native_requests: RequestRegistry<NativeRequestId, PendingNativeRequest>,
94    file_dialog_receiver: mpsc::Receiver<QueuedFileDialogRequest>,
95    file_dialog_cancellation_receiver: mpsc::Receiver<FileDialogRequestId>,
96    file_dialog_bridge: Arc<FileDialogBridge>,
97    pending_file_dialog_requests: RequestRegistry<FileDialogRequestId, PendingFileDialogRequest>,
98    active_file_dialog_requests: HashMap<WindowId, FileDialogRequestId>,
99    queued_file_dialog_requests: HashMap<WindowId, VecDeque<FileDialogRequestId>>,
100    native_file_dialog_requests: VecDeque<NativeFileDialogRequest>,
101    global_shortcut_receiver: mpsc::Receiver<QueuedGlobalShortcutRequest>,
102    global_shortcut_bridge: Arc<GlobalShortcutBridge>,
103    pending_global_shortcut_requests: RequestRegistry<
104        crate::global_shortcuts::GlobalShortcutRequestId,
105        PendingGlobalShortcutRequest,
106    >,
107    native_global_shortcut_requests: VecDeque<NativeGlobalShortcutRequest>,
108    platform_capabilities: Arc<RwLock<PlatformCapabilities>>,
109    application_shell: ApplicationShellService,
110    activations: ApplicationActivationService,
111    launch_activation: LaunchActivation,
112    single_instance_policy: Option<SingleInstancePolicy>,
113    display_catalog: Arc<RwLock<DisplayCatalog>>,
114    pub(crate) simulation_receiver: mpsc::Receiver<simulation::SimulationRequest>,
115    pub(crate) simulation_bridge: Arc<simulation::SimulationBridge>,
116    pub(crate) simulation_waiters: simulation::SimulationWaiters,
117    pub(crate) native_commands: Rc<RefCell<VecDeque<NativeWindowCommand>>>,
118    pub(crate) primary_window: WindowId,
119    pub(crate) last_window_policy: LastWindowPolicy,
120    pub(crate) should_exit: bool,
121    pub(crate) close_request: Option<Box<dyn FnMut(WindowId) -> bool>>,
122    pub(crate) restoration: Option<restoration::RestorationManager>,
123    pub(crate) restoration_window_factories:
124        HashMap<String, (WindowOptions, RestorableWindowFactory)>,
125    pub(crate) profiler: PerformanceProfiler,
126    pub(crate) hub: PerformanceHub,
127    pub(crate) scheduler_counters: ApplicationSchedulerCounters,
128}
129impl Drop for Application {
130    fn drop(&mut self) {
131        self.application_shell.stop();
132    }
133}
134impl Application {
135    /// Creates an application from a retained root builder.
136    ///
137    /// The builder runs during construction and may run again whenever state
138    /// or environment values read by the builder change. Keep the builder
139    /// free of external side effects such as network requests, file writes,
140    /// or analytics; put those operations in event callbacks or tasks.
141    pub fn new(
142        build: impl FnMut(&mut BuildContext) -> Widget + 'static,
143    ) -> Result<Self, TreeError> {
144        Self::new_with_options(WindowOptions::default(), build).map_err(|error| match error {
145            WindowError::Tree(error) => error,
146            WindowError::Options(_)
147            | WindowError::Restoration(_)
148            | WindowError::ApplicationStopped => {
149                unreachable!("default application options are valid while constructing")
150            }
151        })
152    }
153
154    /// Builds the initial retained root with portable native-window options.
155    pub fn new_with_options(
156        options: WindowOptions,
157        build: impl FnMut(&mut BuildContext) -> Widget + 'static,
158    ) -> Result<Self, WindowError> {
159        Self::new_inner(options, None, None, build)
160    }
161
162    /// Creates an application whose explicitly selected values can survive a
163    /// restart. Restoration loads synchronously before the initial root build;
164    /// a corrupt, incompatible, or unmigratable snapshot is reported through
165    /// diagnostics and simply exposes application defaults instead.
166    pub fn new_with_restoration(
167        options: WindowOptions,
168        restoration: RestorationConfig,
169        build: impl FnMut(&mut BuildContext) -> Widget + 'static,
170    ) -> Result<Self, WindowError> {
171        Self::new_inner(options, Some(restoration), None, build)
172    }
173
174    /// Creates a restoration-enabled application whose primary native window
175    /// also has a stable application identity. Use this instead of
176    /// [`Self::new_with_restoration`] when its logical size/fullscreen state
177    /// should be reconstructed across launches.
178    pub fn new_restorable(
179        restoration_id: WindowRestorationId,
180        kind: impl Into<String>,
181        options: WindowOptions,
182        restoration: RestorationConfig,
183        build: impl FnMut(&mut BuildContext) -> Widget + 'static,
184    ) -> Result<Self, WindowError> {
185        Self::new_inner(
186            options,
187            Some(restoration),
188            Some(RestorableWindowMetadata::new(restoration_id, kind)?),
189            build,
190        )
191    }
192
193    fn new_inner(
194        options: WindowOptions,
195        restoration_config: Option<RestorationConfig>,
196        primary_restoration: Option<RestorableWindowMetadata>,
197        build: impl FnMut(&mut BuildContext) -> Widget + 'static,
198    ) -> Result<Self, WindowError> {
199        Self::with_primary(tasks::TaskScheduler::new(), restoration_config, |manager| {
200            manager
201                .open_window_with_inner(options, build, primary_restoration)
202                .map(|handle| handle.id())
203        })
204    }
205
206    /// Adopts an existing standalone runtime without remounting its tree or
207    /// replacing its task scheduler. The action callback observes legacy action
208    /// IDs on the UI thread; native event handling uses the application host.
209    /// Existing reactive builders, focus and queued task completions are retained.
210    pub fn from_runtime(
211        mut runtime: Runtime,
212        on_action: impl FnMut(incular_widgets::internal::ActionId) + 'static,
213    ) -> Self {
214        runtime.legacy_action_handler = Some(Box::new(on_action));
215        Self::with_primary(runtime.scheduler.clone(), None, |manager| {
216            Ok(manager.adopt_runtime(runtime))
217        })
218        .expect("adopting a mounted runtime with default window options is infallible")
219    }
220
221    fn with_primary(
222        scheduler: Rc<RefCell<tasks::TaskScheduler>>,
223        restoration_config: Option<RestorationConfig>,
224        create: impl FnOnce(&WindowManager) -> Result<WindowId, WindowError>,
225    ) -> Result<Self, WindowError> {
226        let restoration = restoration_config.map(restoration::RestorationManager::load);
227        if let Some(restoration) = &restoration {
228            restoration.attach_scheduler(&scheduler);
229        }
230        let registry = Rc::new(RefCell::new(WindowRegistry::default()));
231        let native_commands = Rc::new(RefCell::new(VecDeque::new()));
232        let (sender, command_receiver) = mpsc::channel();
233        let (cancellation_sender, request_cancellation_receiver) = mpsc::channel();
234        let (file_dialog_sender, file_dialog_receiver) = mpsc::channel();
235        let (file_dialog_cancellation_sender, file_dialog_cancellation_receiver) = mpsc::channel();
236        let file_dialog_bridge = Arc::new(FileDialogBridge::new(
237            file_dialog_sender,
238            file_dialog_cancellation_sender,
239        ));
240        let (global_shortcut_sender, global_shortcut_receiver) = mpsc::channel();
241        let global_shortcut_bridge = Arc::new(GlobalShortcutBridge::new(global_shortcut_sender));
242        let (simulation_sender, simulation_receiver) = mpsc::channel();
243        let simulation_bridge = Arc::new(simulation::SimulationBridge::new(simulation_sender));
244        let platform_capabilities = Arc::new(RwLock::new(PlatformCapabilities::default()));
245        let application_shell = ApplicationShellService::new(platform_capabilities.clone());
246        let activations = ApplicationActivationService::default();
247        let display_catalog = Arc::new(RwLock::new(DisplayCatalog::default()));
248        let application_lifecycle = Rc::new(std::cell::Cell::new(
249            crate::application_types::ApplicationLifecycle::Starting,
250        ));
251        let bridge = Arc::new(WindowCommandBridge::new(sender, cancellation_sender));
252        let manager = WindowManager {
253            registry: Rc::downgrade(&registry),
254            scheduler: scheduler.clone(),
255            bridge,
256            file_dialog_bridge: file_dialog_bridge.clone(),
257            global_shortcut_bridge: global_shortcut_bridge.clone(),
258            application_shell: application_shell.clone(),
259            native_commands: native_commands.clone(),
260            restoration: restoration.clone(),
261            application_capabilities: platform_capabilities.clone(),
262            displays: display_catalog.clone(),
263            application_lifecycle,
264        };
265        let primary_window = create(&manager)?;
266        Ok(Self {
267            scheduler,
268            registry,
269            manager,
270            command_receiver,
271            request_cancellation_receiver,
272            pending_native_requests: RequestRegistry::new(),
273            file_dialog_receiver,
274            file_dialog_cancellation_receiver,
275            file_dialog_bridge,
276            pending_file_dialog_requests: RequestRegistry::new(),
277            active_file_dialog_requests: HashMap::new(),
278            queued_file_dialog_requests: HashMap::new(),
279            native_file_dialog_requests: VecDeque::new(),
280            global_shortcut_receiver,
281            global_shortcut_bridge,
282            pending_global_shortcut_requests: RequestRegistry::new(),
283            native_global_shortcut_requests: VecDeque::new(),
284            platform_capabilities,
285            application_shell,
286            activations,
287            launch_activation: LaunchActivation::current_process(),
288            single_instance_policy: None,
289            display_catalog,
290            simulation_receiver,
291            simulation_bridge,
292            simulation_waiters: simulation::SimulationWaiters::default(),
293            native_commands,
294            primary_window,
295            last_window_policy: LastWindowPolicy::ExitOnLastWindow,
296            should_exit: false,
297            close_request: None,
298            restoration,
299            restoration_window_factories: HashMap::new(),
300            profiler: PerformanceProfiler::new(ProfilerMode::Normal),
301            hub: PerformanceHub::new(),
302            scheduler_counters: ApplicationSchedulerCounters::default(),
303        })
304    }
305
306    /// Adds an independent retained root. Native creation is deferred until a
307    /// desktop adapter reaches an active Winit event-loop callback.
308    pub fn open_window(
309        &mut self,
310        options: WindowOptions,
311        root: Widget,
312    ) -> Result<WindowHandle, WindowError> {
313        self.manager.open_window(options, root)
314    }
315
316    /// Adds an independent declarative root with its own window environment
317    /// and cancellation scope.
318    pub fn open_window_with(
319        &mut self,
320        options: WindowOptions,
321        build: impl FnMut(&mut BuildContext) -> Widget + 'static,
322    ) -> Result<WindowHandle, WindowError> {
323        self.manager.open_window_with(options, build)
324    }
325
326    /// Opens an auxiliary window with a stable restoration ID. A normal user
327    /// close removes this descriptor and its window-scoped values from the
328    /// next session; application shutdown preserves currently active
329    /// restorable windows.
330    pub fn open_restorable_window_with(
331        &mut self,
332        restoration_id: WindowRestorationId,
333        kind: impl Into<String>,
334        options: WindowOptions,
335        build: impl FnMut(&mut BuildContext) -> Widget + 'static,
336    ) -> Result<WindowHandle, WindowError> {
337        self.manager
338            .open_restorable_window_with(restoration_id, kind, options, build)
339    }
340
341    /// Registers an in-memory factory for one stable auxiliary window kind.
342    /// Builders remain ordinary closures and are never stored in a snapshot.
343    pub fn register_restorable_window_factory(
344        &mut self,
345        kind: impl Into<String>,
346        options: WindowOptions,
347        factory: impl Fn(&mut BuildContext) -> Widget + 'static,
348    ) -> Result<(), WindowError> {
349        let kind = kind.into();
350        if kind.trim().is_empty() || kind.chars().any(char::is_control) {
351            return Err(WindowError::Restoration(
352                "window factory kind must be a non-empty printable stable identifier".to_owned(),
353            ));
354        }
355        let factory: RestorableWindowFactory = Rc::new(move |context: &mut BuildContext| {
356            incular_core::hot_reload::call_with(&mut &factory, context)
357        });
358        self.restoration_window_factories
359            .insert(kind, (options, factory));
360        Ok(())
361    }
362
363    /// Restores all persisted auxiliary windows whose stable factories have
364    /// been registered. Call this after registration and before entering the
365    /// native runner; it therefore cannot flash a default auxiliary UI before
366    /// restore. Unknown kinds, unparseable ids, and duplicate descriptors are
367    /// skipped safely and counted in diagnostics; only a genuine window-open
368    /// failure aborts the remaining restores.
369    pub fn restore_restorable_windows(&mut self) -> Result<usize, WindowError> {
370        let Some(restoration) = self.restoration.clone() else {
371            return Ok(0);
372        };
373        let mut active = self
374            .registry
375            .borrow()
376            .slots
377            .iter()
378            .filter_map(|slot| slot.record.as_ref())
379            .filter_map(|record| record.restoration.as_ref())
380            .map(|metadata| metadata.id.as_key().as_str().to_owned())
381            .collect::<HashSet<_>>();
382        let descriptors = restoration.windows();
383        let mut restored = 0;
384        for descriptor in descriptors {
385            if !active.insert(descriptor.restoration_id.clone()) {
386                continue;
387            }
388            let restoration_id = match WindowRestorationId::new(descriptor.restoration_id) {
389                Ok(id) => id,
390                Err(_) => {
391                    restoration.note_skipped_window();
392                    continue;
393                }
394            };
395            let Some((options, factory)) = self
396                .restoration_window_factories
397                .get(&descriptor.kind)
398                .cloned()
399            else {
400                restoration.note_skipped_window();
401                continue;
402            };
403            let kind = descriptor.kind;
404            let factory = factory.clone();
405            self.manager
406                .open_restorable_window_with(restoration_id, kind, options, move |cx| {
407                    factory(cx)
408                })?;
409            restored += 1;
410        }
411        Ok(restored)
412    }
413
414    #[must_use]
415    pub const fn primary_window(&self) -> WindowId {
416        self.primary_window
417    }
418
419    #[must_use]
420    pub fn active_window_ids(&self) -> Vec<WindowId> {
421        self.registry.borrow().ids()
422    }
423
424    /// Returns a thread-safe command handle for one live Incular window.
425    #[must_use]
426    pub fn window_handle(&self, window_id: WindowId) -> Option<WindowHandle> {
427        self.manager.handle(window_id)
428    }
429
430    /// Effective retained cursor for the window's current hover position.
431    /// Native adapters use this after input and frame updates; applications
432    /// should normally express cursor intent through `MouseRegion` instead.
433    #[doc(hidden)]
434    #[must_use]
435    pub fn window_mouse_cursor(&self, window_id: WindowId) -> Option<MouseCursor> {
436        self.registry
437            .borrow()
438            .get(window_id)
439            .map(|record| record.runtime.effective_mouse_cursor())
440    }
441
442    /// Returns the default application-menu bridges mounted in one retained
443    /// window root. Desktop adapters bind them to their native menu delegate.
444    #[doc(hidden)]
445    #[must_use]
446    pub fn platform_menu_bindings(&self, window_id: WindowId) -> Vec<PlatformMenuBinding> {
447        self.registry
448            .borrow()
449            .get(window_id)
450            .map_or_else(Vec::new, |record| record.runtime.platform_menu_bindings())
451    }
452
453    /// Returns the latest backend capability snapshot for application-level
454    /// services. Before a native backend attaches, fields are `Unknown`.
455    #[must_use]
456    pub fn platform_capabilities(&self) -> PlatformCapabilities {
457        *self
458            .platform_capabilities
459            .read()
460            .expect("application capability snapshot lock")
461    }
462
463    /// Returns the application-scoped activation stream. Native open-file,
464    /// deep-link, reopen, secondary-launch, and global-shortcut events all use
465    /// this one ordered delivery path rather than a window input queue.
466    #[must_use]
467    pub fn activations(&self) -> ApplicationActivationService {
468        self.activations.clone()
469    }
470
471    /// Returns the application-scoped native global shortcut service.
472    #[must_use]
473    pub fn global_shortcuts(&self) -> GlobalShortcutService {
474        GlobalShortcutService::new(
475            self.global_shortcut_bridge.clone(),
476            self.platform_capabilities.clone(),
477        )
478    }
479
480    /// Returns the application-owned native shell service. Tray/status items,
481    /// notifications, and taskbar/Dock state are independent of retained widget
482    /// lifetime and may therefore be created before or after windows.
483    #[must_use]
484    pub fn application_shell(&self) -> ApplicationShellService {
485        self.application_shell.clone()
486    }
487
488    #[doc(hidden)]
489    pub fn take_native_application_shell_requests(&self) -> Vec<NativeApplicationShellRequest> {
490        self.application_shell.take_native_requests()
491    }
492
493    #[doc(hidden)]
494    pub fn complete_application_shell_request(&self, completion: NativeApplicationShellCompletion) {
495        self.application_shell.complete(completion);
496    }
497
498    #[doc(hidden)]
499    pub fn handle_application_shell_event(&self, event: NativeApplicationShellEvent) -> bool {
500        self.application_shell.handle_native_event(event)
501    }
502
503    /// Replaces the launch payload that the desktop runner publishes for this
504    /// process. This is useful for custom launchers which already classified
505    /// document or URL arguments and want to preserve those semantics.
506    pub fn set_launch_activation(&mut self, activation: LaunchActivation) {
507        self.launch_activation = activation;
508    }
509
510    #[doc(hidden)]
511    #[must_use]
512    pub fn launch_activation(&self) -> LaunchActivation {
513        self.launch_activation.clone()
514    }
515
516    /// Enables or disables optional single-instance forwarding. Incular never
517    /// enforces this policy unless the application explicitly supplies one.
518    pub fn set_single_instance_policy(&mut self, policy: Option<SingleInstancePolicy>) {
519        self.single_instance_policy = policy;
520    }
521
522    #[must_use]
523    pub fn single_instance_policy(&self) -> Option<&SingleInstancePolicy> {
524        self.single_instance_policy.as_ref()
525    }
526
527    /// Connects URL activations to an existing Router platform-route provider.
528    ///
529    /// The application supplies URL-to-route interpretation. This keeps deep
530    /// link parsing in the route layer and prevents desktop adapters from
531    /// acquiring application navigation policy.
532    #[must_use]
533    pub fn bridge_activation_routes(
534        &self,
535        provider: Rc<dyn RouteInformationProvider>,
536        map: impl Fn(&str) -> Option<RouteInformation> + 'static,
537    ) -> ActivationRouteBridge {
538        ActivationRouteBridge::new(&self.activations, provider, map)
539    }
540
541    /// Publishes a normalized activation from a native/application host. This
542    /// method is public only for platform embedders and deterministic tests;
543    /// normal application code observes [`Self::activations`] instead.
544    #[doc(hidden)]
545    pub fn handle_application_activation(&mut self, activation: ApplicationActivation) {
546        self.activations.publish(activation);
547    }
548
549    /// Replaces the backend's immutable connected-display snapshot.
550    ///
551    /// Display IDs are allocated/generationally managed by the native backend.
552    /// Replacing the snapshot immediately invalidates removed IDs at this API
553    /// boundary; a later display using the same slot must have a new generation.
554    /// Replaces the native backend's immutable display snapshot catalog.
555    ///
556    /// This is public for sibling platform adapters and headless/test hosts; UI
557    /// code should read displays through [`Application::displays`] or a
558    /// [`WindowHandle`](crate::WindowHandle), not manufacture native topology.
559    #[doc(hidden)]
560    pub fn publish_displays(
561        &mut self,
562        displays: impl IntoIterator<Item = DisplaySnapshot>,
563        primary: Option<DisplayId>,
564    ) {
565        self.display_catalog
566            .write()
567            .expect("display catalog lock")
568            .replace(displays, primary);
569    }
570
571    #[must_use]
572    pub fn displays(&self) -> Vec<DisplaySnapshot> {
573        self.display_catalog
574            .read()
575            .expect("display catalog lock")
576            .all()
577    }
578
579    #[must_use]
580    pub fn display(&self, id: DisplayId) -> Option<DisplaySnapshot> {
581        self.display_catalog
582            .read()
583            .expect("display catalog lock")
584            .get(id)
585    }
586
587    #[must_use]
588    pub fn primary_display(&self) -> Option<DisplaySnapshot> {
589        self.display_catalog
590            .read()
591            .expect("display catalog lock")
592            .primary()
593    }
594
595    #[must_use]
596    pub fn current_display(&self, window_id: WindowId) -> Option<DisplaySnapshot> {
597        let display_id = self
598            .window_handle(window_id)?
599            .observed_state()
600            .current_display?;
601        self.display(display_id)
602    }
603
604    /// Publishes one backend/session capability snapshot and applies it to all
605    /// currently live windows. Custom embedders may call this before entering
606    /// their native event loop.
607    pub fn set_platform_capabilities(&mut self, capabilities: PlatformCapabilities) {
608        *self
609            .platform_capabilities
610            .write()
611            .expect("application capability snapshot lock") = capabilities;
612        for slot in &self.registry.borrow().slots {
613            if let Some(record) = slot.record.as_ref() {
614                *record
615                    .capabilities
616                    .write()
617                    .expect("window capability snapshot lock") = capabilities;
618            }
619        }
620    }
621
622    /// Returns the capability snapshot associated with one live window.
623    #[must_use]
624    pub fn window_capabilities(&self, window_id: WindowId) -> Option<PlatformCapabilities> {
625        let registry = self.registry.borrow();
626        let record = registry.get(window_id)?;
627        Some(
628            *record
629                .capabilities
630                .read()
631                .expect("window capability snapshot lock"),
632        )
633    }
634
635    /// Overrides capabilities for one live window when native support depends
636    /// on that concrete surface/session rather than the application backend.
637    pub fn set_window_capabilities(
638        &mut self,
639        window_id: WindowId,
640        capabilities: PlatformCapabilities,
641    ) -> bool {
642        let registry = self.registry.borrow();
643        let Some(record) = registry.get(window_id) else {
644            return false;
645        };
646        *record
647            .capabilities
648            .write()
649            .expect("window capability snapshot lock") = capabilities;
650        true
651    }
652
653    /// Publishes the native adapter's resolved presentation for the visible
654    /// semantic transients in one retained window. Custom embedders should
655    /// replace the complete snapshot after each host-negotiation pass.
656    pub fn set_transient_presentations(
657        &mut self,
658        window_id: WindowId,
659        presentations: Vec<TransientPresentationResolution>,
660    ) -> bool {
661        self.with_window_mut(window_id, |record| {
662            let native = presentations
663                .iter()
664                .filter_map(|presentation| {
665                    (presentation.resolved == ResolvedTransientPresentation::Native)
666                        .then_some(presentation.id)
667                })
668                .collect::<Vec<_>>();
669            record.runtime.set_native_transient_presentations(native);
670            *record
671                .transient_presentations
672                .write()
673                .expect("transient presentation snapshot lock") = presentations;
674        })
675        .is_some()
676    }
677
678    /// Publishes the desktop bounds a confirmed native transient may use for
679    /// collision placement. Bounds are parent-view-local logical geometry.
680    pub fn set_native_transient_bounds(
681        &mut self,
682        window_id: WindowId,
683        bounds: Option<incular_core::Rect>,
684    ) -> bool {
685        self.with_window_mut(window_id, |record| {
686            record.runtime.set_native_transient_bounds(bounds)
687        })
688        .unwrap_or(false)
689    }
690
691    /// Development-only, read-only view of application windows.  Keeping this
692    /// extraction here means platform runners never need to reach into the
693    /// retained-window registry.
694    #[cfg(feature = "devtools")]
695    #[must_use]
696    pub fn devtools_windows(&self) -> Vec<incular_devtools_protocol::WindowSummary> {
697        self.active_window_ids()
698            .into_iter()
699            .filter_map(|id| {
700                self.window_diagnostics(id)
701                    .map(|window| incular_devtools_protocol::WindowSummary {
702                        id: incular_devtools_protocol::DevWindowId::new(
703                            u64::from(id.index()) + 1,
704                            u64::from(id.generation()),
705                        ),
706                        title: window.title,
707                        logical_size: [window.logical_size.width, window.logical_size.height],
708                        scale_factor: window.scale_factor,
709                    })
710            })
711            .collect()
712    }
713
714    /// Extracts one window's retained widget tree for DevTools.  The method
715    /// performs no layout, paint, or allocation proportional to the entire
716    /// application beyond the explicit bounded protocol snapshot.
717    #[cfg(feature = "devtools")]
718    pub fn devtools_widget_tree(
719        &self,
720        window: incular_devtools_protocol::DevWindowId,
721    ) -> Result<Vec<incular_devtools_protocol::TreeDelta>, incular_devtools_protocol::ErrorCode>
722    {
723        let id = WindowId::from_parts(
724            window.index().saturating_sub(1) as u32,
725            window.generation() as u32,
726        );
727        let registry = self.registry.borrow();
728        let record = registry
729            .get(id)
730            .ok_or(incular_devtools_protocol::ErrorCode::StaleId)?;
731        let root = record
732            .runtime
733            .tree()
734            .root()
735            .ok_or(incular_devtools_protocol::ErrorCode::UnknownId)?;
736        let (mut nodes, truncated) = record.runtime.tree().devtools_snapshot(root, false);
737        let root = nodes
738            .first()
739            .cloned()
740            .ok_or(incular_devtools_protocol::ErrorCode::UnknownId)?;
741        nodes.remove(0);
742        Ok(vec![incular_devtools_protocol::TreeDelta::Snapshot {
743            window,
744            root: Box::new(root),
745            nodes,
746            truncated,
747        }])
748    }
749
750    /// Returns curated details for a live DevTools element id, rejecting stale
751    /// ids before touching the retained tree.
752    #[cfg(feature = "devtools")]
753    pub fn devtools_node_details(
754        &self,
755        id: incular_devtools_protocol::DevWidgetId,
756    ) -> Result<incular_devtools_protocol::NodeDetails, incular_devtools_protocol::ErrorCode> {
757        for window in self.active_window_ids() {
758            let registry = self.registry.borrow();
759            let Some(record) = registry.get(window) else {
760                continue;
761            };
762            let tree = record.runtime.tree();
763            if let Some(element) = tree.devtools_resolve_id(id) {
764                let mut details = tree
765                    .devtools_node_details(
766                        element,
767                        id,
768                        incular_devtools_protocol::DevWindowId::new(
769                            u64::from(window.index()) + 1,
770                            u64::from(window.generation()),
771                        ),
772                    )
773                    .ok_or(incular_devtools_protocol::ErrorCode::StaleId)?;
774                let root = record.runtime.devtools_reactive_root();
775                details.consumed_signals = crate::devtools_registry::with_all(|signals| {
776                    signals
777                        .iter()
778                        .filter(|(_, registration)| {
779                            (registration.subscribers)().into_iter().any(
780                                |(subscriber_root, subscriber)| {
781                                    subscriber_root == root && subscriber == element
782                                },
783                            )
784                        })
785                        .map(|(signal_id, _)| {
786                            incular_devtools_protocol::DevSignalId::new(*signal_id, 1)
787                        })
788                        .collect()
789                });
790                return Ok(details);
791            }
792        }
793        Err(incular_devtools_protocol::ErrorCode::StaleId)
794    }
795
796    /// Applies a supported temporary property override to one live retained
797    /// node. Generational IDs make stale edits fail closed.
798    #[cfg(feature = "devtools")]
799    pub fn devtools_edit_property(
800        &mut self,
801        id: incular_devtools_protocol::DevWidgetId,
802        name: &str,
803        value: &incular_devtools_protocol::DebugValue,
804    ) -> bool {
805        let mut registry = self.registry.borrow_mut();
806        for slot in &mut registry.slots {
807            let Some(record) = slot.record.as_mut() else {
808                continue;
809            };
810            if record.runtime.devtools_edit_property(id, name, value) {
811                return true;
812            }
813        }
814        false
815    }
816
817    /// Restores one overridden property and forgets the override, so a later
818    /// application rebuild no longer re-applies it.
819    #[cfg(feature = "devtools")]
820    pub fn devtools_revert_property(
821        &mut self,
822        id: incular_devtools_protocol::DevWidgetId,
823        name: &str,
824        original: &incular_devtools_protocol::DebugValue,
825    ) -> bool {
826        let mut registry = self.registry.borrow_mut();
827        for slot in &mut registry.slots {
828            let Some(record) = slot.record.as_mut() else {
829                continue;
830            };
831            if record.runtime.devtools_revert_property(id, name, original) {
832                return true;
833            }
834        }
835        false
836    }
837
838    /// Reads the exact world-space bounds for the selected-widget overlay.
839    #[cfg(feature = "devtools")]
840    pub fn devtools_node_bounds(
841        &self,
842        id: incular_devtools_protocol::DevWidgetId,
843    ) -> Option<[f32; 4]> {
844        for window in self.active_window_ids() {
845            let registry = self.registry.borrow();
846            let Some(record) = registry.get(window) else {
847                continue;
848            };
849            let tree = record.runtime.tree();
850            if let Some(element) = tree.devtools_resolve_id(id) {
851                return tree.element_bounds(element).map(|bounds| {
852                    [
853                        bounds.origin.x,
854                        bounds.origin.y,
855                        bounds.size.width,
856                        bounds.size.height,
857                    ]
858                });
859            }
860        }
861        None
862    }
863
864    /// Read-only retained geometry for selected-node debug adornments.
865    #[cfg(feature = "devtools")]
866    pub fn devtools_node_overlay_geometry(
867        &self,
868        id: incular_devtools_protocol::DevWidgetId,
869    ) -> Option<incular_widgets::devtools::DevOverlayGeometry> {
870        for window in self.active_window_ids() {
871            let registry = self.registry.borrow();
872            let Some(record) = registry.get(window) else {
873                continue;
874            };
875            let tree = record.runtime.tree();
876            if let Some(element) = tree.devtools_resolve_id(id) {
877                return tree.devtools_overlay_geometry(element);
878            }
879        }
880        None
881    }
882
883    /// Bounded exact retained layout rectangles for one target window. The
884    /// platform adapter consumes these in its compositor-only overlay pass;
885    /// no widget, layout or semantics state changes as a consequence.
886    #[cfg(feature = "devtools")]
887    pub fn devtools_window_layout_bounds(&self, window: WindowId, limit: usize) -> Vec<[f32; 4]> {
888        self.registry
889            .borrow()
890            .get(window)
891            .map(|record| record.runtime.tree().devtools_layout_bounds(limit))
892            .unwrap_or_default()
893    }
894
895    #[cfg(feature = "devtools")]
896    pub fn devtools_window_semantics_bounds(
897        &self,
898        window: WindowId,
899        limit: usize,
900    ) -> Vec<[f32; 4]> {
901        self.registry
902            .borrow()
903            .get(window)
904            .map(|record| record.runtime.tree().devtools_semantics_bounds(limit))
905            .unwrap_or_default()
906    }
907
908    #[cfg(feature = "devtools")]
909    pub fn devtools_window_hit_regions(&self, window: WindowId, limit: usize) -> Vec<[f32; 4]> {
910        self.registry
911            .borrow()
912            .get(window)
913            .map(|record| record.runtime.tree().devtools_hit_regions(limit))
914            .unwrap_or_default()
915    }
916
917    #[cfg(feature = "devtools")]
918    pub fn devtools_window_scroll_viewports(
919        &self,
920        window: WindowId,
921        limit: usize,
922    ) -> Vec<[f32; 4]> {
923        self.registry
924            .borrow()
925            .get(window)
926            .map(|record| record.runtime.tree().devtools_scroll_viewports(limit))
927            .unwrap_or_default()
928    }
929
930    #[cfg(feature = "devtools")]
931    pub fn devtools_window_layer_bounds(&self, window: WindowId, limit: usize) -> Vec<[f32; 4]> {
932        self.registry
933            .borrow()
934            .get(window)
935            .map(|record| record.runtime.tree().devtools_layer_bounds(limit))
936            .unwrap_or_default()
937    }
938
939    /// Bounded existing per-node work counters for target-side phase flashes.
940    #[cfg(feature = "devtools")]
941    pub fn devtools_window_phase_nodes(
942        &self,
943        window: WindowId,
944        limit: usize,
945    ) -> Vec<incular_widgets::devtools::DevPhaseNode> {
946        self.registry
947            .borrow()
948            .get(window)
949            .map(|record| record.runtime.tree().devtools_phase_nodes(limit))
950            .unwrap_or_default()
951    }
952
953    /// Enables one bounded per-node Deep trace for the next frame of this
954    /// window. Normal/Performance profiler modes never call this, so the hot
955    /// path has only compile-time-gated instrumentation checks.
956    #[cfg(feature = "devtools")]
957    pub fn devtools_begin_deep_trace(&mut self, window: WindowId, max_events: usize) -> bool {
958        self.with_window_mut(window, |record| {
959            record.runtime.tree_mut().begin_deep_trace(max_events);
960        })
961        .is_some()
962    }
963
964    /// Takes the completed per-node trace without cloning its event buffer.
965    #[cfg(feature = "devtools")]
966    pub fn devtools_take_deep_trace(
967        &mut self,
968        window: WindowId,
969        frame: u64,
970    ) -> Option<incular_devtools_protocol::DeepFrameTrace> {
971        let (events, dropped_events) = self
972            .with_window_mut(window, |record| record.runtime.tree_mut().take_deep_trace())
973            .flatten()?;
974        Some(incular_devtools_protocol::DeepFrameTrace {
975            window: incular_devtools_protocol::DevWindowId::new(
976                u64::from(window.index()) + 1,
977                u64::from(window.generation()),
978            ),
979            frame,
980            truncated: dropped_events > 0,
981            dropped_events,
982            events,
983        })
984    }
985
986    /// Exact retained subtree bounds for the selected DevTools node, together
987    /// with its owning native window. Stale IDs return no result.
988    #[cfg(feature = "devtools")]
989    pub fn devtools_subtree_layout_bounds(
990        &self,
991        id: incular_devtools_protocol::DevWidgetId,
992        limit: usize,
993    ) -> Option<(WindowId, Vec<[f32; 4]>)> {
994        for window in self.active_window_ids() {
995            let registry = self.registry.borrow();
996            let Some(record) = registry.get(window) else {
997                continue;
998            };
999            let tree = record.runtime.tree();
1000            if let Some(element) = tree.devtools_resolve_id(id) {
1001                return Some((window, tree.devtools_subtree_layout_bounds(element, limit)));
1002            }
1003        }
1004        None
1005    }
1006
1007    /// Hit-tests a retained root for Select Widget mode without dispatching
1008    /// the pointer to application widgets.
1009    #[cfg(feature = "devtools")]
1010    pub fn devtools_hit_test(
1011        &self,
1012        window: WindowId,
1013        point: incular_core::Offset,
1014    ) -> Option<(incular_devtools_protocol::DevWidgetId, [f32; 4])> {
1015        let registry = self.registry.borrow();
1016        let record = registry.get(window)?;
1017        let tree = record.runtime.tree();
1018        let element = tree.devtools_deepest_at(point)?;
1019        let id = tree.devtools_id_for_element(element)?;
1020        let bounds = tree.element_bounds(element)?;
1021        Some((
1022            id,
1023            [
1024                bounds.origin.x,
1025                bounds.origin.y,
1026                bounds.size.width,
1027                bounds.size.height,
1028            ],
1029        ))
1030    }
1031
1032    /// Framework-owned memory/resource inventory.  This is deliberately a
1033    /// count of retained Incular objects, not a misleading attempt to inspect
1034    /// Rust's allocator or another crate's heap.
1035    #[cfg(feature = "devtools")]
1036    #[must_use]
1037    pub fn devtools_resource_counts(&self) -> incular_devtools_protocol::ResourceCounts {
1038        let mut counts = incular_devtools_protocol::ResourceCounts::default();
1039        for window in self.active_window_ids() {
1040            let registry = self.registry.borrow();
1041            let Some(record) = registry.get(window) else {
1042                continue;
1043            };
1044            let tree = record.runtime.tree();
1045            counts.elements += tree.element_count();
1046            counts.render_objects += tree.render_object_count();
1047            counts.layers += usize::try_from(tree.compositor_diagnostics().layers).unwrap_or(0);
1048            counts.semantics_nodes += tree.semantics().len();
1049            counts.tasks_active += record.runtime.runtime_diagnostics().active_tracked_tasks;
1050        }
1051        crate::devtools_registry::with_all(|signals| counts.signals = signals.len());
1052        counts
1053    }
1054
1055    /// Returns only signals which explicitly opted into DevTools visibility.
1056    /// Values are already reduced to bounded summaries at write time.
1057    #[cfg(feature = "devtools")]
1058    #[must_use]
1059    pub fn devtools_signals(&self) -> Vec<incular_devtools_protocol::SignalSummary> {
1060        crate::devtools_registry::with_all(|signals| {
1061            signals
1062                .iter()
1063                .map(|(id, registration)| {
1064                    let (_, last_write) = (registration.last_write)();
1065                    let writes = (registration.write_count)();
1066                    incular_devtools_protocol::SignalSummary {
1067                        id: incular_devtools_protocol::DevSignalId::new(*id, 1),
1068                        name: registration.name.clone(),
1069                        type_name: registration.type_name.to_owned(),
1070                        generation: writes,
1071                        write_count: writes,
1072                        subscriber_count: (registration.subscriber_count)(),
1073                        last_write_summary: last_write,
1074                        editable: registration.editable_kind.is_some(),
1075                    }
1076                })
1077                .collect()
1078        })
1079    }
1080
1081    /// Resolves actual live reactive dependencies for a DevTools-visible
1082    /// signal.  The registry stores only weak signal state and Element ids;
1083    /// this method resolves those ids against the matching live window tree,
1084    /// so unmounted elements are never retained or reported.
1085    #[cfg(feature = "devtools")]
1086    #[must_use]
1087    pub fn devtools_signal_subscribers(
1088        &self,
1089        signal: incular_devtools_protocol::DevSignalId,
1090    ) -> Vec<incular_devtools_protocol::SignalSubscriber> {
1091        let entries = crate::devtools_registry::with_all(|signals| {
1092            signals
1093                .iter()
1094                .find(|(id, _)| *id == signal.index())
1095                .and_then(|(_, registration)| {
1096                    (signal.generation() == 1).then(|| (registration.subscribers)())
1097                })
1098                .unwrap_or_default()
1099        });
1100        if entries.is_empty() {
1101            return Vec::new();
1102        }
1103
1104        let windows = self.active_window_ids();
1105        let registry = self.registry.borrow();
1106        windows
1107            .into_iter()
1108            .flat_map(|window| {
1109                let Some(record) = registry.get(window) else {
1110                    return Vec::new();
1111                };
1112                let root = record.runtime.devtools_reactive_root();
1113                let tree = record.runtime.tree();
1114                entries
1115                    .iter()
1116                    .filter(|(entry_root, _)| *entry_root == root)
1117                    .filter_map(|(_, element)| {
1118                        tree.devtools_id_for_element(*element).map(|dev_id| {
1119                            incular_devtools_protocol::SignalSubscriber {
1120                                signal,
1121                                element: dev_id,
1122                                path: tree.devtools_element_path(*element),
1123                            }
1124                        })
1125                    })
1126                    .collect::<Vec<_>>()
1127            })
1128            .collect()
1129    }
1130
1131    /// Applies an explicitly opted-in typed DevTools edit on the UI thread.
1132    /// The conversion and the normal `Signal::set` invalidation both live in
1133    /// the signal registration; unsupported types are rejected.
1134    #[cfg(feature = "devtools")]
1135    pub fn devtools_edit_signal(
1136        &self,
1137        signal: incular_devtools_protocol::DevSignalId,
1138        value: &incular_devtools_protocol::EditableValue,
1139    ) -> bool {
1140        if signal.generation() != 1 {
1141            return false;
1142        }
1143        crate::devtools_registry::with_all(|signals| {
1144            signals
1145                .iter()
1146                .find(|(id, registration)| {
1147                    *id == signal.index() && registration.editable_kind.is_some()
1148                })
1149                .is_some_and(|(_, registration)| (registration.apply_edit)(value))
1150        })
1151    }
1152
1153    #[must_use]
1154    pub fn contains_window(&self, window_id: WindowId) -> bool {
1155        self.registry.borrow().contains(window_id)
1156    }
1157
1158    /// Visible transient surfaces belonging to `window_id` after its latest
1159    /// retained layout. Geometry is window-local logical space and therefore
1160    /// remains anchored when the native parent moves; the platform adapter is
1161    /// responsible only for mapping that geometry into desktop coordinates.
1162    #[must_use]
1163    pub fn transient_surfaces(
1164        &self,
1165        window_id: WindowId,
1166    ) -> Vec<incular_widgets::TransientSurfaceSnapshot> {
1167        self.registry
1168            .borrow()
1169            .get(window_id)
1170            .map_or_else(Vec::new, |record| record.runtime.transient_surfaces())
1171    }
1172
1173    pub fn set_last_window_policy(&mut self, policy: LastWindowPolicy) {
1174        self.last_window_policy = policy;
1175    }
1176
1177    #[must_use]
1178    pub const fn last_window_policy(&self) -> LastWindowPolicy {
1179        self.last_window_policy
1180    }
1181
1182    /// Installs the application policy used for a native close request. The
1183    /// callback returns `true` to accept closing and `false` to keep the
1184    /// window alive (for example, an unsaved-work dialog flow).
1185    pub fn on_close_request(&mut self, callback: impl FnMut(WindowId) -> bool + 'static) {
1186        self.close_request = Some(Box::new(callback));
1187    }
1188
1189    pub fn set_wake_handler(&mut self, wake: Arc<dyn RuntimeWake>) {
1190        self.scheduler.borrow_mut().set_wake(wake.clone());
1191        self.manager.bridge.set_wake(wake.clone());
1192        self.file_dialog_bridge.set_wake(wake.clone());
1193        self.global_shortcut_bridge.set_wake(wake.clone());
1194        self.application_shell.set_wake(wake.clone());
1195        self.simulation_bridge.set_wake(wake);
1196    }
1197
1198    /// Returns a cloneable, in-process controller for the primary window.
1199    /// Simulation commands are serviced by the native event loop and never
1200    /// synthesize OS-level mouse or keyboard input.
1201    #[must_use]
1202    pub fn simulation(&self) -> Simulation {
1203        Simulation::new(self.simulation_bridge.clone(), self.primary_window)
1204    }
1205
1206    pub(crate) fn with_window_mut<R>(
1207        &mut self,
1208        window_id: WindowId,
1209        callback: impl FnOnce(&mut WindowRecord) -> R,
1210    ) -> Option<R> {
1211        let mut record = self.registry.borrow_mut().take(window_id)?;
1212        let result = callback(&mut record);
1213        // `callback` may have opened a different window through BuildContext,
1214        // so borrow the registry only after it returns.
1215        // The root being serviced remains generationally reserved throughout.
1216        self.registry.borrow_mut().restore(window_id, record);
1217        Some(result)
1218    }
1219    /// Starts application-scoped Tokio work. It is not cancelled when any
1220    /// individual window closes.
1221    pub fn spawn<F, T>(&self, future: F) -> Task<T>
1222    where
1223        F: std::future::Future<Output = T> + Send + 'static,
1224        T: Send + 'static,
1225    {
1226        tasks::TaskScheduler::spawner(&self.scheduler).spawn(future)
1227    }
1228
1229    pub fn spawn_blocking<T>(
1230        &self,
1231        work: impl FnOnce() -> T + Send + 'static,
1232        complete: impl FnOnce(Result<T, TaskFailure>, &mut Runtime) + 'static,
1233    ) -> TaskHandle
1234    where
1235        T: Send + 'static,
1236    {
1237        self.scheduler.borrow_mut().spawn_blocking(work, complete)
1238    }
1239
1240    /// Queues a UI callback for the primary window. For work targeting another
1241    /// root, capture that root's `BuildContext` dispatcher instead.
1242    pub fn dispatch(&self, callback: impl FnOnce(&mut Runtime) + Send + 'static) {
1243        self.scheduler
1244            .borrow()
1245            .dispatcher_for(Some(self.primary_window))
1246            .dispatch(callback);
1247    }
1248
1249    pub fn spawn_into<F, T>(
1250        &self,
1251        future: F,
1252        complete: impl FnOnce(Result<T, TaskFailure>, &mut Runtime) + 'static,
1253    ) -> TaskHandle
1254    where
1255        F: std::future::Future<Output = T> + Send + 'static,
1256        T: Send + 'static,
1257    {
1258        tasks::TaskScheduler::spawner(&self.scheduler).spawn_into(future, complete)
1259    }
1260
1261    #[must_use]
1262    #[cfg(not(target_arch = "wasm32"))]
1263    pub fn tokio_handle(&self) -> TokioHandle {
1264        self.scheduler.borrow().tokio_handle()
1265    }
1266
1267    /// Drains at most one bounded Tokio UI turn, routing each completion to
1268    /// the window scope that created it. Closed/reused IDs discard stale work.
1269    pub fn process_runtime_work_at(&mut self, _now: Instant) {
1270        let work = self.scheduler.borrow_mut().take_turn();
1271        for item in work {
1272            let requested_window = match &item {
1273                tasks::UiWork::Dispatch { window_id, .. } => *window_id,
1274                tasks::UiWork::Task(pending) => pending.control.window_id(),
1275            };
1276            // Application-scoped work (including restoration write completion)
1277            // is not owned by a particular root. If the original primary has
1278            // closed while another window remains, deliver it to that live UI
1279            // turn rather than discarding persistence bookkeeping.
1280            let window_id = requested_window.or_else(|| {
1281                if self.contains_window(self.primary_window) {
1282                    Some(self.primary_window)
1283                } else {
1284                    self.active_window_ids().into_iter().next()
1285                }
1286            });
1287            let Some(window_id) = window_id else {
1288                self.discard_stale_ui_work(item);
1289                continue;
1290            };
1291            let record = self.registry.borrow_mut().take(window_id);
1292            if let Some(mut record) = record {
1293                record.runtime.process_ui_work(&self.scheduler, item);
1294                self.registry.borrow_mut().restore(window_id, record);
1295            } else {
1296                self.discard_stale_ui_work(item);
1297            }
1298        }
1299        self.drain_window_commands();
1300        self.drain_native_request_cancellations();
1301        self.drain_file_dialog_requests();
1302        self.drain_file_dialog_cancellations();
1303        self.drain_global_shortcut_requests();
1304        self.process_simulation_requests();
1305    }
1306
1307    pub fn process_runtime_work(&mut self) {
1308        self.process_runtime_work_at(Instant::now());
1309    }
1310
1311    fn discard_stale_ui_work(&mut self, work: tasks::UiWork) {
1312        if let tasks::UiWork::Task(pending) = work {
1313            self.scheduler.borrow().detach_scope(&pending.control);
1314            (pending.discard)();
1315            self.scheduler
1316                .borrow_mut()
1317                .record_discard(pending.blocking, true);
1318        }
1319    }
1320
1321    /// Routes external platform transfer data to one retained window and
1322    /// returns the operation requested by the currently hit-tested target.
1323    /// Native adapters with richer drag protocols can feed this response back
1324    /// to the source; adapters such as Winit file-drop may simply ignore it.
1325    #[must_use]
1326    pub fn handle_external_drag_event(
1327        &mut self,
1328        window_id: WindowId,
1329        event: ExternalDragEvent,
1330    ) -> ExternalDragResponse {
1331        self.with_window_mut(window_id, |record| {
1332            record.input_events = record.input_events.wrapping_add(1);
1333            record.runtime.handle_external_drag(event)
1334        })
1335        .unwrap_or_default()
1336    }
1337
1338    /// Routes a normalized event to exactly one retained root.
1339    pub fn handle_window_event(&mut self, event: WindowEvent) {
1340        let window_id = event.window_id;
1341        match event.kind {
1342            WindowEventKind::Platform(PlatformEvent::CloseRequested) => {
1343                self.request_close(window_id);
1344            }
1345            WindowEventKind::Platform(PlatformEvent::Metrics(metrics)) => {
1346                let _ = self.with_window_mut(window_id, |record| {
1347                    if record.metrics != metrics {
1348                        record.metrics = metrics;
1349                        record.surface_generation = record
1350                            .surface_generation
1351                            .checked_add(1)
1352                            .expect("runtime surface generation exhausted");
1353                        record.runtime.update_window_metrics(metrics);
1354                    }
1355                });
1356                self.manager.sync_restorable_windows(true);
1357            }
1358            WindowEventKind::Platform(PlatformEvent::Environment(environment)) => {
1359                let _ = self.with_window_mut(window_id, |record| {
1360                    record.runtime.set_environment(environment);
1361                });
1362            }
1363            WindowEventKind::Platform(PlatformEvent::Input(input)) => {
1364                let _ = self.with_window_mut(window_id, |record| {
1365                    record.input_events = record.input_events.wrapping_add(1);
1366                    let _ = record.runtime.handle_input(input);
1367                });
1368            }
1369            WindowEventKind::Platform(PlatformEvent::TextInputAction(action)) => {
1370                let _ = self.with_window_mut(window_id, |record| {
1371                    record.input_events = record.input_events.wrapping_add(1);
1372                    let _ = record.runtime.handle_text_input_action(action);
1373                });
1374            }
1375            WindowEventKind::Platform(PlatformEvent::Lifecycle(lifecycle)) => {
1376                self.handle_application_lifecycle(lifecycle);
1377            }
1378            WindowEventKind::Platform(PlatformEvent::ExternalDrag(event)) => {
1379                let _ = self.handle_external_drag_event(window_id, event);
1380            }
1381            WindowEventKind::Lifecycle(lifecycle) => {
1382                let _ = self.with_window_mut(window_id, |record| {
1383                    record.lifecycle = lifecycle;
1384                    match lifecycle {
1385                        WindowLifecycle::Focused => {
1386                            record.native_focused = true;
1387                            let mut environment = record.runtime.environment();
1388                            environment.window_focused = true;
1389                            record.runtime.set_environment(environment);
1390                        }
1391                        WindowLifecycle::Unfocused => {
1392                            record.native_focused = false;
1393                            record.runtime.dismiss_transients(
1394                                incular_widgets::TransientDismissReason::ParentDeactivated,
1395                            );
1396                            let mut environment = record.runtime.environment();
1397                            environment.window_focused = false;
1398                            record.runtime.set_environment(environment);
1399                        }
1400                        WindowLifecycle::Visible
1401                        | WindowLifecycle::Hidden
1402                        | WindowLifecycle::Creating
1403                        | WindowLifecycle::Closing
1404                        | WindowLifecycle::Closed => {}
1405                    }
1406                });
1407            }
1408            WindowEventKind::RedrawRequested => {}
1409            WindowEventKind::StateChanged(state) => {
1410                let _ = self.with_window_mut(window_id, |record| {
1411                    *record
1412                        .observed_state
1413                        .write()
1414                        .expect("window observed-state snapshot lock") = state;
1415                });
1416            }
1417        }
1418    }
1419
1420    /// Applies one genuine application-level lifecycle transition to every
1421    /// retained window root. Window focus remains a separate per-window
1422    /// lifecycle channel and never synthesizes these application states.
1423    pub fn handle_application_lifecycle(&mut self, lifecycle: PlatformLifecycle) {
1424        self.manager.application_lifecycle.set(match lifecycle {
1425            PlatformLifecycle::Active | PlatformLifecycle::Resumed => {
1426                crate::application_types::ApplicationLifecycle::Active
1427            }
1428            PlatformLifecycle::Inactive => crate::application_types::ApplicationLifecycle::Inactive,
1429            PlatformLifecycle::Suspended => {
1430                crate::application_types::ApplicationLifecycle::Suspended
1431            }
1432            PlatformLifecycle::Stopping => crate::application_types::ApplicationLifecycle::Stopping,
1433        });
1434        if matches!(lifecycle, PlatformLifecycle::Stopping) {
1435            self.shutdown();
1436            return;
1437        }
1438        let ids = self.active_window_ids();
1439        for id in ids {
1440            let _ = self.with_window_mut(id, |record| {
1441                let _ = record
1442                    .runtime
1443                    .handle_platform_event(PlatformEvent::Lifecycle(lifecycle));
1444            });
1445        }
1446        if matches!(lifecycle, PlatformLifecycle::Suspended) {
1447            self.flush_restoration_and_wait();
1448        }
1449    }
1450
1451    /// Records a backend failure for a user-driven operation that does not
1452    /// have an explicit result consumer (for example a `WindowDragRegion`).
1453    pub fn record_platform_operation_error(
1454        &mut self,
1455        window_id: WindowId,
1456        error: PlatformOperationError,
1457    ) -> bool {
1458        self.with_window_mut(window_id, |record| {
1459            record.last_platform_error = Some(error);
1460        })
1461        .is_some()
1462    }
1463
1464    pub fn set_window_environment(
1465        &mut self,
1466        window_id: WindowId,
1467        environment: RuntimeEnvironment,
1468    ) -> bool {
1469        self.with_window_mut(window_id, |record| {
1470            record.runtime.set_environment(environment)
1471        })
1472        .unwrap_or(false)
1473    }
1474
1475    /// Installs a platform clipboard for one window's input/editing bridge.
1476    /// The clipboard service itself may be application-wide; the runtime keeps
1477    /// the mutable adapter at the window input boundary.
1478    pub fn set_window_clipboard(&mut self, window_id: WindowId, clipboard: Box<dyn Clipboard>) {
1479        let _ = self.with_window_mut(window_id, |record| {
1480            record.runtime.set_clipboard(clipboard);
1481        });
1482    }
1483
1484    /// Drains text-input commands for one native window. The caller applies
1485    /// them on that window's platform/UI thread.
1486    pub fn take_window_text_input_commands(
1487        &mut self,
1488        window_id: WindowId,
1489    ) -> Vec<TextInputCommand> {
1490        self.with_window_mut(window_id, |record| {
1491            record.runtime.take_text_input_commands()
1492        })
1493        .unwrap_or_default()
1494    }
1495
1496    #[must_use]
1497    pub fn frame_requested(&self, window_id: WindowId) -> bool {
1498        self.registry
1499            .borrow()
1500            .slots
1501            .get(window_id.index() as usize)
1502            .filter(|slot| slot.generation == window_id.generation())
1503            .and_then(|slot| slot.record.as_ref())
1504            .is_some_and(|record| record.runtime.frame_requested())
1505    }
1506
1507    pub fn note_frame_requested(&mut self, window_id: WindowId) {
1508        scheduler_counters::REDRAW_REQUESTS.fetch_add(1, Ordering::Relaxed);
1509        self.scheduler_counters.redraw_requests =
1510            self.scheduler_counters.redraw_requests.wrapping_add(1);
1511        let _ = self.with_window_mut(window_id, |record| {
1512            record.requested_frames = record.requested_frames.wrapping_add(1);
1513        });
1514    }
1515
1516    /// Records one UI-relevant runtime wake (async completion or message).
1517    /// Wakes that mutate no visible state must not produce redraws; the idle
1518    /// contract test relies on these counters staying independent.
1519    pub fn note_runtime_wake(&mut self) {
1520        scheduler_counters::RUNTIME_WAKES.fetch_add(1, Ordering::Relaxed);
1521        self.scheduler_counters.runtime_wakes =
1522            self.scheduler_counters.runtime_wakes.wrapping_add(1);
1523    }
1524
1525    pub fn run_window_frame_at(
1526        &mut self,
1527        window_id: WindowId,
1528        constraints: Constraints,
1529        now: Instant,
1530    ) -> Result<Option<(DisplayList, FrameStats)>, TreeError> {
1531        let mut sensitivity_update = None;
1532        let mut content_size_request = None;
1533        let outcome = self
1534            .with_window_mut(window_id, |record| {
1535                if record.metrics.physical_size.is_zero() {
1536                    record.skipped_frames = record.skipped_frames.wrapping_add(1);
1537                    scheduler_counters::FRAMES_SKIPPED.fetch_add(1, Ordering::Relaxed);
1538                    Ok(None)
1539                } else {
1540                    let frame_constraints =
1541                        ContentSizeCoordinator::layout_constraints(&record.options, constraints);
1542                    record
1543                        .runtime
1544                        .run_frame_at(frame_constraints, now)
1545                        .map(|(list, stats)| {
1546                            let sensitivity = record.runtime.content_sensitivity();
1547                            if record.last_content_sensitivity != Some(sensitivity) {
1548                                record.last_content_sensitivity = Some(sensitivity);
1549                                sensitivity_update = Some(sensitivity);
1550                            }
1551                            content_size_request = record.content_sizing.reconcile(
1552                                &record.options,
1553                                record.metrics,
1554                                record.runtime.tree().root_layout_size(),
1555                            );
1556                            record.last_frame = FrameRecord {
1557                                frame: 0,
1558                                timings: stats.timings,
1559                                work: FrameWork {
1560                                    updated_elements: stats.updated_elements as u64,
1561                                    rebuilt_elements: stats.rebuilt_elements,
1562                                    laid_out_render_objects: stats.laid_out_render_objects,
1563                                    repainted_render_objects: stats.repainted_render_objects,
1564                                    composited_layers: stats.composited,
1565                                    active_animations: stats.active_animations,
1566                                    display_list_commands: stats.display_list_commands,
1567                                    requested_another_frame: stats.requested_another_frame,
1568                                },
1569                                over_budget: false,
1570                            };
1571                            Some((list, stats))
1572                        })
1573                }
1574            })
1575            .unwrap_or(Ok(None));
1576        if let Some(size) = content_size_request {
1577            self.native_commands
1578                .borrow_mut()
1579                .push_back(NativeWindowCommand::Operate(WindowCommand::new(
1580                    window_id,
1581                    WindowOperation::SetLogicalSize(size),
1582                )));
1583        }
1584        if let Some(sensitivity) = sensitivity_update {
1585            self.native_commands
1586                .borrow_mut()
1587                .push_back(NativeWindowCommand::Operate(WindowCommand::new(
1588                    window_id,
1589                    WindowOperation::SetContentSensitivity(sensitivity),
1590                )));
1591        }
1592        if matches!(outcome, Ok(None)) && self.contains_window(window_id) {
1593            self.scheduler_counters.frames_skipped =
1594                self.scheduler_counters.frames_skipped.wrapping_add(1);
1595        }
1596        if matches!(outcome, Ok(Some(_))) {
1597            scheduler_counters::FRAMES_STARTED.fetch_add(1, Ordering::Relaxed);
1598            self.scheduler_counters.frames_started =
1599                self.scheduler_counters.frames_started.wrapping_add(1);
1600        }
1601        outcome
1602    }
1603
1604    /// Projects this window's retained semantics through its window-local
1605    /// AccessKit bridge. Native adapters call this on the UI/event-loop thread
1606    /// only after AccessKit activation; a changed semantic revision produces a
1607    /// full or incremental tree update while ordinary paint/compositor frames
1608    /// produce no work.
1609    pub fn sync_accessibility(
1610        &mut self,
1611        window_id: WindowId,
1612        projection: &mut AccessKitProjection,
1613    ) -> Option<NativeAccessibilityUpdate> {
1614        let update = self
1615            .with_window_mut(window_id, |record| {
1616                projection.sync(
1617                    record.runtime.tree().semantics(),
1618                    record.metrics.scale_factor,
1619                )
1620            })
1621            .flatten();
1622        let diagnostics = projection.diagnostics();
1623        let _ = self.with_window_mut(window_id, |record| {
1624            record.accessibility = diagnostics;
1625        });
1626        update
1627    }
1628
1629    /// Projects this window's retained semantics into stable-ID node updates
1630    /// for hosts that present them without AccessKit, such as a browser page.
1631    /// Bounds stay in logical pixels.
1632    pub fn sync_semantics_projection(
1633        &mut self,
1634        window_id: WindowId,
1635        projection: &mut MobileAccessibilityProjection,
1636    ) -> Option<MobileAccessibilityUpdate> {
1637        let update = self
1638            .with_window_mut(window_id, |record| {
1639                projection.sync(record.runtime.tree().semantics())
1640            })
1641            .flatten();
1642        let diagnostics = projection.diagnostics();
1643        let _ = self.with_window_mut(window_id, |record| {
1644            record.accessibility = diagnostics;
1645        });
1646        update
1647    }
1648
1649    /// Receives an already validated native semantic request for exactly one
1650    /// window. It uses the ordinary retained runtime action dispatcher, so
1651    /// native callbacks never mutate widget/controller state directly.
1652    pub fn dispatch_accessibility_action(
1653        &mut self,
1654        window_id: WindowId,
1655        request: SemanticActionRequest,
1656    ) -> bool {
1657        self.with_window_mut(window_id, |record| {
1658            record
1659                .runtime
1660                .dispatch_semantic_action(request.node, request.action)
1661        })
1662        .unwrap_or(false)
1663    }
1664
1665    /// Turns one window's per-frame semantics pass on or off. Native hosts
1666    /// enable it when an assistive technology requests the tree; enabling
1667    /// schedules a frame so the first native update is complete.
1668    pub fn set_semantics_enabled(&mut self, window_id: WindowId, enabled: bool) {
1669        let _ = self.with_window_mut(window_id, |record| {
1670            let runtime = &mut record.runtime;
1671            if runtime.tree().semantics_enabled() != enabled {
1672                runtime.tree_mut().set_semantics_enabled(enabled);
1673                if enabled {
1674                    runtime.request_frame();
1675                }
1676            }
1677        });
1678    }
1679
1680    /// Updates one window's value-free bridge diagnostics after a lifecycle or
1681    /// action event that did not emit a tree update.
1682    pub fn set_accessibility_diagnostics(
1683        &mut self,
1684        window_id: WindowId,
1685        diagnostics: AccessibilityDiagnostics,
1686    ) {
1687        let _ = self.with_window_mut(window_id, |record| {
1688            record.accessibility = diagnostics;
1689        });
1690    }
1691
1692    /// Selects how much profiling state is retained. Normal keeps only
1693    /// counters and the latest frame; higher modes retain bounded history.
1694    pub fn set_profiler_mode(&mut self, mode: ProfilerMode) {
1695        self.profiler.set_mode(mode);
1696    }
1697
1698    /// Applies the DevTools animation speed to every live window. This is a
1699    /// retained animation-clock setting, not a scheduler or Tokio setting.
1700    pub fn set_animation_time_scale(&mut self, scale: f32) {
1701        for window in self.active_window_ids() {
1702            let _ = self.with_window_mut(window, |record| {
1703                record.runtime.set_animation_time_scale(scale);
1704            });
1705        }
1706    }
1707
1708    /// Rebuilds every retained builder in every live window on the next
1709    /// frame. Element identity, focus, scroll offsets and signal values are
1710    /// kept; only widget descriptions and the callbacks they carry are
1711    /// replaced. Use this after application code was swapped underneath a
1712    /// running tree.
1713    #[cfg(feature = "hot-reload")]
1714    pub fn reassemble(&mut self) {
1715        for window in self.active_window_ids() {
1716            let _ = self.with_window_mut(window, |record| record.runtime.reassemble());
1717        }
1718    }
1719
1720    /// Loads a hot patch and [reassembles](Self::reassemble) every window.
1721    /// A patch that fails to load leaves the running code untouched.
1722    ///
1723    /// # Safety
1724    ///
1725    /// The patch must have been built against the executable image of this
1726    /// exact process; see [`HotPatch::apply`](crate::HotPatch::apply).
1727    #[cfg(feature = "hot-reload")]
1728    pub unsafe fn apply_hot_patch(
1729        &mut self,
1730        patch: crate::HotPatch,
1731    ) -> Result<(), crate::HotPatchError> {
1732        // SAFETY: forwarded caller contract.
1733        unsafe { patch.apply() }?;
1734        self.reassemble();
1735        Ok(())
1736    }
1737
1738    #[must_use]
1739    pub const fn profiler_mode(&self) -> ProfilerMode {
1740        self.profiler.mode()
1741    }
1742
1743    /// Derives frame budgets from an actual refresh rate (Hz). `None` clears.
1744    pub fn set_refresh_rate_hz(&mut self, hz: Option<f32>) {
1745        self.profiler.set_refresh_rate_hz(hz);
1746    }
1747
1748    /// Merges renderer-reported metrics into one window's latest record and
1749    /// feeds the application profiler history with a complete frame sample.
1750    pub fn note_render_metrics(
1751        &mut self,
1752        window_id: WindowId,
1753        mut render: RenderFrameMetrics,
1754        gpu: Option<GpuSample>,
1755    ) {
1756        let frame_id = self.profiler.next_frame_id();
1757        render.frame = frame_id;
1758        let mut completed = None;
1759        {
1760            let mut registry = self.registry.borrow_mut();
1761            if let Some(slot) = registry.slots.get_mut(window_id.index() as usize)
1762                && slot.generation == window_id.generation()
1763                && let Some(record) = slot.record.as_mut()
1764            {
1765                record.render_metrics = render;
1766                record.gpu_sample = gpu;
1767                record.last_frame.frame = frame_id;
1768                // Renderer prepare/encode/submit completes the CPU picture.
1769                let mut timings = record.last_frame.timings;
1770                timings.cpu_total = timings
1771                    .cpu_total
1772                    .saturating_add(render.prepare_us)
1773                    .saturating_add(render.encode_us)
1774                    .saturating_add(render.submit_us);
1775                record.last_frame.timings = timings;
1776                completed = Some(record.last_frame);
1777            }
1778        }
1779        if let Some(record) = completed {
1780            self.profiler.record(record);
1781        }
1782        // Throttled publish for observers (debug overlays). Production frames
1783        // with no observer and Normal mode skip the snapshot build entirely.
1784        if self.hub.observed()
1785            && self.profiler.mode() >= ProfilerMode::Diagnostic
1786            && self.hub.publish_due(std::time::Duration::from_millis(200))
1787        {
1788            self.hub.publish(self.performance_snapshot());
1789        }
1790    }
1791
1792    /// Shared handle to the observable performance snapshot. Overlay builders
1793    /// read [`PerformanceHub::version`] so only they rebuild on publish.
1794    #[must_use]
1795    pub const fn performance_hub(&self) -> &PerformanceHub {
1796        &self.hub
1797    }
1798
1799    /// Installs the debug performance overlay into `window_id`.
1800    ///
1801    /// The application tree must contain a widget keyed with
1802    /// [`PERFORMANCE_OVERLAY_KEY`]; that placeholder element is replaced by a
1803    /// repaint-contained overlay whose builder re-runs **only** when the hub
1804    /// publishes, so measured widget work is unaffected. Requires a profiler
1805    /// mode of [`ProfilerMode::Diagnostic`] or higher to observe data.
1806    pub fn install_performance_overlay(&mut self, window_id: WindowId) -> Result<(), TreeError> {
1807        let hub = self.hub.clone();
1808        let installed = self.with_window_mut(window_id, |record| {
1809            let tree = record.runtime.tree();
1810            let Some(target) = tree.element_with_key(&Key::from(PERFORMANCE_OVERLAY_KEY)) else {
1811                return Err(TreeError::MissingElement(
1812                    tree.root().expect("mounted window root"),
1813                ));
1814            };
1815            record.runtime.register_builder(target, move || {
1816                hub.set_observed(true);
1817                // Reading the version subscribes this element alone.
1818                let _version = hub.version();
1819                overlay_widget(&hub.snapshot())
1820            })?;
1821            Ok(())
1822        });
1823        match installed {
1824            Some(result) => result,
1825            None => Err(TreeError::WindowUnknown),
1826        }
1827    }
1828
1829    #[must_use]
1830    pub fn profiler_history(&self) -> &FrameHistory {
1831        self.profiler.history()
1832    }
1833
1834    /// Builds a complete read-only performance view. This allocates and is
1835    /// intended for diagnostics tooling, the debug overlay, and JSON export —
1836    /// never for the per-frame hot path.
1837    #[must_use]
1838    pub fn performance_snapshot(&self) -> PerformanceSnapshot {
1839        let mut windows = Vec::new();
1840        let registry = self.registry.borrow();
1841        for slot in &registry.slots {
1842            let Some(record) = slot.record.as_ref() else {
1843                continue;
1844            };
1845            windows.push(WindowPerformance {
1846                requested_frames: record.requested_frames,
1847                presented_frames: record.presented_frames,
1848                skipped_frames: record.skipped_frames,
1849                latest: Some(record.last_frame),
1850                render: record.render_metrics,
1851                gpu: record.gpu_sample,
1852            });
1853        }
1854        // Widget/text totals come from the primary window's retained tree;
1855        // accessibility counters merge across every live window adapter.
1856        let mut widgets = WidgetWorkSnapshot::default();
1857        let mut text = TextCacheSnapshot::default();
1858        let mut accessibility = AccessibilitySnapshot::default();
1859        for slot in &registry.slots {
1860            let Some(record) = slot.record.as_ref() else {
1861                continue;
1862            };
1863            let tree = record.runtime.tree();
1864            if widgets.elements_total == 0 {
1865                let diagnostics = tree.diagnostics();
1866                widgets = WidgetWorkSnapshot {
1867                    mounts: diagnostics.mounts,
1868                    unmounts: diagnostics.unmounts,
1869                    rebuilds: diagnostics.rebuilds,
1870                    layouts: diagnostics.layouts,
1871                    paints: diagnostics.paints,
1872                    composites: diagnostics.composites,
1873                    animation_ticks: diagnostics.animation_ticks,
1874                    scroll_offset_updates: diagnostics.scroll_offset_updates,
1875                    reconciliation_fast_paths: diagnostics.reconciliation_fast_paths,
1876                    layout_cache_hits: diagnostics.layout_cache_hits,
1877                    display_lists_reused: diagnostics.display_lists_reused,
1878                    compositor_only_updates: diagnostics.compositor_only_updates,
1879                    lazy_layouts: diagnostics.lazy_layouts,
1880                    items_built: diagnostics.items_built,
1881                    items_reused: diagnostics.items_reused,
1882                    child_list_scans: diagnostics.child_list_scans,
1883                    identical_child_bailouts: diagnostics.identical_child_bailouts,
1884                    elements_created: diagnostics.elements_created,
1885                    elements_removed: diagnostics.elements_removed,
1886                    elements_moved: diagnostics.elements_moved,
1887                    dirty_requests: diagnostics.dirty_requests,
1888                    dirty_queue_deduplicated: diagnostics.dirty_queue_deduplicated,
1889                    elements_total: tree.element_count(),
1890                    render_objects_total: tree.render_object_count(),
1891                    layers_total: usize::try_from(tree.compositor_diagnostics().layers)
1892                        .unwrap_or(0),
1893                };
1894                let text_diagnostics = tree.text_diagnostics();
1895                text = TextCacheSnapshot {
1896                    layouts_requested: text_diagnostics.layouts_requested,
1897                    cache_hits: text_diagnostics.cache_hits,
1898                    cache_misses: text_diagnostics.cache_misses,
1899                    paragraphs_reshaped: text_diagnostics.paragraphs_reshaped,
1900                    parley_layouts_reused: text_diagnostics.parley_layouts_reused,
1901                    documents_composed: text_diagnostics.documents_composed,
1902                };
1903            }
1904            accessibility.nodes_published += record.accessibility.nodes_published;
1905            accessibility.updates_skipped_unchanged +=
1906                record.accessibility.semantic_updates_skipped_unchanged;
1907        }
1908        let mut scheduler = scheduler_counters();
1909        self.scheduler_counters.apply_to(&mut scheduler);
1910        PerformanceSnapshot {
1911            scheduler,
1912            budget: *self.profiler.budget(),
1913            fps: self.profiler.frames_per_second(),
1914            windows,
1915            frame_statistics: self.profiler.history().statistics(),
1916            widgets,
1917            text,
1918            accessibility,
1919        }
1920    }
1921
1922    #[must_use]
1923    pub const fn profiler_budget(&self) -> &BudgetStatistics {
1924        self.profiler.budget()
1925    }
1926
1927    pub fn note_presented(&mut self, window_id: WindowId, presented: bool) {
1928        if presented {
1929            scheduler_counters::FRAMES_PRESENTED.fetch_add(1, Ordering::Relaxed);
1930            self.scheduler_counters.frames_presented =
1931                self.scheduler_counters.frames_presented.wrapping_add(1);
1932        } else {
1933            scheduler_counters::FRAMES_SKIPPED.fetch_add(1, Ordering::Relaxed);
1934            self.scheduler_counters.frames_skipped =
1935                self.scheduler_counters.frames_skipped.wrapping_add(1);
1936        }
1937        self.profiler.note_present(presented);
1938        let _ = self.with_window_mut(window_id, |record| {
1939            if presented {
1940                record.presented_frames = record.presented_frames.wrapping_add(1);
1941            } else {
1942                record.skipped_frames = record.skipped_frames.wrapping_add(1);
1943            }
1944        });
1945    }
1946
1947    pub fn request_close(&mut self, window_id: WindowId) -> bool {
1948        if !self.contains_window(window_id) {
1949            return false;
1950        }
1951        if self
1952            .close_request
1953            .as_mut()
1954            .is_some_and(|callback| !callback(window_id))
1955        {
1956            return false;
1957        }
1958        self.close_window(window_id)
1959    }
1960
1961    pub fn close_window(&mut self, window_id: WindowId) -> bool {
1962        self.fail_simulation_window(window_id);
1963        self.fail_native_requests_for_window(window_id, PlatformOperationError::stale_resource());
1964        self.fail_file_dialog_requests_for_window(window_id, FileDialogError::ParentClosed);
1965        let record = self.registry.borrow_mut().close(window_id);
1966        let Some(mut record) = record else {
1967            self.registry.borrow_mut().stale_window_commands += 1;
1968            return false;
1969        };
1970        record
1971            .transient_presentations
1972            .write()
1973            .expect("transient presentation snapshot lock")
1974            .clear();
1975        record.scope.cancel();
1976        record.runtime.dispose_window();
1977        self.native_commands
1978            .borrow_mut()
1979            .push_back(NativeWindowCommand::Operate(WindowCommand::new(
1980                window_id,
1981                WindowOperation::Close,
1982            )));
1983        // Explicit user close removes the auxiliary descriptor and its
1984        // window-scoped values; shutdown bypasses this method and therefore
1985        // preserves active descriptors. Not-yet-restored descriptors for other
1986        // windows are retained.
1987        let closed_restoration = record.restoration.clone();
1988        if let (Some(metadata), Some(restoration)) = (&closed_restoration, &self.restoration) {
1989            restoration.remove_window_descriptor(metadata.id.as_key().as_str());
1990            restoration.remove_scope(&[
1991                RestorationKey::new("window").expect("static key"),
1992                metadata.id.as_key().clone(),
1993            ]);
1994        }
1995        self.manager.sync_restorable_windows(true);
1996        if self.registry.borrow().visible_count() == 0
1997            && self.last_window_policy == LastWindowPolicy::ExitOnLastWindow
1998        {
1999            self.should_exit = true;
2000        }
2001        true
2002    }
2003
2004    fn drain_window_commands(&mut self) {
2005        while let Ok(queued) = self.command_receiver.try_recv() {
2006            let QueuedWindowCommand { command, request } = queued;
2007            let window_id = command.window_id;
2008            match command.operation.clone() {
2009                WindowOperation::Close => {
2010                    if let Some(request) = request {
2011                        Self::resolve_queued_request(
2012                            request,
2013                            Err(PlatformOperationError::with_context(
2014                                PlatformOperationErrorKind::InvalidState,
2015                                "window close does not expose a native completion request",
2016                            )),
2017                        );
2018                    }
2019                    let _ = self.close_window(window_id);
2020                }
2021                operation => {
2022                    let applied = self.with_window_mut(window_id, |record| {
2023                        match &operation {
2024                            WindowOperation::SetTitle(title) => {
2025                                record.options.title = title.clone()
2026                            }
2027                            WindowOperation::SetVisible(visible) => {
2028                                record.options.visible = *visible;
2029                                record.requested_state.visible = *visible;
2030                                record.lifecycle = if *visible {
2031                                    WindowLifecycle::Visible
2032                                } else {
2033                                    WindowLifecycle::Hidden
2034                                };
2035                            }
2036                            WindowOperation::SetLogicalSize(size) => {
2037                                if size.width.is_finite()
2038                                    && size.height.is_finite()
2039                                    && size.width > 0.0
2040                                    && size.height > 0.0
2041                                {
2042                                    record.content_sizing.reset();
2043                                    record.options.initial_logical_size = *size;
2044                                } else {
2045                                    return false;
2046                                }
2047                            }
2048                            WindowOperation::SetOuterPosition(_) => {}
2049                            WindowOperation::BeginMoveDrag
2050                            | WindowOperation::BeginResizeDrag(_) => {}
2051                            WindowOperation::SetMinimized(minimized) => {
2052                                record.requested_state.minimized = *minimized;
2053                            }
2054                            WindowOperation::SetMaximized(maximized) => {
2055                                record.options.maximized = *maximized;
2056                                record.requested_state.maximized = *maximized;
2057                            }
2058                            WindowOperation::SetFullscreen(fullscreen) => {
2059                                record.options.fullscreen = *fullscreen;
2060                                record.requested_state.fullscreen = *fullscreen;
2061                            }
2062                            WindowOperation::SetResizable(resizable) => {
2063                                record.options.resizable = *resizable;
2064                                record.requested_state.resizable = *resizable;
2065                            }
2066                            WindowOperation::SetDecorations(decorations) => {
2067                                record.options.decorations = *decorations;
2068                                record.requested_state.decorations = *decorations;
2069                            }
2070                            WindowOperation::SetLogicalSizeLimits(limits) => {
2071                                record.options.minimum_logical_size = limits.minimum();
2072                                record.options.maximum_logical_size = limits.maximum();
2073                                record.requested_state.size_limits = *limits;
2074                                record.content_sizing.reset();
2075                            }
2076                            WindowOperation::SetWindowLevel(level) => {
2077                                record.options.window_level = *level;
2078                                record.requested_state.level = *level;
2079                            }
2080                            WindowOperation::SetWindowIcon(icon) => {
2081                                record.options.window_icon = icon.clone();
2082                            }
2083                            WindowOperation::RequestUserAttention(_) => {}
2084                            WindowOperation::SetCursorGrab(_)
2085                            | WindowOperation::SetCursorVisible(_)
2086                            | WindowOperation::SetCursorPosition(_) => {}
2087                            WindowOperation::SetContentSensitivity(sensitivity) => {
2088                                record.last_content_sensitivity = Some(*sensitivity);
2089                            }
2090                            WindowOperation::RequestFocus => {}
2091                            WindowOperation::RequestRedraw => record.runtime.request_frame(),
2092                            WindowOperation::Close => unreachable!(),
2093                        }
2094                        true
2095                    });
2096                    if applied == Some(true) {
2097                        if let Some(request) = request {
2098                            let request_id = command
2099                                .request_id
2100                                .expect("result-bearing command has request id");
2101                            if !request.cancelled.load(Ordering::Acquire) {
2102                                self.pending_native_requests.insert(
2103                                    request_id,
2104                                    PendingNativeRequest {
2105                                        window_id,
2106                                        sender: request.sender,
2107                                    },
2108                                );
2109                            }
2110                        } else {
2111                            debug_assert!(command.request_id.is_none());
2112                        }
2113                        self.native_commands
2114                            .borrow_mut()
2115                            .push_back(NativeWindowCommand::Operate(command));
2116                        if matches!(operation, WindowOperation::SetVisible(false))
2117                            && self.registry.borrow().visible_count() == 0
2118                            && self.last_window_policy == LastWindowPolicy::ExitOnLastWindow
2119                        {
2120                            self.should_exit = true;
2121                        }
2122                        self.manager.sync_restorable_windows(true);
2123                    } else if applied == Some(false) {
2124                        if let Some(request) = request {
2125                            Self::resolve_queued_request(
2126                                request,
2127                                Err(PlatformOperationError::with_context(
2128                                    PlatformOperationErrorKind::InvalidState,
2129                                    "window operation arguments were rejected by the runtime",
2130                                )),
2131                            );
2132                        }
2133                    } else if applied.is_none() {
2134                        if let Some(request) = request {
2135                            Self::resolve_queued_request(
2136                                request,
2137                                Err(PlatformOperationError::stale_resource()),
2138                            );
2139                        }
2140                        self.registry.borrow_mut().stale_window_commands += 1;
2141                    }
2142                }
2143            }
2144        }
2145    }
2146
2147    fn resolve_queued_request(request: QueuedNativeRequest, result: PlatformOperationResult) {
2148        if !request.cancelled.load(Ordering::Acquire) {
2149            let _ = request.sender.send(result);
2150        }
2151    }
2152
2153    fn drain_native_request_cancellations(&mut self) {
2154        while let Ok(request_id) = self.request_cancellation_receiver.try_recv() {
2155            self.pending_native_requests.remove(&request_id);
2156        }
2157    }
2158
2159    fn fail_native_requests_for_window(
2160        &mut self,
2161        window_id: WindowId,
2162        error: PlatformOperationError,
2163    ) {
2164        let request_ids = self
2165            .pending_native_requests
2166            .iter()
2167            .filter_map(|(request_id, request)| {
2168                (request.window_id == window_id).then_some(*request_id)
2169            })
2170            .collect::<Vec<_>>();
2171        for request_id in request_ids {
2172            if let Some(request) = self.pending_native_requests.remove(&request_id) {
2173                let _ = request.sender.send(Err(error.clone()));
2174            }
2175        }
2176    }
2177
2178    fn fail_all_native_requests(&mut self, error: PlatformOperationError) {
2179        while self.request_cancellation_receiver.try_recv().is_ok() {}
2180        for (_, request) in self.pending_native_requests.drain() {
2181            let _ = request.sender.send(Err(error.clone()));
2182        }
2183        while let Ok(queued) = self.command_receiver.try_recv() {
2184            if let Some(request) = queued.request {
2185                Self::resolve_queued_request(request, Err(error.clone()));
2186            }
2187        }
2188    }
2189
2190    fn drain_file_dialog_requests(&mut self) {
2191        while let Ok(queued) = self.file_dialog_receiver.try_recv() {
2192            if queued.cancelled.load(std::sync::atomic::Ordering::Acquire) {
2193                continue;
2194            }
2195            if !self.contains_window(queued.window_id) {
2196                let _ = queued.sender.send(Err(FileDialogError::ParentClosed));
2197                continue;
2198            }
2199            let capabilities = self
2200                .window_capabilities(queued.window_id)
2201                .expect("live file-dialog parent has capabilities")
2202                .application_services
2203                .file_dialogs;
2204            if let Some(error) = explicitly_unsupported_request(capabilities, &queued.request) {
2205                let _ = queued.sender.send(Err(error));
2206                continue;
2207            }
2208            let request_id = queued.request_id;
2209            let window_id = queued.window_id;
2210            self.pending_file_dialog_requests.insert(
2211                request_id,
2212                PendingFileDialogRequest {
2213                    window_id,
2214                    request: queued.request,
2215                    sender: Some(queued.sender),
2216                    cancelled: queued.cancelled,
2217                },
2218            );
2219            if self.active_file_dialog_requests.contains_key(&window_id) {
2220                self.queued_file_dialog_requests
2221                    .entry(window_id)
2222                    .or_default()
2223                    .push_back(request_id);
2224            } else {
2225                self.activate_file_dialog(request_id);
2226            }
2227        }
2228    }
2229
2230    fn activate_file_dialog(&mut self, request_id: FileDialogRequestId) {
2231        let Some(pending) = self.pending_file_dialog_requests.get(&request_id) else {
2232            return;
2233        };
2234        if pending.cancelled.load(std::sync::atomic::Ordering::Acquire) {
2235            let window_id = pending.window_id;
2236            self.pending_file_dialog_requests.remove(&request_id);
2237            self.promote_next_file_dialog(window_id);
2238            return;
2239        }
2240        let window_id = pending.window_id;
2241        debug_assert!(!self.active_file_dialog_requests.contains_key(&window_id));
2242        self.active_file_dialog_requests
2243            .insert(window_id, request_id);
2244        self.native_file_dialog_requests
2245            .push_back(NativeFileDialogRequest {
2246                request_id,
2247                window_id,
2248                request: pending.request.clone(),
2249            });
2250    }
2251
2252    fn promote_next_file_dialog(&mut self, window_id: WindowId) {
2253        if self.active_file_dialog_requests.contains_key(&window_id)
2254            || !self.contains_window(window_id)
2255        {
2256            return;
2257        }
2258        loop {
2259            let next = self
2260                .queued_file_dialog_requests
2261                .get_mut(&window_id)
2262                .and_then(VecDeque::pop_front);
2263            let Some(request_id) = next else {
2264                self.queued_file_dialog_requests.remove(&window_id);
2265                return;
2266            };
2267            let cancelled = self
2268                .pending_file_dialog_requests
2269                .get(&request_id)
2270                .is_none_or(|pending| pending.cancelled.load(std::sync::atomic::Ordering::Acquire));
2271            if cancelled {
2272                self.pending_file_dialog_requests.remove(&request_id);
2273                continue;
2274            }
2275            self.activate_file_dialog(request_id);
2276            return;
2277        }
2278    }
2279
2280    fn drain_file_dialog_cancellations(&mut self) {
2281        while let Ok(request_id) = self.file_dialog_cancellation_receiver.try_recv() {
2282            let Some(window_id) = self
2283                .pending_file_dialog_requests
2284                .get(&request_id)
2285                .map(|pending| pending.window_id)
2286            else {
2287                continue;
2288            };
2289            let is_active = self.active_file_dialog_requests.get(&window_id) == Some(&request_id);
2290            if is_active {
2291                // If the desktop host has not taken this request yet, cancellation
2292                // can prevent native presentation and the FIFO may advance.
2293                let not_started = self.pending_file_dialog_requests.phase(&request_id)
2294                    == Some(RequestPhase::Queued);
2295                self.native_file_dialog_requests
2296                    .retain(|request| request.request_id != request_id);
2297                if not_started {
2298                    self.active_file_dialog_requests.remove(&window_id);
2299                    self.pending_file_dialog_requests.remove(&request_id);
2300                    self.promote_next_file_dialog(window_id);
2301                } else if let Some(pending) = self.pending_file_dialog_requests.get_mut(&request_id)
2302                {
2303                    // The native modal operation already owns the slot. Detach
2304                    // result delivery, but keep serialization until completion.
2305                    pending.sender = None;
2306                    self.pending_file_dialog_requests.abandon(&request_id);
2307                }
2308            } else {
2309                if let Some(queue) = self.queued_file_dialog_requests.get_mut(&window_id) {
2310                    queue.retain(|queued| *queued != request_id);
2311                    if queue.is_empty() {
2312                        self.queued_file_dialog_requests.remove(&window_id);
2313                    }
2314                }
2315                self.pending_file_dialog_requests.remove(&request_id);
2316            }
2317        }
2318    }
2319
2320    fn fail_file_dialog_requests_for_window(
2321        &mut self,
2322        window_id: WindowId,
2323        error: FileDialogError,
2324    ) {
2325        self.native_file_dialog_requests
2326            .retain(|request| request.window_id != window_id);
2327        self.active_file_dialog_requests.remove(&window_id);
2328        self.queued_file_dialog_requests.remove(&window_id);
2329        let request_ids = self
2330            .pending_file_dialog_requests
2331            .iter()
2332            .filter_map(|(request_id, pending)| {
2333                (pending.window_id == window_id).then_some(*request_id)
2334            })
2335            .collect::<Vec<_>>();
2336        for request_id in request_ids {
2337            if let Some(mut pending) = self.pending_file_dialog_requests.remove(&request_id)
2338                && !pending.cancelled.load(std::sync::atomic::Ordering::Acquire)
2339                && let Some(sender) = pending.sender.take()
2340            {
2341                let _ = sender.send(Err(error.clone()));
2342            }
2343        }
2344    }
2345
2346    fn fail_all_file_dialog_requests(&mut self, error: FileDialogError) {
2347        self.file_dialog_bridge.stop();
2348        while self.file_dialog_cancellation_receiver.try_recv().is_ok() {}
2349        while let Ok(queued) = self.file_dialog_receiver.try_recv() {
2350            if !queued.cancelled.load(std::sync::atomic::Ordering::Acquire) {
2351                let _ = queued.sender.send(Err(error.clone()));
2352            }
2353        }
2354        for (_, mut pending) in self.pending_file_dialog_requests.drain() {
2355            if !pending.cancelled.load(std::sync::atomic::Ordering::Acquire)
2356                && let Some(sender) = pending.sender.take()
2357            {
2358                let _ = sender.send(Err(error.clone()));
2359            }
2360        }
2361        self.active_file_dialog_requests.clear();
2362        self.queued_file_dialog_requests.clear();
2363        self.native_file_dialog_requests.clear();
2364    }
2365
2366    /// Takes native file-dialog work ready to start. At most one request per
2367    /// parent window is active; later requests for that window remain FIFO in
2368    /// the runtime until completion or cancellation.
2369    pub fn take_native_file_dialog_requests(&mut self) -> Vec<NativeFileDialogRequest> {
2370        self.drain_file_dialog_requests();
2371        self.drain_file_dialog_cancellations();
2372        let requests: Vec<_> = self.native_file_dialog_requests.drain(..).collect();
2373        for request in &requests {
2374            self.pending_file_dialog_requests
2375                .dispatch(&request.request_id);
2376        }
2377        requests
2378    }
2379
2380    /// Completes one active file dialog and promotes the next request for the
2381    /// same parent. Backends must invoke this only on the application UI/event
2382    /// loop thread; native dialog objects never cross this boundary.
2383    pub fn complete_file_dialog(
2384        &mut self,
2385        completion: NativeFileDialogCompletion,
2386    ) -> FileDialogCompletionStatus {
2387        let Some(pending) = self
2388            .pending_file_dialog_requests
2389            .get(&completion.request_id)
2390        else {
2391            return FileDialogCompletionStatus::UnknownRequest;
2392        };
2393        if pending.window_id != completion.window_id {
2394            return FileDialogCompletionStatus::TargetMismatch;
2395        }
2396        if self.active_file_dialog_requests.get(&completion.window_id)
2397            != Some(&completion.request_id)
2398        {
2399            return FileDialogCompletionStatus::UnknownRequest;
2400        }
2401
2402        let Ok(mut pending) = self
2403            .pending_file_dialog_requests
2404            .complete(&completion.request_id, |_| true)
2405        else {
2406            return FileDialogCompletionStatus::UnknownRequest;
2407        };
2408        self.active_file_dialog_requests
2409            .remove(&completion.window_id);
2410
2411        let (result, status) = match completion.result {
2412            Ok(outcome) if result_matches_request(&pending.request, &outcome) => {
2413                (Ok(outcome), FileDialogCompletionStatus::Completed)
2414            }
2415            Ok(_) => (
2416                Err(FileDialogError::InvalidResult),
2417                FileDialogCompletionStatus::InvalidResult,
2418            ),
2419            Err(error) => (Err(error), FileDialogCompletionStatus::Completed),
2420        };
2421        if !pending.cancelled.load(std::sync::atomic::Ordering::Acquire)
2422            && let Some(sender) = pending.sender.take()
2423        {
2424            let _ = sender.send(result);
2425        }
2426        self.promote_next_file_dialog(completion.window_id);
2427        status
2428    }
2429
2430    fn drain_global_shortcut_requests(&mut self) {
2431        drain_queued(
2432            &self.global_shortcut_receiver,
2433            &mut self.pending_global_shortcut_requests,
2434            &mut self.native_global_shortcut_requests,
2435        );
2436        let abandoned: Vec<_> = self
2437            .pending_global_shortcut_requests
2438            .iter()
2439            .filter(|(_, pending)| pending.reply.is_abandoned())
2440            .map(|(id, pending)| (*id, pending.operation))
2441            .collect();
2442        for (id, operation) in abandoned {
2443            if self.pending_global_shortcut_requests.phase(&id) == Some(RequestPhase::Queued) {
2444                if matches!(operation, NativeGlobalShortcutOperation::Register { .. }) {
2445                    self.pending_global_shortcut_requests.remove(&id);
2446                    self.native_global_shortcut_requests
2447                        .retain(|request| request.request_id != id);
2448                }
2449            } else {
2450                self.pending_global_shortcut_requests.abandon(&id);
2451            }
2452        }
2453    }
2454
2455    fn fail_all_global_shortcut_requests(&mut self) {
2456        self.global_shortcut_bridge.stop();
2457        while let Ok(queued) = self.global_shortcut_receiver.try_recv() {
2458            queued.reply.finish(
2459                queued.operation,
2460                Err(incular_platform::GlobalShortcutError::ApplicationStopped),
2461            );
2462        }
2463        for (_, pending) in self.pending_global_shortcut_requests.drain() {
2464            pending.reply.finish(
2465                pending.operation,
2466                Err(incular_platform::GlobalShortcutError::ApplicationStopped),
2467            );
2468        }
2469        self.native_global_shortcut_requests.clear();
2470    }
2471
2472    /// Takes application-scoped global-shortcut work ready for the native UI
2473    /// thread. Custom desktop embedders may implement this boundary without
2474    /// exposing native hotkey handles to runtime/application code.
2475    #[doc(hidden)]
2476    pub fn take_native_global_shortcut_requests(&mut self) -> Vec<NativeGlobalShortcutRequest> {
2477        self.drain_global_shortcut_requests();
2478        let requests: Vec<_> = self.native_global_shortcut_requests.drain(..).collect();
2479        for request in &requests {
2480            self.pending_global_shortcut_requests
2481                .dispatch(&request.request_id);
2482        }
2483        requests
2484    }
2485
2486    /// Completes one native global-shortcut request. If the requesting future
2487    /// was abandoned after a registration succeeded, the caller is instructed
2488    /// to roll that native registration back immediately.
2489    #[doc(hidden)]
2490    pub fn complete_global_shortcut_request(
2491        &mut self,
2492        completion: NativeGlobalShortcutCompletion,
2493    ) -> GlobalShortcutCompletionStatus {
2494        let Ok(pending) = self
2495            .pending_global_shortcut_requests
2496            .complete(&completion.request_id, |_| true)
2497        else {
2498            return GlobalShortcutCompletionStatus::UnknownRequest;
2499        };
2500        pending.reply.finish(pending.operation, completion.result)
2501    }
2502
2503    /// Completes one result-bearing native operation. Backends call this only
2504    /// after applying the operation on the owning native/UI thread.
2505    pub fn complete_native_operation(
2506        &mut self,
2507        completion: NativeOperationCompletion,
2508    ) -> NativeOperationCompletionStatus {
2509        let pending = match self
2510            .pending_native_requests
2511            .complete(&completion.request_id, |pending| {
2512                pending.window_id == completion.window_id
2513            }) {
2514            Ok(pending) => pending,
2515            Err(CompletionRejection::TargetMismatch) => {
2516                return NativeOperationCompletionStatus::TargetMismatch;
2517            }
2518            Err(_) => return NativeOperationCompletionStatus::UnknownRequest,
2519        };
2520        if !self.contains_window(completion.window_id) {
2521            let _ = pending
2522                .sender
2523                .send(Err(PlatformOperationError::stale_resource()));
2524            return NativeOperationCompletionStatus::StaleTarget;
2525        }
2526        if let Err(error) = &completion.result {
2527            let error = error.clone();
2528            let _ = self.with_window_mut(completion.window_id, |record| {
2529                record.last_platform_error = Some(error);
2530            });
2531        }
2532        let _ = pending.sender.send(completion.result);
2533        NativeOperationCompletionStatus::Completed
2534    }
2535
2536    /// Takes all UI-thread native operations. Adapters must call this only
2537    /// from their event-loop callback; creation is consequently valid for
2538    /// Winit 0.30's active-loop restriction.
2539    pub fn take_native_window_commands(&mut self) -> Vec<NativeWindowCommand> {
2540        self.drain_window_commands();
2541        self.drain_native_request_cancellations();
2542        let commands: Vec<_> = self.native_commands.borrow_mut().drain(..).collect();
2543        for command in &commands {
2544            if let NativeWindowCommand::Operate(command) = command
2545                && let Some(id) = command.request_id
2546            {
2547                self.pending_native_requests.dispatch(&id);
2548            }
2549        }
2550        commands
2551    }
2552
2553    #[must_use]
2554    pub const fn should_exit(&self) -> bool {
2555        self.should_exit
2556    }
2557
2558    #[must_use]
2559    pub fn window_diagnostics(&self, window_id: WindowId) -> Option<WindowDiagnostics> {
2560        let registry = self.registry.borrow();
2561        let slot = registry.slots.get(window_id.index() as usize)?;
2562        if slot.generation != window_id.generation() {
2563            return None;
2564        }
2565        let record = slot.record.as_ref()?;
2566        Some(WindowDiagnostics {
2567            id: window_id,
2568            title: record.options.title.clone(),
2569            logical_size: record.metrics.logical_size(),
2570            physical_size: record.metrics.physical_size,
2571            scale_factor: record.metrics.scale_factor,
2572            visible: record.options.visible,
2573            native_focused: record.native_focused,
2574            lifecycle: record.lifecycle,
2575            application_lifecycle: record.runtime.lifecycle(),
2576            frame_requested: record.runtime.frame_requested(),
2577            requested_frames: record.requested_frames,
2578            presented_frames: record.presented_frames,
2579            skipped_frames: record.skipped_frames,
2580            input_events: record.input_events,
2581            surface_generation: record.surface_generation,
2582            content_sensitivity: record.runtime.content_sensitivity(),
2583            elements: record.runtime.tree().element_count(),
2584            render_objects: record.runtime.tree().render_object_count(),
2585            semantics: record.runtime.tree().semantics().len(),
2586            accessibility: record.accessibility,
2587            capabilities: *record
2588                .capabilities
2589                .read()
2590                .expect("window capability snapshot lock"),
2591            last_platform_error: record.last_platform_error.clone(),
2592            requested_state: record.requested_state,
2593            observed_state: *record
2594                .observed_state
2595                .read()
2596                .expect("window observed-state snapshot lock"),
2597            transient_presentations: record
2598                .transient_presentations
2599                .read()
2600                .expect("transient presentation snapshot lock")
2601                .clone(),
2602        })
2603    }
2604
2605    #[must_use]
2606    pub fn diagnostics(&self) -> ApplicationDiagnostics {
2607        let registry = self.registry.borrow();
2608        ApplicationDiagnostics {
2609            windows_created: registry.windows_created,
2610            windows_closed: registry.windows_closed,
2611            active_windows: registry.ids().len(),
2612            stale_window_commands: registry.stale_window_commands,
2613            pending_native_requests: self.pending_native_requests.len(),
2614            pending_file_dialog_requests: self.pending_file_dialog_requests.len(),
2615        }
2616    }
2617
2618    /// Returns value-free health and persistence-coalescing data when the
2619    /// application opted into restoration.
2620    #[must_use]
2621    pub fn restoration_diagnostics(&self) -> Option<RestorationDiagnostics> {
2622        self.restoration
2623            .as_ref()
2624            .map(restoration::RestorationManager::diagnostics)
2625    }
2626
2627    /// Returns the configured snapshot path for developer tooling. In-memory
2628    /// and custom stores intentionally have no filesystem location.
2629    #[must_use]
2630    pub fn restoration_path(&self) -> Option<std::path::PathBuf> {
2631        self.restoration
2632            .as_ref()
2633            .and_then(restoration::RestorationManager::store_location)
2634    }
2635
2636    /// Removes every opt-in persisted value and every restorable-window
2637    /// descriptor. The deletion is coalesced through the normal Tokio-backed
2638    /// persistence pipeline; call [`Self::flush_restoration`] before an
2639    /// immediate process handoff if required.
2640    pub fn reset_restoration(&mut self) -> bool {
2641        let Some(restoration) = &self.restoration else {
2642            return false;
2643        };
2644        restoration.reset();
2645        true
2646    }
2647
2648    /// Bypasses the normal 250ms debounce but still writes on Tokio's blocking
2649    /// pool. This is useful for an explicit user action or lifecycle flush.
2650    pub fn flush_restoration(&mut self) -> bool {
2651        let Some(restoration) = &self.restoration else {
2652            return false;
2653        };
2654        restoration.flush();
2655        true
2656    }
2657
2658    /// Flushes restoration and bounded-waits for the blocking save to settle,
2659    /// mirroring the shutdown handoff. Used when the host may freeze or kill
2660    /// the process after the lifecycle event (for example mobile suspend).
2661    fn flush_restoration_and_wait(&mut self) {
2662        let Some(restoration) = self.restoration.clone() else {
2663            return;
2664        };
2665        restoration.flush();
2666        // A browser thread cannot block: the save completes on a later turn.
2667        #[cfg(not(target_arch = "wasm32"))]
2668        {
2669            let deadline = Instant::now() + std::time::Duration::from_millis(100);
2670            while !restoration.is_clean() && Instant::now() < deadline {
2671                self.process_runtime_work();
2672                std::thread::sleep(std::time::Duration::from_millis(1));
2673            }
2674        }
2675    }
2676
2677    #[must_use]
2678    pub fn debug_dump(&self) -> String {
2679        let mut lines = vec!["Application".to_owned()];
2680        if let Some(restoration) = &self.restoration {
2681            lines.push(restoration.debug_dump());
2682        }
2683        for id in self.active_window_ids() {
2684            if let Some(window) = self.window_diagnostics(id) {
2685                lines.push(format!(
2686                    "  {id} title={:?} {:.0}x{:.0} @{} focused={}\\n    root elements={} render={} semantics={} dirty={}",
2687                    window.title,
2688                    window.logical_size.width,
2689                    window.logical_size.height,
2690                    window.scale_factor,
2691                    window.native_focused,
2692                    window.elements,
2693                    window.render_objects,
2694                    window.semantics,
2695                    window.frame_requested,
2696                ));
2697            }
2698        }
2699        lines.join("\\n")
2700    }
2701
2702    pub fn shutdown(&mut self) {
2703        self.flush_restoration_before_shutdown();
2704        self.application_shell.stop();
2705        self.manager.bridge.stop();
2706        self.fail_all_file_dialog_requests(FileDialogError::ApplicationStopped);
2707        self.fail_all_global_shortcut_requests();
2708        self.fail_all_native_requests(PlatformOperationError::with_context(
2709            PlatformOperationErrorKind::Unavailable,
2710            "application runtime stopped before native completion",
2711        ));
2712        let ids = self.active_window_ids();
2713        for id in ids {
2714            self.fail_simulation_window(id);
2715            if let Some(mut record) = self.registry.borrow_mut().close(id) {
2716                record.scope.cancel();
2717                record.runtime.dispose_window();
2718            }
2719        }
2720        self.native_commands.borrow_mut().clear();
2721        self.scheduler.borrow_mut().shutdown();
2722        self.stop_simulation();
2723        self.should_exit = true;
2724    }
2725
2726    fn flush_restoration_before_shutdown(&mut self) {
2727        self.flush_restoration_and_wait();
2728    }
2729
2730    /// Compatibility escape hatch for existing single-window embedders and
2731    /// tests. Extra windows are cancelled; desktop `run` retains all windows.
2732    /// Pending restoration state is flushed with the same bounded wait as
2733    /// shutdown so the extracted runtime does not silently drop it.
2734    #[must_use]
2735    pub fn into_runtime(mut self) -> Runtime {
2736        self.flush_restoration_and_wait();
2737        self.manager.bridge.stop();
2738        self.fail_all_file_dialog_requests(FileDialogError::ApplicationStopped);
2739        self.fail_all_global_shortcut_requests();
2740        let primary = self.primary_window;
2741        let mut primary_record = self
2742            .registry
2743            .borrow_mut()
2744            .close(primary)
2745            .expect("application primary window exists");
2746        for id in self.active_window_ids() {
2747            if let Some(record) = self.registry.borrow_mut().close(id) {
2748                record.scope.cancel();
2749            }
2750        }
2751        primary_record.runtime.window_manager = None;
2752        primary_record.runtime.window_id = None;
2753        primary_record.runtime
2754    }
2755}
2756/// Widget key that marks the performance-overlay mount point.
2757pub const PERFORMANCE_OVERLAY_KEY: &str = incular_widgets::internal::PERFORMANCE_OVERLAY_KEY;
2758
2759/// Builds the repaint-contained overlay visual from one snapshot.
2760fn overlay_widget(snapshot: &PerformanceSnapshot) -> incular_widgets::Widget {
2761    use incular_widgets::{DecoratedBox, Padding, Text, Widget};
2762    let lines = snapshot.overlay_lines();
2763    let monospace = || {
2764        incular_widgets::TextStyle::default()
2765            .family(incular_text::FontFamily::Monospace)
2766            .font_size(11.)
2767            .color(incular_core::Color::rgba(190, 220, 255, 255))
2768    };
2769    let rows: Vec<Widget> = lines
2770        .into_iter()
2771        .map(|line| Widget::from(Text::new(line).style(monospace())))
2772        .collect();
2773    // The overlay is diagnostic chrome, not an application control. Keep the
2774    // visual mounted in the tree while allowing the widgets underneath it to
2775    // receive pointer events, just like Flutter's non-interactive performance
2776    // overlay.
2777    Widget::from(
2778        incular_widgets::IgnorePointer::new(Widget::from(incular_widgets::RepaintBoundary::new(
2779            Widget::from(
2780                DecoratedBox::new(Padding::all(
2781                    6.,
2782                    Widget::from(incular_widgets::Column::new(rows)),
2783                ))
2784                .background(incular_core::Color::rgba(12, 14, 18, 216))
2785                .radius(4.)
2786                .border(incular_rendering::Border {
2787                    color: incular_core::Color::rgba(120, 170, 245, 90),
2788                    width: 1.,
2789                }),
2790            ),
2791        )))
2792        .ignoring(true),
2793    )
2794}