blob: bcc984a435b0e01501fa4c2fcdd862691fd34708 [file] [log] [blame]
// 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.
#![feature(async_await, await_macro)]
use carnelian::{
set_node_color, App, AppAssistant, Color, Message, Point, ViewAssistant, ViewAssistantContext,
ViewAssistantPtr, ViewKey,
};
use failure::{Error, ResultExt};
use fidl::encoding::OutOfLine;
use fidl::endpoints::create_endpoints;
use fidl_fuchsia_modular::AppConfig;
use fidl_fuchsia_modular_auth::{Account, IdentityProvider};
use fidl_fuchsia_modular_internal::{SessionContextMarker, SessionmgrMarker};
use fidl_fuchsia_sys::EnvironmentControllerProxy;
use fidl_fuchsia_ui_input::PointerEvent;
use fuchsia_app::{
client::{App as LaunchedApp, Launcher},
fuchsia_single_component_package_url,
};
use fuchsia_async as fasync;
use fuchsia_scenic::{
Circle, EntityNode, Rectangle, SessionPtr, ShapeNode, ViewHolder, ViewTokenPair,
};
use fuchsia_syslog::{self as fx_log, fx_log_info, fx_log_warn};
use futures::prelude::*;
use rand::Rng;
use std::collections::BTreeMap;
use std::sync::Arc;
mod layout;
mod message;
mod replica;
mod session_context;
mod toggle;
use crate::layout::{layout, ChildViewData};
use crate::message::VoilaMessage;
use crate::replica::make_replica_env;
use crate::session_context::SessionContext;
use crate::toggle::Toggle;
const CLOUD_PROVIDER_URI: &str = fuchsia_single_component_package_url!("cloud_provider_in_memory");
const ERMINE_URI: &str = fuchsia_single_component_package_url!("ermine");
const MONDRIAN_URI: &str = fuchsia_single_component_package_url!("mondrian");
const BACKGROUND_Z: f32 = 0.0;
const CIRCLE_Z: f32 = BACKGROUND_Z - 8.0;
const REPLICA_Z: f32 = BACKGROUND_Z - 9.0;
const TOGGLE_Z: f32 = BACKGROUND_Z - 9.0;
struct VoilaAppAssistant {}
impl AppAssistant for VoilaAppAssistant {
fn setup(&mut self) -> Result<(), Error> {
Ok(())
}
fn create_view_assistant(
&mut self,
_key: ViewKey,
session: &SessionPtr,
) -> Result<ViewAssistantPtr, Error> {
let cloud_provider = Launcher::new()?.launch(CLOUD_PROVIDER_URI.to_string(), None)?;
Ok(Box::new(VoilaViewAssistant {
background_node: ShapeNode::new(session.clone()),
circle_node: ShapeNode::new(session.clone()),
cloud_provider_app: Arc::new(cloud_provider),
replicas: BTreeMap::new(),
}))
}
}
struct VoilaViewAssistant {
background_node: ShapeNode,
circle_node: ShapeNode,
cloud_provider_app: Arc<LaunchedApp>,
replicas: BTreeMap<u32, ReplicaData>,
}
/// Represents an emulated replica and holds its internal state.
#[allow(unused)]
struct ReplicaData {
environment_ctrl: EnvironmentControllerProxy,
sessionmgr_app: LaunchedApp,
view: ChildViewData,
}
impl VoilaViewAssistant {
fn create_replica(
&mut self,
profile_id: &str,
session: &SessionPtr,
root_node: &EntityNode,
) -> Result<(), Error> {
let replica_random_number = rand::thread_rng().gen_range(1, 1000000);
let replica_id = format!("voila-r{}", replica_random_number.to_string());
fx_log_info!("creating a replica {}", replica_id);
// Launch an instance of sessionmgr for the replica.
let (server, environment_ctrl, app) =
make_replica_env(&replica_id, Arc::clone(&self.cloud_provider_app))?;
fasync::spawn(server.unwrap_or_else(|e| panic!("error providing services: {:?}", e)));
let sessionmgr = app.connect_to_service::<SessionmgrMarker>()?;
// Set up the emulated account.
let mut account = Account {
id: replica_id.clone(),
identity_provider: IdentityProvider::Dev,
display_name: replica_id.clone(),
image_url: "https://example.com".to_string(),
url: "https://example.com".to_string(),
profile_id: profile_id.to_string(),
};
// Set up shell configs.
let mut session_shell_config = AppConfig { url: ERMINE_URI.to_string(), args: None };
let mut story_shell_config = AppConfig { url: MONDRIAN_URI.to_string(), args: None };
// Set up views.
let mut token_pair = ViewTokenPair::new()?;
let host_node = EntityNode::new(session.clone());
let host_view_holder = ViewHolder::new(session.clone(), token_pair.view_holder_token, None);
host_node.attach(&host_view_holder);
root_node.add_child(&host_node);
let view_data = ChildViewData::new(host_node, host_view_holder, Toggle::new(&session)?);
root_node.add_child(&view_data.toggle.node());
let session_data = ReplicaData {
environment_ctrl: environment_ctrl,
sessionmgr_app: app,
view: view_data,
};
self.replicas.insert(session_data.view.id(), session_data);
// Set up SessionContext.
let (session_context_client, session_context_server) =
create_endpoints::<SessionContextMarker>()?;
let session_context = SessionContext {};
let session_context_stream = session_context_server.into_stream()?;
fasync::spawn_local(
async move {
await!(session_context.handle_requests_from_stream(session_context_stream))
.unwrap_or_else(|err| {
fx_log_warn!("error handling SessionContext request channel: {:?}", err);
})
},
);
sessionmgr
.initialize(
"session_id", /* session_id */
Some(OutOfLine(&mut account)),
&mut session_shell_config,
&mut story_shell_config,
false, /* use_session_shell_for_story_shell_factory */
None, /* ledger_token_manager */
None, /* agent_token_manager */
session_context_client,
&mut token_pair.view_token,
)
.context("Failed to issue initialize request for sessionmgr")?;
Ok(())
}
}
impl ViewAssistant for VoilaViewAssistant {
fn setup(&mut self, context: &ViewAssistantContext) -> Result<(), Error> {
set_node_color(
context.session,
&self.background_node,
&Color { r: 0x00, g: 0x00, b: 0xff, a: 0xff },
);
context.root_node.add_child(&self.background_node);
set_node_color(
context.session,
&self.circle_node,
&Color { r: 0xff, g: 0x00, b: 0xff, a: 0xff },
);
context.root_node.add_child(&self.circle_node);
let profile_random_number = rand::thread_rng().gen_range(1, 1000000);
let profile_id = format!("voila-p{}", profile_random_number.to_string());
self.create_replica(&profile_id, context.session, context.root_node)?;
self.create_replica(&profile_id, context.session, context.root_node)?;
Ok(())
}
fn update(&mut self, context: &ViewAssistantContext) -> Result<(), Error> {
let center_x = context.size.width * 0.5;
let center_y = context.size.height * 0.5;
self.background_node.set_shape(&Rectangle::new(
context.session.clone(),
context.size.width,
context.size.height,
));
self.background_node.set_translation(center_x, center_y, BACKGROUND_Z);
let circle_radius = context.size.width.min(context.size.height) * 0.25;
self.circle_node.set_shape(&Circle::new(context.session.clone(), circle_radius));
self.circle_node.set_translation(center_x, center_y, CIRCLE_Z);
let mut views: Vec<&mut ChildViewData> =
self.replicas.iter_mut().map(|(_key, child_session)| &mut child_session.view).collect();
layout(&mut views, &context.size, &context.session)?;
Ok(())
}
fn handle_message(&mut self, message: Message) {
if let Some(voila_message) = message.downcast_ref::<VoilaMessage>() {
match voila_message {
VoilaMessage::ReplicaConnectivityToggled(key) => {
let maybe_replica = self.replicas.get_mut(key);
match maybe_replica {
Some(replica) => replica.view.toggle.toggle(),
None => {
fx_log_warn!("did not find the toggle to toggle");
}
}
}
}
}
}
fn handle_pointer_event(
&mut self,
context: &mut ViewAssistantContext,
pointer_event: &PointerEvent,
) -> Result<(), Error> {
for (key, child_session) in &mut self.replicas {
if child_session
.view
.toggle
.bounds
.contains(&Point::new(pointer_event.x, pointer_event.y))
{
child_session.view.toggle.handle_pointer_event(context, pointer_event, *key);
}
}
Ok(())
}
}
fn main() -> Result<(), Error> {
fx_log::init_with_tags(&["voila"])?;
fx_log::set_severity(fx_log::levels::INFO);
let assistant = VoilaAppAssistant {};
App::run(Box::new(assistant))
}