scene_management/
scene_manager.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
// Copyright 2019 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

use crate::pointerinjector_config::{
    InjectorViewportChangeFn, InjectorViewportHangingGet, InjectorViewportPublisher,
    InjectorViewportSpec, InjectorViewportSubscriber,
};
use crate::{DisplayMetrics, ViewingDistance};
use anyhow::{Context, Error, Result};
use async_trait::async_trait;
use async_utils::hanging_get::server as hanging_get;
use fidl::endpoints::{create_proxy, Proxy};
use fidl_fuchsia_ui_composition::{self as ui_comp, ContentId, TransformId};
use fidl_fuchsia_ui_pointerinjector_configuration::{
    SetupRequest as PointerInjectorConfigurationSetupRequest,
    SetupRequestStream as PointerInjectorConfigurationSetupRequestStream,
};
use flatland_frame_scheduling_lib::*;
use fuchsia_sync::Mutex;
use futures::channel::mpsc::{unbounded, UnboundedReceiver, UnboundedSender};
use futures::channel::oneshot;
use futures::prelude::*;
use input_pipeline::Size;
use log::{error, info, warn};
use std::collections::VecDeque;
use std::ffi::CStr;
use std::process;
use std::sync::{Arc, Weak};
use {
    fidl_fuchsia_accessibility_scene as a11y_scene, fidl_fuchsia_math as math,
    fidl_fuchsia_ui_app as ui_app, fidl_fuchsia_ui_display_singleton as singleton_display,
    fidl_fuchsia_ui_views as ui_views, fuchsia_async as fasync, fuchsia_scenic as scenic,
    fuchsia_trace as trace, math as fmath,
};

/// Presentation messages.
pub enum PresentationMessage {
    /// Request a present call.
    RequestPresent,
    // Requests a present call; also, provides a channel that will get a ping back
    // when the next frame has been presented on screen.
    RequestPresentWithPingback(oneshot::Sender<()>),
}

/// Unbounded sender used for presentation messages.
pub type PresentationSender = UnboundedSender<PresentationMessage>;

/// Unbounded receiver used for presentation messages.
pub type PresentationReceiver = UnboundedReceiver<PresentationMessage>;

const _CURSOR_SIZE: (u32, u32) = (18, 29);
const CURSOR_HOTSPOT: (u32, u32) = (2, 4);

// TODO(https://fxbug.dev/42158302): Remove hardcoded scale when Flatland provides
// what is needed to determine the cursor scale factor.
const CURSOR_SCALE_MULTIPLIER: u32 = 5;
const CURSOR_SCALE_DIVIDER: u32 = 4;

// Converts a cursor size to physical pixels.
fn physical_cursor_size(value: u32) -> u32 {
    (CURSOR_SCALE_MULTIPLIER * value) / CURSOR_SCALE_DIVIDER
}

pub type FlatlandPtr = Arc<Mutex<ui_comp::FlatlandProxy>>;

#[derive(Clone)]
struct TransformContentIdPair {
    transform_id: TransformId,
    content_id: ContentId,
}

/// FlatlandInstance encapsulates a FIDL connection to a Flatland instance, along with some other
/// state resulting from initializing the instance in a standard way; see FlatlandInstance::new().
/// For example, a view is created during initialization, and so FlatlandInstance stores the
/// corresponding ViewRef and a ParentViewportWatcher FIDL connection.
struct FlatlandInstance {
    // TODO(https://fxbug.dev/42168246): Arc<Mutex<>>, yuck.
    flatland: FlatlandPtr,
    view_ref: ui_views::ViewRef,
    root_transform_id: TransformId,
    parent_viewport_watcher: ui_comp::ParentViewportWatcherProxy,
    focuser: ui_views::FocuserProxy,
}

impl FlatlandInstance {
    fn new(
        flatland: ui_comp::FlatlandProxy,
        view_creation_token: ui_views::ViewCreationToken,
        id_generator: &mut scenic::flatland::IdGenerator,
    ) -> Result<FlatlandInstance, Error> {
        let (parent_viewport_watcher, parent_viewport_watcher_request) =
            create_proxy::<ui_comp::ParentViewportWatcherMarker>();

        let (focuser, focuser_request) = create_proxy::<ui_views::FocuserMarker>();

        let view_bound_protocols = ui_comp::ViewBoundProtocols {
            view_focuser: Some(focuser_request),
            ..Default::default()
        };

        let view_identity = ui_views::ViewIdentityOnCreation::from(scenic::ViewRefPair::new()?);
        let view_ref = scenic::duplicate_view_ref(&view_identity.view_ref)?;
        flatland.create_view2(
            view_creation_token,
            view_identity,
            view_bound_protocols,
            parent_viewport_watcher_request,
        )?;

        let root_transform_id = id_generator.next_transform_id();
        flatland.create_transform(&root_transform_id)?;
        flatland.set_root_transform(&root_transform_id)?;

        Ok(FlatlandInstance {
            flatland: Arc::new(Mutex::new(flatland)),
            view_ref,
            root_transform_id,
            parent_viewport_watcher,
            focuser,
        })
    }
}

