carnelian/app/strategies/
base.rs

1// Copyright 2020 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::app::strategies::flatland::FlatlandAppStrategy;
6use crate::app::strategies::framebuffer::{
7    connect_to_display_provider, DisplayCoordinator, DisplayDirectAppStrategy, DisplayId,
8};
9use crate::app::{Config, InternalSender, MessageInternal, ViewMode};
10use crate::input::{self};
11use crate::view::strategies::base::{ViewStrategyParams, ViewStrategyPtr};
12use crate::view::ViewKey;
13use anyhow::Error;
14use async_trait::async_trait;
15use fidl_fuchsia_hardware_display::{ProviderProxy, VirtconMode};
16use fidl_fuchsia_input_report as hid_input_report;
17use fuchsia_component::server::{ServiceFs, ServiceObjLocal};
18use futures::channel::mpsc::UnboundedSender;
19use keymaps::select_keymap;
20
21// This trait exists to keep the hosted implementation and the
22// direct implementations as separate as possible.
23// At the moment this abstraction is quite leaky, but it is good
24// enough and can be refined with experience.
25#[async_trait(?Send)]
26pub(crate) trait AppStrategy {
27    async fn create_view_strategy(
28        &mut self,
29        key: ViewKey,
30        app_sender: UnboundedSender<MessageInternal>,
31        strategy_params: ViewStrategyParams,
32    ) -> Result<ViewStrategyPtr, Error>;
33    #[allow(dead_code)]
34    fn supports_scenic(&self) -> bool;
35    fn create_view_for_testing(&self, _: &UnboundedSender<MessageInternal>) -> Result<(), Error> {
36        Ok(())
37    }
38    fn create_view_strategy_params_for_additional_view(
39        &mut self,
40        view_key: ViewKey,
41    ) -> ViewStrategyParams;
42    fn start_services<'a, 'b>(
43        &self,
44        _app_sender: UnboundedSender<MessageInternal>,
45        _fs: &'a mut ServiceFs<ServiceObjLocal<'b, ()>>,
46    ) -> Result<(), Error> {
47        Ok(())
48    }
49    async fn post_setup(&mut self, _internal_sender: &InternalSender) -> Result<(), Error>;
50    fn handle_input_report(
51        &mut self,
52        _device_id: &input::DeviceId,
53        _input_report: &hid_input_report::InputReport,
54    ) -> Vec<input::Event> {
55        Vec::new()
56    }
57    fn handle_keyboard_autorepeat(&mut self, _device_id: &input::DeviceId) -> Vec<input::Event> {
58        Vec::new()
59    }
60    fn handle_register_input_device(
61        &mut self,
62        _device_id: &input::DeviceId,
63        _device_descriptor: &hid_input_report::DeviceDescriptor,
64    ) {
65    }
66    async fn handle_new_display_coordinator(&mut self, _provider: ProviderProxy) {}
67    async fn handle_display_coordinator_event(
68        &mut self,
69        _event: fidl_fuchsia_hardware_display::CoordinatorListenerRequest,
70    ) {
71    }
72    fn set_virtcon_mode(&mut self, _virtcon_mode: VirtconMode) {}
73    fn handle_view_closed(&mut self, _view_key: ViewKey) {}
74    fn get_focused_view_key(&self) -> Option<ViewKey> {
75        panic!("get_focused_view_key not implemented");
76    }
77    fn get_visible_view_key_for_display(&self, _display_id: DisplayId) -> Option<ViewKey> {
78        panic!("get_visible_view_key_for_display not implemented");
79    }
80}
81
82pub(crate) type AppStrategyPtr = Box<dyn AppStrategy>;
83
84fn make_flatland_app_strategy() -> Result<AppStrategyPtr, Error> {
85    Ok::<AppStrategyPtr, Error>(Box::new(FlatlandAppStrategy {}))
86}
87
88fn make_direct_app_strategy(
89    display_coordinator: Option<DisplayCoordinator>,
90    app_config: &Config,
91    internal_sender: InternalSender,
92) -> Result<AppStrategyPtr, Error> {
93    let strat = DisplayDirectAppStrategy::new(
94        display_coordinator,
95        select_keymap(&app_config.keymap_name),
96        internal_sender,
97        &app_config,
98    );
99
100    Ok(Box::new(strat))
101}
102
103pub(crate) async fn create_app_strategy(
104    internal_sender: &InternalSender,
105) -> Result<AppStrategyPtr, Error> {
106    let app_config = Config::get();
107    match app_config.view_mode {
108        ViewMode::Auto => {
109            // Tries to open the display coordinator. If a display service is not available
110            // within `TIMEOUT`, or the DisplayCoordinator creation fails, assume we want to run
111            // as hosted.
112            // TODO(https://fxbug.dev/437869025): Revisit this policy.
113            const TIMEOUT: zx::MonotonicDuration = zx::MonotonicDuration::from_seconds(2);
114            let display_coordinator = match connect_to_display_provider(Some(TIMEOUT)).await {
115                Ok(provider) => {
116                    DisplayCoordinator::open(provider, &app_config.virtcon_mode, &internal_sender)
117                        .await
118                        .ok()
119                }
120                Err(error) => {
121                    eprintln!(
122                        "display coordinator is not available, fall back to flatland: {}",
123                        error
124                    );
125                    None
126                }
127            };
128            if display_coordinator.is_none() {
129                make_flatland_app_strategy()
130            } else {
131                make_direct_app_strategy(display_coordinator, app_config, internal_sender.clone())
132            }
133        }
134        ViewMode::Direct => {
135            DisplayCoordinator::watch_displays(internal_sender.clone()).await;
136            make_direct_app_strategy(None, app_config, internal_sender.clone())
137        }
138        ViewMode::Hosted => make_flatland_app_strategy(),
139    }
140}