| // 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)) |
| } |