Skip to main content

scene_management/
scene_manager.rs

1// Copyright 2019 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5use crate::lib::{Position, Size};
6use crate::pointerinjector_config::{
7    InjectorViewportChangeFn, InjectorViewportHangingGet, InjectorViewportPublisher,
8    InjectorViewportSpec, InjectorViewportSubscriber,
9};
10use crate::{DisplayMetrics, ViewingDistance};
11use anyhow::{Context, Error, Result};
12use async_trait::async_trait;
13use async_utils::hanging_get::server as hanging_get;
14use fidl::endpoints::{Proxy, create_proxy};
15use fidl_fuchsia_accessibility_scene as a11y_scene;
16use fidl_fuchsia_math as math;
17use fidl_fuchsia_ui_app as ui_app;
18use fidl_fuchsia_ui_composition::{self as ui_comp, ContentId, TransformId};
19use fidl_fuchsia_ui_display_singleton as singleton_display;
20use fidl_fuchsia_ui_pointerinjector_configuration::{
21    SetupRequest as PointerInjectorConfigurationSetupRequest,
22    SetupRequestStream as PointerInjectorConfigurationSetupRequestStream,
23};
24use fidl_fuchsia_ui_views as ui_views;
25use flatland_frame_scheduling_lib::*;
26use fuchsia_async::{self as fasync, TimeoutExt};
27use fuchsia_scenic as scenic;
28use fuchsia_trace as trace;
29use futures::channel::mpsc::{UnboundedReceiver, UnboundedSender, unbounded};
30use futures::channel::oneshot;
31use futures::prelude::*;
32use log::{error, info, warn};
33use math as fmath;
34use std::cell::{Cell, RefCell};
35use std::collections::VecDeque;
36use std::ffi::CStr;
37use std::process;
38use std::rc::Rc;
39use std::sync::{Arc, Weak};
40use zx;
41
42/// Presentation messages.
43pub enum PresentationMessage {
44    /// Request a present call.
45    RequestPresent,
46    // Requests a present call; also, provides a channel that will get a ping back
47    // when the next frame has been presented on screen.
48    RequestPresentWithPingback(oneshot::Sender<()>),
49}
50
51/// Unbounded sender used for presentation messages.
52pub type PresentationSender = UnboundedSender<PresentationMessage>;
53
54/// Unbounded receiver used for presentation messages.
55pub type PresentationReceiver = UnboundedReceiver<PresentationMessage>;
56
57const _CURSOR_SIZE: (u32, u32) = (18, 29);
58const CURSOR_HOTSPOT: (u32, u32) = (2, 4);
59
60// TODO(https://fxbug.dev/42158302): Remove hardcoded scale when Flatland provides
61// what is needed to determine the cursor scale factor.
62const CURSOR_SCALE_MULTIPLIER: u32 = 5;
63const CURSOR_SCALE_DIVIDER: u32 = 4;
64
65const DEFAULT_VIEW_CONNECTION_TIMEOUT: zx::MonotonicDuration =
66    zx::MonotonicDuration::from_seconds(15);
67
68// Converts a cursor size to physical pixels.
69fn physical_cursor_size(value: u32) -> u32 {
70    (CURSOR_SCALE_MULTIPLIER * value) / CURSOR_SCALE_DIVIDER
71}
72
73pub type FlatlandPtr = Arc<ui_comp::FlatlandProxy>;
74
75#[derive(Clone)]
76struct TransformContentIdPair {
77    transform_id: TransformId,
78    content_id: ContentId,
79}
80
81/// FlatlandInstance encapsulates a FIDL connection to a Flatland instance, along with some other
82/// state resulting from initializing the instance in a standard way; see FlatlandInstance::new().
83/// For example, a view is created during initialization, and so FlatlandInstance stores the
84/// corresponding ViewRef and a ParentViewportWatcher FIDL connection.
85struct FlatlandInstance {
86    flatland: FlatlandPtr,
87    view_ref: ui_views::ViewRef,
88    root_transform_id: TransformId,
89    parent_viewport_watcher: ui_comp::ParentViewportWatcherProxy,
90    focuser: ui_views::FocuserProxy,
91}
92
93impl FlatlandInstance {
94    fn new(
95        flatland: ui_comp::FlatlandProxy,
96        view_creation_token: ui_views::ViewCreationToken,
97        id_generator: &mut scenic::flatland::IdGenerator,
98    ) -> Result<FlatlandInstance, Error> {
99        let (parent_viewport_watcher, parent_viewport_watcher_request) =
100            create_proxy::<ui_comp::ParentViewportWatcherMarker>();
101
102        let (focuser, focuser_request) = create_proxy::<ui_views::FocuserMarker>();
103
104        let view_bound_protocols = ui_comp::ViewBoundProtocols {
105            view_focuser: Some(focuser_request),
106            ..Default::default()
107        };
108
109        let view_identity = ui_views::ViewIdentityOnCreation::from(scenic::ViewRefPair::new()?);
110        let view_ref = scenic::duplicate_view_ref(&view_identity.view_ref)?;
111        flatland.create_view2(
112            view_creation_token,
113            view_identity,
114            view_bound_protocols,
115            parent_viewport_watcher_request,
116        )?;
117
118        let root_transform_id = id_generator.next_transform_id();
119        flatland.create_transform(&root_transform_id)?;
120        flatland.set_root_transform(&root_transform_id)?;
121        flatland.set_hit_regions(&root_transform_id, &[])?;
122
123        Ok(FlatlandInstance {
124            flatland: Arc::new(flatland),
125            view_ref,
126            root_transform_id,
127            parent_viewport_watcher,
128            focuser,
129        })
130    }
131}
132
133fn request_present_with_pingback(
134    presentation_sender: &PresentationSender,
135) -> Result<oneshot::Receiver<()>, Error> {
136    let (sender, receiver) = oneshot::channel::<()>();
137    presentation_sender.unbounded_send(PresentationMessage::RequestPresentWithPingback(sender))?;
138    Ok(receiver)
139}
140
141async fn setup_child_view(
142    parent_flatland: &FlatlandInstance,
143    viewport_creation_token: scenic::flatland::ViewportCreationToken,
144    id_generator: &mut scenic::flatland::IdGenerator,
145    client_viewport_size: math::SizeU,
146) -> Result<ui_comp::ChildViewWatcherProxy, Error> {
147    let child_viewport_transform_id = id_generator.next_transform_id();
148    let child_viewport_content_id = id_generator.next_content_id();
149
150    let (child_view_watcher, child_view_watcher_request) =
151        create_proxy::<ui_comp::ChildViewWatcherMarker>();
152
153    {
154        let flatland = &parent_flatland.flatland;
155        flatland.create_transform(&child_viewport_transform_id)?;
156        flatland.add_child(&parent_flatland.root_transform_id, &child_viewport_transform_id)?;
157
158        let link_properties = ui_comp::ViewportProperties {
159            logical_size: Some(client_viewport_size),
160            ..Default::default()
161        };
162
163        flatland.create_viewport(
164            &child_viewport_content_id,
165            viewport_creation_token,
166            &link_properties,
167            child_view_watcher_request,
168        )?;
169        flatland.set_content(&child_viewport_transform_id, &child_viewport_content_id)?;
170    }
171
172    Ok(child_view_watcher)
173}
174
175/// SceneManager manages the platform/framework-controlled part of the global Scenic scene
176/// graph, with the fundamental goal of connecting the physical display to the product-defined user
177/// shell.  The part of the scene graph managed by the scene manager is split between three Flatland
178/// instances, which are linked by view/viewport pairs.
179//
180// The scene graph looks like this:
181//
182//         FD          FD:  FlatlandDisplay
183//         |
184//         R*          R*:  root transform of |root_flatland|,
185//         |                and also the corresponding view/view-ref (see below)
186//        / \
187//       /   \         Rc:  transform holding whatever is necessary to render the cursor
188//     Rpi    Rc
189//      |      \       Rpi: transform with viewport linking to |pointerinjector_flatland|
190//      |       (etc.)      (see docs on struct field for rationale)
191//      |
192//      P*             P*:  root transform of |pointerinjector_flatland|,
193//      |                   and also the corresponding view/view-ref (see below)
194//      |
195//      Pa             Pa:  transform with viewport linking to an external Flatland instance
196//      |                   owned by a11y manager.
197//      |
198//      A*             A*:  root transform of |a11y_flatland| (owned by a11y manager),
199//      |                   and also the corresponding view/view-ref (see below).
200//      |
201//      As             As:  transform with viewport linking to |scene_flatland|.
202//      |
203//      |
204//      S*             S*:  root transform of |scene_flatland|,
205//      |                   and also the corresponding view/view-ref (see below)
206//      |
207//      (user shell)   The session uses the SceneManager.SetRootView() FIDL API to attach the user
208//                     shell to the scene graph depicted above.
209//
210// A11y View can be disabled via `attach_a11y_view` flag. If disabled, Pa and A* is removed from the
211// scene graph.
212//
213// There is a reason why the "corresponding view/view-refs" are called out in the diagram above.
214// When registering an input device with the fuchsia.ui.pointerinjector.Registry API, the Config
215// must specify two ViewRefs, the "context" and the "target"; the former must be a strict ancestor
216// or the former (the target denotes the first eligible view to receive input; it will always be
217// the root of the focus chain).  The context ViewRef is R* and the target ViewRef is P*.  Note that
218// the possibly-inserted accessiblity view is the direct descendant of |pointerinjector_flatland|.
219// This gives the accessiblity manager the ability to give itself focus, and therefore receive all
220// input.
221pub struct SceneManager {
222    // Flatland connection between the physical display and the rest of the scene graph.
223    _display: ui_comp::FlatlandDisplayProxy,
224
225    // The size that will ultimately be assigned to the View created with the
226    // `fuchsia.session.scene.Manager` protocol.
227    client_viewport_size: math::SizeU,
228
229    // Flatland instance that connects to |display|.  Hosts a viewport which connects it to
230    // to a view in |pointerinjector_flatland|.
231    //
232    // See the above diagram of SceneManager's scene graph topology.
233    root_flatland: FlatlandInstance,
234
235    // Flatland instance that sits beneath |root_flatland| in the scene graph.  The reason that this
236    // exists is that two different ViewRefs must be provided when configuring the input pipeline to
237    // inject pointer events into Scenic via fuchsia.ui.pointerinjector.Registry; since a Flatland
238    // instance can have only a single view, we add an additional Flatland instance into the scene
239    // graph to obtain the second view (the "target" view; the "context" view is obtained from
240    // |root_flatland|).
241    //
242    // See the above diagram of SceneManager's scene graph topology.
243    _pointerinjector_flatland: FlatlandInstance,
244
245    // Flatland instance that embeds the system shell (i.e. via the SetRootView() FIDL API).  Its
246    // root view is attached to a viewport owned by the accessibility manager (via
247    // fuchsia.accessibility.scene.Provider/CreateView()).
248    scene_flatland: FlatlandInstance,
249
250    // These are the ViewRefs returned by get_pointerinjection_view_refs().  They are used to
251    // configure input-pipeline handlers for pointer events.
252    context_view_ref: ui_views::ViewRef,
253    target_view_ref: ui_views::ViewRef,
254
255    // Used to sent presentation requests for |root_flatand| and |scene_flatland|, respectively.
256    root_flatland_presentation_sender: PresentationSender,
257    _pointerinjector_flatland_presentation_sender: PresentationSender,
258    scene_flatland_presentation_sender: PresentationSender,
259
260    // Holds a pair of IDs that are used to embed the system shell inside |scene_flatland|, a
261    // TransformId identifying a transform in the scene graph, and a ContentId which identifies a
262    // a viewport that is set as the content of that transform.
263    scene_root_viewport_ids: RefCell<Option<TransformContentIdPair>>,
264
265    // Generates a sequential stream of ContentIds and TransformIds.  By guaranteeing
266    // uniqueness across all Flatland instances, we avoid potential confusion during debugging.
267    id_generator: RefCell<scenic::flatland::IdGenerator>,
268
269    // Supports callers of fuchsia.ui.pointerinjector.configuration.setup.WatchViewport(), allowing
270    // each invocation to subscribe to changes in the viewport region.
271    viewport_hanging_get: Rc<RefCell<InjectorViewportHangingGet>>,
272
273    // Used to publish viewport changes to subscribers of |viewport_hanging_get|.
274    // TODO(https://fxbug.dev/42168647): use this to publish changes to screen resolution.
275    _viewport_publisher: Rc<RefCell<InjectorViewportPublisher>>,
276
277    // Used to position the cursor.
278    cursor_transform_id: Option<TransformId>,
279
280    // Used to track cursor visibility.
281    cursor_visibility: Cell<bool>,
282
283    // Used to track the display metrics for the root scene.
284    display_metrics: DisplayMetrics,
285
286    // Used to convert between logical and physical pixels.
287    //
288    // (physical pixel) = (device_pixel_ratio) * (logical pixel)
289    device_pixel_ratio: f32,
290
291    // Lock to serialize set_root_view calls and prevent interleaving race conditions.
292    set_root_view_lock: futures::lock::Mutex<()>,
293}
294
295/// A [SceneManager] manages a Scenic scene graph, and allows clients to add views to it.
296/// Each [`SceneManager`] can choose how to configure the scene, including lighting, setting the
297/// frames of added views, etc.
298///
299/// # Example
300///
301/// ```
302/// let view_provider = some_apips.connect_to_service::<ViewProviderMarker>()?;
303///
304/// let scenic = connect_to_service::<ScenicMarker>()?;
305/// let mut scene_manager = scene_management::FlatSceneManager::new(scenic).await?;
306/// scene_manager.set_root_view(viewport_token).await?;
307///
308/// ```
309#[async_trait(?Send)]
310pub trait SceneManagerTrait {
311    /// Sets the root view for the scene.
312    ///
313    /// ViewRef will be unset for Flatland views.
314    ///
315    /// Removes any previous root view, as well as all of its descendants.
316    async fn set_root_view(
317        &self,
318        viewport_creation_token: ui_views::ViewportCreationToken,
319        view_ref: Option<ui_views::ViewRef>,
320    ) -> Result<(), Error>;
321
322    /// DEPRECATED: Use ViewportToken version above.
323    /// Sets the root view for the scene.
324    ///
325    /// Removes any previous root view, as well as all of its descendants.
326    async fn set_root_view_deprecated(
327        &self,
328        view_provider: ui_app::ViewProviderProxy,
329    ) -> Result<ui_views::ViewRef, Error>;
330
331    /// Requests a new frame be presented in the scene.
332    fn present_root_view(&self);
333
334    /// Sets the position of the cursor in the current scene. If no cursor has been created it will
335    /// create one using default settings.
336    ///
337    /// # Parameters
338    /// - `position_physical_px`: A [`Position`] struct representing the cursor position, in physical
339    ///   pixels.
340    ///
341    /// # Notes
342    /// If a custom cursor has not been set using `set_cursor_image` or `set_cursor_shape` a default
343    /// cursor will be created and added to the scene.  The implementation of the `SceneManager` trait
344    /// is responsible for translating the raw input position into "pips".
345    fn set_cursor_position(&self, position_physical_px: Position);
346
347    /// Sets the visibility of the cursor in the current scene. The cursor is visible by default.
348    ///
349    /// # Parameters
350    /// - `visible`: Boolean value indicating if the cursor should be visible.
351    fn set_cursor_visibility(&self, visible: bool);
352
353    // Supports the implementation of fuchsia.ui.pointerinjector.configurator.Setup.GetViewRefs()
354    fn get_pointerinjection_view_refs(&self) -> (ui_views::ViewRef, ui_views::ViewRef);
355
356    /// Input pipeline handlers such as TouchInjectorHandler require the display size in order to be
357    /// instantiated.  This method exposes that information.
358    fn get_pointerinjection_display_size(&self) -> crate::lib::Size;
359
360    // Support the hanging get implementation of
361    // fuchsia.ui.pointerinjector.configurator.Setup.WatchViewport().
362    fn get_pointerinjector_viewport_watcher_subscription(&self) -> InjectorViewportSubscriber;
363
364    fn get_display_metrics(&self) -> &DisplayMetrics;
365}
366
367#[async_trait(?Send)]
368impl SceneManagerTrait for SceneManager {
369    /// Sets the root view for the scene.
370    ///
371    /// ViewRef will be unset for Flatland views.
372    ///
373    /// Removes any previous root view, as well as all of its descendants.
374    async fn set_root_view(
375        &self,
376        viewport_creation_token: ui_views::ViewportCreationToken,
377        _view_ref: Option<ui_views::ViewRef>,
378    ) -> Result<(), Error> {
379        let _guard = self.set_root_view_lock.lock().await;
380        self.set_root_view_internal(viewport_creation_token).await.map(|_view_ref| {})
381    }
382
383    /// DEPRECATED: Use ViewportToken version above.
384    /// Sets the root view for the scene.
385    ///
386    /// Removes any previous root view, as well as all of its descendants.
387    async fn set_root_view_deprecated(
388        &self,
389        view_provider: ui_app::ViewProviderProxy,
390    ) -> Result<ui_views::ViewRef, Error> {
391        let _guard = self.set_root_view_lock.lock().await;
392        let link_token_pair = scenic::flatland::ViewCreationTokenPair::new()?;
393
394        // Use view provider to initiate creation of the view which will be connected to the
395        // viewport that we create below.
396        view_provider.create_view2(ui_app::CreateView2Args {
397            view_creation_token: Some(link_token_pair.view_creation_token),
398            ..Default::default()
399        })?;
400
401        self.set_root_view_internal(link_token_pair.viewport_creation_token).await
402    }
403
404    /// Requests a new frame be presented in the scene.
405    fn present_root_view(&self) {
406        self.root_flatland_presentation_sender
407            .unbounded_send(PresentationMessage::RequestPresent)
408            .expect("send failed");
409    }
410
411    // Supports the implementation of fuchsia.ui.pointerinjector.configurator.Setup.GetViewRefs()
412    fn get_pointerinjection_view_refs(&self) -> (ui_views::ViewRef, ui_views::ViewRef) {
413        (
414            scenic::duplicate_view_ref(&self.context_view_ref).expect("failed to copy ViewRef"),
415            scenic::duplicate_view_ref(&self.target_view_ref).expect("failed to copy ViewRef"),
416        )
417    }
418
419    /// Sets the position of the cursor in the current scene. If no cursor has been created it will
420    /// create one using default settings.
421    ///
422    /// # Parameters
423    /// - `position_physical_px`: A [`Position`] struct representing the cursor position, in physical
424    ///   pixels.
425    ///
426    /// # Notes
427    /// If a custom cursor has not been set using `set_cursor_image` or `set_cursor_shape` a default
428    /// cursor will be created and added to the scene.  The implementation of the `SceneManager` trait
429    /// is responsible for translating the raw input position into "pips".
430    fn set_cursor_position(&self, position_physical_px: Position) {
431        if let Some(cursor_transform_id) = self.cursor_transform_id {
432            let position_logical = position_physical_px / self.device_pixel_ratio;
433            let x =
434                position_logical.x.round() as i32 - physical_cursor_size(CURSOR_HOTSPOT.0) as i32;
435            let y =
436                position_logical.y.round() as i32 - physical_cursor_size(CURSOR_HOTSPOT.1) as i32;
437            self.root_flatland
438                .flatland
439                .set_translation(&cursor_transform_id, &fmath::Vec_ { x, y })
440                .expect("fidl error");
441            self.root_flatland_presentation_sender
442                .unbounded_send(PresentationMessage::RequestPresent)
443                .expect("send failed");
444        }
445    }
446
447    /// Sets the visibility of the cursor in the current scene. The cursor is visible by default.
448    ///
449    /// # Parameters
450    /// - `visible`: Boolean value indicating if the cursor should be visible.
451    fn set_cursor_visibility(&self, visible: bool) {
452        if let Some(cursor_transform_id) = self.cursor_transform_id {
453            if self.cursor_visibility.get() != visible {
454                self.cursor_visibility.set(visible);
455                let flatland = &self.root_flatland.flatland;
456                if visible {
457                    flatland
458                        .add_child(&self.root_flatland.root_transform_id, &cursor_transform_id)
459                        .expect("failed to add cursor to scene");
460                } else {
461                    flatland
462                        .remove_child(&self.root_flatland.root_transform_id, &cursor_transform_id)
463                        .expect("failed to remove cursor from scene");
464                }
465                self.root_flatland_presentation_sender
466                    .unbounded_send(PresentationMessage::RequestPresent)
467                    .expect("send failed");
468            }
469        }
470    }
471
472    /// Input pipeline handlers such as TouchInjectorHandler require the display size in order to be
473    /// instantiated.  This method exposes that information.
474    fn get_pointerinjection_display_size(&self) -> Size {
475        // Input pipeline expects size in physical pixels.
476        self.display_metrics.size_in_pixels()
477    }
478
479    // Support the hanging get implementation of
480    // fuchsia.ui.pointerinjector.configurator.Setup.WatchViewport().
481    fn get_pointerinjector_viewport_watcher_subscription(&self) -> InjectorViewportSubscriber {
482        self.viewport_hanging_get.borrow_mut().new_subscriber()
483    }
484
485    fn get_display_metrics(&self) -> &DisplayMetrics {
486        &self.display_metrics
487    }
488}
489
490const ROOT_VIEW_DEBUG_NAME: &str = "SceneManager Display";
491const POINTER_INJECTOR_DEBUG_NAME: &str = "SceneManager PointerInjector";
492const SCENE_DEBUG_NAME: &str = "SceneManager Scene";
493const ROOT_VIEW_PRESENT_TRACING_NAME: &CStr = c"Flatland::PerAppPresent[SceneManager Display]";
494const POINTER_INJECTOR_PRESENT_TRACING_NAME: &CStr =
495    c"Flatland::PerAppPresent[SceneManager PointerInjector]";
496const SCENE_TRACING_NAME: &CStr = c"Flatland::PerAppPresent[SceneManager Scene]";
497
498impl SceneManager {
499    #[allow(clippy::vec_init_then_push, reason = "mass allow for https://fxbug.dev/381896734")]
500    pub async fn new(
501        display: ui_comp::FlatlandDisplayProxy,
502        singleton_display_info: singleton_display::InfoProxy,
503        root_flatland: ui_comp::FlatlandProxy,
504        pointerinjector_flatland: ui_comp::FlatlandProxy,
505        scene_flatland: ui_comp::FlatlandProxy,
506        a11y_view_provider: Option<a11y_scene::ProviderProxy>,
507        display_rotation: u64,
508        display_pixel_density: Option<f32>,
509        viewing_distance: Option<ViewingDistance>,
510    ) -> Result<Self, Error> {
511        // If scenic closes, all the Scenic connections become invalid. This task exits the
512        // process in response.
513        start_exit_on_scenic_closed_task(display.clone());
514
515        let mut id_generator = scenic::flatland::IdGenerator::new();
516
517        // Generate unique transform/content IDs that will be used to create the sub-scenegraphs
518        // in the Flatland instances managed by SceneManager.
519        let pointerinjector_viewport_transform_id = id_generator.next_transform_id();
520        let pointerinjector_viewport_content_id = id_generator.next_content_id();
521
522        root_flatland.set_debug_name(ROOT_VIEW_DEBUG_NAME)?;
523        pointerinjector_flatland.set_debug_name(POINTER_INJECTOR_DEBUG_NAME)?;
524        scene_flatland.set_debug_name(SCENE_DEBUG_NAME)?;
525
526        let root_view_creation_pair = scenic::flatland::ViewCreationTokenPair::new()?;
527        let root_flatland = FlatlandInstance::new(
528            root_flatland,
529            root_view_creation_pair.view_creation_token,
530            &mut id_generator,
531        )?;
532
533        let pointerinjector_view_creation_pair = scenic::flatland::ViewCreationTokenPair::new()?;
534        let pointerinjector_flatland = FlatlandInstance::new(
535            pointerinjector_flatland,
536            pointerinjector_view_creation_pair.view_creation_token,
537            &mut id_generator,
538        )?;
539
540        let scene_view_creation_pair = scenic::flatland::ViewCreationTokenPair::new()?;
541        let scene_flatland = FlatlandInstance::new(
542            scene_flatland,
543            scene_view_creation_pair.view_creation_token,
544            &mut id_generator,
545        )?;
546
547        // Create display metrics, and set the device pixel ratio of FlatlandDisplay.
548        let info = singleton_display_info.get_metrics().await?;
549        let extent_in_px =
550            info.extent_in_px.ok_or_else(|| anyhow::anyhow!("Did not receive display size"))?;
551        let display_metrics = DisplayMetrics::new(
552            Size { width: extent_in_px.width as f32, height: extent_in_px.height as f32 },
553            display_pixel_density,
554            viewing_distance,
555            None,
556        );
557
558        display.set_device_pixel_ratio(&fmath::VecF {
559            x: display_metrics.pixels_per_pip(),
560            y: display_metrics.pixels_per_pip(),
561        })?;
562
563        // Connect the FlatlandDisplay to |root_flatland|'s view.
564        {
565            // We don't need to watch the child view, since we also own it. So, we discard the
566            // client end of the the channel pair.
567            let (_, child_view_watcher_request) = create_proxy::<ui_comp::ChildViewWatcherMarker>();
568
569            display.set_content(
570                root_view_creation_pair.viewport_creation_token,
571                child_view_watcher_request,
572            )?;
573        }
574
575        // Obtain layout info from FlatlandDisplay. Logical size may be different from the
576        // display size if DPR is applied.
577        let layout_info = root_flatland.parent_viewport_watcher.get_layout().await?;
578        let root_viewport_size = layout_info
579            .logical_size
580            .ok_or_else(|| anyhow::anyhow!("Did not receive layout info from the display"))?;
581
582        let (
583            display_rotation_enum,
584            injector_viewport_translation,
585            flip_injector_viewport_dimensions,
586        ) = match display_rotation % 360 {
587            0 => Ok((ui_comp::Orientation::Ccw0Degrees, math::Vec_ { x: 0, y: 0 }, false)),
588            90 => Ok((
589                // Rotation is specified in the opposite winding direction to the
590                // specified |display_rotation| value. Winding in the opposite direction is equal
591                // to -90 degrees, which is equivalent to 270.
592                ui_comp::Orientation::Ccw270Degrees,
593                math::Vec_ { x: root_viewport_size.width as i32, y: 0 },
594                true,
595            )),
596            180 => Ok((
597                ui_comp::Orientation::Ccw180Degrees,
598                math::Vec_ {
599                    x: root_viewport_size.width as i32,
600                    y: root_viewport_size.height as i32,
601                },
602                false,
603            )),
604            270 => Ok((
605                // Rotation is specified in the opposite winding direction to the
606                // specified |display_rotation| value. Winding in the opposite direction is equal
607                // to -270 degrees, which is equivalent to 90.
608                ui_comp::Orientation::Ccw90Degrees,
609                math::Vec_ { x: 0, y: root_viewport_size.height as i32 },
610                true,
611            )),
612            _ => Err(anyhow::anyhow!("Invalid display rotation; must be {{0,90,180,270}}")),
613        }?;
614        let client_viewport_size = match flip_injector_viewport_dimensions {
615            true => {
616                math::SizeU { width: root_viewport_size.height, height: root_viewport_size.width }
617            }
618            false => {
619                math::SizeU { width: root_viewport_size.width, height: root_viewport_size.height }
620            }
621        };
622
623        // Create the pointerinjector view and embed it as a child of the root view.
624        {
625            let flatland = &root_flatland.flatland;
626            flatland.create_transform(&pointerinjector_viewport_transform_id)?;
627            flatland.add_child(
628                &root_flatland.root_transform_id,
629                &pointerinjector_viewport_transform_id,
630            )?;
631            flatland
632                .set_orientation(&pointerinjector_viewport_transform_id, display_rotation_enum)?;
633            flatland.set_translation(
634                &pointerinjector_viewport_transform_id,
635                &injector_viewport_translation,
636            )?;
637
638            let link_properties = ui_comp::ViewportProperties {
639                logical_size: Some(client_viewport_size),
640                ..Default::default()
641            };
642
643            let (_, child_view_watcher_request) = create_proxy::<ui_comp::ChildViewWatcherMarker>();
644
645            flatland.create_viewport(
646                &pointerinjector_viewport_content_id,
647                pointerinjector_view_creation_pair.viewport_creation_token,
648                &link_properties,
649                child_view_watcher_request,
650            )?;
651            flatland.set_content(
652                &pointerinjector_viewport_transform_id,
653                &pointerinjector_viewport_content_id,
654            )?;
655        }
656
657        let mut a11y_view_watcher: Option<ui_comp::ChildViewWatcherProxy> = None;
658        match a11y_view_provider {
659            Some(a11y_view_provider) => {
660                let a11y_view_creation_pair = scenic::flatland::ViewCreationTokenPair::new()?;
661
662                // Bridge the pointerinjector and a11y Flatland instances.
663                a11y_view_watcher = Some(
664                    setup_child_view(
665                        &pointerinjector_flatland,
666                        a11y_view_creation_pair.viewport_creation_token,
667                        &mut id_generator,
668                        client_viewport_size,
669                    )
670                    .await?,
671                );
672
673                // Request for the a11y manager to create its view.
674                a11y_view_provider.create_view(
675                    a11y_view_creation_pair.view_creation_token,
676                    scene_view_creation_pair.viewport_creation_token,
677                )?;
678            }
679            None => {
680                // Bridge the pointerinjector and scene Flatland instances. This skips the A11y View.
681                let _ = setup_child_view(
682                    &pointerinjector_flatland,
683                    scene_view_creation_pair.viewport_creation_token,
684                    &mut id_generator,
685                    client_viewport_size,
686                )
687                .await?;
688            }
689        }
690
691        // Start Present() loops for both Flatland instances, and request that both be presented.
692        let (root_flatland_presentation_sender, root_receiver) = unbounded();
693        start_flatland_presentation_loop(
694            root_receiver,
695            Arc::downgrade(&root_flatland.flatland),
696            ROOT_VIEW_DEBUG_NAME.to_string(),
697        );
698        let (pointerinjector_flatland_presentation_sender, pointerinjector_receiver) = unbounded();
699        start_flatland_presentation_loop(
700            pointerinjector_receiver,
701            Arc::downgrade(&pointerinjector_flatland.flatland),
702            POINTER_INJECTOR_DEBUG_NAME.to_string(),
703        );
704        let (scene_flatland_presentation_sender, scene_receiver) = unbounded();
705        start_flatland_presentation_loop(
706            scene_receiver,
707            Arc::downgrade(&scene_flatland.flatland),
708            SCENE_DEBUG_NAME.to_string(),
709        );
710
711        let mut pingback_channels = Vec::new();
712        pingback_channels.push(request_present_with_pingback(&root_flatland_presentation_sender)?);
713        pingback_channels
714            .push(request_present_with_pingback(&pointerinjector_flatland_presentation_sender)?);
715        pingback_channels.push(request_present_with_pingback(&scene_flatland_presentation_sender)?);
716
717        if let Some(a11y_view_watcher) = a11y_view_watcher {
718            // Wait for a11y view to attach before proceeding.
719            let a11y_view_status = a11y_view_watcher.get_status().await?;
720            match a11y_view_status {
721                ui_comp::ChildViewStatus::ContentHasPresented => {}
722            }
723        }
724
725        // Read device pixel ratio from layout info.
726        let device_pixel_ratio = display_metrics.pixels_per_pip();
727        let viewport_hanging_get: Rc<RefCell<InjectorViewportHangingGet>> =
728            create_viewport_hanging_get({
729                InjectorViewportSpec {
730                    width: display_metrics.width_in_pixels() as f32,
731                    height: display_metrics.height_in_pixels() as f32,
732                    scale: 1. / device_pixel_ratio,
733                    x_offset: 0.,
734                    y_offset: 0.,
735                }
736            });
737        let viewport_publisher =
738            Rc::new(RefCell::new(viewport_hanging_get.borrow_mut().new_publisher()));
739
740        let context_view_ref = scenic::duplicate_view_ref(&root_flatland.view_ref)?;
741        let target_view_ref = scenic::duplicate_view_ref(&pointerinjector_flatland.view_ref)?;
742
743        // Wait for all pingbacks to ensure the scene is fully set up before returning.
744        for receiver in pingback_channels {
745            _ = receiver.await;
746        }
747
748        Ok(SceneManager {
749            _display: display,
750            client_viewport_size,
751            root_flatland,
752            _pointerinjector_flatland: pointerinjector_flatland,
753            scene_flatland,
754            context_view_ref,
755            target_view_ref,
756            root_flatland_presentation_sender,
757            _pointerinjector_flatland_presentation_sender:
758                pointerinjector_flatland_presentation_sender,
759            scene_flatland_presentation_sender,
760            scene_root_viewport_ids: RefCell::new(None),
761            id_generator: RefCell::new(id_generator),
762            viewport_hanging_get,
763            _viewport_publisher: viewport_publisher,
764            cursor_transform_id: None,
765            cursor_visibility: Cell::new(true),
766            display_metrics,
767            device_pixel_ratio,
768            set_root_view_lock: futures::lock::Mutex::new(()),
769        })
770    }
771
772    async fn set_root_view_internal(
773        &self,
774        viewport_creation_token: ui_views::ViewportCreationToken,
775    ) -> Result<ui_views::ViewRef> {
776        // Remove any existing viewport.
777        {
778            let mut scene_root_viewport_ids = self.scene_root_viewport_ids.borrow_mut();
779            if let Some(ids) = &*scene_root_viewport_ids {
780                let flatland = &self.scene_flatland.flatland;
781                flatland
782                    .set_content(&ids.transform_id, &ContentId { value: 0 })
783                    .context("could not set content")?;
784                flatland.remove_child(&self.scene_flatland.root_transform_id, &ids.transform_id)?;
785                flatland
786                    .release_transform(&ids.transform_id)
787                    .context("could not release transform")?;
788                let _ = flatland.release_viewport(&ids.content_id);
789            }
790            *scene_root_viewport_ids = None;
791        }
792
793        // Create new viewport.
794        let ids = {
795            let mut id_generator = self.id_generator.borrow_mut();
796            TransformContentIdPair {
797                transform_id: id_generator.next_transform_id(),
798                content_id: id_generator.next_content_id(),
799            }
800        };
801        let (child_view_watcher, child_view_watcher_request) =
802            create_proxy::<ui_comp::ChildViewWatcherMarker>();
803        {
804            let flatland = &self.scene_flatland.flatland;
805            let viewport_properties = ui_comp::ViewportProperties {
806                logical_size: Some(self.client_viewport_size),
807                ..Default::default()
808            };
809            flatland.create_viewport(
810                &ids.content_id,
811                viewport_creation_token,
812                &viewport_properties,
813                child_view_watcher_request,
814            )?;
815            flatland.create_transform(&ids.transform_id).context("could not create transform")?;
816            flatland.add_child(&self.scene_flatland.root_transform_id, &ids.transform_id)?;
817            flatland
818                .set_content(&ids.transform_id, &ids.content_id)
819                .context("could not set content #2")?;
820        }
821        *self.scene_root_viewport_ids.borrow_mut() = Some(ids);
822
823        // Present the previous scene graph mutations.  This MUST be done before awaiting the result
824        // of get_view_ref() below, because otherwise the view won't become attached to the global
825        // scene graph topology, and the awaited ViewRef will never come.
826        let mut pingback_channels = Vec::new();
827        pingback_channels.push(
828            request_present_with_pingback(&self.scene_flatland_presentation_sender)
829                .context("could not request present with pingback")?,
830        );
831
832        let _child_status = child_view_watcher
833            .get_status()
834            .on_timeout(fasync::MonotonicInstant::after(DEFAULT_VIEW_CONNECTION_TIMEOUT), || {
835                make_timeout_error("fuchsia.ui.composition.ChildViewWatcher")
836            })
837            .await
838            .context("could not call get_status")?;
839        let child_view_ref = child_view_watcher
840            .get_view_ref()
841            .on_timeout(fasync::MonotonicInstant::after(DEFAULT_VIEW_CONNECTION_TIMEOUT), || {
842                make_timeout_error("fuchsia.ui.composition.ChildViewWatcher")
843            })
844            .await
845            .context("could not get view_ref")?;
846        let child_view_ref_copy =
847            scenic::duplicate_view_ref(&child_view_ref).context("could not duplicate view_ref")?;
848
849        let request_focus_result = self
850            .root_flatland
851            .focuser
852            .request_focus(child_view_ref)
853            .on_timeout(fasync::MonotonicInstant::after(DEFAULT_VIEW_CONNECTION_TIMEOUT), || {
854                make_timeout_error("fuchsia.ui.views.Focuser")
855            })
856            .await;
857        match request_focus_result {
858            Err(e) => warn!("Request focus failed with err: {}", e),
859            Ok(Err(value)) => warn!("Request focus failed with err: {:?}", value),
860            Ok(_) => {}
861        }
862        pingback_channels.push(
863            request_present_with_pingback(&self.root_flatland_presentation_sender)
864                .context("could not request present with pingback #2")?,
865        );
866
867        // Wait for all pingbacks to ensure the scene is fully set up before returning.
868        for receiver in pingback_channels {
869            _ = receiver.await;
870        }
871
872        Ok(child_view_ref_copy)
873    }
874}
875
876fn make_timeout_error<T>(protocol_name: &'static str) -> Result<T, fidl::Error> {
877    Err(fidl::Error::ClientChannelClosed {
878        status: zx::Status::TIMED_OUT,
879        protocol_name,
880        epitaph: None,
881    })
882}
883
884pub fn create_viewport_hanging_get(
885    initial_spec: InjectorViewportSpec,
886) -> Rc<RefCell<InjectorViewportHangingGet>> {
887    let notify_fn: InjectorViewportChangeFn = Box::new(|viewport_spec, responder| {
888        if let Err(fidl_error) = responder.send(&(*viewport_spec).into()) {
889            info!("Viewport hanging get notification, FIDL error: {}", fidl_error);
890        }
891        // TODO(https://fxbug.dev/42168817): the HangingGet docs don't explain what value to return.
892        true
893    });
894
895    Rc::new(RefCell::new(hanging_get::HangingGet::new(initial_spec, notify_fn)))
896}
897
898pub fn start_exit_on_scenic_closed_task(flatland_proxy: ui_comp::FlatlandDisplayProxy) {
899    fasync::Task::local(async move {
900        let _ = flatland_proxy.on_closed().await;
901        info!("Scenic died, closing SceneManager too.");
902        process::exit(1);
903    })
904    .detach()
905}
906
907pub fn start_flatland_presentation_loop(
908    mut receiver: PresentationReceiver,
909    weak_flatland: Weak<ui_comp::FlatlandProxy>,
910    debug_name: String,
911) {
912    fasync::Task::local(async move {
913        let mut present_count = 0;
914        let scheduler = ThroughputScheduler::new();
915        let mut flatland_event_stream = {
916            if let Some(flatland) = weak_flatland.upgrade() {
917                flatland.take_event_stream()
918            } else {
919                warn!(
920                    "Failed to upgrade Flatand weak ref; exiting presentation loop for {debug_name}"
921                );
922                return;
923            }
924        };
925
926        let mut channels_awaiting_pingback = VecDeque::from([Vec::new()]);
927
928        loop {
929            futures::select! {
930                message = receiver.next() => {
931                    match message {
932                        Some(PresentationMessage::RequestPresent) => {
933                            scheduler.request_present();
934                        }
935                        Some(PresentationMessage::RequestPresentWithPingback(channel)) => {
936                            channels_awaiting_pingback.back_mut().unwrap().push(channel);
937                            scheduler.request_present();
938                        }
939                        None => {}
940                    }
941                }
942                flatland_event = flatland_event_stream.next() => {
943                    match flatland_event {
944                        Some(Ok(ui_comp::FlatlandEvent::OnNextFrameBegin{ values })) => {
945                            trace::duration!("scene_manager", "SceneManager::OnNextFrameBegin",
946                                             "debug_name" => &*debug_name);
947                            let credits = values
948                                          .additional_present_credits
949                                          .expect("Present credits must exist");
950                            let infos = values
951                                .future_presentation_infos
952                                .expect("Future presentation infos must exist")
953                                .iter()
954                                .map(
955                                |x| PresentationInfo{
956                                    latch_point: zx::MonotonicInstant::from_nanos(x.latch_point.unwrap()),
957                                    presentation_time: zx::MonotonicInstant::from_nanos(
958                                                        x.presentation_time.unwrap())
959                                })
960                                .collect();
961                            scheduler.on_next_frame_begin(credits, infos);
962                        }
963                        Some(Ok(ui_comp::FlatlandEvent::OnFramePresented{ frame_presented_info })) => {
964                            trace::duration!("scene_manager", "SceneManager::OnFramePresented",
965                                             "debug_name" => &*debug_name);
966                            let actual_presentation_time =
967                                zx::MonotonicInstant::from_nanos(frame_presented_info.actual_presentation_time);
968                            let presented_infos: Vec<PresentedInfo> =
969                                frame_presented_info.presentation_infos
970                                .into_iter()
971                                .map(|x| x.into())
972                                .collect();
973
974                            // Pingbacks for presented updates. For each presented frame, drain all
975                            // of the corresponding pingback channels
976                            for _ in 0..presented_infos.len() {
977                                for channel in channels_awaiting_pingback.pop_back().unwrap() {
978                                    _ = channel.send(());
979                                }
980                            }
981
982                            scheduler.on_frame_presented(actual_presentation_time, presented_infos);
983                        }
984                        Some(Ok(ui_comp::FlatlandEvent::OnError{ error })) => {
985                            error!(
986                                "Received FlatlandError code: {}; exiting listener loop for {debug_name}",
987                                error.into_primitive()
988                            );
989                            return;
990                        }
991                        _ => {}
992                    }
993                }
994                present_parameters = scheduler.wait_to_update().fuse() => {
995                    trace::duration!("scene_manager", "SceneManager::Present",
996                                     "debug_name" => &*debug_name);
997
998                    match debug_name.as_str() {
999                        ROOT_VIEW_DEBUG_NAME => {
1000                            trace::flow_begin!("gfx", ROOT_VIEW_PRESENT_TRACING_NAME, present_count.into());
1001                        }
1002                        POINTER_INJECTOR_DEBUG_NAME => {
1003                            trace::flow_begin!("gfx", POINTER_INJECTOR_PRESENT_TRACING_NAME, present_count.into());
1004                        }
1005                        SCENE_DEBUG_NAME => {
1006                            trace::flow_begin!("gfx", SCENE_TRACING_NAME, present_count.into());
1007                        }
1008                        _ => {
1009                            warn!("SceneManager::Present with unknown debug_name {:?}", debug_name);
1010                        }
1011                    }
1012                    present_count += 1;
1013                    channels_awaiting_pingback.push_front(Vec::new());
1014                    if let Some(flatland) = weak_flatland.upgrade() {
1015                        flatland
1016                            .present(present_parameters.into())
1017                            .expect("Present failed for {debug_name}");
1018                    } else {
1019                        warn!(
1020                            "Failed to upgrade Flatand weak ref; exiting listener loop for {debug_name}"
1021                        );
1022                        return;
1023                    }
1024            }
1025        }
1026    }})
1027    .detach()
1028}
1029
1030pub fn handle_pointer_injector_configuration_setup_request_stream(
1031    mut request_stream: PointerInjectorConfigurationSetupRequestStream,
1032    scene_manager: Rc<dyn SceneManagerTrait>,
1033) {
1034    fasync::Task::local(async move {
1035        let subscriber = scene_manager.get_pointerinjector_viewport_watcher_subscription();
1036
1037        loop {
1038            let request = request_stream.try_next().await;
1039            match request {
1040                Ok(Some(PointerInjectorConfigurationSetupRequest::GetViewRefs { responder })) => {
1041                    let (context_view_ref, target_view_ref) =
1042                        scene_manager.get_pointerinjection_view_refs();
1043                    if let Err(e) = responder.send(context_view_ref, target_view_ref) {
1044                        warn!("Failed to send GetViewRefs() response: {}", e);
1045                    }
1046                }
1047                Ok(Some(PointerInjectorConfigurationSetupRequest::WatchViewport { responder })) => {
1048                    if let Err(e) = subscriber.register(responder) {
1049                        warn!("Failed to register WatchViewport() subscriber: {}", e);
1050                    }
1051                }
1052                Ok(None) => {
1053                    return;
1054                }
1055                Err(e) => {
1056                    error!("Error obtaining SetupRequest: {}", e);
1057                    return;
1058                }
1059            }
1060        }
1061    })
1062    .detach()
1063}