fn request_present_with_pingback(
    presentation_sender: &PresentationSender,
) -> Result<oneshot::Receiver<()>, Error> {
    let (sender, receiver) = oneshot::channel::<()>();
    presentation_sender.unbounded_send(PresentationMessage::RequestPresentWithPingback(sender))?;
    Ok(receiver)
}

async fn setup_child_view(
    parent_flatland: &FlatlandInstance,
    viewport_creation_token: scenic::flatland::ViewportCreationToken,
    id_generator: &mut scenic::flatland::IdGenerator,
    client_viewport_size: math::SizeU,
) -> Result<ui_comp::ChildViewWatcherProxy, Error> {
    let child_viewport_transform_id = id_generator.next_transform_id();
    let child_viewport_content_id = id_generator.next_content_id();

    let (child_view_watcher, child_view_watcher_request) =
        create_proxy::<ui_comp::ChildViewWatcherMarker>();

    {
        let flatland = parent_flatland.flatland.lock();
        flatland.create_transform(&child_viewport_transform_id)?;
        flatland.add_child(&parent_flatland.root_transform_id, &child_viewport_transform_id)?;

        let link_properties = ui_comp::ViewportProperties {
            logical_size: Some(client_viewport_size),
            ..Default::default()
        };

        flatland.create_viewport(
            &child_viewport_content_id,
            viewport_creation_token,
            &link_properties,
            child_view_watcher_request,
        )?;
        flatland.set_content(&child_viewport_transform_id, &child_viewport_content_id)?;
    }

    Ok(child_view_watcher)
}

/// SceneManager manages the platform/framework-controlled part of the global Scenic scene
/// graph, with the fundamental goal of connecting the physical display to the product-defined user
/// shell.  The part of the scene graph managed by the scene manager is split between three Flatland
/// instances, which are linked by view/viewport pairs.
//
// The scene graph looks like this:
//
//         FD          FD:  FlatlandDisplay
//         |
//         R*          R*:  root transform of |root_flatland|,
//         |                and also the corresponding view/view-ref (see below)
//        / \
//       /   \         Rc:  transform holding whatever is necessary to render the cursor
//     Rpi    Rc
//      |      \       Rpi: transform with viewport linking to |pointerinjector_flatland|
//      |       (etc.)      (see docs on struct field for rationale)
//      |
//      P*             P*:  root transform of |pointerinjector_flatland|,
//      |                   and also the corresponding view/view-ref (see below)
//      |
//      Pa             Pa:  transform with viewport linking to an external Flatland instance
//      |                   owned by a11y manager.
//      |
//      A*             A*:  root transform of |a11y_flatland| (owned by a11y manager),
//      |                   and also the corresponding view/view-ref (see below).
//      |
//      As             As:  transform with viewport linking to |scene_flatland|.
//      |
//      |
//      S*             S*:  root transform of |scene_flatland|,
//      |                   and also the corresponding view/view-ref (see below)
//      |
//      (user shell)   The session uses the SceneManager.SetRootView() FIDL API to attach the user
//                     shell to the scene graph depicted above.
//
// A11y View can be disabled via `attach_a11y_view` flag. If disabled, Pa and A* is removed from the
// scene graph.
//
// There is a reason why the "corresponding view/view-refs" are called out in the diagram above.
// When registering an input device with the fuchsia.ui.pointerinjector.Registry API, the Config
// must specify two ViewRefs, the "context" and the "target"; the former must be a strict ancestor
// or the former (the target denotes the first eligible view to receive input; it will always be
// the root of the focus chain).  The context ViewRef is R* and the target ViewRef is P*.  Note that
// the possibly-inserted accessiblity view is the direct descendant of |pointerinjector_flatland|.
// This gives the accessiblity manager the ability to give itself focus, and therefore receive all
// input.
pub struct SceneManager {
    // Flatland connection between the physical display and the rest of the scene graph.
    _display: ui_comp::FlatlandDisplayProxy,

    // The size that will ultimately be assigned to the View created with the
    // `fuchsia.session.scene.Manager` protocol.
    client_viewport_size: math::SizeU,

    // Flatland instance that connects to |display|.  Hosts a viewport which connects it to
    // to a view in |pointerinjector_flatland|.
    //
    // See the above diagram of SceneManager's scene graph topology.
    root_flatland: FlatlandInstance,

    // Flatland instance that sits beneath |root_flatland| in the scene graph.  The reason that this
    // exists is that two different ViewRefs must be provided when configuring the input pipeline to
    // inject pointer events into Scenic via fuchsia.ui.pointerinjector.Registry; since a Flatland
    // instance can have only a single view, we add an additional Flatland instance into the scene
    // graph to obtain the second view (the "target" view; the "context" view is obtained from
    // |root_flatland|).
    //
    // See the above diagram of SceneManager's scene graph topology.
    _pointerinjector_flatland: FlatlandInstance,

    // Flatland instance that embeds the system shell (i.e. via the SetRootView() FIDL API).  Its
    // root view is attached to a viewport owned by the accessibility manager (via
    // fuchsia.accessibility.scene.Provider/CreateView()).
    scene_flatland: FlatlandInstance,

