blob: bf76efc8c0ec74fa6a0881f4469edeb267276e26 [file] [log] [blame]
// Copyright 2018 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::view::{NewViewParams, ViewAssistantPtr, ViewController, ViewControllerPtr, ViewKey};
use failure::{Error, ResultExt};
use fidl::endpoints::{RequestStream, ServerEnd, ServiceMarker};
use fidl_fuchsia_ui_app as viewsv2;
use fidl_fuchsia_ui_viewsv1::{
ViewManagerMarker, ViewManagerProxy, ViewProviderMarker, ViewProviderRequest::CreateView,
ViewProviderRequestStream,
};
use fidl_fuchsia_ui_viewsv1token::ViewOwnerMarker;
use fuchsia_app::{self as component, client::connect_to_service, server::FdioServer};
use fuchsia_async as fasync;
use fuchsia_scenic::SessionPtr;
use fuchsia_zircon::EventPair;
use futures::{TryFutureExt, TryStreamExt};
use lazy_static::lazy_static;
use parking_lot::Mutex;
use std::{any::Any, collections::BTreeMap, sync::Arc};
/// Trait that a mod author must implement. Currently responsible for creating
/// a view assistant when the Fuchsia view framework requests that the mod create
/// a view.
pub trait AppAssistant: Send {
/// This method is responsible for setting up the AppAssistant implementation.
/// _It's not clear if this is going to so useful, as anything that isn't
/// initialized in the creation of the structure implementing AppAssistant
/// is going to have to be represented as an `Option`, which is awkward._
fn setup(&mut self) -> Result<(), Error>;
/// Called when the Fuchsia view system requests that a view be created.
fn create_view_assistant(&mut self, session: &SessionPtr) -> Result<ViewAssistantPtr, Error>;
}
pub type AppAssistantPtr = Mutex<Box<dyn AppAssistant>>;
/// Struct that implements module-wide responsibilties, currently limited
/// to creating views on request.
pub struct App {
pub(crate) view_manager: ViewManagerProxy,
view_controllers: BTreeMap<ViewKey, ViewControllerPtr>,
next_key: ViewKey,
assistant: Option<AppAssistantPtr>,
}
/// Reference to the singleton app. _This type is likely to change in the future so
/// using this type alias might make for easier forward migration._
pub type AppPtr = Arc<Mutex<App>>;
lazy_static! {
/// Singleton reference to the running application
pub static ref APP: AppPtr = App::new().expect("Failed to create app");
}
impl App {
fn new() -> Result<AppPtr, Error> {
let view_manager = connect_to_service::<ViewManagerMarker>()?;
Ok(Arc::new(Mutex::new(App {
view_manager,
view_controllers: BTreeMap::new(),
next_key: 0,
assistant: None,
})))
}
/// Starts an application based on fuchsia-ui. The `assistant` parameter will
/// be used to create new views when asked to do so by the Fuchsia view system.
pub fn run(assistant: Box<AppAssistant>) -> Result<(), Error> {
let mut executor = fasync::Executor::new().context("Error creating executor")?;
APP.lock().set_assistant(Mutex::new(assistant));
let fut = Self::start_services(&APP)?;
APP.lock().assistant.as_ref().unwrap().lock().setup()?;
executor.run_singlethreaded(fut)?;
Ok(())
}
fn set_assistant(&mut self, assistant: AppAssistantPtr) {
self.assistant = Some(assistant);
}
/// Method for app and view assistants to use from closures in
/// order to reconnect with a specific `ViewController`.
pub fn find_view_controller(&self, key: ViewKey) -> Option<&ViewControllerPtr> {
self.view_controllers.get(&key)
}
/// Send a message to a specific view controller. Messages not handled by the ViewController
/// will be forwarded to the `ViewControllerAssistant`.
pub fn send_message(&mut self, target: ViewKey, msg: &Any) {
if let Some(view) = self.view_controllers.get(&target) {
view.lock().send_message(msg);
}
}
pub(crate) fn create_view_assistant(
&mut self, session: &SessionPtr,
) -> Result<ViewAssistantPtr, Error> {
Ok(self
.assistant
.as_ref()
.unwrap()
.lock()
.create_view_assistant(session)?)
}
fn create_view(&mut self, req: ServerEnd<ViewOwnerMarker>) -> Result<(), Error> {
let view_controller = ViewController::new(self, NewViewParams::V1(req), self.next_key)?;
self.view_controllers.insert(self.next_key, view_controller);
self.next_key += 1;
Ok(())
}
fn create_view2(&mut self, view_token: EventPair) -> Result<(), Error> {
let view_controller =
ViewController::new(self, NewViewParams::V2(view_token), self.next_key)?;
self.view_controllers.insert(self.next_key, view_controller);
self.next_key += 1;
Ok(())
}
fn spawn_view_provider_server(chan: fasync::Channel, app: &AppPtr) {
let app = app.clone();
fasync::spawn(
ViewProviderRequestStream::from_channel(chan)
.try_for_each(move |req| {
let CreateView { view_owner, .. } = req;
app.lock()
.create_view(view_owner)
.unwrap_or_else(|e| eprintln!("create_view error: {:?}", e));
futures::future::ready(Ok(()))
})
.unwrap_or_else(|e| eprintln!("error running view_provider server: {:?}", e)),
)
}
fn spawn_v2_view_provider_server(chan: fasync::Channel, app: &AppPtr) {
let app = app.clone();
fasync::spawn(
viewsv2::ViewProviderRequestStream::from_channel(chan)
.try_for_each(move |req| {
let viewsv2::ViewProviderRequest::CreateView { token, .. } = req;
app.lock()
.create_view2(token)
.unwrap_or_else(|e| eprintln!("create_view2 error: {:?}", e));
futures::future::ready(Ok(()))
})
.unwrap_or_else(|e| eprintln!("error running V2 view_provider server: {:?}", e)),
)
}
fn start_services(app: &AppPtr) -> Result<FdioServer, Error> {
let app_view_provider = app.clone();
let app_view_provider2 = app.clone();
let services_server = component::server::ServicesServer::new();
let services_server = services_server
.add_service((ViewProviderMarker::NAME, move |channel| {
Self::spawn_view_provider_server(channel, &app_view_provider);
}))
.add_service((viewsv2::ViewProviderMarker::NAME, move |channel| {
Self::spawn_v2_view_provider_server(channel, &app_view_provider2);
}));
Ok(services_server.start()?)
}
}