1use std::{cell::RefCell, fmt::Debug, rc::Rc};
14
15use cranpose_core::{MemoryApplier, NodeId, collections::map::HashSet};
16use cranpose_foundation::{
17 PointerButton, PointerButtons, PointerId, PointerSource, RotaryScrollEvent,
18};
19use cranpose_render_common::{NodeUpdate, RenderScene, Renderer};
20use cranpose_ui::{
21 LayoutNode, LayoutTree, MovedSceneNode, PlatformTextInputHandler, SemanticsTree,
22 pointer_icon_session::PointerIconState,
23};
24use cranpose_ui_graphics::{Point, PointerIcon, Size};
25
26use crate::{
27 AppShell, FramePacingMode, FrameRatePreference, FrameSchedule, FrameScheduler,
28 FrameUpdateResult, PlatformFrameDriver, ShellApp, SurfaceRoutingScratch,
29 hit_path_tracker::HitPathTracker, shell_frame::DevOverlay,
30};
31
32#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
36pub enum RootId {
37 Primary,
39 Window(NodeId),
41}
42
43pub struct RootSurface<R: Renderer> {
51 pub(crate) id: RootId,
52 pub(crate) root: Option<NodeId>,
53 pub(crate) renderer: R,
54 pub(crate) cursor: (f32, f32),
55 pub(crate) viewport: (f32, f32),
56 pub(crate) buffer_size: (u32, u32),
57 pub(crate) layout_tree: Option<LayoutTree>,
58 pub(crate) semantics_tree: Option<SemanticsTree>,
59 semantics_moved: bool,
62 seen_layer_moves: u64,
65 pub(crate) modal_focus: Vec<(NodeId, Option<NodeId>)>,
66 pub(crate) frames: u64,
68 pub(crate) key_moved_focus_at: Option<u64>,
73 pub(crate) dpad_navigation: bool,
76 pub(crate) focus_check_due: bool,
77 pub(crate) key_press: Option<crate::shell_input::key_activation::KeyPress>,
79 pub(crate) frame_rate_preference: FrameRatePreference,
80 pub(crate) scene_dirty: bool,
81 pub(crate) scoped_layout_scene_nodes: Vec<NodeId>,
82 pub(crate) scoped_moved_scene_nodes: Vec<NodeId>,
84 pub(crate) scoped_draw_nodes: Vec<NodeId>,
85 pub(crate) scoped_layer_property_nodes: Vec<NodeId>,
86 pub(crate) structural_scene_nodes: Vec<NodeId>,
87 pub(crate) scene_updates: Vec<(NodeId, NodeUpdate)>,
88 pub(crate) redraw_walk: Vec<NodeId>,
91 pub(crate) retained_visual_nodes: HashSet<NodeId>,
92 pub(crate) is_dirty: bool,
93 pub(crate) buttons_pressed: PointerButtons,
94 pub(crate) pointer_source: PointerSource,
95 pub(crate) hit_path_tracker: HitPathTracker,
96 pub(crate) hovered_nodes: Vec<NodeId>,
97 pub(crate) on_rotary_scroll: Option<Rc<dyn Fn(RotaryScrollEvent) -> bool>>,
98 pub(crate) dev_overlay: DevOverlay,
99 pub(crate) inspector: crate::inspector::DeveloperInspector,
100 pub(crate) frame_scheduler: FrameScheduler,
101 pub(crate) pointer_icon: PointerIconState,
102 pub(crate) last_update: FrameUpdateResult,
103 pub(crate) frame_owed: bool,
104 pub(crate) screen_origin: Option<Point>,
105}
106
107impl<R: Renderer> RootSurface<R> {
108 pub(crate) fn new(
109 id: RootId,
110 renderer: R,
111 buffer_size: (u32, u32),
112 viewport: (f32, f32),
113 ) -> Self {
114 Self {
115 id,
116 root: None,
117 renderer,
118 cursor: (0.0, 0.0),
119 viewport,
120 buffer_size,
121 layout_tree: None,
122 semantics_tree: None,
123 semantics_moved: false,
124 seen_layer_moves: 0,
125 modal_focus: Vec::new(),
126 frames: 0,
127 key_moved_focus_at: None,
128 dpad_navigation: false,
129 focus_check_due: false,
130 key_press: None,
131 frame_rate_preference: FrameRatePreference::default(),
132 scene_dirty: true,
133 scoped_layout_scene_nodes: Vec::new(),
134 scoped_moved_scene_nodes: Vec::new(),
135 scoped_draw_nodes: Vec::new(),
136 scoped_layer_property_nodes: Vec::new(),
137 structural_scene_nodes: Vec::new(),
138 scene_updates: Vec::new(),
139 redraw_walk: Vec::new(),
140 retained_visual_nodes: HashSet::default(),
141 is_dirty: true,
142 buttons_pressed: PointerButtons::NONE,
143 pointer_source: PointerSource::Unknown,
144 hit_path_tracker: HitPathTracker::new(),
145 hovered_nodes: Vec::new(),
146 on_rotary_scroll: None,
147 dev_overlay: DevOverlay::default(),
148 inspector: crate::inspector::DeveloperInspector::default(),
149 frame_scheduler: FrameScheduler::default(),
150 pointer_icon: PointerIconState::new(),
151 last_update: FrameUpdateResult::default(),
152 frame_owed: false,
153 screen_origin: None,
154 }
155 }
156
157 pub(crate) fn root_node(&self, app: &ShellApp) -> Option<NodeId> {
158 match self.id {
159 RootId::Primary => app.composition.root(),
160 RootId::Window(_) => self.root,
161 }
162 }
163
164 pub(crate) fn owns_nodes_under(&self, window_root: Option<NodeId>) -> bool {
165 match self.id {
166 RootId::Primary => window_root.is_none(),
167 RootId::Window(_) => self.root.is_some() && self.root == window_root,
168 }
169 }
170
171 pub(crate) fn viewport_size(&self) -> Size {
172 Size {
173 width: self.viewport.0,
174 height: self.viewport.1,
175 }
176 }
177
178 pub(crate) fn screen_point_inside(&self, screen: Point) -> Option<Point> {
179 let origin = self.screen_origin?;
180 let local = Point {
181 x: screen.x - origin.x,
182 y: screen.y - origin.y,
183 };
184 (local.x >= 0.0
185 && local.y >= 0.0
186 && local.x <= self.viewport.0
187 && local.y <= self.viewport.1)
188 .then_some(local)
189 }
190
191 pub(crate) fn set_root(&mut self, root: Option<NodeId>) {
192 if self.root == root {
193 return;
194 }
195 self.root = root;
196 self.forget_snapshots();
197 self.invalidate_scene_root(root);
198 self.retained_visual_nodes.clear();
199 self.hit_path_tracker.clear();
200 self.hovered_nodes.clear();
201 self.buttons_pressed = PointerButtons::NONE;
202 self.is_dirty = true;
203 }
204
205 pub(crate) fn invalidate_scene_root(&mut self, root: Option<NodeId>) {
206 self.scoped_layout_scene_nodes.clear();
207 self.scoped_layout_scene_nodes.extend(root);
208 self.scoped_moved_scene_nodes.clear();
209 self.scoped_draw_nodes.clear();
210 self.scoped_layer_property_nodes.clear();
211 self.structural_scene_nodes.clear();
212 self.scene_updates.clear();
213 self.scene_dirty = true;
214 }
215
216 pub(crate) fn forget_snapshots(&mut self) {
219 self.layout_tree = None;
220 self.semantics_moved = true;
221 }
222
223 pub(crate) fn layers_moved_since_look(&mut self) -> bool {
225 let moves = self.renderer.scene().layer_moves();
226 let moved = moves != self.seen_layer_moves;
227 self.seen_layer_moves = moves;
228 moved
229 }
230
231 pub(crate) fn has_active_pointer_gesture(&self) -> bool {
232 self.buttons_pressed != PointerButtons::NONE
233 && self.hit_path_tracker.has_path(PointerId::PRIMARY)
234 }
235
236 pub(crate) fn has_active_primary_gesture(&self) -> bool {
237 self.buttons_pressed.contains(PointerButton::Primary)
238 && self.hit_path_tracker.has_path(PointerId::PRIMARY)
239 }
240
241 pub(crate) fn begin_frame(&mut self) {
245 self.frames += 1;
246 if self.frame_scheduler.take_held_still() {
247 self.renderer.note_held_still();
248 }
249 if self.has_active_pointer_gesture() {
250 self.renderer.note_pointer_held();
251 }
252 }
253
254 pub(crate) fn renderer_warmup_due(&self, app: &ShellApp) -> bool {
255 self.renderer.needs_frame_warmup() && !app.composition.runtime().has_frame_callbacks()
256 }
257
258 pub(crate) fn needs_redraw_in_context(&self, app: &ShellApp) -> bool {
259 app.has_stale_work_in_context()
260 || app.composition.should_recompose()
261 || self.is_dirty
262 || self.scene_dirty
263 || self.renderer_warmup_due(app)
264 }
265
266 pub(crate) fn compute_frame_schedule(
267 &self,
268 app: &ShellApp,
269 surfaces_dirty: bool,
270 ) -> FrameSchedule {
271 let (needs_update, needs_frame) = app.app_context.enter(|| {
272 let needs_frame = self.is_dirty
273 || self.scene_dirty
274 || app.wants_frame_in_context()
275 || self.has_active_pointer_gesture()
276 || self.renderer_warmup_due(app);
277 (app.needs_ui_update_in_context(surfaces_dirty), needs_frame)
278 });
279 FrameSchedule {
280 needs_update,
281 needs_frame,
282 next_deadline: app.next_event_time(),
283 }
284 }
285
286 pub(crate) fn layout_tree_in_context(&mut self, app: &mut ShellApp) -> Option<&LayoutTree> {
287 if self.layout_tree.is_none() {
288 let root = self.root_node(app)?;
289 let mut applier = app.composition.applier_mut();
290 match cranpose_ui::build_layout_tree_from_applier(&mut applier, root) {
291 Ok(layout_tree) => {
292 self.layout_tree = layout_tree;
293 }
294 Err(err) => {
295 log::debug!("failed to build layout snapshot: {err}");
296 return None;
297 }
298 }
299 }
300 self.layout_tree.as_ref()
301 }
302
303 pub(crate) fn top_modal(&self, app: &mut ShellApp) -> Option<NodeId> {
306 let root = self.root_node(app)?;
307 let mut applier = app.composition.applier_mut();
308 cranpose_ui::top_modal_from_applier(&mut applier, root).unwrap_or_else(|err| {
309 log::debug!("failed to find the top modal under root #{root}: {err}");
310 None
311 })
312 }
313
314 pub(crate) fn semantics_tree_in_context(
315 &mut self,
316 app: &mut ShellApp,
317 ) -> Option<&SemanticsTree> {
318 if !app.semantics_enabled && !self.inspector.state.open {
319 return None;
320 }
321 self.semantics_tree_for_input(app)
322 }
323
324 pub(crate) fn semantics_tree_for_input(
325 &mut self,
326 app: &mut ShellApp,
327 ) -> Option<&SemanticsTree> {
328 app.flush_semantics_invalidations();
329 let root = self.root_node(app)?;
330 let semantics_dirty = {
331 let mut applier = app.composition.applier_mut();
332 cranpose_ui::tree_needs_semantics(&mut *applier, root).unwrap_or_else(|err| {
333 log::debug!("failed to check semantics dirty status for root #{root}: {err}");
334 true
335 })
336 };
337 if self.semantics_tree.is_none() || self.semantics_moved || semantics_dirty {
338 let mut applier = app.composition.applier_mut();
339 match cranpose_ui::update_semantics_tree_from_applier(
340 &mut applier,
341 root,
342 &mut self.semantics_tree,
343 ) {
344 Ok(()) => {
345 #[cfg(debug_assertions)]
346 crate::semantics_check::assert_matches_rebuild(
347 &mut applier,
348 root,
349 self.semantics_tree.as_ref(),
350 );
351 self.semantics_moved = false;
352 app.semantics_snapshot_revision =
353 app.semantics_snapshot_revision.wrapping_add(1);
354 }
355 Err(err) => {
356 log::debug!("failed to build semantics snapshot: {err}");
357 return None;
358 }
359 }
360 }
361 self.semantics_tree.as_ref()
362 }
363}
364
365#[derive(Default)]
366pub(crate) struct TextInputRoutes {
367 active: Option<RootId>,
368 shown: Option<RootId>,
369 handlers: Vec<(RootId, Rc<dyn PlatformTextInputHandler>)>,
370}
371
372impl TextInputRoutes {
373 pub(crate) fn active(&self) -> RootId {
374 self.active.unwrap_or(RootId::Primary)
375 }
376
377 pub(crate) fn set_active(&mut self, root: RootId) {
378 self.active = Some(root);
379 }
380
381 pub(crate) fn set_handler(&mut self, root: RootId, handler: Rc<dyn PlatformTextInputHandler>) {
382 self.remove(root);
383 self.handlers.push((root, handler));
384 }
385
386 pub(crate) fn remove(&mut self, root: RootId) {
387 self.handlers.retain(|(id, _)| *id != root);
388 if self.shown == Some(root) {
389 self.shown = None;
390 }
391 }
392
393 fn handler(&self, root: RootId) -> Option<Rc<dyn PlatformTextInputHandler>> {
394 self.handlers
395 .iter()
396 .find(|(id, _)| *id == root)
397 .map(|(_, handler)| Rc::clone(handler))
398 }
399
400 fn take_show_target(&mut self) -> Option<Rc<dyn PlatformTextInputHandler>> {
401 let active = self.active();
402 let handler = self.handler(active);
403 if handler.is_some() {
404 self.shown = Some(active);
405 }
406 handler
407 }
408
409 fn take_hide_target(&mut self) -> Option<Rc<dyn PlatformTextInputHandler>> {
410 let target = self.shown.take().unwrap_or_else(|| self.active());
411 self.handler(target)
412 }
413}
414
415pub(crate) struct TextInputRouter {
416 pub(crate) routes: Rc<RefCell<TextInputRoutes>>,
417}
418
419impl PlatformTextInputHandler for TextInputRouter {
420 fn show_keyboard(&self) {
421 let handler = self.routes.borrow_mut().take_show_target();
422 if let Some(handler) = handler {
423 handler.show_keyboard();
424 }
425 }
426
427 fn hide_keyboard(&self) {
428 let handler = self.routes.borrow_mut().take_hide_target();
429 if let Some(handler) = handler {
430 handler.hide_keyboard();
431 }
432 }
433}
434
435#[derive(Clone, Copy)]
436pub(crate) enum SurfaceDirtyLane {
437 Layout,
438 Moved,
439 Draw,
440 LayerProperties,
441 Structural,
442}
443
444fn append_surface_dirty_node(
445 surface: &mut RootSurface<impl Renderer>,
446 surface_index: usize,
447 node: NodeId,
448 lane: SurfaceDirtyLane,
449 seen: &mut HashSet<(usize, NodeId)>,
450) {
451 match lane {
455 SurfaceDirtyLane::Layout => {
456 surface.scene_dirty = true;
457 surface.scoped_layout_scene_nodes.push(node);
458 }
459 SurfaceDirtyLane::Moved => {
460 surface.scene_dirty = true;
461 surface.scoped_moved_scene_nodes.push(node);
462 }
463 SurfaceDirtyLane::Draw => surface.scoped_draw_nodes.push(node),
464 SurfaceDirtyLane::LayerProperties => surface.scoped_layer_property_nodes.push(node),
465 SurfaceDirtyLane::Structural => {
466 if seen.insert((surface_index, node)) {
467 surface.structural_scene_nodes.push(node);
468 }
469 }
470 }
471}
472
473#[inline]
476pub(crate) fn route_nodes_by_surface<R: Renderer>(
477 app: &mut ShellApp,
478 surfaces: &mut [RootSurface<R>],
479 nodes: impl IntoIterator<Item = NodeId>,
480 lane: SurfaceDirtyLane,
481 scratch: &mut SurfaceRoutingScratch,
482) {
483 let nodes = nodes.into_iter();
484 if nodes.size_hint().1 == Some(0) {
485 scratch.attached.clear();
486 scratch.owners.clear();
487 return;
488 }
489 route_batch_by_surface(app, surfaces, nodes, lane, scratch);
490}
491
492#[inline(never)]
493fn route_batch_by_surface<R: Renderer>(
494 app: &mut ShellApp,
495 surfaces: &mut [RootSurface<R>],
496 nodes: impl Iterator<Item = NodeId>,
497 lane: SurfaceDirtyLane,
498 scratch: &mut SurfaceRoutingScratch,
499) {
500 let Some(primary_root) = app.composition.root() else {
501 scratch.attached.clear();
502 scratch.owners.clear();
503 return;
504 };
505 let mut applier = app.composition.applier_mut();
506 applier.scene_nodes_attached_to_into(
507 nodes,
508 primary_root,
509 &mut scratch.attached,
510 &mut scratch.attachment,
511 );
512 if scratch.attached.is_empty() {
513 scratch.owners.clear();
514 return;
515 }
516 scratch.seen.clear();
517 if app.app_context.window_roots().is_empty() {
518 scratch.owners.clear();
519 if let Some(index) = surfaces
520 .iter()
521 .position(|surface| surface.id == RootId::Primary)
522 {
523 let surface = &mut surfaces[index];
524 for node in scratch.attached.iter().flatten().copied() {
525 append_surface_dirty_node(surface, index, node, lane, &mut scratch.seen);
526 }
527 }
528 return;
529 }
530 cranpose_ui::nearest_window_roots_into(
531 &mut applier,
532 scratch.attached.iter().flatten().copied(),
533 &mut scratch.owners,
534 &mut scratch.window_roots,
535 );
536 for (node, owner) in scratch.attached.iter().flatten().zip(&scratch.owners) {
537 let node = *node;
538 if let Some(index) = surfaces
539 .iter()
540 .position(|surface| surface.owns_nodes_under(*owner))
541 {
542 let surface = &mut surfaces[index];
543 append_surface_dirty_node(surface, index, node, lane, &mut scratch.seen);
544 }
545 }
546}
547
548pub(crate) fn route_geometry_scene_nodes<R: Renderer>(
554 app: &mut ShellApp,
555 surfaces: &mut [RootSurface<R>],
556 laid_out: impl IntoIterator<Item = NodeId>,
557 moved: &[MovedSceneNode],
558 scratch: &mut SurfaceRoutingScratch,
559) {
560 if moved.is_empty() {
561 route_nodes_by_surface(app, surfaces, laid_out, SurfaceDirtyLane::Layout, scratch);
562 return;
563 }
564 scratch.laid_out_starts.clear();
565 scratch.laid_out_starts.extend(
566 surfaces
567 .iter()
568 .map(|surface| surface.scoped_layout_scene_nodes.len()),
569 );
570 route_nodes_by_surface(app, surfaces, laid_out, SurfaceDirtyLane::Layout, scratch);
571 let mut routed = std::mem::take(&mut scratch.moved);
572 routed.clear();
573 {
574 let applier = app.composition.applier_mut();
575 scratch.laid_out.reset(&applier);
576 for (surface, &start) in surfaces.iter().zip(&scratch.laid_out_starts) {
577 for &node in &surface.scoped_layout_scene_nodes[start..] {
578 scratch.laid_out.set(&applier, node, 1);
579 }
580 }
581 let marks = &scratch.laid_out;
582 let window_roots = !app.app_context.window_roots().is_empty();
583 let mut last_placer = None;
586 for &moved in moved {
587 let placed_again = moved.placer.is_some_and(|placer| match last_placer {
588 Some((last, laid_out)) if last == placer => laid_out,
589 _ => {
590 let laid_out = marks.get(&applier, placer) != 0;
591 last_placer = Some((placer, laid_out));
592 laid_out
593 }
594 });
595 if !placed_again || window_roots && is_window_root(&applier, moved.node) {
596 routed.push(moved.node);
597 }
598 }
599 }
600 route_nodes_by_surface(
601 app,
602 surfaces,
603 routed.iter().copied(),
604 SurfaceDirtyLane::Moved,
605 scratch,
606 );
607 scratch.moved = routed;
608}
609
610fn is_window_root(applier: &MemoryApplier, node: NodeId) -> bool {
612 applier.get_ref(node).is_ok_and(|node| {
613 let node: &dyn std::any::Any = node;
614 node.downcast_ref::<LayoutNode>()
615 .is_some_and(LayoutNode::is_window_root)
616 })
617}
618
619pub struct SurfaceMut<'a, R: Renderer> {
626 pub(crate) shell: &'a mut AppShell<R>,
627 pub(crate) index: usize,
628}
629
630impl<'a, R> SurfaceMut<'a, R>
631where
632 R: Renderer,
633 R::Error: Debug,
634{
635 pub(crate) fn new(shell: &'a mut AppShell<R>, index: usize) -> Self {
636 Self { shell, index }
637 }
638
639 pub fn shell(&mut self) -> &mut AppShell<R> {
642 self.shell
643 }
644
645 pub(crate) fn shell_app(&mut self) -> &mut ShellApp {
646 &mut self.shell.app
647 }
648
649 pub(crate) fn shell_app_ref(&self) -> &ShellApp {
650 &self.shell.app
651 }
652
653 pub(crate) fn surface(&self) -> &RootSurface<R> {
654 &self.shell.surfaces[self.index]
655 }
656
657 pub(crate) fn surface_mut(&mut self) -> &mut RootSurface<R> {
658 &mut self.shell.surfaces[self.index]
659 }
660
661 pub(crate) fn parts(&mut self) -> (&mut ShellApp, &mut RootSurface<R>) {
662 let shell = &mut *self.shell;
663 (&mut shell.app, &mut shell.surfaces[self.index])
664 }
665
666 pub fn id(&self) -> RootId {
668 self.surface().id
669 }
670
671 pub fn root(&self) -> Option<NodeId> {
673 let surface = self.surface();
674 surface.root_node(self.shell_app_ref())
675 }
676
677 pub fn renderer(&mut self) -> &mut R {
679 &mut self.surface_mut().renderer
680 }
681
682 pub fn with_renderer<T>(&mut self, block: impl FnOnce(&mut R) -> T) -> T {
685 let (app, surface) = self.parts();
686 app.app_context.enter(|| block(&mut surface.renderer))
687 }
688
689 pub fn scene(&self) -> &R::Scene {
691 self.surface().renderer.scene()
692 }
693
694 pub fn set_viewport(&mut self, width: f32, height: f32) {
702 self.surface_mut().viewport = (width, height);
703 match self.id() {
704 RootId::Primary => self.shell_app().request_forced_layout_pass(),
705 RootId::Window(_) => {
706 if let Some(root) = self.root() {
707 self.shell_app_ref()
708 .app_context
709 .enter(|| cranpose_ui::schedule_measure_repass(root));
710 }
711 self.shell_app().request_layout_pass();
712 }
713 }
714 let root = self.root();
715 self.surface_mut().invalidate_scene_root(root);
716 self.mark_dirty();
717 }
718
719 pub fn set_screen_origin(&mut self, origin: Option<Point>) {
725 self.surface_mut().screen_origin = origin;
726 }
727
728 pub fn screen_origin(&self) -> Option<Point> {
731 self.surface().screen_origin
732 }
733
734 pub fn viewport_size(&self) -> (f32, f32) {
736 self.surface().viewport
737 }
738
739 pub fn set_buffer_size(&mut self, width: u32, height: u32) {
741 self.surface_mut().buffer_size = (width, height);
742 }
743
744 pub fn buffer_size(&self) -> (u32, u32) {
746 self.surface().buffer_size
747 }
748
749 pub fn mark_dirty(&mut self) {
751 self.surface_mut().is_dirty = true;
752 }
753
754 pub fn needs_redraw(&self) -> bool {
758 self.shell_app_ref()
759 .app_context
760 .enter(|| self.surface().needs_redraw_in_context(self.shell_app_ref()))
761 }
762
763 pub fn has_active_pointer_gesture(&self) -> bool {
766 self.surface().has_active_pointer_gesture()
767 }
768
769 pub fn has_active_primary_gesture(&self) -> bool {
774 self.surface().has_active_primary_gesture()
775 }
776
777 pub fn last_update_result(&self) -> FrameUpdateResult {
780 self.surface().last_update
781 }
782
783 pub fn frame_owed(&self) -> bool {
788 self.surface().frame_owed
789 }
790
791 pub fn take_frame_owed(&mut self) -> bool {
793 std::mem::take(&mut self.surface_mut().frame_owed)
794 }
795
796 fn compute_frame_schedule(&self) -> FrameSchedule {
797 self.surface()
798 .compute_frame_schedule(self.shell_app_ref(), self.shell.any_surface_dirty())
799 }
800
801 pub fn frame_schedule(&self) -> FrameSchedule {
804 let schedule = self.compute_frame_schedule();
805 self.surface().frame_scheduler.record(schedule);
806 schedule
807 }
808
809 pub fn schedule_platform_frame<D>(&self, driver: &D) -> FrameSchedule
811 where
812 D: PlatformFrameDriver + ?Sized,
813 {
814 let schedule = self.compute_frame_schedule();
815 self.surface().frame_scheduler.schedule(schedule, driver);
816 schedule
817 }
818
819 pub fn frame_scheduler_snapshot(&self) -> FrameSchedule {
821 self.surface().frame_scheduler.snapshot()
822 }
823
824 pub fn set_frame_rate_preference(&mut self, preference: FrameRatePreference) {
827 self.surface_mut().frame_rate_preference = preference;
828 }
829
830 pub fn frame_rate_preference(&self) -> FrameRatePreference {
832 self.surface().frame_rate_preference
833 }
834
835 pub fn dev_overlay_control_center(&self, mode: FramePacingMode) -> Option<(f32, f32)> {
838 self.surface().dev_overlay.control_center(mode)
839 }
840
841 pub(crate) fn dev_overlay_press(&mut self, x: f32, y: f32) -> bool {
842 if !self.shell_app_ref().dev_options.frame_pacing_controls {
843 return false;
844 }
845 let Some(mode) = self.surface().dev_overlay.control_at(x, y) else {
846 return false;
847 };
848 self.shell().set_frame_pacing_mode(mode);
849 true
850 }
851
852 pub fn with_layout_tree<T>(&mut self, block: impl FnOnce(Option<&LayoutTree>) -> T) -> T {
854 let (app, surface) = self.parts();
855 let _app_context = app.app_context.enter_scope();
856 block(surface.layout_tree_in_context(app))
857 }
858
859 pub fn with_semantics_tree<T>(&mut self, block: impl FnOnce(Option<&SemanticsTree>) -> T) -> T {
862 let (app, surface) = self.parts();
863 let _app_context = app.app_context.enter_scope();
864 block(surface.semantics_tree_in_context(app))
865 }
866
867 pub fn take_pointer_icon_change(&self) -> Option<PointerIcon> {
870 self.surface().pointer_icon.take_change()
871 }
872
873 pub fn refresh_pointer_icon(&self) {
876 self.surface().pointer_icon.refresh();
877 }
878
879 pub fn set_platform_text_input(&mut self, handler: Rc<dyn PlatformTextInputHandler>) {
884 let id = self.id();
885 let app = self.shell_app();
886 app.text_input_routes.borrow_mut().set_handler(id, handler);
887 app.install_text_input_router();
888 }
889
890 pub fn activate(&mut self) {
893 let id = self.id();
894 self.shell_app()
895 .text_input_routes
896 .borrow_mut()
897 .set_active(id);
898 }
899}
900
901#[expect(clippy::multiple_inherent_impl, reason = "one file per shell concern")]
902impl<R> AppShell<R>
903where
904 R: Renderer,
905 R::Error: Debug,
906{
907 pub fn set_dpad_navigation(&mut self, dpad: bool) {
914 let primary = &mut self.surfaces[0];
915 primary.dpad_navigation = dpad;
916 primary.focus_check_due = dpad;
917 }
918
919 pub(crate) fn end_update(&mut self, laid_out: bool) {
922 for surface in &mut self.surfaces {
923 surface.is_dirty = false;
924 }
925 let primary = &mut self.surfaces[0];
926 if primary.dpad_navigation && (laid_out || primary.focus_check_due) {
927 primary.focus_check_due = false;
928 self.primary().keep_focus();
929 }
930 }
931}