    // These are the ViewRefs returned by get_pointerinjection_view_refs().  They are used to
    // configure input-pipeline handlers for pointer events.
    context_view_ref: ui_views::ViewRef,
    target_view_ref: ui_views::ViewRef,

    // Used to sent presentation requests for |root_flatand| and |scene_flatland|, respectively.
    root_flatland_presentation_sender: PresentationSender,
    _pointerinjector_flatland_presentation_sender: PresentationSender,
    scene_flatland_presentation_sender: PresentationSender,

    // Holds a pair of IDs that are used to embed the system shell inside |scene_flatland|, a
    // TransformId identifying a transform in the scene graph, and a ContentId which identifies a
    // a viewport that is set as the content of that transform.
    scene_root_viewport_ids: Option<TransformContentIdPair>,

    // Generates a sequential stream of ContentIds and TransformIds.  By guaranteeing
    // uniqueness across all Flatland instances, we avoid potential confusion during debugging.
    id_generator: scenic::flatland::IdGenerator,

    // Supports callers of fuchsia.ui.pointerinjector.configuration.setup.WatchViewport(), allowing
    // each invocation to subscribe to changes in the viewport region.
    viewport_hanging_get: Arc<Mutex<InjectorViewportHangingGet>>,

    // Used to publish viewport changes to subscribers of |viewport_hanging_get|.
    // TODO(https://fxbug.dev/42168647): use this to publish changes to screen resolution.
    _viewport_publisher: Arc<Mutex<InjectorViewportPublisher>>,

    // Used to position the cursor.
    cursor_transform_id: Option<TransformId>,

    // Used to track cursor visibility.
    cursor_visibility: bool,

    // Used to track the display metrics for the root scene.
    display_metrics: DisplayMetrics,

    // Used to convert between logical and physical pixels.
    //
    // (physical pixel) = (device_pixel_ratio) * (logical pixel)
    device_pixel_ratio: f32,
}

/// A [SceneManager] manages a Scenic scene graph, and allows clients to add views to it.
/// Each [`SceneManager`] can choose how to configure the scene, including lighting, setting the
/// frames of added views, etc.
///
/// # Example
///
/// ```
/// let view_provider = some_apips.connect_to_service::<ViewProviderMarker>()?;
///
/// let scenic = connect_to_service::<ScenicMarker>()?;
/// let mut scene_manager = scene_management::FlatSceneManager::new(scenic).await?;
/// scene_manager.set_root_view(viewport_token).await?;
///
/// ```
#[async_trait]
pub trait SceneManagerTrait: Send {
    /// Sets the root view for the scene.
    ///
    /// ViewRef will be unset for Flatland views.
    ///
    /// Removes any previous root view, as well as all of its descendants.
    async fn set_root_view(
        &mut self,
        viewport_creation_token: ui_views::ViewportCreationToken,
        view_ref: Option<ui_views::ViewRef>,
    ) -> Result<(), Error>;

    /// DEPRECATED: Use ViewportToken version above.
    /// Sets the root view for the scene.
    ///
    /// Removes any previous root view, as well as all of its descendants.
    async fn set_root_view_deprecated(
        &mut self,
        view_provider: ui_app::ViewProviderProxy,
    ) -> Result<ui_views::ViewRef, Error>;

    /// Requests a new frame be presented in the scene.
    fn present_root_view(&self);

    /// Sets the position of the cursor in the current scene. If no cursor has been created it will
    /// create one using default settings.
    ///
    /// # Parameters
    /// - `position_physical_px`: A [`Position`] struct representing the cursor position, in physical
    ///   pixels.
    ///
    /// # Notes
    /// If a custom cursor has not been set using `set_cursor_image` or `set_cursor_shape` a default
    /// cursor will be created and added to the scene.  The implementation of the `SceneManager` trait
    /// is responsible for translating the raw input position into "pips".
    fn set_cursor_position(&mut self, position_physical_px: input_pipeline::Position);

    /// Sets the visibility of the cursor in the current scene. The cursor is visible by default.
    ///
    /// # Parameters
    /// - `visible`: Boolean value indicating if the cursor should be visible.
    fn set_cursor_visibility(&mut self, visible: bool);

    // Supports the implementation of fuchsia.ui.pointerinjector.configurator.Setup.GetViewRefs()
    fn get_pointerinjection_view_refs(&self) -> (ui_views::ViewRef, ui_views::ViewRef);

    /// Input pipeline handlers such as TouchInjectorHandler require the display size in order to be
    /// instantiated.  This method exposes that information.
    fn get_pointerinjection_display_size(&self) -> input_pipeline::Size;

    // Support the hanging get implementation of
    // fuchsia.ui.pointerinjector.configurator.Setup.WatchViewport().
    fn get_pointerinjector_viewport_watcher_subscription(&self) -> InjectorViewportSubscriber;

    fn get_display_metrics(&self) -> &DisplayMetrics;
}

#[async_trait]
impl SceneManagerTrait for SceneManager {
    /// Sets the root view for the scene.
    ///
    /// ViewRef will be unset for Flatland views.
    ///
    /// Removes any previous root view, as well as all of its descendants.
    async fn set_root_view(
        &mut self,
        viewport_creation_token: ui_views::ViewportCreationToken,
        _view_ref: Option<ui_views::ViewRef>,
    ) -> Result<(), Error> {
        self.set_root_view_internal(viewport_creation_token).await.map(|_view_ref| {})
    }

