Skip to main content

carnelian/
app.rs

1// Copyright 2018 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::IdGenerator2;
6use crate::app::strategies::base::{AppStrategyPtr, create_app_strategy};
7use crate::app::strategies::framebuffer::DisplayId;
8use crate::drawing::DisplayRotation;
9use crate::geometry::Size;
10use crate::input::{DeviceId, UserInputMessage};
11use crate::message::Message;
12use crate::scene::facets::FacetId;
13use crate::view::strategies::base::ViewStrategyParams;
14use crate::view::{ViewAssistantPtr, ViewController, ViewKey};
15use anyhow::{Context as _, Error, bail, format_err};
16use fidl_fuchsia_hardware_display::{
17    ClientPriorityValue, CoordinatorListenerRequest, ProviderProxy,
18};
19use fidl_fuchsia_input_report as fidl_input_report;
20use fuchsia_async::{self as fasync, DurationExt, Timer};
21use fuchsia_component::{self as component};
22use fuchsia_trace::duration;
23
24use futures::StreamExt;
25use futures::channel::mpsc::{UnboundedSender, unbounded};
26use futures::future::{Either, Future};
27use serde::Deserialize;
28use std::any::Any;
29use std::collections::BTreeMap;
30use std::fmt::Debug;
31use std::fs;
32use std::path::PathBuf;
33use std::pin::{Pin, pin};
34use std::sync::OnceLock;
35
36pub(crate) mod strategies;
37
38/// Type alias for a non-sync future
39pub type LocalBoxFuture<'a, T> = Pin<Box<dyn Future<Output = T> + 'a>>;
40
41const fn keyboard_autorepeat_default() -> bool {
42    true
43}
44
45fn duration_from_millis(time_in_millis: u64) -> Result<std::time::Duration, Error> {
46    Ok(std::time::Duration::from_millis(time_in_millis))
47}
48
49fn deserialize_millis<'de, D>(deserializer: D) -> Result<std::time::Duration, D::Error>
50where
51    D: serde::Deserializer<'de>,
52{
53    let ms = u64::deserialize(deserializer)?;
54    duration_from_millis(ms).map_err(serde::de::Error::custom)
55}
56
57const fn keyboard_autorepeat_slow_interval_default() -> std::time::Duration {
58    const KEYBOARD_AUTOREPEAT_SLOW_INTERVAL: std::time::Duration =
59        std::time::Duration::from_millis(250);
60    KEYBOARD_AUTOREPEAT_SLOW_INTERVAL
61}
62
63const fn keyboard_autorepeat_fast_interval_default() -> std::time::Duration {
64    const KEYBOARD_AUTOREPEAT_FAST_INTERVAL: std::time::Duration =
65        std::time::Duration::from_millis(50);
66    KEYBOARD_AUTOREPEAT_FAST_INTERVAL
67}
68
69const fn display_resource_release_delay_default() -> std::time::Duration {
70    const DISPLAY_RESOURCE_RELEASE_DELAY_DEFAULT: std::time::Duration =
71        std::time::Duration::from_secs(5);
72    DISPLAY_RESOURCE_RELEASE_DELAY_DEFAULT
73}
74
75const fn startup_delay_default() -> std::time::Duration {
76    const STARTUP_DELAY_DEFAULT: std::time::Duration = std::time::Duration::from_secs(0);
77    STARTUP_DELAY_DEFAULT
78}
79
80/// Enum used by Config to control what sort of views
81/// will be used.
82#[derive(Debug, Deserialize)]
83#[serde(rename_all = "lowercase")]
84pub enum ViewMode {
85    /// Choose automatically based on the environment
86    Auto,
87    /// Only create views hosted by Scenic.
88    Hosted,
89    /// Only create views directly running directly on the display coordinator.
90    Direct,
91}
92
93impl Default for ViewMode {
94    fn default() -> Self {
95        Self::Auto
96    }
97}
98
99/// Grab-bag of configuration options for Carnelian apps.
100#[derive(Debug, Deserialize)]
101pub struct Config {
102    #[serde(default = "keyboard_autorepeat_default")]
103    /// Whether, when running without Scenic, this application should
104    /// receive keyboard repeat events.
105    pub keyboard_autorepeat: bool,
106    #[serde(
107        default = "keyboard_autorepeat_slow_interval_default",
108        deserialize_with = "deserialize_millis"
109    )]
110    /// The initial and maximum interval between keyboard repeat events, in
111    /// milliseconds, when running without Scenic.
112    pub keyboard_autorepeat_slow_interval: std::time::Duration,
113    #[serde(
114        default = "keyboard_autorepeat_fast_interval_default",
115        deserialize_with = "deserialize_millis"
116    )]
117    /// The minimum interval between keyboard repeat events, in
118    /// milliseconds, when running without Scenic.
119    pub keyboard_autorepeat_fast_interval: std::time::Duration,
120    /// Client priority when connecting to the display coordinator. If not set,
121    /// the client will use the priority value for primary clients.
122    #[serde(default)]
123    pub client_priority: Option<ClientPriorityValue>,
124    /// What sort of view system to use.
125    #[serde(default)]
126    pub view_mode: ViewMode,
127    #[serde(default)]
128    /// Application option to exercise transparent rotation.
129    pub display_rotation: DisplayRotation,
130    #[serde(default)]
131    /// Application option to select keymap. If named keymap is not found
132    /// the fallback is US QWERTY.
133    pub keymap_name: Option<String>,
134    #[serde(
135        default = "display_resource_release_delay_default",
136        deserialize_with = "deserialize_millis"
137    )]
138    /// How long should carnelian wait before releasing display resources when
139    /// it loses ownership of the display while running directly on the display. The default
140    /// value is five seconds, so that the resource will not be rapidly allocated
141    /// and deallocated when switching quickly between virtcon and the regular display.
142    pub display_resource_release_delay: std::time::Duration,
143    #[serde(default)]
144    /// In a bringup build the display coordinator might not support multiple
145    /// buffers so Carnelian might have to run with only a
146    /// single buffer. This configuration option is to allow testing rendering
147    /// with a single buffer even in build that supports multiple.
148    pub buffer_count: Option<usize>,
149    #[serde(default)]
150    /// Whether input events are needed.
151    pub input: bool,
152    #[serde(default)]
153    /// Whether output can be translucent and needs blending.
154    pub needs_blending: bool,
155    #[serde(default = "startup_delay_default", deserialize_with = "deserialize_millis")]
156    /// How long to wait before entering event loop.
157    pub startup_delay: std::time::Duration,
158}
159
160impl Config {
161    pub(crate) fn get() -> &'static Config {
162        // Some input tests access the config. Rather than requiring setup everywhere,
163        // default the config values for testing purposes.
164        CONFIG.get_or_init(|| Config::default())
165    }
166}
167
168impl Default for Config {
169    fn default() -> Self {
170        Self {
171            keyboard_autorepeat: keyboard_autorepeat_default(),
172            keyboard_autorepeat_slow_interval: keyboard_autorepeat_slow_interval_default(),
173            keyboard_autorepeat_fast_interval: keyboard_autorepeat_fast_interval_default(),
174            client_priority: None,
175            view_mode: ViewMode::default(),
176            display_rotation: DisplayRotation::Deg0,
177            keymap_name: None,
178            display_resource_release_delay: display_resource_release_delay_default(),
179            buffer_count: None,
180            input: true,
181            needs_blending: false,
182            startup_delay: Default::default(),
183        }
184    }
185}
186
187pub(crate) static CONFIG: OnceLock<Config> = OnceLock::new();
188
189pub(crate) type InternalSender = UnboundedSender<MessageInternal>;
190
191/// Target of a Any-based message
192#[derive(Debug, Clone, Copy)]
193pub enum MessageTarget {
194    /// target a facet in a view
195    Facet(ViewKey, FacetId),
196    /// target the view assistant in a view.
197    View(ViewKey),
198    /// targe the application assistant.
199    Application,
200}
201
202/// Options when creating a view.
203pub type CreateViewOptions = Box<dyn Any>;
204
205/// Context struct passed to the application assistant creator
206// function.
207#[derive(Clone)]
208pub struct AppSender {
209    sender: InternalSender,
210}
211
212impl AppSender {
213    /// Send a message to a view controller.
214    pub fn queue_message(&self, target: MessageTarget, message: Message) {
215        self.sender
216            .unbounded_send(MessageInternal::TargetedMessage(target, message))
217            .expect("AppSender::queue_message - unbounded_send");
218    }
219
220    /// Request that a frame be rendered at the next appropriate time.
221    pub fn request_render(&self, target: ViewKey) {
222        self.sender
223            .unbounded_send(MessageInternal::RequestRender(target))
224            .expect("AppSender::request_render - unbounded_send");
225    }
226
227    /// Request the creation of an additional view.
228    pub fn create_additional_view(&self, options: Option<CreateViewOptions>) -> ViewKey {
229        let view_key = IdGenerator2::<ViewKey>::next().expect("view_key");
230        self.sender
231            .unbounded_send(MessageInternal::CreateAdditionalView(view_key, options))
232            .expect("AppSender::create_additional_view - unbounded_send");
233        view_key
234    }
235
236    /// Close an additional view. It is a fatal error to attempt to close a view that
237    /// was not created with `create_additional_view()`.
238    pub fn close_additional_view(&self, view_key: ViewKey) {
239        self.sender
240            .unbounded_send(MessageInternal::CloseAdditionalView(view_key))
241            .expect("AppSender::close_additional_view - unbounded_send");
242    }
243
244    /// Create an futures mpsc sender and a task to poll the receiver and
245    /// forward the message to the app sender. This setup works around the problem
246    /// that dyn Any references cannot be `Send` at the cost of an extra trip through
247    /// the executor.
248    /// The 'static trait bounds here means that messages send across thread may not
249    /// contain any non-static references. The data in the messages must be owned, but
250    /// no not themselves need to be static.
251    pub fn create_cross_thread_sender<T: 'static + Send>(
252        &self,
253        target: MessageTarget,
254    ) -> UnboundedSender<T> {
255        let (sender, mut receiver) = unbounded::<T>();
256        let app_sender = self.sender.clone();
257        let f = async move {
258            while let Some(message) = receiver.next().await {
259                app_sender
260                    .unbounded_send(MessageInternal::TargetedMessage(target, Box::new(message)))
261                    .expect("unbounded_send");
262            }
263        };
264        // This task can be detached as it will exit when the unbounded sender
265        // it provides is dropped.
266        fasync::Task::local(f).detach();
267        sender
268    }
269
270    /// Create an context for testing things that need an app context.
271    pub fn new_for_testing_purposes_only() -> AppSender {
272        let (internal_sender, _) = unbounded::<MessageInternal>();
273        AppSender { sender: internal_sender }
274    }
275}
276
277fn make_app_assistant_fut<T: AppAssistant + Default + 'static>(
278    _: &AppSender,
279) -> LocalBoxFuture<'_, Result<AppAssistantPtr, Error>> {
280    let f = async move {
281        let assistant = Box::new(T::default());
282        Ok::<AppAssistantPtr, Error>(assistant)
283    };
284    Box::pin(f)
285}
286
287/// Convenience function to create an application assistant that implements Default.
288pub fn make_app_assistant<T: AppAssistant + Default + 'static>() -> AssistantCreatorFunc {
289    Box::new(make_app_assistant_fut::<T>)
290}
291
292/// Parameter struction for view creation
293pub struct ViewCreationParameters {
294    /// ViewKey for the new view.
295    pub view_key: ViewKey,
296    /// App sender that might be of use to the new view assistant.
297    pub app_sender: AppSender,
298    /// Display ID of the hosting display for views running directly
299    /// on the display coordinator.
300    pub display_id: Option<DisplayId>,
301    /// Options passed to `create_additional_view()`, if this view is being created
302    /// by that function and if the caller passed any.
303    pub options: Option<Box<dyn Any>>,
304}
305
306impl Debug for ViewCreationParameters {
307    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
308        f.debug_struct("ViewCreationParameters")
309            .field("view_key", &self.view_key)
310            .field("display_id", &self.display_id)
311            .field("options", &self.options)
312            .finish()
313    }
314}
315
316/// Trait that a mod author must implement. Currently responsible for creating
317/// a view assistant when the Fuchsia view framework requests that the mod create
318/// a view.
319pub trait AppAssistant {
320    /// This method is responsible for setting up the AppAssistant implementation.
321    /// _It's not clear if this is going to so useful, as anything that isn't
322    /// initialized in the creation of the structure implementing AppAssistant
323    /// is going to have to be represented as an `Option`, which is awkward._
324    fn setup(&mut self) -> Result<(), Error>;
325
326    /// Called when the Fuchsia view system requests that a view be created, or once at startup
327    /// when running without Scenic.
328    fn create_view_assistant(&mut self, _: ViewKey) -> Result<ViewAssistantPtr, Error> {
329        todo!("Must implement create_view_assistant_with_parameters or create_view_assistant");
330    }
331
332    /// Called when the Fuchsia view system requests that a view be created. Provides
333    /// parameters to view creation that include anything provided the the view creation
334    /// requestor and an AppSender.
335    fn create_view_assistant_with_parameters(
336        &mut self,
337        params: ViewCreationParameters,
338    ) -> Result<ViewAssistantPtr, Error> {
339        self.create_view_assistant(params.view_key)
340    }
341
342    /// Return the list of names of services this app wants to provide
343    fn outgoing_services_names(&self) -> Vec<&'static str> {
344        Vec::new()
345    }
346
347    /// Handle a request to connect to a service provided by this app
348    fn handle_service_connection_request(
349        &mut self,
350        _service_name: &str,
351        _channel: fasync::Channel,
352    ) -> Result<(), Error> {
353        return Err(format_err!("handle_service_connection_request not implemented"));
354    }
355
356    /// Filter Carnelian configuration at runtime, if needed.
357    fn filter_config(&mut self, _config: &mut Config) {}
358
359    /// This method is called when `App::queue_message` is called with `Application`
360    /// as target.
361    #[allow(unused_variables)]
362    fn handle_message(&mut self, message: Message) {}
363}
364
365/// Reference to an application assistant.
366pub type AppAssistantPtr = Box<dyn AppAssistant>;
367
368/// Struct that implements module-wide responsibilities, currently limited
369/// to creating views on request.
370pub struct App {
371    strategy: AppStrategyPtr,
372    view_controllers: BTreeMap<ViewKey, ViewController>,
373    assistant: AppAssistantPtr,
374    messages: Vec<(ViewKey, Message)>,
375    sender: InternalSender,
376    _inspect_server: Option<inspect_runtime::PublishedInspectController>,
377}
378
379#[derive(Debug)]
380pub(crate) enum MessageInternal {
381    ServiceConnection(zx::Channel, &'static str),
382    CreateView(ViewStrategyParams),
383    CreateAdditionalView(ViewKey, Option<CreateViewOptions>),
384    CloseAdditionalView(ViewKey),
385    MetricsChanged(ViewKey, Size),
386    SizeChanged(ViewKey, Size),
387    Focus(ViewKey, bool),
388    CloseViewsOnDisplay(DisplayId),
389    RequestRender(ViewKey),
390    Render(ViewKey),
391    ImageFreed(ViewKey, u64, u32),
392    TargetedMessage(MessageTarget, Message),
393    RegisterDevice(DeviceId, Box<fidl_input_report::DeviceDescriptor>),
394    InputReport(DeviceId, fidl_input_report::InputReport),
395    KeyboardAutoRepeat(DeviceId),
396    OwnershipChanged(bool),
397    DropDisplayResources,
398    FlatlandOnNextFrameBegin(ViewKey, fidl_fuchsia_ui_composition::OnNextFrameBeginValues),
399    FlatlandOnFramePresented(ViewKey, fidl_fuchsia_scenic_scheduling::FramePresentedInfo),
400    FlatlandOnError(ViewKey, fuchsia_scenic::flatland::FlatlandError),
401    NewDisplayCoordinator(ProviderProxy),
402    DisplayCoordinatorListenerRequest(CoordinatorListenerRequest),
403
404    UserInputMessage(ViewKey, UserInputMessage),
405}
406
407/// Future that returns an application assistant.
408pub type AssistantCreator<'a> = LocalBoxFuture<'a, Result<AppAssistantPtr, Error>>;
409/// Function that creates an AssistantCreator future.
410pub type AssistantCreatorFunc = Box<dyn FnOnce(&AppSender) -> AssistantCreator<'_>>;
411
412impl App {
413    fn new(sender: InternalSender, strategy: AppStrategyPtr, assistant: AppAssistantPtr) -> App {
414        App {
415            strategy,
416            view_controllers: BTreeMap::new(),
417            assistant,
418            messages: Vec::new(),
419            sender,
420            _inspect_server: inspect_runtime::publish(
421                fuchsia_inspect::component::inspector(),
422                inspect_runtime::PublishOptions::default(),
423            ),
424        }
425    }
426
427    fn load_and_filter_config(assistant: &mut AppAssistantPtr) -> Result<(), Error> {
428        let mut config = Self::load_config()?;
429        assistant.filter_config(&mut config);
430        CONFIG.set(config).expect("config set");
431        Ok(())
432    }
433
434    /// Starts an application based on Carnelian. The `assistant` parameter will
435    /// be used to create new views when asked to do so by the Fuchsia view system.
436    pub fn run(assistant_creator_func: AssistantCreatorFunc) -> Result<(), Error> {
437        let mut executor = fasync::LocalExecutor::default();
438        let (internal_sender, mut internal_receiver) = unbounded::<MessageInternal>();
439        let f = async {
440            let app_sender = AppSender { sender: internal_sender.clone() };
441            let assistant_creator = assistant_creator_func(&app_sender);
442            let mut assistant = assistant_creator.await?;
443            Self::load_and_filter_config(&mut assistant)?;
444            let strat = create_app_strategy(&internal_sender).await?;
445            let mut app = App::new(internal_sender, strat, assistant);
446            app.app_init_common().await?;
447            let startup_delay = Config::get().startup_delay;
448            if !startup_delay.is_zero() {
449                duration!("gfx", "App::run-startup-delay");
450                std::thread::sleep(Config::get().startup_delay);
451            }
452            while let Some(message) = internal_receiver.next().await {
453                app.handle_message(message).await?;
454            }
455            Ok::<(), Error>(())
456        };
457        executor.run_singlethreaded(f)?;
458        Ok(())
459    }
460
461    async fn handle_message(&mut self, message: MessageInternal) -> Result<(), Error> {
462        match message {
463            MessageInternal::ServiceConnection(channel, service_name) => {
464                let channel = fasync::Channel::from_channel(channel);
465                self.assistant
466                    .handle_service_connection_request(service_name, channel)
467                    .unwrap_or_else(|e| {
468                        eprintln!("error running {} server: {:?}", service_name, e)
469                    });
470            }
471            MessageInternal::CreateView(params) => {
472                self.create_view_with_params(params, None).await?
473            }
474            MessageInternal::CreateAdditionalView(view_key, options) => {
475                self.create_additional_view(view_key, options).await?
476            }
477            MessageInternal::CloseAdditionalView(view_key) => {
478                self.close_additional_view(view_key)?;
479            }
480            MessageInternal::MetricsChanged(view_id, metrics) => {
481                if let Ok(view) = self.get_view(view_id) {
482                    view.handle_metrics_changed(metrics);
483                }
484            }
485            MessageInternal::SizeChanged(view_id, new_size) => {
486                if let Ok(view) = self.get_view(view_id) {
487                    view.handle_size_changed(new_size);
488                }
489            }
490            MessageInternal::Focus(view_id, focused) => {
491                if let Ok(view) = self.get_view(view_id) {
492                    view.focus(focused);
493                }
494            }
495            MessageInternal::RequestRender(view_id) => {
496                if let Ok(view) = self.get_view(view_id) {
497                    view.request_render();
498                }
499            }
500            MessageInternal::Render(view_id) => {
501                if let Ok(view) = self.get_view(view_id) {
502                    view.render().await;
503                }
504            }
505            MessageInternal::CloseViewsOnDisplay(display_id) => {
506                let view_keys = self.get_view_keys_for_display(display_id);
507                for view_key in view_keys.into_iter() {
508                    self.close_view(view_key);
509                }
510            }
511            MessageInternal::ImageFreed(view_id, image_id, collection_id) => {
512                self.image_freed(view_id, image_id, collection_id)
513            }
514            MessageInternal::TargetedMessage(target, message) => match target {
515                MessageTarget::Facet(view_id, facet_id) => {
516                    let view = self.get_view(view_id).context("TargetedMessage")?;
517                    view.send_facet_message(facet_id, message).context("TargetedMessage")?;
518                }
519                MessageTarget::View(view_id) => {
520                    let view = self.get_view(view_id).context("TargetedMessage")?;
521                    view.send_message(message);
522                }
523                MessageTarget::Application => {
524                    self.assistant.handle_message(message);
525                }
526            },
527            MessageInternal::RegisterDevice(device_id, device_descriptor) => {
528                self.strategy.handle_register_input_device(&device_id, &device_descriptor);
529            }
530            MessageInternal::InputReport(device_id, input_report) => {
531                let input_events = self.strategy.handle_input_report(&device_id, &input_report);
532                if let Some(focused_view_key) = self.get_focused_view_key() {
533                    let view = self.get_view(focused_view_key).context("InputReport")?;
534                    view.handle_input_events(input_events).context("InputReport")?;
535                } else {
536                    eprintln!("dropping input report due to no focused view");
537                }
538            }
539            MessageInternal::KeyboardAutoRepeat(device_id) => {
540                let input_events = self.strategy.handle_keyboard_autorepeat(&device_id);
541                if let Some(focused_view_key) = self.get_focused_view_key() {
542                    let view = self.get_view(focused_view_key).context("KeyboardAutoRepeat")?;
543                    view.handle_input_events(input_events).context("KeyboardAutoRepeat")?;
544                } else {
545                    eprintln!("dropping keyboard auto repeat due to no focused view");
546                }
547            }
548            MessageInternal::UserInputMessage(view_id, user_input_message) => {
549                let view = self.get_view(view_id).context("UserInputMessage")?;
550                view.handle_user_input_message(user_input_message)?;
551            }
552            MessageInternal::OwnershipChanged(owned) => {
553                self.ownership_changed(owned);
554            }
555            MessageInternal::DropDisplayResources => {
556                self.drop_display_resources();
557            }
558            MessageInternal::FlatlandOnNextFrameBegin(view_id, info) => {
559                let view = self.get_view(view_id).context("FlatlandOnNextFrameBegin")?;
560                view.handle_on_next_frame_begin(&info);
561            }
562            MessageInternal::FlatlandOnFramePresented(view_id, info) => {
563                let view = self.get_view(view_id).context("FlatlandOnFramePresented")?;
564                view.present_done(info);
565            }
566            MessageInternal::FlatlandOnError(view_id, error) => {
567                eprintln!("flatland error view: {}, error: {:#?}", view_id, error);
568            }
569            MessageInternal::NewDisplayCoordinator(provider) => {
570                self.strategy.handle_new_display_coordinator(provider).await;
571            }
572            MessageInternal::DisplayCoordinatorListenerRequest(request) => match request {
573                CoordinatorListenerRequest::OnVsync { display_id, .. } => {
574                    if let Some(view_key) =
575                        self.strategy.get_visible_view_key_for_display(display_id.into())
576                    {
577                        if let Ok(view) = self.get_view(view_key) {
578                            view.handle_display_coordinator_listener_request(request).await;
579                        } else {
580                            // We seem to get two vsyncs after the display is removed.
581                            // Log it to help run down why that is.
582                            eprintln!("vsync for display {:?} with no view", display_id);
583                        }
584                    }
585                }
586
587                _ => self.strategy.handle_display_coordinator_event(request),
588            },
589        }
590        Ok(())
591    }
592
593    async fn app_init_common(&mut self) -> Result<(), Error> {
594        self.assistant.setup().context("app setup")?;
595        self.start_services()?;
596        Ok(())
597    }
598
599    /// Tests an application based on Carnelian. The `assistant` parameter will
600    /// be used to create a single new view for testing. The test will run until the
601    /// first update call, or until a five second timeout. The Result returned is the
602    /// result of the test, an Ok(()) result means the test passed.
603    pub fn test(assistant_creator_func: AssistantCreatorFunc) -> Result<(), Error> {
604        let mut executor = fasync::LocalExecutor::default();
605        let (internal_sender, mut internal_receiver) = unbounded::<MessageInternal>();
606        let f = async {
607            let app_sender = AppSender { sender: internal_sender.clone() };
608            let assistant_creator = assistant_creator_func(&app_sender);
609            let mut assistant = assistant_creator.await?;
610            Self::load_and_filter_config(&mut assistant)?;
611            let strat = create_app_strategy(&internal_sender).await?;
612            strat.create_view_for_testing(&internal_sender)?;
613            let mut app = App::new(internal_sender, strat, assistant);
614            let mut frame_count = 0;
615            app.app_init_common().await?;
616            loop {
617                let timeout =
618                    pin!(Timer::new(zx::MonotonicDuration::from_millis(500_i64).after_now()));
619                let either = futures::future::select(timeout, internal_receiver.next());
620                let resolved = either.await;
621                match resolved {
622                    Either::Left(_) => {
623                        return Err(format_err!(
624                            "Carnelian test got timeout before seeing 10 frames"
625                        ));
626                    }
627                    Either::Right((right_result, _)) => {
628                        let message = right_result.expect("message");
629                        match message {
630                            MessageInternal::Render(_) => {
631                                frame_count += 1;
632                            }
633                            _ => (),
634                        }
635                        app.handle_message(message).await.expect("handle_message failed");
636                        if frame_count > 10 {
637                            break;
638                        }
639                    }
640                }
641            }
642            Ok::<(), Error>(())
643        };
644
645        executor.run_singlethreaded(f)?;
646
647        Ok(())
648    }
649
650    fn get_focused_view_key(&self) -> Option<ViewKey> {
651        self.strategy.get_focused_view_key()
652    }
653
654    fn get_view(&mut self, view_key: ViewKey) -> Result<&mut ViewController, Error> {
655        if let Some(view) = self.view_controllers.get_mut(&view_key) {
656            Ok(view)
657        } else {
658            bail!("Could not find view controller for {}", view_key);
659        }
660    }
661
662    fn get_view_keys_for_display(&mut self, display_id: DisplayId) -> Vec<ViewKey> {
663        self.view_controllers
664            .iter()
665            .filter_map(|(view_key, view_controller)| {
666                view_controller.is_hosted_on_display(display_id).then_some(*view_key)
667            })
668            .collect()
669    }
670
671    fn close_view(&mut self, view_key: ViewKey) {
672        let view = self.view_controllers.remove(&view_key);
673        if let Some(mut view) = view {
674            view.close();
675        }
676        self.strategy.handle_view_closed(view_key);
677    }
678
679    fn ownership_changed(&mut self, owned: bool) {
680        for (_, view_controller) in &mut self.view_controllers {
681            view_controller.ownership_changed(owned);
682        }
683    }
684
685    fn drop_display_resources(&mut self) {
686        for (_, view_controller) in &mut self.view_controllers {
687            view_controller.drop_display_resources();
688        }
689    }
690
691    /// Send a message to a specific view controller. Messages not handled by the ViewController
692    /// will be forwarded to the `ViewControllerAssistant`.
693    pub fn queue_message(&mut self, target: ViewKey, msg: Message) {
694        self.messages.push((target, msg));
695    }
696
697    // Creates a view assistant for views that are using the render view mode feature, either
698    // in hosted or direct mode.
699    fn create_view_assistant(
700        &mut self,
701        view_key: ViewKey,
702        display_id: Option<DisplayId>,
703        options: Option<CreateViewOptions>,
704    ) -> Result<ViewAssistantPtr, Error> {
705        Ok(self.assistant.create_view_assistant_with_parameters(ViewCreationParameters {
706            view_key,
707            display_id,
708            app_sender: AppSender { sender: self.sender.clone() },
709            options,
710        })?)
711    }
712
713    async fn create_view_with_params(
714        &mut self,
715        params: ViewStrategyParams,
716        options: Option<CreateViewOptions>,
717    ) -> Result<(), Error> {
718        let view_key = if let Some(view_key) = params.view_key() {
719            view_key
720        } else {
721            IdGenerator2::<ViewKey>::next().expect("view_key")
722        };
723        let view_assistant = self
724            .create_view_assistant(view_key, params.display_id(), options)
725            .context("create_view_assistant")?;
726        let sender = &self.sender;
727        let view_strat = {
728            let view_strat = self
729                .strategy
730                .create_view_strategy(view_key, sender.clone(), params)
731                .await
732                .context("create_view_strategy")?;
733            self.strategy.post_setup(sender).await.context("post_setup")?;
734            view_strat
735        };
736        let view_controller =
737            ViewController::new_with_strategy(view_key, view_assistant, view_strat, sender.clone())
738                .await
739                .context("new_with_strategy")?;
740
741        self.view_controllers.insert(view_key, view_controller);
742        Ok(())
743    }
744
745    async fn create_additional_view(
746        &mut self,
747        view_key: ViewKey,
748        options: Option<CreateViewOptions>,
749    ) -> Result<(), Error> {
750        let params = self.strategy.create_view_strategy_params_for_additional_view(view_key);
751        self.create_view_with_params(params, options).await
752    }
753
754    fn close_additional_view(&mut self, view_key: ViewKey) -> Result<(), Error> {
755        self.close_view(view_key);
756        Ok(())
757    }
758
759    fn start_services(self: &mut App) -> Result<(), Error> {
760        let mut fs = component::server::ServiceFs::new_local();
761
762        self.strategy.start_services(self.sender.clone(), &mut fs)?;
763
764        let outgoing_services_names = self.assistant.outgoing_services_names();
765        let mut public = fs.dir("svc");
766        for name in outgoing_services_names {
767            let sender = self.sender.clone();
768            public.add_service_at(name, move |channel| {
769                sender
770                    .unbounded_send(MessageInternal::ServiceConnection(channel, name))
771                    .expect("unbounded_send");
772                None
773            });
774        }
775
776        match fs.take_and_serve_directory_handle() {
777            Err(e) => eprintln!("Error publishing services: {:#}", e),
778            Ok(_) => (),
779        }
780
781        fasync::Task::local(fs.collect()).detach();
782        Ok(())
783    }
784
785    pub(crate) fn image_freed(&mut self, view_id: ViewKey, image_id: u64, collection_id: u32) {
786        if let Ok(view) = self.get_view(view_id) {
787            view.image_freed(image_id, collection_id);
788        }
789    }
790
791    fn load_config() -> Result<Config, Error> {
792        const CARNELIAN_CONFIG_PATH: &str = "/pkg/data/config/carnelian.toml";
793        let config_path = PathBuf::from(CARNELIAN_CONFIG_PATH);
794        if !config_path.exists() {
795            return Ok(Config::default());
796        }
797        let config_contents = fs::read_to_string(config_path)?;
798        let config = toml::from_str(&config_contents)?;
799        Ok(config)
800    }
801}