    /// DEPRECATED: Use ViewportToken version above.
    /// Sets the root view for the scene.
    ///
    /// Removes any previous root view, as well as all of its descendants.
    async fn set_root_view_deprecated(
        &mut self,
        view_provider: ui_app::ViewProviderProxy,
    ) -> Result<ui_views::ViewRef, Error> {
        let link_token_pair = scenic::flatland::ViewCreationTokenPair::new()?;

        // Use view provider to initiate creation of the view which will be connected to the
        // viewport that we create below.
        view_provider.create_view2(ui_app::CreateView2Args {
            view_creation_token: Some(link_token_pair.view_creation_token),
            ..Default::default()
        })?;

        self.set_root_view_internal(link_token_pair.viewport_creation_token).await
    }

    /// Requests a new frame be presented in the scene.
    fn present_root_view(&self) {
        self.root_flatland_presentation_sender
            .unbounded_send(PresentationMessage::RequestPresent)
            .expect("send failed");
    }

    // Supports the implementation of fuchsia.ui.pointerinjector.configurator.Setup.GetViewRefs()
    fn get_pointerinjection_view_refs(&self) -> (ui_views::ViewRef, ui_views::ViewRef) {
        (
            scenic::duplicate_view_ref(&self.context_view_ref).expect("failed to copy ViewRef"),
            scenic::duplicate_view_ref(&self.target_view_ref).expect("failed to copy ViewRef"),
        )
    }

    /// Sets the position of the cursor in the current scene. If no cursor has been created it will
    /// create one using default settings.
    ///
    /// # Parameters
    /// - `position_physical_px`: A [`Position`] struct representing the cursor position, in physical
    ///   pixels.
    ///
    /// # Notes
    /// If a custom cursor has not been set using `set_cursor_image` or `set_cursor_shape` a default
    /// cursor will be created and added to the scene.  The implementation of the `SceneManager` trait
    /// is responsible for translating the raw input position into "pips".
    fn set_cursor_position(&mut self, position_physical_px: input_pipeline::Position) {
        if let Some(cursor_transform_id) = self.cursor_transform_id {
            let position_logical = position_physical_px / self.device_pixel_ratio;
            let x =
                position_logical.x.round() as i32 - physical_cursor_size(CURSOR_HOTSPOT.0) as i32;
            let y =
                position_logical.y.round() as i32 - physical_cursor_size(CURSOR_HOTSPOT.1) as i32;
            let flatland = self.root_flatland.flatland.lock();
            flatland
                .set_translation(&cursor_transform_id, &fmath::Vec_ { x, y })
                .expect("fidl error");
            self.root_flatland_presentation_sender
                .unbounded_send(PresentationMessage::RequestPresent)
                .expect("send failed");
        }
    }

    /// Sets the visibility of the cursor in the current scene. The cursor is visible by default.
    ///
    /// # Parameters
    /// - `visible`: Boolean value indicating if the cursor should be visible.
    fn set_cursor_visibility(&mut self, visible: bool) {
        if let Some(cursor_transform_id) = self.cursor_transform_id {
            if self.cursor_visibility != visible {
                self.cursor_visibility = visible;
                let flatland = self.root_flatland.flatland.lock();
                if visible {
                    flatland
                        .add_child(&self.root_flatland.root_transform_id, &cursor_transform_id)
                        .expect("failed to add cursor to scene");
                } else {
                    flatland
                        .remove_child(&self.root_flatland.root_transform_id, &cursor_transform_id)
                        .expect("failed to remove cursor from scene");
                }
                self.root_flatland_presentation_sender
                    .unbounded_send(PresentationMessage::RequestPresent)
                    .expect("send failed");
            }
        }
    }

    /// Input pipeline handlers such as TouchInjectorHandler require the display size in order to be
    /// instantiated.  This method exposes that information.
    fn get_pointerinjection_display_size(&self) -> Size {
        // Input pipeline expects size in physical pixels.
        self.display_metrics.size_in_pixels()
    }

    // Support the hanging get implementation of
    // fuchsia.ui.pointerinjector.configurator.Setup.WatchViewport().
    fn get_pointerinjector_viewport_watcher_subscription(&self) -> InjectorViewportSubscriber {
        self.viewport_hanging_get.lock().new_subscriber()
    }

    fn get_display_metrics(&self) -> &DisplayMetrics {
        &self.display_metrics
    }
}

const ROOT_VIEW_DEBUG_NAME: &str = "SceneManager Display";
const POINTER_INJECTOR_DEBUG_NAME: &str = "SceneManager PointerInjector";
const SCENE_DEBUG_NAME: &str = "SceneManager Scene";
const ROOT_VIEW_PRESENT_TRACING_NAME: &CStr = c"Flatland::PerAppPresent[SceneManager Display]";
const POINTER_INJECTOR_PRESENT_TRACING_NAME: &CStr =
    c"Flatland::PerAppPresent[SceneManager PointerInjector]";
const SCENE_TRACING_NAME: &CStr = c"Flatland::PerAppPresent[SceneManager Scene]";

impl SceneManager {
    #[allow(clippy::vec_init_then_push, reason = "mass allow for https://fxbug.dev/381896734")]
    pub async fn new(
        display: ui_comp::FlatlandDisplayProxy,
        singleton_display_info: singleton_display::InfoProxy,
        root_flatland: ui_comp::FlatlandProxy,
        pointerinjector_flatland: ui_comp::FlatlandProxy,
        scene_flatland: ui_comp::FlatlandProxy,
        a11y_view_provider: Option<a11y_scene::ProviderProxy>,
        display_rotation: u64,
        display_pixel_density: Option<f32>,
        viewing_distance: Option<ViewingDistance>,
    ) -> Result<Self, Error> {
        // If scenic closes, all the Scenic connections become invalid. This task exits the
        // process in response.
        start_exit_on_scenic_closed_task(display.clone());

        let mut id_generator = scenic::flatland::IdGenerator::new();

        // Generate unique transform/content IDs that will be used to create the sub-scenegraphs
        // in the Flatland instances managed by SceneManager.
        let pointerinjector_viewport_transform_id = id_generator.next_transform_id();
        let pointerinjector_viewport_content_id = id_generator.next_content_id();

        root_flatland.set_debug_name(ROOT_VIEW_DEBUG_NAME)?;
        pointerinjector_flatland.set_debug_name(POINTER_INJECTOR_DEBUG_NAME)?;
        scene_flatland.set_debug_name(SCENE_DEBUG_NAME)?;

        let root_view_creation_pair = scenic::flatland::ViewCreationTokenPair::new()?;
        let root_flatland = FlatlandInstance::new(
            root_flatland,
            root_view_creation_pair.view_creation_token,
            &mut id_generator,
        )?;

        let pointerinjector_view_creation_pair = scenic::flatland::ViewCreationTokenPair::new()?;
        let pointerinjector_flatland = FlatlandInstance::new(
            pointerinjector_flatland,
            pointerinjector_view_creation_pair.view_creation_token,
            &mut id_generator,
        )?;

        let scene_view_creation_pair = scenic::flatland::ViewCreationTokenPair::new()?;
        let scene_flatland = FlatlandInstance::new(
            scene_flatland,
            scene_view_creation_pair.view_creation_token,
            &mut id_generator,
        )?;

        // Create display metrics, and set the device pixel ratio of FlatlandDisplay.
        let info = singleton_display_info.get_metrics().await?;
        let extent_in_px =
            info.extent_in_px.ok_or_else(|| anyhow::anyhow!("Did not receive display size"))?;
        let display_metrics = DisplayMetrics::new(
            Size { width: extent_in_px.width as f32, height: extent_in_px.height as f32 },
            display_pixel_density,
            viewing_distance,
            None,
        );

        display.set_device_pixel_ratio(&fmath::VecF {
            x: display_metrics.pixels_per_pip(),
            y: display_metrics.pixels_per_pip(),
        })?;

        // Connect the FlatlandDisplay to |root_flatland|'s view.
        {
            // We don't need to watch the child view, since we also own it. So, we discard the
            // client end of the the channel pair.
            let (_, child_view_watcher_request) = create_proxy::<ui_comp::ChildViewWatcherMarker>();

            display.set_content(
                root_view_creation_pair.viewport_creation_token,
                child_view_watcher_request,
            )?;
        }

        // Obtain layout info from FlatlandDisplay. Logical size may be different from the
        // display size if DPR is applied.
        let layout_info = root_flatland.parent_viewport_watcher.get_layout().await?;
        let root_viewport_size = layout_info
            .logical_size
            .ok_or_else(|| anyhow::anyhow!("Did not receive layout info from the display"))?;

        let (
            display_rotation_enum,
            injector_viewport_translation,
            flip_injector_viewport_dimensions,
        ) = match display_rotation % 360 {
            0 => Ok((ui_comp::Orientation::Ccw0Degrees, math::Vec_ { x: 0, y: 0 }, false)),
            90 => Ok((
                // Rotation is specified in the opposite winding direction to the
                // specified |display_rotation| value. Winding in the opposite direction is equal
                // to -90 degrees, which is equivalent to 270.
                ui_comp::Orientation::Ccw270Degrees,
                math::Vec_ { x: root_viewport_size.width as i32, y: 0 },
                true,
            )),
            180 => Ok((
                ui_comp::Orientation::Ccw180Degrees,
                math::Vec_ {
                    x: root_viewport_size.width as i32,
                    y: root_viewport_size.height as i32,
                },
                false,
            )),
            270 => Ok((
                // Rotation is specified in the opposite winding direction to the
                // specified |display_rotation| value. Winding in the opposite direction is equal
                // to -270 degrees, which is equivalent to 90.
                ui_comp::Orientation::Ccw90Degrees,
                math::Vec_ { x: 0, y: root_viewport_size.height as i32 },
                true,
            )),
            _ => Err(anyhow::anyhow!("Invalid display rotation; must be {{0,90,180,270}}")),
        }?;
        let client_viewport_size = match flip_injector_viewport_dimensions {
            true => {
                math::SizeU { width: root_viewport_size.height, height: root_viewport_size.width }
            }
            false => {
                math::SizeU { width: root_viewport_size.width, height: root_viewport_size.height }
            }
        };

        // Create the pointerinjector view and embed it as a child of the root view.
        {
            let flatland = root_flatland.flatland.lock();
            flatland.create_transform(&pointerinjector_viewport_transform_id)?;
            flatland.add_child(
                &root_flatland.root_transform_id,
                &pointerinjector_viewport_transform_id,
            )?;
            flatland
                .set_orientation(&pointerinjector_viewport_transform_id, display_rotation_enum)?;
            flatland.set_translation(
                &pointerinjector_viewport_transform_id,
                &injector_viewport_translation,
            )?;

            let link_properties = ui_comp::ViewportProperties {
                logical_size: Some(client_viewport_size),
                ..Default::default()
            };

            let (_, child_view_watcher_request) = create_proxy::<ui_comp::ChildViewWatcherMarker>();

            flatland.create_viewport(
                &pointerinjector_viewport_content_id,
                pointerinjector_view_creation_pair.viewport_creation_token,
                &link_properties,
                child_view_watcher_request,
            )?;
            flatland.set_content(
                &pointerinjector_viewport_transform_id,
                &pointerinjector_viewport_content_id,
            )?;
        }

        let mut a11y_view_watcher: Option<ui_comp::ChildViewWatcherProxy> = None;
        match a11y_view_provider {
            Some(a11y_view_provider) => {
                let a11y_view_creation_pair = scenic::flatland::ViewCreationTokenPair::new()?;

                // Bridge the pointerinjector and a11y Flatland instances.
                a11y_view_watcher = Some(
                    setup_child_view(
                        &pointerinjector_flatland,
                        a11y_view_creation_pair.viewport_creation_token,
                        &mut id_generator,
                        client_viewport_size,
                    )
                    .await?,
                );

                // Request for the a11y manager to create its view.
                a11y_view_provider.create_view(
                    a11y_view_creation_pair.view_creation_token,
                    scene_view_creation_pair.viewport_creation_token,
                )?;
            }
            None => {
                // Bridge the pointerinjector and scene Flatland instances. This skips the A11y View.
                let _ = setup_child_view(
                    &pointerinjector_flatland,
                    scene_view_creation_pair.viewport_creation_token,
                    &mut id_generator,
                    client_viewport_size,
                )
                .await?;
            }
        }

        // Start Present() loops for both Flatland instances, and request that both be presented.
        let (root_flatland_presentation_sender, root_receiver) = unbounded();
        start_flatland_presentation_loop(
            root_receiver,
            Arc::downgrade(&root_flatland.flatland),
            ROOT_VIEW_DEBUG_NAME.to_string(),
        );
        let (pointerinjector_flatland_presentation_sender, pointerinjector_receiver) = unbounded();
        start_flatland_presentation_loop(
            pointerinjector_receiver,
            Arc::downgrade(&pointerinjector_flatland.flatland),
            POINTER_INJECTOR_DEBUG_NAME.to_string(),
        );
        let (scene_flatland_presentation_sender, scene_receiver) = unbounded();
        start_flatland_presentation_loop(
            scene_receiver,
            Arc::downgrade(&scene_flatland.flatland),
            SCENE_DEBUG_NAME.to_string(),
        );

        let mut pingback_channels = Vec::new();
        pingback_channels.push(request_present_with_pingback(&root_flatland_presentation_sender)?);
        pingback_channels
            .push(request_present_with_pingback(&pointerinjector_flatland_presentation_sender)?);
        pingback_channels.push(request_present_with_pingback(&scene_flatland_presentation_sender)?);

        if let Some(a11y_view_watcher) = a11y_view_watcher {
            // Wait for a11y view to attach before proceeding.
            let a11y_view_status = a11y_view_watcher.get_status().await?;
            match a11y_view_status {
                ui_comp::ChildViewStatus::ContentHasPresented => {}
            }
        }

        // Read device pixel ratio from layout info.
        let device_pixel_ratio = display_metrics.pixels_per_pip();
        let viewport_hanging_get: Arc<Mutex<InjectorViewportHangingGet>> =
            create_viewport_hanging_get({
                InjectorViewportSpec {
                    width: display_metrics.width_in_pixels() as f32,
                    height: display_metrics.height_in_pixels() as f32,
                    scale: 1. / device_pixel_ratio,
                    x_offset: 0.,
                    y_offset: 0.,
                }
            });
        let viewport_publisher = Arc::new(Mutex::new(viewport_hanging_get.lock().new_publisher()));

        let context_view_ref = scenic::duplicate_view_ref(&root_flatland.view_ref)?;
        let target_view_ref = scenic::duplicate_view_ref(&pointerinjector_flatland.view_ref)?;

        // Wait for all pingbacks to ensure the scene is fully set up before returning.
        for receiver in pingback_channels {
            _ = receiver.await;
        }

        Ok(SceneManager {
            _display: display,
            client_viewport_size,
            root_flatland,
            _pointerinjector_flatland: pointerinjector_flatland,
            scene_flatland,
            context_view_ref,
            target_view_ref,
            root_flatland_presentation_sender,
            _pointerinjector_flatland_presentation_sender:
                pointerinjector_flatland_presentation_sender,
            scene_flatland_presentation_sender,
            scene_root_viewport_ids: None,
            id_generator,
            viewport_hanging_get,
            _viewport_publisher: viewport_publisher,
            cursor_transform_id: None,
            cursor_visibility: true,
            display_metrics,
            device_pixel_ratio,
        })
    }

    async fn set_root_view_internal(
        &mut self,
        viewport_creation_token: ui_views::ViewportCreationToken,
    ) -> Result<ui_views::ViewRef> {
        // Remove any existing viewport.
        if let Some(ids) = &self.scene_root_viewport_ids {
            let locked = self.scene_flatland.flatland.lock();
            locked
                .set_content(&ids.transform_id, &ContentId { value: 0 })
                .context("could not set content")?;
            locked.remove_child(&self.scene_flatland.root_transform_id, &ids.transform_id)?;
            locked.release_transform(&ids.transform_id).context("could not release transform")?;
            let _ = locked.release_viewport(&ids.content_id);
            self.scene_root_viewport_ids = None;
        }

        // Create new viewport.
        let ids = TransformContentIdPair {
            transform_id: self.id_generator.next_transform_id(),
            content_id: self.id_generator.next_content_id(),
        };
        let (child_view_watcher, child_view_watcher_request) =
            create_proxy::<ui_comp::ChildViewWatcherMarker>();
        {
            let locked = self.scene_flatland.flatland.lock();
            let viewport_properties = ui_comp::ViewportProperties {
                logical_size: Some(self.client_viewport_size),
                ..Default::default()
            };
            locked.create_viewport(
                &ids.content_id,
                viewport_creation_token,
                &viewport_properties,
                child_view_watcher_request,
            )?;
            locked.create_transform(&ids.transform_id).context("could not create transform")?;
            locked.add_child(&self.scene_flatland.root_transform_id, &ids.transform_id)?;
            locked
                .set_content(&ids.transform_id, &ids.content_id)
                .context("could not set content #2")?;
        }
        self.scene_root_viewport_ids = Some(ids);

        // Present the previous scene graph mutations.  This MUST be done before awaiting the result
        // of get_view_ref() below, because otherwise the view won't become attached to the global
        // scene graph topology, and the awaited ViewRef will never come.
        let mut pingback_channels = Vec::new();
        pingback_channels.push(
            request_present_with_pingback(&self.scene_flatland_presentation_sender)
                .context("could not request present with pingback")?,
        );

        let _child_status =
            child_view_watcher.get_status().await.context("could not call get_status")?;
        let child_view_ref =
            child_view_watcher.get_view_ref().await.context("could not get view_ref")?;
        let child_view_ref_copy =
            scenic::duplicate_view_ref(&child_view_ref).context("could not duplicate view_ref")?;

        let request_focus_result = self.root_flatland.focuser.request_focus(child_view_ref).await;
        match request_focus_result {
            Err(e) => warn!("Request focus failed with err: {}", e),
            Ok(Err(value)) => warn!("Request focus failed with err: {:?}", value),
            Ok(_) => {}
        }
        pingback_channels.push(
            request_present_with_pingback(&self.root_flatland_presentation_sender)
                .context("could not request present with pingback #2")?,
        );

        // Wait for all pingbacks to ensure the scene is fully set up before returning.
        for receiver in pingback_channels {
            _ = receiver.await;
        }

        Ok(child_view_ref_copy)
    }
}

pub fn create_viewport_hanging_get(
    initial_spec: InjectorViewportSpec,
) -> Arc<Mutex<InjectorViewportHangingGet>> {
    let notify_fn: InjectorViewportChangeFn = Box::new(|viewport_spec, responder| {
        if let Err(fidl_error) = responder.send(&(*viewport_spec).into()) {
            info!("Viewport hanging get notification, FIDL error: {}", fidl_error);
        }
        // TODO(https://fxbug.dev/42168817): the HangingGet docs don't explain what value to return.
        true
    });

    Arc::new(Mutex::new(hanging_get::HangingGet::new(initial_spec, notify_fn)))
}

pub fn start_exit_on_scenic_closed_task(flatland_proxy: ui_comp::FlatlandDisplayProxy) {
    fasync::Task::local(async move {
        let _ = flatland_proxy.on_closed().await;
        info!("Scenic died, closing SceneManager too.");
        process::exit(1);
    })
    .detach()
}

pub fn start_flatland_presentation_loop(
    mut receiver: PresentationReceiver,
    weak_flatland: Weak<Mutex<ui_comp::FlatlandProxy>>,
    debug_name: String,
) {
    fasync::Task::local(async move {
        let mut present_count = 0;
        let scheduler = ThroughputScheduler::new();
        let mut flatland_event_stream = {
            if let Some(flatland) = weak_flatland.upgrade() {
                flatland.lock().take_event_stream()
            } else {
                warn!(
                    "Failed to upgrade Flatand weak ref; exiting presentation loop for {debug_name}"
                );
                return;
            }
        };

        let mut channels_awaiting_pingback = VecDeque::from([Vec::new()]);

        loop {
            futures::select! {
                message = receiver.next() => {
                    match message {
                        Some(PresentationMessage::RequestPresent) => {
                            scheduler.request_present();
                        }
                        Some(PresentationMessage::RequestPresentWithPingback(channel)) => {
                            channels_awaiting_pingback.back_mut().unwrap().push(channel);
                            scheduler.request_present();
                        }
                        None => {}
                    }
                }
                flatland_event = flatland_event_stream.next() => {
                    match flatland_event {
                        Some(Ok(ui_comp::FlatlandEvent::OnNextFrameBegin{ values })) => {
                            trace::duration!(c"scene_manager", c"SceneManager::OnNextFrameBegin",
                                             "debug_name" => &*debug_name);
                            let credits = values
                                          .additional_present_credits
                                          .expect("Present credits must exist");
                            let infos = values
                                .future_presentation_infos
                                .expect("Future presentation infos must exist")
                                .iter()
                                .map(
                                |x| PresentationInfo{
                                    latch_point: zx::MonotonicInstant::from_nanos(x.latch_point.unwrap()),
                                    presentation_time: zx::MonotonicInstant::from_nanos(
                                                        x.presentation_time.unwrap())
                                })
                                .collect();
                            scheduler.on_next_frame_begin(credits, infos);
                        }
                        Some(Ok(ui_comp::FlatlandEvent::OnFramePresented{ frame_presented_info })) => {
                            trace::duration!(c"scene_manager", c"SceneManager::OnFramePresented",
                                             "debug_name" => &*debug_name);
                            let actual_presentation_time =
                                zx::MonotonicInstant::from_nanos(frame_presented_info.actual_presentation_time);
                            let presented_infos: Vec<PresentedInfo> =
                                frame_presented_info.presentation_infos
                                .into_iter()
                                .map(|x| x.into())
                                .collect();

                            // Pingbacks for presented updates. For each presented frame, drain all
                            // of the corresponding pingback channels
                            for _ in 0..presented_infos.len() {
                                for channel in channels_awaiting_pingback.pop_back().unwrap() {
                                    _ = channel.send(());
                                }
                            }

                            scheduler.on_frame_presented(actual_presentation_time, presented_infos);
                        }
                        Some(Ok(ui_comp::FlatlandEvent::OnError{ error })) => {
                            error!(
                                "Received FlatlandError code: {}; exiting listener loop for {debug_name}",
                                error.into_primitive()
                            );
                            return;
                        }
                        _ => {}
                    }
                }
                present_parameters = scheduler.wait_to_update().fuse() => {
                    trace::duration!(c"scene_manager", c"SceneManager::Present",
                                     "debug_name" => &*debug_name);

                    match debug_name.as_str() {
                        ROOT_VIEW_DEBUG_NAME => {
                            trace::flow_begin!(c"gfx", ROOT_VIEW_PRESENT_TRACING_NAME, present_count.into());
                        }
                        POINTER_INJECTOR_DEBUG_NAME => {
                            trace::flow_begin!(c"gfx", POINTER_INJECTOR_PRESENT_TRACING_NAME, present_count.into());
                        }
                        SCENE_DEBUG_NAME => {
                            trace::flow_begin!(c"gfx", SCENE_TRACING_NAME, present_count.into());
                        }
                        _ => {
                            warn!("SceneManager::Present with unknown debug_name {:?}", debug_name);
                        }
                    }
                    present_count += 1;
                    channels_awaiting_pingback.push_front(Vec::new());
                    if let Some(flatland) = weak_flatland.upgrade() {
                        flatland
                            .lock()
                            .present(present_parameters.into())
                            .expect("Present failed for {debug_name}");
                    } else {
                        warn!(
                            "Failed to upgrade Flatand weak ref; exiting listener loop for {debug_name}"
                        );
                        return;
                    }
            }
        }
    }})
    .detach()
}

pub fn handle_pointer_injector_configuration_setup_request_stream(
    mut request_stream: PointerInjectorConfigurationSetupRequestStream,
    scene_manager: Arc<futures::lock::Mutex<dyn SceneManagerTrait>>,
) {
    fasync::Task::local(async move {
        let subscriber =
            scene_manager.lock().await.get_pointerinjector_viewport_watcher_subscription();

        loop {
            let request = request_stream.try_next().await;
            match request {
                Ok(Some(PointerInjectorConfigurationSetupRequest::GetViewRefs { responder })) => {
                    let (context_view_ref, target_view_ref) =
                        scene_manager.lock().await.get_pointerinjection_view_refs();
                    if let Err(e) = responder.send(context_view_ref, target_view_ref) {
                        warn!("Failed to send GetViewRefs() response: {}", e);
                    }
                }
                Ok(Some(PointerInjectorConfigurationSetupRequest::WatchViewport { responder })) => {
                    if let Err(e) = subscriber.register(responder) {
                        warn!("Failed to register WatchViewport() subscriber: {}", e);
                    }
                }
                Ok(None) => {
                    return;
                }
                Err(e) => {
                    error!("Error obtaining SetupRequest: {}", e);
                    return;
                }
            }
        }
    })
    .detach()
}